summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_refresh_reveal.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-02-29 16:42:10 +0100
committerChristian Grothoff <christian@grothoff.org>2020-02-29 16:42:10 +0100
commit0a2b049864c8dae0c53c203d46fca89e0e66849d (patch)
tree3e836be37902320a4a3a099ee62d960198057952 /src/exchange/taler-exchange-httpd_refresh_reveal.c
parentde9ab28ab9e55597baf2ca32194ec65b441f0f36 (diff)
downloadexchange-0a2b049864c8dae0c53c203d46fca89e0e66849d.tar.gz
exchange-0a2b049864c8dae0c53c203d46fca89e0e66849d.tar.bz2
exchange-0a2b049864c8dae0c53c203d46fca89e0e66849d.zip
big rename fest related to #6067 API renaming
Diffstat (limited to 'src/exchange/taler-exchange-httpd_refresh_reveal.c')
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_reveal.c979
1 files changed, 0 insertions, 979 deletions
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c
deleted file mode 100644
index 802f0a8a3..000000000
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.c
+++ /dev/null
@@ -1,979 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2019 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
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_refresh_reveal.c
- * @brief Handle /refresh/reveal requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include "taler_mhd_lib.h"
-#include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh_reveal.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
-
-
-/**
- * Maximum number of fresh coins we allow per refresh operation.
- */
-#define MAX_FRESH_COINS 256
-
-/**
- * How often do we at most retry the reveal transaction sequence?
- * Twice should really suffice in all cases (as the possible conflict
- * cannot happen more than once).
- */
-#define MAX_REVEAL_RETRIES 2
-
-
-/**
- * Send a response for "/refresh/reveal".
- *
- * @param connection the connection to send the response to
- * @param num_newcoins number of new coins for which we reveal data
- * @param sigs array of @a num_newcoins signatures revealed
- * @return a MHD result code
- */
-static int
-reply_refresh_reveal_success (struct MHD_Connection *connection,
- unsigned int num_newcoins,
- const struct TALER_DenominationSignature *sigs)
-{
- json_t *list;
- int ret;
-
- list = json_array ();
- for (unsigned int newcoin_index = 0;
- newcoin_index < num_newcoins;
- newcoin_index++)
- {
- json_t *obj;
-
- obj = json_object ();
- json_object_set_new (obj,
- "ev_sig",
- GNUNET_JSON_from_rsa_signature (
- sigs[newcoin_index].rsa_signature));
- GNUNET_assert (0 ==
- json_array_append_new (list,
- obj));
- }
-
- {
- json_t *root;
-
- root = json_object ();
- json_object_set_new (root,
- "ev_sigs",
- list);
- ret = TALER_MHD_reply_json (connection,
- root,
- MHD_HTTP_OK);
- json_decref (root);
- }
- return ret;
-}
-
-
-/**
- * Send a response for a failed "/refresh/reveal", where the
- * revealed value(s) do not match the original commitment.
- *
- * @param connection the connection to send the response to
- * @param rc commitment computed by the exchange
- * @return a MHD result code
- */
-static int
-reply_refresh_reveal_mismatch (struct MHD_Connection *connection,
- const struct TALER_RefreshCommitmentP *rc)
-{
- return TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_CONFLICT,
- "{s:s, s:I, s:o}",
- "hint", "commitment violation",
- "code",
- (json_int_t)
- TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
- "rc_expected",
- GNUNET_JSON_from_data_auto (rc));
-}
-
-
-/**
- * State for a /refresh/reveal operation.
- */
-struct RevealContext
-{
-
- /**
- * Commitment of the refresh operaton.
- */
- struct TALER_RefreshCommitmentP rc;
-
- /**
- * Transfer public key at gamma.
- */
- struct TALER_TransferPublicKeyP gamma_tp;
-
- /**
- * Transfer private keys revealed to us.
- */
- struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
-
- /**
- * Denominations being requested.
- */
- const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation **dkis;
-
- /**
- * Envelopes to be signed.
- */
- const struct TALER_RefreshCoinData *rcds;
-
- /**
- * Signatures over the link data (of type
- * #TALER_SIGNATURE_WALLET_COIN_LINK)
- */
- const struct TALER_CoinSpendSignatureP *link_sigs;
-
- /**
- * Envelopes with the signatures to be returned. Initially NULL.
- */
- struct TALER_DenominationSignature *ev_sigs;
-
- /**
- * Size of the @e dkis, @e rcds and @e ev_sigs arrays (if non-NULL).
- */
- unsigned int num_fresh_coins;
-
- /**
- * Result from preflight checks. #GNUNET_NO for no result,
- * #GNUNET_YES if preflight found previous successful operation,
- * #GNUNET_SYSERR if prefight check failed hard (and generated
- * an MHD response already).
- */
- int preflight_ok;
-
-};
-
-
-/**
- * Function called with information about a refresh order we already
- * persisted. Stores the result in @a cls so we don't do the calculation
- * again.
- *
- * @param cls closure with a `struct RevealContext`
- * @param num_newcoins size of the @a rrcs array
- * @param rrcs array of @a num_newcoins information about coins to be created
- * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1
- * @param tprivs array of @e num_tprivs transfer private keys
- * @param tp transfer public key information
- */
-static void
-check_exists_cb (void *cls,
- uint32_t num_newcoins,
- const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
- unsigned int num_tprivs,
- const struct TALER_TransferPrivateKeyP *tprivs,
- const struct TALER_TransferPublicKeyP *tp)
-{
- struct RevealContext *rctx = cls;
-
- if (0 == num_newcoins)
- {
- GNUNET_break (0);
- return;
- }
- GNUNET_break (TALER_CNC_KAPPA - 1 == num_tprivs);
- GNUNET_break_op (0 ==
- GNUNET_memcmp (tp,
- &rctx->gamma_tp));
- GNUNET_break_op (0 ==
- memcmp (tprivs,
- &rctx->transfer_privs,
- sizeof (struct TALER_TransferPrivateKeyP)
- * num_tprivs));
- /* We usually sign early (optimistic!), but in case we change that *and*
- we do find the operation in the database, we could use this: */
- if (NULL == rctx->ev_sigs)
- {
- rctx->ev_sigs = GNUNET_new_array (num_newcoins,
- struct TALER_DenominationSignature);
- for (unsigned int i = 0; i<num_newcoins; i++)
- rctx->ev_sigs[i].rsa_signature
- = GNUNET_CRYPTO_rsa_signature_dup (rrcs[i].coin_sig.rsa_signature);
- }
-}
-
-
-/**
- * Check if the "/refresh/reveal" was already successful before.
- * If so, just return the old result.
- *
- * @param cls closure of type `struct RevealContext`
- * @param connection MHD request which triggered the transaction
- * @param session database session to use
- * @param[out] mhd_ret set to MHD response status for @a connection,
- * if transaction failed (!)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-refresh_reveal_preflight (void *cls,
- struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- int *mhd_ret)
-{
- struct RevealContext *rctx = cls;
- enum GNUNET_DB_QueryStatus qs;
-
- /* Try to see if we already have given an answer before. */
- qs = TEH_plugin->get_refresh_reveal (TEH_plugin->cls,
- session,
- &rctx->rc,
- &check_exists_cb,
- rctx);
- switch (qs)
- {
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- return qs; /* continue normal execution */
- case GNUNET_DB_STATUS_SOFT_ERROR:
- return qs;
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (qs);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFRESH_REVEAL_DB_FETCH_REVEAL_ERROR,
- "failed to fetch reveal data");
- rctx->preflight_ok = GNUNET_SYSERR;
- return GNUNET_DB_STATUS_HARD_ERROR;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- default:
- /* Hossa, already found our reply! */
- GNUNET_assert (NULL != rctx->ev_sigs);
- rctx->preflight_ok = GNUNET_YES;
- return qs;
- }
-}
-
-
-/**
- * Execute a "/refresh/reveal". The client is revealing to us the
- * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins. Verify that the
- * revealed transfer keys would allow linkage to the blinded coins.
- *
- * IF it returns a non-error code, the transaction logic MUST
- * NOT queue a MHD response. IF it returns an hard error, the
- * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
- * it returns the soft error code, the function MAY be called again to
- * retry and MUST not queue a MHD response.
- *
- * @param cls closure of type `struct RevealContext`
- * @param connection MHD request which triggered the transaction
- * @param session database session to use
- * @param[out] mhd_ret set to MHD response status for @a connection,
- * if transaction failed (!)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-refresh_reveal_transaction (void *cls,
- struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- int *mhd_ret)
-{
- struct RevealContext *rctx = cls;
- struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
- enum GNUNET_DB_QueryStatus qs;
-
- /* Obtain basic information about the refresh operation and what
- gamma we committed to. */
- qs = TEH_plugin->get_melt (TEH_plugin->cls,
- session,
- &rctx->rc,
- &refresh_melt);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
- "rc");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- return qs;
- if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
- (refresh_melt.session.noreveal_index >= TALER_CNC_KAPPA) )
- {
- GNUNET_break (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR,
- "failed to fetch valid challenge from database");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
-
- /* Verify commitment */
- {
- /* Note that the contents of rcs[refresh_melt.session.noreveal_index]
- will be aliased and are *not* allocated (or deallocated) in
- this function -- in contrast to the other offsets! */
- struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
- struct TALER_RefreshCommitmentP rc_expected;
- unsigned int off;
-
- off = 0; /* did we pass session.noreveal_index yet? */
- for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
- {
- struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
-
- if (i == refresh_melt.session.noreveal_index)
- {
- /* Take these coin envelopes from the client */
- rce->transfer_pub = rctx->gamma_tp;
- rce->new_coins = (struct TALER_RefreshCoinData *) rctx->rcds;
- off = 1;
- }
- else
- {
- /* Reconstruct coin envelopes from transfer private key */
- struct TALER_TransferPrivateKeyP *tpriv = &rctx->transfer_privs[i
- - off];
- struct TALER_TransferSecretP ts;
-
- GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
- &rce->transfer_pub.ecdhe_pub);
- TALER_link_reveal_transfer_secret (tpriv,
- &refresh_melt.session.coin.coin_pub,
- &ts);
- rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
- struct TALER_RefreshCoinData);
- for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
- {
- struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
- struct TALER_PlanchetSecretsP ps;
- struct TALER_PlanchetDetail pd;
-
- rcd->dk = &rctx->dkis[j]->denom_pub;
- TALER_planchet_setup_refresh (&ts,
- j,
- &ps);
- GNUNET_assert (GNUNET_OK ==
- TALER_planchet_prepare (rcd->dk,
- &ps,
- &pd));
- rcd->coin_ev = pd.coin_ev;
- rcd->coin_ev_size = pd.coin_ev_size;
- }
- }
- }
- TALER_refresh_get_commitment (&rc_expected,
- TALER_CNC_KAPPA,
- rctx->num_fresh_coins,
- rcs,
- &refresh_melt.session.coin.coin_pub,
- &refresh_melt.session.amount_with_fee);
-
- /* Free resources allocated above */
- for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
- {
- struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
-
- if (i == refresh_melt.session.noreveal_index)
- continue; /* This offset is special... */
- for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
- {
- struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
-
- GNUNET_free (rcd->coin_ev);
- }
- GNUNET_free (rce->new_coins);
- }
-
- /* Verify rc_expected matches rc */
- if (0 != GNUNET_memcmp (&rctx->rc,
- &rc_expected))
- {
- GNUNET_break_op (0);
- *mhd_ret = reply_refresh_reveal_mismatch (connection,
- &rc_expected);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- } /* end of checking "rc_expected" */
-
- /* check amounts add up! */
- {
- struct TALER_Amount refresh_cost;
-
- refresh_cost = refresh_melt.melt_fee;
- for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
- {
- struct TALER_Amount fee_withdraw;
- struct TALER_Amount value;
- struct TALER_Amount total;
-
- TALER_amount_ntoh (&fee_withdraw,
- &rctx->dkis[i]->issue.properties.fee_withdraw);
- TALER_amount_ntoh (&value,
- &rctx->dkis[i]->issue.properties.value);
- if ( (GNUNET_OK !=
- TALER_amount_add (&total,
- &fee_withdraw,
- &value)) ||
- (GNUNET_OK !=
- TALER_amount_add (&refresh_cost,
- &refresh_cost,
- &total)) )
- {
- GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFRESH_REVEAL_COST_CALCULATION_OVERFLOW,
- "failed to add up refresh costs");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
- if (0 < TALER_amount_cmp (&refresh_cost,
- &refresh_melt.session.amount_with_fee))
- {
- GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFRESH_REVEAL_AMOUNT_INSUFFICIENT,
- "melted coin value is insufficient to cover cost of operation");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
-}
-
-
-/**
- * Persist result of a "/refresh/reveal".
- *
- * @param cls closure of type `struct RevealContext`
- * @param connection MHD request which triggered the transaction
- * @param session database session to use
- * @param[out] mhd_ret set to MHD response status for @a connection,
- * if transaction failed (!)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-refresh_reveal_persist (void *cls,
- struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- int *mhd_ret)
-{
- struct RevealContext *rctx = cls;
- enum GNUNET_DB_QueryStatus qs;
-
- /* Persist operation result in DB */
- {
- struct TALER_EXCHANGEDB_RefreshRevealedCoin rrcs[rctx->num_fresh_coins];
-
- for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
- {
- struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
-
- rrc->denom_pub = rctx->dkis[i]->denom_pub;
- rrc->orig_coin_link_sig = rctx->link_sigs[i];
- rrc->coin_ev = rctx->rcds[i].coin_ev;
- rrc->coin_ev_size = rctx->rcds[i].coin_ev_size;
- rrc->coin_sig = rctx->ev_sigs[i];
- }
- qs = TEH_plugin->insert_refresh_reveal (TEH_plugin->cls,
- session,
- &rctx->rc,
- rctx->num_fresh_coins,
- rrcs,
- TALER_CNC_KAPPA - 1,
- rctx->transfer_privs,
- &rctx->gamma_tp);
- }
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- {
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFRESH_REVEAL_DB_COMMIT_ERROR,
- "failed to persist reveal data");
- }
- return qs;
-}
-
-
-/**
- * Resolve denomination hashes using the @a key_state
- *
- * @param key_state the key state
- * @param connection the MHD connection to handle
- * @param rctx context for the operation, partially built at this time
- * @param link_sigs_json link signatures in JSON format
- * @param new_denoms_h_json requests for fresh coins to be created
- * @param coin_evs envelopes of gamma-selected coins to be signed
- * @return MHD result code
- */
-static int
-resolve_refresh_reveal_denominations (struct TEH_KS_StateHandle *key_state,
- struct MHD_Connection *connection,
- struct RevealContext *rctx,
- const json_t *link_sigs_json,
- const json_t *new_denoms_h_json,
- const json_t *coin_evs)
-{
- unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
- const struct
- TALER_EXCHANGEDB_DenominationKeyIssueInformation *dkis[num_fresh_coins];
- struct GNUNET_HashCode dki_h[num_fresh_coins];
- struct TALER_RefreshCoinData rcds[num_fresh_coins];
- struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
- struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
- int res;
-
- /* Parse denomination key hashes */
- for (unsigned int i = 0; i<num_fresh_coins; i++)
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL,
- &dki_h[i]),
- GNUNET_JSON_spec_end ()
- };
- unsigned int hc;
- enum TALER_ErrorCode ec;
-
- res = TALER_MHD_parse_json_array (connection,
- new_denoms_h_json,
- spec,
- i,
- -1);
- if (GNUNET_OK != res)
- {
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
- dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state,
- &dki_h[i],
- TEH_KS_DKU_WITHDRAW,
- &ec,
- &hc);
- if (NULL == dkis[i])
- {
- return TALER_MHD_reply_with_error (connection,
- hc,
- ec,
- "failed to find denomination key");
- }
- GNUNET_assert (NULL != dkis[i]->denom_priv.rsa_private_key);
- }
-
- /* Parse coin envelopes */
- for (unsigned int i = 0; i<num_fresh_coins; i++)
- {
- struct TALER_RefreshCoinData *rcd = &rcds[i];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_varsize (NULL,
- (void **) &rcd->coin_ev,
- &rcd->coin_ev_size),
- GNUNET_JSON_spec_end ()
- };
-
- res = TALER_MHD_parse_json_array (connection,
- coin_evs,
- spec,
- i,
- -1);
- if (GNUNET_OK != res)
- {
- for (unsigned int j = 0; j<i; j++)
- GNUNET_free_non_null (rcds[j].coin_ev);
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
- rcd->dk = &dkis[i]->denom_pub;
- }
-
- /* lookup old_coin_pub in database */
- {
- enum GNUNET_DB_QueryStatus qs;
-
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- (qs = TEH_plugin->get_melt (TEH_plugin->cls,
- NULL,
- &rctx->rc,
- &refresh_melt)))
- {
- switch (qs)
- {
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
- "rc");
- break;
- case GNUNET_DB_STATUS_HARD_ERROR:
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR,
- "failed to fetch session data");
- break;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- default:
- GNUNET_break (0); /* should be impossible */
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_INTERNAL_INVARIANT_FAILURE,
- "assertion failed");
- break;
- }
- goto cleanup;
- }
- }
- /* Parse link signatures array */
- for (unsigned int i = 0; i<num_fresh_coins; i++)
- {
- struct GNUNET_JSON_Specification link_spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &link_sigs[i]),
- GNUNET_JSON_spec_end ()
- };
-
- res = TALER_MHD_parse_json_array (connection,
- link_sigs_json,
- link_spec,
- i,
- -1);
- if (GNUNET_OK != res)
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- /* Check link_sigs[i] signature */
- {
- struct TALER_LinkDataPS ldp;
-
- ldp.purpose.size = htonl (sizeof (ldp));
- ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
- ldp.h_denom_pub = dki_h[i];
- ldp.old_coin_pub = refresh_melt.session.coin.coin_pub;
- ldp.transfer_pub = rctx->gamma_tp;
- GNUNET_CRYPTO_hash (rcds[i].coin_ev,
- rcds[i].coin_ev_size,
- &ldp.coin_envelope_hash);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
- &ldp.purpose,
- &link_sigs[i].eddsa_signature,
- &refresh_melt.session.coin.coin_pub.
- eddsa_pub))
- {
- GNUNET_break_op (0);
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_REFRESH_REVEAL_LINK_SIGNATURE_INVALID,
- "link_sig");
- goto cleanup;
- }
- }
- }
-
- rctx->num_fresh_coins = num_fresh_coins;
- rctx->rcds = rcds;
- rctx->dkis = dkis;
- rctx->link_sigs = link_sigs;
-
- /* sign _early_ (optimistic!) to keep out of transaction scope! */
- rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins,
- struct TALER_DenominationSignature);
- for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
- {
- rctx->ev_sigs[i].rsa_signature
- = GNUNET_CRYPTO_rsa_sign_blinded (
- rctx->dkis[i]->denom_priv.rsa_private_key,
- rctx->rcds[i].coin_ev,
- rctx->rcds[i].coin_ev_size);
- if (NULL == rctx->ev_sigs[i].rsa_signature)
- {
- GNUNET_break (0);
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFRESH_REVEAL_SIGNING_ERROR,
- "internal signing error");
- goto cleanup;
- }
- }
-
- /* We try the three transactions a few times, as theoretically
- the pre-check might be satisfied by a concurrent transaction
- voiding our final commit due to uniqueness violation; naturally,
- on hard errors we exit immediately */
- for (unsigned int retries = 0; retries < MAX_REVEAL_RETRIES; retries++)
- {
- /* do transactional work */
- rctx->preflight_ok = GNUNET_NO;
- if ( (GNUNET_OK ==
- TEH_DB_run_transaction (connection,
- "reveal pre-check",
- &res,
- &refresh_reveal_preflight,
- rctx)) &&
- (GNUNET_YES == rctx->preflight_ok) )
- {
- /* Generate final (positive) response */
- GNUNET_assert (NULL != rctx->ev_sigs);
- res = reply_refresh_reveal_success (connection,
- num_fresh_coins,
- rctx->ev_sigs);
- GNUNET_break (MHD_NO != res);
- goto cleanup; /* aka 'break' */
- }
- if (GNUNET_SYSERR == rctx->preflight_ok)
- {
- GNUNET_break (0);
- goto cleanup; /* aka 'break' */
- }
- if (GNUNET_OK !=
- TEH_DB_run_transaction (connection,
- "run reveal",
- &res,
- &refresh_reveal_transaction,
- rctx))
- {
- /* reveal failed, too bad */
- GNUNET_break_op (0);
- goto cleanup; /* aka 'break' */
- }
- if (GNUNET_OK ==
- TEH_DB_run_transaction (connection,
- "persist reveal",
- &res,
- &refresh_reveal_persist,
- rctx))
- {
- /* Generate final (positive) response */
- GNUNET_assert (NULL != rctx->ev_sigs);
- res = reply_refresh_reveal_success (connection,
- num_fresh_coins,
- rctx->ev_sigs);
- break;
- }
- } /* end for (retries...) */
-
-cleanup:
- GNUNET_break (MHD_NO != res);
- /* free resources */
- if (NULL != rctx->ev_sigs)
- {
- for (unsigned int i = 0; i<num_fresh_coins; i++)
- if (NULL != rctx->ev_sigs[i].rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free (rctx->ev_sigs[i].rsa_signature);
- GNUNET_free (rctx->ev_sigs);
- rctx->ev_sigs = NULL; /* just to be safe... */
- }
- for (unsigned int i = 0; i<num_fresh_coins; i++)
- GNUNET_free_non_null (rcds[i].coin_ev);
- return res;
-}
-
-
-/**
- * Handle a "/refresh/reveal" request. Parses the given JSON
- * transfer private keys and if successful, passes everything to
- * #resolve_refresh_reveal_denominations() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @param connection the MHD connection to handle
- * @param rctx context for the operation, partially built at this time
- * @param tp_json private transfer keys in JSON format
- * @param link_sigs_json link signatures in JSON format
- * @param new_denoms_h_json requests for fresh coins to be created
- * @param coin_evs envelopes of gamma-selected coins to be signed
- * @return MHD result code
- */
-static int
-handle_refresh_reveal_json (struct MHD_Connection *connection,
- struct RevealContext *rctx,
- const json_t *tp_json,
- const json_t *link_sigs_json,
- const json_t *new_denoms_h_json,
- const json_t *coin_evs)
-{
- unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
- unsigned int num_tprivs = json_array_size (tp_json);
-
- GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1);
- if ( (num_fresh_coins >= MAX_FRESH_COINS) ||
- (0 == num_fresh_coins) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
- "new_denoms_h");
-
- }
- if (json_array_size (new_denoms_h_json) !=
- json_array_size (coin_evs))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH,
- "new_denoms/coin_evs");
- }
- if (json_array_size (new_denoms_h_json) !=
- json_array_size (link_sigs_json))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH,
- "new_denoms/link_sigs");
- }
-
- /* Parse transfer private keys array */
- for (unsigned int i = 0; i<num_tprivs; i++)
- {
- struct GNUNET_JSON_Specification trans_spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &rctx->transfer_privs[i]),
- GNUNET_JSON_spec_end ()
- };
- int res;
-
- res = TALER_MHD_parse_json_array (connection,
- tp_json,
- trans_spec,
- i,
- -1);
- if (GNUNET_OK != res)
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
-
- {
- struct TEH_KS_StateHandle *key_state;
- int ret;
-
- key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
- if (NULL == key_state)
- {
- TALER_LOG_ERROR ("Lacking keys to operate\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFRESH_REVEAL_KEYS_MISSING,
- "exchange lacks keys");
- }
- ret = resolve_refresh_reveal_denominations (key_state,
- connection,
- rctx,
- link_sigs_json,
- new_denoms_h_json,
- coin_evs);
- TEH_KS_release (key_state);
- return ret;
- }
-}
-
-
-/**
- * Handle a "/refreshes/$RCH/reveal" request. This time, the client reveals the
- * private transfer keys except for the cut-and-choose value returned from
- * "/coins/$COIN_PUB/melt". This function parses the revealed keys and secrets and
- * ultimately passes everything to #resolve_refresh_reveal_denominations()
- * which will verify that the revealed information is valid then runs the
- * transaction in #refresh_reveal_transaction() and finally returns the signed
- * refreshed coins.
- *
- * @param rh context of the handler
- * @param coin_pub public key of the coin
- * @param root uploaded JSON data
- * @param args array of additional options (length: 2, session hash and the string "reveal")
- * @return MHD result code
- */
-int
-TEH_REFRESH_handler_reveal (const struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- const json_t *root,
- const char *const args[2])
-{
- int res;
- json_t *coin_evs;
- json_t *transfer_privs;
- json_t *link_sigs;
- json_t *new_denoms_h;
- struct RevealContext rctx;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp),
- GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
- GNUNET_JSON_spec_json ("link_sigs", &link_sigs),
- GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
- GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h),
- GNUNET_JSON_spec_end ()
- };
-
- (void) rh;
- memset (&rctx,
- 0,
- sizeof (rctx));
-
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (args[0],
- strlen (args[0]),
- &rctx.rc,
- sizeof (rctx.rc)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFRESHES_INVALID_RCH,
- "refresh commitment hash malformed");
- }
- if (0 != strcmp (args[1],
- "reveal"))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_OPERATION_INVALID,
- "expected 'reveal' operation");
- }
- res = TALER_MHD_parse_json_data (connection,
- root,
- spec);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- }
-
- /* Check we got enough transfer private keys */
- /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
- if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
- {
- GNUNET_JSON_parse_free (spec);
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
- "transfer_privs");
- }
- res = handle_refresh_reveal_json (connection,
- &rctx,
- transfer_privs,
- link_sigs,
- new_denoms_h,
- coin_evs);
- GNUNET_JSON_parse_free (spec);
- return res;
-}
-
-
-/* end of taler-exchange-httpd_refresh_reveal.c */