exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 1e7931392a5abd759afee2a18065ccbcdbf6176f
parent 694cd42c3f4fdae4dc0a2261cc7da88e12e2a62f
Author: Florian Dold <dold@taler.net>
Date:   Mon, 31 Aug 2026 23:29:18 +0200

CS withdraw: derive nonces from blinding seeds

Diffstat:
Msrc/include/taler/exchange/post-recoup-withdraw.h | 4++++
Msrc/include/taler/taler_crypto_lib.h | 12------------
Msrc/lib/exchange_api_post-recoup-withdraw.c | 24++++++++++++++++++------
Msrc/testing/testing_api_cmd_recoup.c | 11+++++++++++
Msrc/util/crypto.c | 16----------------
Msrc/util/test_crypto.c | 21+++++++++++++++------
Msrc/util/test_helper_cs.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
7 files changed, 102 insertions(+), 58 deletions(-)

diff --git a/src/include/taler/exchange/post-recoup-withdraw.h b/src/include/taler/exchange/post-recoup-withdraw.h @@ -43,6 +43,8 @@ struct TALER_EXCHANGE_PostRecoupWithdrawHandle; * @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 blinding_seed seed used to derive the CS nonce; NULL for RSA + * @param coin_offset offset of the coin in the original withdraw operation * @param ps secret internals of the original planchet * @param h_planchets hash of the commitment of the corresponding original withdraw request * @return handle to operation, NULL if the inputs are invalid @@ -55,6 +57,8 @@ TALER_EXCHANGE_post_recoup_withdraw_create ( const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_DenominationSignature *denom_sig, const struct TALER_ExchangeBlindingValues *exchange_vals, + const struct TALER_BlindingMasterSeedP *blinding_seed, + uint32_t coin_offset, const struct TALER_PlanchetMasterSecretP *ps, const struct TALER_HashBlindedPlanchetsP *h_planchets); diff --git a/src/include/taler/taler_crypto_lib.h b/src/include/taler/taler_crypto_lib.h @@ -1930,18 +1930,6 @@ TALER_planchet_setup_coin_priv ( /** - * @brief Method to derive withdraw /csr nonce - * - * @param ps seed for the coins' planchet - * @param[out] nonce withdraw nonce included in the request to generate R_0 and R_1 - */ -void -TALER_cs_withdraw_nonce_derive ( - const struct TALER_PlanchetMasterSecretP *ps, - struct GNUNET_CRYPTO_CsSessionNonce *nonce); - - -/** * @brief Method to derive a seed for blinding from a seed for withdraw * * @param seed input withdraw seed diff --git a/src/lib/exchange_api_post-recoup-withdraw.c b/src/lib/exchange_api_post-recoup-withdraw.c @@ -238,6 +238,8 @@ TALER_EXCHANGE_post_recoup_withdraw_create ( const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_DenominationSignature *denom_sig, const struct TALER_ExchangeBlindingValues *exchange_vals, + const struct TALER_BlindingMasterSeedP *blinding_seed, + uint32_t coin_offset, const struct TALER_PlanchetMasterSecretP *ps, const struct TALER_HashBlindedPlanchetsP *h_planchets) { @@ -292,13 +294,23 @@ TALER_EXCHANGE_post_recoup_withdraw_create ( case GNUNET_CRYPTO_BSA_CS: { union GNUNET_CRYPTO_BlindSessionNonce nonce; + const uint32_t indices[] = { coin_offset }; - /* 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.cs_nonce); + if (NULL == blinding_seed) + { + json_decref (ph->body); + GNUNET_free (ph->base_url); + TALER_EXCHANGE_keys_decref (ph->keys); + GNUNET_free (ph); + GNUNET_break (0); + return NULL; + } + TALER_cs_derive_only_cs_blind_nonces_from_seed ( + blinding_seed, + false, + 1, + indices, + &nonce); GNUNET_assert ( 0 == json_object_set_new (ph->body, diff --git a/src/testing/testing_api_cmd_recoup.c b/src/testing/testing_api_cmd_recoup.c @@ -192,6 +192,7 @@ recoup_run (void *cls, const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; const struct TALER_DenominationSignature *coin_sig; const struct TALER_WithdrawMasterSeedP *seed; + const struct TALER_BlindingMasterSeedP *blinding_seed; const struct TALER_HashBlindedPlanchetsP *h_planchets; struct TALER_PlanchetMasterSecretP secret; char *cref; @@ -248,6 +249,14 @@ recoup_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } + if (GNUNET_OK != + TALER_TESTING_get_trait_blinding_seed (coin_cmd, + &blinding_seed)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } GNUNET_CRYPTO_eddsa_key_get_public ( &coin_priv->eddsa_priv, &ps->reserve_history.details.recoup_details.coin_pub.eddsa_pub); @@ -302,6 +311,8 @@ recoup_run (void *cls, denom_pub, coin_sig, ewv, + blinding_seed, + idx, &secret, h_planchets); GNUNET_assert (NULL != ps->ph); diff --git a/src/util/crypto.c b/src/util/crypto.c @@ -245,22 +245,6 @@ TALER_transfer_secret_to_planchet_secret ( void -TALER_cs_withdraw_nonce_derive ( - const struct TALER_PlanchetMasterSecretP *ps, - struct GNUNET_CRYPTO_CsSessionNonce *nonce) -{ - GNUNET_assert (GNUNET_YES == - GNUNET_CRYPTO_hkdf_gnunet ( - nonce, - sizeof (*nonce), - "n", - strlen ("n"), - ps, - sizeof(*ps))); -} - - -void TALER_cs_withdraw_seed_to_blinding_seed ( const struct TALER_WithdrawMasterSeedP *seed, struct TALER_BlindingMasterSeedP *blinding_seed) diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c @@ -214,6 +214,8 @@ test_planchets_rsa (uint8_t age) static int test_planchets_cs (uint8_t age) { + struct TALER_WithdrawMasterSeedP seed; + struct TALER_BlindingMasterSeedP blinding_seed; struct TALER_PlanchetMasterSecretP ps; struct TALER_CoinSpendPrivateKeyP coin_priv; union GNUNET_CRYPTO_BlindingSecretP bks; @@ -227,6 +229,7 @@ test_planchets_cs (uint8_t age) struct TALER_ExchangeBlindingValues alg_values; struct TALER_AgeCommitmentHashP *ach = NULL; struct TALER_AgeCommitmentHashP ah = {0}; + const uint32_t coin_offset = 0; if (0 < age) { @@ -245,16 +248,22 @@ test_planchets_cs (uint8_t age) TALER_age_commitment_proof_free (&acp); } - GNUNET_CRYPTO_random_block (&ps, - sizeof (ps)); + TALER_withdraw_master_seed_setup_random (&seed); + TALER_withdraw_expand_secrets (1, + &seed, + &ps); + TALER_cs_withdraw_seed_to_blinding_seed (&seed, + &blinding_seed); GNUNET_assert (GNUNET_OK == TALER_denom_priv_create (&dk_priv, &dk_pub, GNUNET_CRYPTO_BSA_CS)); -#pragma message "phase out TALER_cs_withdraw_nonce_derive" - TALER_cs_withdraw_nonce_derive ( - &ps, - &nonce.cs_nonce); + TALER_cs_derive_only_cs_blind_nonces_from_seed ( + &blinding_seed, + false, + 1, + &coin_offset, + &nonce); // FIXME: define Taler abstraction for this: alg_values.blinding_inputs = GNUNET_CRYPTO_get_blinding_input_values (dk_priv.bsign_priv_key, diff --git a/src/util/test_helper_cs.c b/src/util/test_helper_cs.c @@ -255,6 +255,43 @@ test_revocation (struct TALER_CRYPTO_CsDenominationHelper *dh) /** + * Set up planchet secrets and Clause-Schnorr nonces for a withdraw-like + * operation. + * + * @param num number of planchets and nonces to derive + * @param for_melt true to use the refresh nonce domain + * @param[out] ps planchet secrets to initialize + * @param[out] nonces Clause-Schnorr nonces to initialize + */ +static void +setup_withdraw_secrets ( + size_t num, + bool for_melt, + struct TALER_PlanchetMasterSecretP ps[static num], + union GNUNET_CRYPTO_BlindSessionNonce nonces[static num]) +{ + struct TALER_WithdrawMasterSeedP seed; + struct TALER_BlindingMasterSeedP blinding_seed; + uint32_t indices[num]; + + TALER_withdraw_master_seed_setup_random (&seed); + TALER_withdraw_expand_secrets (num, + &seed, + ps); + TALER_cs_withdraw_seed_to_blinding_seed (&seed, + &blinding_seed); + for (uint32_t i = 0; i<num; i++) + indices[i] = i; + TALER_cs_derive_only_cs_blind_nonces_from_seed ( + &blinding_seed, + for_melt, + num, + indices, + nonces); +} + + +/** * Test R derivation logic. * * @param dh handle to the helper @@ -277,7 +314,10 @@ test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) }; union GNUNET_CRYPTO_BlindSessionNonce nonce; - TALER_planchet_master_setup_random (&ps); + setup_withdraw_secrets (1, + false, + &ps, + &nonce); for (unsigned int i = 0; i<MAX_KEYS; i++) { struct TALER_PlanchetDetail pd; @@ -286,10 +326,6 @@ test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) continue; GNUNET_assert (GNUNET_CRYPTO_BSA_CS == keys[i].denom_pub.bsign_pub_key->cipher); -#pragma message "phase out TALER_cs_withdraw_nonce_derive" - TALER_cs_withdraw_nonce_derive ( - &ps, - &nonce.cs_nonce); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting R derivation with key %s\n", GNUNET_h2s (&keys[i].h_cs.hash)); @@ -438,7 +474,10 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) }; union GNUNET_CRYPTO_BlindSessionNonce nonce; - TALER_planchet_master_setup_random (&ps); + setup_withdraw_secrets (1, + false, + &ps, + &nonce); for (unsigned int i = 0; i<MAX_KEYS; i++) { if (! keys[i].valid) @@ -451,8 +490,6 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) .nonce = &nonce.cs_nonce }; - TALER_cs_withdraw_nonce_derive (&ps, - &nonce.cs_nonce); ec = TALER_CRYPTO_helper_cs_r_batch_derive ( dh, 1, @@ -636,8 +673,10 @@ test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, struct TALER_ExchangeBlindingValues alg_values[batch_size]; union GNUNET_CRYPTO_BlindSessionNonce nonces[batch_size]; - for (unsigned int i = 0; i<batch_size; i++) - TALER_planchet_master_setup_random (&ps[i]); + setup_withdraw_secrets (batch_size, + false, + ps, + nonces); for (unsigned int k = 0; k<MAX_KEYS; k++) { if (! keys[k].valid) @@ -652,9 +691,6 @@ test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, { cdr[i].h_cs = &keys[k].h_cs; cdr[i].nonce = &nonces[i].cs_nonce; - TALER_cs_withdraw_nonce_derive ( - &ps[i], - &nonces[i].cs_nonce); bi[i].cipher = GNUNET_CRYPTO_BSA_CS; alg_values[i].blinding_inputs = &bi[i]; } @@ -853,8 +889,12 @@ perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, struct TALER_ExchangeBlindingValues alg_values = { .blinding_inputs = &bv }; + union GNUNET_CRYPTO_BlindSessionNonce nonce; - TALER_planchet_master_setup_random (&ps); + setup_withdraw_secrets (1, + true, + &ps, + &nonce); duration = GNUNET_TIME_UNIT_ZERO; TALER_CRYPTO_helper_cs_poll (dh); for (unsigned int j = 0; j<NUM_SIGN_PERFS;) @@ -876,15 +916,11 @@ perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, { struct TALER_CoinPubHashP c_hash; struct TALER_PlanchetDetail pd; - union GNUNET_CRYPTO_BlindSessionNonce nonce; struct TALER_CRYPTO_CsDeriveRequest cdr = { .h_cs = &keys[i].h_cs, .nonce = &nonce.cs_nonce }; - TALER_cs_withdraw_nonce_derive ( - &ps, - &nonce.cs_nonce); ec = TALER_CRYPTO_helper_cs_r_batch_derive ( dh, 1,