From 18c12f62347c2c3767a36577f352fc39df532801 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 15 Dec 2020 21:09:36 +0100 Subject: misc new /keys management related bugfixes --- src/exchange-tools/taler-exchange-offline.c | 8 +- src/exchange/taler-exchange-httpd.c | 12 +- src/exchange/taler-exchange-httpd_keys.c | 272 +++++++++++++-------- src/exchange/taler-exchange-httpd_keys.h | 63 ++++- .../taler-exchange-httpd_management_post_keys.c | 14 +- src/lib/exchange_api_management_post_keys.c | 4 + 6 files changed, 249 insertions(+), 124 deletions(-) diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c index 0fbcd6726..28bbbe023 100644 --- a/src/exchange-tools/taler-exchange-offline.c +++ b/src/exchange-tools/taler-exchange-offline.c @@ -635,8 +635,8 @@ denom_revocation_cb ( "Upload failed for command %u with status %u: %s (%s)\n", (unsigned int) drr->idx, hr->http_status, - TALER_ErrorCode_get_hint (hr->ec), - hr->hint); + hr->hint, + TALER_JSON_get_error_hint (hr->reply)); global_ret = 10; } GNUNET_CONTAINER_DLL_remove (drr_head, @@ -724,8 +724,8 @@ signkey_revocation_cb ( "Upload failed for command %u with status %u: %s (%s)\n", (unsigned int) srr->idx, hr->http_status, - TALER_ErrorCode_get_hint (hr->ec), - hr->hint); + hr->hint, + TALER_JSON_get_error_hint (hr->reply)); global_ret = 10; } GNUNET_CONTAINER_DLL_remove (srr_head, diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 6009672f1..eb9d7c46b 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -696,7 +696,7 @@ handle_post_auditors (const struct TEH_RequestHandler *rh, if ( (NULL == args[0]) || (NULL == args[1]) || - (NULL != args[0]) ) + (NULL != args[2]) ) { GNUNET_break_op (0); return r404 (connection, "/auditors/$AUDITOR_PUB/$H_DENOM_PUB"); @@ -1476,12 +1476,7 @@ run_main_loop (int fh, } atexit (&write_stats); - ret = TEH_keys_init (); - if (GNUNET_OK == ret) - { - ret = TEH_loop_run (); - TEH_loop_done (); - } + ret = TEH_loop_run (); switch (ret) { case GNUNET_OK: @@ -1696,6 +1691,9 @@ main (int argc, if (GNUNET_OK != TEH_WIRE_init ()) return 42; + if (GNUNET_OK != + TEH_keys_init ()) + return 43; ret = TEH_loop_init (); if (GNUNET_OK == ret) { diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index fe7a024cf..e955cd953 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -293,6 +293,12 @@ struct TEH_KeyStateHandle */ struct GNUNET_TIME_Absolute next_reload; + /** + * True if #finish_keys_response() was not yet run and this key state + * is only suitable for the /management/keys API. + */ + bool management_only; + }; @@ -888,7 +894,10 @@ TEH_keys_init () if (0 != pthread_key_create (&key_state, &destroy_key_state_cb)) + { + GNUNET_break (0); return GNUNET_SYSERR; + } key_state_available = true; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (TEH_cfg, @@ -1124,87 +1133,6 @@ auditor_denom_cb ( } -/** - * Create a key state. - * - * @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 * -build_key_state (struct HelperState *hs) -{ - struct TEH_KeyStateHandle *ksh; - enum GNUNET_DB_QueryStatus qs; - - ksh = GNUNET_new (struct TEH_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) - { - if (GNUNET_OK != - setup_key_helpers (&ksh->helpers)) - { - GNUNET_free (ksh); - return NULL; - } - } - else - { - ksh->helpers = *hs; - } - ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); - ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32, - GNUNET_NO /* MUST be NO! */); - ksh->auditors = json_array (); - /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ - qs = TEH_plugin->iterate_denominations (TEH_plugin->cls, - &denomination_info_cb, - ksh); - if (qs < 0) - { - GNUNET_break (0); - destroy_key_state (ksh, - true); - return NULL; - } - /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ - qs = TEH_plugin->iterate_active_signkeys (TEH_plugin->cls, - &signkey_info_cb, - ksh); - if (qs < 0) - { - GNUNET_break (0); - destroy_key_state (ksh, - true); - return NULL; - } - qs = TEH_plugin->iterate_active_auditors (TEH_plugin->cls, - &auditor_info_cb, - ksh); - if (qs < 0) - { - GNUNET_break (0); - destroy_key_state (ksh, - true); - return NULL; - } - qs = TEH_plugin->iterate_auditor_denominations (TEH_plugin->cls, - &auditor_denom_cb, - ksh); - if (qs < 0) - { - GNUNET_break (0); - destroy_key_state (ksh, - true); - return NULL; - } - return ksh; -} - - /** * Closure for #add_sign_key_cb. */ @@ -1462,9 +1390,10 @@ create_krd (struct TEH_KeyStateHandle *ksh, enum TALER_ErrorCode ec; if (TALER_EC_NONE != - (ec = TEH_keys_exchange_sign (&ks, - &exchange_pub, - &exchange_sig))) + (ec = TEH_keys_exchange_sign2 (ksh, + &ks, + &exchange_pub, + &exchange_sig))) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not create key response data: cannot sign (%s)\n", @@ -1544,7 +1473,7 @@ create_krd (struct TEH_KeyStateHandle *ksh, /** - * Update the "/keys" responses in @a ksh up to @a now into the future. + * Update the "/keys" responses in @a ksh, computing the detailed replies. * * This function is to recompute all (including cherry-picked) responses we * might want to return, based on the state already in @a ksh. @@ -1553,7 +1482,7 @@ create_krd (struct TEH_KeyStateHandle *ksh, * @return #GNUNET_OK on success */ static int -update_keys_response (struct TEH_KeyStateHandle *ksh) +finish_keys_response (struct TEH_KeyStateHandle *ksh) { json_t *recoup; struct SignKeyCtx sctx; @@ -1686,10 +1615,109 @@ update_keys_response (struct TEH_KeyStateHandle *ksh) json_decref (sctx.signkeys); json_decref (recoup); json_decref (denoms); + ksh->management_only = false; return GNUNET_OK; } +/** + * Create a key state. + * + * @param[in] hs helper state to (re)use, NULL if not available + * @param management_only if we should NOT run 'finish_keys_response()' + * because we only need the state for the /management/keys API + * @return NULL on error (i.e. failed to access database) + */ +static struct TEH_KeyStateHandle * +build_key_state (struct HelperState *hs, + bool management_only) +{ + struct TEH_KeyStateHandle *ksh; + enum GNUNET_DB_QueryStatus qs; + + ksh = GNUNET_new (struct TEH_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) + { + if (GNUNET_OK != + setup_key_helpers (&ksh->helpers)) + { + GNUNET_free (ksh); + return NULL; + } + } + else + { + ksh->helpers = *hs; + } + ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32, + GNUNET_NO /* MUST be NO! */); + ksh->auditors = json_array (); + /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ + qs = TEH_plugin->iterate_denominations (TEH_plugin->cls, + &denomination_info_cb, + ksh); + if (qs < 0) + { + GNUNET_break (0); + destroy_key_state (ksh, + true); + return NULL; + } + /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ + qs = TEH_plugin->iterate_active_signkeys (TEH_plugin->cls, + &signkey_info_cb, + ksh); + if (qs < 0) + { + GNUNET_break (0); + destroy_key_state (ksh, + true); + return NULL; + } + qs = TEH_plugin->iterate_active_auditors (TEH_plugin->cls, + &auditor_info_cb, + ksh); + if (qs < 0) + { + GNUNET_break (0); + destroy_key_state (ksh, + true); + return NULL; + } + qs = TEH_plugin->iterate_auditor_denominations (TEH_plugin->cls, + &auditor_denom_cb, + ksh); + if (qs < 0) + { + GNUNET_break (0); + destroy_key_state (ksh, + true); + return NULL; + } + if (management_only) + { + ksh->management_only = true; + return ksh; + } + if (GNUNET_OK != + finish_keys_response (ksh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Could not finish /keys response (likely no signing keys available yet)\n"); + destroy_key_state (ksh, + true); + return NULL; + } + return ksh; +} + + void TEH_keys_update_states () { @@ -1699,8 +1727,16 @@ TEH_keys_update_states () } -struct TEH_KeyStateHandle * -TEH_get_key_state (void) +/** + * Obtain the key state for the current thread. Should ONLY be used + * directly if @a management_only is true. Otherwise use #TEH_get_key_state(). + * + * @param management_only if we should NOT run 'finish_keys_response()' + * because we only need the state for the /management/keys API + * @return NULL on error + */ +static struct TEH_KeyStateHandle * +get_key_state (bool management_only) { struct TEH_KeyStateHandle *old_ksh; struct TEH_KeyStateHandle *ksh; @@ -1709,7 +1745,8 @@ TEH_get_key_state (void) old_ksh = pthread_getspecific (key_state); if (NULL == old_ksh) { - ksh = build_key_state (NULL); + ksh = build_key_state (NULL, + management_only); if (NULL == ksh) return NULL; if (0 != pthread_setspecific (key_state, @@ -1728,7 +1765,8 @@ TEH_get_key_state (void) "Rebuilding /keys, generation upgrade from %llu to %llu\n", (unsigned long long) old_ksh->key_generation, (unsigned long long) key_generation); - ksh = build_key_state (&old_ksh->helpers); + ksh = build_key_state (&old_ksh->helpers, + management_only); if (0 != pthread_setspecific (key_state, ksh)) { @@ -1748,6 +1786,24 @@ TEH_get_key_state (void) } +struct TEH_KeyStateHandle * +TEH_get_key_state (void) +{ + struct TEH_KeyStateHandle *ksh; + + ksh = get_key_state (false); + if (NULL == ksh) + return NULL; + if (ksh->management_only) + { + if (GNUNET_OK != + finish_keys_response (ksh)) + return NULL; + } + return ksh; +} + + struct TEH_DenominationKey * TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub, enum TALER_ErrorCode *ec, @@ -1830,13 +1886,12 @@ TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub) enum TALER_ErrorCode -TEH_keys_exchange_sign_ (const struct - GNUNET_CRYPTO_EccSignaturePurpose *purpose, - struct TALER_ExchangePublicKeyP *pub, - struct TALER_ExchangeSignatureP *sig) +TEH_keys_exchange_sign_ ( + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig) { struct TEH_KeyStateHandle *ksh; - enum TALER_ErrorCode ec; ksh = TEH_get_key_state (); if (NULL == ksh) @@ -1847,6 +1902,22 @@ TEH_keys_exchange_sign_ (const struct "Cannot sign request, no valid signing keys available.\n"); return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; } + return TEH_keys_exchange_sign2_ (ksh, + purpose, + pub, + sig); +} + + +enum TALER_ErrorCode +TEH_keys_exchange_sign2_ ( + struct TEH_KeyStateHandle *ksh, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig) +{ + enum TALER_ErrorCode ec; + ec = TALER_CRYPTO_helper_esign_sign_ (ksh->helpers.esh, purpose, pub, @@ -1971,11 +2042,6 @@ TEH_keys_get_handler (const struct TEH_RequestHandler *rh, { return suspend_request (connection); } - if (GNUNET_OK != - update_keys_response (ksh)) - { - return suspend_request (connection); - } krd = bsearch (&last_issue_date, ksh->krd_array, ksh->krd_array_length, @@ -2152,7 +2218,7 @@ TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub, struct HelperDenomination *hd; int ok; - ksh = TEH_get_key_state (); + ksh = get_key_state (true); if (NULL == ksh) { GNUNET_break (0); @@ -2184,7 +2250,7 @@ TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, struct HelperSignkey *hsk; struct GNUNET_PeerIdentity pid; - ksh = TEH_get_key_state (); + ksh = get_key_state (true); if (NULL == ksh) { GNUNET_break (0); @@ -2354,7 +2420,7 @@ TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh, struct TEH_KeyStateHandle *ksh; json_t *reply; - ksh = TEH_get_key_state (); + ksh = get_key_state (true); if (NULL == ksh) { GNUNET_break (0); diff --git a/src/exchange/taler-exchange-httpd_keys.h b/src/exchange/taler-exchange-httpd_keys.h index 1bdabd0fd..54dc8c73e 100644 --- a/src/exchange/taler-exchange-httpd_keys.h +++ b/src/exchange/taler-exchange-httpd_keys.h @@ -217,10 +217,31 @@ TEH_resume_keys_requests (bool do_shutdown); * @return #TALER_EC_NONE on success */ enum TALER_ErrorCode -TEH_keys_exchange_sign_ (const struct - GNUNET_CRYPTO_EccSignaturePurpose *purpose, - struct TALER_ExchangePublicKeyP *pub, - struct TALER_ExchangeSignatureP *sig); +TEH_keys_exchange_sign_ ( + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig); + + +/** + * Sign the message in @a purpose with the exchange's signing key. + * + * The @a purpose data is the beginning of the data of which the signature is + * to be created. The `size` field in @a purpose must correctly indicate the + * number of bytes of the data structure, including its header. Use + * #TEH_keys_exchange_sign() instead of calling this function directly! + * + * @param purpose the message to sign + * @param[out] pub set to the current public signing key of the exchange + * @param[out] sig signature over purpose using current signing key + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TEH_keys_exchange_sign2_ ( + struct TEH_KeyStateHandle *ksh, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig); /** @@ -250,6 +271,40 @@ TEH_keys_exchange_sign_ (const struct }) +/** + * @ingroup crypto + * @brief EdDSA sign a given block. + * + * The @a ps data must be a fixed-size struct for which the signature is to be + * created. The `size` field in @a ps->purpose must correctly indicate the + * number of bytes of the data structure, including its header. + * + * This allows requesting multiple denominations with the same @a ksh which + * thus will remain valid until the next call to + * #TEH_keys_denomination_by_hash() or #TEH_get_key_state() or + * #TEH_keys_exchange_sign(). + * + * @param ksh key state to use + * @param ps packed struct with what to sign, MUST begin with a purpose + * @param[out] pub where to store the public key to use for the signing + * @param[out] sig where to write the signature + * @return #TALER_EC_NONE on success + */ +#define TEH_keys_exchange_sign2(ksh,ps,pub,sig) \ + ({ \ + /* check size is set correctly */ \ + GNUNET_assert (htonl ((ps)->purpose.size) == \ + sizeof (*ps)); \ + /* check 'ps' begins with the purpose */ \ + GNUNET_static_assert (((void*) (ps)) == \ + ((void*) &(ps)->purpose)); \ + TEH_keys_exchange_sign2_ (ksh, \ + &(ps)->purpose, \ + pub, \ + sig); \ + }) + + /** * Revoke the given exchange's signing key. * This function should be called AFTER the database was diff --git a/src/exchange/taler-exchange-httpd_management_post_keys.c b/src/exchange/taler-exchange-httpd_management_post_keys.c index 84ec1f531..df044b6d7 100644 --- a/src/exchange/taler-exchange-httpd_management_post_keys.c +++ b/src/exchange/taler-exchange-httpd_management_post_keys.c @@ -157,7 +157,7 @@ add_keys (void *cls, MHD_HTTP_NOT_FOUND, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); - return qs; + return GNUNET_DB_STATUS_HARD_ERROR; } } else @@ -183,11 +183,12 @@ add_keys (void *cls, &akc->d_sigs[i].master_sig)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error ( + *mhd_ret = TALER_MHD_reply_with_error ( connection, MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID, + TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID, GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); + return GNUNET_DB_STATUS_HARD_ERROR; } } if (is_active) @@ -253,7 +254,7 @@ add_keys (void *cls, MHD_HTTP_NOT_FOUND, TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN, TALER_B2S (&akc->s_sigs[i].exchange_pub)); - return qs; + return GNUNET_DB_STATUS_HARD_ERROR; } } else @@ -273,11 +274,12 @@ add_keys (void *cls, &akc->s_sigs[i].master_sig)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error ( + *mhd_ret = TALER_MHD_reply_with_error ( connection, MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID, + TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID, GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); + return GNUNET_DB_STATUS_HARD_ERROR; } } if (is_active) diff --git a/src/lib/exchange_api_management_post_keys.c b/src/lib/exchange_api_management_post_keys.c index b885df7f2..7b37bb0ca 100644 --- a/src/lib/exchange_api_management_post_keys.c +++ b/src/lib/exchange_api_management_post_keys.c @@ -95,6 +95,10 @@ handle_post_keys_finished (void *cls, hr.ec = TALER_JSON_get_error_code (json); hr.hint = TALER_JSON_get_error_hint (json); break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; default: /* unexpected response code */ GNUNET_break_op (0); -- cgit v1.2.3