2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 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 * @author Martin Schanzenbach
22 * @file src/identity-provider/gnunet-service-identity-provider.c
23 * @brief Identity Token Service
27 #include "gnunet_util_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_identity_service.h"
31 #include "gnunet_gnsrecord_lib.h"
32 #include "gnunet_namestore_service.h"
33 #include "gnunet_credential_service.h"
34 #include "gnunet_statistics_service.h"
35 #include "gnunet_gns_service.h"
36 #include "gnunet_signatures.h"
37 #include "identity_provider.h"
38 #include "identity_token.h"
47 * Normal operation state
49 #define STATE_POST_INIT 1
52 * Minimum interval between updates
54 #define MIN_WAIT_TIME GNUNET_TIME_UNIT_MINUTES
57 * Standard token expiration time
59 #define DEFAULT_TOKEN_EXPIRATION_INTERVAL GNUNET_TIME_UNIT_HOURS
62 * Service state (to detect initial update pass)
67 * Head of ego entry DLL
69 static struct EgoEntry *ego_head;
72 * Tail of ego entry DLL
74 static struct EgoEntry *ego_tail;
79 static struct GNUNET_IDENTITY_Handle *identity_handle;
82 * Token expiration interval
84 static struct GNUNET_TIME_Relative token_expiration_interval;
89 static struct GNUNET_NAMESTORE_Handle *ns_handle;
94 static struct GNUNET_GNS_Handle *gns_handle;
99 static struct GNUNET_CREDENTIAL_Handle *credential_handle;
104 static struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
109 static struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
114 static struct GNUNET_SCHEDULER_Task *timeout_task;
119 static struct GNUNET_SCHEDULER_Task *update_task;
122 * Timeout for next update pass
124 static struct GNUNET_TIME_Relative min_rel_exp;
128 * Currently processed token
130 static struct IdentityToken *token;
133 * Label for currently processed token
138 * Scopes for processed token
143 * Expiration for processed token
145 static uint64_t rd_exp;
148 * ECDHE Privkey for processed token metadata
150 static struct GNUNET_CRYPTO_EcdhePrivateKey ecdhe_privkey;
153 * Handle to the statistics service.
155 static struct GNUNET_STATISTICS_Handle *stats;
160 static const struct GNUNET_CONFIGURATION_Handle *cfg;
162 struct VerifiedAttributeEntry
167 struct VerifiedAttributeEntry *prev;
172 struct VerifiedAttributeEntry *next;
180 struct ExchangeHandle
186 struct GNUNET_SERVICE_Client *client;
191 struct TokenTicket *ticket;
196 struct IdentityToken *token;
201 struct GNUNET_GNS_LookupRequest *lookup_request;
206 struct GNUNET_CRYPTO_EcdsaPrivateKey aud_privkey;
225 struct GNUNET_SERVICE_Client *client;
230 struct GNUNET_CRYPTO_EcdsaPrivateKey iss_key;
235 struct GNUNET_CRYPTO_EcdsaPublicKey iss_pkey;
240 struct GNUNET_CRYPTO_EcdsaPublicKey aud_key;
245 struct GNUNET_TIME_Absolute expiration;
255 struct VerifiedAttributeEntry *v_attr_head;
260 struct VerifiedAttributeEntry *v_attr_tail;
270 struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
275 struct GNUNET_CREDENTIAL_Request *credential_request;
280 struct GNUNET_CONTAINER_MultiHashMap *attr_map;
285 struct IdentityToken *token;
290 struct TokenTicket *ticket;
295 struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
298 * The label the token is stored under
309 * DLL for ego handles to egos containing the ID_ATTRS in a map in json_t format
317 struct EgoEntry *next;
322 struct EgoEntry *prev;
327 struct GNUNET_IDENTITY_Ego *ego;
330 * Attribute map. Contains the attributes as json_t
332 struct GNUNET_CONTAINER_MultiHashMap *attr_map;
335 * Attributes are old and should be updated if GNUNET_YES
337 int attributes_dirty;
341 * Continuation for token store call
344 * @param success error code
345 * @param emsg error message
348 store_token_cont (void *cls,
353 if (GNUNET_SYSERR == success)
355 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
356 "Failed to update token: %s\n",
360 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
365 * This function updates the old token with new attributes,
366 * removes deleted attributes and expiration times.
368 * @param cls the ego entry
371 handle_token_update (void *cls)
373 char *token_metadata;
376 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
377 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
378 struct GNUNET_CRYPTO_EcdhePrivateKey *new_ecdhe_privkey;
379 struct EgoEntry *ego_entry = cls;
380 struct GNUNET_GNSRECORD_Data token_record[2];
381 struct GNUNET_HashCode key_hash;
382 struct GNUNET_TIME_Relative token_rel_exp;
383 struct GNUNET_TIME_Relative token_ttl;
384 struct GNUNET_TIME_Absolute token_exp;
385 struct GNUNET_TIME_Absolute token_nbf;
386 struct GNUNET_TIME_Absolute new_exp;
387 struct GNUNET_TIME_Absolute new_iat;
388 struct GNUNET_TIME_Absolute new_nbf;
389 struct IdentityToken *new_token;
390 struct TokenAttr *cur_value;
391 struct TokenAttr *attr;
392 size_t token_metadata_len;
394 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
395 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
398 //Note: We need the token expiration time here. Not the record expiration
400 //There are two types of tokens: Token that expire on GNS level with
401 //an absolute expiration time. Those are basically tokens that will
402 //be automatically revoked on (record)expiration.
403 //Tokens stored with relative expiration times will expire on the token level (token expiration)
404 //but this service will reissue new tokens that can be retrieved from GNS
407 for (attr = token->attr_head; NULL != attr; attr = attr->next)
409 if (0 == strcmp (attr->name, "exp"))
411 GNUNET_assert (1 == sscanf (attr->val_head->value,
413 &token_exp.abs_value_us));
414 } else if (0 == strcmp (attr->name, "nbf")) {
415 GNUNET_assert (1 == sscanf (attr->val_head->value,
417 &token_nbf.abs_value_us));
420 token_rel_exp = GNUNET_TIME_absolute_get_difference (token_nbf, token_exp);
422 token_ttl = GNUNET_TIME_absolute_get_remaining (token_exp);
423 if (0 != GNUNET_TIME_absolute_get_remaining (token_exp).rel_value_us)
425 //This token is not yet expired! Save and skip
426 if (min_rel_exp.rel_value_us > token_ttl.rel_value_us)
428 min_rel_exp = token_ttl;
434 GNUNET_free (scopes);
436 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440 "Token is expired. Create a new one\n");
441 new_token = token_create (&pub_key,
443 new_exp = GNUNET_TIME_relative_to_absolute (token_rel_exp);
444 new_nbf = GNUNET_TIME_absolute_get ();
446 for (attr = token->attr_head; NULL != attr; attr = attr->next)
448 if (0 == strcmp (attr->name, "exp"))
450 token_add_attr_int (new_token, attr->name, new_exp.abs_value_us);
452 else if (0 == strcmp (attr->name, "nbf"))
454 token_add_attr_int (new_token, attr->name, new_nbf.abs_value_us);
456 else if (0 == strcmp (attr->name, "iat"))
458 token_add_attr_int (new_token, attr->name, new_iat.abs_value_us);
460 else if ((0 == strcmp (attr->name, "iss"))
461 || (0 == strcmp (attr->name, "aud")))
465 else if (0 == strcmp (attr->name, "sub"))
467 token_add_attr (new_token,
469 attr->val_head->value);
473 GNUNET_CRYPTO_hash (attr->name,
476 //Check if attr still exists. omit of not
478 GNUNET_CONTAINER_multihashmap_contains (ego_entry->attr_map,
481 cur_value = GNUNET_CONTAINER_multihashmap_get (ego_entry->attr_map,
483 GNUNET_CONTAINER_DLL_insert (new_token->attr_head,
484 new_token->attr_tail,
490 // reassemble and set
491 GNUNET_assert (token_serialize (new_token,
496 token_record[0].data = enc_token_str;
497 token_record[0].data_size = strlen (enc_token_str) + 1;
498 token_record[0].expiration_time = rd_exp; //Old expiration time
499 token_record[0].record_type = GNUNET_GNSRECORD_TYPE_ID_TOKEN;
500 token_record[0].flags = GNUNET_GNSRECORD_RF_NONE;
503 token_metadata_len = sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)
504 + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)
505 + strlen (scopes) + 1; //With 0-Terminator
506 token_metadata = GNUNET_malloc (token_metadata_len);
507 write_ptr = token_metadata;
508 GNUNET_memcpy (token_metadata, new_ecdhe_privkey, sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey));
509 write_ptr += sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey);
510 GNUNET_memcpy (write_ptr, &token->aud_key, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
511 write_ptr += sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
512 GNUNET_memcpy (write_ptr, scopes, strlen (scopes) + 1); //with 0-Terminator;
514 token_record[1].data = token_metadata;
515 token_record[1].data_size = token_metadata_len;
516 token_record[1].expiration_time = rd_exp;
517 token_record[1].record_type = GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA;
518 token_record[1].flags = GNUNET_GNSRECORD_RF_PRIVATE;
520 ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
527 token_destroy (new_token);
528 token_destroy (token);
529 GNUNET_free (new_ecdhe_privkey);
530 GNUNET_free (enc_token_str);
534 GNUNET_free (scopes);
540 update_identities(void *cls);
549 * @param value the json_t attribute value
550 * @return #GNUNET_YES
553 clear_ego_attrs (void *cls,
554 const struct GNUNET_HashCode *key,
557 struct TokenAttr *attr = value;
558 struct TokenAttrValue *val;
559 struct TokenAttrValue *tmp_val;
560 for (val = attr->val_head; NULL != val;)
563 GNUNET_CONTAINER_DLL_remove (attr->val_head,
566 GNUNET_free (val->value);
570 GNUNET_free (attr->name);
578 token_collect_error_cb (void *cls)
580 struct EgoEntry *ego_entry = cls;
582 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
583 ">>> Updating Ego failed!\n");
584 //Clear attribute map for ego
585 GNUNET_CONTAINER_multihashmap_iterate (ego_entry->attr_map,
588 GNUNET_CONTAINER_multihashmap_clear (ego_entry->attr_map);
589 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
596 token_collect_finished_cb (void *cls)
598 struct EgoEntry *ego_entry = cls;
600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601 ">>> Updating Ego finished\n");
602 //Clear attribute map for ego
603 GNUNET_CONTAINER_multihashmap_iterate (ego_entry->attr_map,
606 GNUNET_CONTAINER_multihashmap_clear (ego_entry->attr_map);
607 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
614 * Update all ID_TOKEN records for an identity and store them
616 * @param cls the identity entry
617 * @param zone the identity
618 * @param lbl the name of the record
619 * @param rd_count number of records
620 * @param rd record data
623 token_collect (void *cls,
624 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
626 unsigned int rd_count,
627 const struct GNUNET_GNSRECORD_Data *rd)
629 struct EgoEntry *ego_entry = cls;
630 const struct GNUNET_GNSRECORD_Data *token_record;
631 const struct GNUNET_GNSRECORD_Data *token_metadata_record;
632 struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key;
633 struct GNUNET_CRYPTO_EcdhePrivateKey *priv_key;
635 //There should be only a single record for a token under a label
638 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
642 if (rd[0].record_type == GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA)
644 token_metadata_record = &rd[0];
645 token_record = &rd[1];
649 token_record = &rd[0];
650 token_metadata_record = &rd[1];
652 if (token_metadata_record->record_type != GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA)
654 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
657 if (token_record->record_type == GNUNET_GNSRECORD_TYPE_ID_TOKEN)
659 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
663 //Get metadata and decrypt token
664 priv_key = (struct GNUNET_CRYPTO_EcdhePrivateKey *)token_metadata_record->data;
665 ecdhe_privkey = *priv_key;
666 aud_key = (struct GNUNET_CRYPTO_EcdsaPublicKey *)&priv_key[1];
667 scopes = GNUNET_strdup ((char*) aud_key+sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
669 token_parse2 (token_record->data,
674 label = GNUNET_strdup (lbl);
675 rd_exp = token_record->expiration_time;
677 GNUNET_SCHEDULER_add_now (&handle_token_update,
683 attribute_collect_error_cb (void *cls)
685 struct EgoEntry *ego_entry = cls;
687 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
688 ">>> Updating Attributes failed!\n");
689 ego_entry->attributes_dirty = GNUNET_NO;
690 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
696 attribute_collect_finished_cb (void *cls)
698 struct EgoEntry *ego_entry = cls;
700 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
701 ">>> Updating Attributes finished\n");
702 ego_entry->attributes_dirty = GNUNET_NO;
703 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
710 * Collect all ID_ATTR records for an identity and store them
712 * @param cls the identity entry
713 * @param zone the identity
714 * @param lbl the name of the record
715 * @param rd_count number of records
716 * @param rd record data
720 attribute_collect (void *cls,
721 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
723 unsigned int rd_count,
724 const struct GNUNET_GNSRECORD_Data *rd)
726 struct EgoEntry *ego_entry = cls;
727 struct GNUNET_HashCode key;
728 struct TokenAttr *attr;
729 struct TokenAttrValue *val;
735 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
738 GNUNET_CRYPTO_hash (lbl,
743 if (rd->record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
745 val_str = GNUNET_GNSRECORD_value_to_string (rd->record_type,
748 attr = GNUNET_malloc (sizeof (struct TokenAttr));
749 attr->name = GNUNET_strdup (lbl);
750 val = GNUNET_malloc (sizeof (struct TokenAttrValue));
751 val->value = val_str;
752 GNUNET_CONTAINER_DLL_insert (attr->val_head,
755 GNUNET_assert (GNUNET_OK ==
756 GNUNET_CONTAINER_multihashmap_put (ego_entry->attr_map,
759 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
762 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
766 attr = GNUNET_malloc (sizeof (struct TokenAttr));
767 attr->name = GNUNET_strdup (lbl);
768 for (i = 0; i < rd_count; i++)
770 if (rd[i].record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
772 val_str = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
775 val = GNUNET_malloc (sizeof (struct TokenAttrValue));
776 val->value = val_str;
777 GNUNET_CONTAINER_DLL_insert (attr->val_head,
782 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (ego_entry->attr_map,
785 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
786 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
791 * Update identity information for ego. If attribute map is
792 * dirty, first update the attributes.
794 * @param cls the ego to update
797 update_identities(void *cls)
799 struct EgoEntry *next_ego = cls;
800 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
803 if (NULL == next_ego)
805 if (min_rel_exp.rel_value_us < MIN_WAIT_TIME.rel_value_us)
806 min_rel_exp = MIN_WAIT_TIME;
807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
808 ">>> Finished. Rescheduling in %"SCNu64"\n",
809 min_rel_exp.rel_value_us);
811 //finished -> reschedule
812 update_task = GNUNET_SCHEDULER_add_delayed (min_rel_exp,
815 min_rel_exp.rel_value_us = 0;
818 priv_key = GNUNET_IDENTITY_ego_get_private_key (next_ego->ego);
819 if (GNUNET_YES == next_ego->attributes_dirty)
821 //Starting over. We must update the Attributes for they might have changed.
822 ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle,
824 &attribute_collect_error_cb,
828 &attribute_collect_finished_cb,
834 //Ego will be dirty next time
835 next_ego->attributes_dirty = GNUNET_YES;
836 ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle,
838 &token_collect_error_cb,
842 &token_collect_finished_cb,
849 * Function called initially to start update task
854 GNUNET_log (GNUNET_ERROR_TYPE_INFO, ">>> Starting Service\n");
855 //Initially iterate all itenties and refresh all tokens
856 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
862 * Initial ego collection function.
867 * @param identifier ego name
871 struct GNUNET_IDENTITY_Ego *ego,
873 const char *identifier)
875 struct EgoEntry *new_entry;
876 if ((NULL == ego) && (STATE_INIT == state))
878 state = STATE_POST_INIT;
882 if (STATE_INIT == state) {
883 new_entry = GNUNET_malloc (sizeof (struct EgoEntry));
884 new_entry->ego = ego;
885 new_entry->attr_map = GNUNET_CONTAINER_multihashmap_create (5,
887 new_entry->attributes_dirty = GNUNET_YES;
888 GNUNET_CONTAINER_DLL_insert_tail(ego_head, ego_tail, new_entry);
898 struct EgoEntry *ego_entry;
899 struct EgoEntry *ego_tmp;
901 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
905 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
909 if (NULL != timeout_task)
910 GNUNET_SCHEDULER_cancel (timeout_task);
911 if (NULL != update_task)
912 GNUNET_SCHEDULER_cancel (update_task);
913 if (NULL != identity_handle)
914 GNUNET_IDENTITY_disconnect (identity_handle);
915 if (NULL != gns_handle)
916 GNUNET_GNS_disconnect (gns_handle);
917 if (NULL != credential_handle)
918 GNUNET_CREDENTIAL_disconnect (credential_handle);
920 GNUNET_NAMESTORE_zone_iteration_stop (ns_it);
922 GNUNET_NAMESTORE_cancel (ns_qe);
923 if (NULL != ns_handle)
924 GNUNET_NAMESTORE_disconnect (ns_handle);
930 for (ego_entry = ego_head;
934 if (0 != GNUNET_CONTAINER_multihashmap_size (ego_tmp->attr_map))
936 GNUNET_CONTAINER_multihashmap_iterate (ego_tmp->attr_map,
941 GNUNET_CONTAINER_multihashmap_destroy (ego_tmp->attr_map);
942 ego_entry = ego_entry->next;
943 GNUNET_free (ego_tmp);
951 * @param tc task context
954 do_shutdown (void *cls)
956 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
957 "Shutting down...\n");
962 static struct GNUNET_MQ_Envelope*
963 create_exchange_result_message (const char* token,
965 uint64_t ticket_nonce,
968 struct GNUNET_MQ_Envelope *env;
969 struct ExchangeResultMessage *erm;
970 uint16_t token_len = strlen (token) + 1;
972 env = GNUNET_MQ_msg_extra (erm,
974 GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE_RESULT);
975 erm->ticket_nonce = htonl (ticket_nonce);
977 GNUNET_memcpy (&erm[1], token, token_len);
982 static struct GNUNET_MQ_Envelope*
983 create_issue_result_message (const char* label,
988 struct GNUNET_MQ_Envelope *env;
989 struct IssueResultMessage *irm;
993 GNUNET_asprintf (&tmp_str, "%s,%s,%s", label, ticket, token);
994 len = strlen (tmp_str) + 1;
995 env = GNUNET_MQ_msg_extra (irm,
997 GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE_RESULT);
999 GNUNET_memcpy (&irm[1], tmp_str, strlen (tmp_str) + 1);
1000 GNUNET_free (tmp_str);
1005 cleanup_issue_handle (struct IssueHandle *handle)
1007 if (NULL != handle->attr_map)
1008 GNUNET_CONTAINER_multihashmap_destroy (handle->attr_map);
1009 if (NULL != handle->scopes)
1010 GNUNET_free (handle->scopes);
1011 if (NULL != handle->token)
1012 token_destroy (handle->token);
1013 if (NULL != handle->ticket)
1014 ticket_destroy (handle->ticket);
1015 if (NULL != handle->label)
1016 GNUNET_free (handle->label);
1017 GNUNET_free (handle);
1021 store_token_issue_cont (void *cls,
1025 struct IssueHandle *handle = cls;
1026 struct GNUNET_MQ_Envelope *env;
1030 handle->ns_qe = NULL;
1031 if (GNUNET_SYSERR == success)
1033 cleanup_issue_handle (handle);
1034 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n",
1036 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1039 if (GNUNET_OK != ticket_serialize (handle->ticket,
1043 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n",
1044 "Error serializing ticket\n");
1045 cleanup_issue_handle (handle);
1046 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1049 if (GNUNET_OK != token_to_string (handle->token,
1053 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n",
1054 "Error serializing token\n");
1055 GNUNET_free (ticket_str);
1056 cleanup_issue_handle (handle);
1057 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1060 env = create_issue_result_message (handle->label,
1064 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(handle->client),
1066 cleanup_issue_handle (handle);
1067 GNUNET_free (ticket_str);
1068 GNUNET_free (token_str);
1073 * Build a token and store it
1075 * @param cls the IssueHandle
1078 sign_and_return_token (void *cls)
1080 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1081 struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey;
1082 struct IssueHandle *handle = cls;
1083 struct GNUNET_GNSRECORD_Data token_record[2];
1085 char *enc_token_str;
1086 char *token_metadata;
1090 size_t token_metadata_len;
1094 GNUNET_asprintf (&nonce_str, "%lu", handle->nonce);
1095 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Request nonce: %s\n", nonce_str);
1097 GNUNET_CRYPTO_ecdsa_key_get_public (&handle->iss_key,
1099 handle->ticket = ticket_create (handle->nonce,
1104 time = GNUNET_TIME_absolute_get().abs_value_us;
1105 exp_time = time + token_expiration_interval.rel_value_us;
1107 token_add_attr_int (handle->token, "nbf", time);
1108 token_add_attr_int (handle->token, "iat", time);
1109 token_add_attr_int (handle->token, "exp", exp_time);
1110 token_add_attr (handle->token, "nonce", nonce_str);
1112 //Token in a serialized encrypted format
1113 GNUNET_assert (token_serialize (handle->token,
1118 //Token record E,E_K (Token)
1119 token_record[0].data = enc_token_str;
1120 token_record[0].data_size = strlen (enc_token_str) + 1;
1121 token_record[0].expiration_time = exp_time;
1122 token_record[0].record_type = GNUNET_GNSRECORD_TYPE_ID_TOKEN;
1123 token_record[0].flags = GNUNET_GNSRECORD_RF_NONE;
1126 token_metadata_len = sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)
1127 + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)
1128 + strlen (handle->scopes) + 1; //With 0-Terminator
1129 token_metadata = GNUNET_malloc (token_metadata_len);
1130 write_ptr = token_metadata;
1131 GNUNET_memcpy (token_metadata, ecdhe_privkey, sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey));
1132 write_ptr += sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey);
1133 GNUNET_memcpy (write_ptr, &handle->aud_key, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1134 write_ptr += sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
1135 GNUNET_memcpy (write_ptr, handle->scopes, strlen (handle->scopes) + 1); //with 0-Terminator;
1137 token_record[1].data = token_metadata;
1138 token_record[1].data_size = token_metadata_len;
1139 token_record[1].expiration_time = exp_time;
1140 token_record[1].record_type = GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA;
1141 token_record[1].flags = GNUNET_GNSRECORD_RF_PRIVATE;
1144 handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
1149 &store_token_issue_cont,
1151 GNUNET_free (ecdhe_privkey);
1152 GNUNET_free (nonce_str);
1153 GNUNET_free (enc_token_str);
1154 GNUNET_free (token_metadata);
1158 * Credential to JSON
1159 * @param cred the credential
1160 * @return the resulting json, NULL if failed
1163 credential_to_json (struct GNUNET_CREDENTIAL_Credential *cred)
1168 char attribute[cred->issuer_attribute_len + 1];
1171 issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key);
1174 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1175 "Issuer in credential malformed\n");
1178 subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key);
1179 if (NULL == subject)
1181 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1182 "Subject in credential malformed\n");
1183 GNUNET_free (issuer);
1186 GNUNET_STRINGS_base64_encode ((char*)&cred->signature,
1187 sizeof (struct GNUNET_CRYPTO_EcdsaSignature),
1190 cred->issuer_attribute,
1191 cred->issuer_attribute_len);
1192 attribute[cred->issuer_attribute_len] = '\0';
1193 cred_obj = json_object ();
1194 json_object_set_new (cred_obj, "issuer", json_string (issuer));
1195 json_object_set_new (cred_obj, "subject", json_string (subject));
1196 json_object_set_new (cred_obj, "attribute", json_string (attribute));
1197 json_object_set_new (cred_obj, "signature", json_string (signature));
1198 json_object_set_new (cred_obj, "expiration", json_integer (cred->expiration.abs_value_us));
1199 GNUNET_free (issuer);
1200 GNUNET_free (subject);
1201 GNUNET_free (signature);
1207 handle_vattr_collection (void* cls,
1208 unsigned int d_count,
1209 struct GNUNET_CREDENTIAL_Delegation *dc,
1210 unsigned int c_count,
1211 struct GNUNET_CREDENTIAL_Credential *cred)
1213 struct IssueHandle *handle = cls;
1214 struct VerifiedAttributeEntry *vattr;
1218 handle->credential_request = NULL;
1222 GNUNET_SCHEDULER_add_now (&sign_and_return_token, handle);
1225 cred_array = json_array();
1226 for (i=0;i<c_count;i++)
1228 cred_json = credential_to_json (cred);
1229 if (NULL == cred_json)
1231 json_array_append (cred_array, cred_json);
1232 token_add_attr_json (handle->token,
1233 handle->v_attr_head->name,
1236 json_decref (cred_array);
1237 vattr = handle->v_attr_head;
1239 GNUNET_CONTAINER_DLL_remove (handle->v_attr_head,
1240 handle->v_attr_tail,
1242 GNUNET_free (vattr->name);
1243 GNUNET_free (vattr);
1245 if (NULL == handle->v_attr_head)
1247 GNUNET_SCHEDULER_add_now (&sign_and_return_token, handle);
1250 handle->credential_request = GNUNET_CREDENTIAL_collect (credential_handle,
1252 handle->v_attr_head->name,
1254 &handle_vattr_collection,
1261 attr_collect_error (void *cls)
1263 struct IssueHandle *handle = cls;
1265 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding attribute Error!\n");
1266 handle->ns_it = NULL;
1267 GNUNET_SCHEDULER_add_now (&sign_and_return_token, handle);
1272 attr_collect_finished (void *cls)
1274 struct IssueHandle *handle = cls;
1276 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute END: \n");
1277 handle->ns_it = NULL;
1279 if (NULL == handle->v_attr_head)
1281 GNUNET_SCHEDULER_add_now (&sign_and_return_token, handle);
1284 handle->credential_request = GNUNET_CREDENTIAL_collect (credential_handle,
1286 handle->v_attr_head->name,
1288 &handle_vattr_collection,
1292 * Collect attributes for token
1295 attr_collect (void *cls,
1296 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
1298 unsigned int rd_count,
1299 const struct GNUNET_GNSRECORD_Data *rd)
1301 struct IssueHandle *handle = cls;
1304 struct GNUNET_HashCode key;
1306 GNUNET_CRYPTO_hash (label,
1310 if (0 == rd_count ||
1311 ( (NULL != handle->attr_map) &&
1312 (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->attr_map,
1317 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", label);
1325 if (rd->record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
1327 data = GNUNET_GNSRECORD_value_to_string (rd->record_type,
1330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding value: %s\n", data);
1331 token_add_attr (handle->token,
1336 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1341 for (; i < rd_count; i++)
1343 if (rd->record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
1345 data = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
1348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding value: %s\n", data);
1349 token_add_attr (handle->token, label, data);
1354 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1358 cleanup_exchange_handle (struct ExchangeHandle *handle)
1360 if (NULL != handle->ticket)
1361 ticket_destroy (handle->ticket);
1362 if (NULL != handle->token)
1363 token_destroy (handle->token);
1364 GNUNET_free (handle);
1368 process_lookup_result (void *cls, uint32_t rd_count,
1369 const struct GNUNET_GNSRECORD_Data *rd)
1371 struct ExchangeHandle *handle = cls;
1372 struct GNUNET_MQ_Envelope *env;
1376 handle->lookup_request = NULL;
1379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1380 "Number of tokens %d != 2.",
1382 cleanup_exchange_handle (handle);
1383 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1388 GNUNET_GNSRECORD_value_to_string (GNUNET_GNSRECORD_TYPE_ID_TOKEN,
1393 GNUNET_assert (GNUNET_OK == token_parse (record_str,
1394 &handle->aud_privkey,
1398 GNUNET_assert (GNUNET_OK == token_to_string (handle->token,
1399 &handle->aud_privkey,
1402 env = create_exchange_result_message (token_str,
1404 handle->ticket->payload->nonce,
1406 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(handle->client),
1408 cleanup_exchange_handle (handle);
1409 GNUNET_free (record_str);
1410 GNUNET_free (token_str);
1414 * Checks a exchange message
1416 * @param cls client sending the message
1417 * @param xm message of type `struct ExchangeMessage`
1418 * @return #GNUNET_OK if @a xm is well-formed
1421 check_exchange_message (void *cls,
1422 const struct ExchangeMessage *xm)
1426 size = ntohs (xm->header.size);
1427 if (size <= sizeof (struct ExchangeMessage))
1430 return GNUNET_SYSERR;
1437 * Handler for exchange message
1440 * @param client who sent the message
1441 * @param message the message
1444 handle_exchange_message (void *cls,
1445 const struct ExchangeMessage *xm)
1447 struct ExchangeHandle *xchange_handle;
1448 struct GNUNET_SERVICE_Client *client = cls;
1452 ticket = (const char *) &xm[1];
1453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1454 "Received EXCHANGE of `%s' from client\n",
1456 xchange_handle = GNUNET_malloc (sizeof (struct ExchangeHandle));
1457 xchange_handle->aud_privkey = xm->aud_privkey;
1458 xchange_handle->r_id = xm->id;
1459 if (GNUNET_SYSERR == ticket_parse (ticket,
1460 &xchange_handle->aud_privkey,
1461 &xchange_handle->ticket))
1463 GNUNET_free (xchange_handle);
1464 GNUNET_SERVICE_client_drop (client);
1467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for token under %s\n",
1468 xchange_handle->ticket->payload->label);
1469 GNUNET_asprintf (&lookup_query,
1471 xchange_handle->ticket->payload->label);
1472 GNUNET_SERVICE_client_continue (client);
1473 xchange_handle->client = client;
1474 xchange_handle->lookup_request
1475 = GNUNET_GNS_lookup (gns_handle,
1477 &xchange_handle->ticket->payload->identity_key,
1478 GNUNET_GNSRECORD_TYPE_ID_TOKEN,
1479 GNUNET_GNS_LO_LOCAL_MASTER,
1481 &process_lookup_result,
1483 GNUNET_free (lookup_query);
1489 find_existing_token_error (void *cls)
1491 struct IssueHandle *handle = cls;
1492 cleanup_issue_handle (handle);
1493 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error looking for existing token\n");
1494 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1499 find_existing_token_finished (void *cls)
1501 struct IssueHandle *handle = cls;
1504 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1505 ">>> No existing token found\n");
1507 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
1509 GNUNET_STRINGS_base64_encode ((char*)&rnd_key,
1512 handle->ns_it = NULL;
1513 handle->ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle,
1515 &attr_collect_error,
1519 &attr_collect_finished,
1526 * Look for existing token
1528 * @param cls the identity entry
1529 * @param zone the identity
1530 * @param lbl the name of the record
1531 * @param rd_count number of records
1532 * @param rd record data
1536 find_existing_token (void *cls,
1537 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
1539 unsigned int rd_count,
1540 const struct GNUNET_GNSRECORD_Data *rd)
1542 struct IssueHandle *handle = cls;
1543 const struct GNUNET_GNSRECORD_Data *token_metadata_record;
1544 struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key;
1545 struct GNUNET_HashCode key;
1546 int scope_count_token;
1550 //There should be only a single record for a token under a label
1553 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1557 if (rd[0].record_type == GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA)
1559 token_metadata_record = &rd[0];
1563 token_metadata_record = &rd[1];
1565 if (token_metadata_record->record_type != GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA)
1567 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1570 ecdhe_privkey = *((struct GNUNET_CRYPTO_EcdhePrivateKey *)token_metadata_record->data);
1572 (struct GNUNET_CRYPTO_EcdsaPublicKey *)(token_metadata_record->data+sizeof(struct GNUNET_CRYPTO_EcdhePrivateKey));
1573 tmp_scopes = GNUNET_strdup ((char*) aud_key+sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1575 if (0 != memcmp (aud_key, &handle->aud_key,
1576 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1578 char *tmp2 = GNUNET_STRINGS_data_to_string_alloc (aud_key,
1579 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1580 //Audience does not match!
1581 char *tmp = GNUNET_GNSRECORD_value_to_string (GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA,
1582 token_metadata_record->data,
1583 token_metadata_record->data_size);
1584 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1585 "Token does not match audience %s vs %s. Moving on\n",
1588 GNUNET_free (tmp_scopes);
1591 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1595 scope = strtok (tmp_scopes, ",");
1596 scope_count_token = 0;
1597 while (NULL != scope)
1599 GNUNET_CRYPTO_hash (scope,
1603 if ((NULL != handle->attr_map) &&
1604 (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->attr_map, &key)))
1606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1607 "Issued token does not include `%s'. Moving on\n", scope);
1608 GNUNET_free (tmp_scopes);
1609 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1612 scope_count_token++;
1613 scope = strtok (NULL, ",");
1615 GNUNET_free (tmp_scopes);
1616 //All scopes in token are also in request. Now
1618 if ((NULL != handle->attr_map) &&
1619 (GNUNET_CONTAINER_multihashmap_size (handle->attr_map) == scope_count_token))
1621 //We have an existing token
1622 handle->label = GNUNET_strdup (lbl);
1623 handle->ns_it = NULL;
1624 handle->ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle,
1626 &attr_collect_error,
1630 &attr_collect_finished,
1635 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1636 "Nuber of attributes in token do not match request\n");
1638 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1642 * Checks an issue message
1644 * @param cls client sending the message
1645 * @param im message of type `struct IssueMessage`
1646 * @return #GNUNET_OK if @a im is well-formed
1649 check_issue_message(void *cls,
1650 const struct IssueMessage *im)
1654 size = ntohs (im->header.size);
1655 if (size <= sizeof (struct IssueMessage))
1658 return GNUNET_SYSERR;
1660 scopes = (char *) &im[1];
1661 if ('\0' != scopes[size - sizeof (struct IssueMessage) - 1])
1663 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1664 "Malformed scopes received!\n");
1666 return GNUNET_SYSERR;
1673 * Handler for issue message
1676 * @param client who sent the message
1677 * @param message the message
1680 handle_issue_message (void *cls,
1681 const struct IssueMessage *im)
1686 const char *v_attrs;
1687 struct GNUNET_HashCode key;
1688 struct IssueHandle *issue_handle;
1689 struct VerifiedAttributeEntry *vattr_entry;
1690 struct GNUNET_SERVICE_Client *client = cls;
1692 scopes = (const char *) &im[1];
1693 v_attrs = (const char *) &im[1] + ntohl(im->scope_len);
1694 issue_handle = GNUNET_malloc (sizeof (struct IssueHandle));
1695 issue_handle->attr_map = GNUNET_CONTAINER_multihashmap_create (5,
1697 scopes_tmp = GNUNET_strdup (scopes);
1699 for (scope = strtok (scopes_tmp, ","); NULL != scope; scope = strtok (NULL, ","))
1701 GNUNET_CRYPTO_hash (scope,
1704 GNUNET_CONTAINER_multihashmap_put (issue_handle->attr_map,
1707 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1709 GNUNET_free (scopes_tmp);
1710 scopes_tmp = GNUNET_strdup (v_attrs);
1712 for (scope = strtok (scopes_tmp, ","); NULL != scope; scope = strtok (NULL, ","))
1714 vattr_entry = GNUNET_new (struct VerifiedAttributeEntry);
1715 vattr_entry->name = GNUNET_strdup (scope);
1716 GNUNET_CONTAINER_DLL_insert (issue_handle->v_attr_head,
1717 issue_handle->v_attr_tail,
1720 GNUNET_free (scopes_tmp);
1724 issue_handle->r_id = im->id;
1725 issue_handle->aud_key = im->aud_key;
1726 issue_handle->iss_key = im->iss_key;
1727 GNUNET_CRYPTO_ecdsa_key_get_public (&im->iss_key,
1728 &issue_handle->iss_pkey);
1729 issue_handle->expiration = GNUNET_TIME_absolute_ntoh (im->expiration);
1730 issue_handle->nonce = ntohl (im->nonce);
1731 GNUNET_SERVICE_client_continue (client);
1732 issue_handle->client = client;
1733 issue_handle->scopes = GNUNET_strdup (scopes);
1734 issue_handle->token = token_create (&issue_handle->iss_pkey,
1735 &issue_handle->aud_key);
1737 issue_handle->ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle,
1739 &find_existing_token_error,
1741 &find_existing_token,
1743 &find_existing_token_finished,
1749 * Main function that will be run
1751 * @param cls closure
1752 * @param args remaining command-line arguments
1753 * @param cfgfile name of the configuration file used (for saving, can be NULL)
1754 * @param c configuration
1758 const struct GNUNET_CONFIGURATION_Handle *c,
1759 struct GNUNET_SERVICE_Handle *server)
1763 stats = GNUNET_STATISTICS_create ("identity-provider", cfg);
1765 //Connect to identity and namestore services
1766 ns_handle = GNUNET_NAMESTORE_connect (cfg);
1767 if (NULL == ns_handle)
1769 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to namestore");
1772 gns_handle = GNUNET_GNS_connect (cfg);
1773 if (NULL == gns_handle)
1775 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to gns");
1777 credential_handle = GNUNET_CREDENTIAL_connect (cfg);
1778 if (NULL == credential_handle)
1780 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to credential");
1782 identity_handle = GNUNET_IDENTITY_connect (cfg,
1787 GNUNET_CONFIGURATION_get_value_time (cfg,
1788 "identity-provider",
1789 "TOKEN_EXPIRATION_INTERVAL",
1790 &token_expiration_interval))
1792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1793 "Time window for zone iteration: %s\n",
1794 GNUNET_STRINGS_relative_time_to_string (token_expiration_interval,
1797 token_expiration_interval = DEFAULT_TOKEN_EXPIRATION_INTERVAL;
1800 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1804 * Called whenever a client is disconnected.
1806 * @param cls closure
1807 * @param client identification of the client
1808 * @param app_ctx @a client
1811 client_disconnect_cb (void *cls,
1812 struct GNUNET_SERVICE_Client *client,
1815 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1816 "Client %p disconnected\n",
1822 * Add a client to our list of active clients.
1825 * @param client client to add
1826 * @param mq message queue for @a client
1827 * @return internal namestore client structure for this client
1830 client_connect_cb (void *cls,
1831 struct GNUNET_SERVICE_Client *client,
1832 struct GNUNET_MQ_Handle *mq)
1834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1835 "Client %p connected\n",
1843 * Define "main" method using service macro.
1846 ("identity-provider",
1847 GNUNET_SERVICE_OPTION_NONE,
1850 &client_disconnect_cb,
1852 GNUNET_MQ_hd_var_size (issue_message,
1853 GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE,
1854 struct IssueMessage,
1856 GNUNET_MQ_hd_var_size (exchange_message,
1857 GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE,
1858 struct ExchangeMessage,
1860 GNUNET_MQ_handler_end());
1861 /* end of gnunet-service-identity-provider.c */