diff options
author | Gian Demarmels <gian@demarmels.org> | 2022-02-05 00:33:16 +0100 |
---|---|---|
committer | Gian Demarmels <gian@demarmels.org> | 2022-02-05 00:33:16 +0100 |
commit | f46dc9ea5eb035d39631955ff7330456a3021301 (patch) | |
tree | 2117f7e4589614113b18c53ff38731e10d18801d | |
parent | c42376cf400b59006fa9ed0a195c014d10c7eca1 (diff) | |
parent | aea7fc36c3da02d550367c41ae7dd758daf698f4 (diff) | |
download | exchange-f46dc9ea5eb035d39631955ff7330456a3021301.tar.xz |
Merge branch 'master' of ssh://git.taler.net/exchange
-rw-r--r-- | src/include/taler_exchange_service.h | 160 | ||||
-rw-r--r-- | src/lib/exchange_api_melt.c | 230 | ||||
-rw-r--r-- | src/lib/exchange_api_refresh_common.c | 364 | ||||
-rw-r--r-- | src/lib/exchange_api_refresh_common.h | 26 | ||||
-rw-r--r-- | src/lib/exchange_api_refreshes_reveal.c | 59 |
5 files changed, 376 insertions, 463 deletions
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 92ff3bf14..3aa39031c 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -1063,9 +1063,15 @@ struct TALER_EXCHANGE_CsRResponse struct { /** - * Signature over the coin. + * Length of the @e alg_values array. + */ + unsigned int arg_values_len; + + /** + * Values contributed by the exchange for the + * respective coin's withdraw operation. */ - struct TALER_DenominationCsPublicR r_pubs; + const struct TALER_ExchangeWithdrawValues *alg_values; } success; /** @@ -1093,11 +1099,28 @@ typedef void /** + * Information we pass per coin to a /csr request. + */ +struct TALER_EXCHANGE_NonceKey +{ + /** + * Which denomination key is the /csr request for? + */ + const struct TALER_EXCHANGE_DenomPublicKey *pk; + + /** + * What is the client nonce for the request? + */ + struct TALER_CsNonce nonce; +}; + + +/** * Get a CS R using a /csr request. * * @param exchange the exchange handle; the exchange must be ready to operate - * @param pk denomination of coin the R's will be used for - * @param nonce public nonce for CS R request + * @param nks_len length of the @a nks array + * @param nks array of denominations and nonces * @param res_cb the callback to call when the final result for this request is available * @param res_cb_cls closure for the above callback * @return handle for the operation on success, NULL on error, i.e. @@ -1106,8 +1129,8 @@ typedef void */ struct TALER_EXCHANGE_CsRHandle * TALER_EXCHANGE_csr (struct TALER_EXCHANGE_Handle *exchange, - const struct TALER_EXCHANGE_DenomPublicKey *pk, - const struct TALER_CsNonce *nonce, + unsigned int nks_len, + struct TALER_EXCHANGE_NonceKey *nks, TALER_EXCHANGE_CsRCallback res_cb, void *res_cb_cls); @@ -1384,9 +1407,20 @@ struct TALER_EXCHANGE_WithdrawResponse struct { /** + * Private key of the coin. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; + + /** * Signature over the coin. */ struct TALER_DenominationSignature sig; + + /** + * Values contributed from the exchange during the + * withdraw protocol. + */ + struct TALER_ExchangeWithdrawValues exchange_vals; } success; /** @@ -1541,50 +1575,45 @@ TALER_EXCHANGE_withdraw2_cancel (struct TALER_EXCHANGE_Withdraw2Handle *wh); /** - * Melt (partially spent) coins to obtain fresh coins that are - * unlinkable to the original coin(s). Note that melting more - * than one coin in a single request will make those coins linkable, - * so the safest operation only melts one coin at a time. - * - * This API is typically used by a wallet. Note that to ensure that - * no money is lost in case of hardware failures, is operation does - * not actually initiate the request. Instead, it generates a buffer - * which the caller must store before proceeding with the actual call - * to #TALER_EXCHANGE_melt() that will generate the request. - * - * This function does verify that the given request data is internally - * consistent. However, the @a melts_sigs are NOT verified. - * - * Aside from some non-trivial cryptographic operations that might - * take a bit of CPU time to complete, this function returns - * its result immediately and does not start any asynchronous - * processing. This function is also thread-safe. - * - * @param melt_priv private keys of the coin to melt - * @param melt_amount amount specifying how much - * the coin will contribute to the melt (including fee) - * @param melt_sig signatures affirming the - * validity of the public keys corresponding to the - * @a melt_priv private key - * @param melt_pk denomination key information - * record corresponding to the @a melt_sig - * validity of the keys - * @param fresh_pks_len length of the @a pks array - * @param fresh_pks array of @a pks_len denominations of fresh coins to create - * @return NULL - * if the inputs are invalid (i.e. denomination key not with this exchange). - * Otherwise, JSON data structure to store persistently - * before proceeding to #TALER_EXCHANGE_melt(). - * Non-null results should be freed using GNUNET_free(). + * Information needed to melt (partially spent) coins to obtain fresh coins + * that are unlinkable to the original coin(s). Note that melting more than + * one coin in a single request will make those coins linkable, so we only melt one coin at a time. */ -json_t * -TALER_EXCHANGE_refresh_prepare ( - const struct TALER_CoinSpendPrivateKeyP *melt_priv, - const struct TALER_Amount *melt_amount, - const struct TALER_DenominationSignature *melt_sig, - const struct TALER_EXCHANGE_DenomPublicKey *melt_pk, - unsigned int fresh_pks_len, - const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks); +struct TALER_EXCHANGE_RefreshData +{ + /** + * private key of the coin to melt + */ + struct TALER_CoinSpendPrivateKeyP melt_priv; + + /** + * amount specifying how much the coin will contribute to the melt + * (including fee) + */ + struct TALER_Amount melt_amount; + + /** + * signatures affirming the validity of the public keys corresponding to the + * @e melt_priv private key + */ + struct TALER_DenominationSignature melt_sig; + + /** + * denomination key information record corresponding to the @e melt_sig + * validity of the keys + */ + struct TALER_EXCHANGE_DenomPublicKey melt_pk; + + /** + * array of @e pks_len denominations of fresh coins to create + */ + const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks; + + /** + * length of the @e pks array + */ + unsigned int fresh_pks_len; +}; /* ********************* /coins/$COIN_PUB/melt ***************************** */ @@ -1603,6 +1632,8 @@ struct TALER_EXCHANGE_MeltHandle; * * @param cls closure * @param hr HTTP response data + * @param num_coins number of fresh coins to be created, length of the @a exchange_vals array, 0 if the operation failed + * @param alg_values array @a num_coins of exchange values contributed to the refresh operation * @param noreveal_index choice by the exchange in the cut-and-choose protocol, * UINT32_MAX on error * @param sign_key exchange key used to sign @a full_response, or NULL @@ -1611,6 +1642,8 @@ typedef void (*TALER_EXCHANGE_MeltCallback) ( void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, + unsigned int num_coins, + const struct TALER_ExchangeWithdrawValues *alg_values, uint32_t noreveal_index, const struct TALER_ExchangePublicKeyP *sign_key); @@ -1626,8 +1659,8 @@ typedef void * prior to calling this function. * * @param exchange the exchange handle; the exchange must be ready to operate - * @param refresh_data the refresh data as returned from - #TALER_EXCHANGE_refresh_prepare()) + * @param ps the fresh secret that defines the refresh operation + * @param rd the refresh data specifying the characteristics of the operation * @param melt_cb the callback to call with the result * @param melt_cb_cls closure for @a melt_cb * @return a handle for this request; NULL if the argument was invalid. @@ -1635,7 +1668,8 @@ typedef void */ struct TALER_EXCHANGE_MeltHandle * TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange, - const json_t *refresh_data, + const struct TALER_PlanchetSecretsP *ps, + const struct TALER_EXCHANGE_RefreshData *rd, TALER_EXCHANGE_MeltCallback melt_cb, void *melt_cb_cls); @@ -1664,6 +1698,7 @@ TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh); * @param cls closure * @param hr HTTP response data * @param num_coins number of fresh coins created, length of the @a sigs and @a coin_privs arrays, 0 if the operation failed + * @param exchange_vals array of contributions from the exchange on the refreshes * @param coin_privs array of @a num_coins private keys for the coins that were created, NULL on error * @param sigs array of signature over @a num_coins coins, NULL on error */ @@ -1672,7 +1707,7 @@ typedef void void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, unsigned int num_coins, - const struct TALER_PlanchetSecretsP *coin_privs, + const struct TALER_CoinSpendPrivateKeyP *coin_privs, const struct TALER_DenominationSignature *sigs); @@ -1692,8 +1727,10 @@ struct TALER_EXCHANGE_RefreshesRevealHandle; * prior to calling this function. * * @param exchange the exchange handle; the exchange must be ready to operate - * @param refresh_data the refresh data as returned from - #TALER_EXCHANGE_refresh_prepare()) + * @param ps the fresh secret that defines the refresh operation + * @param rd the refresh data that characterizes the refresh operation + * @param num_coins number of fresh coins to be created, length of the @a exchange_vals array, must match value in @a rd + * @param alg_values array @a num_coins of exchange values contributed to the refresh operation * @param noreveal_index response from the exchange to the * #TALER_EXCHANGE_melt() invocation * @param reveal_cb the callback to call with the final result of the @@ -1705,7 +1742,10 @@ struct TALER_EXCHANGE_RefreshesRevealHandle; struct TALER_EXCHANGE_RefreshesRevealHandle * TALER_EXCHANGE_refreshes_reveal ( struct TALER_EXCHANGE_Handle *exchange, - const json_t *refresh_data, + const struct TALER_PlanchetSecretsP *ps, + const struct TALER_EXCHANGE_RefreshData *rd, + unsigned int num_coins, + const struct TALER_ExchangeWithdrawValues *alg_values, uint32_t noreveal_index, TALER_EXCHANGE_RefreshesRevealCallback reveal_cb, void *reveal_cb_cls); @@ -2124,6 +2164,7 @@ typedef void * @param exchange the exchange handle; the exchange must be ready to operate * @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 planchet * @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 @@ -2135,6 +2176,7 @@ struct TALER_EXCHANGE_RecoupHandle * TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange, const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_DenominationSignature *denom_sig, + const struct TALER_ExchangeWithdrawValues *exchange_vals, const struct TALER_PlanchetSecretsP *ps, TALER_EXCHANGE_RecoupResultCallback recoup_cb, void *recoup_cb_cls); @@ -2184,7 +2226,8 @@ typedef void * @param exchange the exchange handle; the exchange must be ready to operate * @param pk kind of coin to pay back * @param denom_sig signature over the coin by the exchange using @a pk - * @param ps secret internals of the original planchet + * @param exchange_vals contribution from the exchange on the withdraw + * @param ps secret internals of the original refresh-reveal operation * @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 @@ -2196,6 +2239,7 @@ TALER_EXCHANGE_recoup_refresh ( struct TALER_EXCHANGE_Handle *exchange, const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_DenominationSignature *denom_sig, + const struct TALER_ExchangeWithdrawValues *exchange_vals, const struct TALER_PlanchetSecretsP *ps, TALER_EXCHANGE_RecoupRefreshResultCallback recoup_cb, void *recoup_cb_cls); diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c index f375171b9..dc9a400d6 100644 --- a/src/lib/exchange_api_melt.c +++ b/src/lib/exchange_api_melt.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015-2021 Taler Systems SA + Copyright (C) 2015-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -73,7 +73,23 @@ struct TALER_EXCHANGE_MeltHandle /** * Actual information about the melt operation. */ - struct MeltData *md; + struct MeltData md; + + /** + * The secret the entire melt operation is seeded from. + */ + const struct TALER_PlanchetSecretsP *ps; + + /** + * Details about the characteristics of the requested melt operation. + */ + const struct TALER_EXCHANGE_RefreshData *rd; + + /** + * Array of `num_fresh_coins` contributory values of + * the exchange to the melt operation. + */ + struct TALER_ExchangeWithdrawValues *alg_values; /** * Public key of the coin being melted. @@ -106,9 +122,12 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh, struct TALER_ExchangeSignatureP exchange_sig; const struct TALER_EXCHANGE_Keys *key_state; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub), - GNUNET_JSON_spec_uint32 ("noreveal_index", noreveal_index), + GNUNET_JSON_spec_fixed_auto ("exchange_sig", + &exchange_sig), + GNUNET_JSON_spec_fixed_auto ("exchange_pub", + exchange_pub), + GNUNET_JSON_spec_uint32 ("noreveal_index", + noreveal_index), GNUNET_JSON_spec_end () }; struct TALER_RefreshMeltConfirmationPS confirm; @@ -338,6 +357,12 @@ handle_melt_finished (void *cls, { mh->melt_cb (mh->melt_cb_cls, &hr, + (0 == hr.http_status) + ? 0 + : mh->rd->fresh_pks_len, + (0 == hr.http_status) + ? NULL + : mh->alg_values, noreveal_index, (0 == hr.http_status) ? NULL @@ -419,40 +444,37 @@ handle_melt_finished (void *cls, if (NULL != mh->melt_cb) mh->melt_cb (mh->melt_cb_cls, &hr, + 0, + NULL, UINT32_MAX, NULL); TALER_EXCHANGE_melt_cancel (mh); } -struct TALER_EXCHANGE_MeltHandle * -TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange, - const json_t *refresh_data, - TALER_EXCHANGE_MeltCallback melt_cb, - void *melt_cb_cls) +static enum GNUNET_GenericReturnValue +start_melt (struct TALER_EXCHANGE_MeltHandle *mh) { const struct TALER_EXCHANGE_Keys *key_state; const struct TALER_EXCHANGE_DenomPublicKey *dki; json_t *melt_obj; - struct TALER_EXCHANGE_MeltHandle *mh; CURL *eh; struct GNUNET_CURL_Context *ctx; - struct MeltData *md; struct TALER_CoinSpendSignatureP confirm_sig; char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; struct TALER_DenominationHash h_denom_pub; struct TALER_CoinSpendPublicKeyP coin_pub; - GNUNET_assert (GNUNET_YES == - TEAH_handle_is_ready (exchange)); - md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data, - exchange->key_data.currency); - if (NULL == md) + if (GNUNET_OK != + TALER_EXCHANGE_get_melt_data_ (mh->ps, + mh->rd, + mh->alg_values, + &mh->md)) { GNUNET_break (0); - return NULL; + return GNUNET_SYSERR; } - TALER_denom_pub_hash (&md->melted_coin.pub_key, + TALER_denom_pub_hash (&mh->md.melted_coin.pub_key, &h_denom_pub); TALER_wallet_melt_sign (&md->melted_coin.melt_amount_with_fee, &md->melted_coin.fee_melt, @@ -489,29 +511,19 @@ TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange, pub_str); } - key_state = TALER_EXCHANGE_get_keys (exchange); + ctx = TEAH_handle_to_context (mh->exchange); + key_state = TALER_EXCHANGE_get_keys (mh->exchange); dki = TALER_EXCHANGE_get_denomination_key (key_state, - &md->melted_coin.pub_key); + &mh->md.melted_coin.pub_key); /* and now we can at last begin the actual request handling */ - mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle); - mh->exchange = exchange; - mh->coin_pub = coin_pub; - mh->dki = *dki; - memset (&mh->dki.key, - 0, - sizeof (mh->dki.key)); /* lifetime not warranted, so better - not copy the pointers */ - mh->melt_cb = melt_cb; - mh->melt_cb_cls = melt_cb_cls; - mh->md = md; - mh->url = TEAH_path_to_url (exchange, + + mh->url = TEAH_path_to_url (mh->exchange, arg_str); if (NULL == mh->url) { json_decref (melt_obj); - GNUNET_free (mh); - return NULL; + return GNUNET_SYSERR; } eh = TALER_EXCHANGE_curl_easy_get_ (mh->url); if ( (NULL == eh) || @@ -524,17 +536,154 @@ TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange, if (NULL != eh) curl_easy_cleanup (eh); json_decref (melt_obj); - GNUNET_free (mh->url); - GNUNET_free (mh); - return NULL; + return GNUNET_SYSERR; } json_decref (melt_obj); - ctx = TEAH_handle_to_context (exchange); mh->job = GNUNET_CURL_job_add2 (ctx, eh, mh->ctx.headers, &handle_melt_finished, mh); + return GNUNET_OK; +} + + +static void +fail_mh (struct TALER_EXCHANGE_MeltHandle *mh) +{ + // FIXME: do return more than NULLs if + // the /csr failed! + mh->melt_cb (mh->melt_cb_cls, + NULL, + 0, + NULL, + UINT32_MAX, + NULL); + TALER_EXCHANGE_melt_cancel (mh); +} + + +/** + * Callbacks of this type are used to serve the result of submitting a + * CS R request to a exchange. + * + * @param cls closure with our `struct TALER_EXCHANGE_MeltHandle *` + * @param csrr response details + */ +static void +csr_cb (void *cls, + const struct TALER_EXCHANGE_CsRResponse *csrr) +{ + struct TALER_EXCHANGE_MeltHandle *mh = cls; + unsigned int nks_off = 0; + + for (unsigned int i = 0; i<rd->fresh_pks_len; i++) + { + const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = &rd->fresh_pks[i]; + struct TALER_ExchangeWithdrawValues *wv = &mh->alg_values[i]; + + switch (fresh_pk->cipher) + { + case TALER_DENOMINATION_INVALID: + GNUNET_break (0); + fail_mh (mh). + return; + case TALER_DENOMINATION_RSA: + GNUNET_assert (TALER_DENOMINATION_RSA == wv->cipher); + break; + case TALER_DENOMINATION_CS: + GNUNET_assert (TALER_DENOMINATION_CS == wv->cipher); + *wv = csrr->details.success.alg_values[nks_off]; + nks_off++; + break; + } + } + if (GNUNET_OK != + start_melt (mh)) + { + GNUNET_break (0); + fail_mh (mh); + return; + } +} + + +struct TALER_EXCHANGE_MeltHandle * +TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange, + const struct TALER_PlanchetSecretsP *ps, + const struct TALER_EXCHANGE_RefreshData *rd, + TALER_EXCHANGE_MeltCallback melt_cb, + void *melt_cb_cls) +s +{ + const struct TALER_EXCHANGE_NonceKey *nks[GNUNET_NZL (rd->refresh_pks_len)]; + unsigned int nks_off = 0; + + if (0 == rd->refresh_pks_len) + { + GNUNET_break (0); + return NULL; + } + GNUNET_assert (GNUNET_YES == + TEAH_handle_is_ready (exchange)); + mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle); + mh->exchange = exchange; + mh->rd = rd; + mh->ps = ps; + mh->melt_cb = melt_cb; + mh->melt_cb_cls = melt_cb_cls; + mh->alg_values = GNUNET_new_array (struct TALER_ExchangeWithdrawValues, + rd->fresh_pks_len); + for (unsigned int i = 0; i<rd->fresh_pks_len; i++) + { + const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = &rd->fresh_pks[i]; + struct TALER_ExchangeWithdrawValues *wv = &mh->alg_values[i]; + + switch (fresh_pk->cipher) + { + case TALER_DENOMINATION_INVALID: + GNUNET_break (0); + GNUNET_free (mh->alg_values); + GNUNET_free (mh); + return NULL; + case TALER_DENOMINATION_RSA: + wv->cipher = TALER_DENOMINATION_RSA; + break; + case TALER_DENOMINATION_CS: + wv->cipher = TALER_DENOMINATION_CS; + nks[nks_off].pk = fresh_pk; + // derive nonce for refresh by index and ps; + // FIXME: include fresh_pk or not? + TALER_CRYPTO_XXX (ps, + fresh_pk, + i, + &nks[nks_off].nonce); + nks_off++; + break; + } + } + if (0 != nks_off) + { + mh->csr = TALER_EXCHANGE_csr (exchange, + nks_off, + nks, + &csr_cb, + mh); + if (NULL == mh->csr) + { + GNUNET_break (0); + TALER_EXCHANGE_melt_cancel (mh->csr); + return NULL; + } + return mh; + } + if (GNUNET_OK != + start_melt (mh)) + { + GNUNET_break (0); + TALER_EXCHANGE_melt_cancel (mh->csr); + return NULL; + } return mh; } @@ -547,8 +696,7 @@ TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh) GNUNET_CURL_job_cancel (mh->job); mh->job = NULL; } - TALER_EXCHANGE_free_melt_data_ (mh->md); /* does not free 'md' itself */ - GNUNET_free (mh->md); + TALER_EXCHANGE_free_melt_data_ (&mh->md); /* does not free 'md' itself */ GNUNET_free (mh->url); TALER_curl_easy_post_finished (&mh->ctx); GNUNET_free (mh); diff --git a/src/lib/exchange_api_refresh_common.c b/src/lib/exchange_api_refresh_common.c index 65c7d6ba4..cf04bca58 100644 --- a/src/lib/exchange_api_refresh_common.c +++ b/src/lib/exchange_api_refresh_common.c @@ -23,24 +23,11 @@ #include "exchange_api_refresh_common.h" -/** - * Free all information associated with a melted coin session. - * - * @param mc melted coin to release, the pointer itself is NOT - * freed (as it is typically not allocated by itself) - */ -static void -free_melted_coin (struct MeltedCoin *mc) -{ - TALER_denom_pub_free (&mc->pub_key); - TALER_denom_sig_free (&mc->sig); -} - - void TALER_EXCHANGE_free_melt_data_ (struct MeltData *md) { - free_melted_coin (&md->melted_coin); + TALER_denom_pub_free (&md->melted_coin.pub_key); + TALER_denom_sig_free (&md->melted_coin.sig); if (NULL != md->fresh_pks) { for (unsigned int i = 0; i<md->num_fresh_coins; i++) @@ -55,296 +42,12 @@ TALER_EXCHANGE_free_melt_data_ (struct MeltData *md) } -/** - * Serialize information about a coin we are melting. - * - * @param mc information to serialize - * @return NULL on error - */ -static json_t * -serialize_melted_coin (const struct MeltedCoin *mc) -{ - json_t *tprivs; - - tprivs = json_array (); - GNUNET_assert (NULL != tprivs); - for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++) - GNUNET_assert (0 == - json_array_append_new ( - tprivs, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ( - "transfer_priv", - &mc->transfer_priv[i])))); - return GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("coin_priv", - &mc->coin_priv), - TALER_JSON_pack_denom_sig ("denom_sig", - &mc->sig), - TALER_JSON_pack_denom_pub ("denom_pub", - &mc->pub_key), - TALER_JSON_pack_amount ("melt_amount_with_fee", - &mc->melt_amount_with_fee), - TALER_JSON_pack_amount ("original_value", - &mc->original_value), - TALER_JSON_pack_amount ("melt_fee", - &mc->fee_melt), - GNUNET_JSON_pack_timestamp ("expire_deposit", - mc->expire_deposit), - GNUNET_JSON_pack_array_steal ("transfer_privs", - tprivs)); -} - - -/** - * Deserialize information about a coin we are melting. - * - * @param[out] mc information to deserialize - * @param currency expected currency - * @param in JSON object to read data from - * @return #GNUNET_NO to report errors - */ -static enum GNUNET_GenericReturnValue -deserialize_melted_coin (struct MeltedCoin *mc, - const char *currency, - const json_t *in) -{ - json_t *trans_privs; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_priv", - &mc->coin_priv), - TALER_JSON_spec_denom_sig ("denom_sig", - &mc->sig), - TALER_JSON_spec_denom_pub ("denom_pub", - &mc->pub_key), - TALER_JSON_spec_amount ("melt_amount_with_fee", - currency, - &mc->melt_amount_with_fee), - TALER_JSON_spec_amount ("original_value", - currency, - &mc->original_value), - TALER_JSON_spec_amount ("melt_fee", - currency, - &mc->fee_melt), - GNUNET_JSON_spec_timestamp ("expire_deposit", - &mc->expire_deposit), - GNUNET_JSON_spec_json ("transfer_privs", - &trans_privs), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (in, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_NO; - } - if (TALER_CNC_KAPPA != json_array_size (trans_privs)) - { - GNUNET_JSON_parse_free (spec); - GNUNET_break_op (0); - return GNUNET_NO; - } - for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++) - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("transfer_priv", - &mc->transfer_priv[i]), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json_array_get (trans_privs, - i), - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_NO; - } - } - json_decref (trans_privs); - return GNUNET_OK; -} - - -/** - * Serialize melt data. - * - * @param md data to serialize - * @return serialized melt data - */ -static json_t * -serialize_melt_data (const struct MeltData *md) -{ - json_t *fresh_coins; - - fresh_coins = json_array (); - GNUNET_assert (NULL != fresh_coins); - for (int i = 0; i<md->num_fresh_coins; i++) - { - json_t *planchet_secrets; - - planchet_secrets = json_array (); - GNUNET_assert (NULL != planchet_secrets); - for (unsigned int j = 0; j<TALER_CNC_KAPPA; j++) - { - json_t *ps; - - ps = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("ps", - &md->fresh_coins[j][i])); - GNUNET_assert (0 == - json_array_append_new (planchet_secrets, - ps)); - } - GNUNET_assert (0 == - json_array_append_new ( - fresh_coins, - GNUNET_JSON_PACK ( - TALER_JSON_pack_denom_pub ("denom_pub", - &md->fresh_pks[i]), - GNUNET_JSON_pack_array_steal ("planchet_secrets", - planchet_secrets))) - ); - } - return GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("fresh_coins", - fresh_coins), - GNUNET_JSON_pack_object_steal ("melted_coin", - serialize_melted_coin (&md->melted_coin)), - GNUNET_JSON_pack_data_auto ("rc", - &md->rc)); -} - - -struct MeltData * -TALER_EXCHANGE_deserialize_melt_data_ (const json_t *melt_data, - const char *currency) -{ - struct MeltData *md = GNUNET_new (struct MeltData); - json_t *fresh_coins; - json_t *melted_coin; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("rc", - &md->rc), - GNUNET_JSON_spec_json ("melted_coin", - &melted_coin), - GNUNET_JSON_spec_json ("fresh_coins", - &fresh_coins), - GNUNET_JSON_spec_end () - }; - bool ok; - - if (GNUNET_OK != - GNUNET_JSON_parse (melt_data, - spec, - NULL, NULL)) - { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - GNUNET_free (md); - return NULL; - } - if (! (json_is_array (fresh_coins) && - json_is_object (melted_coin)) ) - { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - return NULL; - } - if (GNUNET_OK != - deserialize_melted_coin (&md->melted_coin, - currency, - melted_coin)) - { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - return NULL; - } - md->num_fresh_coins = json_array_size (fresh_coins); - md->fresh_pks = GNUNET_new_array (md->num_fresh_coins, - struct TALER_DenominationPublicKey); - for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++) - md->fresh_coins[i] = GNUNET_new_array (md->num_fresh_coins, - struct TALER_PlanchetSecretsP); - ok = true; - for (unsigned int i = 0; i<md->num_fresh_coins; i++) - { - const json_t *ji = json_array_get (fresh_coins, - i); - json_t *planchet_secrets; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_json ("planchet_secrets", - &planchet_secrets), - TALER_JSON_spec_denom_pub ("denom_pub", - &md->fresh_pks[i]), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (ji, - ispec, - NULL, NULL)) - { - GNUNET_break (0); - ok = false; - break; - } - if ( (! json_is_array (planchet_secrets)) || - (TALER_CNC_KAPPA != json_array_size (planchet_secrets)) ) - { - GNUNET_break (0); - ok = false; - GNUNET_JSON_parse_free (ispec); - break; - } - for (unsigned int j = 0; j<TALER_CNC_KAPPA; j++) - { - struct GNUNET_JSON_Specification jspec[] = { - GNUNET_JSON_spec_fixed_auto ("ps", - &md->fresh_coins[j][i]), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json_array_get (planchet_secrets, - j), - jspec, - NULL, NULL)) - { - GNUNET_break (0); - ok = false; - break; - } - } - json_decref (planchet_secrets); - if (! ok) - break; - } - - GNUNET_JSON_parse_free (spec); - if (! ok) - { - TALER_EXCHANGE_free_melt_data_ (md); - GNUNET_free (md); - return NULL; - } - return md; -} - - -json_t * -TALER_EXCHANGE_refresh_prepare ( - const struct TALER_CoinSpendPrivateKeyP *melt_priv, - const struct TALER_Amount *melt_amount, - const struct TALER_DenominationSignature *melt_sig, - const struct TALER_EXCHANGE_DenomPublicKey *melt_pk, - unsigned int fresh_pks_len, - const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks) +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_get_melt_data_ ( + const struct TALER_PlanchetSecretsP *ps, + const struct struct TALER_EXCHANGE_RefreshData *rd, + const struct TALER_ExchangeWithdrawValues *alg_values, + struct MeltData *md) { struct MeltData md; json_t *ret; @@ -359,68 +62,68 @@ TALER_EXCHANGE_refresh_prepare ( memset (&md, 0, sizeof (md)); - md.num_fresh_coins = fresh_pks_len; - md.melted_coin.coin_priv = *melt_priv; - md.melted_coin.melt_amount_with_fee = *melt_amount; - md.melted_coin.fee_melt = melt_pk->fee_refresh; - md.melted_coin.original_value = melt_pk->value; - md.melted_coin.expire_deposit - = melt_pk->expire_deposit; + md.num_fresh_coins = rd->fresh_pks_len; + md.melted_coin.coin_priv = rd->melt_priv; + md.melted_coin.melt_amount_with_fee = rd->melt_amount; + md.melted_coin.fee_melt = rd->melt_pk->fee_refresh; + md.melted_coin.original_value = rd->melt_pk->value; + md.melted_coin.expire_deposit = rd->melt_pk->expire_deposit; GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (melt_amount->currency, &total)); TALER_denom_pub_deep_copy (&md.melted_coin.pub_key, - &melt_pk->key); + &rd->melt_pk->key); TALER_denom_sig_deep_copy (&md.melted_coin.sig, - melt_sig); - md.fresh_pks = GNUNET_new_array (fresh_pks_len, + rd->melt_sig); + md.fresh_pks = GNUNET_new_array (rd->fresh_pks_len, struct TALER_DenominationPublicKey); - for (unsigned int i = 0; i<fresh_pks_len; i++) + for (unsigned int i = 0; i<rd->fresh_pks_len; i++) { TALER_denom_pub_deep_copy (&md.fresh_pks[i], &fresh_pks[i].key); if ( (0 > TALER_amount_add (&total, &total, - &fresh_pks[i].value)) || + &rd->fresh_pks[i].value)) || (0 > TALER_amount_add (&total, &total, - &fresh_pks[i].fee_withdraw)) ) + &rd->fresh_pks[i].fee_withdraw)) ) { GNUNET_break (0); TALER_EXCHANGE_free_melt_data_ (&md); - return NULL; + return GNUNET_SYSERR; } } /* verify that melt_amount is above total cost */ if (1 == TALER_amount_cmp (&total, - melt_amount) ) + rd->melt_amount) ) { /* Eh, this operation is more expensive than the @a melt_amount. This is not OK. */ GNUNET_break (0); TALER_EXCHANGE_free_melt_data_ (&md); - return NULL; + return GNUNET_SYSERR; } /* build up coins */ for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++) { + // FIXME: derive! GNUNET_CRYPTO_ecdhe_key_create ( &md.melted_coin.transfer_priv[i].ecdhe_priv); GNUNET_CRYPTO_ecdhe_key_get_public ( &md.melted_coin.transfer_priv[i].ecdhe_priv, &rce[i].transfer_pub.ecdhe_pub); - TALER_link_derive_transfer_secret (melt_priv, - &md.melted_coin.transfer_priv[i], - &trans_sec[i]); - md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len, + TALER_link_derive_transfer_secret (&rd->melt_priv, + &md.melted_coin.transfer_priv[i], + &trans_sec[i]); + md.fresh_coins[i] = GNUNET_new_array (rd->fresh_pks_len, struct TALER_PlanchetSecretsP); - rce[i].new_coins = GNUNET_new_array (fresh_pks_len, + rce[i].new_coins = GNUNET_new_array (rd->fresh_pks_len, struct TALER_RefreshCoinData); - for (unsigned int j = 0; j<fresh_pks_len; j++) + for (unsigned int j = 0; j<rd->fresh_pks_len; j++) { struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j]; struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j]; @@ -458,15 +161,12 @@ TALER_EXCHANGE_refresh_prepare ( fresh_pks_len, rce, &coin_pub, - melt_amount); - /* finally, serialize everything */ - ret = serialize_melt_data (&md); + &rd->melt_amount); for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) { for (unsigned int j = 0; j < fresh_pks_len; j++) GNUNET_free (rce[i].new_coins[j].coin_ev); GNUNET_free (rce[i].new_coins); } - TALER_EXCHANGE_free_melt_data_ (&md); - return ret; + return GNUNET_OK; } diff --git a/src/lib/exchange_api_refresh_common.h b/src/lib/exchange_api_refresh_common.h index 1c037d966..653d48866 100644 --- a/src/lib/exchange_api_refresh_common.h +++ b/src/lib/exchange_api_refresh_common.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015-2020 Taler Systems SA + Copyright (C) 2015-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -103,6 +103,12 @@ struct MeltData struct TALER_DenominationPublicKey *fresh_pks; /** + * Array of @e num_fresh_coins with exchange contributions + * made during the refresh. + */ + struct TALER_ExchangeWithdrawValues *exchange_vals; + + /** * Arrays of @e num_fresh_coins with information about the fresh * coins to be created, for each cut-and-choose dimension. */ @@ -111,15 +117,19 @@ struct MeltData /** - * Deserialize melt data. + * Compute the melt data from the refresh data and secret. * - * @param data json data to deserialize - * @param currency expected currency for the coins - * @return deserialized melt data, NULL on error + * @param ps secret internals of the refresh-reveal operation + * @param rd refresh data with the characteristics of the operation + * @param alg_values contributions from the exchange into the melt + * @param[out] rd where to write the derived melt data */ -struct MeltData * -TALER_EXCHANGE_deserialize_melt_data_ (const json_t *data, - const char *currency); +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_get_melt_data_ ( + const struct TALER_PlanchetSecretsP *ps, + const struct struct TALER_EXCHANGE_RefreshData *rd, + const struct TALER_ExchangeWithdrawValues *alg_values, + struct MeltData *md); /** diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c index 82f92322a..cdfb5140f 100644 --- a/src/lib/exchange_api_refreshes_reveal.c +++ b/src/lib/exchange_api_refreshes_reveal.c @@ -73,7 +73,7 @@ struct TALER_EXCHANGE_RefreshesRevealHandle /** * Actual information about the melt operation. */ - struct MeltData *md; + struct MeltData md; /** * The index selected by the exchange in cut-and-choose to not be revealed. @@ -95,12 +95,14 @@ struct TALER_EXCHANGE_RefreshesRevealHandle * * @param rrh operation handle * @param json reply from the exchange - * @param[out] sigs array of length `num_fresh_coins`, initialized to contain RSA signatures + * @param[out] sigs array of length `num_fresh_coins`, initialized to contain the coin private keys + * @param[out] sigs array of length `num_fresh_coins`, initialized to contain signatures * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors */ static enum GNUNET_GenericReturnValue refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, const json_t *json, + struct TALER_CoinSpendPrivateKeyP *coin_privs, struct TALER_DenominationSignature *sigs) { json_t *jsona; @@ -165,6 +167,7 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, /* needed to verify the signature, and we didn't store it earlier, hence recomputing it here... */ + coin_privs[i] = fc->coin_priv; GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, &coin_pub.eddsa_pub); /* FIXME-Oec: Age commitment hash. */ @@ -223,13 +226,15 @@ handle_refresh_reveal_finished (void *cls, case MHD_HTTP_OK: { struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins]; - int ret; + struct TALER_CoinSpendPrivateKeyP coin_privs[rrh->md->num_fresh_coins]; + enum GNUNET_GenericReturnValue ret; memset (sigs, 0, sizeof (sigs)); ret = refresh_reveal_ok (rrh, j, + coin_privs, sigs); if (GNUNET_OK != ret) { @@ -241,7 +246,7 @@ handle_refresh_reveal_finished (void *cls, rrh->reveal_cb (rrh->reveal_cb_cls, &hr, rrh->md->num_fresh_coins, - rrh->md->fresh_coins[rrh->noreveal_index], + coin_privs, sigs); rrh->reveal_cb = NULL; } @@ -298,7 +303,10 @@ handle_refresh_reveal_finished (void *cls, struct TALER_EXCHANGE_RefreshesRevealHandle * TALER_EXCHANGE_refreshes_reveal ( struct TALER_EXCHANGE_Handle *exchange, - const json_t *refresh_data, + const struct TALER_PlanchetSecretsP *ps, + const struct TALER_EXCHANGE_RefreshData *rd, + unsigned int num_coins, + const struct TALER_ExchangeWithdrawValues *alg_values, uint32_t noreveal_index, TALER_EXCHANGE_RefreshesRevealCallback reveal_cb, void *reveal_cb_cls) @@ -311,10 +319,11 @@ TALER_EXCHANGE_refreshes_reveal ( json_t *link_sigs; CURL *eh; struct GNUNET_CURL_Context *ctx; - struct MeltData *md; + struct MeltData md; struct TALER_TransferPublicKeyP transfer_pub; char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32]; + GNUNET_assert (num_coins == rd->fresh_pks_len); if (noreveal_index >= TALER_CNC_KAPPA) { /* We check this here, as it would be really bad to below just @@ -330,9 +339,11 @@ TALER_EXCHANGE_refreshes_reveal ( GNUNET_break (0); return NULL; } - md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data, - exchange->key_data.currency); - if (NULL == md) + if (GNUNET_OK != + TALER_EXCHANGE_get_melt_data_ (ps, + rd, + alg_values, + &md)) { GNUNET_break (0); return NULL; @@ -340,33 +351,31 @@ TALER_EXCHANGE_refreshes_reveal ( /* now transfer_pub */ GNUNET_CRYPTO_ecdhe_key_get_public ( - &md->melted_coin.transfer_priv[noreveal_index].ecdhe_priv, + &md.melted_coin.transfer_priv[noreveal_index].ecdhe_priv, &transfer_pub.ecdhe_pub); /* now new_denoms */ GNUNET_assert (NULL != (new_denoms_h = json_array ())); GNUNET_assert (NULL != (coin_evs = json_array ())); GNUNET_assert (NULL != (link_sigs = json_array ())); - for (unsigned int i = 0; i<md->num_fresh_coins; i++) + for (unsigned int i = 0; i<md.num_fresh_coins; i++) { struct TALER_DenominationHash denom_hash; struct TALER_ExchangeWithdrawValues alg_values; struct TALER_PlanchetDetail pd; struct TALER_CoinPubHash c_hash; - TALER_denom_pub_hash (&md->fresh_pks[i], + TALER_denom_pub_hash (&md.fresh_pks[i], &denom_hash); GNUNET_assert (0 == json_array_append_new (new_denoms_h, GNUNET_JSON_from_data_auto ( &denom_hash))); - // TODO: implement cipher handling - alg_values.cipher = TALER_DENOMINATION_RSA; if (GNUNET_OK != - TALER_planchet_prepare (&md->fresh_pks[i], - &alg_values, - &md->fresh_coins[noreveal_index][i], + TALER_planchet_prepare (&md.fresh_pks[i], + &rrh->exchange_vals[i], + &md.fresh_coins[noreveal_index][i], &c_hash, &pd)) { @@ -374,6 +383,7 @@ TALER_EXCHANGE_refreshes_reveal ( GNUNET_break (0); json_decref (new_denoms_h); json_decref (coin_evs); + TALER_EXCHANGE_free_melt_data_ (&md); return NULL; } GNUNET_assert (0 == @@ -394,7 +404,7 @@ TALER_EXCHANGE_refreshes_reveal ( blinded_msg, pd.blinded_planchet.details.rsa_blinded_planchet. blinded_msg_size, - &md->melted_coin.coin_priv, + &md.melted_coin.coin_priv, &link_sig); GNUNET_assert (0 == json_array_append_new ( @@ -417,7 +427,7 @@ TALER_EXCHANGE_refreshes_reveal ( GNUNET_assert (0 == json_array_append_new (transfer_privs, GNUNET_JSON_from_data_auto ( - &md->melted_coin.transfer_priv[j]))); + &md.melted_coin.transfer_priv[j]))); } /* build main JSON request */ @@ -436,9 +446,8 @@ TALER_EXCHANGE_refreshes_reveal ( char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2]; char *end; - end = GNUNET_STRINGS_data_to_string (&md->rc, - sizeof (struct - TALER_RefreshCommitmentP), + end = GNUNET_STRINGS_data_to_string (&md.rc, + sizeof (md.rc), pub_str, sizeof (pub_str)); *end = '\0'; @@ -459,6 +468,7 @@ TALER_EXCHANGE_refreshes_reveal ( if (NULL == rrh->url) { json_decref (reveal_obj); + TALER_EXCHANGE_free_melt_data_ (&md); GNUNET_free (rrh); return NULL; } @@ -473,6 +483,7 @@ TALER_EXCHANGE_refreshes_reveal ( if (NULL != eh) curl_easy_cleanup (eh); json_decref (reveal_obj); + TALER_EXCHANGE_free_melt_data_ (&md); GNUNET_free (rrh->url); GNUNET_free (rrh); return NULL; @@ -499,8 +510,8 @@ TALER_EXCHANGE_refreshes_reveal_cancel ( } GNUNET_free (rrh->url); TALER_curl_easy_post_finished (&rrh->ctx); - TALER_EXCHANGE_free_melt_data_ (rrh->md); /* does not free 'md' itself */ - GNUNET_free (rrh->md); + TALER_EXCHANGE_free_melt_data_ (&rrh->md); + GNUNET_free (rrh->exchange_vals); GNUNET_free (rrh); } |