-towards fixing the protocol

This commit is contained in:
Christian Grothoff 2022-02-14 23:02:25 +01:00
parent f4f40a31ef
commit bd77bcb52d
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
11 changed files with 120 additions and 5 deletions

View File

@ -91,7 +91,13 @@ handle_link_data (void *cls,
TALER_JSON_pack_exchange_withdraw_values ("ewv", TALER_JSON_pack_exchange_withdraw_values ("ewv",
&pos->alg_values), &pos->alg_values),
GNUNET_JSON_pack_data_auto ("link_sig", GNUNET_JSON_pack_data_auto ("link_sig",
&pos->orig_coin_link_sig)); &pos->orig_coin_link_sig),
GNUNET_JSON_pack_allow_null (
pos->have_nonce
? GNUNET_JSON_pack_data_auto ("cs_nonce",
&pos->nonce)
: GNUNET_JSON_pack_string ("cs_nonce",
NULL)));
if ( (NULL == obj) || if ( (NULL == obj) ||
(0 != (0 !=
json_array_append_new (list, json_array_append_new (list,

View File

@ -174,6 +174,7 @@ verify_and_execute_recoup_refresh (
const struct TALER_CoinPublicInfo *coin, const struct TALER_CoinPublicInfo *coin,
const struct TALER_ExchangeWithdrawValues *exchange_vals, const struct TALER_ExchangeWithdrawValues *exchange_vals,
const union TALER_DenominationBlindingKeyP *coin_bks, const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_CsNonce *nonce,
const struct TALER_CoinSpendSignatureP *coin_sig) const struct TALER_CoinSpendSignatureP *coin_sig)
{ {
struct RecoupContext pc; struct RecoupContext pc;
@ -263,6 +264,9 @@ verify_and_execute_recoup_refresh (
TALER_EC_EXCHANGE_RECOUP_REFRESH_BLINDING_FAILED, TALER_EC_EXCHANGE_RECOUP_REFRESH_BLINDING_FAILED,
NULL); NULL);
} }
if (TALER_DENOMINATION_CS == blinded_planchet.cipher)
blinded_planchet.details.cs_blinded_planchet.nonce
= *nonce;
TALER_coin_ev_hash (&blinded_planchet, TALER_coin_ev_hash (&blinded_planchet,
&coin->denom_pub_hash, &coin->denom_pub_hash,
&h_blind); &h_blind);
@ -360,6 +364,7 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection,
union TALER_DenominationBlindingKeyP coin_bks; union TALER_DenominationBlindingKeyP coin_bks;
struct TALER_CoinSpendSignatureP coin_sig; struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_ExchangeWithdrawValues exchange_vals; struct TALER_ExchangeWithdrawValues exchange_vals;
struct TALER_CsNonce nonce;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&coin.denom_pub_hash), &coin.denom_pub_hash),
@ -371,12 +376,18 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection,
&coin_bks), &coin_bks),
GNUNET_JSON_spec_fixed_auto ("coin_sig", GNUNET_JSON_spec_fixed_auto ("coin_sig",
&coin_sig), &coin_sig),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
&nonce)),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
memset (&coin, memset (&coin,
0, 0,
sizeof (coin)); sizeof (coin));
memset (&nonce,
0,
sizeof (nonce));
coin.coin_pub = *coin_pub; coin.coin_pub = *coin_pub;
ret = TALER_MHD_parse_json_data (connection, ret = TALER_MHD_parse_json_data (connection,
root, root,
@ -392,6 +403,7 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection,
&coin, &coin,
&exchange_vals, &exchange_vals,
&coin_bks, &coin_bks,
&nonce,
&coin_sig); &coin_sig);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return res; return res;

View File

