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:
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,