From fe18c104d8a8d3667b3c872078549c7e78c71db0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 8 Nov 2022 14:56:32 +0100 Subject: -fix #7428 --- src/lib/exchange_api_common.h | 10 ++- src/lib/exchange_api_reserves_open.c | 129 +++++++++++++++++++++++++++++++---- 2 files changed, 121 insertions(+), 18 deletions(-) (limited to 'src/lib') diff --git a/src/lib/exchange_api_common.h b/src/lib/exchange_api_common.h index 80c36daf1..1b9ddce34 100644 --- a/src/lib/exchange_api_common.h +++ b/src/lib/exchange_api_common.h @@ -129,12 +129,10 @@ TALER_EXCHANGE_check_coin_amount_conflict_ ( /** - * Verify that @a proof contains a coin history - * that demonstrates that @a coin_pub was previously - * used with a denomination key that is different - * from @a ch_denom_pub. Note that the coin history - * MUST have been checked before using - * #TALER_EXCHANGE_check_coin_amount_conflict_(). + * Verify that @a proof contains a coin history that demonstrates that @a + * coin_pub was previously used with a denomination key that is different from + * @a ch_denom_pub. Note that the coin history MUST have been checked before + * using #TALER_EXCHANGE_check_coin_amount_conflict_(). * * @param proof a proof to check * @param ch_denom_pub hash of the conflicting denomination diff --git a/src/lib/exchange_api_reserves_open.c b/src/lib/exchange_api_reserves_open.c index 64d259ba0..2b7ef0d90 100644 --- a/src/lib/exchange_api_reserves_open.c +++ b/src/lib/exchange_api_reserves_open.c @@ -27,11 +27,39 @@ #include #include "taler_exchange_service.h" #include "taler_json_lib.h" +#include "exchange_api_common.h" #include "exchange_api_handle.h" #include "taler_signatures.h" #include "exchange_api_curl_defaults.h" +/** + * Information we keep per coin to validate the reply. + */ +struct CoinData +{ + /** + * Public key of the coin. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Signature by the coin. + */ + struct TALER_CoinSpendSignatureP coin_sig; + + /** + * The hash of the denomination's public key + */ + struct TALER_DenominationHashP h_denom_pub; + + /** + * How much did this coin contribute. + */ + struct TALER_Amount contribution; +}; + + /** * @brief A /reserves/$RID/open Handle */ @@ -69,6 +97,16 @@ struct TALER_EXCHANGE_ReservesOpenHandle */ void *cb_cls; + /** + * Information we keep per coin to validate the reply. + */ + struct CoinData *coins; + + /** + * Length of the @e coins array. + */ + unsigned int num_coins; + /** * Public key of the reserve we are querying. */ @@ -282,12 +320,74 @@ handle_reserves_open_finished (void *cls, rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_CONFLICT: - // FIXME: not yet specified (#7428), but needed in - // case of double-spending or insufficient - // reserve balance! - rs.hr.ec = TALER_JSON_get_error_code (j); - rs.hr.hint = TALER_JSON_get_error_hint (j); - break; + { + const struct TALER_EXCHANGE_Keys *keys; + const struct CoinData *cd = NULL; + struct TALER_CoinSpendPublicKeyP coin_pub; + const struct TALER_EXCHANGE_DenomPublicKey *dk; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("coin_pub", + &coin_pub), + GNUNET_JSON_spec_end () + }; + + keys = TALER_EXCHANGE_get_keys (roh->exchange); + GNUNET_assert (NULL != keys); + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + rs.hr.http_status = 0; + rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + for (unsigned int i = 0; inum_coins; i++) + { + const struct CoinData *cdi = &roh->coins[i]; + + if (0 == GNUNET_memcmp (&coin_pub, + &cdi->coin_pub)) + { + cd = cdi; + break; + } + } + if (NULL == cd) + { + GNUNET_break_op (0); + rs.hr.http_status = 0; + rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys, + &cd->h_denom_pub); + if (NULL == dk) + { + GNUNET_break_op (0); + rs.hr.http_status = 0; + rs.hr.ec = TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR; + break; + } + if (GNUNET_OK != + TALER_EXCHANGE_check_coin_conflict_ (keys, + j, + dk, + &coin_pub, + &cd->coin_sig, + &cd->contribution)) + { + GNUNET_break_op (0); + rs.hr.http_status = 0; + rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); + break; + } case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: if (GNUNET_OK != handle_reserves_open_kyc (roh, @@ -402,18 +502,21 @@ TALER_EXCHANGE_reserves_open ( min_purses, reserve_priv, &roh->reserve_sig); + roh->coins = GNUNET_new_array (coin_payments_length, + struct CoinData); cpa = json_array (); GNUNET_assert (NULL != cpa); for (unsigned int i = 0; iage_commitment_proof; struct TALER_AgeCommitmentHash ahac; struct TALER_AgeCommitmentHash *achp = NULL; + struct CoinData *cd = &roh->coins[i]; json_t *cp; + cd->contribution = pd->amount; + cd->h_denom_pub = pd->h_denom_pub; if (NULL != acp) { TALER_age_commitment_hash (&acp->commitment, @@ -423,9 +526,9 @@ TALER_EXCHANGE_reserves_open ( TALER_wallet_reserve_open_deposit_sign (&pd->amount, &roh->reserve_sig, &pd->coin_priv, - &coin_sig); + &cd->coin_sig); GNUNET_CRYPTO_eddsa_key_get_public (&pd->coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); + &cd->coin_pub.eddsa_pub); cp = GNUNET_JSON_PACK ( GNUNET_JSON_pack_allow_null ( @@ -438,9 +541,9 @@ TALER_EXCHANGE_reserves_open ( TALER_JSON_pack_denom_sig ("ub_sig", &pd->denom_sig), GNUNET_JSON_pack_data_auto ("coin_pub", - &coin_pub), + &cd->coin_pub), GNUNET_JSON_pack_data_auto ("coin_sig", - &coin_sig)); + &cd->coin_sig)); GNUNET_assert (0 == json_array_append_new (cpa, cp)); @@ -468,6 +571,7 @@ TALER_EXCHANGE_reserves_open ( GNUNET_break (0); curl_easy_cleanup (eh); json_decref (open_obj); + GNUNET_free (roh->coins); GNUNET_free (roh->url); GNUNET_free (roh); return NULL; @@ -494,6 +598,7 @@ TALER_EXCHANGE_reserves_open_cancel ( roh->job = NULL; } TALER_curl_easy_post_finished (&roh->post_ctx); + GNUNET_free (roh->coins); GNUNET_free (roh->url); GNUNET_free (roh); } -- cgit v1.2.3