From 31f74059e0d710254397688aabc201b230ef27da Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Sun, 26 Jun 2022 16:59:27 +0200 Subject: [new /keys response] create and parse denomination implemented - /keys response now contains signed denomintations - hashes of denominations now XOR'ed per group into a single hash-code - final hash-code is now XOR of all group hash codes - final hash-code is signed - lib/exchange_api_handle support for new "denominations" implemented - parses array of denomation groups - creates running xor of hashes - verifies signature at the end - previous diff/merge logic for keys remains intact. --- src/exchange/taler-exchange-httpd_keys.c | 119 ++++++++------ src/include/taler_json_lib.h | 44 ++++++ src/json/json_helper.c | 188 +++++++++++++++++++++- src/json/json_pack.c | 23 +-- src/lib/exchange_api_handle.c | 257 ++++++++++++++----------------- 5 files changed, 424 insertions(+), 207 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 7580a8d7b..de5f1fbc9 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -1726,12 +1726,13 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh, * @a recoup and @a denoms. * * @param[in,out] ksh key state handle we build @a krd for - * @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms + * @param[in] denom_keys_hash hash over all the denomination keys in @a denoms * @param last_cpd timestamp to use * @param signkeys list of sign keys to return * @param recoup list of revoked keys to return * @param denoms list of denominations to return * @param grouped_denominations list of grouped denominations to return + * @param[in] h_grouped XOR of all hashes in @a grouped_demoninations * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue @@ -1741,11 +1742,14 @@ create_krd (struct TEH_KeyStateHandle *ksh, json_t *signkeys, json_t *recoup, json_t *denoms, - json_t *grouped_denominations) + json_t *grouped_denominations, + const struct GNUNET_HashCode *h_grouped) { struct KeysResponseData krd; struct TALER_ExchangePublicKeyP exchange_pub; struct TALER_ExchangeSignatureP exchange_sig; + struct TALER_ExchangePublicKeyP grouped_exchange_pub; + struct TALER_ExchangeSignatureP grouped_exchange_sig; json_t *keys; GNUNET_assert (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)); @@ -1753,11 +1757,13 @@ create_krd (struct TEH_KeyStateHandle *ksh, GNUNET_assert (NULL != recoup); GNUNET_assert (NULL != denoms); GNUNET_assert (NULL != grouped_denominations); + GNUNET_assert (NULL != h_grouped); GNUNET_assert (NULL != ksh->auditors); GNUNET_assert (NULL != TEH_currency); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Creating /keys at cherry pick date %s\n", GNUNET_TIME_timestamp2s (last_cpd)); + /* Sign hash over denomination keys */ { enum TALER_ErrorCode ec; @@ -1779,6 +1785,33 @@ create_krd (struct TEH_KeyStateHandle *ksh, } } + /* Sign grouped hash */ + { + enum TALER_ErrorCode ec; + + if (TALER_EC_NONE != + (ec = + TALER_exchange_online_key_set_sign ( + &TEH_keys_exchange_sign2_, + ksh, + last_cpd, + h_grouped, + &grouped_exchange_pub, + &grouped_exchange_sig))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Could not create key response data: cannot sign grouped hash (%s)\n", + TALER_ErrorCode_get_hint (ec)); + return GNUNET_SYSERR; + } + } + + /* both public keys really must be the same */ + GNUNET_assert (0 == + memcmp (&grouped_exchange_pub, + &exchange_pub, + sizeof(exchange_pub))); + { const struct SigningKey *sk; @@ -1815,7 +1848,9 @@ create_krd (struct TEH_KeyStateHandle *ksh, GNUNET_JSON_pack_data_auto ("eddsa_pub", &exchange_pub), GNUNET_JSON_pack_data_auto ("eddsa_sig", - &exchange_sig)); + &exchange_sig), + GNUNET_JSON_pack_data_auto ("denominations_sig", + &grouped_exchange_sig)); GNUNET_assert (NULL != keys); /* Set wallet limit if KYC is configured */ @@ -1998,6 +2033,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) struct GNUNET_TIME_Timestamp last_cpd; struct GNUNET_CONTAINER_Heap *heap; struct GNUNET_HashContext *hash_context = NULL; + struct GNUNET_HashCode grouped_hash_xor = {0}; sctx.signkeys = json_array (); GNUNET_assert (NULL != sctx.signkeys); @@ -2043,8 +2079,11 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) /* groupData is the value we store for each group meta-data */ struct groupData { - json_t *json; /* The json blob with the group meta-data and list of denominations */ - struct GNUNET_HashContext *hash_context; /* hash over all denominations in that group */ + /* The json blob with the group meta-data and list of denominations */ + json_t *json; + + /* xor of all hashes of denominations in that group */ + struct GNUNET_HashCode hash_xor; }; /* heap = min heap, sorted by start time */ @@ -2065,6 +2104,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_CRYPTO_hash_context_finish ( GNUNET_CRYPTO_hash_context_copy (hash_context), &hc); + if (GNUNET_OK != create_krd (ksh, &hc, @@ -2072,7 +2112,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) sctx.signkeys, recoup, denoms, - grouped_denominations)) + grouped_denominations, + + &grouped_hash_xor)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to generate key response data for %s\n", @@ -2139,21 +2181,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) json_t *list; json_t *entry; struct GNUNET_HashCode key; - - /* Find the group/JSON-blob for the key */ - struct - { - enum TALER_DenominationCipher cipher; - struct TALER_Amount value; - struct TALER_DenomFeeSet fees; - struct TALER_AgeMask age_mask; - } meta = { + struct TALER_DenominationGroup meta = { .cipher = dk->denom_pub.cipher, .value = dk->meta.value, .fees = dk->meta.fees, .age_mask = dk->meta.age_mask, }; + /* Search the group/JSON-blob for the key */ GNUNET_CRYPTO_hash (&meta, sizeof(meta), &key); group = @@ -2168,15 +2203,15 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) char *cipher; group = GNUNET_new (struct groupData); - group->hash_context = GNUNET_CRYPTO_hash_context_start (); + memset (group, 0, sizeof(*group)); switch (meta.cipher) { case TALER_DENOMINATION_RSA: - cipher = age_restricted ? "RSA+age_restriction": "RSA"; + cipher = age_restricted ? "RSA+age_restricted": "RSA"; break; case TALER_DENOMINATION_CS: - cipher = age_restricted ? "CS+age_restriction": "CS"; + cipher = age_restricted ? "CS+age_restricted": "CS"; break; default: GNUNET_assert (false); @@ -2190,10 +2225,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) if (age_restricted) { - char *mask = TALER_age_mask_to_string (&meta.age_mask); int r = json_object_set (group->json, "age_mask", - json_string (mask)); + json_integer (meta.age_mask.bits)); GNUNET_assert (0 == r); } @@ -2252,12 +2286,11 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_assert (NULL != entry); } - // Build up the running hash of all denominations in this group - // - // TODO: FIXME-oec: this is cipher and age_restriction dependend?! - GNUNET_CRYPTO_hash_context_read (group->hash_context, - &dk->h_denom_pub, - sizeof (struct GNUNET_HashCode)); + // Build up the running xor of all hashes of the denominations in this + // group + GNUNET_CRYPTO_hash_xor (&dk->h_denom_pub.hash, + &group->hash_xor, + &group->hash_xor); // Finally, add the denomination to the list of denominations in this // group @@ -2267,37 +2300,29 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_assert (0 == json_array_append_new (list, entry)); } - } + } /* loop over heap ends */ // Create the JSON-array of grouped denominations if (0 < GNUNET_CONTAINER_multihashmap_size (denominations_by_group)) { struct GNUNET_CONTAINER_MultiHashMapIterator *iter; - struct GNUNET_HashCode all_hashcode; - struct GNUNET_HashContext *all_hash_ctx; struct groupData *group = NULL; - all_hash_ctx = - GNUNET_CRYPTO_hash_context_start (); - iter = GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group); while (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_iterator_next (iter, NULL, (const - void **) - &group)) + GNUNET_CONTAINER_multihashmap_iterator_next (iter, + NULL, + (const + void **) &group)) { struct GNUNET_HashCode hc; - GNUNET_CRYPTO_hash_context_finish ( - group->hash_context, - &hc); - - GNUNET_CRYPTO_hash_context_read (all_hash_ctx, - &hc, - sizeof (struct GNUNET_HashCode)); + GNUNET_CRYPTO_hash_xor (&group->hash_xor, + &grouped_hash_xor, + &grouped_hash_xor); GNUNET_assert (0 == json_object_set ( @@ -2317,12 +2342,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_CONTAINER_multihashmap_iterator_destroy (iter); GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group); - GNUNET_CRYPTO_hash_context_finish ( - all_hash_ctx, - &all_hashcode); - - // FIXME-oec: TODO: - // sign all_hashcode and add the signature to the /keys response } } @@ -2333,7 +2352,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_CRYPTO_hash_context_finish (hash_context, &hc); - if (GNUNET_OK != create_krd (ksh, &hc, @@ -2341,7 +2359,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) sctx.signkeys, recoup, denoms, - grouped_denominations)) + grouped_denominations, + &grouped_hash_xor)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to generate key response data for %s\n", diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index a8ad6f243..f0b105e98 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -359,6 +359,36 @@ TALER_JSON_spec_amount_any_nbo (const char *name, TALER_JSON_pack_amount ("account_fee", &(gfs)->account), \ TALER_JSON_pack_amount ("purse_fee", &(gfs)->purse) +/** + * Group of Denominations. These are the common fields of an array of + * denominations. + * + * The corresponding JSON-blob will also contain an array of particular + * denominations with only the timestamps, cipher-specific public key and the + * master signature. + * + **/ +struct TALER_DenominationGroup +{ + /* currency must be set prior to calling TALER_JSON_spec_denomination_group */ + const char *currency; + enum TALER_DenominationCipher cipher; + struct TALER_Amount value; + struct TALER_DenomFeeSet fees; + struct TALER_AgeMask age_mask; +}; + +/** + * Generate a parser for a group of denominations. + * NOTE: group.currency MUST have been set prior to calling this function. + * + * @param field name of the field, maybe NULL + * @param[out] group denomination group information + * @return corresponding field spec + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_denomination_group (const char *field, + struct TALER_DenominationGroup *group); /** * Generate line in parser specification for denomination public key. @@ -371,6 +401,20 @@ struct GNUNET_JSON_Specification TALER_JSON_spec_denom_pub (const char *field, struct TALER_DenominationPublicKey *pk); +/** + * Generate a parser specification for a denomination public key of a given + * cipher. + * + * @param field name of the field + * @parm cipher which cipher type to parse for + * @param[out] pk key to fill + * @return corresponding field spec + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_denom_pub_cipher (const char *field, + const enum TALER_DenominationCipher cipher, + struct TALER_DenominationPublicKey *pk); + /** * Generate line in parser specification for denomination signature. diff --git a/src/json/json_helper.c b/src/json/json_helper.c index 11aeceefb..9752bb9f8 100644 --- a/src/json/json_helper.c +++ b/src/json/json_helper.c @@ -35,11 +35,15 @@ static enum TALER_DenominationCipher string_to_cipher (const char *cipher_s) { - if (0 == strcasecmp (cipher_s, - "RSA")) + if ((0 == strcasecmp (cipher_s, + "RSA")) || + (0 == strcasecmp (cipher_s, + "RSA+age_restricted"))) return TALER_DENOMINATION_RSA; - if (0 == strcasecmp (cipher_s, - "CS")) + if ((0 == strcasecmp (cipher_s, + "CS")) || + (0 == strcasecmp (cipher_s, + "CS+age_restricted"))) return TALER_DENOMINATION_CS; return TALER_DENOMINATION_INVALID; } @@ -239,6 +243,84 @@ TALER_JSON_spec_amount_any_nbo (const char *name, } +static enum GNUNET_GenericReturnValue +parse_denomination_group (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_DenominationGroup *group = spec->ptr; + const char *cipher; + bool age_mask_missing = false; + bool has_age_restricted_suffix = false; + struct GNUNET_JSON_Specification gspec[] = { + GNUNET_JSON_spec_string ("cipher", + &cipher), + TALER_JSON_spec_amount ("value", + group->currency, + &group->value), + TALER_JSON_SPEC_DENOM_FEES ("fee", + group->currency, + &group->fees), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("age_mask", + &group->age_mask.bits), + &age_mask_missing), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + gspec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + group->cipher = string_to_cipher (cipher); + if (TALER_DENOMINATION_INVALID == group->cipher) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* age_mask and suffix must be consistent */ + has_age_restricted_suffix = + (NULL != strstr (cipher, "+age_restricted")); + if (has_age_restricted_suffix && age_mask_missing) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (age_mask_missing) + group->age_mask.bits = 0; + + return GNUNET_OK; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_denomination_group (const char *name, + struct TALER_DenominationGroup *group) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_denomination_group, + .cleaner = NULL, + .field = name, + .ptr = group, + .ptr_size = sizeof(*group), + .size_ptr = NULL, + }; + + + return ret; +} + + /** * Parse given JSON object to an encrypted contract. * @@ -330,11 +412,14 @@ parse_denom_pub (void *cls, { struct TALER_DenominationPublicKey *denom_pub = spec->ptr; const char *cipher; + bool age_mask_missing = false; struct GNUNET_JSON_Specification dspec[] = { GNUNET_JSON_spec_string ("cipher", &cipher), - GNUNET_JSON_spec_uint32 ("age_mask", - &denom_pub->age_mask.bits), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("age_mask", + &denom_pub->age_mask.bits), + &age_mask_missing), GNUNET_JSON_spec_end () }; const char *emsg; @@ -350,6 +435,10 @@ parse_denom_pub (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } + + if (age_mask_missing) + denom_pub->age_mask.bits = 0; + denom_pub->cipher = string_to_cipher (cipher); switch (denom_pub->cipher) { @@ -433,6 +522,93 @@ TALER_JSON_spec_denom_pub (const char *field, } +/** + * Parse given JSON object partially into a denomination public key. + * + * Depending on the cipher in cls, it parses the corresponding public key type. + * + * @param cls closure, enum TALER_DenominationCipher + * @param cipher cipher to parse for + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_denom_pub_cipher (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_DenominationPublicKey *denom_pub = spec->ptr; + enum TALER_DenominationCipher cipher = (enum TALER_DenominationCipher) cls; + const char *emsg; + unsigned int eline; + + switch (cipher) + { + case TALER_DENOMINATION_RSA: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_rsa_public_key ( + "rsa_pub", + &denom_pub->details.rsa_public_key), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; + } + case TALER_DENOMINATION_CS: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_fixed ("cs_pub", + &denom_pub->details.cs_public_key, + sizeof (denom_pub->details.cs_public_key)), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; + } + default: + GNUNET_break_op (0); + return GNUNET_SYSERR; + } +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_denom_pub_cipher (const char *field, + enum TALER_DenominationCipher cipher, + struct TALER_DenominationPublicKey *pk) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_denom_pub_cipher, + .cleaner = &clean_denom_pub, + .field = field, + .cls = (void *) cipher, + .ptr = pk + }; + + return ret; +} + + /** * Parse given JSON object to denomination signature. * diff --git a/src/json/json_pack.c b/src/json/json_pack.c index 090a8b96b..bb52eeb05 100644 --- a/src/json/json_pack.c +++ b/src/json/json_pack.c @@ -79,35 +79,38 @@ TALER_JSON_pack_denom_pub ( struct GNUNET_JSON_PackSpec ps = { .field_name = name, }; + struct GNUNET_JSON_PackSpec mask_or_end; if (NULL == pk) return ps; + + mask_or_end = (0 != pk->age_mask.bits) ? + GNUNET_JSON_pack_uint64 ("age_mask", pk->age_mask.bits) : + GNUNET_JSON_pack_end_ (); + switch (pk->cipher) { case TALER_DENOMINATION_RSA: ps.object = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("cipher", - "RSA"), - GNUNET_JSON_pack_uint64 ("age_mask", - pk->age_mask.bits), + GNUNET_JSON_pack_string ("cipher", "RSA"), GNUNET_JSON_pack_rsa_public_key ("rsa_public_key", - pk->details.rsa_public_key)); + pk->details.rsa_public_key), + mask_or_end); break; case TALER_DENOMINATION_CS: ps.object = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("cipher", - "CS"), - GNUNET_JSON_pack_uint64 ("age_mask", - pk->age_mask.bits), + GNUNET_JSON_pack_string ("cipher", "CS"), GNUNET_JSON_pack_data_varsize ("cs_public_key", &pk->details.cs_public_key, - sizeof (pk->details.cs_public_key))); + sizeof (pk->details.cs_public_key)), + mask_or_end); break; default: GNUNET_assert (0); } + return ps; } diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index 0e76b289d..be7bb3c39 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -303,24 +303,31 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key, /** - * Parse a exchange's denomination key encoded in JSON. + * Parse a exchange's denomination key encoded in JSON partially. + * + * Only the values for master_sig, timestamps and the cipher-specific public + * key are parsed. All other fields (fees, age_mask, value) MUST have been set + * prior to calling this function, otherwise the signature verification + * performed within this function will fail. * * @param currency expected currency of all fees * @param[out] denom_key where to return the result + * @param cipher cipher type to parse * @param check_sigs should we check signatures? * @param[in] denom_key_obj json to parse * @param master_key master key to use to verify signature - * @param hash_context where to accumulate data for signature verification + * @param hash_xor where to accumulate data for signature verification via XOR * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is * invalid or the json malformed. */ static enum GNUNET_GenericReturnValue -parse_json_denomkey (const char *currency, - struct TALER_EXCHANGE_DenomPublicKey *denom_key, - bool check_sigs, - json_t *denom_key_obj, - struct TALER_MasterPublicKeyP *master_key, - struct GNUNET_HashContext *hash_context) +parse_json_denomkey_partially (const char *currency, + struct TALER_EXCHANGE_DenomPublicKey *denom_key, + enum TALER_DenominationCipher cipher, + bool check_sigs, + json_t *denom_key_obj, + struct TALER_MasterPublicKeyP *master_key, + struct GNUNET_HashCode *hash_xor) { struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("master_sig", @@ -333,14 +340,9 @@ parse_json_denomkey (const char *currency, &denom_key->valid_from), GNUNET_JSON_spec_timestamp ("stamp_expire_legal", &denom_key->expire_legal), - TALER_JSON_spec_amount ("value", - currency, - &denom_key->value), - TALER_JSON_SPEC_DENOM_FEES ("fee", - currency, - &denom_key->fees), - TALER_JSON_spec_denom_pub ("denom_pub", - &denom_key->key), + TALER_JSON_spec_denom_pub_cipher (NULL, + cipher, + &denom_key->key), GNUNET_JSON_spec_end () }; @@ -354,10 +356,11 @@ parse_json_denomkey (const char *currency, } TALER_denom_pub_hash (&denom_key->key, &denom_key->h_key); - if (NULL != hash_context) - GNUNET_CRYPTO_hash_context_read (hash_context, - &denom_key->h_key, - sizeof (struct GNUNET_HashCode)); + if (NULL != hash_xor) + GNUNET_CRYPTO_hash_xor (&denom_key->h_key.hash, + hash_xor, + hash_xor); + if (! check_sigs) return GNUNET_OK; EXITIF (GNUNET_SYSERR == @@ -729,15 +732,13 @@ decode_keys_json (const json_t *resp_obj, struct TALER_EXCHANGE_Keys *key_data, enum TALER_EXCHANGE_VersionCompatibility *vc) { - struct TALER_ExchangeSignatureP sig; - struct GNUNET_HashContext *hash_context = NULL; - struct GNUNET_HashContext *hash_context_restricted = NULL; - bool have_age_restricted_denom = false; + struct TALER_ExchangeSignatureP denominations_sig; + struct GNUNET_HashCode hash_xor = {0}; struct TALER_ExchangePublicKeyP pub; const char *currency; struct GNUNET_JSON_Specification mspec[] = { - GNUNET_JSON_spec_fixed_auto ("eddsa_sig", - &sig), + GNUNET_JSON_spec_fixed_auto ("denominations_sig", + &denominations_sig), GNUNET_JSON_spec_fixed_auto ("eddsa_pub", &pub), GNUNET_JSON_spec_fixed_auto ("master_public_key", @@ -760,7 +761,7 @@ decode_keys_json (const json_t *resp_obj, GNUNET_break_op (0); return GNUNET_SYSERR; } -#if DEBUG +#if 1 /* DEBUG */ json_dumpf (resp_obj, stderr, JSON_INDENT (2)); @@ -829,13 +830,6 @@ decode_keys_json (const json_t *resp_obj, } } - /* parse the master public key and issue date of the response */ - if (check_sig) - { - hash_context = GNUNET_CRYPTO_hash_context_start (); - hash_context_restricted = GNUNET_CRYPTO_hash_context_start (); - } - /* parse the global fees */ { json_t *global_fees; @@ -933,93 +927,101 @@ decode_keys_json (const json_t *resp_obj, /* parse the denomination keys, merging with the possibly EXISTING array as required (/keys cherry picking) */ { - /* The denominations can be in "denoms" and (optionally) in - * "age_restricted_denoms" - */ - struct - { - char *name; - struct GNUNET_HashContext *hc; - bool is_optional_age_restriction; - } - hive[2] = { - { - "denoms", - hash_context, - false - }, - { - "age_restricted_denoms", - hash_context_restricted, - true - } - }; - - for (size_t s = 0; s < sizeof(hive) / sizeof(hive[0]); s++) - { - json_t *denom_keys_array; - json_t *denom_key_obj; - unsigned int index; + json_t *denominations_by_group; + json_t *group_obj; + unsigned int group_idx; - denom_keys_array = json_object_get (resp_obj, - hive[s].name); + denominations_by_group = + json_object_get ( + resp_obj, + "denominations"); - if (NULL == denom_keys_array) - continue; + EXITIF (JSON_ARRAY != + json_typeof (denominations_by_group)); - EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); + json_array_foreach (denominations_by_group, group_idx, group_obj) { + /* First, parse { cipher, fees, value, age_mask } of the current group */ - json_array_foreach (denom_keys_array, index, denom_key_obj) { - struct TALER_EXCHANGE_DenomPublicKey dk; - bool found = false; + struct TALER_DenominationGroup group = { + .currency = currency + }; + struct GNUNET_JSON_Specification group_spec[] = { + TALER_JSON_spec_denomination_group (NULL, &group), + GNUNET_JSON_spec_end () + }; - /* mark that we have at least one age restricted denomination, needed - * for the hash calculation and signature verification below. */ - have_age_restricted_denom |= hive[s].is_optional_age_restriction; + EXITIF (GNUNET_SYSERR == + GNUNET_JSON_parse (group_obj, + group_spec, + NULL, + NULL)); - memset (&dk, - 0, - sizeof (dk)); - EXITIF (GNUNET_SYSERR == - parse_json_denomkey (key_data->currency, - &dk, - check_sig, - denom_key_obj, - &key_data->master_pub, - hive[s].hc)); + /* Now, parse the individual denominations */ + { + json_t *denom_keys_array; + json_t *denom_key_obj; + unsigned int index; + denom_keys_array = json_object_get (group_obj, "denoms"); + EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); + + json_array_foreach (denom_keys_array, index, denom_key_obj) { + struct TALER_EXCHANGE_DenomPublicKey dk = {0}; + bool found = false; + + memset (&dk, 0, sizeof (dk)); + + /* Set the common fields from the group for this particular + * denomination. Required to make the validity check inside + * parse_json_denomkey_partially pass */ + dk.key.cipher = group.cipher; + dk.value = group.value; + dk.fees = group.fees; + dk.key.age_mask = group.age_mask; + + EXITIF (GNUNET_SYSERR == + parse_json_denomkey_partially (key_data->currency, + &dk, + group.cipher, + check_sig, + denom_key_obj, + &key_data->master_pub, + check_sig ? &hash_xor: NULL)); + + for (unsigned int j = 0; + jnum_denom_keys; + j++) + { + if (0 == denoms_cmp (&dk, + &key_data->denom_keys[j])) + { + found = true; + break; + } + } - for (unsigned int j = 0; - jnum_denom_keys; - j++) - { - if (0 == denoms_cmp (&dk, - &key_data->denom_keys[j])) + if (found) { - found = true; - break; + /* 0:0:0 did not support /keys cherry picking */ + TALER_LOG_DEBUG ("Skipping denomination key: already know it\n"); + TALER_denom_pub_free (&dk.key); + continue; } + + if (key_data->denom_keys_size == key_data->num_denom_keys) + GNUNET_array_grow (key_data->denom_keys, + key_data->denom_keys_size, + key_data->denom_keys_size * 2 + 2); + key_data->denom_keys[key_data->num_denom_keys++] = dk; + + /* Update "last_denom_issue_date" */ + TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n", + GNUNET_TIME_timestamp2s (dk.valid_from)); + key_data->last_denom_issue_date + = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date, + dk.valid_from); } - if (found) - { - /* 0:0:0 did not support /keys cherry picking */ - TALER_LOG_DEBUG ("Skipping denomination key: already know it\n"); - TALER_denom_pub_free (&dk.key); - continue; - } - if (key_data->denom_keys_size == key_data->num_denom_keys) - GNUNET_array_grow (key_data->denom_keys, - key_data->denom_keys_size, - key_data->denom_keys_size * 2 + 2); - key_data->denom_keys[key_data->num_denom_keys++] = dk; - - /* Update "last_denom_issue_date" */ - TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n", - GNUNET_TIME_timestamp2s (dk.valid_from)); - key_data->last_denom_issue_date - = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date, - dk.valid_from); }; - } + }; } /* parse the auditor information */ @@ -1139,30 +1141,6 @@ decode_keys_json (const json_t *resp_obj, if (check_sig) { - struct GNUNET_HashCode hc; - - /* If we had any age restricted denominations, add their hash to the end of - * the normal denominations. */ - if (have_age_restricted_denom) - { - struct GNUNET_HashCode hcr; - - GNUNET_CRYPTO_hash_context_finish (hash_context_restricted, - &hcr); - hash_context_restricted = NULL; - GNUNET_CRYPTO_hash_context_read (hash_context, - &hcr, - sizeof(struct GNUNET_HashCode)); - } - else - { - GNUNET_CRYPTO_hash_context_abort (hash_context_restricted); - hash_context_restricted = NULL; - } - - GNUNET_CRYPTO_hash_context_finish (hash_context, - &hc); - hash_context = NULL; EXITIF (GNUNET_OK != TALER_EXCHANGE_test_signing_key (key_data, &pub)); @@ -1170,18 +1148,15 @@ decode_keys_json (const json_t *resp_obj, EXITIF (GNUNET_OK != TALER_exchange_online_key_set_verify ( key_data->list_issue_date, - &hc, + &hash_xor, &pub, - &sig)); + &denominations_sig)); } + return GNUNET_OK; -EXITIF_exit: +EXITIF_exit: *vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR; - if (NULL != hash_context) - GNUNET_CRYPTO_hash_context_abort (hash_context); - if (NULL != hash_context_restricted) - GNUNET_CRYPTO_hash_context_abort (hash_context_restricted); return GNUNET_SYSERR; } -- cgit v1.2.3