2 This file is part of GNUnet.
3 Copyright (C) 2011-2013 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @file gns/gnunet-service-credential.c
22 * @brief GNU Credential Service (main service)
23 * @author Adnan Husain
26 #include "gnunet_util_lib.h"
27 #include "gnunet_credential_service.h"
28 #include "gnunet_statistics_service.h"
29 #include "credential.h"
30 #include "credential_serialization.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_signatures.h"
34 // For Looking up GNS request
35 #include <gnunet_dnsparser_lib.h>
36 #include <gnunet_identity_service.h>
37 #include <gnunet_gnsrecord_lib.h>
38 #include <gnunet_namestore_service.h>
39 #include <gnunet_gns_service.h>
40 #include "gnunet_gns_service.h"
45 #define GNUNET_CREDENTIAL_MAX_LENGTH 255
47 struct VerifyRequestHandle;
49 struct GNUNET_CREDENTIAL_DelegationChainEntry
54 struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
59 struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
62 * The issued attribute
64 char *issuer_attribute;
67 * The delegated attribute
69 char *subject_attribute;
75 struct CredentialRecordEntry
80 struct CredentialRecordEntry *next;
85 struct CredentialRecordEntry *prev;
91 struct GNUNET_CREDENTIAL_CredentialRecordData *data;
100 * DLL for delegations - Used as a queue
101 * Insert tail - Pop head
103 struct DelegationQueueEntry
108 struct DelegationQueueEntry *next;
113 struct DelegationQueueEntry *prev;
116 * Children of this attribute
118 struct DelegationQueueEntry *children_head;
121 * Children of this attribute
123 struct DelegationQueueEntry *children_tail;
128 struct GNUNET_GNS_LookupRequest *lookup_request;
133 struct VerifyRequestHandle *handle;
136 * Parent attribute delegation
138 struct DelegationQueueEntry *parent;
143 struct GNUNET_CRYPTO_EcdsaPublicKey *issuer_key;
146 * Issuer attribute delegated to
148 char *issuer_attribute;
151 * The current attribute to look up
153 char *lookup_attribute;
156 * Trailing attribute context
161 * Still to resolve delegation as string
163 char *unresolved_attribute_delegation;
166 * The delegation chain entry
168 struct GNUNET_CREDENTIAL_DelegationChainEntry *delegation_chain_entry;
171 * Delegation chain length until now
178 * Handle to a lookup operation from api
180 struct VerifyRequestHandle
184 * We keep these in a DLL.
186 struct VerifyRequestHandle *next;
189 * We keep these in a DLL.
191 struct VerifyRequestHandle *prev;
194 * Handle to the requesting client
196 struct GNUNET_SERVICE_Client *client;
201 struct GNUNET_GNS_LookupRequest *lookup_request;
207 struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
212 char *issuer_attribute;
217 struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
222 struct CredentialRecordEntry *cred_chain_head;
227 struct CredentialRecordEntry *cred_chain_tail;
232 struct DelegationQueueEntry *chain_start;
237 struct DelegationQueueEntry *chain_end;
240 * Current Delegation Pointer
242 struct DelegationQueueEntry *current_delegation;
245 * The found credential
247 struct GNUNET_CREDENTIAL_CredentialRecordData *credential;
250 * Length of the credential
252 uint32_t credential_size;
255 * Length of found delegation chain
267 uint64_t pending_lookups;
275 static struct VerifyRequestHandle *vrh_head;
280 static struct VerifyRequestHandle *vrh_tail;
283 * Handle to the statistics service
285 static struct GNUNET_STATISTICS_Handle *statistics;
288 * Handle to GNS service.
290 static struct GNUNET_GNS_Handle *gns;
294 cleanup_delegation_queue (struct DelegationQueueEntry *dq_entry)
296 struct DelegationQueueEntry *child;
297 if (NULL == dq_entry)
300 for (child = dq_entry->children_head; NULL != child; child = dq_entry->children_head)
302 GNUNET_CONTAINER_DLL_remove (dq_entry->children_head,
303 dq_entry->children_tail,
305 cleanup_delegation_queue (child);
307 if (NULL != dq_entry->issuer_key)
308 GNUNET_free (dq_entry->issuer_key);
309 if (NULL != dq_entry->lookup_attribute)
310 GNUNET_free (dq_entry->lookup_attribute);
311 if (NULL != dq_entry->issuer_attribute)
312 GNUNET_free (dq_entry->issuer_attribute);
313 if (NULL != dq_entry->unresolved_attribute_delegation)
314 GNUNET_free (dq_entry->unresolved_attribute_delegation);
315 if (NULL != dq_entry->attr_trailer)
316 GNUNET_free (dq_entry->attr_trailer);
317 if (NULL != dq_entry->lookup_request)
319 GNUNET_GNS_lookup_cancel (dq_entry->lookup_request);
320 dq_entry->lookup_request = NULL;
322 if (NULL != dq_entry->delegation_chain_entry)
324 if (NULL != dq_entry->delegation_chain_entry->subject_attribute)
325 GNUNET_free (dq_entry->delegation_chain_entry->subject_attribute);
326 if (NULL != dq_entry->delegation_chain_entry->issuer_attribute)
327 GNUNET_free (dq_entry->delegation_chain_entry->issuer_attribute);
328 GNUNET_free (dq_entry->delegation_chain_entry);
330 GNUNET_free (dq_entry);
334 cleanup_handle (struct VerifyRequestHandle *vrh)
336 struct CredentialRecordEntry *cr_entry;
337 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
339 if (NULL != vrh->lookup_request)
341 GNUNET_GNS_lookup_cancel (vrh->lookup_request);
342 vrh->lookup_request = NULL;
344 if (NULL != vrh->credential)
345 GNUNET_free (vrh->credential);
346 cleanup_delegation_queue (vrh->chain_start);
347 if (NULL != vrh->issuer_attribute)
348 GNUNET_free (vrh->issuer_attribute);
349 for (cr_entry = vrh->cred_chain_head;
350 NULL != vrh->cred_chain_head;
351 cr_entry = vrh->cred_chain_head)
353 GNUNET_CONTAINER_DLL_remove (vrh->cred_chain_head,
354 vrh->cred_chain_tail,
356 if (NULL != cr_entry->data)
357 GNUNET_free (cr_entry->data);
358 GNUNET_free (cr_entry);
364 * Task run during shutdown.
370 shutdown_task (void *cls)
372 struct VerifyRequestHandle *vrh;
374 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
377 while (NULL != (vrh = vrh_head))
379 //CREDENTIAL_resolver_lookup_cancel (clh->lookup);
380 GNUNET_CONTAINER_DLL_remove (vrh_head,
383 cleanup_handle (vrh);
388 GNUNET_GNS_disconnect (gns);
391 if (NULL != statistics)
393 GNUNET_STATISTICS_destroy (statistics,
401 * Checks a #GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY message
403 * @param cls client sending the message
404 * @param v_msg message of type `struct VerifyMessage`
405 * @return #GNUNET_OK if @a v_msg is well-formed
408 check_verify (void *cls,
409 const struct VerifyMessage *v_msg)
414 msg_size = ntohs (v_msg->header.size);
415 if (msg_size < sizeof (struct VerifyMessage))
418 return GNUNET_SYSERR;
420 if ((ntohs (v_msg->issuer_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH) ||
421 (ntohs (v_msg->subject_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH))
424 return GNUNET_SYSERR;
426 attrs = (const char *) &v_msg[1];
428 if ( ('\0' != attrs[ntohs(v_msg->header.size) - sizeof (struct VerifyMessage) - 1]) ||
429 (strlen (attrs) > GNUNET_CREDENTIAL_MAX_LENGTH * 2) )
432 return GNUNET_SYSERR;
440 * @param handle the handle to the request
443 send_lookup_response (struct VerifyRequestHandle *vrh)
445 struct GNUNET_MQ_Envelope *env;
446 struct VerifyResultMessage *rmsg;
447 struct DelegationQueueEntry *dq_entry;
448 size_t size = vrh->credential_size;
449 struct GNUNET_CREDENTIAL_Delegation dd[vrh->d_count];
450 struct GNUNET_CREDENTIAL_Credential cred;
452 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
453 "Sending response\n");
454 dq_entry = vrh->chain_end;
455 for (int i=0; i<vrh->d_count; i++)
457 dd[i].issuer_key = dq_entry->delegation_chain_entry->issuer_key;
458 dd[i].subject_key = dq_entry->delegation_chain_entry->subject_key;
459 dd[i].issuer_attribute = dq_entry->delegation_chain_entry->issuer_attribute;
460 dd[i].issuer_attribute_len = strlen (dq_entry->delegation_chain_entry->issuer_attribute)+1;
461 dd[i].subject_attribute_len = 0;
462 if (NULL != dq_entry->delegation_chain_entry->subject_attribute)
464 dd[i].subject_attribute = dq_entry->delegation_chain_entry->subject_attribute;
465 dd[i].subject_attribute_len = strlen(dq_entry->delegation_chain_entry->subject_attribute)+1;
467 dq_entry = dq_entry->parent;
471 * Get serialized record data
472 * Append at the end of rmsg
474 cred.issuer_key = vrh->credential->issuer_key;
475 cred.subject_key = vrh->credential->subject_key;
476 cred.issuer_attribute_len = strlen((char*)&vrh->credential[1]);
477 cred.issuer_attribute = (char*)&vrh->credential[1];
478 size = GNUNET_CREDENTIAL_delegation_chain_get_size (vrh->d_count,
481 env = GNUNET_MQ_msg_extra (rmsg,
483 GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY_RESULT);
484 //Assign id so that client can find associated request
485 rmsg->id = vrh->request_id;
486 rmsg->d_count = htonl (vrh->d_count);
488 if (NULL != vrh->credential)
489 rmsg->cred_found = htonl (GNUNET_YES);
491 rmsg->cred_found = htonl (GNUNET_NO);
493 GNUNET_assert (-1 != GNUNET_CREDENTIAL_delegation_chain_serialize (vrh->d_count,
499 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(vrh->client),
501 GNUNET_CONTAINER_DLL_remove (vrh_head, vrh_tail, vrh);
504 GNUNET_STATISTICS_update (statistics,
505 "Completed verifications", 1,
511 backward_resolution (void* cls,
513 const struct GNUNET_GNSRECORD_Data *rd)
516 struct VerifyRequestHandle *vrh;
517 struct GNUNET_CREDENTIAL_CredentialRecordData *cred;
518 const struct GNUNET_CREDENTIAL_AttributeRecordData *attr;
519 struct CredentialRecordEntry *cred_pointer;
520 struct DelegationQueueEntry *current_delegation;
521 struct DelegationQueueEntry *dq_entry;
523 char *lookup_attribute;
527 current_delegation = cls;
528 current_delegation->lookup_request = NULL;
529 vrh = current_delegation->handle;
530 vrh->pending_lookups--;
531 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
532 "Got %d attrs\n", rd_count);
534 for (i=0; i < rd_count; i++)
536 if (GNUNET_GNSRECORD_TYPE_ATTRIBUTE != rd[i].record_type)
539 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
540 "Found new attribute delegation. Creating new Job...\n");
542 dq_entry = GNUNET_new (struct DelegationQueueEntry);
543 if (NULL != current_delegation->attr_trailer)
545 if (rd[i].data_size == sizeof (struct GNUNET_CREDENTIAL_AttributeRecordData))
547 GNUNET_asprintf (&expanded_attr,
549 current_delegation->attr_trailer);
552 GNUNET_asprintf (&expanded_attr,
555 current_delegation->attr_trailer);
557 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
558 "Expanded to %s\n", expanded_attr);
559 dq_entry->unresolved_attribute_delegation = expanded_attr;
561 if (rd[i].data_size > sizeof (struct GNUNET_CREDENTIAL_AttributeRecordData))
563 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
564 "Not Expanding %s\n", (char*)&attr[1]);
565 dq_entry->unresolved_attribute_delegation = GNUNET_strdup ((char*)&attr[1]);
569 //Add a credential chain entry
570 dq_entry->delegation_chain_entry = GNUNET_new (struct GNUNET_CREDENTIAL_DelegationChainEntry);
571 dq_entry->delegation_chain_entry->subject_key = attr->subject_key;
572 dq_entry->issuer_key = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPublicKey);
573 GNUNET_memcpy (dq_entry->issuer_key,
575 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
576 if (rd[i].data_size > sizeof (struct GNUNET_CREDENTIAL_AttributeRecordData))
577 dq_entry->delegation_chain_entry->subject_attribute = GNUNET_strdup ((char*)&attr[1]);
578 dq_entry->delegation_chain_entry->issuer_key = *current_delegation->issuer_key;
579 dq_entry->delegation_chain_entry->issuer_attribute = GNUNET_strdup (current_delegation->lookup_attribute);
581 dq_entry->parent = current_delegation;
582 dq_entry->d_count = current_delegation->d_count + 1;
583 GNUNET_CONTAINER_DLL_insert (current_delegation->children_head,
584 current_delegation->children_tail,
586 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
587 "Checking for cred match\n");
589 * Check if this delegation already matches one of our credentials
591 for(cred_pointer = vrh->cred_chain_head; cred_pointer != NULL;
592 cred_pointer = cred_pointer->next)
594 cred = cred_pointer->data;
595 if(0 != memcmp (&attr->subject_key,
596 &cred_pointer->data->issuer_key,
597 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
599 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
600 "Checking if %s matches %s\n",
601 dq_entry->unresolved_attribute_delegation, (char*)&cred[1]);
603 if (0 != strcmp (dq_entry->unresolved_attribute_delegation, (char*)&cred[1]))
606 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
608 vrh->credential = GNUNET_malloc (cred_pointer->data_size);
609 memcpy (vrh->credential,
611 cred_pointer->data_size);
612 vrh->credential_size = cred_pointer->data_size;
613 vrh->chain_end = dq_entry;
614 vrh->d_count = dq_entry->d_count;
616 send_lookup_response (vrh);
620 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
621 "Building new lookup request\n");
622 //Continue with backward resolution
623 char issuer_attribute_name[strlen (dq_entry->unresolved_attribute_delegation)+1];
624 strcpy (issuer_attribute_name,
625 dq_entry->unresolved_attribute_delegation);
626 char *next_attr = strtok (issuer_attribute_name, ".");
627 GNUNET_asprintf (&lookup_attribute,
630 GNUNET_asprintf (&dq_entry->lookup_attribute,
633 if (strlen (next_attr) == strlen (dq_entry->unresolved_attribute_delegation))
635 dq_entry->attr_trailer = NULL;
637 next_attr += strlen (next_attr) + 1;
638 dq_entry->attr_trailer = GNUNET_strdup (next_attr);
641 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
642 "Looking up %s\n", dq_entry->lookup_attribute);
643 if (NULL != dq_entry->attr_trailer)
644 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
645 "%s still to go...\n", dq_entry->attr_trailer);
647 vrh->pending_lookups++;
648 dq_entry->handle = vrh;
649 dq_entry->lookup_request = GNUNET_GNS_lookup (gns,
651 dq_entry->issuer_key, //issuer_key,
652 GNUNET_GNSRECORD_TYPE_ATTRIBUTE,
653 GNUNET_GNS_LO_DEFAULT,
654 NULL, //shorten_key, always NULL
655 &backward_resolution,
657 GNUNET_free (lookup_attribute);
660 if(0 == vrh->pending_lookups)
662 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
663 "We are all out of attributes...\n");
664 send_lookup_response (vrh);
672 * Result from GNS lookup.
674 * @param cls the closure (our client lookup handle)
675 * @param rd_count the number of records in @a rd
676 * @param rd the record data
679 handle_credential_query (void* cls,
681 const struct GNUNET_GNSRECORD_Data *rd)
683 struct VerifyRequestHandle *vrh = cls;
684 struct DelegationQueueEntry *dq_entry;
685 const struct GNUNET_CREDENTIAL_CredentialRecordData *crd;
686 struct CredentialRecordEntry *cr_entry;
687 int cred_record_count;
690 vrh->lookup_request = NULL;
691 cred_record_count = 0;
692 for (i=0; i < rd_count; i++)
694 if (GNUNET_GNSRECORD_TYPE_CREDENTIAL != rd[i].record_type)
698 if(GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify(GNUNET_SIGNATURE_PURPOSE_CREDENTIAL,
703 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
704 "Invalid credential found\n");
707 cr_entry = GNUNET_new (struct CredentialRecordEntry);
708 cr_entry->data = GNUNET_malloc (rd[i].data_size);
709 memcpy (cr_entry->data,
712 cr_entry->data_size = rd[i].data_size;
713 GNUNET_CONTAINER_DLL_insert_tail (vrh->cred_chain_head,
714 vrh->cred_chain_tail,
717 if (0 != memcmp (&crd->issuer_key,
719 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
721 if (0 != strcmp ((char*)&crd[1], vrh->issuer_attribute))
723 vrh->credential = GNUNET_malloc (rd[i].data_size);
724 memcpy (vrh->credential,
727 vrh->credential_size = rd[i].data_size;
728 vrh->chain_end = NULL;
729 //Found match prematurely
730 send_lookup_response (vrh);
736 * Check for attributes from the issuer and follow the chain
737 * till you get the required subject's attributes
739 char issuer_attribute_name[strlen (vrh->issuer_attribute)];
740 strcpy (issuer_attribute_name,
741 vrh->issuer_attribute);
742 strcpy (issuer_attribute_name + strlen (vrh->issuer_attribute),
744 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
745 "Looking up %s\n", issuer_attribute_name);
746 dq_entry = GNUNET_new (struct DelegationQueueEntry);
747 dq_entry->issuer_key = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPublicKey);
748 memcpy (dq_entry->issuer_key,
750 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
751 dq_entry->issuer_attribute = GNUNET_strdup (vrh->issuer_attribute);
752 dq_entry->handle = vrh;
753 dq_entry->lookup_attribute = GNUNET_strdup (vrh->issuer_attribute);
754 dq_entry->d_count = 0;
755 vrh->chain_start = dq_entry;
756 vrh->pending_lookups = 1;
757 //Start with backward resolution
758 dq_entry->lookup_request = GNUNET_GNS_lookup (gns,
759 issuer_attribute_name,
760 &vrh->issuer_key, //issuer_key,
761 GNUNET_GNSRECORD_TYPE_ATTRIBUTE,
762 GNUNET_GNS_LO_DEFAULT,
763 NULL, //shorten_key, always NULL
764 &backward_resolution,
770 * Handle Credential verification requests from client
772 * @param cls the closure
773 * @param client the client
774 * @param message the message
777 handle_verify (void *cls,
778 const struct VerifyMessage *v_msg)
780 char attrs[GNUNET_CREDENTIAL_MAX_LENGTH*2 + 1];
781 char issuer_attribute[GNUNET_CREDENTIAL_MAX_LENGTH + 1];
782 char subject_attribute[GNUNET_CREDENTIAL_MAX_LENGTH + 1 + 4];
783 struct VerifyRequestHandle *vrh;
784 struct GNUNET_SERVICE_Client *client = cls;
785 char *attrptr = attrs;
788 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
789 "Received VERIFY message\n");
791 utf_in = (const char *) &v_msg[1];
792 GNUNET_STRINGS_utf8_tolower (utf_in, attrptr);
794 GNUNET_memcpy (issuer_attribute, attrs, ntohs (v_msg->issuer_attribute_len));
795 issuer_attribute[ntohs (v_msg->issuer_attribute_len)] = '\0';
796 GNUNET_memcpy (subject_attribute, attrs+strlen(issuer_attribute), ntohs (v_msg->subject_attribute_len));
797 strcpy (subject_attribute+ntohs (v_msg->subject_attribute_len),
799 subject_attribute[ntohs (v_msg->subject_attribute_len)+4] = '\0';
800 vrh = GNUNET_new (struct VerifyRequestHandle);
801 GNUNET_CONTAINER_DLL_insert (vrh_head, vrh_tail, vrh);
802 vrh->client = client;
803 vrh->request_id = v_msg->id;
804 vrh->issuer_key = v_msg->issuer_key;
805 vrh->subject_key = v_msg->subject_key;
806 vrh->issuer_attribute = GNUNET_strdup (issuer_attribute);
808 if (NULL == subject_attribute)
810 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
811 "No subject attribute provided!\n");
812 send_lookup_response (vrh);
815 if (NULL == issuer_attribute)
817 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
818 "No issuer attribute provided!\n");
819 send_lookup_response (vrh);
822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
826 * First, get attribute from subject
828 vrh->lookup_request = GNUNET_GNS_lookup (gns,
830 &v_msg->subject_key, //subject_pkey,
831 GNUNET_GNSRECORD_TYPE_CREDENTIAL,
832 GNUNET_GNS_LO_DEFAULT,
833 NULL, //shorten_key, always NULL
834 &handle_credential_query,
840 * One of our clients disconnected, clean up after it.
843 * @param client the client that disconnected
846 client_disconnect_cb (void *cls,
847 struct GNUNET_SERVICE_Client *client,
850 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
851 "Client %p disconnected\n",
856 * Add a client to our list of active clients.
859 * @param client client to add
860 * @param mq message queue for @a client
861 * @return this client
864 client_connect_cb (void *cls,
865 struct GNUNET_SERVICE_Client *client,
866 struct GNUNET_MQ_Handle *mq)
868 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
869 "Client %p connected\n",
875 * Process Credential requests.
878 * @param server the initialized server
879 * @param c configuration to use
883 const struct GNUNET_CONFIGURATION_Handle *c,
884 struct GNUNET_SERVICE_Handle *handle)
887 gns = GNUNET_GNS_connect (c);
891 _("Failed to connect to GNS\n"));
894 statistics = GNUNET_STATISTICS_create ("credential", c);
895 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
900 * Define "main" method using service macro
904 GNUNET_SERVICE_OPTION_NONE,
907 &client_disconnect_cb,
909 GNUNET_MQ_hd_var_size (verify,
910 GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY,
911 struct VerifyMessage,
913 GNUNET_MQ_handler_end());
915 /* end of gnunet-service-credential.c */