From b5d88fc2d1832fd27bdd7df0860c07ae3c61312c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 14 Dec 2020 15:42:32 +0100 Subject: activating implementation of #6175 --- contrib/gana | 2 +- contrib/gana-update.sh | 6 +- src/auditor/generate-auditor-basedb.sh | 5 + src/auditor/generate-revoke-basedb.sh | 5 + src/exchange/taler-exchange-httpd.c | 26 +- src/exchange/taler-exchange-httpd.h | 5 + src/exchange/taler-exchange-httpd_deposit.c | 73 +++- src/exchange/taler-exchange-httpd_deposits_get.c | 17 +- src/exchange/taler-exchange-httpd_keys.c | 459 ++++++++++++++++----- src/exchange/taler-exchange-httpd_keys.h | 75 +++- src/exchange/taler-exchange-httpd_keystate.c | 2 +- .../taler-exchange-httpd_management_auditors.c | 3 +- ...nge-httpd_management_denominations_HDP_revoke.c | 3 +- .../taler-exchange-httpd_management_post_keys.c | 16 +- ...r-exchange-httpd_management_signkey_EP_revoke.c | 3 +- .../taler-exchange-httpd_management_wire.c | 3 +- src/exchange/taler-exchange-httpd_melt.c | 167 ++++---- src/exchange/taler-exchange-httpd_recoup.c | 56 ++- .../taler-exchange-httpd_refreshes_reveal.c | 102 +++-- src/exchange/taler-exchange-httpd_refund.c | 44 +- src/exchange/taler-exchange-httpd_responses.c | 41 +- src/exchange/taler-exchange-httpd_transfers_get.c | 23 +- src/exchange/taler-exchange-httpd_withdraw.c | 100 +++-- src/exchange/test_taler_exchange_httpd.conf | 1 + src/exchange/test_taler_exchange_httpd.get | 2 +- src/exchange/test_taler_exchange_httpd.sh | 6 +- src/exchange/test_taler_exchange_httpd_restart.sh | 11 - src/exchange/test_taler_exchange_unix.conf | 1 + src/include/taler_mhd_lib.h | 15 + src/mhd/mhd_responses.c | 31 ++ src/testing/Makefile.am | 5 +- src/testing/test_auditor_api.c | 6 +- src/testing/test_exchange_api.c | 10 +- .../test_exchange_api_keys_cherry_picking.c | 8 +- src/testing/test_exchange_api_revocation.c | 2 +- src/testing/test_exchange_api_twisted.c | 11 +- src/testing/test_exchange_management_api.c | 2 +- src/testing/test_taler_exchange_wirewatch.c | 2 +- src/testing/testing_api_cmd_offline_sign_keys.c | 3 +- src/testing/testing_api_cmd_revoke.c | 13 +- src/testing/testing_api_helpers_exchange.c | 2 +- src/testing/testing_api_loop.c | 81 ++-- src/util/taler-helper-crypto-rsa.c | 62 ++- 43 files changed, 1038 insertions(+), 472 deletions(-) diff --git a/contrib/gana b/contrib/gana index 3501eb7b8..912dc84dc 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit 3501eb7b857d573258c1ab1c42d7e827c36cec9d +Subproject commit 912dc84dc52a1291b635e19da32c7c824719f8d4 diff --git a/contrib/gana-update.sh b/contrib/gana-update.sh index a5da6f1b1..196d4b530 100755 --- a/contrib/gana-update.sh +++ b/contrib/gana-update.sh @@ -10,9 +10,9 @@ make cd ../../.. if ! diff contrib/gana/gnu-taler-error-codes/taler_error_codes.h src/include/taler_error_codes.h > /dev/null then - echo "Deploying latest new GANA database..." cp contrib/gana/gnu-taler-error-codes/taler_error_codes.h src/include/taler_error_codes.h +fi +if ! diff contrib/gana/gnu-taler-error-codes/taler_error_codes.c src/util/taler_error_codes.c > /dev/null +then cp contrib/gana/gnu-taler-error-codes/taler_error_codes.c src/util/taler_error_codes.c -else - echo "GANA database already up-to-date" fi diff --git a/src/auditor/generate-auditor-basedb.sh b/src/auditor/generate-auditor-basedb.sh index 86e8fe2ec..c41d4a982 100755 --- a/src/auditor/generate-auditor-basedb.sh +++ b/src/auditor/generate-auditor-basedb.sh @@ -116,6 +116,11 @@ mv a2e.dat $ABD # Launch services echo "Launching services" taler-bank-manage-testing $CONF postgres:///$TARGET_DB serve & +TFN=`which taler-exchange-httpd` +TBINPFX=`dirname $TFN` +TLIBEXEC=${BINPFX}/../lib/libexec/taler/ +$TLIBEXEC/taler-helper-crypto-eddsa -c $CONF 2> taler-helper-crypto-eddsa.log & +$TLIBEXEC/taler-helper-crypto-rsa -c $CONF 2> taler-helper-crypto-rsa.log & taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log & taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log & taler-exchange-wirewatch -c $CONF 2> taler-exchange-wirewatch.log & diff --git a/src/auditor/generate-revoke-basedb.sh b/src/auditor/generate-revoke-basedb.sh index ef77e4d7d..3874e349b 100755 --- a/src/auditor/generate-revoke-basedb.sh +++ b/src/auditor/generate-revoke-basedb.sh @@ -104,6 +104,11 @@ mv a2e.dat $ABD # Launch services echo "Launching services" taler-bank-manage-testing $CONF postgres:///$TARGET_DB serve &> revocation-bank.log & +TFN=`which taler-exchange-httpd` +TBINPFX=`dirname $TFN` +TLIBEXEC=${BINPFX}/../lib/libexec/taler/ +$TLIBEXEC/taler-helper-crypto-eddsa -c $CONF 2> taler-helper-crypto-eddsa.log & +$TLIBEXEC/taler-helper-crypto-rsa -c $CONF 2> taler-helper-crypto-rsa.log & taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log & EXCHANGE_PID=$! taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log & diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index d9c565406..8ee82ce19 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -562,8 +562,8 @@ handle_post_management (const struct TEH_RequestHandler *rh, return r404 (connection, "/management/denominations/$HDP/revoke"); if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[2], - strlen (args[2]), + GNUNET_STRINGS_string_to_data (args[1], + strlen (args[1]), &h_denom_pub, sizeof (h_denom_pub))) { @@ -571,7 +571,7 @@ handle_post_management (const struct TEH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - args[2]); + args[1]); } return TEH_handler_management_denominations_HDP_revoke (connection, &h_denom_pub, @@ -591,8 +591,8 @@ handle_post_management (const struct TEH_RequestHandler *rh, return r404 (connection, "/management/signkeys/$HDP/revoke"); if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[2], - strlen (args[2]), + GNUNET_STRINGS_string_to_data (args[1], + strlen (args[1]), &exchange_pub, sizeof (exchange_pub))) { @@ -600,7 +600,7 @@ handle_post_management (const struct TEH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - args[2]); + args[1]); } return TEH_handler_management_signkeys_EP_revoke (connection, &exchange_pub, @@ -805,7 +805,7 @@ handle_mhd_request (void *cls, { .url = "keys", .method = MHD_HTTP_METHOD_GET, - .handler.get = &TEH_handler_keys, // FIXME => TEH_keys_get_handler + .handler.get = &TEH_keys_get_handler, }, /* Requests for wiring information */ { @@ -1427,6 +1427,7 @@ run_single_request (void) } MHD_run (mhd); } + TEH_resume_keys_requests (); MHD_stop_daemon (mhd); mhd = NULL; if (cld != waitpid (cld, @@ -1463,6 +1464,7 @@ run_main_loop (int fh, = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG | MHD_USE_DUAL_STACK | MHD_USE_INTERNAL_POLLING_THREAD + | MHD_ALLOW_SUSPEND_RESUME | MHD_USE_TCP_FASTOPEN, (-1 == fh) ? serve_port : 0, NULL, NULL, @@ -1484,11 +1486,17 @@ run_main_loop (int fh, } atexit (&write_stats); - ret = TEH_KS_loop (); + ret = TEH_keys_init (); + if (GNUNET_OK == ret) + { + ret = TEH_KS_loop (); + TEH_keys_done (); + } switch (ret) { case GNUNET_OK: case GNUNET_SYSERR: + TEH_resume_keys_requests (); MHD_stop_daemon (mhd); break; case GNUNET_NO: @@ -1544,11 +1552,13 @@ run_main_loop (int fh, num_connections) sleep (1); /* Now we're really done, practice clean shutdown */ + TEH_resume_keys_requests (); MHD_stop_daemon (mhd); } break; default: GNUNET_break (0); + TEH_resume_keys_requests (); MHD_stop_daemon (mhd); break; } diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 316e565b6..497ff16a7 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -78,6 +78,11 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin; */ extern char *TEH_currency; +/** + * Are we shutting down? + */ +extern volatile bool MHD_terminating; + /** * @brief Struct describing an URL and the handler for it. */ diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index bd38c6256..e8ca04f8c 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -33,6 +33,7 @@ #include "taler-exchange-httpd_deposit.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" /** @@ -75,18 +76,18 @@ reply_deposit_success (struct MHD_Connection *connection, .coin_pub = *coin_pub, .merchant = *merchant }; + enum TALER_ErrorCode ec; TALER_amount_hton (&dc.amount_without_fee, amount_without_fee); - if (GNUNET_OK != - TEH_KS_sign (&dc, - &pub, - &sig)) + if (TALER_EC_NONE != + (ec = TEH_keys_exchange_sign (&dc, + &pub, + &sig))) { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); + return TALER_MHD_reply_with_ec (connection, + ec, + NULL); } return TALER_MHD_reply_json_pack ( connection, @@ -430,9 +431,10 @@ TEH_handler_deposit (struct MHD_Connection *connection, /* check denomination exists and is valid */ { struct TEH_KS_StateHandle *key_state; - struct TALER_EXCHANGEDB_DenominationKey *dki; + struct TEH_DenominationKey *dk; enum TALER_ErrorCode ec; unsigned int hc; + struct GNUNET_TIME_Absolute now; key_state = TEH_KS_acquire (dc.exchange_timestamp); if (NULL == key_state) @@ -444,12 +446,10 @@ TEH_handler_deposit (struct MHD_Connection *connection, TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, "no keys"); } - dki = TEH_KS_denomination_key_lookup_by_hash (key_state, - &deposit.coin.denom_pub_hash, - TEH_KS_DKU_DEPOSIT, - &ec, - &hc); - if (NULL == dki) + dk = TEH_keys_denomination_by_hash (&deposit.coin.denom_pub_hash, + &ec, + &hc); + if (NULL == dk) { TALER_LOG_DEBUG ("Unknown denomination key in /deposit request\n"); TEH_KS_release (key_state); @@ -459,8 +459,42 @@ TEH_handler_deposit (struct MHD_Connection *connection, ec, NULL); } - TALER_amount_ntoh (&deposit.deposit_fee, - &dki->issue.properties.fee_deposit); + now = GNUNET_TIME_absolute_get (); + if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) + { + /* This denomination is past the expiration time for deposits */ + TEH_KS_release (key_state); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); + } + if (now.abs_value_us < dk->meta.start.abs_value_us) + { + /* This denomination is not yet valid */ + TEH_KS_release (key_state); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_PRECONDITION_FAILED, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, + NULL); + } + if (dk->recoup_possible) + { + /* This denomination has been revoked */ + TEH_KS_release (key_state); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, + NULL); + } + + deposit.deposit_fee = dk->meta.fee_deposit; if (GNUNET_YES != TALER_amount_cmp_currency (&deposit.amount_with_fee, &deposit.deposit_fee) ) @@ -476,7 +510,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, /* check coin signature */ if (GNUNET_YES != TALER_test_coin_valid (&deposit.coin, - &dki->denom_pub)) + &dk->denom_pub)) { TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); TEH_KS_release (key_state); @@ -486,8 +520,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, NULL); } - TALER_amount_ntoh (&dc.value, - &dki->issue.properties.value); + dc.value = dk->meta.value; TEH_KS_release (key_state); } if (0 < TALER_amount_cmp (&deposit.deposit_fee, diff --git a/src/exchange/taler-exchange-httpd_deposits_get.c b/src/exchange/taler-exchange-httpd_deposits_get.c index 5b75bdcfd..a4932a1ed 100644 --- a/src/exchange/taler-exchange-httpd_deposits_get.c +++ b/src/exchange/taler-exchange-httpd_deposits_get.c @@ -27,6 +27,7 @@ #include "taler_mhd_lib.h" #include "taler_signatures.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" #include "taler-exchange-httpd_deposits_get.h" #include "taler-exchange-httpd_responses.h" @@ -65,18 +66,18 @@ reply_deposit_details (struct MHD_Connection *connection, .coin_pub = *coin_pub, .execution_time = GNUNET_TIME_absolute_hton (exec_time) }; + enum TALER_ErrorCode ec; TALER_amount_hton (&cw.coin_contribution, coin_contribution); - if (GNUNET_OK != - TEH_KS_sign (&cw, - &pub, - &sig)) + if (TALER_EC_NONE != + (ec = TEH_keys_exchange_sign (&cw, + &pub, + &sig))) { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); + return TALER_MHD_reply_with_ec (connection, + ec, + NULL); } return TALER_MHD_reply_json_pack (connection, MHD_HTTP_OK, diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 6e778677a..adc950792 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -232,17 +232,7 @@ struct SigningKey }; -/** - * Snapshot of the (coin and signing) keys (including private keys) of - * the exchange. There can be multiple instances of this struct, as it is - * reference counted and only destroyed once the last user is done - * with it. The current instance is acquired using - * #TEH_KS_acquire(). Using this function increases the - * reference count. The contents of this structure (except for the - * reference counter) should be considered READ-ONLY until it is - * ultimately destroyed (as there can be many concurrent users). - */ -struct KeyStateHandle +struct TEH_KeyStateHandle { /** @@ -307,7 +297,30 @@ struct KeyStateHandle /** - * Thread-local. Contains a pointer to `struct KeyStateHandle` or NULL. + * Entry of /keys requests that are currently suspended because we are + * waiting for /keys to become ready. + */ +struct SuspendedKeysRequests +{ + /** + * Kept in a DLL. + */ + struct SuspendedKeysRequests *next; + + /** + * Kept in a DLL. + */ + struct SuspendedKeysRequests *prev; + + /** + * The suspended connection. + */ + struct MHD_Connection *connection; +}; + + +/** + * Thread-local. Contains a pointer to `struct TEH_KeyStateHandle` or NULL. * Stores the per-thread latest generation of our key state. */ static pthread_key_t key_state; @@ -321,6 +334,16 @@ static pthread_key_t key_state; */ static volatile uint64_t key_generation; +/** + * Head of DLL of suspended /keys requests. + */ +static struct SuspendedKeysRequests *skr_head; + +/** + * Tail of DLL of suspended /keys requests. + */ +static struct SuspendedKeysRequests *skr_tail; + /** * For how long should a signing key be legally retained? * Configuration value. @@ -343,6 +366,73 @@ static struct TALER_SecurityModulePublicKeyP esign_sm_pub; */ static pthread_mutex_t sm_pub_mutex = PTHREAD_MUTEX_INITIALIZER; +/** + * Mutex protecting access to #skr_head and #skr_tail. + * (Could be split into two locks if ever needed.) + */ +static pthread_mutex_t skr_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * Are we shutting down? + */ +static bool terminating; + +/** + * Did we ever initialize #key_state? + */ +static bool key_state_available; + + +/** + * Suspend /keys request while we (hopefully) are waiting to be + * provisioned with key material. + * + * @param[in] connection to suspend + */ +static MHD_RESULT +suspend_request (struct MHD_Connection *connection) +{ + struct SuspendedKeysRequests *skr; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Suspending /keys request until key material changes\n"); + GNUNET_assert (0 == pthread_mutex_lock (&skr_mutex)); + if (terminating) + { + GNUNET_assert (0 == pthread_mutex_unlock (&skr_mutex)); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + "Exchange terminating"); + } + skr = GNUNET_new (struct SuspendedKeysRequests); + skr->connection = connection; + MHD_suspend_connection (connection); + GNUNET_CONTAINER_DLL_insert (skr_head, + skr_tail, + skr); + GNUNET_assert (0 == pthread_mutex_unlock (&skr_mutex)); + return MHD_YES; +} + + +void +TEH_resume_keys_requests (void) +{ + struct SuspendedKeysRequests *skr; + + GNUNET_assert (0 == pthread_mutex_lock (&skr_mutex)); + while (NULL != (skr = skr_head)) + { + GNUNET_CONTAINER_DLL_remove (skr_head, + skr_tail, + skr); + MHD_resume_connection (skr->connection); + GNUNET_free (skr); + } + GNUNET_assert (0 == pthread_mutex_unlock (&skr_mutex)); +} + /** * Clear memory for responses to "/keys" in @a ksh. @@ -350,7 +440,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 KeyStateHandle *ksh) +clear_response_cache (struct TEH_KeyStateHandle *ksh) { for (unsigned int i = 0; ikrd_array_length; i++) { @@ -530,6 +620,12 @@ helper_denom_cb ( struct HelperDenomination *hd; check_denom_sm_pub (sm_pub); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "RSA helper announces key %s for denomination type %s with validity %s\n", + GNUNET_h2s (h_denom_pub), + section_name, + GNUNET_STRINGS_relative_time_to_string (validity_duration, + GNUNET_NO)); hd = GNUNET_CONTAINER_multihashmap_get (hs->denom_keys, h_denom_pub); if (NULL != hd) @@ -594,6 +690,11 @@ helper_esign_cb ( struct GNUNET_PeerIdentity pid; check_esign_sm_pub (sm_pub); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "EdDSA helper announces signing key %s with validity %s\n", + TALER_B2S (exchange_pub), + GNUNET_STRINGS_relative_time_to_string (validity_duration, + GNUNET_NO)); pid.public_key = exchange_pub->eddsa_pub; hsk = GNUNET_CONTAINER_multipeermap_get (hs->esign_keys, &pid); @@ -675,7 +776,7 @@ sync_key_helpers (struct HelperState *hs) /** * Free denomination key data. * - * @param cls a `struct KeyStateHandle`, unused + * @param cls a `struct TEH_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) @@ -706,7 +807,7 @@ clear_denomination_cb (void *cls, /** * Free denomination key data. * - * @param cls a `struct KeyStateHandle`, unused + * @param cls a `struct TEH_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) @@ -733,7 +834,7 @@ clear_signkey_cb (void *cls, * @param free_helper true to also release the helper state */ static void -destroy_key_state (struct KeyStateHandle *ksh, +destroy_key_state (struct TEH_KeyStateHandle *ksh, bool free_helper) { clear_response_cache (ksh); @@ -762,12 +863,12 @@ destroy_key_state (struct KeyStateHandle *ksh, * Free all resources associated with @a cls. Called when * the respective pthread is destroyed. * - * @param[in] cls a `struct KeyStateHandle`. + * @param[in] cls a `struct TEH_KeyStateHandle`. */ static void destroy_key_state_cb (void *cls) { - struct KeyStateHandle *ksh = cls; + struct TEH_KeyStateHandle *ksh = cls; destroy_key_state (ksh, true); @@ -786,6 +887,7 @@ TEH_keys_init () pthread_key_create (&key_state, &destroy_key_state_cb)) return GNUNET_SYSERR; + key_state_available = true; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (TEH_cfg, "exchange", @@ -801,21 +903,33 @@ TEH_keys_init () } -/** - * Close down keys submodule. - */ void TEH_keys_done () { - GNUNET_assert (0 == - pthread_key_delete (key_state)); + GNUNET_assert (0 == pthread_mutex_lock (&skr_mutex)); + terminating = true; + GNUNET_assert (0 == pthread_mutex_unlock (&skr_mutex)); +} + + +/** + * Fully clean up our state. + */ +void __attribute__ ((destructor)) +TEH_keys_finished () +{ + if (key_state_available) + { + GNUNET_assert (0 == + pthread_key_delete (key_state)); + } } /** * Function called with information about the exchange's denomination keys. * - * @param cls closure with a `struct KeyStateHandle *` + * @param cls closure with a `struct TEH_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) @@ -832,7 +946,7 @@ denomination_info_cb ( const struct TALER_MasterSignatureP *master_sig, bool recoup_possible) { - struct KeyStateHandle *ksh = cls; + struct TEH_KeyStateHandle *ksh = cls; struct TEH_DenominationKey *dk; dk = GNUNET_new (struct TEH_DenominationKey); @@ -854,7 +968,7 @@ denomination_info_cb ( /** * Function called with information about the exchange's online signing keys. * - * @param cls closure with a `struct KeyStateHandle *` + * @param cls closure with a `struct TEH_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 @@ -866,7 +980,7 @@ signkey_info_cb ( const struct TALER_EXCHANGEDB_SignkeyMetaData *meta, const struct TALER_MasterSignatureP *master_sig) { - struct KeyStateHandle *ksh = cls; + struct TEH_KeyStateHandle *ksh = cls; struct SigningKey *sk; struct GNUNET_PeerIdentity pid; @@ -884,10 +998,66 @@ signkey_info_cb ( } +/** + * Closure for #get_auditor_sigs. + */ +struct GetAuditorSigsContext +{ + /** + * Where to store the matching signatures. + */ + json_t *denom_keys; + + /** + * Public key of the auditor to match against. + */ + const struct TALER_AuditorPublicKeyP *auditor_pub; +}; + + +/** + * Extract the auditor signatures matching the auditor's public + * key from the @a value and generate the respective JSON. + * + * @param cls a `struct GetAuditorSigsContext` + * @param h_denom_pub hash of the denomination public key + * @param value a `struct TEH_DenominationKey` + * @return #GNUNET_OK (continue to iterate) + */ +static int +get_auditor_sigs (void *cls, + const struct GNUNET_HashCode *h_denom_pub, + void *value) +{ + struct GetAuditorSigsContext *ctx = cls; + struct TEH_DenominationKey *dk = value; + + for (struct TEH_AuditorSignature *as = dk->as_head; + NULL != as; + as = as->next) + { + if (0 != + GNUNET_memcmp (ctx->auditor_pub, + &as->apub)) + continue; + GNUNET_break (0 == + json_array_append_new ( + ctx->denom_keys, + json_pack ( + "{s:o, s:o}", + "denom_pub_h", + GNUNET_JSON_from_data_auto (h_denom_pub), + "auditor_sig", + GNUNET_JSON_from_data_auto (&as->asig)))); + } + return GNUNET_OK; +} + + /** * Function called with information about the exchange's auditors. * - * @param cls closure with a `struct KeyStateHandle *` + * @param cls closure with a `struct TEH_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 @@ -899,18 +1069,26 @@ auditor_info_cb ( const char *auditor_url, const char *auditor_name) { - struct KeyStateHandle *ksh = cls; + struct TEH_KeyStateHandle *ksh = cls; + struct GetAuditorSigsContext ctx; + ctx.denom_keys = json_array (); + ctx.auditor_pub = auditor_pub; + GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, + &get_auditor_sigs, + &ctx); GNUNET_break (0 == json_array_append_new ( ksh->auditors, - json_pack ("{s:s, s:o, s:s}", - "name", + json_pack ("{s:s, s:o, s:s, s:o}", + "auditor_name", auditor_name, "auditor_pub", GNUNET_JSON_from_data_auto (auditor_pub), - "url", - auditor_url))); + "auditor_url", + auditor_url, + "denomination_keys", + ctx.denom_keys))); } @@ -918,7 +1096,7 @@ auditor_info_cb ( * Function called with information about the denominations * audited by the exchange's auditors. * - * @param cls closure with a `struct KeyStateHandle *` + * @param cls closure with a `struct TEH_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 @@ -930,7 +1108,7 @@ auditor_denom_cb ( const struct GNUNET_HashCode *h_denom_pub, const struct TALER_AuditorSignatureP *auditor_sig) { - struct KeyStateHandle *ksh = cls; + struct TEH_KeyStateHandle *ksh = cls; struct TEH_DenominationKey *dk; struct TEH_AuditorSignature *as; @@ -959,13 +1137,13 @@ 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 KeyStateHandle * +static struct TEH_KeyStateHandle * build_key_state (struct HelperState *hs) { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; enum GNUNET_DB_QueryStatus qs; - ksh = GNUNET_new (struct KeyStateHandle); + 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! */ @@ -1215,7 +1393,7 @@ get_date_string (struct GNUNET_TIME_Absolute at, * @return #GNUNET_OK on success */ static int -setup_general_response_headers (const struct KeyStateHandle *ksh, +setup_general_response_headers (const struct TEH_KeyStateHandle *ksh, struct MHD_Response *response) { char dat[128]; @@ -1262,9 +1440,10 @@ setup_general_response_headers (const struct KeyStateHandle *ksh, * @param signkeys list of sign keys to return * @param recoup list of revoked keys to return * @param denoms list of denominations to return + * @return #GNUNET_OK on success */ -static void -create_krd (struct KeyStateHandle *ksh, +static int +create_krd (struct TEH_KeyStateHandle *ksh, const struct GNUNET_HashCode *denom_keys_hash, struct GNUNET_TIME_Absolute last_cpd, json_t *signkeys, @@ -1284,15 +1463,23 @@ create_krd (struct KeyStateHandle *ksh, .list_issue_date = GNUNET_TIME_absolute_hton (last_cpd), .hc = *denom_keys_hash }; + enum TALER_ErrorCode ec; - TEH_keys_exchange_sign (&ks, - &exchange_pub, - &exchange_sig); + if (TALER_EC_NONE != + (ec = TEH_keys_exchange_sign (&ks, + &exchange_pub, + &exchange_sig))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Could not create key response data: cannot sign (%s)\n", + TALER_ErrorCode_get_hint (ec)); + return GNUNET_SYSERR; + } } keys = json_pack ( "{s:s, s:o, s:o, s:O, s:O," - " s:O, 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), @@ -1352,9 +1539,11 @@ create_krd (struct KeyStateHandle *ksh, setup_general_response_headers (ksh, krd.response_compressed)); } + krd.cherry_pick_date = last_cpd; GNUNET_array_append (ksh->krd_array, ksh->krd_array_length, krd); + return GNUNET_OK; } @@ -1364,11 +1553,11 @@ create_krd (struct KeyStateHandle *ksh, * This function is to recompute all (including cherry-picked) responses we * might want to return, based on the state already in @a ksh. * - * * @param[in,out] ksh state handle to update + * @return #GNUNET_OK on success */ -static void -update_keys_response (struct KeyStateHandle *ksh) +static int +update_keys_response (struct TEH_KeyStateHandle *ksh) { json_t *recoup; struct SignKeyCtx sctx; @@ -1417,12 +1606,24 @@ update_keys_response (struct KeyStateHandle *ksh) GNUNET_CRYPTO_hash_context_finish ( GNUNET_CRYPTO_hash_context_copy (hash_context), &hc); - create_krd (ksh, - &hc, - last_cpd, - sctx.signkeys, - recoup, - denoms); + if (GNUNET_OK != + create_krd (ksh, + &hc, + last_cpd, + sctx.signkeys, + recoup, + denoms)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to generate key response data for %s\n", + GNUNET_STRINGS_absolute_time_to_string (last_cpd)); + GNUNET_CRYPTO_hash_context_abort (hash_context); + GNUNET_CONTAINER_heap_destroy (heap); + json_decref (denoms); + json_decref (sctx.signkeys); + json_decref (recoup); + return GNUNET_SYSERR; + } last_cpd = dk->meta.start; } GNUNET_CRYPTO_hash_context_read (hash_context, @@ -1468,16 +1669,28 @@ update_keys_response (struct KeyStateHandle *ksh) GNUNET_CRYPTO_hash_context_finish (hash_context, &hc); - create_krd (ksh, - &hc, - last_cpd, - sctx.signkeys, - recoup, - denoms); + if (GNUNET_OK != + create_krd (ksh, + &hc, + last_cpd, + sctx.signkeys, + recoup, + denoms)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to generate key response data for %s\n", + GNUNET_STRINGS_absolute_time_to_string (last_cpd)); + json_decref (denoms); + json_decref (sctx.signkeys); + json_decref (recoup); + return GNUNET_SYSERR; + } + } json_decref (sctx.signkeys); json_decref (recoup); json_decref (denoms); + return GNUNET_OK; } @@ -1486,21 +1699,17 @@ TEH_keys_update_states () { __sync_fetch_and_add (&key_generation, 1); + TEH_resume_keys_requests (); } -/** - * 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 - */ -static struct KeyStateHandle * -get_key_state (void) +struct TEH_KeyStateHandle * +TEH_get_key_state (void) { - struct KeyStateHandle *old_ksh; - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *old_ksh; + struct TEH_KeyStateHandle *ksh; + GNUNET_assert (key_state_available); old_ksh = pthread_getspecific (key_state); if (NULL == old_ksh) { @@ -1540,21 +1749,34 @@ get_key_state (void) struct TEH_DenominationKey * -TEH_keys_denomination_by_hash ( - const struct GNUNET_HashCode *h_denom_pub, - enum TALER_ErrorCode *ec, - unsigned int *hc) +TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub, + enum TALER_ErrorCode *ec, + unsigned int *hc) { - struct KeyStateHandle *ksh; - struct TEH_DenominationKey *dk; + struct TEH_KeyStateHandle *ksh; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { *hc = MHD_HTTP_INTERNAL_SERVER_ERROR; *ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; return NULL; } + return TEH_keys_denomination_by_hash2 (ksh, + h_denom_pub, + ec, + hc); +} + + +struct TEH_DenominationKey * +TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh, + const struct GNUNET_HashCode *h_denom_pub, + enum TALER_ErrorCode *ec, + unsigned int *hc) +{ + struct TEH_DenominationKey *dk; + dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map, h_denom_pub); if (NULL == dk) @@ -1568,16 +1790,15 @@ TEH_keys_denomination_by_hash ( struct TALER_DenominationSignature -TEH_keys_denomination_sign ( - const struct GNUNET_HashCode *h_denom_pub, - const void *msg, - size_t msg_size, - enum TALER_ErrorCode *ec) +TEH_keys_denomination_sign (const struct GNUNET_HashCode *h_denom_pub, + const void *msg, + size_t msg_size, + enum TALER_ErrorCode *ec) { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; struct TALER_DenominationSignature none = { NULL }; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { *ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; @@ -1592,12 +1813,11 @@ TEH_keys_denomination_sign ( void -TEH_keys_denomination_revoke ( - const struct GNUNET_HashCode *h_denom_pub) +TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub) { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -1615,10 +1835,10 @@ TEH_keys_exchange_sign_ (const struct struct TALER_ExchangePublicKeyP *pub, struct TALER_ExchangeSignatureP *sig) { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; enum TALER_ErrorCode ec; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { /* This *can* happen if the exchange's crypto helper is not running @@ -1646,9 +1866,10 @@ TEH_keys_exchange_sign_ (const struct &pid); if (NULL == sk) { - GNUNET_break (0); /* just to be safe, zero out the (valid) signature, as the key - should no longer be used */ + should not or no longer be used */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Cannot sign, offline key signatures are missing!\n"); memset (sig, 0, sizeof (*sig)); @@ -1662,9 +1883,9 @@ TEH_keys_exchange_sign_ (const struct void TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub) { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -1742,19 +1963,19 @@ TEH_keys_get_handler (const struct TEH_RequestHandler *rh, } { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; const struct KeysResponseData *krd; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - "no key state"); + return suspend_request (connection); + } + if (GNUNET_OK != + update_keys_response (ksh)) + { + return suspend_request (connection); } - update_keys_response (ksh); krd = bsearch (&last_issue_date, ksh->krd_array, ksh->krd_array_length, @@ -1927,11 +2148,11 @@ TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub, struct TALER_DenominationPublicKey *denom_pub, struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; struct HelperDenomination *hd; int ok; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -1959,11 +2180,11 @@ int TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, struct TALER_EXCHANGEDB_SignkeyMetaData *meta) { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; struct HelperSignkey *hsk; struct GNUNET_PeerIdentity pid; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -1990,7 +2211,7 @@ struct FutureBuilderContext /** * Our key state. */ - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; /** * Array of denomination keys. @@ -2044,7 +2265,10 @@ add_future_denomkey_cb (void *cls, 0 == json_array_append_new ( fbc->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}", + json_pack ("{s:o, s:o, s:o, s:o, s:o," + " s:o, s:o, s:o, s:o, s:o," + " s:o, s:s}", + /* 1-5 */ "value", TALER_JSON_from_amount (&meta.value), "stamp_start", @@ -2055,8 +2279,9 @@ add_future_denomkey_cb (void *cls, GNUNET_JSON_from_time_abs (meta.expire_deposit), "stamp_expire_legal", GNUNET_JSON_from_time_abs (meta.expire_legal), + /* 6-10 */ "denom_pub", - GNUNET_JSON_from_rsa_public_key (dk->denom_pub.rsa_public_key), + GNUNET_JSON_from_rsa_public_key (hd->denom_pub.rsa_public_key), "fee_withdraw", TALER_JSON_from_amount (&meta.fee_withdraw), "fee_deposit", @@ -2065,8 +2290,11 @@ add_future_denomkey_cb (void *cls, TALER_JSON_from_amount (&meta.fee_refresh), "fee_refund", TALER_JSON_from_amount (&meta.fee_refund), + /* 11- */ "denom_secmod_sig", - GNUNET_JSON_from_data_auto (&hd->sm_sig)))); + GNUNET_JSON_from_data_auto (&hd->sm_sig), + "section_name", + hd->section_name))); return GNUNET_OK; } @@ -2123,10 +2351,10 @@ MHD_RESULT TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh, struct MHD_Connection *connection) { - struct KeyStateHandle *ksh; + struct TEH_KeyStateHandle *ksh; json_t *reply; - ksh = get_key_state (); + ksh = TEH_get_key_state (); if (NULL == ksh) { GNUNET_break (0); @@ -2143,6 +2371,8 @@ TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh, .signkeys = json_array () }; + GNUNET_assert (NULL != fbc.denoms); + GNUNET_assert (NULL != fbc.signkeys); GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers.denom_keys, &add_future_denomkey_cb, &fbc); @@ -2161,6 +2391,11 @@ TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh, GNUNET_JSON_from_data_auto (&denom_sm_pub), "signkey_secmod_public_key", GNUNET_JSON_from_data_auto (&esign_sm_pub)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Returning GET /management/keys response:\n"); + json_dumpf (reply, + stderr, + JSON_INDENT (2)); if (NULL == reply) return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, diff --git a/src/exchange/taler-exchange-httpd_keys.h b/src/exchange/taler-exchange-httpd_keys.h index 5a1314f12..6966290f3 100644 --- a/src/exchange/taler-exchange-httpd_keys.h +++ b/src/exchange/taler-exchange-httpd_keys.h @@ -83,6 +83,33 @@ struct TEH_DenominationKey }; +/** + * Snapshot of the (coin and signing) keys (including private keys) of + * the exchange. There can be multiple instances of this struct, as it is + * reference counted and only destroyed once the last user is done + * with it. The current instance is acquired using + * #TEH_KS_acquire(). Using this function increases the + * reference count. The contents of this structure (except for the + * reference counter) should be considered READ-ONLY until it is + * ultimately destroyed (as there can be many concurrent users). + */ +struct TEH_KeyStateHandle; + + +/** + * Return the current key state for this thread. Possibly re-builds the key + * state if we have reason to believe that something changed. + * + * The result is ONLY valid until the next call to + * #TEH_keys_denomination_by_hash() or #TEH_get_key_state() + * or #TEH_keys_exchange_sign(). + * + * @return NULL on error + */ +struct TEH_KeyStateHandle * +TEH_get_key_state (void); + + /** * Something changed in the database. Rebuild all key states. This function * should be called if the exchange learns about a new signature from an @@ -109,12 +136,30 @@ TEH_keys_update_states (void); * or NULL if @a h_denom_pub could not be found */ struct TEH_DenominationKey * -TEH_keys_denomination_by_hash ( - const struct GNUNET_HashCode *h_denom_pub, - enum TALER_ErrorCode *ec, - unsigned int *hc); +TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub, + enum TALER_ErrorCode *ec, + unsigned int *hc); +/** + * Look up the issue for a denom public key using a given @a ksh. 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 key_state state to look in + * @param h_denom_pub hash of denomination public key + * @param[out] ec set to the error code, in case the operation failed + * @param[out] hc set to the HTTP status code to use + * @return the denomination key issue, + * or NULL if @a h_denom_pub could not be found + */ +struct TEH_DenominationKey * +TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh, + const struct GNUNET_HashCode *h_denom_pub, + enum TALER_ErrorCode *ec, + unsigned int *hc); + /** * Request to sign @a msg using the public key corresponding to * @a h_denom_pub. @@ -127,11 +172,10 @@ TEH_keys_denomination_by_hash ( * see @a ec for details about the failure */ struct TALER_DenominationSignature -TEH_keys_denomination_sign ( - const struct GNUNET_HashCode *h_denom_pub, - const void *msg, - size_t msg_size, - enum TALER_ErrorCode *ec); +TEH_keys_denomination_sign (const struct GNUNET_HashCode *h_denom_pub, + const void *msg, + size_t msg_size, + enum TALER_ErrorCode *ec); /** @@ -146,8 +190,17 @@ TEH_keys_denomination_sign ( * @param h_denom_pub hash of the public key to revoke */ void -TEH_keys_denomination_revoke ( - const struct GNUNET_HashCode *h_denom_pub); +TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub); + + +/** + * Resumse all suspended /keys requests, we may now have key material + * (or are shuting down). + * + * @param[in] connection to suspend + */ +void +TEH_resume_keys_requests (void); /** diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c index 9491234e9..8d5a18510 100644 --- a/src/exchange/taler-exchange-httpd_keystate.c +++ b/src/exchange/taler-exchange-httpd_keystate.c @@ -2054,7 +2054,7 @@ TEH_KS_denomination_key_lookup_by_hash ( GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as time to create coins has passed\n", GNUNET_h2s (denom_pub_hash)); - *ec = TALER_EC_EXCHANGE_WITHDRAW_VALIDITY_IN_PAST; + *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED; *hc = MHD_HTTP_GONE; return NULL; } diff --git a/src/exchange/taler-exchange-httpd_management_auditors.c b/src/exchange/taler-exchange-httpd_management_auditors.c index 33f1c6df3..1a2494dae 100644 --- a/src/exchange/taler-exchange-httpd_management_auditors.c +++ b/src/exchange/taler-exchange-httpd_management_auditors.c @@ -103,7 +103,8 @@ add_auditor (void *cls, "lookup auditor"); return qs; } - if (last_date.abs_value_us > aac->validity_start.abs_value_us) + if ( (0 < qs) && + (last_date.abs_value_us > aac->validity_start.abs_value_us) ) { *mhd_ret = TALER_MHD_reply_with_error ( connection, diff --git a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c index 75ce3d76b..8fb5b0839 100644 --- a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c +++ b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c @@ -29,6 +29,7 @@ #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" MHD_RESULT @@ -81,7 +82,7 @@ TEH_handler_management_denominations_HDP_revoke ( TALER_EC_GENERIC_DB_STORE_FAILED, "denomination revocation"); } - // FIXME: also update our '/keys' replies! (signal all threads!?!?) + TEH_keys_update_states (); return TALER_MHD_reply_static ( connection, MHD_HTTP_NO_CONTENT, diff --git a/src/exchange/taler-exchange-httpd_management_post_keys.c b/src/exchange/taler-exchange-httpd_management_post_keys.c index 06750716e..84ec1f531 100644 --- a/src/exchange/taler-exchange-httpd_management_post_keys.c +++ b/src/exchange/taler-exchange-httpd_management_post_keys.c @@ -212,6 +212,9 @@ add_keys (void *cls, "activate denomination key"); return qs; } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Added offline signature for denomination `%s'\n", + GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); GNUNET_assert (0 != qs); } @@ -296,9 +299,11 @@ add_keys (void *cls, "activate signing key"); return qs; } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Added offline signature for signing key `%s'\n", + TALER_B2S (&akc->s_sigs[i].exchange_pub)); GNUNET_assert (0 != qs); } - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ } @@ -382,9 +387,9 @@ TEH_handler_management_post_keys ( return ret; } akc.ns_sigs = json_array_size (signkey_sigs); - akc.s_sigs = GNUNET_new_array (akc.nd_sigs, + akc.s_sigs = GNUNET_new_array (akc.ns_sigs, struct SigningSig); - for (unsigned int i = 0; i awc->validity_start.abs_value_us) + if ( (0 < qs) && + (last_date.abs_value_us > awc->validity_start.abs_value_us) ) { *mhd_ret = TALER_MHD_reply_with_error ( connection, diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c index 8b5914e2d..76cf52ebe 100644 --- a/src/exchange/taler-exchange-httpd_melt.c +++ b/src/exchange/taler-exchange-httpd_melt.c @@ -30,6 +30,7 @@ #include "taler-exchange-httpd_melt.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" /** @@ -107,16 +108,16 @@ reply_melt_success (struct MHD_Connection *connection, .rc = *rc, .noreveal_index = htonl (noreveal_index) }; + enum TALER_ErrorCode ec; - if (GNUNET_OK != - TEH_KS_sign (&body, - &pub, - &sig)) + if (TALER_EC_NONE != + (ec = TEH_keys_exchange_sign (&body, + &pub, + &sig))) { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); + return TALER_MHD_reply_with_ec (connection, + ec, + NULL); } return TALER_MHD_reply_json_pack ( connection, @@ -477,94 +478,94 @@ check_for_denomination_key (struct MHD_Connection *connection, { /* Baseline: check if deposits/refreshs are generally simply still allowed for this denomination */ - struct TALER_EXCHANGEDB_DenominationKey *dki; + struct TEH_DenominationKey *dk; unsigned int hc; enum TALER_ErrorCode ec; + struct GNUNET_TIME_Absolute now; - dki = TEH_KS_denomination_key_lookup_by_hash ( - key_state, + dk = TEH_keys_denomination_by_hash ( &rmc->refresh_session.coin.denom_pub_hash, - TEH_KS_DKU_DEPOSIT, &ec, &hc); - /* Consider case that denomination was revoked but - this coin was already seen and thus refresh is OK. */ - if (NULL == dki) + if (NULL == dk) { - dki = TEH_KS_denomination_key_lookup_by_hash ( - key_state, - &rmc->refresh_session.coin.denom_pub_hash, - TEH_KS_DKU_RECOUP, - &ec, - &hc); - if (NULL != dki) - { - struct GNUNET_HashCode denom_hash; - enum GNUNET_DB_QueryStatus qs; - - /* Check that the coin is dirty (we have seen it before), as we will - not just allow melting of a *fresh* coin where the denomination was - revoked (those must be recouped) */ - qs = TEH_plugin->get_coin_denomination ( - TEH_plugin->cls, - NULL, - &rmc->refresh_session.coin.coin_pub, - &denom_hash); - if (0 > qs) - { - TEH_KS_release (key_state); - /* There is no good reason for a serialization failure here: */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "coin denomination"); - } - /* sanity check */ - GNUNET_break (0 == - GNUNET_memcmp (&denom_hash, - &rmc->refresh_session.coin.denom_pub_hash)); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - /* We never saw this coin before, so _this_ justification is not OK */ - dki = NULL; - } - else - { - /* Minor optimization: no need to run the - "ensure_coin_known" part of the transaction */ - rmc->coin_is_dirty = true; - } - } + TEH_KS_release (key_state); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, + NULL); } - - /* Consider the case that the denomination expired for deposits, but - recoup of a refreshed coin refilled the balance of the 'zombie' coin - and we should thus allow the refresh during the legal period. */ - if (NULL == dki) + now = GNUNET_TIME_absolute_get (); + if (now.abs_value_us >= dk->meta.expire_legal.abs_value_us) { - dki = TEH_KS_denomination_key_lookup_by_hash (key_state, - &rmc->refresh_session.coin. - denom_pub_hash, - TEH_KS_DKU_ZOMBIE, - &ec, - &hc); - if (NULL != dki) - rmc->zombie_required = true; /* check later that zombie is satisfied */ + /* Way too late now, even zombies have expired */ + TEH_KS_release (key_state); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); } - if (NULL == dki) + if (now.abs_value_us < dk->meta.start.abs_value_us) { + /* This denomination is not yet valid */ TEH_KS_release (key_state); - return TALER_MHD_reply_with_error (connection, - hc, - ec, - NULL); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_PRECONDITION_FAILED, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, + NULL); + } + if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) + { + /* We are past deposit expiration time, but maybe this is a zombie? */ + struct GNUNET_HashCode denom_hash; + enum GNUNET_DB_QueryStatus qs; + + /* Check that the coin is dirty (we have seen it before), as we will + not just allow melting of a *fresh* coin where the denomination was + revoked (those must be recouped) */ + qs = TEH_plugin->get_coin_denomination ( + TEH_plugin->cls, + NULL, + &rmc->refresh_session.coin.coin_pub, + &denom_hash); + if (0 > qs) + { + TEH_KS_release (key_state); + /* There is no good reason for a serialization failure here: */ + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "coin denomination"); + } + /* sanity check */ + GNUNET_break (0 == + GNUNET_memcmp (&denom_hash, + &rmc->refresh_session.coin.denom_pub_hash)); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + /* We never saw this coin before, so _this_ justification is not OK */ + TEH_KS_release (key_state); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); + } + else + { + /* Minor optimization: no need to run the + "ensure_coin_known" part of the transaction */ + rmc->coin_is_dirty = true; + } + rmc->zombie_required = true; /* check later that zombie is satisfied */ } - TALER_amount_ntoh (&rmc->coin_refresh_fee, - &dki->issue.properties.fee_refresh); - TALER_amount_ntoh (&rmc->coin_value, - &dki->issue.properties.value); + rmc->coin_refresh_fee = dk->meta.fee_refresh; + rmc->coin_value = dk->meta.value; /* check client used sane currency */ if (GNUNET_YES != TALER_amount_cmp_currency (&rmc->refresh_session.amount_with_fee, @@ -581,7 +582,7 @@ check_for_denomination_key (struct MHD_Connection *connection, /* check coin is actually properly signed */ if (GNUNET_OK != TALER_test_coin_valid (&rmc->refresh_session.coin, - &dki->denom_pub)) + &dk->denom_pub)) { GNUNET_break_op (0); TEH_KS_release (key_state); diff --git a/src/exchange/taler-exchange-httpd_recoup.c b/src/exchange/taler-exchange-httpd_recoup.c index fe8b8d603..aa521d66b 100644 --- a/src/exchange/taler-exchange-httpd_recoup.c +++ b/src/exchange/taler-exchange-httpd_recoup.c @@ -31,6 +31,7 @@ #include "taler-exchange-httpd_recoup.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" /** @@ -359,7 +360,7 @@ verify_and_execute_recoup (struct MHD_Connection *connection, int refreshed) { struct RecoupContext pc; - const struct TALER_EXCHANGEDB_DenominationKey *dki; + const struct TEH_DenominationKey *dk; struct GNUNET_HashCode c_hash; void *coin_ev; size_t coin_ev_size; @@ -369,6 +370,7 @@ verify_and_execute_recoup (struct MHD_Connection *connection, /* check denomination exists and is in recoup mode */ { struct TEH_KS_StateHandle *key_state; + struct GNUNET_TIME_Absolute now; key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); if (NULL == key_state) @@ -379,12 +381,10 @@ verify_and_execute_recoup (struct MHD_Connection *connection, TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, "no keys"); } - dki = TEH_KS_denomination_key_lookup_by_hash (key_state, - &coin->denom_pub_hash, - TEH_KS_DKU_RECOUP, - &ec, - &hc); - if (NULL == dki) + dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash, + &ec, + &hc); + if (NULL == dk) { TEH_KS_release (key_state); TALER_LOG_WARNING ( @@ -394,13 +394,45 @@ verify_and_execute_recoup (struct MHD_Connection *connection, ec, NULL); } - TALER_amount_ntoh (&pc.value, - &dki->issue.properties.value); + + now = GNUNET_TIME_absolute_get (); + if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) + { + /* This denomination is past the expiration time for recoup */ + TEH_KS_release (key_state); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); + } + if (now.abs_value_us < dk->meta.start.abs_value_us) + { + /* This denomination is not yet valid */ + TEH_KS_release (key_state); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_PRECONDITION_FAILED, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, + NULL); + } + if (! dk->recoup_possible) + { + /* This denomination is not eligible for recoup */ + TEH_KS_release (key_state); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_RECOUP_NOT_ELIGIBLE, + NULL); + } + + pc.value = dk->meta.value; /* check denomination signature */ if (GNUNET_YES != TALER_test_coin_valid (coin, - &dki->denom_pub)) + &dk->denom_pub)) { TALER_LOG_WARNING ("Invalid coin passed for recoup\n"); TEH_KS_release (key_state); @@ -416,7 +448,7 @@ verify_and_execute_recoup (struct MHD_Connection *connection, .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP), .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)), .coin_pub = coin->coin_pub, - .h_denom_pub = dki->issue.properties.denom_hash, + .h_denom_pub = coin->denom_pub_hash, .coin_blind = *coin_bks }; @@ -440,7 +472,7 @@ verify_and_execute_recoup (struct MHD_Connection *connection, if (GNUNET_YES != TALER_rsa_blind (&c_hash, &coin_bks->bks, - dki->denom_pub.rsa_public_key, + dk->denom_pub.rsa_public_key, &coin_ev, &coin_ev_size)) { diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index 6440f6dd0..9b3a42f97 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -29,6 +29,7 @@ #include "taler-exchange-httpd_refreshes_reveal.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" /** @@ -132,7 +133,7 @@ struct RevealContext /** * Denominations being requested. */ - const struct TALER_EXCHANGEDB_DenominationKey **dkis; + const struct TEH_DenominationKey **dks; /** * Envelopes to be signed. @@ -151,7 +152,7 @@ struct RevealContext struct TALER_DenominationSignature *ev_sigs; /** - * Size of the @e dkis, @e rcds and @e ev_sigs arrays (if non-NULL). + * Size of the @e dks, @e rcds and @e ev_sigs arrays (if non-NULL). */ unsigned int num_fresh_coins; @@ -367,7 +368,7 @@ refreshes_reveal_transaction (void *cls, struct TALER_PlanchetDetail pd; struct GNUNET_HashCode c_hash; - rcd->dk = &rctx->dkis[j]->denom_pub; + rcd->dk = &rctx->dks[j]->denom_pub; TALER_planchet_setup_refresh (&ts, j, &ps); @@ -432,18 +433,12 @@ refreshes_reveal_transaction (void *cls, refresh_cost = melt.melt_fee; for (unsigned int i = 0; inum_fresh_coins; i++) { - struct TALER_Amount fee_withdraw; - struct TALER_Amount value; struct TALER_Amount total; - TALER_amount_ntoh (&fee_withdraw, - &rctx->dkis[i]->issue.properties.fee_withdraw); - TALER_amount_ntoh (&value, - &rctx->dkis[i]->issue.properties.value); if ( (0 > TALER_amount_add (&total, - &fee_withdraw, - &value)) || + &rctx->dks[i]->meta.fee_withdraw, + &rctx->dks[i]->meta.value)) || (0 > TALER_amount_add (&refresh_cost, &refresh_cost, @@ -499,7 +494,7 @@ refreshes_reveal_persist (void *cls, { struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i]; - rrc->denom_pub = rctx->dkis[i]->denom_pub; + rrc->denom_pub = rctx->dks[i]->denom_pub; rrc->orig_coin_link_sig = rctx->link_sigs[i]; rrc->coin_ev = rctx->rcds[i].coin_ev; rrc->coin_ev_size = rctx->rcds[i].coin_ev_size; @@ -546,20 +541,31 @@ resolve_refreshes_reveal_denominations (struct TEH_KS_StateHandle *key_state, { unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); /* We know num_fresh_coins is bounded by #MAX_FRESH_COINS, so this is safe */ - const struct TALER_EXCHANGEDB_DenominationKey *dkis[num_fresh_coins]; - struct GNUNET_HashCode dki_h[num_fresh_coins]; + const struct TEH_DenominationKey *dks[num_fresh_coins]; + struct GNUNET_HashCode dk_h[num_fresh_coins]; struct TALER_RefreshCoinData rcds[num_fresh_coins]; struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins]; struct TALER_EXCHANGEDB_Melt melt; enum GNUNET_GenericReturnValue res; MHD_RESULT ret; + struct TEH_KeyStateHandle *ksh; + struct GNUNET_TIME_Absolute now; + ksh = TEH_get_key_state (); + if (NULL == ksh) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + NULL); + } /* Parse denomination key hashes */ + now = GNUNET_TIME_absolute_get (); for (unsigned int i = 0; idenom_priv.rsa_private_key); + + if (now.abs_value_us >= dks[i]->meta.expire_withdraw.abs_value_us) + { + /* This denomination is past the expiration time for withdraws */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); + } + if (now.abs_value_us < dks[i]->meta.start.abs_value_us) + { + /* This denomination is not yet valid */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_PRECONDITION_FAILED, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, + NULL); + } + if (dks[i]->recoup_possible) + { + /* This denomination has been revoked */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, + NULL); + } } /* Parse coin envelopes */ @@ -613,7 +643,7 @@ resolve_refreshes_reveal_denominations (struct TEH_KS_StateHandle *key_state, GNUNET_free (rcds[j].coin_ev); return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - rcd->dk = &dkis[i]->denom_pub; + rcd->dk = &dks[i]->denom_pub; } /* lookup old_coin_pub in database */ @@ -672,7 +702,7 @@ resolve_refreshes_reveal_denominations (struct TEH_KS_StateHandle *key_state, struct TALER_LinkDataPS ldp = { .purpose.size = htonl (sizeof (ldp)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK), - .h_denom_pub = dki_h[i], + .h_denom_pub = dk_h[i], .old_coin_pub = melt.session.coin.coin_pub, .transfer_pub = rctx->gamma_tp }; @@ -699,7 +729,7 @@ resolve_refreshes_reveal_denominations (struct TEH_KS_StateHandle *key_state, rctx->num_fresh_coins = num_fresh_coins; rctx->rcds = rcds; - rctx->dkis = dkis; + rctx->dks = dks; rctx->link_sigs = link_sigs; /* sign _early_ (optimistic!) to keep out of transaction scope! */ @@ -707,18 +737,20 @@ resolve_refreshes_reveal_denominations (struct TEH_KS_StateHandle *key_state, struct TALER_DenominationSignature); for (unsigned int i = 0; inum_fresh_coins; i++) { - rctx->ev_sigs[i].rsa_signature - = GNUNET_CRYPTO_rsa_sign_blinded ( - rctx->dkis[i]->denom_priv.rsa_private_key, + enum TALER_ErrorCode ec; + + rctx->ev_sigs[i] + = TEH_keys_denomination_sign ( + &dk_h[i], rctx->rcds[i].coin_ev, - rctx->rcds[i].coin_ev_size); + rctx->rcds[i].coin_ev_size, + &ec); if (NULL == rctx->ev_sigs[i].rsa_signature) { GNUNET_break (0); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_SIGNING_ERROR, - NULL); + ret = TALER_MHD_reply_with_ec (connection, + ec, + NULL); goto cleanup; } } diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c index 1dfd8931b..6bb943483 100644 --- a/src/exchange/taler-exchange-httpd_refund.c +++ b/src/exchange/taler-exchange-httpd_refund.c @@ -33,6 +33,7 @@ #include "taler-exchange-httpd_refund.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" /** @@ -58,18 +59,18 @@ reply_refund_success (struct MHD_Connection *connection, .merchant = refund->merchant_pub, .rtransaction_id = GNUNET_htonll (refund->rtransaction_id) }; + enum TALER_ErrorCode ec; TALER_amount_hton (&rc.refund_amount, &refund->refund_amount); - if (GNUNET_OK != - TEH_KS_sign (&rc, - &pub, - &sig)) + if (TALER_EC_NONE != + (ec = TEH_keys_exchange_sign (&rc, + &pub, + &sig))) { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no online signing key"); + return TALER_MHD_reply_with_ec (connection, + ec, + NULL); } return TALER_MHD_reply_json_pack ( connection, @@ -460,16 +461,14 @@ verify_and_execute_refund (struct MHD_Connection *connection, } /* Obtain information about the coin's denomination! */ { - struct TALER_EXCHANGEDB_DenominationKey *dki; + struct TEH_DenominationKey *dk; unsigned int hc; enum TALER_ErrorCode ec; - dki = TEH_KS_denomination_key_lookup_by_hash (key_state, - &denom_hash, - TEH_KS_DKU_DEPOSIT, - &ec, - &hc); - if (NULL == dki) + dk = TEH_keys_denomination_by_hash (&denom_hash, + &ec, + &hc); + if (NULL == dk) { /* DKI not found, but we do have a coin with this DK in our database; not good... */ @@ -480,8 +479,19 @@ verify_and_execute_refund (struct MHD_Connection *connection, ec, NULL); } - TALER_amount_ntoh (&refund->details.refund_fee, - &dki->issue.properties.fee_refund); + + if (GNUNET_TIME_absolute_get ().abs_value_us >= + dk->meta.expire_deposit.abs_value_us) + { + /* This denomination is past the expiration time for deposits, and thus refunds */ + TEH_KS_release (key_state); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); + } + refund->details.refund_fee = dk->meta.fee_refund; } TEH_KS_release (key_state); } diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 32b44ffca..c0ec6d95d 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -29,6 +29,7 @@ #include "taler_json_lib.h" #include "taler_mhd_lib.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" /** @@ -256,10 +257,10 @@ TEH_RESPONSE_compile_transaction_history ( TALER_amount_hton (&pc.recoup_amount, &pr->value); - if (GNUNET_OK != - TEH_KS_sign (&pc, - &epub, - &esig)) + if (TALER_EC_NONE != + TEH_keys_exchange_sign (&pc, + &epub, + &esig)) { GNUNET_break (0); json_decref (history); @@ -309,10 +310,10 @@ TEH_RESPONSE_compile_transaction_history ( TALER_amount_hton (&pc.recoup_amount, &recoup->value); - if (GNUNET_OK != - TEH_KS_sign (&pc, - &epub, - &esig)) + if (TALER_EC_NONE != + TEH_keys_exchange_sign (&pc, + &epub, + &esig)) { GNUNET_break (0); json_decref (history); @@ -366,10 +367,10 @@ TEH_RESPONSE_compile_transaction_history ( TALER_amount_hton (&pc.recoup_amount, &pr->value); - if (GNUNET_OK != - TEH_KS_sign (&pc, - &epub, - &esig)) + if (TALER_EC_NONE != + TEH_keys_exchange_sign (&pc, + &epub, + &esig)) { GNUNET_break (0); json_decref (history); @@ -610,10 +611,10 @@ TEH_RESPONSE_compile_reserve_history ( TALER_amount_hton (&pc.recoup_amount, &recoup->value); - if (GNUNET_OK != - TEH_KS_sign (&pc, - &pub, - &sig)) + if (TALER_EC_NONE != + TEH_keys_exchange_sign (&pc, + &pub, + &sig)) { GNUNET_break (0); json_decref (json_history); @@ -686,10 +687,10 @@ TEH_RESPONSE_compile_reserve_history ( GNUNET_CRYPTO_hash (closing->receiver_account_details, strlen (closing->receiver_account_details) + 1, &rcc.h_wire); - if (GNUNET_OK != - TEH_KS_sign (&rcc, - &pub, - &sig)) + if (TALER_EC_NONE != + TEH_keys_exchange_sign (&rcc, + &pub, + &sig)) { GNUNET_break (0); json_decref (json_history); diff --git a/src/exchange/taler-exchange-httpd_transfers_get.c b/src/exchange/taler-exchange-httpd_transfers_get.c index b5237df25..b7f24f23f 100644 --- a/src/exchange/taler-exchange-httpd_transfers_get.c +++ b/src/exchange/taler-exchange-httpd_transfers_get.c @@ -24,6 +24,7 @@ #include #include #include "taler_signatures.h" +#include "taler-exchange-httpd_keys.h" #include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_transfers_get.h" #include "taler-exchange-httpd_responses.h" @@ -99,6 +100,7 @@ reply_transfer_details (struct MHD_Connection *connection, struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangeSignatureP sig; + GNUNET_TIME_round_abs (&exec_time); deposits = json_array (); if (NULL == deposits) @@ -158,16 +160,19 @@ reply_transfer_details (struct MHD_Connection *connection, wdp.h_wire = *h_wire; GNUNET_CRYPTO_hash_context_finish (hash_context, &wdp.h_details); - if (GNUNET_OK != - TEH_KS_sign (&wdp, - &pub, - &sig)) { - json_decref (deposits); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); + enum TALER_ErrorCode ec; + + if (TALER_EC_NONE != + (ec = TEH_keys_exchange_sign (&wdp, + &pub, + &sig))) + { + json_decref (deposits); + return TALER_MHD_reply_with_ec (connection, + ec, + NULL); + } } return TALER_MHD_reply_json_pack (connection, diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c index 0b5eb7370..035273bc6 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.c +++ b/src/exchange/taler-exchange-httpd_withdraw.c @@ -31,6 +31,7 @@ #include "taler-exchange-httpd_withdraw.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_keys.h" /** @@ -133,11 +134,6 @@ struct WithdrawContext */ size_t blinded_msg_len; - /** - * Details about denomination we are about to withdraw. - */ - struct TALER_EXCHANGEDB_DenominationKey *dki; - /** * Set to the resulting signed coin data to be returned to the client. */ @@ -291,17 +287,19 @@ withdraw_transaction (void *cls, #if ! OPTIMISTIC_SIGN if (NULL == wc->collectable.sig.rsa_signature) { - wc->collectable.sig.rsa_signature - = GNUNET_CRYPTO_rsa_sign_blinded (wc->dki->denom_priv.rsa_private_key, - wc->blinded_msg, - wc->blinded_msg_len); + enum TALER_ErrorCode ec; + + wc->collectable.sig + = TEH_keys_denomination_sign (&wc->denom_pub_hash, + wc->blinded_msg, + wc->blinded_msg_len, + &ec); if (NULL == wc->collectable.sig.rsa_signature) { GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_WITHDRAW_SIGNATURE_FAILED, - NULL); + *mhd_ret = TALER_MHD_reply_with_ec (connection, + ec, + NULL); return GNUNET_DB_STATUS_HARD_ERROR; } } @@ -360,6 +358,8 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, &wc.denom_pub_hash), GNUNET_JSON_spec_end () }; + enum TALER_ErrorCode ec; + struct TEH_DenominationKey *dk; (void) rh; if (GNUNET_OK != @@ -397,13 +397,12 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, { unsigned int hc; enum TALER_ErrorCode ec; + struct GNUNET_TIME_Absolute now; - wc.dki = TEH_KS_denomination_key_lookup_by_hash (wc.key_state, - &wc.denom_pub_hash, - TEH_KS_DKU_WITHDRAW, - &ec, - &hc); - if (NULL == wc.dki) + dk = TEH_keys_denomination_by_hash (&wc.denom_pub_hash, + &ec, + &hc); + if (NULL == dk) { GNUNET_JSON_parse_free (spec); TEH_KS_release (wc.key_state); @@ -412,20 +411,47 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, ec, NULL); } + now = GNUNET_TIME_absolute_get (); + if (now.abs_value_us >= dk->meta.expire_withdraw.abs_value_us) + { + /* This denomination is past the expiration time for withdraws */ + TEH_KS_release (wc.key_state); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); + } + if (now.abs_value_us < dk->meta.start.abs_value_us) + { + /* This denomination is not yet valid */ + TEH_KS_release (wc.key_state); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_PRECONDITION_FAILED, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, + NULL); + } + if (dk->recoup_possible) + { + /* This denomination has been revoked */ + TEH_KS_release (wc.key_state); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, + NULL); + } } - GNUNET_assert (NULL != wc.dki->denom_priv.rsa_private_key); - { - struct TALER_Amount amount; - struct TALER_Amount fee_withdraw; - TALER_amount_ntoh (&amount, - &wc.dki->issue.properties.value); - TALER_amount_ntoh (&fee_withdraw, - &wc.dki->issue.properties.fee_withdraw); + { if (0 > TALER_amount_add (&wc.amount_required, - &amount, - &fee_withdraw)) + &dk->meta.value, + &dk->meta.fee_withdraw)) { GNUNET_JSON_parse_free (spec); TEH_KS_release (wc.key_state); @@ -466,19 +492,19 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, #if OPTIMISTIC_SIGN /* Sign before transaction! */ - wc.collectable.sig.rsa_signature - = GNUNET_CRYPTO_rsa_sign_blinded (wc.dki->denom_priv.rsa_private_key, - wc.blinded_msg, - wc.blinded_msg_len); + wc.collectable.sig + = TEH_keys_denomination_sign (&wc.denom_pub_hash, + wc.blinded_msg, + wc.blinded_msg_len, + &ec); if (NULL == wc.collectable.sig.rsa_signature) { GNUNET_break (0); GNUNET_JSON_parse_free (spec); TEH_KS_release (wc.key_state); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_WITHDRAW_SIGNATURE_FAILED, - NULL); + return TALER_MHD_reply_with_ec (connection, + ec, + NULL); } #endif diff --git a/src/exchange/test_taler_exchange_httpd.conf b/src/exchange/test_taler_exchange_httpd.conf index 23307cd2e..b0e3801fb 100644 --- a/src/exchange/test_taler_exchange_httpd.conf +++ b/src/exchange/test_taler_exchange_httpd.conf @@ -18,6 +18,7 @@ TERMS_DIR = ../../contrib/tos # Etag / filename for the terms of service. TERMS_ETAG = 0 +SIGNKEY_LEGAL_DURATION = 2 years # Directory with our privacy policy. PRIVACY_DIR = ../../contrib/pp diff --git a/src/exchange/test_taler_exchange_httpd.get b/src/exchange/test_taler_exchange_httpd.get index 28a9e9bc9..c9058c870 100644 --- a/src/exchange/test_taler_exchange_httpd.get +++ b/src/exchange/test_taler_exchange_httpd.get @@ -20,7 +20,7 @@ # / /agpl -/keys +/seed /robots.txt /terms /privacy diff --git a/src/exchange/test_taler_exchange_httpd.sh b/src/exchange/test_taler_exchange_httpd.sh index dabe79cb7..943488911 100755 --- a/src/exchange/test_taler_exchange_httpd.sh +++ b/src/exchange/test_taler_exchange_httpd.sh @@ -31,10 +31,6 @@ PREFIX= # Setup database taler-exchange-dbinit -c test_taler_exchange_httpd.conf &> /dev/null -# Setup keys. -taler-exchange-keyup -c test_taler_exchange_httpd.conf || exit 1 -# Setup wire accounts. -taler-exchange-wire -c test_taler_exchange_httpd.conf > /dev/null || exit 1 # Run Exchange HTTPD (in background) $PREFIX taler-exchange-httpd -c test_taler_exchange_httpd.conf 2> test-exchange.log & @@ -45,7 +41,7 @@ do echo -n "." sleep 0.1 OK=1 - wget http://localhost:8081/ -o /dev/null -O /dev/null >/dev/null && break + wget http://localhost:8081/seed -o /dev/null -O /dev/null >/dev/null && break OK=0 done if [ 1 != $OK ] diff --git a/src/exchange/test_taler_exchange_httpd_restart.sh b/src/exchange/test_taler_exchange_httpd_restart.sh index a8976fb0c..2897127fb 100755 --- a/src/exchange/test_taler_exchange_httpd_restart.sh +++ b/src/exchange/test_taler_exchange_httpd_restart.sh @@ -53,10 +53,6 @@ PREFIX= # Setup database taler-exchange-dbinit -c test_taler_exchange_unix.conf &> /dev/null -# Setup keys. -taler-exchange-keyup -c test_taler_exchange_unix.conf || exit 1 -# Setup wire accounts. -taler-exchange-wire -c test_taler_exchange_unix.conf > /dev/null || exit 1 # Run Exchange HTTPD (in background) $PREFIX taler-exchange-httpd -c test_taler_exchange_unix.conf 2> test-exchange.log & @@ -79,13 +75,6 @@ then fi echo " DONE" -# Finally run test... -echo -n "Reloading keys ..." -kill -SIGUSR1 $! -sleep 1 -curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null || exit_fail "SIGUSR1 killed HTTP service" -echo " DONE" - # Finally run test... echo -n "Restarting program ..." kill -SIGHUP $! diff --git a/src/exchange/test_taler_exchange_unix.conf b/src/exchange/test_taler_exchange_unix.conf index bc870d4bb..1a65d801c 100644 --- a/src/exchange/test_taler_exchange_unix.conf +++ b/src/exchange/test_taler_exchange_unix.conf @@ -15,6 +15,7 @@ TERMS_DIR = ../../contrib/tos # Etag / filename for the terms of service. TERMS_ETAG = 0 +SIGNKEY_LEGAL_DURATION = 2 years # Directory with our privacy policy. PRIVACY_DIR = ../../contrib/pp diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h index 09e22f9e1..c1b65126c 100644 --- a/src/include/taler_mhd_lib.h +++ b/src/include/taler_mhd_lib.h @@ -143,6 +143,21 @@ TALER_MHD_reply_with_error (struct MHD_Connection *connection, const char *detail); +/** + * Send a response indicating an error. The HTTP status code is + * to be derived from the @a ec. + * + * @param connection the MHD connection to use + * @param ec error code uniquely identifying the error + * @param detail additional optional detail about the error + * @return a MHD result code + */ +MHD_RESULT +TALER_MHD_reply_with_ec (struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + const char *detail); + + /** * Make JSON response object. * diff --git a/src/mhd/mhd_responses.c b/src/mhd/mhd_responses.c index 5ed82cd2a..9e6986a37 100644 --- a/src/mhd/mhd_responses.c +++ b/src/mhd/mhd_responses.c @@ -442,6 +442,37 @@ TALER_MHD_reply_with_error (struct MHD_Connection *connection, } +/** + * Send a response indicating an error. The HTTP status code is + * to be derived from the @a ec. + * + * @param connection the MHD connection to use + * @param ec error code uniquely identifying the error + * @param detail additional optional detail about the error + * @return a MHD result code + */ +MHD_RESULT +TALER_MHD_reply_with_ec (struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + const char *detail) +{ + unsigned int hc = TALER_ErrorCode_get_http_status (ec); + + if ( (0 == hc) || + (UINT_MAX == hc) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid Taler error code %d provided for response!\n", + (int) ec); + hc = MHD_HTTP_INTERNAL_SERVER_ERROR; + } + return TALER_MHD_reply_with_error (connection, + hc, + ec, + detail); +} + + /** * Send a response indicating that the request was too big. * diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 0d50d95c6..ceba62201 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -131,7 +131,6 @@ check_PROGRAMS = \ test_bank_api_with_pybank \ test_bank_api_with_nexus \ test_exchange_api \ - test_exchange_api_keys_cherry_picking \ test_exchange_api_revocation \ test_exchange_api_overlapping_keys_bug \ test_exchange_management_api \ @@ -144,6 +143,10 @@ if HAVE_TWISTER test_bank_api_with_pybank_twisted endif +# test_exchange_api_keys_cherry_picking disabled for now: +# needs to be rewritten as we no longer support /keys timetravel! + + TESTS = \ $(check_PROGRAMS) diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c index f0184143a..48f03119e 100644 --- a/src/testing/test_auditor_api.c +++ b/src/testing/test_auditor_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-2020 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -630,8 +630,8 @@ run (void *cls, TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1, - 5 /* FIXME: wrong number... */), + 2, + 270 /* FIXME: wrong number... */), CMD_RUN_AUDITOR ("virgin-auditor"), TALER_TESTING_cmd_exchanges_with_url ("check-exchange", MHD_HTTP_OK, diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c index d2e18c41b..f24a3ff73 100644 --- a/src/testing/test_exchange_api.c +++ b/src/testing/test_exchange_api.c @@ -880,11 +880,11 @@ run (void *cls, "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", - MHD_HTTP_NOT_FOUND), + MHD_HTTP_GONE), /* Test deposit fails after recoup, with proof in recoup */ /* Note that, the exchange will never return the coin's transaction - * history with recoup data, as we get a 404 on the DK! */ + * history with recoup data, as we get a 410 on the DK! */ TALER_TESTING_cmd_deposit ("recoup-deposit-partial-after-recoup", "recoup-withdraw-coin-2a", 0, @@ -892,7 +892,7 @@ run (void *cls, "{\"items\":[{\"name\":\"extra ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.5", - MHD_HTTP_NOT_FOUND), + MHD_HTTP_GONE), /* Test that revoked coins cannot be withdrawn */ CMD_TRANSFER_TO_EXCHANGE ("recoup-create-reserve-3", "EUR:1.01"), @@ -906,7 +906,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-3-revoked", "recoup-create-reserve-3", "EUR:1", - MHD_HTTP_NOT_FOUND), + MHD_HTTP_GONE), /* check that we are empty before the rejection test */ TALER_TESTING_cmd_check_bank_empty ("check-empty-again"), @@ -958,7 +958,7 @@ run (void *cls, CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", 1, - 5 /* FIXME: wrong number... */), + 270 /* FIXME: wrong number... */), TALER_TESTING_cmd_batch ("wire", wire), TALER_TESTING_cmd_batch ("withdraw", diff --git a/src/testing/test_exchange_api_keys_cherry_picking.c b/src/testing/test_exchange_api_keys_cherry_picking.c index aab94d68a..588ef7520 100644 --- a/src/testing/test_exchange_api_keys_cherry_picking.c +++ b/src/testing/test_exchange_api_keys_cherry_picking.c @@ -129,8 +129,8 @@ run (void *cls, * Make sure we have the same keys situation as * it was before the serialization. */ - TALER_TESTING_cmd_check_keys_with_now - ("check-keys-after-deserialization", + TALER_TESTING_cmd_check_keys_with_now ( + "check-keys-after-deserialization", 4, NDKS_RIGHT_BEFORE_SERIALIZATION, /** @@ -198,8 +198,8 @@ run (void *cls, * ---- * 40 */// - TALER_TESTING_cmd_check_keys_with_now - ("check-keys-3", + TALER_TESTING_cmd_check_keys_with_now ( + "check-keys-3", 3 /* generation */, NDKS_RIGHT_BEFORE_SERIALIZATION, TTH_parse_time (JAN2030)), diff --git a/src/testing/test_exchange_api_revocation.c b/src/testing/test_exchange_api_revocation.c index a952d360c..555891497 100644 --- a/src/testing/test_exchange_api_revocation.c +++ b/src/testing/test_exchange_api_revocation.c @@ -73,7 +73,7 @@ run (void *cls, CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", 1, - 5 /* FIXME: wrong number... */), + 270 /* FIXME: wrong number... */), /** * Fill reserve with EUR:10.02, as withdraw fee is 1 ct per * config. diff --git a/src/testing/test_exchange_api_twisted.c b/src/testing/test_exchange_api_twisted.c index 446bdccc4..05867f89b 100644 --- a/src/testing/test_exchange_api_twisted.c +++ b/src/testing/test_exchange_api_twisted.c @@ -222,7 +222,7 @@ run (void *cls, TALER_TESTING_cmd_check_keys_pull_all_keys ( "check-keys-expiration-0", 2, - 5), + 270), /** * Run some normal commands after this to make sure everything is fine. */ @@ -237,6 +237,15 @@ run (void *cls, }; struct TALER_TESTING_Command commands[] = { + TALER_TESTING_cmd_wire_add ("add-wire-account", + "payto://x-taler-bank/localhost/2", + MHD_HTTP_NO_CONTENT, + false), + TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", + CONFIG_FILE), + TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", + 1, + 270 /* FIXME: wrong number... */), TALER_TESTING_cmd_batch ("refresh-reveal-409-conflict", refresh_409_conflict), TALER_TESTING_cmd_batch ("refund", diff --git a/src/testing/test_exchange_management_api.c b/src/testing/test_exchange_management_api.c index 26c6cae8a..18f6dedf6 100644 --- a/src/testing/test_exchange_management_api.c +++ b/src/testing/test_exchange_management_api.c @@ -144,7 +144,7 @@ run (void *cls, CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", 1, - 5 /* FIXME: wrong number... */), + 270 /* FIXME: wrong number... */), TALER_TESTING_cmd_end () }; diff --git a/src/testing/test_taler_exchange_wirewatch.c b/src/testing/test_taler_exchange_wirewatch.c index b16a9e769..169c959be 100644 --- a/src/testing/test_taler_exchange_wirewatch.c +++ b/src/testing/test_taler_exchange_wirewatch.c @@ -93,7 +93,7 @@ run (void *cls, config_filename), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", 1, - 5 /* FIXME: wrong number... */), + 58 /* FIXME: wrong number... */), TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-on-start"), CMD_EXEC_AGGREGATOR ("run-aggregator-on-empty"), TALER_TESTING_cmd_exec_wirewatch ("run-wirewatch-on-empty", diff --git a/src/testing/testing_api_cmd_offline_sign_keys.c b/src/testing/testing_api_cmd_offline_sign_keys.c index 70654ea10..dd6170d90 100644 --- a/src/testing/testing_api_cmd_offline_sign_keys.c +++ b/src/testing/testing_api_cmd_offline_sign_keys.c @@ -70,6 +70,7 @@ offlinesign_run (void *cls, "taler-exchange-offline", "taler-exchange-offline", "-c", ks->config_filename, + "-L", "INFO", "download", "sign", "upload", @@ -80,8 +81,6 @@ offlinesign_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - /* This function does not tell whether the command - * succeeded or not! */ TALER_TESTING_wait_for_sigchld (is); } diff --git a/src/testing/testing_api_cmd_revoke.c b/src/testing/testing_api_cmd_revoke.c index c43f53727..8863110bd 100644 --- a/src/testing/testing_api_cmd_revoke.c +++ b/src/testing/testing_api_cmd_revoke.c @@ -79,8 +79,9 @@ revoke_cleanup (void *cls, if (NULL != rs->revoke_proc) { - GNUNET_break (0 == GNUNET_OS_process_kill - (rs->revoke_proc, SIGKILL)); + GNUNET_break (0 == + GNUNET_OS_process_kill (rs->revoke_proc, + SIGKILL)); GNUNET_OS_process_wait (rs->revoke_proc); GNUNET_OS_process_destroy (rs->revoke_proc); rs->revoke_proc = NULL; @@ -163,13 +164,13 @@ revoke_run (void *cls, rs->dhks = GNUNET_STRINGS_data_to_string_alloc ( &denom_pub->h_key, sizeof (struct GNUNET_HashCode)); - rs->revoke_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, - "taler-exchange-keyup", - "taler-exchange-keyup", + "taler-exchange-offline", + "taler-exchange-offline", "-c", rs->config_filename, - "-r", rs->dhks, + "revoke-denomination", rs->dhks, + "upload", NULL); if (NULL == rs->revoke_proc) diff --git a/src/testing/testing_api_helpers_exchange.c b/src/testing/testing_api_helpers_exchange.c index 95ba71b7d..a12998e29 100644 --- a/src/testing/testing_api_helpers_exchange.c +++ b/src/testing/testing_api_helpers_exchange.c @@ -617,7 +617,7 @@ TALER_TESTING_wait_exchange_ready (const char *base_url) unsigned int iter; GNUNET_asprintf (&wget_cmd, - "wget -q -t 1 -T 1 %skeys -o /dev/null -O /dev/null", + "wget -q -t 1 -T 1 %sseed -o /dev/null -O /dev/null", base_url); // make sure ends with '/' /* give child time to start and bind against the socket */ fprintf (stderr, diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c index 88af481ea..f269274ec 100644 --- a/src/testing/testing_api_loop.c +++ b/src/testing/testing_api_loop.c @@ -388,24 +388,25 @@ maint_child_death (void *cls) struct TALER_TESTING_Interpreter *is = cls; struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; const struct GNUNET_DISK_FileHandle *pr; - struct GNUNET_OS_Process **processp; char c[16]; + enum GNUNET_OS_ProcessStatusType type; + unsigned long code; if (TALER_TESTING_cmd_is_batch (cmd)) { struct TALER_TESTING_Command *batch_cmd; - GNUNET_assert - (GNUNET_OK == TALER_TESTING_get_trait_cmd - (cmd, 0, &batch_cmd)); /* bad? */ + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_cmd (cmd, + 0, + &batch_cmd)); cmd = batch_cmd; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got SIGCHLD for `%s'.\n", cmd->label); - is->child_death_task = NULL; pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); @@ -424,16 +425,45 @@ maint_child_death (void *cls) } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got the dead child process handle" - ", waiting for termination ...\n"); - - GNUNET_OS_process_wait (*processp); + "Got the dead child process handle, waiting for termination ...\n"); + GNUNET_OS_process_wait_status (*processp, + &type, + &code); GNUNET_OS_process_destroy (*processp); *processp = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "... definitively terminated\n"); + switch (type) + { + case GNUNET_OS_PROCESS_UNKNOWN: + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + case GNUNET_OS_PROCESS_RUNNING: + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + case GNUNET_OS_PROCESS_STOPPED: + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + case GNUNET_OS_PROCESS_EXITED: + if (0 != code) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Process exited with unexpected status %u\n", + (unsigned int) code); + TALER_TESTING_interpreter_fail (is); + return; + } + break; + case GNUNET_OS_PROCESS_SIGNALED: + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + // FIXME: remove reload_keys, obsolete! if (GNUNET_OK == is->reload_keys) { if (NULL == is->exchanged) @@ -444,8 +474,9 @@ maint_child_death (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Triggering key state reload at exchange\n"); - GNUNET_break (0 == GNUNET_OS_process_kill - (is->exchanged, SIGUSR1)); + GNUNET_break (0 == + GNUNET_OS_process_kill (is->exchanged, + SIGUSR1)); sleep (5); /* make sure signal was received and processed */ } } @@ -643,19 +674,8 @@ TALER_TESTING_cert_cb (void *cls, * the interpreter is already running. */ if (GNUNET_YES == is->working) return; - is->working = GNUNET_YES; - - /* Very first start of tests, call "run()" */ - if (1 == is->key_generation) - { - main_ctx->main_cb (main_ctx->main_cb_cls, - is); - return; - } - - /* Tests already started, just trigger the - * next command. */ + /* Trigger the next command. */ TALER_LOG_DEBUG ("Cert_cb, scheduling CMD (ip: %d)\n", is->ip); GNUNET_SCHEDULER_add_now (&interpreter_run, @@ -740,6 +760,7 @@ main_wrapper_exchange_connect (void *cls) main_ctx->exchange_url = exchange_url; is->timeout_task = GNUNET_SCHEDULER_add_shutdown (&do_abort, main_ctx); + is->working = GNUNET_YES; GNUNET_break (NULL != (is->exchange = TALER_EXCHANGE_connect (is->ctx, @@ -747,6 +768,10 @@ main_wrapper_exchange_connect (void *cls) &TALER_TESTING_cert_cb, main_ctx, TALER_EXCHANGE_OPTION_END))); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Starting main test loop\n"); + main_ctx->main_cb (main_ctx->main_cb_cls, + is); } @@ -842,10 +867,10 @@ static int load_urls (struct TALER_TESTING_Interpreter *is) { if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (is->cfg, - "auditor", - "BASE_URL", - &is->auditor_url)) + GNUNET_CONFIGURATION_get_value_string (is->cfg, + "auditor", + "BASE_URL", + &is->auditor_url)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "auditor", diff --git a/src/util/taler-helper-crypto-rsa.c b/src/util/taler-helper-crypto-rsa.c index ec1158ee7..32f0b44dd 100644 --- a/src/util/taler-helper-crypto-rsa.c +++ b/src/util/taler-helper-crypto-rsa.c @@ -1112,17 +1112,16 @@ read_job (void *cls) * Create a new denomination key (we do not have enough). * * @param denom denomination key to create + * @param now current time to use (to get many keys to use the exact same time) * @return #GNUNET_OK on success */ static int -create_key (struct Denomination *denom) +create_key (struct Denomination *denom, + struct GNUNET_TIME_Absolute now) { struct DenominationKey *dk; struct GNUNET_TIME_Absolute anchor; - struct GNUNET_TIME_Absolute now; - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); if (NULL == denom->keys_tail) { anchor = now; @@ -1237,9 +1236,11 @@ purge_key (struct DenominationKey *dk) * correct location sorted by next maintenance activity. * * @param[in,out] denom denomination to update material for + * @param now current time to use (to get many keys to use the exact same time) */ static void -update_keys (struct Denomination *denom) +update_keys (struct Denomination *denom, + struct GNUNET_TIME_Absolute now) { /* create new denomination keys */ while ( (NULL == denom->keys_tail) || @@ -1252,7 +1253,8 @@ update_keys (struct Denomination *denom) lookahead_sign), overlap_duration)).rel_value_us) ) if (GNUNET_OK != - create_key (denom)) + create_key (denom, + now)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create keys for `%s'\n", @@ -1273,18 +1275,19 @@ update_keys (struct Denomination *denom) struct GNUNET_TIME_Absolute at; at = denomination_action_time (denom); - before = NULL; GNUNET_CONTAINER_DLL_remove (denom_head, denom_tail, denom); + before = NULL; for (struct Denomination *pos = denom_head; NULL != pos; pos = pos->next) { - if (denomination_action_time (pos).abs_value_us > at.abs_value_us) + if (denomination_action_time (pos).abs_value_us >= at.abs_value_us) break; before = pos; } + GNUNET_CONTAINER_DLL_insert_after (denom_head, denom_tail, before, @@ -1302,12 +1305,16 @@ static void update_denominations (void *cls) { struct Denomination *denom; + struct GNUNET_TIME_Absolute now; (void) cls; + now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&now); keygen_task = NULL; do { denom = denom_head; - update_keys (denom); + update_keys (denom, + now); } while (denom != denom_head); keygen_task = GNUNET_SCHEDULER_add_at (denomination_action_time (denom), &update_denominations, @@ -1609,18 +1616,35 @@ parse_denomination_cfg (const char *ct, } +/** + * Closure for #load_denominations. + */ +struct LoadContext +{ + /** + * Current time to use. + */ + struct GNUNET_TIME_Absolute now; + + /** + * Status, to be set to #GNUNET_SYSERR on failure + */ + int ret; +}; + + /** * Generate new denomination signing keys for the denomination type of the given @a * denomination_alias. * - * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure + * @param cls a `struct LoadContext`, with 'ret' to be set to #GNUNET_SYSERR on failure * @param denomination_alias name of the denomination's section in the configuration */ static void load_denominations (void *cls, const char *denomination_alias) { - int *ret = cls; + struct LoadContext *ctx = cls; struct Denomination *denom; if (0 != strncasecmp (denomination_alias, @@ -1632,7 +1656,7 @@ load_denominations (void *cls, parse_denomination_cfg (denomination_alias, denom)) { - *ret = GNUNET_SYSERR; + ctx->ret = GNUNET_SYSERR; GNUNET_free (denom); return; } @@ -1656,7 +1680,8 @@ load_denominations (void *cls, GNUNET_CONTAINER_DLL_insert (denom_head, denom_tail, denom); - update_keys (denom); + update_keys (denom, + ctx->now); } @@ -1905,13 +1930,16 @@ run (void *cls, keys = GNUNET_CONTAINER_multihashmap_create (65536, GNUNET_YES); { - int ok; + struct LoadContext lc = { + .ret = GNUNET_OK, + .now = GNUNET_TIME_absolute_get () + }; - ok = GNUNET_OK; + (void) GNUNET_TIME_round_abs (&lc.now); GNUNET_CONFIGURATION_iterate_sections (kcfg, &load_denominations, - &ok); - if (GNUNET_OK != ok) + &lc); + if (GNUNET_OK != lc.ret) { global_ret = 4; GNUNET_SCHEDULER_shutdown (); -- cgit v1.2.3