From 31f74059e0d710254397688aabc201b230ef27da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= 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/lib/exchange_api_handle.c | 257 +++++++++++++++++++----------------------- 1 file changed, 116 insertions(+), 141 deletions(-) (limited to 'src/lib/exchange_api_handle.c') 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