-get recoup/refresh to pass

This commit is contained in:
Christian Grothoff 2022-02-10 20:15:17 +01:00
parent ed5ef2b5f7
commit d58d89dcab
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
12 changed files with 163 additions and 58 deletions

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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

View File

@ -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);

View File

@ -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) \

View File

@ -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];

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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 ()
};

View File

@ -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);