From 5a24334e83dabcb00e0e0f4292a678b6820ea370 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 9 Dec 2020 23:39:11 +0100 Subject: [PATCH] complete new implementation of GET /keys --- src/exchange/taler-exchange-httpd.c | 20 + src/exchange/taler-exchange-httpd.h | 5 + src/exchange/taler-exchange-httpd_keys.c | 661 +++++++++++++++--- src/exchange/taler-exchange-httpd_keys.h | 34 +- ...aler-exchange-httpd_management_post_keys.c | 90 +-- 5 files changed, 643 insertions(+), 167 deletions(-) diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index fe00bf6d4..9717c5eb2 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -103,6 +103,11 @@ struct GNUNET_CONFIGURATION_Handle *TEH_cfg; */ struct GNUNET_TIME_Relative TEH_max_keys_caching; +/** + * How long is the delay before we close reserves? + */ +struct GNUNET_TIME_Relative TEH_reserve_closing_delay; + /** * Master public key (according to the * configuration in the exchange directory). (global) @@ -1065,6 +1070,21 @@ exchange_serve_process_config (void) { req_max = ULONG_LONG_MAX; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + "exchangedb", + "IDLE_RESERVE_EXPIRATION_TIME", + &TEH_reserve_closing_delay)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchangedb", + "IDLE_RESERVE_EXPIRATION_TIME"); + /* use default */ + TEH_reserve_closing_delay + = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, + 4); + } + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (TEH_cfg, "exchange", diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index fd0de4799..316e565b6 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -34,6 +34,11 @@ */ extern struct GNUNET_TIME_Relative TEH_max_keys_caching; +/** + * How long is the delay before we close reserves? + */ +extern struct GNUNET_TIME_Relative TEH_reserve_closing_delay; + /** * The exchange's configuration. */ diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 747318a91..d39af5933 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -22,6 +22,7 @@ #include #include "taler_json_lib.h" #include "taler_mhd_lib.h" +#include "taler-exchange-httpd.h" #include "taler-exchange-httpd_keys.h" #include "taler-exchange-httpd_responses.h" #include "taler_exchangedb_plugin.h" @@ -241,7 +242,7 @@ struct SigningKey * reference counter) should be considered READ-ONLY until it is * ultimately destroyed (as there can be many concurrent users). */ -struct TEH_KeyStateHandle +struct KeyStateHandle { /** @@ -292,11 +293,21 @@ struct TEH_KeyStateHandle */ uint64_t key_generation; + /** + * When did we initiate the key reloading? + */ + struct GNUNET_TIME_Absolute reload_time; + + /** + * When is the next key invalid and we expect to have a different reply? + */ + struct GNUNET_TIME_Absolute next_reload; + }; /** - * Thread-local. Contains a pointer to `struct TEH_KeyStateHandle` or NULL. + * Thread-local. Contains a pointer to `struct KeyStateHandle` or NULL. * Stores the per-thread latest generation of our key state. */ static pthread_key_t key_state; @@ -339,7 +350,7 @@ static pthread_mutex_t sm_pub_mutex = PTHREAD_MUTEX_INITIALIZER; * @param[in,out] ksh key state to update */ static void -clear_response_cache (struct TEH_KeyStateHandle *ksh) +clear_response_cache (struct KeyStateHandle *ksh) { for (unsigned int i = 0; ikrd_array_length; i++) { @@ -664,7 +675,7 @@ sync_key_helpers (struct HelperState *hs) /** * Free denomination key data. * - * @param cls a `struct TEH_KeyStateHandle`, unused + * @param cls a `struct KeyStateHandle`, unused * @param h_denom_pub hash of the denomination public key, unused * @param value a `struct TEH_DenominationKey` to free * @return #GNUNET_OK (continue to iterate) @@ -695,7 +706,7 @@ clear_denomination_cb (void *cls, /** * Free denomination key data. * - * @param cls a `struct TEH_KeyStateHandle`, unused + * @param cls a `struct KeyStateHandle`, unused * @param h_denom_pub hash of the denomination public key, unused * @param value a `struct SigningKey` to free * @return #GNUNET_OK (continue to iterate) @@ -722,7 +733,7 @@ clear_signkey_cb (void *cls, * @param free_helper true to also release the helper state */ static void -destroy_key_state (struct TEH_KeyStateHandle *ksh, +destroy_key_state (struct KeyStateHandle *ksh, bool free_helper) { clear_response_cache (ksh); @@ -751,12 +762,12 @@ destroy_key_state (struct TEH_KeyStateHandle *ksh, * Free all resources associated with @a cls. Called when * the respective pthread is destroyed. * - * @param[in] cls a `struct TEH_KeyStateHandle`. + * @param[in] cls a `struct KeyStateHandle`. */ static void destroy_key_state_cb (void *cls) { - struct TEH_KeyStateHandle *ksh = cls; + struct KeyStateHandle *ksh = cls; destroy_key_state (ksh, true); @@ -804,7 +815,7 @@ TEH_keys_done () /** * Function called with information about the exchange's denomination keys. * - * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param cls closure with a `struct KeyStateHandle *` * @param denom_pub public key of the denomination * @param h_denom_pub hash of @a denom_pub * @param meta meta data information about the denomination type (value, expirations, fees) @@ -821,7 +832,7 @@ denomination_info_cb ( const struct TALER_MasterSignatureP *master_sig, bool recoup_possible) { - struct TEH_KeyStateHandle *ksh = cls; + struct KeyStateHandle *ksh = cls; struct TEH_DenominationKey *dk; dk = GNUNET_new (struct TEH_DenominationKey); @@ -843,7 +854,7 @@ denomination_info_cb ( /** * Function called with information about the exchange's online signing keys. * - * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param cls closure with a `struct KeyStateHandle *` * @param exchange_pub the public key * @param meta meta data information about the denomination type (expirations) * @param master_sig master signature affirming the validity of this denomination @@ -855,7 +866,7 @@ signkey_info_cb ( const struct TALER_EXCHANGEDB_SignkeyMetaData *meta, const struct TALER_MasterSignatureP *master_sig) { - struct TEH_KeyStateHandle *ksh = cls; + struct KeyStateHandle *ksh = cls; struct SigningKey *sk; struct GNUNET_PeerIdentity pid; @@ -876,7 +887,7 @@ signkey_info_cb ( /** * Function called with information about the exchange's auditors. * - * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param cls closure with a `struct KeyStateHandle *` * @param auditor_pub the public key of the auditor * @param auditor_url URL of the REST API of the auditor * @param auditor_name human readable official name of the auditor @@ -888,7 +899,7 @@ auditor_info_cb ( const char *auditor_url, const char *auditor_name) { - struct TEH_KeyStateHandle *ksh = cls; + struct KeyStateHandle *ksh = cls; GNUNET_break (0 == json_array_append_new ( @@ -907,7 +918,7 @@ auditor_info_cb ( * Function called with information about the denominations * audited by the exchange's auditors. * - * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param cls closure with a `struct KeyStateHandle *` * @param auditor_pub the public key of an auditor * @param h_denom_pub hash of a denomination key audited by this auditor * @param auditor_sig signature from the auditor affirming this @@ -919,7 +930,7 @@ auditor_denom_cb ( const struct GNUNET_HashCode *h_denom_pub, const struct TALER_AuditorSignatureP *auditor_sig) { - struct TEH_KeyStateHandle *ksh = cls; + struct KeyStateHandle *ksh = cls; struct TEH_DenominationKey *dk; struct TEH_AuditorSignature *as; @@ -948,13 +959,15 @@ auditor_denom_cb ( * @param[in] hs helper state to (re)use, NULL if not available * @return NULL on error (i.e. failed to access database) */ -static struct TEH_KeyStateHandle * +static struct KeyStateHandle * build_key_state (struct HelperState *hs) { - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; enum GNUNET_DB_QueryStatus qs; - ksh = GNUNET_new (struct TEH_KeyStateHandle); + ksh = GNUNET_new (struct KeyStateHandle); + ksh->reload_time = GNUNET_TIME_absolute_get (); + GNUNET_TIME_round_abs (&ksh->reload_time); /* We must use the key_generation from when we STARTED the process! */ ksh->key_generation = key_generation; if (NULL == hs) @@ -1021,6 +1034,330 @@ build_key_state (struct HelperState *hs) } +/** + * Closure for #add_sign_key_cb. + */ +struct SignKeyCtx +{ + /** + * When does the next signing key expire. Updated. + */ + struct GNUNET_TIME_Absolute next_sk_expire; + + /** + * JSON array of signing keys (being created). + */ + json_t *signkeys; +}; + + +/** + * Function called for all signing keys, used to build up the + * respective JSON response. + * + * @param cls a `struct SignKeyCtx *` with the array to append keys to + * @param pid the exchange public key (in type disguise) + * @param value a `struct SigningKey` + * @return #GNUNET_OK (continue to iterate) + */ +static int +add_sign_key_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct SignKeyCtx *ctx = cls; + struct SigningKey *sk = value; + + ctx->next_sk_expire = + GNUNET_TIME_absolute_min (ctx->next_sk_expire, + sk->meta.expire_sign); + + GNUNET_assert ( + 0 == + json_array_append_new ( + ctx->signkeys, + json_pack ("{s:o, s:o, s:o, s:o, s:o}", + "stamp_start", + GNUNET_JSON_from_time_abs (sk->meta.start), + "stamp_expire", + GNUNET_JSON_from_time_abs (sk->meta.expire_sign), + "stamp_end", + GNUNET_JSON_from_time_abs (sk->meta.expire_legal), + "master_sig", + GNUNET_JSON_from_data_auto (&sk->master_sig), + "key", + GNUNET_JSON_from_data_auto (&sk->exchange_pub)))); + return GNUNET_OK; +} + + +/** + * Closure for #add_denom_key_cb. + */ +struct DenomKeyCtx +{ + /** + * Heap for sorting active denomination keys by start time. + */ + struct GNUNET_CONTAINER_Heap *heap; + + /** + * JSON array of revoked denomination keys. + */ + json_t *recoup; + + /** + * When does the next denomination key expire. Updated. + */ + struct GNUNET_TIME_Absolute next_dk_expire; + +}; + + +/** + * Function called for all denomination keys, used to build up the + * JSON list of *revoked* denomination keys and the + * heap of non-revoked denomination keys by timeout. + * + * @param cls a `struct DenomKeyCtx` + * @param h_denom_pub hash of the denomination key + * @param value a `struct TEH_DenominationKey` + * @return #GNUNET_OK (continue to iterate) + */ +static int +add_denom_key_cb (void *cls, + const struct GNUNET_HashCode *h_denom_pub, + void *value) +{ + struct DenomKeyCtx *dkc = cls; + struct TEH_DenominationKey *dk = value; + + if (dk->recoup_possible) + { + GNUNET_assert ( + 0 == + json_array_append_new ( + dkc->recoup, + json_pack ("{s:o}", + "h_denom_pub", + GNUNET_JSON_from_data_auto (h_denom_pub)))); + } + else + { + dkc->next_dk_expire = + GNUNET_TIME_absolute_min (dkc->next_dk_expire, + dk->meta.expire_withdraw); + (void) GNUNET_CONTAINER_heap_insert (dkc->heap, + dk, + dk->meta.start.abs_value_us); + } + return GNUNET_OK; +} + + +/** + * Produce HTTP "Date:" header. + * + * @param at time to write to @a date + * @param[out] date where to write the header, with + * at least 128 bytes available space. + */ +static void +get_date_string (struct GNUNET_TIME_Absolute at, + char date[128]) +{ + static const char *const days[] = + { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char *const mons[] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec"}; + struct tm now; + time_t t; +#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \ + ! defined(HAVE_GMTIME_R) + struct tm*pNow; +#endif + + date[0] = 0; + t = (time_t) (at.abs_value_us / 1000LL / 1000LL); +#if defined(HAVE_C11_GMTIME_S) + if (NULL == gmtime_s (&t, &now)) + return; +#elif defined(HAVE_W32_GMTIME_S) + if (0 != gmtime_s (&now, &t)) + return; +#elif defined(HAVE_GMTIME_R) + if (NULL == gmtime_r (&t, &now)) + return; +#else + pNow = gmtime (&t); + if (NULL == pNow) + return; + now = *pNow; +#endif + sprintf (date, + "%3s, %02u %3s %04u %02u:%02u:%02u GMT", + days[now.tm_wday % 7], + (unsigned int) now.tm_mday, + mons[now.tm_mon % 12], + (unsigned int) (1900 + now.tm_year), + (unsigned int) now.tm_hour, + (unsigned int) now.tm_min, + (unsigned int) now.tm_sec); +} + + +/** + * Add the headers we want to set for every /keys response. + * + * @param ksh the key state to use + * @param[in,out] response the response to modify + * @return #GNUNET_OK on success + */ +static int +setup_general_response_headers (const struct KeyStateHandle *ksh, + struct MHD_Response *response) +{ + char dat[128]; + + TALER_MHD_add_global_headers (response); + GNUNET_break (MHD_YES == + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_TYPE, + "application/json")); + get_date_string (ksh->reload_time, + dat); + GNUNET_break (MHD_YES == + MHD_add_response_header (response, + MHD_HTTP_HEADER_LAST_MODIFIED, + dat)); + if (0 != ksh->next_reload.abs_value_us) + { + struct GNUNET_TIME_Absolute m; + + m = GNUNET_TIME_relative_to_absolute (TEH_max_keys_caching); + m = GNUNET_TIME_absolute_min (m, + ksh->next_reload); + get_date_string (m, + dat); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Setting /keys 'Expires' header to '%s'\n", + dat); + GNUNET_break (MHD_YES == + MHD_add_response_header (response, + MHD_HTTP_HEADER_EXPIRES, + dat)); + } + return GNUNET_OK; +} + + +/** + * Initialize @a krd using the given values for @a signkeys, + * @a recoup and @a denoms. + * + * @param[in,out] ksh key state handle we build @a krd for + * @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms + * @param 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 + */ +static void +create_krd (struct KeyStateHandle *ksh, + const struct GNUNET_HashCode *denom_keys_hash, + struct GNUNET_TIME_Absolute last_cpd, + json_t *signkeys, + json_t *recoup, + json_t *denoms) +{ + struct KeysResponseData krd; + struct TALER_ExchangePublicKeyP exchange_pub; + struct TALER_ExchangeSignatureP exchange_sig; + json_t *keys; + + /* Sign hash over denomination keys */ + { + struct TALER_ExchangeKeySetPS ks = { + .purpose.size = htonl (sizeof (ks)), + .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET), + .list_issue_date = GNUNET_TIME_absolute_hton (last_cpd), + .hc = *denom_keys_hash + }; + + TEH_keys_exchange_sign (&ks, + &exchange_pub, + &exchange_sig); + } + + keys = json_pack ( + "{s:s, s:o, s:o, s:O, s:O," + " s:O, s:o, s:o, s:o, s:o}", + /* 1-5 */ + "version", EXCHANGE_PROTOCOL_VERSION, + "master_public_key", GNUNET_JSON_from_data_auto (&TEH_master_public_key), + "reserve_closing_delay", GNUNET_JSON_from_time_rel ( + TEH_reserve_closing_delay), + "signkeys", signkeys, + "recoup", recoup, + /* 6-10 */ + "denoms", denoms, + "auditors", ksh->auditors, + "list_issue_date", GNUNET_JSON_from_time_abs (last_cpd), + "eddsa_pub", GNUNET_JSON_from_data_auto (&exchange_pub), + "eddsa_sig", GNUNET_JSON_from_data_auto (&exchange_sig)); + GNUNET_assert (NULL != keys); + + { + char *keys_json; + void *keys_jsonz; + size_t keys_jsonz_size; + int comp; + + /* Convert /keys response to UTF8-String */ + keys_json = json_dumps (keys, + JSON_INDENT (2)); + json_decref (keys); + GNUNET_assert (NULL != keys_json); + + /* Keep copy for later compression... */ + keys_jsonz = GNUNET_strdup (keys_json); + keys_jsonz_size = strlen (keys_json); + + /* Create uncompressed response */ + krd.response_uncompressed + = MHD_create_response_from_buffer (keys_jsonz_size, + keys_json, + MHD_RESPMEM_MUST_FREE); + GNUNET_assert (NULL != krd.response_uncompressed); + GNUNET_assert (GNUNET_OK == + setup_general_response_headers (ksh, + krd.response_uncompressed)); + /* Also compute compressed version of /keys response */ + comp = TALER_MHD_body_compress (&keys_jsonz, + &keys_jsonz_size); + krd.response_compressed + = MHD_create_response_from_buffer (keys_jsonz_size, + keys_jsonz, + MHD_RESPMEM_MUST_FREE); + GNUNET_assert (NULL != krd.response_compressed); + /* If the response is actually compressed, set the + respective header. */ + GNUNET_assert ( (MHD_YES != comp) || + (MHD_YES == + 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)); + } + GNUNET_array_append (ksh->krd_array, + ksh->krd_array_length, + krd); +} + + /** * Update the "/keys" responses in @a ksh up to @a now into the future. * @@ -1029,27 +1366,121 @@ build_key_state (struct HelperState *hs) * * * @param[in,out] ksh state handle to update - * @param now timestamp for when to compute the replies. */ static void -update_keys_response (struct TEH_KeyStateHandle *ksh, - struct GNUNET_TIME_Absolute now) +update_keys_response (struct KeyStateHandle *ksh) { - // FIXME: update 'krd_array' here! - // FIXME: this relates to a good design for cherry-picking, - // which we currently don't have for new /keys! + json_t *recoup; + struct SignKeyCtx sctx; + json_t *denoms; + struct GNUNET_TIME_Absolute last_cpd; + struct GNUNET_CONTAINER_Heap *heap; + struct GNUNET_HashContext *hash_context; + + sctx.signkeys = json_array (); + sctx.next_sk_expire = GNUNET_TIME_UNIT_FOREVER_ABS; + GNUNET_assert (NULL != sctx.signkeys); + GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, + &add_sign_key_cb, + &sctx); + recoup = json_array (); + GNUNET_assert (NULL != recoup); + heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + { + struct DenomKeyCtx dkc = { + .recoup = recoup, + .heap = heap, + .next_dk_expire = GNUNET_TIME_UNIT_FOREVER_ABS, + }; + + GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, + &add_denom_key_cb, + &dkc); + ksh->next_reload + = GNUNET_TIME_absolute_min (dkc.next_dk_expire, + sctx.next_sk_expire); + } + denoms = json_array (); + GNUNET_assert (NULL != denoms); + last_cpd = GNUNET_TIME_UNIT_ZERO_ABS; + hash_context = GNUNET_CRYPTO_hash_context_start (); + { + struct TEH_DenominationKey *dk; + + while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) + { + if ( (last_cpd.abs_value_us != dk->meta.start.abs_value_us) && + (0 != last_cpd.abs_value_us) ) + { + struct GNUNET_HashCode hc; + + GNUNET_CRYPTO_hash_context_finish ( + GNUNET_CRYPTO_hash_context_copy (hash_context), + &hc); + create_krd (ksh, + &hc, + last_cpd, + sctx.signkeys, + recoup, + denoms); + last_cpd = dk->meta.start; + } + GNUNET_CRYPTO_hash_context_read (hash_context, + &dk->h_denom_pub, + sizeof (struct GNUNET_HashCode)); + GNUNET_assert ( + 0 == + json_array_append_new ( + denoms, + json_pack ("{s:o, s:o, s:o, s:o, s:o," + " s:o, s:o, s:o, s:o, s:o," + " s:o}", + "master_sig", + GNUNET_JSON_from_data_auto (&dk->master_sig), + "stamp_start", + GNUNET_JSON_from_time_abs (dk->meta.start), + "stamp_expire_withdraw", + GNUNET_JSON_from_time_abs (dk->meta.expire_withdraw), + "stamp_expire_deposit", + GNUNET_JSON_from_time_abs (dk->meta.expire_deposit), + "stamp_expire_legal", + GNUNET_JSON_from_time_abs (dk->meta.expire_legal), + /* 5 entries until here */ + "denom_pub", + GNUNET_JSON_from_rsa_public_key ( + dk->denom_pub.rsa_public_key), + "value", + TALER_JSON_from_amount (&dk->meta.value), + "fee_withdraw", + TALER_JSON_from_amount (&dk->meta.fee_withdraw), + "fee_deposit", + TALER_JSON_from_amount (&dk->meta.fee_deposit), + "fee_refresh", + TALER_JSON_from_amount (&dk->meta.fee_refresh), + /* 10 entries until here */ + "fee_refund", + TALER_JSON_from_amount (&dk->meta.fee_refund)))); + } + } + GNUNET_CONTAINER_heap_destroy (heap); + { + struct GNUNET_HashCode hc; + + GNUNET_CRYPTO_hash_context_finish (hash_context, + &hc); + create_krd (ksh, + &hc, + last_cpd, + sctx.signkeys, + recoup, + denoms); + } + json_decref (sctx.signkeys); + json_decref (recoup); + json_decref (denoms); } -/** - * Something changed in the database. Rebuild all key states. This function - * should be called if the exchange learns about a new signature from an - * auditor or our master key. - * - * (We do not do so immediately, but merely signal to all threads that they - * need to rebuild their key state upon the next call to - * #TEH_get_key_state()). - */ void TEH_keys_update_states () { @@ -1059,17 +1490,16 @@ TEH_keys_update_states () /** - * Return the current key state for this thread. Possibly - * re-builds the key state if we have reason to believe - * that something changed. + * 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 TEH_KeyStateHandle * -TEH_keys_get_state (void) +static struct KeyStateHandle * +get_key_state (void) { - struct TEH_KeyStateHandle *old_ksh; - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *old_ksh; + struct KeyStateHandle *ksh; old_ksh = pthread_getspecific (key_state); if (NULL == old_ksh) @@ -1115,10 +1545,10 @@ TEH_keys_denomination_by_hash ( enum TALER_ErrorCode *ec, unsigned int *hc) { - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; struct TEH_DenominationKey *dk; - ksh = TEH_keys_get_state (); + ksh = get_key_state (); if (NULL == ksh) { *hc = MHD_HTTP_INTERNAL_SERVER_ERROR; @@ -1144,10 +1574,10 @@ TEH_keys_denomination_sign ( size_t msg_size, enum TALER_ErrorCode *ec) { - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; struct TALER_DenominationSignature none = { NULL }; - ksh = TEH_keys_get_state (); + ksh = get_key_state (); if (NULL == ksh) { *ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; @@ -1165,9 +1595,9 @@ void TEH_keys_denomination_revoke ( const struct GNUNET_HashCode *h_denom_pub) { - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; - ksh = TEH_keys_get_state (); + ksh = get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -1185,10 +1615,10 @@ TEH_keys_exchange_sign_ (const struct struct TALER_ExchangePublicKeyP *pub, struct TALER_ExchangeSignatureP *sig) { - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; enum TALER_ErrorCode ec; - ksh = TEH_keys_get_state (); + ksh = get_key_state (); if (NULL == ksh) { /* This *can* happen if the exchange's crypto helper is not running @@ -1232,9 +1662,9 @@ TEH_keys_exchange_sign_ (const struct void TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub) { - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; - ksh = TEH_keys_get_state (); + ksh = get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -1275,7 +1705,6 @@ TEH_handler_keys_NEW (const struct TEH_RequestHandler *rh, const char *const args[]) { struct GNUNET_TIME_Absolute last_issue_date; - struct GNUNET_TIME_Absolute now; (void) rh; (void) args; @@ -1312,52 +1741,11 @@ TEH_handler_keys_NEW (const struct TEH_RequestHandler *rh, } } - now = GNUNET_TIME_absolute_get (); { - const char *have_fakenow; - - have_fakenow = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "now"); - if (NULL != have_fakenow) - { - unsigned long long fakenown; - - if (1 != - sscanf (have_fakenow, - "%llu", - &fakenown)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - have_fakenow); - } - if (TEH_allow_keys_timetravel) - { - /* The following multiplication may overflow; but this should not really - be a problem, as giving back 'older' data than what the client asks for - (given that the client asks for data in the distant future) is not - problematic */ - now.abs_value_us = (uint64_t) fakenown * 1000000LLU; - } - else - { - /* Option not allowed by configuration */ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_KEYS_TIMETRAVEL_FORBIDDEN, - NULL); - } - } - } - - { - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; const struct KeysResponseData *krd; - ksh = TEH_keys_get_state (); + ksh = get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -1366,8 +1754,7 @@ TEH_handler_keys_NEW (const struct TEH_RequestHandler *rh, TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, "no key state"); } - update_keys_response (ksh, - now); + update_keys_response (ksh); krd = bsearch (&last_issue_date, ksh->krd_array, ksh->krd_array_length, @@ -1416,9 +1803,9 @@ TEH_handler_keys_NEW (const struct TEH_RequestHandler *rh, * @param[in,out] meta denomination type data to complete * @return #GNUNET_OK on success */ -int -TEH_keys_load_fees (const char *section_name, - struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) +static int +load_fees (const char *section_name, + struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) { struct GNUNET_TIME_Relative deposit_duration; struct GNUNET_TIME_Relative legal_duration; @@ -1535,6 +1922,59 @@ TEH_keys_load_fees (const char *section_name, } +int +TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub, + struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) +{ + struct KeyStateHandle *ksh; + struct HelperDenomination *hd; + + ksh = get_key_state (); + if (NULL == ksh) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers.denom_keys, + h_denom_pub); + meta->start = hd->start_time; + meta->expire_withdraw = GNUNET_TIME_absolute_add (meta->start, + hd->validity_duration); + return load_fees (hd->section_name, + meta); +} + + +int +TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, + struct GNUNET_TIME_Absolute *start_sign, + struct GNUNET_TIME_Absolute *end_sign, + struct GNUNET_TIME_Absolute *end_legal) +{ + struct KeyStateHandle *ksh; + struct HelperSignkey *hsk; + struct GNUNET_PeerIdentity pid; + + ksh = get_key_state (); + if (NULL == ksh) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + pid.public_key = exchange_pub->eddsa_pub; + hsk = GNUNET_CONTAINER_multipeermap_get (ksh->helpers.esign_keys, + &pid); + *start_sign = hsk->start_time; + *end_sign = GNUNET_TIME_absolute_add (*start_sign, + hsk->validity_duration); + *end_legal = GNUNET_TIME_absolute_add (*end_sign, + signkey_legal_duration); + return GNUNET_OK; +} + + /** * Closure for #add_future_denomkey_cb and #add_future_signkey_cb. */ @@ -1543,7 +1983,7 @@ struct FutureBuilderContext /** * Our key state. */ - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; /** * Array of denomination keys. @@ -1587,8 +2027,8 @@ add_future_denomkey_cb (void *cls, meta.expire_withdraw = GNUNET_TIME_absolute_add (meta.start, hd->validity_duration); if (GNUNET_OK != - TEH_keys_load_fees (hd->section_name, - &meta)) + load_fees (hd->section_name, + &meta)) { /* Woops, couldn't determine fee structure!? */ return GNUNET_OK; @@ -1672,24 +2112,15 @@ add_future_signkey_cb (void *cls, } -/** - * Function to call to handle requests to "/management/keys" by sending - * back our future key material. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param args array of additional options (must be empty for this function) - * @return MHD result code - */ MHD_RESULT TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh, struct MHD_Connection *connection, const char *const args[]) { - struct TEH_KeyStateHandle *ksh; + struct KeyStateHandle *ksh; json_t *reply; - ksh = TEH_keys_get_state (); + ksh = get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -1741,4 +2172,4 @@ TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh, } -/* end of taler-exchange-httpd_keystate.c */ +/* end of taler-exchange-httpd_keys.c */ diff --git a/src/exchange/taler-exchange-httpd_keys.h b/src/exchange/taler-exchange-httpd_keys.h index 614851482..dc78e0e54 100644 --- a/src/exchange/taler-exchange-httpd_keys.h +++ b/src/exchange/taler-exchange-httpd_keys.h @@ -191,9 +191,9 @@ TEH_keys_exchange_sign_ (const struct /* check 'ps' begins with the purpose */ \ GNUNET_static_assert (((void*) (ps)) == \ ((void*) &(ps)->purpose)); \ - TEH_exchange_sign_ (&(ps)->purpose, \ - pub, \ - sig); \ + TEH_keys_exchange_sign_ (&(ps)->purpose, \ + pub, \ + sig); \ }) @@ -243,19 +243,35 @@ TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh, /** - * Load fees and expiration times (!) for the denomination type configured - * in section @a section_name. Before calling this function, the - * `start` and `validity_duration` times must already be initialized in @a meta. + * Load fees and expiration times (!) for the denomination type configured for + * the denomination matching @a h_denom_pub. * - * @param section_name section in the configuration to use - * @param[in,out] meta denomination type data to complete + * @param h_denom_pub hash of the denomination public key + * to use to derive the section name of the configuration to use + * @param[out] meta denomination type data to complete * @return #GNUNET_OK on success */ int -TEH_keys_load_fees (const char *section_name, +TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub, struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta); +/** + * Load expiration times for the given onling signing key. + * + * @param exchange_pub the online signing key + * @param[out] start_sign starting signing time + * @param[out] end_sign send signing time + * @param[out] end_legal legal expiration time + * @return #GNUNET_OK on success + */ +int +TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, + struct GNUNET_TIME_Absolute *start_sign, + struct GNUNET_TIME_Absolute *end_sign, + struct GNUNET_TIME_Absolute *end_legal); + + /** * Initialize keys submodule. * diff --git a/src/exchange/taler-exchange-httpd_management_post_keys.c b/src/exchange/taler-exchange-httpd_management_post_keys.c index cc52f5cf2..0e4609e20 100644 --- a/src/exchange/taler-exchange-httpd_management_post_keys.c +++ b/src/exchange/taler-exchange-httpd_management_post_keys.c @@ -126,21 +126,12 @@ add_keys (void *cls, bool is_active = false; struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; - qs = TEH_plugin->lookup_future_denomination_key ( + /* For idempotency, check if the key is already active */ + qs = TEH_plugin->lookup_denomination_key ( TEH_plugin->cls, session, &akc->d_sigs[i].h_denom_pub, &meta); - if (0 == qs) - { - /* For idempotency, check if the key is already active */ - qs = TEH_plugin->lookup_denomination_key ( - TEH_plugin->cls, - session, - &akc->d_sigs[i].h_denom_pub, - &meta); - is_active = true; /* if we pass, it's active! */ - } if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -154,12 +145,21 @@ add_keys (void *cls, } if (0 == qs) { - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, - GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); - return qs; + if (GNUNET_OK != + TEH_keys_load_fees (&akc->d_sigs[i].h_denom_pub, + &meta)) + { + *mhd_ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, + GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); + return qs; + } + } + else + { + active = true; } /* check signature is valid */ @@ -214,24 +214,17 @@ add_keys (void *cls, { enum GNUNET_DB_QueryStatus qs; bool is_active = false; + struct GNUNET_TIME_Absolute start_sign; + struct GNUNET_TIME_Absolute end_sign; + struct GNUNET_TIME_Absolute end_legal; - // FIXME: future signing keys are currently not in DB, - // may want to get them from in-memory instead. - qs = TEH_plugin->lookup_future_signing_key ( + qs = TEH_plugin->lookup_signing_key ( TEH_plugin->cls, session, &akc->s_sigs[i].exchange_pub, - &META); - if (0 == qs) - { - /* For idempotency, check if the key is already active */ - qs = TEH_plugin->lookup_signing_key ( - TEH_plugin->cls, - session, - &akc->s_sigs[i].exchange_pub, - &META); - is_active = true; /* if we pass, it's active! */ - } + &start_sign, + &end_sign, + &end_legal); if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -245,12 +238,24 @@ add_keys (void *cls, } if (0 == qs) { - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN, - TALER_B2S (&akc->s_sigs[i].exchange_pub)); - return qs; + if (GNUNET_OK != + TEH_keys_get_timing (&akc->s_sigs[i].exchange_pub, + &start_sign, + &end_sign, + &end_legal)) + { + /* For idempotency, check if the key is already active */ + *mhd_ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN, + TALER_B2S (&akc->s_sigs[i].exchange_pub)); + return qs; + } + } + else + { + is_active = true; /* if we pass, it's active! */ } /* check signature is valid */ @@ -258,12 +263,11 @@ add_keys (void *cls, if (GNUNET_OK != TALER_exchange_offline_signkey_validity_verify ( &akc->s_sigs[i].exchange_pub, - x, - y, - z, + start_sign, + end_sign, + end_legal, &TEH_master_public_key, - & - & akc->s_sigs[i].master_sig)) + &akc->s_sigs[i].master_sig)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error ( @@ -278,7 +282,7 @@ add_keys (void *cls, qs = TEH_plugin->activate_signing_key ( TEH_plugin->cls, session, - &akc->s_sigs[i].exchange_pub, + &akc->s_sigs[i].exchange_pub, // FIXME: provision meta data!? &akc->s_sigs[i].master_sig); if (qs < 0) {