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_assert (NULL != cur_value);
484 GNUNET_CONTAINER_DLL_insert (new_token->attr_head,
485 new_token->attr_tail,
491 // reassemble and set
492 GNUNET_assert (token_serialize (new_token,
497 token_record[0].data = enc_token_str;
498 token_record[0].data_size = strlen (enc_token_str) + 1;
499 token_record[0].expiration_time = rd_exp; //Old expiration time
500 token_record[0].record_type = GNUNET_GNSRECORD_TYPE_ID_TOKEN;
501 token_record[0].flags = GNUNET_GNSRECORD_RF_NONE;
504 token_metadata_len = sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)
505 + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)
506 + strlen (scopes) + 1; //With 0-Terminator
507 token_metadata = GNUNET_malloc (token_metadata_len);
508 write_ptr = token_metadata;
509 GNUNET_memcpy (token_metadata, new_ecdhe_privkey, sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey));
510 write_ptr += sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey);
511 GNUNET_memcpy (write_ptr, &token->aud_key, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
512 write_ptr += sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
513 GNUNET_memcpy (write_ptr, scopes, strlen (scopes) + 1); //with 0-Terminator;
515 token_record[1].data = token_metadata;
516 token_record[1].data_size = token_metadata_len;
517 token_record[1].expiration_time = rd_exp;
518 token_record[1].record_type = GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA;
519 token_record[1].flags = GNUNET_GNSRECORD_RF_PRIVATE;
521 ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
528 token_destroy (new_token);
529 token_destroy (token);
530 GNUNET_free (new_ecdhe_privkey);
531 GNUNET_free (enc_token_str);
535 GNUNET_free (scopes);
541 update_identities(void *cls);
550 * @param value the json_t attribute value
551 * @return #GNUNET_YES
554 clear_ego_attrs (void *cls,
555 const struct GNUNET_HashCode *key,
558 struct TokenAttr *attr = value;
559 struct TokenAttrValue *val;
560 struct TokenAttrValue *tmp_val;
561 for (val = attr->val_head; NULL != val;)
564 GNUNET_CONTAINER_DLL_remove (attr->val_head,
567 GNUNET_free (val->value);
571 GNUNET_free (attr->name);
579 token_collect_error_cb (void *cls)
581 struct EgoEntry *ego_entry = cls;
583 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
584 ">>> Updating Ego failed!\n");
585 //Clear attribute map for ego
586 GNUNET_CONTAINER_multihashmap_iterate (ego_entry->attr_map,
589 GNUNET_CONTAINER_multihashmap_clear (ego_entry->attr_map);
590 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
597 token_collect_finished_cb (void *cls)
599 struct EgoEntry *ego_entry = cls;
601 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602 ">>> Updating Ego finished\n");
603 //Clear attribute map for ego
604 GNUNET_CONTAINER_multihashmap_iterate (ego_entry->attr_map,
607 GNUNET_CONTAINER_multihashmap_clear (ego_entry->attr_map);
608 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
615 * Update all ID_TOKEN records for an identity and store them
617 * @param cls the identity entry
618 * @param zone the identity
619 * @param lbl the name of the record
620 * @param rd_count number of records
621 * @param rd record data
624 token_collect (void *cls,
625 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
627 unsigned int rd_count,
628 const struct GNUNET_GNSRECORD_Data *rd)
630 struct EgoEntry *ego_entry = cls;
631 const struct GNUNET_GNSRECORD_Data *token_record;
632 const struct GNUNET_GNSRECORD_Data *token_metadata_record;
633 struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key;
634 struct GNUNET_CRYPTO_EcdhePrivateKey *priv_key;
636 //There should be only a single record for a token under a label
639 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
643 if (rd[0].record_type == GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA)
645 token_metadata_record = &rd[0];
646 token_record = &rd[1];
650 token_record = &rd[0];
651 token_metadata_record = &rd[1];
653 if (token_metadata_record->record_type != GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA)
655 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
658 if (token_record->record_type == GNUNET_GNSRECORD_TYPE_ID_TOKEN)
660 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
664 //Get metadata and decrypt token
665 priv_key = (struct GNUNET_CRYPTO_EcdhePrivateKey *)token_metadata_record->data;
666 ecdhe_privkey = *priv_key;
667 aud_key = (struct GNUNET_CRYPTO_EcdsaPublicKey *)&priv_key[1];
668 scopes = GNUNET_strdup ((char*) aud_key+sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
670 token_parse2 (token_record->data,
675 label = GNUNET_strdup (lbl);
676 rd_exp = token_record->expiration_time;
678 GNUNET_SCHEDULER_add_now (&handle_token_update,
684 attribute_collect_error_cb (void *cls)
686 struct EgoEntry *ego_entry = cls;
688 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
689 ">>> Updating Attributes failed!\n");
690 ego_entry->attributes_dirty = GNUNET_NO;
691 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
697 attribute_collect_finished_cb (void *cls)
699 struct EgoEntry *ego_entry = cls;
701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702 ">>> Updating Attributes finished\n");
703 ego_entry->attributes_dirty = GNUNET_NO;
704 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
711 * Collect all ID_ATTR records for an identity and store them
713 * @param cls the identity entry
714 * @param zone the identity
715 * @param lbl the name of the record
716 * @param rd_count number of records
717 * @param rd record data
721 attribute_collect (void *cls,
722 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
724 unsigned int rd_count,
725 const struct GNUNET_GNSRECORD_Data *rd)
727 struct EgoEntry *ego_entry = cls;
728 struct GNUNET_HashCode key;
729 struct TokenAttr *attr;
730 struct TokenAttrValue *val;
736 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
739 GNUNET_CRYPTO_hash (lbl,
744 if (rd->record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
746 val_str = GNUNET_GNSRECORD_value_to_string (rd->record_type,
749 attr = GNUNET_malloc (sizeof (struct TokenAttr));
750 attr->name = GNUNET_strdup (lbl);
751 val = GNUNET_malloc (sizeof (struct TokenAttrValue));
752 val->value = val_str;
753 GNUNET_CONTAINER_DLL_insert (attr->val_head,
756 GNUNET_assert (GNUNET_OK ==
757 GNUNET_CONTAINER_multihashmap_put (ego_entry->attr_map,
760 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
763 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
767 attr = GNUNET_malloc (sizeof (struct TokenAttr));
768 attr->name = GNUNET_strdup (lbl);
769 for (i = 0; i < rd_count; i++)
771 if (rd[i].record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
773 val_str = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
776 val = GNUNET_malloc (sizeof (struct TokenAttrValue));
777 val->value = val_str;
778 GNUNET_CONTAINER_DLL_insert (attr->val_head,
783 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (ego_entry->attr_map,
786 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
787 GNUNET_NAMESTORE_zone_iterator_next (ns_it);
792 * Update identity information for ego. If attribute map is
793 * dirty, first update the attributes.
795 * @param cls the ego to update
798 update_identities(void *cls)
800 struct EgoEntry *next_ego = cls;
801 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
804 if (NULL == next_ego)
806 if (min_rel_exp.rel_value_us < MIN_WAIT_TIME.rel_value_us)
807 min_rel_exp = MIN_WAIT_TIME;
808 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
809 ">>> Finished. Rescheduling in %"SCNu64"\n",
810 min_rel_exp.rel_value_us);
812 //finished -> reschedule
813 update_task = GNUNET_SCHEDULER_add_delayed (min_rel_exp,
816 min_rel_exp.rel_value_us = 0;
819 priv_key = GNUNET_IDENTITY_ego_get_private_key (next_ego->ego);
820 if (GNUNET_YES == next_ego->attributes_dirty)
822 //Starting over. We must update the Attributes for they might have changed.
823 ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle,
825 &attribute_collect_error_cb,
829 &attribute_collect_finished_cb,
835 //Ego will be dirty next time
836 next_ego->attributes_dirty = GNUNET_YES;
837 ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle,
839 &token_collect_error_cb,
843 &token_collect_finished_cb,
850 * Function called initially to start update task
855 GNUNET_log (GNUNET_ERROR_TYPE_INFO, ">>> Starting Service\n");
856 //Initially iterate all itenties and refresh all tokens
857 update_task = GNUNET_SCHEDULER_add_now (&update_identities,
863 * Initial ego collection function.
868 * @param identifier ego name
872 struct GNUNET_IDENTITY_Ego *ego,
874 const char *identifier)
876 struct EgoEntry *new_entry;
877 if ((NULL == ego) && (STATE_INIT == state))
879 state = STATE_POST_INIT;
883 if (STATE_INIT == state) {
884 new_entry = GNUNET_malloc (sizeof (struct EgoEntry));
885 new_entry->ego = ego;
886 new_entry->attr_map = GNUNET_CONTAINER_multihashmap_create (5,
888 new_entry->attributes_dirty = GNUNET_YES;
889 GNUNET_CONTAINER_DLL_insert_tail(ego_head, ego_tail, new_entry);
899 struct EgoEntry *ego_entry;
900 struct EgoEntry *ego_tmp;
902 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
906 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
910 if (NULL != timeout_task)
911 GNUNET_SCHEDULER_cancel (timeout_task);
912 if (NULL != update_task)
913 GNUNET_SCHEDULER_cancel (update_task);
914 if (NULL != identity_handle)
915 GNUNET_IDENTITY_disconnect (identity_handle);
916 if (NULL != gns_handle)
917 GNUNET_GNS_disconnect (gns_handle);
918 if (NULL != credential_handle)
919 GNUNET_CREDENTIAL_disconnect (credential_handle);
921 GNUNET_NAMESTORE_zone_iteration_stop (ns_it);
923 GNUNET_NAMESTORE_cancel (ns_qe);
924 if (NULL != ns_handle)
925 GNUNET_NAMESTORE_disconnect (ns_handle);
931 for (ego_entry = ego_head;
935 if (0 != GNUNET_CONTAINER_multihashmap_size (ego_tmp->attr_map))
937 GNUNET_CONTAINER_multihashmap_iterate (ego_tmp->attr_map,
942 GNUNET_CONTAINER_multihashmap_destroy (ego_tmp->attr_map);
943 ego_entry = ego_entry->next;
944 GNUNET_free (ego_tmp);
952 * @param tc task context
955 do_shutdown (void *cls)
957 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
958 "Shutting down...\n");
963 static struct GNUNET_MQ_Envelope*
964 create_exchange_result_message (const char* token,
966 uint64_t ticket_nonce,
969 struct GNUNET_MQ_Envelope *env;
970 struct ExchangeResultMessage *erm;
971 uint16_t token_len = strlen (token) + 1;
973 env = GNUNET_MQ_msg_extra (erm,
975 GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE_RESULT);
976 erm->ticket_nonce = htonl (ticket_nonce);
978 GNUNET_memcpy (&erm[1], token, token_len);
983 static struct GNUNET_MQ_Envelope*
984 create_issue_result_message (const char* label,
989 struct GNUNET_MQ_Envelope *env;
990 struct IssueResultMessage *irm;
994 GNUNET_asprintf (&tmp_str, "%s,%s,%s", label, ticket, token);
995 len = strlen (tmp_str) + 1;
996 env = GNUNET_MQ_msg_extra (irm,
998 GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE_RESULT);
1000 GNUNET_memcpy (&irm[1], tmp_str, strlen (tmp_str) + 1);
1001 GNUNET_free (tmp_str);
1006 cleanup_issue_handle (struct IssueHandle *handle)
1008 if (NULL != handle->attr_map)
1009 GNUNET_CONTAINER_multihashmap_destroy (handle->attr_map);
1010 if (NULL != handle->scopes)
1011 GNUNET_free (handle->scopes);
1012 if (NULL != handle->token)
1013 token_destroy (handle->token);
1014 if (NULL != handle->ticket)
1015 ticket_destroy (handle->ticket);
1016 if (NULL != handle->label)
1017 GNUNET_free (handle->label);
1018 GNUNET_free (handle);
1022 store_token_issue_cont (void *cls,
1026 struct IssueHandle *handle = cls;
1027 struct GNUNET_MQ_Envelope *env;
1031 handle->ns_qe = NULL;
1032 if (GNUNET_SYSERR == success)
1034 cleanup_issue_handle (handle);
1035 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n",
1037 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1040 if (GNUNET_OK != ticket_serialize (handle->ticket,
1044 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n",
1045 "Error serializing ticket\n");
1046 cleanup_issue_handle (handle);
1047 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1050 if (GNUNET_OK != token_to_string (handle->token,
1054 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n",
1055 "Error serializing token\n");
1056 GNUNET_free (ticket_str);
1057 cleanup_issue_handle (handle);
1058 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1061 env = create_issue_result_message (handle->label,
1065 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(handle->client),
1067 cleanup_issue_handle (handle);
1068 GNUNET_free (ticket_str);
1069 GNUNET_free (token_str);
1074 * Build a token and store it
1076 * @param cls the IssueHandle
1079 sign_and_return_token (void *cls)
1081 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1082 struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey;
1083 struct IssueHandle *handle = cls;
1084 struct GNUNET_GNSRECORD_Data token_record[2];
1086 char *enc_token_str;
1087 char *token_metadata;
1091 size_t token_metadata_len;
1095 GNUNET_asprintf (&nonce_str, "%lu", handle->nonce);
1096 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Request nonce: %s\n", nonce_str);
1098 GNUNET_CRYPTO_ecdsa_key_get_public (&handle->iss_key,
1100 handle->ticket = ticket_create (handle->nonce,
1105 time = GNUNET_TIME_absolute_get().abs_value_us;
1106 exp_time = time + token_expiration_interval.rel_value_us;
1108 token_add_attr_int (handle->token, "nbf", time);
1109 token_add_attr_int (handle->token, "iat", time);
1110 token_add_attr_int (handle->token, "exp", exp_time);
1111 token_add_attr (handle->token, "nonce", nonce_str);
1113 //Token in a serialized encrypted format
1114 GNUNET_assert (token_serialize (handle->token,
1119 //Token record E,E_K (Token)
1120 token_record[0].data = enc_token_str;
1121 token_record[0].data_size = strlen (enc_token_str) + 1;
1122 token_record[0].expiration_time = exp_time;
1123 token_record[0].record_type = GNUNET_GNSRECORD_TYPE_ID_TOKEN;
1124 token_record[0].flags = GNUNET_GNSRECORD_RF_NONE;
1127 token_metadata_len = sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)
1128 + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)
1129 + strlen (handle->scopes) + 1; //With 0-Terminator
1130 token_metadata = GNUNET_malloc (token_metadata_len);
1131 write_ptr = token_metadata;
1132 GNUNET_memcpy (token_metadata, ecdhe_privkey, sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey));
1133 write_ptr += sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey);
1134 GNUNET_memcpy (write_ptr, &handle->aud_key, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1135 write_ptr += sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
1136 GNUNET_memcpy (write_ptr, handle->scopes, strlen (handle->scopes) + 1); //with 0-Terminator;
1138 token_record[1].data = token_metadata;
1139 token_record[1].data_size = token_metadata_len;
1140 token_record[1].expiration_time = exp_time;
1141 token_record[1].record_type = GNUNET_GNSRECORD_TYPE_ID_TOKEN_METADATA;
1142 token_record[1].flags = GNUNET_GNSRECORD_RF_PRIVATE;
1145 handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
1150 &store_token_issue_cont,
1152 GNUNET_free (ecdhe_privkey);
1153 GNUNET_free (nonce_str);
1154 GNUNET_free (enc_token_str);
1155 GNUNET_free (token_metadata);
1159 * Credential to JSON
1160 * @param cred the credential
1161 * @return the resulting json, NULL if failed
1164 credential_to_json (struct GNUNET_CREDENTIAL_Credential *cred)
1169 char attribute[cred->issuer_attribute_len + 1];
1172 issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key);
1175 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1176 "Issuer in credential malformed\n");
1179 subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key);
1180 if (NULL == subject)
1182 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1183 "Subject in credential malformed\n");
1184 GNUNET_free (issuer);
1187 GNUNET_STRINGS_base64_encode ((char*)&cred->signature,
1188 sizeof (struct GNUNET_CRYPTO_EcdsaSignature),
1191 cred->issuer_attribute,
1192 cred->issuer_attribute_len);
1193 attribute[cred->issuer_attribute_len] = '\0';
1194 cred_obj = json_object ();
1195 json_object_set_new (cred_obj, "issuer", json_string (issuer));
1196 json_object_set_new (cred_obj, "subject", json_string (subject));
1197 json_object_set_new (cred_obj, "attribute", json_string (attribute));
1198 json_object_set_new (cred_obj, "signature", json_string (signature));
1199 json_object_set_new (cred_obj, "expiration", json_integer (cred->expiration.abs_value_us));
1200 GNUNET_free (issuer);
1201 GNUNET_free (subject);
1202 GNUNET_free (signature);
1208 handle_vattr_collection (void* cls,
1209 unsigned int d_count,
1210 struct GNUNET_CREDENTIAL_Delegation *dc,
1211 unsigned int c_count,
1212 struct GNUNET_CREDENTIAL_Credential *cred)
1214 struct IssueHandle *handle = cls;
1215 struct VerifiedAttributeEntry *vattr;
1219 handle->credential_request = NULL;
1223 GNUNET_SCHEDULER_add_now (&sign_and_return_token, handle);
1226 cred_array = json_array();
1227 for (i=0;i<c_count;i++)
1229 cred_json = credential_to_json (cred);
1230 if (NULL == cred_json)
1232 json_array_append (cred_array, cred_json);
1233 token_add_attr_json (handle->token,
1234 handle->v_attr_head->name,
1237 json_decref (cred_array);
1238 vattr = handle->v_attr_head;
1240 GNUNET_CONTAINER_DLL_remove (handle->v_attr_head,
1241 handle->v_attr_tail,
1243 GNUNET_free (vattr->name);
1244 GNUNET_free (vattr);
1246 if (NULL == handle->v_attr_head)
1248 GNUNET_SCHEDULER_add_now (&sign_and_return_token, handle);
1251 handle->credential_request = GNUNET_CREDENTIAL_collect (credential_handle,
1253 handle->v_attr_head->name,
1255 &handle_vattr_collection,
1262 attr_collect_error (void *cls)
1264 struct IssueHandle *handle = cls;
1266 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding attribute Error!\n");
1267 handle->ns_it = NULL;
1268 GNUNET_SCHEDULER_add_now (&sign_and_return_token, handle);
1273 attr_collect_finished (void *cls)
1275 struct IssueHandle *handle = cls;
1277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute END: \n");
1278 handle->ns_it = NULL;
1280 if (NULL == handle->v_attr_head)
1282 GNUNET_SCHEDULER_add_now (&sign_and_return_token, handle);
1285 handle->credential_request = GNUNET_CREDENTIAL_collect (credential_handle,
1287 handle->v_attr_head->name,
1289 &handle_vattr_collection,
1293 * Collect attributes for token
1296 attr_collect (void *cls,
1297 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
1299 unsigned int rd_count,
1300 const struct GNUNET_GNSRECORD_Data *rd)
1302 struct IssueHandle *handle = cls;
1305 struct GNUNET_HashCode key;
1307 GNUNET_CRYPTO_hash (label,
1311 if (0 == rd_count ||
1312 ( (NULL != handle->attr_map) &&
1313 (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->attr_map,
1318 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1322 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", label);
1326 if (rd->record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
1328 data = GNUNET_GNSRECORD_value_to_string (rd->record_type,
1331 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding value: %s\n", data);
1332 token_add_attr (handle->token,
1337 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1342 for (; i < rd_count; i++)
1344 if (rd->record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
1346 data = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
1349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding value: %s\n", data);
1350 token_add_attr (handle->token, label, data);
1355 GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
1359 cleanup_exchange_handle (struct ExchangeHandle *handle)
1361 if (NULL != handle->ticket)
1362 ticket_destroy (handle->ticket);
1363 if (NULL != handle->token)
1364 token_destroy (handle->token);
1365 GNUNET_free (handle);
1369 process_lookup_result (void *cls, uint32_t rd_count,
1370 const struct GNUNET_GNSRECORD_Data *rd)
1372 struct ExchangeHandle *handle = cls;
1373 struct GNUNET_MQ_Envelope *env;
1377 handle->lookup_request = NULL;
1380 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1381 "Number of tokens %d != 2.",
1383 cleanup_exchange_handle (handle);
1384 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1389 GNUNET_GNSRECORD_value_to_string (GNUNET_GNSRECORD_TYPE_ID_TOKEN,
1394 GNUNET_assert (GNUNET_OK == token_parse (record_str,
1395 &handle->aud_privkey,
1399 GNUNET_assert (GNUNET_OK == token_to_string (handle->token,
1400 &handle->aud_privkey,
1403 env = create_exchange_result_message (token_str,
1405 handle->ticket->payload->nonce,
1407 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(handle->client),
1409 cleanup_exchange_handle (handle);
1410 GNUNET_free (record_str);
1411 GNUNET_free (token_str);
1415 * Checks a exchange message
1417 * @param cls client sending the message
1418 * @param xm message of type `struct ExchangeMessage`
1419 * @return #GNUNET_OK if @a xm is well-formed
1422 check_exchange_message (void *cls,
1423 const struct ExchangeMessage *xm)
1427 size = ntohs (xm->header.size);
1428 if (size <= sizeof (struct ExchangeMessage))
1431 return GNUNET_SYSERR;
1438 * Handler for exchange message
1441 * @param client who sent the message
1442 * @param message the message
1445 handle_exchange_message (void *cls,
1446 const struct ExchangeMessage *xm)
1448 struct ExchangeHandle *xchange_handle;
1449 struct GNUNET_SERVICE_Client *client = cls;
1453 ticket = (const char *) &xm[1];
1454 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1455 "Received EXCHANGE of `%s' from client\n",
1457 xchange_handle = GNUNET_malloc (sizeof (struct ExchangeHandle));
1458 xchange_handle->aud_privkey = xm->aud_privkey;
1459 xchange_handle->r_id = xm->id;
1460 if (GNUNET_SYSERR == ticket_parse (ticket,
1461 &xchange_handle->aud_privkey,
1462 &xchange_handle->ticket))
1464 GNUNET_free (xchange_handle);
1465 GNUNET_SERVICE_client_drop (client);
1468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for token under %s\n",
1469 xchange_handle->ticket->payload->label);
1470 GNUNET_asprintf (&lookup_query,
1472 xchange_handle->ticket->payload->label);
1473 GNUNET_SERVICE_client_continue (client);
1474 xchange_handle->client = client;
1475 xchange_handle->lookup_request
1476 = GNUNET_GNS_lookup (gns_handle,
1478 &xchange_handle->ticket->payload->identity_key,
1479 GNUNET_GNSRECORD_TYPE_ID_TOKEN,
1480 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 */