diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 0e4e0b26c..7dc22fd53 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -288,10 +288,10 @@ verify_and_execute_deposit (struct MHD_Connection *connection, /* check denomination */ { - struct TEH_KS_StateHandle *mks; + struct TEH_KS_StateHandle *key_state; - mks = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); - if (NULL == mks) + key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); + if (NULL == key_state) { TALER_LOG_ERROR ("Lacking keys to operate\n"); return TALER_MHD_reply_with_error (connection, @@ -299,14 +299,14 @@ verify_and_execute_deposit (struct MHD_Connection *connection, TALER_EC_EXCHANGE_BAD_CONFIGURATION, "no keys"); } - dki = TEH_KS_denomination_key_lookup_by_hash (mks, + dki = TEH_KS_denomination_key_lookup_by_hash (key_state, &deposit->coin.denom_pub_hash, TEH_KS_DKU_DEPOSIT, &ec, &hc); if (NULL == dki) { - TEH_KS_release (mks); + TEH_KS_release (key_state); return TALER_MHD_reply_with_error (connection, hc, ec, @@ -314,7 +314,7 @@ verify_and_execute_deposit (struct MHD_Connection *connection, } TALER_amount_ntoh (&dc.value, &dki->issue.properties.value); - TEH_KS_release (mks); + TEH_KS_release (key_state); } /* execute transaction */ dc.deposit = deposit; diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c index b9de15b22..35bf52f65 100644 --- a/src/exchange/taler-exchange-httpd_keystate.c +++ b/src/exchange/taler-exchange-httpd_keystate.c @@ -2273,13 +2273,19 @@ read_again: void TEH_KS_free () { + GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); if (NULL != internal_key_state) { struct TEH_KS_StateHandle *ks = internal_key_state; internal_key_state = NULL; + GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); TEH_KS_release (ks); } + else + { + GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); + } } diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c index 9a8d929fb..c9d0b44fc 100644 --- a/src/exchange/taler-exchange-httpd_refresh_reveal.c +++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c @@ -517,6 +517,275 @@ refresh_reveal_persist (void *cls, } +/** + * Resolve denomination hashes using the @a key_state + * + * @param key_state the key state + * @param connection the MHD connection to handle + * @param rctx context for the operation, partially built at this time + * @param transfer_pub transfer public key + * @param link_sigs_json link signatures in JSON format + * @param new_denoms_h_json requests for fresh coins to be created + * @param coin_evs envelopes of gamma-selected coins to be signed + * @return MHD result code + */ +static int +resolve_refresh_reveal_denominations (struct TEH_KS_StateHandle *key_state, + struct MHD_Connection *connection, + struct RevealContext *rctx, + const json_t *link_sigs_json, + const json_t *new_denoms_h_json, + const json_t *coin_evs) +{ + unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); + const struct + TALER_EXCHANGEDB_DenominationKeyIssueInformation *dkis[num_fresh_coins]; + struct GNUNET_HashCode dki_h[num_fresh_coins]; + struct TALER_RefreshCoinData rcds[num_fresh_coins]; + struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins]; + struct TALER_EXCHANGEDB_RefreshMelt refresh_melt; + int res; + + /* Parse denomination key hashes */ + for (unsigned int i = 0; idenom_priv.rsa_private_key); + } + + /* Parse coin envelopes */ + for (unsigned int i = 0; icoin_ev, + &rcd->coin_ev_size), + GNUNET_JSON_spec_end () + }; + + res = TALER_MHD_parse_json_array (connection, + coin_evs, + spec, + i, + -1); + if (GNUNET_OK != res) + { + for (unsigned int j = 0; jdk = &dkis[i]->denom_pub; + } + + /* lookup old_coin_pub in database */ + { + enum GNUNET_DB_QueryStatus qs; + + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + (qs = TEH_plugin->get_melt (TEH_plugin->cls, + NULL, + &rctx->rc, + &refresh_melt))) + { + switch (qs) + { + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + res = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN, + "rc"); + break; + case GNUNET_DB_STATUS_HARD_ERROR: + res = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR, + "failed to fetch session data"); + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + default: + GNUNET_break (0); /* should be impossible */ + res = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_INTERNAL_INVARIANT_FAILURE, + "assertion failed"); + break; + } + goto cleanup; + } + } + /* Parse link signatures array */ + for (unsigned int i = 0; igamma_tp; + GNUNET_CRYPTO_hash (rcds[i].coin_ev, + rcds[i].coin_ev_size, + &ldp.coin_envelope_hash); + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK, + &ldp.purpose, + &link_sigs[i].eddsa_signature, + &refresh_melt.session.coin.coin_pub. + eddsa_pub)) + { + GNUNET_break_op (0); + res = TALER_MHD_reply_with_error (connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_REFRESH_REVEAL_LINK_SIGNATURE_INVALID, + "link_sig"); + goto cleanup; + } + } + } + + rctx->num_fresh_coins = num_fresh_coins; + rctx->rcds = rcds; + rctx->dkis = dkis; + rctx->link_sigs = link_sigs; + + /* sign _early_ (optimistic!) to keep out of transaction scope! */ + rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins, + 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, + rctx->rcds[i].coin_ev, + rctx->rcds[i].coin_ev_size); + if (NULL == rctx->ev_sigs[i].rsa_signature) + { + GNUNET_break (0); + res = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_REFRESH_REVEAL_SIGNING_ERROR, + "internal signing error"); + goto cleanup; + } + } + + /* We try the three transactions a few times, as theoretically + the pre-check might be satisfied by a concurrent transaction + voiding our final commit due to uniqueness violation; naturally, + on hard errors we exit immediately */ + for (unsigned int retries = 0; retries < MAX_REVEAL_RETRIES; retries++) + { + /* do transactional work */ + rctx->preflight_ok = GNUNET_NO; + if ( (GNUNET_OK == + TEH_DB_run_transaction (connection, + "reveal pre-check", + &res, + &refresh_reveal_preflight, + rctx)) && + (GNUNET_YES == rctx->preflight_ok) ) + { + /* Generate final (positive) response */ + GNUNET_assert (NULL != rctx->ev_sigs); + res = reply_refresh_reveal_success (connection, + num_fresh_coins, + rctx->ev_sigs); + GNUNET_break (MHD_NO != res); + goto cleanup; /* aka 'break' */ + } + if (GNUNET_SYSERR == rctx->preflight_ok) + { + GNUNET_break (0); + goto cleanup; /* aka 'break' */ + } + if (GNUNET_OK != + TEH_DB_run_transaction (connection, + "run reveal", + &res, + &refresh_reveal_transaction, + rctx)) + { + /* reveal failed, too bad */ + GNUNET_break_op (0); + goto cleanup; /* aka 'break' */ + } + if (GNUNET_OK == + TEH_DB_run_transaction (connection, + "persist reveal", + &res, + &refresh_reveal_persist, + rctx)) + { + /* Generate final (positive) response */ + GNUNET_assert (NULL != rctx->ev_sigs); + res = reply_refresh_reveal_success (connection, + num_fresh_coins, + rctx->ev_sigs); + break; + } + } /* end for (retries...) */ + +cleanup: + GNUNET_break (MHD_NO != res); + /* free resources */ + if (NULL != rctx->ev_sigs) + { + for (unsigned int i = 0; iev_sigs[i].rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (rctx->ev_sigs[i].rsa_signature); + GNUNET_free (rctx->ev_sigs); + rctx->ev_sigs = NULL; /* just to be safe... */ + } + for (unsigned int i = 0; i= MAX_FRESH_COINS) || @@ -594,16 +861,10 @@ handle_refresh_reveal_json (struct MHD_Connection *connection, return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - /* Resolve denomination hashes */ { - const struct - TALER_EXCHANGEDB_DenominationKeyIssueInformation *dkis[num_fresh_coins]; - struct GNUNET_HashCode dki_h[num_fresh_coins]; - struct TALER_RefreshCoinData rcds[num_fresh_coins]; - struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins]; - int res; + struct TEH_KS_StateHandle *key_state; + int ret; - /* Resolve denomination hashes */ key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); if (NULL == key_state) { @@ -613,248 +874,14 @@ handle_refresh_reveal_json (struct MHD_Connection *connection, TALER_EC_REFRESH_REVEAL_KEYS_MISSING, "exchange lacks keys"); } - - /* Parse denomination key hashes */ - for (unsigned int i = 0; idenom_priv.rsa_private_key); - } - - /* Parse coin envelopes */ - for (unsigned int i = 0; icoin_ev, - &rcd->coin_ev_size), - GNUNET_JSON_spec_end () - }; - - res = TALER_MHD_parse_json_array (connection, - coin_evs, - spec, - i, - -1); - if (GNUNET_OK != res) - { - for (unsigned int j = 0; jdk = &dkis[i]->denom_pub; - } - - /* lookup old_coin_pub in database */ - { - enum GNUNET_DB_QueryStatus qs; - - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - (qs = TEH_plugin->get_melt (TEH_plugin->cls, - NULL, - &rctx->rc, - &refresh_melt))) - { - switch (qs) - { - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - res = TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN, - "rc"); - break; - case GNUNET_DB_STATUS_HARD_ERROR: - res = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR, - "failed to fetch session data"); - break; - case GNUNET_DB_STATUS_SOFT_ERROR: - default: - GNUNET_break (0); /* should be impossible */ - res = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_INTERNAL_INVARIANT_FAILURE, - "assertion failed"); - break; - } - goto cleanup; - } - } - /* Parse link signatures array */ - for (unsigned int i = 0; igamma_tp; - GNUNET_CRYPTO_hash (rcds[i].coin_ev, - rcds[i].coin_ev_size, - &ldp.coin_envelope_hash); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK, - &ldp.purpose, - &link_sigs[i].eddsa_signature, - &refresh_melt.session.coin.coin_pub. - eddsa_pub)) - { - GNUNET_break_op (0); - res = TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_REFRESH_REVEAL_LINK_SIGNATURE_INVALID, - "link_sig"); - goto cleanup; - } - } - } - - rctx->num_fresh_coins = num_fresh_coins; - rctx->rcds = rcds; - rctx->dkis = dkis; - rctx->link_sigs = link_sigs; - - /* sign _early_ (optimistic!) to keep out of transaction scope! */ - rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins, - 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, - rctx->rcds[i].coin_ev, - rctx->rcds[i].coin_ev_size); - if (NULL == rctx->ev_sigs[i].rsa_signature) - { - GNUNET_break (0); - res = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_REFRESH_REVEAL_SIGNING_ERROR, - "internal signing error"); - goto cleanup; - } - } - - /* We try the three transactions a few times, as theoretically - the pre-check might be satisfied by a concurrent transaction - voiding our final commit due to uniqueness violation; naturally, - on hard errors we exit immediately */ - for (unsigned int retries = 0; retries < MAX_REVEAL_RETRIES; retries++) - { - /* do transactional work */ - rctx->preflight_ok = GNUNET_NO; - if ( (GNUNET_OK == - TEH_DB_run_transaction (connection, - "reveal pre-check", - &res, - &refresh_reveal_preflight, - rctx)) && - (GNUNET_YES == rctx->preflight_ok) ) - { - /* Generate final (positive) response */ - GNUNET_assert (NULL != rctx->ev_sigs); - res = reply_refresh_reveal_success (connection, - num_fresh_coins, - rctx->ev_sigs); - GNUNET_break (MHD_NO != res); - goto cleanup; /* aka 'break' */ - } - if (GNUNET_SYSERR == rctx->preflight_ok) - { - GNUNET_break (0); - goto cleanup; /* aka 'break' */ - } - if (GNUNET_OK != - TEH_DB_run_transaction (connection, - "run reveal", - &res, - &refresh_reveal_transaction, - rctx)) - { - /* reveal failed, too bad */ - GNUNET_break_op (0); - goto cleanup; /* aka 'break' */ - } - if (GNUNET_OK == - TEH_DB_run_transaction (connection, - "persist reveal", - &res, - &refresh_reveal_persist, - rctx)) - { - /* Generate final (positive) response */ - GNUNET_assert (NULL != rctx->ev_sigs); - res = reply_refresh_reveal_success (connection, - num_fresh_coins, - rctx->ev_sigs); - break; - } - } /* end for (retries...) */ - GNUNET_break (MHD_NO != res); - -cleanup: - GNUNET_break (MHD_NO != res); - /* free resources */ - if (NULL != rctx->ev_sigs) - { - for (unsigned int i = 0; iev_sigs[i].rsa_signature) - GNUNET_CRYPTO_rsa_signature_free (rctx->ev_sigs[i].rsa_signature); - GNUNET_free (rctx->ev_sigs); - } - for (unsigned int i = 0; iissue.properties.fee_refund); } - TEH_KS_release (mks); + TEH_KS_release (key_state); } /* Finally run the actual transaction logic */