@ -177,6 +177,7 @@ verify_and_execute_recoup (
const struct TALER_CoinPublicInfo *coin, const struct TALER_CoinPublicInfo *coin,
const struct TALER_ExchangeWithdrawValues *exchange_vals, const struct TALER_ExchangeWithdrawValues *exchange_vals,
const union TALER_DenominationBlindingKeyP *coin_bks, const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_CsNonce *nonce,
const struct TALER_CoinSpendSignatureP *coin_sig) const struct TALER_CoinSpendSignatureP *coin_sig)
{ {
struct RecoupContext pc; struct RecoupContext pc;
@ -268,6 +269,9 @@ verify_and_execute_recoup (
TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED, TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED,
NULL); NULL);
} }
if (TALER_DENOMINATION_CS == blinded_planchet.cipher)
blinded_planchet.details.cs_blinded_planchet.nonce
= *nonce;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_coin_ev_hash (&blinded_planchet, TALER_coin_ev_hash (&blinded_planchet,
&coin->denom_pub_hash, &coin->denom_pub_hash,
@ -373,6 +377,7 @@ TEH_handler_recoup (struct MHD_Connection *connection,
union TALER_DenominationBlindingKeyP coin_bks; union TALER_DenominationBlindingKeyP coin_bks;
struct TALER_CoinSpendSignatureP coin_sig; struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_ExchangeWithdrawValues exchange_vals; struct TALER_ExchangeWithdrawValues exchange_vals;
struct TALER_CsNonce nonce;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&coin.denom_pub_hash), &coin.denom_pub_hash),
@ -384,12 +389,18 @@ TEH_handler_recoup (struct MHD_Connection *connection,
&coin_bks), &coin_bks),
GNUNET_JSON_spec_fixed_auto ("coin_sig", GNUNET_JSON_spec_fixed_auto ("coin_sig",
&coin_sig), &coin_sig),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
&nonce)),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
memset (&coin, memset (&coin,
0, 0,
sizeof (coin)); sizeof (coin));
memset (&nonce,
0,
sizeof (nonce));
coin.coin_pub = *coin_pub; coin.coin_pub = *coin_pub;
ret = TALER_MHD_parse_json_data (connection, ret = TALER_MHD_parse_json_data (connection,
root, root,
@ -408,6 +419,7 @@ TEH_handler_recoup (struct MHD_Connection *connection,
&coin, &coin,
&exchange_vals, &exchange_vals,
&coin_bks, &coin_bks,
&nonce,
&coin_sig); &coin_sig);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return res; return res;

View File

@ -634,6 +634,13 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
rrc->blinded_planchet = rcds[i].blinded_planchet; rrc->blinded_planchet = rcds[i].blinded_planchet;
} }
// FIXME: in CS-case, we MUST check if signatures
// already exist under the given nonce
// (TODO: check: refresh session hash OK?), and if so,
// we MUST return the existing signatures (c0/c1 may have changed!)
// and MUST NOT return the fresh signatures!
// => change this to a 'do_refresh_reveal' and
// change SQL to return existing signatures (if any)!
qs = TEH_plugin->insert_refresh_reveal (TEH_plugin->cls, qs = TEH_plugin->insert_refresh_reveal (TEH_plugin->cls,
melt_serial_id, melt_serial_id,
num_fresh_coins, num_fresh_coins,

View File

@ -535,6 +535,10 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
/* Clean up and send back final response */ /* Clean up and send back final response */
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
// FIXME: in CS-case, we MUST re-transmit any _existing_ signature
// (if database had a record matching the nonce)
// instead of sending a 'fresh' one back (as c0/c1 may differ in
// a client attack!
{ {
MHD_RESULT ret; MHD_RESULT ret;

View File

@ -1218,6 +1218,7 @@ prepare_statements (struct PostgresClosure *pg)
",rrc.ewv" ",rrc.ewv"
",rrc.link_sig" ",rrc.link_sig"
",rrc.freshcoin_index" ",rrc.freshcoin_index"
",rrc.coin_ev"
" FROM refresh_commitments" " FROM refresh_commitments"
" JOIN refresh_revealed_coins rrc" " JOIN refresh_revealed_coins rrc"
" USING (melt_serial_id)" " USING (melt_serial_id)"
@ -6385,6 +6386,7 @@ add_ldl (void *cls,
pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkList); pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkList);
{ {
struct TALER_BlindedPlanchet bp;
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("transfer_pub", GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
&transfer_pub), &transfer_pub),
@ -6398,6 +6400,8 @@ add_ldl (void *cls,
&pos->alg_values), &pos->alg_values),
TALER_PQ_result_spec_denom_pub ("denom_pub", TALER_PQ_result_spec_denom_pub ("denom_pub",
&pos->denom_pub), &pos->denom_pub),
TALER_PQ_result_spec_blinded_planchet ("coin_ev",
&bp),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
@ -6411,6 +6415,12 @@ add_ldl (void *cls,
ldctx->status = GNUNET_SYSERR; ldctx->status = GNUNET_SYSERR;
return; return;
} }
if (TALER_DENOMINATION_CS == bp.cipher)
{
pos->nonce = bp.details.cs_blinded_planchet.nonce;
pos->have_nonce = true;
}
TALER_blinded_planchet_free (&bp);
} }
if ( (NULL != ldctx->last) && if ( (NULL != ldctx->last) &&
(0 == GNUNET_memcmp (&transfer_pub, (0 == GNUNET_memcmp (&transfer_pub,

View File

@ -1380,12 +1380,21 @@ struct TALER_EXCHANGEDB_LinkList
*/ */
struct TALER_CoinSpendSignatureP orig_coin_link_sig; struct TALER_CoinSpendSignatureP orig_coin_link_sig;
/**
* CS nonce, if cipher is CS.
*/
struct TALER_CsNonce nonce;
/** /**
* Offset that generated this coin in the refresh * Offset that generated this coin in the refresh
* operation. * operation.
*/ */
uint32_t coin_refresh_offset; uint32_t coin_refresh_offset;
/**
* Set to true if @e nonce was initialized.
*/
bool have_nonce;
}; };

View File

@ -92,6 +92,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
struct TALER_CoinSpendSignatureP link_sig; struct TALER_CoinSpendSignatureP link_sig;
union TALER_DenominationBlindingKeyP bks; union TALER_DenominationBlindingKeyP bks;
struct TALER_ExchangeWithdrawValues alg_values; struct TALER_ExchangeWithdrawValues alg_values;
struct TALER_CsNonce nonce;
uint32_t coin_idx; uint32_t coin_idx;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_denom_pub ("denom_pub", TALER_JSON_spec_denom_pub ("denom_pub",
@ -104,6 +105,9 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
&link_sig), &link_sig),
GNUNET_JSON_spec_uint32 ("coin_idx", GNUNET_JSON_spec_uint32 ("coin_idx",
&coin_idx), &coin_idx),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
&nonce)),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
struct TALER_TransferSecretP secret; struct TALER_TransferSecretP secret;
@ -111,6 +115,9 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
struct TALER_CoinPubHash c_hash; struct TALER_CoinPubHash c_hash;
/* parse reply */ /* parse reply */
memset (&nonce,
0,
sizeof (nonce));
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (json, GNUNET_JSON_parse (json,
spec, spec,
@ -143,6 +150,16 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (TALER_DENOMINATION_CS == alg_values.cipher)
{
if (GNUNET_is_zero (&nonce))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
pd.blinded_planchet.details.cs_blinded_planchet.nonce = nonce;
}
/* extract coin and signature */ /* extract coin and signature */
if (GNUNET_OK != if (GNUNET_OK !=
TALER_denom_sig_unblind (&lci->sig, TALER_denom_sig_unblind (&lci->sig,

View File

@ -328,6 +328,25 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
&coin_sig), &coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
&bks)); &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]; char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
char *end; char *end;

View File

@ -332,6 +332,27 @@ TALER_EXCHANGE_recoup_refresh (
&coin_sig), &coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
&bks)); &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 (rms,
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 pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
char *end; char *end;

View File

@ -806,18 +806,16 @@ TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet,
blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size); blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size);
break; break;
case TALER_DENOMINATION_CS: case TALER_DENOMINATION_CS:
// FIMXE: c-values MUST NOT be included in idempotency check
// during withdraw/refresh, but right now they are!!!
GNUNET_CRYPTO_hash_context_read ( GNUNET_CRYPTO_hash_context_read (
hash_context, hash_context,
&blinded_planchet->details.cs_blinded_planchet.c[0], &blinded_planchet->details.cs_blinded_planchet.c[0],
sizeof (struct GNUNET_CRYPTO_CsC) * 2); sizeof (struct GNUNET_CRYPTO_CsC) * 2);
#if FIXME
/* Must include this for refresh check, but
must EXCLUDE this in link signature (see TALER_LinkDataPS!) */
GNUNET_CRYPTO_hash_context_read ( GNUNET_CRYPTO_hash_context_read (
hash_context, hash_context,
&blinded_planchet->details.cs_blinded_planchet.nonce, &blinded_planchet->details.cs_blinded_planchet.nonce,
sizeof (struct TALER_CsNonce)); sizeof (struct TALER_CsNonce));
#endif
break; break;
default: default:
GNUNET_break (0); GNUNET_break (0);