diff --git a/src/exchange/taler-exchange-httpd_recoup-refresh.c b/src/exchange/taler-exchange-httpd_recoup-refresh.c index 3e0588940..6089aec48 100644 --- a/src/exchange/taler-exchange-httpd_recoup-refresh.c +++ b/src/exchange/taler-exchange-httpd_recoup-refresh.c @@ -174,6 +174,7 @@ verify_and_execute_recoup_refresh ( const struct TALER_CoinPublicInfo *coin, const struct TALER_ExchangeWithdrawValues *exchange_vals, const union TALER_DenominationBlindingKeyP *coin_bks, + const struct TALER_CsNonce *nonce, const struct TALER_CoinSpendSignatureP *coin_sig) { struct RecoupContext pc; @@ -263,6 +264,9 @@ verify_and_execute_recoup_refresh ( TALER_EC_EXCHANGE_RECOUP_REFRESH_BLINDING_FAILED, NULL); } + if (TALER_DENOMINATION_CS == blinded_planchet.cipher) + blinded_planchet.details.cs_blinded_planchet.nonce + = *nonce; TALER_coin_ev_hash (&blinded_planchet, &coin->denom_pub_hash, &h_blind); @@ -360,6 +364,7 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection, union TALER_DenominationBlindingKeyP coin_bks; struct TALER_CoinSpendSignatureP coin_sig; struct TALER_ExchangeWithdrawValues exchange_vals; + struct TALER_CsNonce nonce; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", &coin.denom_pub_hash), @@ -371,12 +376,18 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection, &coin_bks), GNUNET_JSON_spec_fixed_auto ("coin_sig", &coin_sig), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("cs-nonce", + &nonce)), GNUNET_JSON_spec_end () }; memset (&coin, 0, sizeof (coin)); + memset (&nonce, + 0, + sizeof (nonce)); coin.coin_pub = *coin_pub; ret = TALER_MHD_parse_json_data (connection, root, @@ -392,6 +403,7 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection, &coin, &exchange_vals, &coin_bks, + &nonce, &coin_sig); GNUNET_JSON_parse_free (spec); return res; diff --git a/src/exchange/taler-exchange-httpd_recoup.c b/src/exchange/taler-exchange-httpd_recoup.c index d4ff52376..0208d45a0 100644 --- a/src/exchange/taler-exchange-httpd_recoup.c +++ b/src/exchange/taler-exchange-httpd_recoup.c @@ -177,6 +177,7 @@ verify_and_execute_recoup ( const struct TALER_CoinPublicInfo *coin, const struct TALER_ExchangeWithdrawValues *exchange_vals, const union TALER_DenominationBlindingKeyP *coin_bks, + const struct TALER_CsNonce *nonce, const struct TALER_CoinSpendSignatureP *coin_sig) { struct RecoupContext pc; @@ -268,6 +269,9 @@ verify_and_execute_recoup ( TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED, NULL); } + if (TALER_DENOMINATION_CS == blinded_planchet.cipher) + blinded_planchet.details.cs_blinded_planchet.nonce + = *nonce; if (GNUNET_OK != TALER_coin_ev_hash (&blinded_planchet, &coin->denom_pub_hash, @@ -373,6 +377,7 @@ TEH_handler_recoup (struct MHD_Connection *connection, union TALER_DenominationBlindingKeyP coin_bks; struct TALER_CoinSpendSignatureP coin_sig; struct TALER_ExchangeWithdrawValues exchange_vals; + struct TALER_CsNonce nonce; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", &coin.denom_pub_hash), @@ -384,12 +389,18 @@ TEH_handler_recoup (struct MHD_Connection *connection, &coin_bks), GNUNET_JSON_spec_fixed_auto ("coin_sig", &coin_sig), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("cs-nonce", + &nonce)), GNUNET_JSON_spec_end () }; memset (&coin, 0, sizeof (coin)); + memset (&nonce, + 0, + sizeof (nonce)); coin.coin_pub = *coin_pub; ret = TALER_MHD_parse_json_data (connection, root, @@ -398,6 +409,9 @@ TEH_handler_recoup (struct MHD_Connection *connection, return MHD_NO; /* hard failure */ if (GNUNET_NO == ret) return MHD_YES; /* failure */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Recoup coin with BKS=%s\n", + TALER_B2S (&coin_bks)); { MHD_RESULT res; @@ -405,6 +419,7 @@ TEH_handler_recoup (struct MHD_Connection *connection, &coin, &exchange_vals, &coin_bks, + &nonce, &coin_sig); GNUNET_JSON_parse_free (spec); return res; diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c index adac25659..8e4bbb475 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.c +++ b/src/exchange/taler-exchange-httpd_withdraw.c @@ -471,9 +471,10 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc, = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); wc.wsrd.h_denomination_pub = wc.collectable.denom_pub_hash; - if (GNUNET_OK != TALER_coin_ev_hash (&wc.blinded_planchet, - &wc.collectable.denom_pub_hash, - &wc.wsrd.h_coin_envelope)) + if (GNUNET_OK != + TALER_coin_ev_hash (&wc.blinded_planchet, + &wc.collectable.denom_pub_hash, + &wc.wsrd.h_coin_envelope)) { GNUNET_break (0); GNUNET_JSON_parse_free (spec); @@ -502,6 +503,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc, /* Sign before transaction! */ ec = TALER_EC_NONE; + // FIXME: swap arguments! wc.collectable.sig = TEH_keys_denomination_sign ( &wc.collectable.denom_pub_hash, &wc.blinded_planchet, diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 244333735..8e8203790 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -466,6 +466,9 @@ struct TALER_RsaPubHashP * Master key material for the deriviation of * private coins and blinding factors. */ +// FIXME: split this struct, we should have +// a different one for the Melt/Refresh secrets +// and the withdraw secrets! struct TALER_PlanchetSecretsP { @@ -840,7 +843,10 @@ struct TALER_BlindedCsPlanchet struct GNUNET_CRYPTO_CsC c[2]; /** - * Public Nonce + * Public nonce. + * FIXME: this nonce being here has created TONS + * of trouble. Likely split off from this data + * structure in the future! */ struct TALER_CsNonce nonce; }; @@ -1108,14 +1114,21 @@ TALER_denom_cs_derive_r_public ( /** * Blind coin for blind signing with @a dk using blinding secret @a coin_bks. * + * NOTE/FIXME: As a particular oddity, the @a blinded_planchet + * is only partially initialized by this function in the + * case of CS-denominations. Here, the 'nonce' must + * be initialized separately! This has been a MAJOR + * source of bugs, and points to a likely need for a + * reorganization of either that data structure or + * this function! + * * @param dk denomination public key to blind for * @param coin_bks blinding secret to use * @param age_commitment_hash hash of the age commitment to be used for the coin. NULL if no commitment is made. * @param coin_pub public key of the coin to blind * @param alg_values algorithm specific values to blind the planchet * @param[out] c_hash resulting hashed coin - * @param[out] coin_ev blinded coin to submit - * @param[out] coin_ev_size number of bytes in @a coin_ev + * @param[out] blinded_planchet planchet data to initialize * @return #GNUNET_OK on success */ enum GNUNET_GenericReturnValue diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index a65e796a5..58364b159 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -2236,7 +2236,9 @@ typedef void * @param pk kind of coin to pay back * @param denom_sig signature over the coin by the exchange using @a pk * @param exchange_vals contribution from the exchange on the withdraw - * @param ps secret internals of the original refresh-reveal operation + * @param rps melt secret of the refreshing operation + * @param ps coin-specific secrets derived for this coin during the refreshing operation + * @param idx index of the fresh coin in the refresh operation that is now being recouped * @param recoup_cb the callback to call when the final result for this request is available * @param recoup_cb_cls closure for @a recoup_cb * @return NULL @@ -2249,7 +2251,9 @@ TALER_EXCHANGE_recoup_refresh ( const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_DenominationSignature *denom_sig, const struct TALER_ExchangeWithdrawValues *exchange_vals, + const struct TALER_PlanchetSecretsP *rps, const struct TALER_PlanchetSecretsP *ps, + unsigned int idx, TALER_EXCHANGE_RecoupRefreshResultCallback recoup_cb, void *recoup_cb_cls); diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index 70bbda7fb..7284a1247 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -2444,7 +2444,8 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits, op (bank_row, const uint64_t) \ op (reserve_priv, const struct TALER_ReservePrivateKeyP) \ op (planchet_secret, const struct TALER_PlanchetSecretsP) \ - op (reserve_pub, const struct TALER_ReservePublicKeyP) \ + op (refresh_secret, const struct TALER_PlanchetSecretsP) \ + op (reserve_pub, const struct TALER_ReservePublicKeyP) \ op (merchant_priv, const struct TALER_MerchantPrivateKeyP) \ op (merchant_pub, const struct TALER_MerchantPublicKeyP) \ op (merchant_sig, const struct TALER_MerchantSignatureP) \ diff --git a/src/lib/exchange_api_recoup.c b/src/lib/exchange_api_recoup.c index c507d1e6a..b6a99ba52 100644 --- a/src/lib/exchange_api_recoup.c +++ b/src/lib/exchange_api_recoup.c @@ -329,6 +329,24 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange, &coin_sig), GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", &bks)); + if (TALER_DENOMINATION_CS == denom_sig->cipher) + { + struct TALER_CsNonce nonce; + + // FIXME: add this to the spec! + /* NOTE: this is not elegant, and as per the note in TALER_coin_ev_hash() + it is not strictly clear that the nonce is needed. Best case would be + to find a way to include it more 'naturally' somehow, for example with + the variant union version of bks! */ + TALER_cs_withdraw_nonce_derive (ps, + &nonce); + GNUNET_assert ( + 0 == + json_object_set_new (recoup_obj, + "cs-nonce", + GNUNET_JSON_from_data_auto ( + &nonce))); + } { char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; diff --git a/src/lib/exchange_api_recoup_refresh.c b/src/lib/exchange_api_recoup_refresh.c index 79c66aceb..dbdf9eb65 100644 --- a/src/lib/exchange_api_recoup_refresh.c +++ b/src/lib/exchange_api_recoup_refresh.c @@ -165,6 +165,19 @@ handle_recoup_refresh_finished (void *cls, hr.ec = TALER_JSON_get_error_code (j); hr.hint = TALER_JSON_get_error_hint (j); break; + case MHD_HTTP_FORBIDDEN: + /* Nothing really to verify, exchange says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + hr.ec = TALER_JSON_get_error_code (j); + hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + hr.ec = TALER_JSON_get_error_code (j); + hr.hint = TALER_JSON_get_error_hint (j); + break; case MHD_HTTP_CONFLICT: { /* Insufficient funds, proof attached */ @@ -238,19 +251,6 @@ handle_recoup_refresh_finished (void *cls, TALER_EXCHANGE_recoup_refresh_cancel (ph); return; } - case MHD_HTTP_FORBIDDEN: - /* Nothing really to verify, exchange says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); - break; case MHD_HTTP_GONE: /* Kind of normal: the money was already sent to the merchant (it was too late for the refund). */ @@ -287,7 +287,9 @@ TALER_EXCHANGE_recoup_refresh ( const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_DenominationSignature *denom_sig, const struct TALER_ExchangeWithdrawValues *exchange_vals, + const struct TALER_PlanchetSecretsP *rps, const struct TALER_PlanchetSecretsP *ps, + unsigned int idx, TALER_EXCHANGE_RecoupRefreshResultCallback recoup_cb, void *recoup_cb_cls) { @@ -302,6 +304,7 @@ TALER_EXCHANGE_recoup_refresh ( struct TALER_CoinSpendPrivateKeyP coin_priv; union TALER_DenominationBlindingKeyP bks; + GNUNET_assert (NULL != recoup_cb); GNUNET_assert (GNUNET_YES == TEAH_handle_is_ready (exchange)); TALER_planchet_setup_coin_priv (ps, @@ -331,6 +334,26 @@ TALER_EXCHANGE_recoup_refresh ( GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", &bks)); + if (TALER_DENOMINATION_CS == denom_sig->cipher) + { + struct TALER_CsNonce nonce; + + // FIXME: add this to the spec! + /* NOTE: this is not elegant, and as per the note in TALER_coin_ev_hash() + it is not strictly clear that the nonce is needed. Best case would be + to find a way to include it more 'naturally' somehow, for example with + the variant union version of bks! */ + TALER_cs_refresh_nonce_derive (rps, + idx, + &nonce); + GNUNET_assert ( + 0 == + json_object_set_new (recoup_obj, + "cs-nonce", + GNUNET_JSON_from_data_auto ( + &nonce))); + } + { char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; char *end; diff --git a/src/lib/exchange_api_withdraw2.c b/src/lib/exchange_api_withdraw2.c index 13a43009e..d354946e1 100644 --- a/src/lib/exchange_api_withdraw2.c +++ b/src/lib/exchange_api_withdraw2.c @@ -428,9 +428,10 @@ TALER_EXCHANGE_withdraw2 ( "/reserves/%s/withdraw", pub_str); } + // FIXME: move this to libtalerutil! { struct TALER_WithdrawRequestPS req = { - .purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)), + .purpose.size = htonl (sizeof (req)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW), .reserve_pub = wh->reserve_pub, .h_denomination_pub = pd->denom_pub_hash diff --git a/src/testing/testing_api_cmd_recoup_refresh.c b/src/testing/testing_api_cmd_recoup_refresh.c index fd8f1c36c..2f8917b2d 100644 --- a/src/testing/testing_api_cmd_recoup_refresh.c +++ b/src/testing/testing_api_cmd_recoup_refresh.c @@ -125,14 +125,14 @@ recoup_refresh_cb (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_CoinSpendPublicKeyP *old_coin_pub) { - struct RecoupRefreshState *ps = cls; - struct TALER_TESTING_Interpreter *is = ps->is; + struct RecoupRefreshState *rrs = cls; + struct TALER_TESTING_Interpreter *is = rrs->is; struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; char *cref; unsigned int idx; - ps->ph = NULL; - if (ps->expected_response_code != hr->http_status) + rrs->ph = NULL; + if (rrs->expected_response_code != hr->http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d to command %s in %s:%u\n", @@ -150,7 +150,7 @@ recoup_refresh_cb (void *cls, } if (GNUNET_OK != - parse_coin_reference (ps->coin_reference, + parse_coin_reference (rrs->coin_reference, &cref, &idx)) { @@ -170,7 +170,7 @@ recoup_refresh_cb (void *cls, struct TALER_CoinSpendPublicKeyP oc; melt_cmd = TALER_TESTING_interpreter_lookup_command (is, - ps->melt_reference); + rrs->melt_reference); if (NULL == melt_cmd) { GNUNET_break (0); @@ -185,7 +185,7 @@ recoup_refresh_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Coin %u not found in command %s\n", 0, - ps->melt_reference); + rrs->melt_reference); GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; @@ -228,20 +228,21 @@ recoup_refresh_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) { - struct RecoupRefreshState *ps = cls; + struct RecoupRefreshState *rrs = cls; const struct TALER_TESTING_Command *coin_cmd; const struct TALER_TESTING_Command *melt_cmd; const struct TALER_CoinSpendPrivateKeyP *coin_priv; const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; const struct TALER_DenominationSignature *coin_sig; + const struct TALER_PlanchetSecretsP *rplanchet; const struct TALER_PlanchetSecretsP *planchet; const struct TALER_ExchangeWithdrawValues *ewv; char *cref; unsigned int idx; - ps->is = is; + rrs->is = is; if (GNUNET_OK != - parse_coin_reference (ps->coin_reference, + parse_coin_reference (rrs->coin_reference, &cref, &idx)) { @@ -259,14 +260,13 @@ recoup_refresh_run (void *cls, return; } melt_cmd = TALER_TESTING_interpreter_lookup_command (is, - ps->melt_reference); + rrs->melt_reference); if (NULL == melt_cmd) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv (coin_cmd, idx, @@ -294,7 +294,14 @@ recoup_refresh_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - + if (GNUNET_OK != + TALER_TESTING_get_trait_refresh_secret (melt_cmd, + &rplanchet)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub (coin_cmd, idx, @@ -304,7 +311,6 @@ recoup_refresh_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig (coin_cmd, idx, @@ -314,19 +320,19 @@ recoup_refresh_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Trying to recoup_refresh denomination '%s'\n", TALER_B2S (&denom_pub->h_key)); - - ps->ph = TALER_EXCHANGE_recoup_refresh (is->exchange, - denom_pub, - coin_sig, - ewv, - planchet, - &recoup_refresh_cb, - ps); - GNUNET_assert (NULL != ps->ph); + rrs->ph = TALER_EXCHANGE_recoup_refresh (is->exchange, + denom_pub, + coin_sig, + ewv, + rplanchet, + planchet, + idx, + &recoup_refresh_cb, + rrs); + GNUNET_assert (NULL != rrs->ph); } @@ -341,13 +347,13 @@ static void recoup_refresh_cleanup (void *cls, const struct TALER_TESTING_Command *cmd) { - struct RecoupRefreshState *ps = cls; - if (NULL != ps->ph) + struct RecoupRefreshState *rrs = cls; + if (NULL != rrs->ph) { - TALER_EXCHANGE_recoup_refresh_cancel (ps->ph); - ps->ph = NULL; + TALER_EXCHANGE_recoup_refresh_cancel (rrs->ph); + rrs->ph = NULL; } - GNUNET_free (ps); + GNUNET_free (rrs); } @@ -358,15 +364,15 @@ TALER_TESTING_cmd_recoup_refresh (const char *label, const char *melt_reference, const char *amount) { - struct RecoupRefreshState *ps; + struct RecoupRefreshState *rrs; - ps = GNUNET_new (struct RecoupRefreshState); - ps->expected_response_code = expected_response_code; - ps->coin_reference = coin_reference; - ps->melt_reference = melt_reference; + rrs = GNUNET_new (struct RecoupRefreshState); + rrs->expected_response_code = expected_response_code; + rrs->coin_reference = coin_reference; + rrs->melt_reference = melt_reference; if (GNUNET_OK != TALER_string_to_amount (amount, - &ps->amount)) + &rrs->amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse amount `%s' at %s\n", @@ -376,7 +382,7 @@ TALER_TESTING_cmd_recoup_refresh (const char *label, } { struct TALER_TESTING_Command cmd = { - .cls = ps, + .cls = rrs, .label = label, .run = &recoup_refresh_run, .cleanup = &recoup_refresh_cleanup diff --git a/src/testing/testing_api_cmd_refresh.c b/src/testing/testing_api_cmd_refresh.c index 88c694934..fe443d214 100644 --- a/src/testing/testing_api_cmd_refresh.c +++ b/src/testing/testing_api_cmd_refresh.c @@ -1233,6 +1233,7 @@ melt_traits (void *cls, &rms->bks[index]), TALER_TESTING_make_trait_exchange_wd_value (index, &rms->alg_values[index]), + TALER_TESTING_make_trait_refresh_secret (&rms->ps), TALER_TESTING_trait_end () }; diff --git a/src/util/crypto.c b/src/util/crypto.c index 5a7dbfeee..447805bfe 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -507,10 +507,19 @@ TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet, blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size); break; case TALER_DENOMINATION_CS: + /* NOTE: it is not obvious that we need to hash the + nonce here; if we omit this, we could skip sending + the nonce in the /recoup protocol. OTOH, there is + certainly no further harm (beyond the extra + bytes send on /recoup) from including it. */ GNUNET_CRYPTO_hash_context_read ( hash_context, &blinded_planchet->details.cs_blinded_planchet.nonce, sizeof (blinded_planchet->details.cs_blinded_planchet.nonce)); + GNUNET_CRYPTO_hash_context_read ( + hash_context, + &blinded_planchet->details.cs_blinded_planchet.c[0], + sizeof (struct GNUNET_CRYPTO_CsC) * 2); break; default: GNUNET_break (0);