diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_keys.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_keys.c | 1865 |
1 files changed, 1346 insertions, 519 deletions
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index f9c35eee1..05fb685e0 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020-2022 Taler Systems SA + Copyright (C) 2020-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -17,12 +17,15 @@ * @file taler-exchange-httpd_keys.c * @brief management of our various keys * @author Christian Grothoff + * @author Özgür Kesim */ #include "platform.h" #include "taler_json_lib.h" #include "taler_mhd_lib.h" +#include "taler_kyclogic_lib.h" #include "taler_dbevents.h" #include "taler-exchange-httpd.h" +#include "taler-exchange-httpd_config.h" #include "taler-exchange-httpd_keys.h" #include "taler-exchange-httpd_responses.h" #include "taler_exchangedb_plugin.h" @@ -43,24 +46,6 @@ /** - * Taler protocol version in the format CURRENT:REVISION:AGE - * as used by GNU libtool. See - * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html - * - * Please be very careful when updating and follow - * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info - * precisely. Note that this version has NOTHING to do with the - * release version, and the format is NOT the same that semantic - * versioning uses either. - * - * When changing this version, you likely want to also update - * #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in - * exchange_api_handle.c! - */ -#define EXCHANGE_PROTOCOL_VERSION "14:0:2" - - -/** * Information about a denomination on offer by the denomination helper. */ struct HelperDenomination @@ -403,6 +388,113 @@ struct SuspendedKeysRequests struct GNUNET_TIME_Absolute timeout; }; + +/** + * Information we track about wire fees. + */ +struct WireFeeSet +{ + + /** + * Kept in a DLL. + */ + struct WireFeeSet *next; + + /** + * Kept in a DLL. + */ + struct WireFeeSet *prev; + + /** + * Actual fees. + */ + struct TALER_WireFeeSet fees; + + /** + * Start date of fee validity (inclusive). + */ + struct GNUNET_TIME_Timestamp start_date; + + /** + * End date of fee validity (exclusive). + */ + struct GNUNET_TIME_Timestamp end_date; + + /** + * Wire method the fees apply to. + */ + char *method; +}; + + +/** + * State we keep per thread to cache the /wire response. + */ +struct WireStateHandle +{ + + /** + * JSON reply for /wire response. + */ + json_t *json_reply; + + /** + * ETag for this response (if any). + */ + char *etag; + + /** + * head of DLL of wire fees. + */ + struct WireFeeSet *wfs_head; + + /** + * Tail of DLL of wire fees. + */ + struct WireFeeSet *wfs_tail; + + /** + * Earliest timestamp of all the wire methods when we have no more fees. + */ + struct GNUNET_TIME_Absolute cache_expiration; + + /** + * @e cache_expiration time, formatted. + */ + char dat[128]; + + /** + * For which (global) wire_generation was this data structure created? + * Used to check when we are outdated and need to be re-generated. + */ + uint64_t wire_generation; + + /** + * Is the wire data ready? + */ + bool ready; + +}; + + +/** + * Stores the latest generation of our wire response. + */ +static struct WireStateHandle *wire_state; + +/** + * Handler listening for wire updates by other exchange + * services. + */ +static struct GNUNET_DB_EventHandler *wire_eh; + +/** + * Counter incremented whenever we have a reason to re-build the #wire_state + * because something external changed. + */ +static uint64_t wire_generation; + + /** * Stores the latest generation of our key state. */ @@ -455,6 +547,11 @@ static struct GNUNET_SCHEDULER_Task *keys_tt; static struct GNUNET_TIME_Relative signkey_legal_duration; /** + * What type of asset are we dealing with here? + */ +static char *asset_type; + +/** * RSA security module public key, all zero if not known. */ static struct TALER_SecurityModulePublicKeyP denom_rsa_sm_pub; @@ -476,6 +573,449 @@ static bool terminating; /** + * Free memory associated with @a wsh + * + * @param[in] wsh wire state to destroy + */ +static void +destroy_wire_state (struct WireStateHandle *wsh) +{ + struct WireFeeSet *wfs; + + while (NULL != (wfs = wsh->wfs_head)) + { + GNUNET_CONTAINER_DLL_remove (wsh->wfs_head, + wsh->wfs_tail, + wfs); + GNUNET_free (wfs->method); + GNUNET_free (wfs); + } + json_decref (wsh->json_reply); + GNUNET_free (wsh->etag); + GNUNET_free (wsh); +} + + +/** + * Function called whenever another exchange process has updated + * the wire data in the database. + * + * @param cls NULL + * @param extra unused + * @param extra_size number of bytes in @a extra unused + */ +static void +wire_update_event_cb (void *cls, + const void *extra, + size_t extra_size) +{ + (void) cls; + (void) extra; + (void) extra_size; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received /wire update event\n"); + TEH_check_invariants (); + wire_generation++; + key_generation++; + TEH_resume_keys_requests (false); +} + + +enum GNUNET_GenericReturnValue +TEH_wire_init () +{ + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), + }; + + wire_eh = TEH_plugin->event_listen (TEH_plugin->cls, + GNUNET_TIME_UNIT_FOREVER_REL, + &es, + &wire_update_event_cb, + NULL); + if (NULL == wire_eh) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +void +TEH_wire_done () +{ + if (NULL != wire_state) + { + destroy_wire_state (wire_state); + wire_state = NULL; + } + if (NULL != wire_eh) + { + TEH_plugin->event_listen_cancel (TEH_plugin->cls, + wire_eh); + wire_eh = NULL; + } +} + + +/** + * Add information about a wire account to @a cls. + * + * @param cls a `json_t *` object to expand with wire account details + * @param payto_uri the exchange bank account URI to add + * @param conversion_url URL of a conversion service, NULL if there is no conversion + * @param debit_restrictions JSON array with debit restrictions on the account + * @param credit_restrictions JSON array with credit restrictions on the account + * @param master_sig master key signature affirming that this is a bank + * account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS) + * @param bank_label label the wallet should use to display the account, can be NULL + * @param priority priority for ordering bank account labels + */ +static void +add_wire_account (void *cls, + const char *payto_uri, + const char *conversion_url, + const json_t *debit_restrictions, + const json_t *credit_restrictions, + const struct TALER_MasterSignatureP *master_sig, + const char *bank_label, + int64_t priority) +{ + json_t *a = cls; + + if (GNUNET_OK != + TALER_exchange_wire_signature_check ( + payto_uri, + conversion_url, + debit_restrictions, + credit_restrictions, + &TEH_master_public_key, + master_sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database has wire account with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); + return; + } + if (0 != + json_array_append_new ( + a, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("payto_uri", + payto_uri), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("conversion_url", + conversion_url)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("bank_label", + bank_label)), + GNUNET_JSON_pack_int64 ("priority", + priority), + GNUNET_JSON_pack_array_incref ("debit_restrictions", + (json_t *) debit_restrictions), + GNUNET_JSON_pack_array_incref ("credit_restrictions", + (json_t *) credit_restrictions), + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig)))) + { + GNUNET_break (0); /* out of memory!? */ + return; + } +} + + +/** + * Closure for #add_wire_fee(). + */ +struct AddContext +{ + /** + * Wire method the fees are for. + */ + char *wire_method; + + /** + * Wire state we are building. + */ + struct WireStateHandle *wsh; + + /** + * Array to append the fee to. + */ + json_t *a; + + /** + * Set to the maximum end-date seen. + */ + struct GNUNET_TIME_Absolute max_seen; +}; + + +/** + * Add information about a wire account to @a cls. + * + * @param cls a `struct AddContext` + * @param fees the wire fees we charge + * @param start_date from when are these fees valid (start date) + * @param end_date until when are these fees valid (end date, exclusive) + * @param master_sig master key signature affirming that this is the correct + * fee (of purpose #TALER_SIGNATURE_MASTER_WIRE_FEES) + */ +static void +add_wire_fee (void *cls, + const struct TALER_WireFeeSet *fees, + struct GNUNET_TIME_Timestamp start_date, + struct GNUNET_TIME_Timestamp end_date, + const struct TALER_MasterSignatureP *master_sig) +{ + struct AddContext *ac = cls; + struct WireFeeSet *wfs; + + if (GNUNET_OK != + TALER_exchange_offline_wire_fee_verify ( + ac->wire_method, + start_date, + end_date, + fees, + &TEH_master_public_key, + master_sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database has wire fee with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); + return; + } + ac->max_seen = GNUNET_TIME_absolute_max (ac->max_seen, + end_date.abs_time); + wfs = GNUNET_new (struct WireFeeSet); + wfs->start_date = start_date; + wfs->end_date = end_date; + wfs->fees = *fees; + wfs->method = GNUNET_strdup (ac->wire_method); + GNUNET_CONTAINER_DLL_insert (ac->wsh->wfs_head, + ac->wsh->wfs_tail, + wfs); + if (0 != + json_array_append_new ( + ac->a, + GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("wire_fee", + &fees->wire), + TALER_JSON_pack_amount ("closing_fee", + &fees->closing), + GNUNET_JSON_pack_timestamp ("start_date", + start_date), + GNUNET_JSON_pack_timestamp ("end_date", + end_date), + GNUNET_JSON_pack_data_auto ("sig", + master_sig)))) + { + GNUNET_break (0); /* out of memory!? */ + return; + } +} + + +/** + * Create the /wire response from our database state. + * + * @return NULL on error + */ +static struct WireStateHandle * +build_wire_state (void) +{ + json_t *wire_accounts_array; + json_t *wire_fee_object; + uint64_t wg = wire_generation; /* must be obtained FIRST */ + enum GNUNET_DB_QueryStatus qs; + struct WireStateHandle *wsh; + json_t *wads; + + wsh = GNUNET_new (struct WireStateHandle); + wsh->wire_generation = wg; + wire_accounts_array = json_array (); + GNUNET_assert (NULL != wire_accounts_array); + qs = TEH_plugin->get_wire_accounts (TEH_plugin->cls, + &add_wire_account, + wire_accounts_array); + if (0 > qs) + { + GNUNET_break (0); + json_decref (wire_accounts_array); + wsh->ready = false; + return wsh; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Build /wire data with %u accounts\n", + (unsigned int) json_array_size (wire_accounts_array)); + wire_fee_object = json_object (); + GNUNET_assert (NULL != wire_fee_object); + wsh->cache_expiration = GNUNET_TIME_UNIT_FOREVER_ABS; + { + json_t *account; + size_t index; + + json_array_foreach (wire_accounts_array, + index, + account) + { + char *wire_method; + const char *payto_uri = json_string_value (json_object_get (account, + "payto_uri")); + + GNUNET_assert (NULL != payto_uri); + wire_method = TALER_payto_get_method (payto_uri); + if (NULL == wire_method) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No wire method in `%s'\n", + payto_uri); + wsh->ready = false; + json_decref (wire_accounts_array); + json_decref (wire_fee_object); + return wsh; + } + if (NULL == json_object_get (wire_fee_object, + wire_method)) + { + struct AddContext ac = { + .wire_method = wire_method, + .wsh = wsh, + .a = json_array () + }; + + GNUNET_assert (NULL != ac.a); + qs = TEH_plugin->get_wire_fees (TEH_plugin->cls, + wire_method, + &add_wire_fee, + &ac); + if (0 > qs) + { + GNUNET_break (0); + json_decref (ac.a); + json_decref (wire_fee_object); + json_decref (wire_accounts_array); + GNUNET_free (wire_method); + wsh->ready = false; + return wsh; + } + if (0 != json_array_size (ac.a)) + { + wsh->cache_expiration + = GNUNET_TIME_absolute_min (ac.max_seen, + wsh->cache_expiration); + GNUNET_assert (0 == + json_object_set_new (wire_fee_object, + wire_method, + ac.a)); + } + else + { + json_decref (ac.a); + } + } + GNUNET_free (wire_method); + } + } + + wads = json_array (); /* #7271 */ + GNUNET_assert (NULL != wads); + wsh->json_reply = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("accounts", + wire_accounts_array), + GNUNET_JSON_pack_array_steal ("wads", + wads), + GNUNET_JSON_pack_object_steal ("fees", + wire_fee_object)); + wsh->ready = true; + return wsh; +} + + +void +TEH_wire_update_state (void) +{ + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_EXCHANGE_WIRE_UPDATED), + }; + + TEH_plugin->event_notify (TEH_plugin->cls, + &es, + NULL, + 0); + wire_generation++; + key_generation++; +} + + +/** + * Return the current key state for this thread. Possibly + * re-builds the key state if we have reason to believe + * that something changed. + * + * @return NULL on error + */ +struct WireStateHandle * +get_wire_state (void) +{ + struct WireStateHandle *old_wsh; + + old_wsh = wire_state; + if ( (NULL == old_wsh) || + (old_wsh->wire_generation < wire_generation) ) + { + struct WireStateHandle *wsh; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Rebuilding /wire, generation upgrade from %llu to %llu\n", + (unsigned long long) (NULL == old_wsh) ? 0LL : + old_wsh->wire_generation, + (unsigned long long) wire_generation); + TEH_check_invariants (); + wsh = build_wire_state (); + wire_state = wsh; + if (NULL != old_wsh) + destroy_wire_state (old_wsh); + TEH_check_invariants (); + return wsh; + } + return old_wsh; +} + + +const struct TALER_WireFeeSet * +TEH_wire_fees_by_time ( + struct GNUNET_TIME_Timestamp ts, + const char *method) +{ + struct WireStateHandle *wsh = get_wire_state (); + + for (struct WireFeeSet *wfs = wsh->wfs_head; + NULL != wfs; + wfs = wfs->next) + { + if (0 != strcmp (method, + wfs->method)) + continue; + if ( (GNUNET_TIME_timestamp_cmp (wfs->start_date, + >, + ts)) || + (GNUNET_TIME_timestamp_cmp (ts, + >=, + wfs->end_date)) ) + continue; + return &wfs->fees; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No wire fees for method `%s' at %s configured\n", + method, + GNUNET_TIME_timestamp2s (ts)); + return NULL; +} + + +/** * Function called to forcefully resume suspended keys requests. * * @param cls unused, NULL @@ -575,12 +1115,20 @@ check_dk (void *cls, (void) cls; (void) hc; - GNUNET_assert (TALER_DENOMINATION_INVALID != dk->denom_pub.cipher); - if (TALER_DENOMINATION_RSA == dk->denom_pub.cipher) + switch (dk->denom_pub.bsign_pub_key->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: GNUNET_assert (GNUNET_CRYPTO_rsa_public_key_check ( - dk->denom_pub.details.rsa_public_key)); - // nothing to do for TALER_DENOMINATION_CS - return GNUNET_OK; + dk->denom_pub.bsign_pub_key->details.rsa_public_key)); + return GNUNET_OK; + case GNUNET_CRYPTO_BSA_CS: + /* nothing to do for GNUNET_CRYPTO_BSA_CS */ + return GNUNET_OK; + } + GNUNET_assert (0); + return GNUNET_SYSERR; } @@ -748,7 +1296,7 @@ free_denom_cb (void *cls, * @param value the `struct HelperSignkey` to release * @return #GNUNET_OK (continue to iterate) */ -static int +static enum GNUNET_GenericReturnValue free_esign_cb (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) @@ -811,13 +1359,10 @@ destroy_key_helpers (struct HelperState *hs) * denomination. */ static struct TALER_AgeMask -load_age_mask (const char*section_name) +load_age_mask (const char *section_name) { static const struct TALER_AgeMask null_mask = {0}; - struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask (); - - if (age_mask.bits == 0) - return null_mask; + enum GNUNET_GenericReturnValue ret; if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( TEH_cfg, @@ -825,22 +1370,29 @@ load_age_mask (const char*section_name) "AGE_RESTRICTED"))) return null_mask; + if (GNUNET_SYSERR == + (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, + section_name, + "AGE_RESTRICTED"))) { - enum GNUNET_GenericReturnValue ret; + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section_name, + "AGE_RESTRICTED", + "Value must be YES or NO\n"); + return null_mask; + } - if (GNUNET_SYSERR == - (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, - section_name, - "AGE_RESTRICTED"))) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section_name, - "AGE_RESTRICTED", - "Value must be YES or NO\n"); - return null_mask; - } + if (GNUNET_OK == ret) + { + if (! TEH_age_restriction_enabled) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "age restriction set in section %s, yet, age restriction is not enabled\n", + section_name); + return TEH_age_restriction_config.mask; } - return age_mask; + + + return null_mask; } @@ -857,7 +1409,7 @@ load_age_mask (const char*section_name) * @param validity_duration how long does the key remain available for signing; * zero if the key has been revoked or purged * @param h_rsa hash of the @a denom_pub that is available (or was purged) - * @param denom_pub the public key itself, NULL if the key was revoked or purged + * @param bs_pub the public key itself, NULL if the key was revoked or purged * @param sm_pub public key of the security module, NULL if the key was revoked or purged * @param sm_sig signature from the security module, NULL if the key was revoked or purged * The signature was already verified against @a sm_pub. @@ -869,7 +1421,7 @@ helper_rsa_cb ( struct GNUNET_TIME_Timestamp start_time, struct GNUNET_TIME_Relative validity_duration, const struct TALER_RsaPubHashP *h_rsa, - const struct TALER_DenominationPublicKey *denom_pub, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, const struct TALER_SecurityModulePublicKeyP *sm_pub, const struct TALER_SecurityModuleSignatureP *sm_sig) { @@ -899,10 +1451,9 @@ helper_rsa_cb ( hd->validity_duration = validity_duration; hd->h_details.h_rsa = *h_rsa; hd->sm_sig = *sm_sig; - GNUNET_assert (TALER_DENOMINATION_RSA == denom_pub->cipher); - TALER_denom_pub_deep_copy (&hd->denom_pub, - denom_pub); - GNUNET_assert (TALER_DENOMINATION_RSA == hd->denom_pub.cipher); + GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == bs_pub->cipher); + hd->denom_pub.bsign_pub_key = + GNUNET_CRYPTO_bsign_pub_incref (bs_pub); /* load the age mask for the denomination, if applicable */ hd->denom_pub.age_mask = load_age_mask (section_name); TALER_denom_pub_hash (&hd->denom_pub, @@ -938,7 +1489,7 @@ helper_rsa_cb ( * @param validity_duration how long does the key remain available for signing; * zero if the key has been revoked or purged * @param h_cs hash of the @a denom_pub that is available (or was purged) - * @param denom_pub the public key itself, NULL if the key was revoked or purged + * @param bs_pub the public key itself, NULL if the key was revoked or purged * @param sm_pub public key of the security module, NULL if the key was revoked or purged * @param sm_sig signature from the security module, NULL if the key was revoked or purged * The signature was already verified against @a sm_pub. @@ -950,7 +1501,7 @@ helper_cs_cb ( struct GNUNET_TIME_Timestamp start_time, struct GNUNET_TIME_Relative validity_duration, const struct TALER_CsPubHashP *h_cs, - const struct TALER_DenominationPublicKey *denom_pub, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, const struct TALER_SecurityModulePublicKeyP *sm_pub, const struct TALER_SecurityModuleSignatureP *sm_sig) { @@ -980,9 +1531,9 @@ helper_cs_cb ( hd->validity_duration = validity_duration; hd->h_details.h_cs = *h_cs; hd->sm_sig = *sm_sig; - GNUNET_assert (TALER_DENOMINATION_CS == denom_pub->cipher); - TALER_denom_pub_deep_copy (&hd->denom_pub, - denom_pub); + GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher); + hd->denom_pub.bsign_pub_key + = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); /* load the age mask for the denomination, if applicable */ hd->denom_pub.age_mask = load_age_mask (section_name); TALER_denom_pub_hash (&hd->denom_pub, @@ -1088,6 +1639,7 @@ setup_key_helpers (struct HelperState *hs) = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_NO /* MUST BE NO! */); hs->rsadh = TALER_CRYPTO_helper_rsa_connect (TEH_cfg, + "taler-exchange", &helper_rsa_cb, hs); if (NULL == hs->rsadh) @@ -1096,6 +1648,7 @@ setup_key_helpers (struct HelperState *hs) return GNUNET_SYSERR; } hs->csdh = TALER_CRYPTO_helper_cs_connect (TEH_cfg, + "taler-exchange", &helper_cs_cb, hs); if (NULL == hs->csdh) @@ -1104,6 +1657,7 @@ setup_key_helpers (struct HelperState *hs) return GNUNET_SYSERR; } hs->esh = TALER_CRYPTO_helper_esign_connect (TEH_cfg, + "taler-exchange", &helper_esign_cb, hs); if (NULL == hs->esh) @@ -1273,6 +1827,17 @@ TEH_keys_init () "SIGNKEY_LEGAL_DURATION"); return GNUNET_SYSERR; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "exchange", + "ASSET_TYPE", + &asset_type)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "exchange", + "ASSET_TYPE"); + asset_type = GNUNET_strdup ("fiat"); + } keys_eh = TEH_plugin->event_listen (TEH_plugin->cls, GNUNET_TIME_UNIT_FOREVER_REL, &es, @@ -1333,7 +1898,25 @@ denomination_info_cb ( struct TEH_KeyStateHandle *ksh = cls; struct TEH_DenominationKey *dk; - GNUNET_assert (TALER_DENOMINATION_INVALID != denom_pub->cipher); + if (GNUNET_OK != + TALER_exchange_offline_denom_validity_verify ( + h_denom_pub, + meta->start, + meta->expire_withdraw, + meta->expire_deposit, + meta->expire_legal, + &meta->value, + &meta->fees, + &TEH_master_public_key, + master_sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database has denomination with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); + return; + } + + GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID != + denom_pub->bsign_pub_key->cipher); if (GNUNET_TIME_absolute_is_zero (meta->start.abs_time) || GNUNET_TIME_absolute_is_zero (meta->expire_withdraw.abs_time) || GNUNET_TIME_absolute_is_zero (meta->expire_deposit.abs_time) || @@ -1345,8 +1928,8 @@ denomination_info_cb ( return; } dk = GNUNET_new (struct TEH_DenominationKey); - TALER_denom_pub_deep_copy (&dk->denom_pub, - denom_pub); + TALER_denom_pub_copy (&dk->denom_pub, + denom_pub); dk->h_denom_pub = *h_denom_pub; dk->meta = *meta; dk->master_sig = *master_sig; @@ -1359,7 +1942,6 @@ denomination_info_cb ( &dk->h_denom_pub.hash, dk, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } @@ -1382,6 +1964,19 @@ signkey_info_cb ( struct SigningKey *sk; struct GNUNET_PeerIdentity pid; + if (GNUNET_OK != + TALER_exchange_offline_signkey_validity_verify ( + exchange_pub, + meta->start, + meta->expire_sign, + meta->expire_legal, + &TEH_master_public_key, + master_sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database has signing key with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); + return; + } sk = GNUNET_new (struct SigningKey); sk->exchange_pub = *exchange_pub; sk->meta = *meta; @@ -1663,14 +2258,14 @@ add_denom_key_cb (void *cls, /** * Add the headers we want to set for every /keys response. * - * @param ksh the key state to use + * @param cls the key state to use * @param[in,out] response the response to modify - * @return #GNUNET_OK on success */ -static enum GNUNET_GenericReturnValue -setup_general_response_headers (struct TEH_KeyStateHandle *ksh, +static void +setup_general_response_headers (void *cls, struct MHD_Response *response) { + struct TEH_KeyStateHandle *ksh = cls; char dat[128]; TALER_MHD_add_global_headers (response); @@ -1688,17 +2283,24 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh, { struct GNUNET_TIME_Relative r; struct GNUNET_TIME_Absolute a; + struct GNUNET_TIME_Timestamp km; struct GNUNET_TIME_Timestamp m; + struct GNUNET_TIME_Timestamp we; r = GNUNET_TIME_relative_min (TEH_max_keys_caching, ksh->rekey_frequency); a = GNUNET_TIME_relative_to_absolute (r); - m = GNUNET_TIME_absolute_to_timestamp (a); + km = GNUNET_TIME_absolute_to_timestamp (a); + we = GNUNET_TIME_absolute_to_timestamp (wire_state->cache_expiration); + m = GNUNET_TIME_timestamp_min (we, + km); TALER_MHD_get_date_string (m.abs_time, dat); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Setting /keys 'Expires' header to '%s'\n", - dat); + "Setting /keys 'Expires' header to '%s' (rekey frequency is %s)\n", + dat, + GNUNET_TIME_relative2s (ksh->rekey_frequency, + false)); GNUNET_break (MHD_YES == MHD_add_response_header (response, MHD_HTTP_HEADER_EXPIRES, @@ -1717,7 +2319,27 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh, MHD_add_response_header (response, MHD_HTTP_HEADER_CACHE_CONTROL, "public,max-age=3600")); - return GNUNET_OK; +} + + +/** + * Function called with wallet balance thresholds. + * + * @param[in,out] cls a `json **` where to put the array of json amounts discovered + * @param threshold another threshold amount to add + */ +static void +wallet_threshold_cb (void *cls, + const struct TALER_Amount *threshold) +{ + json_t **ret = cls; + + if (NULL == *ret) + *ret = json_array (); + GNUNET_assert (0 == + json_array_append_new (*ret, + TALER_JSON_from_amount ( + threshold))); } @@ -1727,44 +2349,45 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh, * * @param[in,out] ksh key state handle we build @a krd for * @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 + * @param last_cherry_pick_date timestamp to use + * @param[in,out] signkeys list of sign keys to return + * @param[in,out] recoup list of revoked keys to return + * @param[in,out] grouped_denominations list of grouped denominations to return * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue create_krd (struct TEH_KeyStateHandle *ksh, const struct GNUNET_HashCode *denom_keys_hash, - struct GNUNET_TIME_Timestamp last_cpd, + struct GNUNET_TIME_Timestamp last_cherry_pick_date, json_t *signkeys, json_t *recoup, - json_t *denoms, - json_t *grouped_denominations, - const struct GNUNET_HashCode *h_grouped) + json_t *grouped_denominations) { 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; + struct WireStateHandle *wsh; json_t *keys; - GNUNET_assert (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)); + wsh = get_wire_state (); + if (! wsh->ready) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_assert (! GNUNET_TIME_absolute_is_zero ( + last_cherry_pick_date.abs_time)); GNUNET_assert (NULL != signkeys); 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)); + GNUNET_TIME_timestamp2s (last_cherry_pick_date)); - /* Sign hash over denomination keys */ + /* Sign hash over master signatures of all denomination keys until this time + (in reverse order). */ { enum TALER_ErrorCode ec; @@ -1773,7 +2396,7 @@ create_krd (struct TEH_KeyStateHandle *ksh, TALER_exchange_online_key_set_sign ( &TEH_keys_exchange_sign2_, ksh, - last_cpd, + last_cherry_pick_date, denom_keys_hash, &exchange_pub, &exchange_sig))) @@ -1785,33 +2408,6 @@ 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; @@ -1822,11 +2418,33 @@ create_krd (struct TEH_KeyStateHandle *ksh, ksh->signature_expires); } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Build /keys data with %u wire accounts\n", + (unsigned int) json_array_size ( + json_object_get (wsh->json_reply, + "accounts"))); + keys = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("version", EXCHANGE_PROTOCOL_VERSION), + GNUNET_JSON_pack_string ("base_url", + TEH_base_url), GNUNET_JSON_pack_string ("currency", TEH_currency), + GNUNET_JSON_pack_object_steal ( + "currency_specification", + TALER_CONFIG_currency_specs_to_json (TEH_cspec)), + TALER_JSON_pack_amount ("stefan_abs", + &TEH_stefan_abs), + TALER_JSON_pack_amount ("stefan_log", + &TEH_stefan_log), + GNUNET_JSON_pack_double ("stefan_lin", + (double) TEH_stefan_lin), + GNUNET_JSON_pack_string ("asset_type", + asset_type), + GNUNET_JSON_pack_bool ("rewards_allowed", + GNUNET_YES == + TEH_enable_rewards), GNUNET_JSON_pack_data_auto ("master_public_key", &TEH_master_public_key), GNUNET_JSON_pack_time_rel ("reserve_closing_delay", @@ -1835,8 +2453,15 @@ create_krd (struct TEH_KeyStateHandle *ksh, signkeys), GNUNET_JSON_pack_array_incref ("recoup", recoup), - GNUNET_JSON_pack_array_incref ("denoms", - denoms), + GNUNET_JSON_pack_array_incref ("wads", + json_object_get (wsh->json_reply, + "wads")), + GNUNET_JSON_pack_array_incref ("accounts", + json_object_get (wsh->json_reply, + "accounts")), + GNUNET_JSON_pack_object_incref ("wire_fees", + json_object_get (wsh->json_reply, + "fees")), GNUNET_JSON_pack_array_incref ("denominations", grouped_denominations), GNUNET_JSON_pack_array_incref ("auditors", @@ -1844,27 +2469,28 @@ create_krd (struct TEH_KeyStateHandle *ksh, GNUNET_JSON_pack_array_incref ("global_fees", ksh->global_fees), GNUNET_JSON_pack_timestamp ("list_issue_date", - last_cpd), - GNUNET_JSON_pack_data_auto ("eddsa_pub", + last_cherry_pick_date), + GNUNET_JSON_pack_data_auto ("exchange_pub", &exchange_pub), - GNUNET_JSON_pack_data_auto ("eddsa_sig", - &exchange_sig), - GNUNET_JSON_pack_data_auto ("denominations_sig", - &grouped_exchange_sig)); + GNUNET_JSON_pack_data_auto ("exchange_sig", + &exchange_sig)); GNUNET_assert (NULL != keys); /* Set wallet limit if KYC is configured */ - if ( (TEH_KYC_NONE != TEH_kyc_config.mode) && - (GNUNET_OK == - TALER_amount_is_valid (&TEH_kyc_config.wallet_balance_limit)) ) { - GNUNET_assert ( - 0 == - json_object_set_new ( - keys, - "wallet_balance_limit_without_kyc", - TALER_JSON_from_amount ( - &TEH_kyc_config.wallet_balance_limit))); + json_t *wblwk = NULL; + + TALER_KYCLOGIC_kyc_iterate_thresholds ( + TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE, + &wallet_threshold_cb, + &wblwk); + if (NULL != wblwk) + GNUNET_assert ( + 0 == + json_object_set_new ( + keys, + "wallet_balance_limit_without_kyc", + wblwk)); } /* Signal support for the configured, enabled extensions. */ @@ -1872,42 +2498,31 @@ create_krd (struct TEH_KeyStateHandle *ksh, json_t *extensions = json_object (); bool has_extensions = false; + GNUNET_assert (NULL != extensions); /* Fill in the configurations of the enabled extensions */ - for (const struct TALER_Extension *extension = TALER_extensions_get_head (); - NULL != extension; - extension = extension->next) + for (const struct TALER_Extensions *iter = TALER_extensions_get_head (); + NULL != iter && NULL != iter->extension; + iter = iter->next) { - json_t *ext; - json_t *config_json; + const struct TALER_Extension *extension = iter->extension; + json_t *manifest; int r; - /* skip if not configured == disabled */ - if (NULL == extension->config || - NULL == extension->config_json) + /* skip if not enabled */ + if (! extension->enabled) continue; /* flag our findings so far */ has_extensions = true; - GNUNET_assert (NULL != extension->config_json); - config_json = json_copy (extension->config_json); - GNUNET_assert (NULL != config_json); - - ext = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_bool ("critical", - extension->critical), - GNUNET_JSON_pack_string ("version", - extension->version), - GNUNET_JSON_pack_object_steal ("config", - config_json) - ); - GNUNET_assert (NULL != ext); + manifest = extension->manifest (extension); + GNUNET_assert (manifest); r = json_object_set_new ( extensions, extension->name, - ext); + manifest); GNUNET_assert (0 == r); } @@ -1917,18 +2532,22 @@ create_krd (struct TEH_KeyStateHandle *ksh, json_t *sig; int r; - r = json_object_set ( + r = json_object_set_new ( keys, "extensions", extensions); GNUNET_assert (0 == r); - sig = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("extensions_sig", - &TEH_extensions_sig)); + /* Add the signature of the extensions, if it is not zero */ + if (TEH_extensions_signed) + { + sig = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("extensions_sig", + &TEH_extensions_sig)); - r = json_object_update (keys, sig); - GNUNET_assert (0 == r); + r = json_object_update (keys, sig); + GNUNET_assert (0 == r); + } } else { @@ -1975,9 +2594,8 @@ create_krd (struct TEH_KeyStateHandle *ksh, keys_json, MHD_RESPMEM_MUST_FREE); GNUNET_assert (NULL != krd.response_uncompressed); - GNUNET_assert (GNUNET_OK == - setup_general_response_headers (ksh, - krd.response_uncompressed)); + setup_general_response_headers (ksh, + krd.response_uncompressed); GNUNET_break (MHD_YES == MHD_add_response_header (krd.response_uncompressed, MHD_HTTP_HEADER_ETAG, @@ -1997,16 +2615,25 @@ create_krd (struct TEH_KeyStateHandle *ksh, MHD_add_response_header (krd.response_compressed, MHD_HTTP_HEADER_CONTENT_ENCODING, "deflate")) ); - GNUNET_assert (GNUNET_OK == - setup_general_response_headers (ksh, - krd.response_compressed)); + setup_general_response_headers (ksh, + krd.response_compressed); + /* Set cache control headers: our response varies depending on these headers */ + GNUNET_break (MHD_YES == + MHD_add_response_header (krd.response_compressed, + MHD_HTTP_HEADER_VARY, + MHD_HTTP_HEADER_ACCEPT_ENCODING)); + /* Information is always public, revalidate after 1 day */ + GNUNET_break (MHD_YES == + MHD_add_response_header (krd.response_compressed, + MHD_HTTP_HEADER_CACHE_CONTROL, + "public,max-age=86400")); GNUNET_break (MHD_YES == MHD_add_response_header (krd.response_compressed, MHD_HTTP_HEADER_ETAG, etag)); krd.etag = GNUNET_strdup (etag); } - krd.cherry_pick_date = last_cpd; + krd.cherry_pick_date = last_cherry_pick_date; GNUNET_array_append (ksh->krd_array, ksh->krd_array_length, krd); @@ -2015,6 +2642,194 @@ create_krd (struct TEH_KeyStateHandle *ksh, /** + * Element in the `struct SignatureContext` array. + */ +struct SignatureElement +{ + + /** + * Offset of the denomination in the group array, + * for sorting (2nd rank, ascending). + */ + unsigned int offset; + + /** + * Offset of the group in the denominations array, + * for sorting (2nd rank, ascending). + */ + unsigned int group_offset; + + /** + * Pointer to actual master signature to hash over. + */ + struct TALER_MasterSignatureP master_sig; +}; + +/** + * Context for collecting the array of master signatures + * needed to verify the exchange_sig online signature. + */ +struct SignatureContext +{ + /** + * Array of signatures to hash over. + */ + struct SignatureElement *elements; + + /** + * Write offset in the @e elements array. + */ + unsigned int elements_pos; + + /** + * Allocated space for @e elements. + */ + unsigned int elements_size; +}; + + +/** + * Determine order to sort two elements by before + * we hash the master signatures. Used for + * sorting with qsort(). + * + * @param a pointer to a `struct SignatureElement` + * @param b pointer to a `struct SignatureElement` + * @return 0 if equal, -1 if a < b, 1 if a > b. + */ +static int +signature_context_sort_cb (const void *a, + const void *b) +{ + const struct SignatureElement *sa = a; + const struct SignatureElement *sb = b; + + if (sa->group_offset < sb->group_offset) + return -1; + if (sa->group_offset > sb->group_offset) + return 1; + if (sa->offset < sb->offset) + return -1; + if (sa->offset > sb->offset) + return 1; + /* We should never have two disjoint elements + with same time and offset */ + GNUNET_assert (sa == sb); + return 0; +} + + +/** + * Append a @a master_sig to the @a sig_ctx using the + * given attributes for (later) sorting. + * + * @param[in,out] sig_ctx signature context to update + * @param group_offset offset for the group + * @param offset offset for the entry + * @param master_sig master signature for the entry + */ +static void +append_signature (struct SignatureContext *sig_ctx, + unsigned int group_offset, + unsigned int offset, + const struct TALER_MasterSignatureP *master_sig) +{ + struct SignatureElement *element; + unsigned int new_size; + + if (sig_ctx->elements_pos == sig_ctx->elements_size) + { + if (0 == sig_ctx->elements_size) + new_size = 1024; + else + new_size = sig_ctx->elements_size * 2; + GNUNET_array_grow (sig_ctx->elements, + sig_ctx->elements_size, + new_size); + } + element = &sig_ctx->elements[sig_ctx->elements_pos++]; + element->offset = offset; + element->group_offset = group_offset; + element->master_sig = *master_sig; +} + + +/** + *GroupData is the value we store for each group meta-data */ +struct GroupData +{ + /** + * The json blob with the group meta-data and list of denominations + */ + json_t *json; + + /** + * List of denominations for the group, + * included in @e json, do not free separately! + */ + json_t *list; + + /** + * Offset of the group in the final array. + */ + unsigned int group_off; + +}; + + +/** + * Helper function called to clean up the group data + * in the denominations_by_group below. + * + * @param cls unused + * @param key unused + * @param value a `struct GroupData` to free + * @return #GNUNET_OK + */ +static int +free_group (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GroupData *gd = value; + + (void) cls; + (void) key; + GNUNET_free (gd); + return GNUNET_OK; +} + + +static void +compute_msig_hash (struct SignatureContext *sig_ctx, + struct GNUNET_HashCode *hc) +{ + struct GNUNET_HashContext *hash_context; + + hash_context = GNUNET_CRYPTO_hash_context_start (); + qsort (sig_ctx->elements, + sig_ctx->elements_pos, + sizeof (struct SignatureElement), + &signature_context_sort_cb); + for (unsigned int i = 0; i<sig_ctx->elements_pos; i++) + { + struct SignatureElement *element = &sig_ctx->elements[i]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding %u,%u,%s\n", + element->group_offset, + element->offset, + TALER_B2S (&element->master_sig)); + GNUNET_CRYPTO_hash_context_read (hash_context, + &element->master_sig, + sizeof (element->master_sig)); + } + GNUNET_CRYPTO_hash_context_finish (hash_context, + hc); +} + + +/** * Update the "/keys" responses in @a ksh, computing the detailed replies. * * This function is to recompute all (including cherry-picked) responses we @@ -2026,23 +2841,50 @@ create_krd (struct TEH_KeyStateHandle *ksh, static enum GNUNET_GenericReturnValue finish_keys_response (struct TEH_KeyStateHandle *ksh) { + enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; json_t *recoup; - struct SignKeyCtx sctx; - json_t *denoms = NULL; + struct SignKeyCtx sctx = { + .min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL + }; json_t *grouped_denominations = NULL; - struct GNUNET_TIME_Timestamp last_cpd; + struct GNUNET_TIME_Timestamp last_cherry_pick_date; struct GNUNET_CONTAINER_Heap *heap; - struct GNUNET_HashContext *hash_context = NULL; - struct GNUNET_HashCode grouped_hash_xor = {0}; + struct SignatureContext sig_ctx = { 0 }; + /* Remember if we have any denomination with age restriction */ + bool has_age_restricted_denomination = false; + struct WireStateHandle *wsh; + wsh = get_wire_state (); + if (! wsh->ready) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (0 == + json_array_size (json_object_get (wsh->json_reply, + "accounts")) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No wire accounts available. Refusing to generate /keys response.\n"); + return GNUNET_NO; + } sctx.signkeys = json_array (); GNUNET_assert (NULL != sctx.signkeys); - sctx.min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL; + recoup = json_array (); + GNUNET_assert (NULL != recoup); + grouped_denominations = json_array (); + GNUNET_assert (NULL != grouped_denominations); + GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, &add_sign_key_cb, &sctx); - recoup = json_array (); - GNUNET_assert (NULL != recoup); + if (0 == json_array_size (sctx.signkeys)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No online signing keys available. Refusing to generate /keys response.\n"); + ret = GNUNET_NO; + goto CLEANUP; + } heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX); { struct DenomKeyCtx dkc = { @@ -2059,121 +2901,52 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) sctx.min_sk_frequency); } - denoms = json_array (); - GNUNET_assert (NULL != denoms); - hash_context = GNUNET_CRYPTO_hash_context_start (); - - grouped_denominations = json_array (); - GNUNET_assert (NULL != grouped_denominations); - - last_cpd = GNUNET_TIME_UNIT_ZERO_TS; + last_cherry_pick_date = GNUNET_TIME_UNIT_ZERO_TS; - // FIXME: This block contains the implementation of the DEPRECATED - // "denom_pubs" array along with the new grouped "denominations". - // "denom_pubs" Will be removed sooner or later. { struct TEH_DenominationKey *dk; struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group; - /* groupData is the value we store for each group meta-data */ - struct groupData - { - /** - * 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; - }; denominations_by_group = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO /* NO, because keys are only on the stack */); - - - /* heap = min heap, sorted by start time */ + /* heap = max heap, sorted by start time */ while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) { - if (GNUNET_TIME_timestamp_cmp (last_cpd, + if (GNUNET_TIME_timestamp_cmp (last_cherry_pick_date, !=, dk->meta.start) && - (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)) ) + (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) ) { /* - * This is not the first entry in the heap (because last_cpd != + * This is not the first entry in the heap (because last_cherry_pick_date != * GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different * start time. Therefore, we create a new entry in ksh. */ struct GNUNET_HashCode hc; - GNUNET_CRYPTO_hash_context_finish ( - GNUNET_CRYPTO_hash_context_copy (hash_context), - &hc); - + compute_msig_hash (&sig_ctx, + &hc); if (GNUNET_OK != create_krd (ksh, &hc, - last_cpd, + last_cherry_pick_date, sctx.signkeys, recoup, - denoms, - grouped_denominations, - &grouped_hash_xor)) + grouped_denominations)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to generate key response data for %s\n", - GNUNET_TIME_timestamp2s (last_cpd)); - GNUNET_CRYPTO_hash_context_abort (hash_context); + GNUNET_TIME_timestamp2s (last_cherry_pick_date)); /* drain heap before destroying it */ while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) /* intentionally empty */; GNUNET_CONTAINER_heap_destroy (heap); - json_decref (denoms); - json_decref (grouped_denominations); - json_decref (sctx.signkeys); - json_decref (recoup); - return GNUNET_SYSERR; + goto CLEANUP; } } - last_cpd = dk->meta.start; - - { - json_t *denom; - - denom = - 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), - TALER_JSON_pack_denom_pub ("denom_pub", - &dk->denom_pub), - TALER_JSON_pack_amount ("value", - &dk->meta.value), - TALER_JSON_PACK_DENOM_FEES ("fee", - &dk->meta.fees)); - - GNUNET_CRYPTO_hash_context_read (hash_context, - &dk->h_denom_pub, - sizeof (struct GNUNET_HashCode)); - - GNUNET_assert ( - 0 == - json_array_append_new ( - denoms, - denom)); - - } - + last_cherry_pick_date = dk->meta.start; /* * Group the denominations by {cipher, value, fees, age_mask}. * @@ -2182,69 +2955,70 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) * denominations_by_group. */ { - static const char *denoms_key = "denoms"; - struct groupData *group; - json_t *list; + struct GroupData *group; json_t *entry; struct GNUNET_HashCode key; struct TALER_DenominationGroup meta = { - .cipher = dk->denom_pub.cipher, + .cipher = dk->denom_pub.bsign_pub_key->cipher, .value = dk->meta.value, .fees = dk->meta.fees, .age_mask = dk->meta.age_mask, }; - memset (&meta.hash, 0, sizeof(meta.hash)); - /* Search the group/JSON-blob for the key */ - GNUNET_CRYPTO_hash (&meta, sizeof(meta), &key); - - group = - (struct groupData *) GNUNET_CONTAINER_multihashmap_get ( - denominations_by_group, - &key); - + TALER_denomination_group_get_key (&meta, + &key); + group = GNUNET_CONTAINER_multihashmap_get ( + denominations_by_group, + &key); if (NULL == group) { /* There is no group for this meta-data yet, so we create a new group */ bool age_restricted = meta.age_mask.bits != 0; - char *cipher; - - group = GNUNET_new (struct groupData); - memset (group, 0, sizeof(*group)); + const char *cipher; + group = GNUNET_new (struct GroupData); switch (meta.cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: cipher = age_restricted ? "RSA+age_restricted" : "RSA"; break; - case TALER_DENOMINATION_CS: + case GNUNET_CRYPTO_BSA_CS: cipher = age_restricted ? "CS+age_restricted" : "CS"; break; default: GNUNET_assert (false); } - + /* Create a new array for the denominations in this group */ + group->list = json_array (); + GNUNET_assert (NULL != group->list); 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_JSON_pack_string ("cipher", + cipher), + GNUNET_JSON_pack_array_steal ("denoms", + group->list), + TALER_JSON_PACK_DENOM_FEES ("fee", + &meta.fees), + TALER_JSON_pack_amount ("value", + &meta.value)); GNUNET_assert (NULL != group->json); - if (age_restricted) { - int r = json_object_set (group->json, - "age_mask", - json_integer (meta.age_mask.bits)); - GNUNET_assert (0 == r); + GNUNET_assert ( + 0 == + json_object_set_new (group->json, + "age_mask", + json_integer ( + meta.age_mask.bits))); + /* Remember that we have found at least _one_ age restricted denomination */ + has_age_restricted_denomination = true; } - - /* Create a new array for the denominations in this group */ - list = json_array (); - GNUNET_assert (NULL != list); + group->group_off + = json_array_size (grouped_denominations); GNUNET_assert (0 == - json_object_set (group->json, denoms_key, list)); - + json_array_append_new ( + grouped_denominations, + group->json)); GNUNET_assert ( GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (denominations_by_group, @@ -2256,23 +3030,32 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) /* Now that we have found/created the right group, add the denomination to the list */ { + struct HelperDenomination *hd; struct GNUNET_JSON_PackSpec key_spec; - + bool private_key_lost; + + hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + &dk->h_denom_pub.hash); + private_key_lost + = (NULL == hd) || + GNUNET_TIME_absolute_is_past ( + GNUNET_TIME_absolute_add ( + hd->start_time.abs_time, + hd->validity_duration)); switch (meta.cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: key_spec = - GNUNET_JSON_pack_rsa_public_key ("rsa_pub", - dk->denom_pub.details. - rsa_public_key); + GNUNET_JSON_pack_rsa_public_key ( + "rsa_pub", + dk->denom_pub.bsign_pub_key->details.rsa_public_key); break; - case TALER_DENOMINATION_CS: + case GNUNET_CRYPTO_BSA_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)); + GNUNET_JSON_pack_data_varsize ( + "cs_pub", + &dk->denom_pub.bsign_pub_key->details.cs_public_key, + sizeof (dk->denom_pub.bsign_pub_key->details.cs_public_key)); break; default: GNUNET_assert (false); @@ -2281,6 +3064,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) entry = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("master_sig", &dk->master_sig), + GNUNET_JSON_pack_allow_null ( + private_key_lost + ? GNUNET_JSON_pack_bool ("lost", + true) + : GNUNET_JSON_pack_string ("dummy", + NULL)), GNUNET_JSON_pack_timestamp ("stamp_start", dk->meta.start), GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw", @@ -2294,104 +3083,80 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_assert (NULL != entry); } - /* 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); - + /* Build up the running hash of all master signatures of the + denominations */ + append_signature (&sig_ctx, + group->group_off, + (unsigned int) json_array_size (group->list), + &dk->master_sig); /* 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 (json_is_array (group->list)); GNUNET_assert (0 == - json_array_append_new (list, entry)); + json_array_append_new (group->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 groupData *group = NULL; - - iter = - GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group); - - while (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_iterator_next (iter, - NULL, - (const - void **) &group)) - { - /* Add the XOR over all hashes of denominations in this group to the group */ - GNUNET_assert (0 == - json_object_set ( - group->json, - "hash", - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto (NULL, - &group->hash_xor)))); - - /* Add this group to the array */ - GNUNET_assert (0 == - json_array_append_new ( - grouped_denominations, - group->json)); - - /* Build the running XOR over all hash(_xor) */ - GNUNET_CRYPTO_hash_xor (&group->hash_xor, - &grouped_hash_xor, - &grouped_hash_xor); - - GNUNET_free (group); - } - - GNUNET_CONTAINER_multihashmap_iterator_destroy (iter); - GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group); - - } + GNUNET_CONTAINER_multihashmap_iterate (denominations_by_group, + &free_group, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group); } - GNUNET_CONTAINER_heap_destroy (heap); - if (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)) + + if (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) { struct GNUNET_HashCode hc; - GNUNET_CRYPTO_hash_context_finish (hash_context, - &hc); + compute_msig_hash (&sig_ctx, + &hc); if (GNUNET_OK != create_krd (ksh, &hc, - last_cpd, + last_cherry_pick_date, sctx.signkeys, recoup, - denoms, - grouped_denominations, - &grouped_hash_xor)) + 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); - json_decref (sctx.signkeys); - json_decref (recoup); - return GNUNET_SYSERR; + GNUNET_TIME_timestamp2s (last_cherry_pick_date)); + goto CLEANUP; } ksh->management_only = false; + + /* Sanity check: Make sure that age restriction is enabled IFF at least + * one age restricted denomination exist */ + if (! has_age_restricted_denomination && TEH_age_restriction_enabled) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Age restriction is enabled, but NO denominations with age restriction found!\n"); + goto CLEANUP; + } + else if (has_age_restricted_denomination && ! TEH_age_restriction_enabled) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Age restriction is NOT enabled, but denominations with age restriction found!\n"); + goto CLEANUP; + } } else { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No denomination keys available. Refusing to generate /keys response.\n"); - GNUNET_CRYPTO_hash_context_abort (hash_context); } - json_decref (sctx.signkeys); + ret = GNUNET_OK; + +CLEANUP: + GNUNET_array_grow (sig_ctx.elements, + sig_ctx.elements_size, + 0); + json_decref (grouped_denominations); + if (NULL != sctx.signkeys) + json_decref (sctx.signkeys); json_decref (recoup); - json_decref (denoms); - return GNUNET_OK; + return ret; } @@ -2401,7 +3166,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) * @param cls `struct TEH_KeyStateHandle *` we are building * @param fees the global fees we charge * @param purse_timeout when do purses time out - * @param kyc_timeout when do reserves without KYC time out * @param history_expiration how long are account histories preserved * @param purse_account_limit how many purses are free per account * @param start_date from when are these fees valid (start date) @@ -2414,7 +3178,6 @@ global_fee_info_cb ( void *cls, const struct TALER_GlobalFeeSet *fees, struct GNUNET_TIME_Relative purse_timeout, - struct GNUNET_TIME_Relative kyc_timeout, struct GNUNET_TIME_Relative history_expiration, uint32_t purse_account_limit, struct GNUNET_TIME_Timestamp start_date, @@ -2424,6 +3187,21 @@ global_fee_info_cb ( struct TEH_KeyStateHandle *ksh = cls; struct TEH_GlobalFee *gf; + if (GNUNET_OK != + TALER_exchange_offline_global_fee_verify ( + start_date, + end_date, + fees, + purse_timeout, + history_expiration, + purse_account_limit, + &TEH_master_public_key, + master_sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database has global fee with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); + return; + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Found global fees with %u purses\n", purse_account_limit); @@ -2432,7 +3210,6 @@ global_fee_info_cb ( gf->end_date = end_date; gf->fees = *fees; gf->purse_timeout = purse_timeout; - gf->kyc_timeout = kyc_timeout; gf->history_expiration = history_expiration; gf->purse_account_limit = purse_account_limit; gf->master_sig = *master_sig; @@ -2451,8 +3228,6 @@ global_fee_info_cb ( TALER_JSON_PACK_GLOBAL_FEES (fees), GNUNET_JSON_pack_time_rel ("history_expiration", history_expiration), - GNUNET_JSON_pack_time_rel ("account_kyc_timeout", - kyc_timeout), GNUNET_JSON_pack_time_rel ("purse_timeout", purse_timeout), GNUNET_JSON_pack_uint64 ("purse_account_limit", @@ -2499,9 +3274,9 @@ build_key_state (struct HelperState *hs, ksh->helpers = hs; } ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); + true); ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32, - GNUNET_NO /* MUST be NO! */); + false /* MUST be false! */); ksh->auditors = json_array (); GNUNET_assert (NULL != ksh->auditors); /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ @@ -2577,7 +3352,7 @@ build_key_state (struct HelperState *hs, finish_keys_response (ksh)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Could not finish /keys response (likely no signing keys available yet)\n"); + "Could not finish /keys response (required data not configured yet)\n"); destroy_key_state (ksh, true); return NULL; @@ -2604,8 +3379,8 @@ TEH_keys_update_states () } -struct TEH_KeyStateHandle * -TEH_keys_get_state2 (bool management_only) +static struct TEH_KeyStateHandle * +keys_get_state (bool management_only) { struct TEH_KeyStateHandle *old_ksh; struct TEH_KeyStateHandle *ksh; @@ -2623,7 +3398,7 @@ TEH_keys_get_state2 (bool management_only) if ( (old_ksh->key_generation < key_generation) || (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires.abs_time)) ) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Rebuilding /keys, generation upgrade from %llu to %llu\n", (unsigned long long) old_ksh->key_generation, (unsigned long long) key_generation); @@ -2641,19 +3416,28 @@ TEH_keys_get_state2 (bool management_only) struct TEH_KeyStateHandle * +TEH_keys_get_state_for_management_only (void) +{ + return keys_get_state (true); +} + + +struct TEH_KeyStateHandle * TEH_keys_get_state (void) { struct TEH_KeyStateHandle *ksh; - ksh = TEH_keys_get_state2 (false); + ksh = keys_get_state (false); if (NULL == ksh) return NULL; + if (ksh->management_only) { if (GNUNET_OK != finish_keys_response (ksh)) return NULL; } + return ksh; } @@ -2696,16 +3480,17 @@ TEH_keys_denomination_by_hash ( NULL); return NULL; } - return TEH_keys_denomination_by_hash2 (ksh, - h_denom_pub, - conn, - mret); + + return TEH_keys_denomination_by_hash_from_state (ksh, + h_denom_pub, + conn, + mret); } struct TEH_DenominationKey * -TEH_keys_denomination_by_hash2 ( - struct TEH_KeyStateHandle *ksh, +TEH_keys_denomination_by_hash_from_state ( + const struct TEH_KeyStateHandle *ksh, const struct TALER_DenominationHashP *h_denom_pub, struct MHD_Connection *conn, MHD_RESULT *mret) @@ -2727,105 +3512,137 @@ TEH_keys_denomination_by_hash2 ( enum TALER_ErrorCode -TEH_keys_denomination_sign_withdraw ( - const struct TALER_DenominationHashP *h_denom_pub, - const struct TALER_BlindedPlanchet *bp, - struct TALER_BlindedDenominationSignature *bs) +TEH_keys_denomination_batch_sign ( + unsigned int csds_length, + const struct TEH_CoinSignData csds[static csds_length], + bool for_melt, + struct TALER_BlindedDenominationSignature bss[static csds_length]) { struct TEH_KeyStateHandle *ksh; struct HelperDenomination *hd; + struct TALER_CRYPTO_RsaSignRequest rsrs[csds_length]; + struct TALER_CRYPTO_CsSignRequest csrs[csds_length]; + struct TALER_BlindedDenominationSignature rs[csds_length]; + struct TALER_BlindedDenominationSignature cs[csds_length]; + unsigned int rsrs_pos = 0; + unsigned int csrs_pos = 0; + enum TALER_ErrorCode ec; ksh = TEH_keys_get_state (); if (NULL == ksh) return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, - &h_denom_pub->hash); - if (NULL == hd) - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; - if (bp->cipher != hd->denom_pub.cipher) - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - switch (hd->denom_pub.cipher) + for (unsigned int i = 0; i<csds_length; i++) { - case TALER_DENOMINATION_RSA: - TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_RSA]++; + const struct TALER_DenominationHashP *h_denom_pub = csds[i].h_denom_pub; + const struct TALER_BlindedPlanchet *bp = csds[i].bp; + + hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + &h_denom_pub->hash); + if (NULL == hd) + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + if (bp->blinded_message->cipher != + hd->denom_pub.bsign_pub_key->cipher) + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + switch (hd->denom_pub.bsign_pub_key->cipher) { - struct TALER_CRYPTO_RsaSignRequest rsr = { - .h_rsa = &hd->h_details.h_rsa, - .msg = bp->details.rsa_blinded_planchet.blinded_msg, - .msg_size = bp->details.rsa_blinded_planchet.blinded_msg_size - }; - - return TALER_CRYPTO_helper_rsa_sign ( - ksh->helpers->rsadh, - &rsr, - bs); + case GNUNET_CRYPTO_BSA_RSA: + rsrs[rsrs_pos].h_rsa = &hd->h_details.h_rsa; + rsrs[rsrs_pos].msg + = bp->blinded_message->details.rsa_blinded_message.blinded_msg; + rsrs[rsrs_pos].msg_size + = bp->blinded_message->details.rsa_blinded_message.blinded_msg_size; + rsrs_pos++; + break; + case GNUNET_CRYPTO_BSA_CS: + csrs[csrs_pos].h_cs = &hd->h_details.h_cs; + csrs[csrs_pos].blinded_planchet + = &bp->blinded_message->details.cs_blinded_message; + csrs_pos++; + break; + default: + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; } - case TALER_DENOMINATION_CS: - TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_CS]++; - return TALER_CRYPTO_helper_cs_sign_withdraw ( - ksh->helpers->csdh, - &hd->h_details.h_cs, - &bp->details.cs_blinded_planchet, - bs); - default: - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; } -} - -enum TALER_ErrorCode -TEH_keys_denomination_sign_melt ( - const struct TALER_DenominationHashP *h_denom_pub, - const struct TALER_BlindedPlanchet *bp, - struct TALER_BlindedDenominationSignature *bs) -{ - struct TEH_KeyStateHandle *ksh; - struct HelperDenomination *hd; + if ( (0 != csrs_pos) && + (0 != rsrs_pos) ) + { + memset (rs, + 0, + sizeof (rs)); + memset (cs, + 0, + sizeof (cs)); + } + ec = TALER_EC_NONE; + if (0 != csrs_pos) + { + ec = TALER_CRYPTO_helper_cs_batch_sign ( + ksh->helpers->csdh, + csrs_pos, + csrs, + for_melt, + (0 == rsrs_pos) ? bss : cs); + if (TALER_EC_NONE != ec) + { + for (unsigned int i = 0; i<csrs_pos; i++) + TALER_blinded_denom_sig_free (&cs[i]); + return ec; + } + TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_CS] += csrs_pos; + } + if (0 != rsrs_pos) + { + ec = TALER_CRYPTO_helper_rsa_batch_sign ( + ksh->helpers->rsadh, + rsrs_pos, + rsrs, + (0 == csrs_pos) ? bss : rs); + if (TALER_EC_NONE != ec) + { + for (unsigned int i = 0; i<csrs_pos; i++) + TALER_blinded_denom_sig_free (&cs[i]); + for (unsigned int i = 0; i<rsrs_pos; i++) + TALER_blinded_denom_sig_free (&rs[i]); + return ec; + } + TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_RSA] += rsrs_pos; + } - ksh = TEH_keys_get_state (); - if (NULL == ksh) - return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, - &h_denom_pub->hash); - if (NULL == hd) - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; - if (bp->cipher != hd->denom_pub.cipher) - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - switch (hd->denom_pub.cipher) + if ( (0 != csrs_pos) && + (0 != rsrs_pos) ) { - case TALER_DENOMINATION_RSA: - TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_RSA]++; + rsrs_pos = 0; + csrs_pos = 0; + for (unsigned int i = 0; i<csds_length; i++) { - struct TALER_CRYPTO_RsaSignRequest rsr = { - .h_rsa = &hd->h_details.h_rsa, - .msg = bp->details.rsa_blinded_planchet.blinded_msg, - .msg_size = bp->details.rsa_blinded_planchet.blinded_msg_size - }; - - return TALER_CRYPTO_helper_rsa_sign ( - ksh->helpers->rsadh, - &rsr, - bs); + const struct TALER_BlindedPlanchet *bp = csds[i].bp; + + switch (bp->blinded_message->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + bss[i] = rs[rsrs_pos++]; + break; + case GNUNET_CRYPTO_BSA_CS: + bss[i] = cs[csrs_pos++]; + break; + default: + GNUNET_assert (0); + } } - case TALER_DENOMINATION_CS: - TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_CS]++; - return TALER_CRYPTO_helper_cs_sign_melt ( - ksh->helpers->csdh, - &hd->h_details.h_cs, - &bp->details.cs_blinded_planchet, - bs); - default: - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; } + return TALER_EC_NONE; } enum TALER_ErrorCode -TEH_keys_denomination_cs_r_pub_melt ( - const struct TALER_DenominationHashP *h_denom_pub, - const struct TALER_CsNonce *nonce, - struct TALER_DenominationCSPublicRPairP *r_pub) +TEH_keys_denomination_cs_r_pub ( + const struct TEH_CsDeriveData *cdd, + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP *r_pub) { + const struct TALER_DenominationHashP *h_denom_pub = cdd->h_denom_pub; + const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdd->nonce; struct TEH_KeyStateHandle *ksh; struct HelperDenomination *hd; @@ -2840,47 +3657,66 @@ TEH_keys_denomination_cs_r_pub_melt ( { return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; } - if (TALER_DENOMINATION_CS != hd->denom_pub.cipher) + if (GNUNET_CRYPTO_BSA_CS != + hd->denom_pub.bsign_pub_key->cipher) { return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; } - return TALER_CRYPTO_helper_cs_r_derive_melt (ksh->helpers->csdh, - &hd->h_details.h_cs, - nonce, - r_pub); + { + struct TALER_CRYPTO_CsDeriveRequest cdr = { + .h_cs = &hd->h_details.h_cs, + .nonce = nonce + }; + return TALER_CRYPTO_helper_cs_r_derive (ksh->helpers->csdh, + &cdr, + for_melt, + r_pub); + } } enum TALER_ErrorCode -TEH_keys_denomination_cs_r_pub_withdraw ( - const struct TALER_DenominationHashP *h_denom_pub, - const struct TALER_CsNonce *nonce, - struct TALER_DenominationCSPublicRPairP *r_pub) +TEH_keys_denomination_cs_batch_r_pub ( + unsigned int cdds_length, + const struct TEH_CsDeriveData cdds[static cdds_length], + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static cdds_length]) { struct TEH_KeyStateHandle *ksh; struct HelperDenomination *hd; + struct TALER_CRYPTO_CsDeriveRequest cdrs[cdds_length]; ksh = TEH_keys_get_state (); if (NULL == ksh) { return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; } - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, - &h_denom_pub->hash); - if (NULL == hd) + for (unsigned int i = 0; i<cdds_length; i++) { - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; - } - if (TALER_DENOMINATION_CS != hd->denom_pub.cipher) - { - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + const struct TALER_DenominationHashP *h_denom_pub = cdds[i].h_denom_pub; + const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdds[i].nonce; + + hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + &h_denom_pub->hash); + if (NULL == hd) + { + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + } + if (GNUNET_CRYPTO_BSA_CS != + hd->denom_pub.bsign_pub_key->cipher) + { + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + cdrs[i].h_cs = &hd->h_details.h_cs; + cdrs[i].nonce = nonce; } - return TALER_CRYPTO_helper_cs_r_derive_withdraw (ksh->helpers->csdh, - &hd->h_details.h_cs, - nonce, - r_pub); + return TALER_CRYPTO_helper_cs_r_batch_derive (ksh->helpers->csdh, + cdds_length, + cdrs, + for_melt, + r_pubs); } @@ -2903,22 +3739,23 @@ TEH_keys_denomination_revoke (const struct TALER_DenominationHashP *h_denom_pub) GNUNET_break (0); return; } - switch (hd->denom_pub.cipher) + switch (hd->denom_pub.bsign_pub_key->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: TALER_CRYPTO_helper_rsa_revoke (ksh->helpers->rsadh, &hd->h_details.h_rsa); TEH_keys_update_states (); return; - case TALER_DENOMINATION_CS: + case GNUNET_CRYPTO_BSA_CS: TALER_CRYPTO_helper_cs_revoke (ksh->helpers->csdh, &hd->h_details.h_cs); TEH_keys_update_states (); return; - default: - GNUNET_break (0); - return; } + GNUNET_break (0); + return; } @@ -3083,7 +3920,8 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc, const struct KeysResponseData *krd; ksh = TEH_keys_get_state (); - if (NULL == ksh) + if ( (NULL == ksh) || + (0 == ksh->krd_array_length) ) { if ( ( (SKR_LIMIT == skr_size) && (rc->connection == skr_connection) ) || @@ -3127,28 +3965,11 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc, if ( (NULL != etag) && (0 == strcmp (etag, krd->etag)) ) - { - MHD_RESULT ret; - struct MHD_Response *resp; - - resp = MHD_create_response_from_buffer (0, - NULL, - MHD_RESPMEM_PERSISTENT); - TALER_MHD_add_global_headers (resp); - GNUNET_break (GNUNET_OK == - setup_general_response_headers (ksh, - resp)); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_ETAG, - krd->etag)); - ret = MHD_queue_response (rc->connection, - MHD_HTTP_NOT_MODIFIED, - resp); - GNUNET_break (MHD_YES == ret); - MHD_destroy_response (resp); - return ret; - } + return TEH_RESPONSE_reply_not_modified (rc->connection, + krd->etag, + &setup_general_response_headers, + ksh); + return MHD_queue_response (rc->connection, MHD_HTTP_OK, (MHD_YES == @@ -3264,9 +4085,10 @@ TEH_keys_load_fees (struct TEH_KeyStateHandle *ksh, meta); if (GNUNET_OK == ok) { - GNUNET_assert (TALER_DENOMINATION_INVALID != hd->denom_pub.cipher); - TALER_denom_pub_deep_copy (denom_pub, - &hd->denom_pub); + GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID != + hd->denom_pub.bsign_pub_key->cipher); + TALER_denom_pub_copy (denom_pub, + &hd->denom_pub); } else { @@ -3289,7 +4111,7 @@ TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, struct HelperSignkey *hsk; struct GNUNET_PeerIdentity pid; - ksh = TEH_keys_get_state2 (true); + ksh = TEH_keys_get_state_for_management_only (); if (NULL == ksh) { GNUNET_break (0); @@ -3299,6 +4121,11 @@ TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, pid.public_key = exchange_pub->eddsa_pub; hsk = GNUNET_CONTAINER_multipeermap_get (ksh->helpers->esign_keys, &pid); + if (NULL == hsk) + { + GNUNET_break (0); + return GNUNET_NO; + } meta->start = hsk->start_time; meta->expire_sign = GNUNET_TIME_absolute_to_timestamp ( @@ -3459,7 +4286,7 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, json_t *reply; (void) rh; - ksh = TEH_keys_get_state2 (true); + ksh = TEH_keys_get_state_for_management_only (); if (NULL == ksh) { return TALER_MHD_reply_with_error (connection, @@ -3479,6 +4306,7 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, if ( (GNUNET_is_zero (&denom_rsa_sm_pub)) && (GNUNET_is_zero (&denom_cs_sm_pub)) ) { + /* Either IPC failed, or neither helper had any denominations configured. */ return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_GATEWAY, TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE, @@ -3491,7 +4319,6 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE, NULL); } - // then a secmod helper is not yet running and we should return an MHD_HTTP_BAD_GATEWAY! GNUNET_assert (NULL != fbc.denoms); GNUNET_assert (NULL != fbc.signkeys); GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->denom_keys, |