From c165ef31fee97df950081437ebf7484e1b4764ad Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Wed, 22 Jun 2022 21:01:30 +0200 Subject: first steps towards new /keys response: grouped denominations added --- src/exchange/taler-exchange-httpd_keys.c | 321 +++++++++++++++++++++++-------- 1 file changed, 241 insertions(+), 80 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index b3f0eb489..45bed1a19 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -283,7 +283,6 @@ struct SigningKey }; - struct TEH_KeyStateHandle { @@ -404,7 +403,6 @@ struct SuspendedKeysRequests struct GNUNET_TIME_Absolute timeout; }; - /** * Stores the latest generation of our key state. */ @@ -1353,7 +1351,7 @@ denomination_info_cb ( dk->meta = *meta; dk->master_sig = *master_sig; dk->recoup_possible = recoup_possible; - dk->denom_pub.age_mask = meta->age_mask; + dk->denom_pub.age_mask = meta->age_mask; /* FIXME-oec: age_mask -> reserved_field */ GNUNET_assert ( GNUNET_OK == @@ -1361,6 +1359,7 @@ denomination_info_cb ( &dk->h_denom_pub.hash, dk, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } @@ -1727,12 +1726,12 @@ 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 and age_restricted_denoms + * @param[in] denom_keys_hash hash over all the denominatoin 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 age_restricted_denoms list of age restricted denominations to return, can be NULL + * @param grouped_denominations list of grouped denominations to return * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue @@ -1742,7 +1741,7 @@ create_krd (struct TEH_KeyStateHandle *ksh, json_t *signkeys, json_t *recoup, json_t *denoms, - json_t *age_restricted_denoms) + json_t *grouped_denominations) { struct KeysResponseData krd; struct TALER_ExchangePublicKeyP exchange_pub; @@ -1753,6 +1752,7 @@ create_krd (struct TEH_KeyStateHandle *ksh, GNUNET_assert (NULL != signkeys); GNUNET_assert (NULL != recoup); GNUNET_assert (NULL != denoms); + GNUNET_assert (NULL != grouped_denominations); GNUNET_assert (NULL != ksh->auditors); GNUNET_assert (NULL != TEH_currency); GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -1778,6 +1778,7 @@ create_krd (struct TEH_KeyStateHandle *ksh, return GNUNET_SYSERR; } } + { const struct SigningKey *sk; @@ -1803,6 +1804,8 @@ create_krd (struct TEH_KeyStateHandle *ksh, recoup), GNUNET_JSON_pack_array_incref ("denoms", denoms), + GNUNET_JSON_pack_array_incref ("denominations", + grouped_denominations), GNUNET_JSON_pack_array_incref ("auditors", ksh->auditors), GNUNET_JSON_pack_array_incref ("global_fees", @@ -1833,7 +1836,6 @@ create_krd (struct TEH_KeyStateHandle *ksh, { json_t *extensions = json_object (); bool has_extensions = false; - bool age_restriction_enabled = false; /* Fill in the configurations of the enabled extensions */ for (const struct TALER_Extension *extension = TALER_extensions_get_head (); @@ -1851,8 +1853,6 @@ create_krd (struct TEH_KeyStateHandle *ksh, /* flag our findings so far */ has_extensions = true; - age_restriction_enabled = (extension->type == - TALER_Extension_AgeRestriction); GNUNET_assert (NULL != extension->config_json); @@ -1901,20 +1901,6 @@ create_krd (struct TEH_KeyStateHandle *ksh, { json_decref (extensions); } - - // Special case for age restrictions: if enabled, provide the list of - // age-restricted denominations. - if (age_restriction_enabled && - NULL != age_restricted_denoms) - { - GNUNET_assert ( - 0 == - json_object_set ( - keys, - "age_restricted_denoms", - age_restricted_denoms)); - } - } @@ -2010,12 +1996,10 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) json_t *recoup; struct SignKeyCtx sctx; json_t *denoms = NULL; - json_t *age_restricted_denoms = NULL; + json_t *grouped_denominations = NULL; struct GNUNET_TIME_Timestamp last_cpd; struct GNUNET_CONTAINER_Heap *heap; struct GNUNET_HashContext *hash_context = NULL; - struct GNUNET_HashContext *hash_context_restricted = NULL; - bool have_age_restricted_denoms = false; sctx.signkeys = json_array (); GNUNET_assert (NULL != sctx.signkeys); @@ -2045,20 +2029,25 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_assert (NULL != denoms); hash_context = GNUNET_CRYPTO_hash_context_start (); - /* If age restriction is enabled, initialize the array of age restricted - denoms and prepare a hash for them, separate from the others. We will join - those hashes afterwards.*/ - if (0) - { - age_restricted_denoms = json_array (); - GNUNET_assert (NULL != age_restricted_denoms); - hash_context_restricted = GNUNET_CRYPTO_hash_context_start (); - } + grouped_denominations = json_array (); + GNUNET_assert (NULL != grouped_denominations); last_cpd = GNUNET_TIME_UNIT_ZERO_TS; { struct TEH_DenominationKey *dk; + struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group; + + denominations_by_group = + GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_NO /* NO, because keys are only on the stack */); + + /* 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 */ + }; /* heap = min heap, sorted by start time */ while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) @@ -2068,12 +2057,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) dk->meta.start) && (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)) ) { - struct GNUNET_HashCode hc; - - /* FIXME-oec: Do we need to take hash_context_restricted into account - * in this if-branch!? Current tests suggests: no, (they don't fail). - * But something seems to be odd about only finishing hash_context. + /* + * This is not the first entry in the heap (because last_cpd != + * GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different + * start time. Therefore, we create an entry in the ksh. */ + struct GNUNET_HashCode hc; GNUNET_CRYPTO_hash_context_finish ( GNUNET_CRYPTO_hash_context_copy (hash_context), @@ -2085,7 +2074,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) sctx.signkeys, recoup, denoms, - age_restricted_denoms)) + grouped_denominations)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to generate key response data for %s\n", @@ -2096,8 +2085,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) /* intentionally empty */; GNUNET_CONTAINER_heap_destroy (heap); json_decref (denoms); - if (NULL != age_restricted_denoms) - json_decref (age_restricted_denoms); + json_decref (grouped_denominations); json_decref (sctx.signkeys); json_decref (recoup); return GNUNET_SYSERR; @@ -2108,9 +2096,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) { json_t *denom; - json_t *array; - struct GNUNET_HashContext *hc; - denom = GNUNET_JSON_PACK ( @@ -2131,33 +2116,222 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) TALER_JSON_PACK_DENOM_FEES ("fee", &dk->meta.fees)); - /* Put the denom into the correct array depending on the settings and - * the properties of the denomination. Also, we build up the right - * hash for the corresponding array. */ - if (0 && - (0 != dk->denom_pub.age_mask.bits)) + GNUNET_CRYPTO_hash_context_read (hash_context, + &dk->h_denom_pub, + sizeof (struct GNUNET_HashCode)); + + GNUNET_assert ( + 0 == + json_array_append_new ( + denoms, + denom)); + + } + + /** + * Group the denominations by {cipher, value, fees, age_mask}. + * + * For each group we save the group meta-data and the list of + * denominations in this group as a json-blob in the multihashmap + * denominations_by_group. + **/ + + { + static const char *denoms_key = "denoms"; + struct groupData *group; + 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_AgeMask age_mask; + struct TALER_Amount value; + struct TALER_DenomFeeSet fees; + } meta = { + .cipher = dk->denom_pub.cipher, + .value = dk->meta.value, + .fees = dk->meta.fees, + .age_mask = dk->meta.age_mask, + }; + + GNUNET_CRYPTO_hash (&meta, sizeof(meta), &key); + + group = (struct groupData *) GNUNET_CONTAINER_multihashmap_get ( + denominations_by_group, + &key); + + if (NULL == group) { - have_age_restricted_denoms = true; - array = age_restricted_denoms; - hc = hash_context_restricted; + /* + * There is no group for this meta-data yet, so let's create a new + * group entry. + */ + + bool age_restricted = meta.age_mask.bits != 0; + char *cipher; + + group = GNUNET_new (struct groupData); + group->hash_context = GNUNET_CRYPTO_hash_context_start (); + + switch (meta.cipher) + { + case TALER_DENOMINATION_RSA: + cipher = age_restricted ? "RSA+age_restriction": "RSA"; + break; + case TALER_DENOMINATION_CS: + cipher = age_restricted ? "CS+age_restriction": "CS"; + break; + default: + GNUNET_assert (false); + } + + group->json = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", cipher), + TALER_JSON_PACK_DENOM_FEES ("fee", &meta.fees), + TALER_JSON_pack_amount ("value", &meta.value)); + GNUNET_assert (NULL != group->json); + + if (age_restricted) + { + GNUNET_assert (0 == + json_object_set (group->json, + "age_mask", + json_integer (meta.age_mask.bits))); + } + + /* Create a new array for the denominations in this group */ + list = json_array (); + GNUNET_assert (NULL != list); + GNUNET_assert (0 == + json_object_set (group->json, denoms_key, list)); + + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (denominations_by_group, + &key, + group, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); } - else + + /* + * Now that we have found/created the right group, add the denomination + * to the list + */ { - array = denoms; - hc = hash_context; + struct GNUNET_JSON_PackSpec key_spec; + + switch (meta.cipher) + { + case TALER_DENOMINATION_RSA: + key_spec = + GNUNET_JSON_pack_rsa_public_key ("rsa_pub", + dk->denom_pub.details. + rsa_public_key); + break; + case TALER_DENOMINATION_CS: + key_spec = + GNUNET_JSON_pack_data_varsize ("cs_pub", + &dk->denom_pub.details. + cs_public_key, + sizeof (dk->denom_pub.details. + cs_public_key)); + break; + default: + GNUNET_assert (false); + } + + entry = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("master_sig", + &dk->master_sig), + GNUNET_JSON_pack_timestamp ("stamp_start", + dk->meta.start), + GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw", + dk->meta.expire_withdraw), + GNUNET_JSON_pack_timestamp ("stamp_expire_deposit", + dk->meta.expire_deposit), + GNUNET_JSON_pack_timestamp ("stamp_expire_legal", + dk->meta.expire_legal), + key_spec + ); + GNUNET_assert (NULL != entry); } - GNUNET_CRYPTO_hash_context_read (hc, + /* + * 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)); - GNUNET_assert ( - 0 == - json_array_append_new ( - array, - denom)); + /* Finally, add the denomination to the list of denominations in this + * group */ + list = json_object_get (group->json, denoms_key); + GNUNET_assert (NULL != list); + GNUNET_assert (true == json_is_array (list)); + GNUNET_assert (0 == + json_array_append_new (list, entry)); } } + + /* 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)) + { + 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_assert (0 == + json_object_set ( + group->json, + "hash", + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto (NULL, &hc)))); + + GNUNET_assert (0 == + json_array_append_new ( + grouped_denominations, + group->json)); + + GNUNET_free (group); + } + + 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 */ + } } GNUNET_CONTAINER_heap_destroy (heap); @@ -2165,18 +2339,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) { struct GNUNET_HashCode hc; - /* If age restriction is active and we had at least one denomination of - * that sort, we simply add the hash of all age restricted denominations at - * the end of the others. */ - if (0 && have_age_restricted_denoms) - { - struct GNUNET_HashCode hcr; - GNUNET_CRYPTO_hash_context_finish (hash_context_restricted, &hcr); - GNUNET_CRYPTO_hash_context_read (hash_context, - &hcr, - sizeof (struct GNUNET_HashCode)); - } - GNUNET_CRYPTO_hash_context_finish (hash_context, &hc); @@ -2187,14 +2349,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) sctx.signkeys, recoup, denoms, - age_restricted_denoms)) + grouped_denominations)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to generate key response data for %s\n", GNUNET_TIME_timestamp2s (last_cpd)); json_decref (denoms); - if (0 && NULL != age_restricted_denoms) - json_decref (age_restricted_denoms); json_decref (sctx.signkeys); json_decref (recoup); return GNUNET_SYSERR; @@ -2210,8 +2370,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) json_decref (sctx.signkeys); json_decref (recoup); json_decref (denoms); - if (NULL != age_restricted_denoms) - json_decref (age_restricted_denoms); return GNUNET_OK; } @@ -2387,11 +2545,13 @@ build_key_state (struct HelperState *hs, true); return NULL; } + if (management_only) { ksh->management_only = true; return ksh; } + if (GNUNET_OK != finish_keys_response (ksh)) { @@ -2401,6 +2561,7 @@ build_key_state (struct HelperState *hs, true); return NULL; } + return ksh; } -- cgit v1.2.3