aboutsummaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_age-withdraw_reveal.c')
-rw-r--r--src/exchange/taler-exchange-httpd_age-withdraw_reveal.c932
1 files changed, 221 insertions, 711 deletions
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
index d604632d..9bb6ac40 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
@@ -19,10 +19,12 @@
* @author Özgür Kesim
*/
#include "platform.h"
+#include <gnunet/gnunet_common.h>
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include <microhttpd.h>
#include "taler-exchange-httpd_metrics.h"
+#include "taler_error_codes.h"
#include "taler_exchangedb_plugin.h"
#include "taler_mhd_lib.h"
#include "taler-exchange-httpd_mhd.h"
@@ -50,95 +52,30 @@ struct AgeRevealContext
struct TALER_ReservePublicKeyP reserve_pub;
/**
- * Number of coins/denonations in the reveal
+ * Number of coins to reveal. MUST be equal to
+ * @e num_secrets/(kappa -1).
*/
uint32_t num_coins;
/**
- * #num_coins hashes of the denominations from which the coins are withdrawn.
- * Those must support age restriction.
+ * Number of secrets in the reveal. MUST be a multiple of (kappa-1).
*/
- struct TALER_DenominationHashP *denoms_h;
+ uint32_t num_secrets;
/**
- * #num_coins denomination keys, found in the system, according to denoms_h;
- */
- struct TEH_DenominationKey *denom_keys;
-
- /**
- * Total sum of all denominations' values
- **/
- struct TALER_Amount total_amount;
-
- /**
- * Total sum of all denominations' fees
- */
- struct TALER_Amount total_fee;
-
- /**
- * #num_coins hashes of blinded coin planchets.
- */
- struct TALER_BlindedPlanchet *coin_evs;
-
- /**
- * secrets for #num_coins*(kappa - 1) disclosed coins.
+ * @e num_secrets secrets for disclosed coins.
*/
struct TALER_PlanchetMasterSecretP *disclosed_coin_secrets;
/**
* The data from the original age-withdraw. Will be retrieved from
- * the DB via @a ach.
+ * the DB via @a ach and @a reserve_pub.
*/
- struct TALER_EXCHANGEDB_AgeWithdrawCommitment commitment;
+ struct TALER_EXCHANGEDB_AgeWithdraw *commitment;
};
/**
- * Information per planchet in the batch.
- */
-struct PlanchetContext
-{
-
- /**
- * Hash of the (blinded) message to be signed by the Exchange.
- */
- struct TALER_BlindedCoinHashP h_coin_envelope;
-
- /**
- * Value of the coin being exchanged (matching the denomination key)
- * plus the transaction fee. We include this in what is being
- * signed so that we can verify a reserve's remaining total balance
- * without needing to access the respective denomination key
- * information each time.
- */
- struct TALER_Amount amount_with_fee;
-
- /**
- * Blinded planchet.
- */
- struct TALER_BlindedPlanchet blinded_planchet;
-
- /**
- * Set to the resulting signed coin data to be returned to the client.
- */
- struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
-
-};
-
-/**
- * Helper function to free resources in the context
- */
-void
-age_reveal_context_free (struct AgeRevealContext *actx)
-{
- GNUNET_free (actx->denoms_h);
- GNUNET_free (actx->denom_keys);
- GNUNET_free (actx->coin_evs);
- GNUNET_free (actx->disclosed_coin_secrets);
-}
-
-
-/**
* Parse the json body of an '/age-withdraw/$ACH/reveal' request. It extracts
* the denomination hashes, blinded coins and disclosed coins and allocates
* memory for those.
@@ -154,50 +91,40 @@ age_reveal_context_free (struct AgeRevealContext *actx)
static enum GNUNET_GenericReturnValue
parse_age_withdraw_reveal_json (
struct MHD_Connection *connection,
- const json_t *j_denoms_h,
- const json_t *j_coin_evs,
const json_t *j_disclosed_coin_secrets,
struct AgeRevealContext *actx,
MHD_RESULT *mhd_ret)
{
enum GNUNET_GenericReturnValue result = GNUNET_SYSERR;
+ size_t num_entries;
/* Verify JSON-structure consistency */
{
const char *error = NULL;
- actx->num_coins = json_array_size (j_denoms_h); /* 0, if j_denoms_h is not an array */
+ num_entries = json_array_size (j_disclosed_coin_secrets); /* 0, if not an array */
- if (! json_is_array (j_denoms_h))
- error = "denoms_h must be an array";
- else if (! json_is_array (j_coin_evs))
- error = "coin_evs must be an array";
- else if (! json_is_array (j_disclosed_coin_secrets))
+ if (! json_is_array (j_disclosed_coin_secrets))
error = "disclosed_coin_secrets must be an array";
- else if (actx->num_coins == 0)
- error = "denoms_h must not be empty";
- else if (actx->num_coins != json_array_size (j_coin_evs))
- error = "denoms_h and coins_evs must be arrays of the same size";
- else if (actx->num_coins > TALER_MAX_FRESH_COINS)
- /**
- * The wallet had committed to more than the maximum coins allowed, the
- * reserve has been charged, but now the user can not withdraw any money
- * from it. Note that the user can't get their money back in this case!
- **/
+ else if (num_entries == 0)
+ error = "disclosed_coin_secrets must not be empty";
+ else if (num_entries > TALER_MAX_FRESH_COINS * (TALER_CNC_KAPPA - 1))
error = "maximum number of coins that can be withdrawn has been exceeded";
- else if (actx->num_coins * (TALER_CNC_KAPPA - 1)
- != json_array_size (j_disclosed_coin_secrets))
- error = "the size of array disclosed_coin_secrets must be "
- TALER_CNC_KAPPA_MINUS_ONE_STR " times the size of denoms_h";
+ else if (0 != num_entries % (TALER_CNC_KAPPA - 1))
+ error = "the size of disclosed_coin_secrets must be a multiple of "
+ TALER_CNC_KAPPA_MINUS_ONE_STR;
if (NULL != error)
{
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- error);
+ *mhd_ret = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ error);
return GNUNET_SYSERR;
}
+
+ actx->num_secrets = num_entries;
+ actx->num_coins = num_entries / (TALER_CNC_KAPPA - 1);
+
}
/* Continue parsing the parts */
@@ -205,78 +132,10 @@ parse_age_withdraw_reveal_json (
unsigned int idx = 0;
json_t *value = NULL;
- /* Parse denomination keys */
- actx->denoms_h = GNUNET_new_array (actx->num_coins,
- struct TALER_DenominationHashP);
-
- json_array_foreach (j_denoms_h, idx, value) {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &actx->denoms_h[idx]),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value, spec, NULL, NULL))
- {
- char msg[256] = {0};
- GNUNET_snprintf (msg,
- sizeof(msg),
- "couldn't parse entry no. %d in array denoms_h",
- idx + 1);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- msg);
- goto EXIT;
- }
- };
-
- /* Parse blinded envelopes */
- actx->coin_evs = GNUNET_new_array (actx->num_coins,
- struct TALER_BlindedPlanchet);
-
- json_array_foreach (j_coin_evs, idx, value) {
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_blinded_planchet (NULL, &actx->coin_evs[idx]),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value, spec, NULL, NULL))
- {
- char msg[256] = {0};
- GNUNET_snprintf (msg,
- sizeof(msg),
- "couldn't parse entry no. %d in array coin_evs",
- idx + 1);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- msg);
- goto EXIT;
- }
-
- /* Check for duplicate planchets */
- for (unsigned int i = 0; i < idx; i++)
- {
- if (0 == TALER_blinded_planchet_cmp (&actx->coin_evs[idx],
- &actx->coin_evs[i]))
- {
- GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "duplicate planchet");
- goto EXIT;
- }
-
- }
- };
-
/* Parse diclosed keys */
- actx->disclosed_coin_secrets = GNUNET_new_array (
- actx->num_coins * (TALER_CNC_KAPPA - 1),
- struct TALER_PlanchetMasterSecretP);
+ actx->disclosed_coin_secrets =
+ GNUNET_new_array (num_entries,
+ struct TALER_PlanchetMasterSecretP);
json_array_foreach (j_disclosed_coin_secrets, idx, value) {
struct GNUNET_JSON_Specification spec[] = {
@@ -292,18 +151,15 @@ parse_age_withdraw_reveal_json (
sizeof(msg),
"couldn't parse entry no. %d in array disclosed_coin_secrets",
idx + 1);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- msg);
+ *mhd_ret = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ msg);
goto EXIT;
}
};
}
result = GNUNET_OK;
- *mhd_ret = MHD_YES;
-
EXIT:
return result;
@@ -328,256 +184,181 @@ find_original_commitment (
struct MHD_Connection *connection,
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
const struct TALER_ReservePublicKeyP *reserve_pub,
- struct TALER_EXCHANGEDB_AgeWithdrawCommitment *commitment,
+ struct TALER_EXCHANGEDB_AgeWithdraw **commitment,
MHD_RESULT *result)
{
enum GNUNET_DB_QueryStatus qs;
- qs = TEH_plugin->get_age_withdraw_info (TEH_plugin->cls,
- reserve_pub,
- h_commitment,
- commitment);
- switch (qs)
+ for (unsigned int try = 0; try < 3; try++)
{
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- return GNUNET_OK; /* Only happy case */
+ qs = TEH_plugin->get_age_withdraw (TEH_plugin->cls,
+ reserve_pub,
+ h_commitment,
+ *commitment);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ return GNUNET_OK; /* Only happy case */
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_AGE_WITHDRAW_COMMITMENT_UNKNOWN,
- NULL);
- break;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ *result = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_AGE_WITHDRAW_COMMITMENT_UNKNOWN,
+ NULL);
+ return GNUNET_SYSERR;
- case GNUNET_DB_STATUS_HARD_ERROR:
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get_age_withdraw_info");
- break;
-
- case GNUNET_DB_STATUS_SOFT_ERROR:
- /* FIXME oec: Do we queue a result in this case or retry? */
- default:
- GNUNET_break (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- NULL);
- }
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ *result = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get_age_withdraw_info");
+ return GNUNET_SYSERR;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ break; /* try again */
+ default:
+ GNUNET_break (0);
+ *result = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
+ return GNUNET_SYSERR;
+ }
+ }
+ /* after unsuccessfull retries*/
+ *result = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get_age_withdraw_info");
return GNUNET_SYSERR;
}
/**
- * Check if the given denomination is still or already valid, has not been
- * revoked and supports age restriction.
+ * @brief Derives a age-restricted planchet from a given secret and calculates the hash
*
- * @param connection HTTP-connection to the client
- * @param ksh The handle to the current state of (denomination) keys in the exchange
- * @param denom_h Hash of the denomination key to check
- * @param[out] dks On success, will contain the denomination key details
- * @param[out] result On failure, an MHD-response will be qeued and result will be set to accordingly
- * @return true on success (denomination valid), false otherwise
+ * @param connection Connection to the client
+ * @param ksh The denomination keys in memory
+ * @param secret The secret to a planchet
+ * @param denom_pub_h The hash of the denomination for the planchet
+ * @param max_age The maximum age allowed
+ * @param[out] hc Hashcode to write
+ * @param[out] result On error, a HTTP-response will be queued and result set accordingly
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise, with an error message
+ * written to the client and @e result set.
*/
-static bool
-denomination_is_valid (
+static enum GNUNET_GenericReturnValue
+calculate_blinded_hash (
struct MHD_Connection *connection,
- struct TEH_KeyStateHandle *ksh,
- const struct TALER_DenominationHashP *denom_h,
- struct TEH_DenominationKey *dks,
+ const struct TEH_KeyStateHandle *keys,
+ const struct TALER_PlanchetMasterSecretP *secret,
+ const struct TALER_DenominationHashP *denom_pub_h,
+ uint8_t max_age,
+ struct TALER_BlindedCoinHashP *bch,
MHD_RESULT *result)
{
- dks = TEH_keys_denomination_by_hash2 (ksh,
- denom_h,
- connection,
- result);
- if (NULL == dks)
- {
- /* The denomination doesn't exist */
- GNUNET_assert (result != NULL);
- /* Note: a HTTP-response has been queued and result has been set by
- * TEH_keys_denominations_by_hash2 */
- return false;
- }
-
- if (GNUNET_TIME_absolute_is_past (dks->meta.expire_withdraw.abs_time))
- {
- /* This denomination is past the expiration time for withdraws */
- *result = TEH_RESPONSE_reply_expired_denom_pub_hash (
- connection,
- denom_h,
- TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
- "age-withdraw_reveal");
- return false;
- }
-
- if (GNUNET_TIME_absolute_is_future (dks->meta.start.abs_time))
- {
- /* This denomination is not yet valid */
- *result = TEH_RESPONSE_reply_expired_denom_pub_hash (
- connection,
- denom_h,
- TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
- "age-withdraw_reveal");
- return false;
- }
-
- if (dks->recoup_possible)
- {
- /* This denomination has been revoked */
- *result = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_GONE,
- TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
- NULL);
- return false;
- }
+ enum GNUNET_GenericReturnValue ret;
+ struct TEH_DenominationKey *denom_key;
+ struct TALER_AgeCommitmentHash ach;
+
+ /* First, retrieve denomination details */
+ denom_key = TEH_keys_denomination_by_hash_from_state (keys,
+ denom_pub_h,
+ connection,
+ result);
+ if (NULL == denom_key)
+ return GNUNET_SYSERR;
- if (0 == dks->denom_pub.age_mask.bits)
+ /* calculate age commitment hash */
{
- /* This denomation does not support age restriction */
- char msg[256] = {0};
- GNUNET_snprintf (msg,
- sizeof(msg),
- "denomination %s does not support age restriction",
- GNUNET_h2s (&denom_h->hash));
-
- *result = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
- msg);
- return false;
- }
+ struct TALER_AgeCommitmentProof acp;
+ ret = TALER_age_restriction_from_secret (secret,
+ &denom_key->denom_pub.age_mask,
+ max_age,
+ &acp);
- return true;
-}
-
-
-/**
- * Check if the given array of hashes of denomination_keys a) belong
- * to valid denominations and b) those are marked as age restricted.
- *
- * @param connection The HTTP connection to the client
- * @param len The lengths of the array @a denoms_h
- * @param denoms_h array of hashes of denomination public keys
- * @param coin_evs array of blinded coin planchets
- * @param[out] dks On success, will be filled with the denomination keys. Caller must deallocate.
- * @param amount_with_fee The committed amount including fees
- * @param[out] total_amount On success, will contain the total sum of all denominations
- * @param[out] total_fee On success, will contain the total sum of all fees
- * @param[out] result In the error cases, a response will be queued with MHD and this will be the result.
- * @return GNUNET_OK if the denominations are valid and support age-restriction
- * GNUNET_SYSERR otherwise
- */
-static enum GNUNET_GenericReturnValue
-are_denominations_valid (
- struct MHD_Connection *connection,
- uint32_t len,
- const struct TALER_DenominationHashP *denoms_h,
- const struct TALER_BlindedPlanchet *coin_evs,
- struct TEH_DenominationKey **dks,
- const struct TALER_Amount *amount_with_fee,
- struct TALER_Amount *total_amount,
- struct TALER_Amount *total_fee,
- MHD_RESULT *result)
-{
- struct TEH_KeyStateHandle *ksh;
-
- GNUNET_assert (*dks == NULL);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_break (0);
+ *result = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, /* FIXME[oec]: better error code */
+ "derivation of age restriction failed");
+ return ret;
+ }
- ksh = TEH_keys_get_state ();
- if (NULL == ksh)
- {
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
- NULL);
- return GNUNET_SYSERR;
+ TALER_age_commitment_hash (&acp.commitment, &ach);
}
- *dks = GNUNET_new_array (len, struct TEH_DenominationKey);
- TALER_amount_set_zero (TEH_currency, total_amount);
- TALER_amount_set_zero (TEH_currency, total_fee);
-
- for (uint32_t i = 0; i < len; i++)
+ /* Next: calculate planchet */
{
- if (! denomination_is_valid (connection,
- ksh,
- &denoms_h[i],
- dks[i],
- result))
- return GNUNET_SYSERR;
+ struct TALER_CoinPubHashP c_hash;
+ struct TALER_PlanchetDetail detail;
+ struct TALER_CoinSpendPrivateKeyP coin_priv;
+ union TALER_DenominationBlindingKeyP bks;
+ struct TALER_ExchangeWithdrawValues alg_values = {
+ .cipher = denom_key->denom_pub.cipher,
+ };
- /* Ensure the ciphers from the planchets match the denominations' */
- if (dks[i]->denom_pub.cipher != coin_evs[i].cipher)
+ if (TALER_DENOMINATION_CS == alg_values.cipher)
{
- GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
- NULL);
- return GNUNET_SYSERR;
- }
+ struct TALER_CsNonce nonce;
- /* Accumulate the values */
- if (0 > TALER_amount_add (
- total_amount,
- total_amount,
- &dks[i]->meta.value))
- {
- GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW,
- "amount");
- return GNUNET_SYSERR;
- }
+ TALER_cs_withdraw_nonce_derive (
+ secret,
+ &nonce);
- /* Accumulate the withdraw fees */
- if (0 > TALER_amount_add (
- total_fee,
- total_fee,
- &dks[i]->meta.fees.withdraw))
- {
- GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW,
- "fee");
- return GNUNET_SYSERR;
+ {
+ enum TALER_ErrorCode ec;
+ struct TEH_CsDeriveData cdd = {
+ .h_denom_pub = &denom_key->h_denom_pub,
+ .nonce = &nonce,
+ };
+
+ ec = TEH_keys_denomination_cs_r_pub (&cdd,
+ false,
+ &alg_values.details.
+ cs_values);
+ /* FIXME Handle error? */
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
- }
- /* Compare the committed amount against the totals */
- {
- struct TALER_Amount sum;
- TALER_amount_set_zero (TEH_currency, &sum);
+ TALER_planchet_blinding_secret_create (secret,
+ &alg_values,
+ &bks);
+
+ TALER_planchet_setup_coin_priv (secret,
+ &alg_values,
+ &coin_priv);
- GNUNET_assert (0 < TALER_amount_add (
- &sum,
- total_amount,
- total_fee));
+ ret = TALER_planchet_prepare (&denom_key->denom_pub,
+ &alg_values,
+ &bks,
+ &coin_priv,
+ &ach,
+ &c_hash,
+ &detail);
- if (0 != TALER_amount_cmp (&sum, amount_with_fee))
+ if (GNUNET_OK != ret)
{
- GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_INCORRECT,
- NULL);
- return GNUNET_SYSERR;
+ GNUNET_break (0);
+ *result = TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ "{ss}",
+ "details",
+ "failed to prepare planchet from base key");
+ return ret;
}
+
+ ret = TALER_coin_ev_hash (&detail.blinded_planchet,
+ &denom_key->h_denom_pub,
+ bch);
+ GNUNET_assert (GNUNET_OK == ret);
}
- return GNUNET_OK;
+ return GNUNET_SYSERR;
}
/**
- * Checks the validity of the disclosed coins as follows:
+ * @brief Checks the validity of the disclosed coins as follows:
* - Derives and calculates the disclosed coins'
* - public keys,
* - nonces (if applicable),
@@ -594,163 +375,83 @@ are_denominations_valid (
* https://docs.taler.net/design-documents/024-age-restriction.html#withdraw
*
* @param connection HTTP-connection to the client
- * @param h_commitment_orig Original commitment
- * @param max_age Maximum age allowed for the age restriction
- * @param noreveal_idx Index that was given to the client in response to the age-withdraw request
- * @param num_coins Number of coins
- * @param coin_evs The blindet planchets of the undisclosed coins, @a num_coins many
- * @param denom_keys The array of denomination keys, @a num_coins. Needed to detect Clause-Schnorr-based denominations
+ * @param commitment Original commitment
* @param disclosed_coin_secrets The secrets of the disclosed coins, (TALER_CNC_KAPPA - 1)*num_coins many
+ * @param num_coins number of coins to reveal via @a disclosed_coin_secrets
* @param[out] result On error, a HTTP-response will be queued and result set accordingly
* @return GNUNET_OK on success, GNUNET_SYSERR otherwise
*/
static enum GNUNET_GenericReturnValue
verify_commitment_and_max_age (
struct MHD_Connection *connection,
- const struct TALER_AgeWithdrawCommitmentHashP *h_commitment_orig,
- const uint32_t max_age,
- const uint32_t noreveal_idx,
- const uint32_t num_coins,
- const struct TALER_BlindedPlanchet *coin_evs,
- const struct TEH_DenominationKey *denom_keys,
+ const struct TALER_EXCHANGEDB_AgeWithdraw *commitment,
const struct TALER_PlanchetMasterSecretP *disclosed_coin_secrets,
+ uint32_t num_coins,
MHD_RESULT *result)
{
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
struct GNUNET_HashContext *hash_context;
+ struct TEH_KeyStateHandle *keys;
+
+ if (num_coins != commitment->num_coins)
+ {
+ GNUNET_break_op (0);
+ *result = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "#coins");
+ return GNUNET_SYSERR;
+ }
+
+ /* We need the current keys in memory for the meta-data of the denominations */
+ keys = TEH_keys_get_state ();
+ if (NULL == keys)
+ {
+ *result = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ NULL);
+ return GNUNET_SYSERR;
+ }
hash_context = GNUNET_CRYPTO_hash_context_start ();
- for (size_t c = 0; c < num_coins; c++)
+ for (size_t coin_idx = 0; coin_idx < num_coins; coin_idx++)
{
- size_t k = 0; /* either 0 or 1, to index into coin_evs */
+ size_t i = 0; /* either 0 or 1, to index into coin_evs */
- for (size_t idx = 0; idx<TALER_CNC_KAPPA; idx++)
+ for (size_t gamma = 0; gamma<TALER_CNC_KAPPA; gamma++)
{
- if (idx == (size_t) noreveal_idx)
+ if (gamma == (size_t) commitment->noreveal_index)
{
GNUNET_CRYPTO_hash_context_read (hash_context,
- &coin_evs[c],
- sizeof(coin_evs[c]));
+ &commitment->h_coin_evs[coin_idx],
+ sizeof(commitment->h_coin_evs[coin_idx]));
}
else
{
- /* FIXME[oec] Refactor this block out into its own function */
-
- size_t j = (TALER_CNC_KAPPA - 1) * c + k; /* Index into disclosed_coin_secrets[] */
+ /* j is the index into disclosed_coin_secrets[] */
+ size_t j = (TALER_CNC_KAPPA - 1) * coin_idx + i;
const struct TALER_PlanchetMasterSecretP *secret;
- struct TALER_AgeCommitmentHash ach;
struct TALER_BlindedCoinHashP bch;
- GNUNET_assert (k<2);
+ GNUNET_assert (i<2);
GNUNET_assert ((TALER_CNC_KAPPA - 1) * num_coins > j);
secret = &disclosed_coin_secrets[j];
- k++;
+ i++;
- /* First: calculate age commitment hash */
- {
- struct TALER_AgeCommitmentProof acp;
- ret = TALER_age_restriction_from_secret (
- secret,
- &denom_keys[c].denom_pub.age_mask,
- max_age,
- &acp);
-
- if (GNUNET_OK != ret)
- {
- GNUNET_break (0);
- *result = TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- "{sssi}",
- "failed to derive age restriction from base key",
- "index",
- j);
- return ret;
- }
-
- TALER_age_commitment_hash (&acp.commitment, &ach);
- }
+ ret = calculate_blinded_hash (connection,
+ keys,
+ secret,
+ &commitment->denom_pub_hashes[coin_idx],
+ commitment->max_age,
+ &bch,
+ result);
- /* Next: calculate planchet */
+ if (GNUNET_OK != ret)
{
- struct TALER_CoinPubHashP c_hash;
- struct TALER_PlanchetDetail detail;
- struct TALER_CoinSpendPrivateKeyP coin_priv;
- union TALER_DenominationBlindingKeyP bks;
- struct TALER_ExchangeWithdrawValues alg_values = {
- .cipher = denom_keys[c].denom_pub.cipher,
- };
-
- if (TALER_DENOMINATION_CS == alg_values.cipher)
- {
- struct TALER_CsNonce nonce;
-
- TALER_cs_withdraw_nonce_derive (
- secret,
- &nonce);
-
- {
- enum TALER_ErrorCode ec;
- struct TEH_CsDeriveData cdd = {
- .h_denom_pub = &denom_keys[c].h_denom_pub,
- .nonce = &nonce,
- };
-
- ec = TEH_keys_denomination_cs_r_pub (&cdd,
- false,
- &alg_values.details.
- cs_values);
- /* FIXME Handle error? */
- GNUNET_assert (TALER_EC_NONE == ec);
- }
- }
-
- TALER_planchet_blinding_secret_create (secret,
- &alg_values,
- &bks);
-
- TALER_planchet_setup_coin_priv (secret,
- &alg_values,
- &coin_priv);
-
- ret = TALER_planchet_prepare (&denom_keys[c].denom_pub,
- &alg_values,
- &bks,
- &coin_priv,
- &ach,
- &c_hash,
- &detail);
-
- if (GNUNET_OK != ret)
- {
- GNUNET_break (0);
- *result = TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- "{sssi}",
- "details",
- "failed to prepare planchet from base key",
- "index",
- j);
- return ret;
- }
-
- ret = TALER_coin_ev_hash (&detail.blinded_planchet,
- &denom_keys[c].h_denom_pub,
- &bch);
- if (GNUNET_OK != ret)
- {
- GNUNET_break (0);
- *result = TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- "{sssi}",
- "details",
- "failed to hash planchet from base key",
- "index",
- j);
- return ret;
- }
-
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ return GNUNET_SYSERR;
}
/* Continue the running hash of all coin hashes with the calculated
@@ -768,7 +469,7 @@ verify_commitment_and_max_age (
GNUNET_CRYPTO_hash_context_finish (hash_context,
&calc_hash);
- if (0 != GNUNET_CRYPTO_hash_cmp (&h_commitment_orig->hash,
+ if (0 != GNUNET_CRYPTO_hash_cmp (&commitment->h_commitment.hash,
&calc_hash))
{
GNUNET_break_op (0);
@@ -788,26 +489,22 @@ verify_commitment_and_max_age (
* @brief Send a response for "/age-withdraw/$RCH/reveal"
*
* @param connection The http connection to the client to send the response to
- * @param num_coins Number of new coins with age restriction for which we reveal data
- * @param awrcs array of @a num_coins signatures revealed
+ * @param commitment The data from the commitment with signatures
* @return a MHD result code
*/
static MHD_RESULT
reply_age_withdraw_reveal_success (
struct MHD_Connection *connection,
- unsigned int num_coins,
- const struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin *awrcs)
+ const struct TALER_EXCHANGEDB_AgeWithdraw *commitment)
{
json_t *list = json_array ();
GNUNET_assert (NULL != list);
- for (unsigned int index = 0;
- index < num_coins;
- index++)
+ for (unsigned int i = 0; i < commitment->num_coins; i++)
{
json_t *obj = GNUNET_JSON_PACK (
TALER_JSON_pack_blinded_denom_sig ("ev_sig",
- &awrcs[index].coin_sig));
+ &commitment->denom_sigs[i]));
GNUNET_assert (0 ==
json_array_append_new (list,
obj));
@@ -821,160 +518,6 @@ reply_age_withdraw_reveal_success (
}
-/**
- * @brief Signs and persists the undisclosed coins
- *
- * @param connection HTTP-connection to the client
- * @param h_commitment Original commitment
- * @param num_coins Number of coins
- * @param coin_evs The Hashes of the undisclosed, blinded coins, @a num_coins many
- * @param denom_keys The array of denomination keys, @a num_coins. Needed to detect Clause-Schnorr-based denominations
- * @param[out] result On error, a HTTP-response will be queued and result set accordingly
- * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
- */
-static enum GNUNET_GenericReturnValue
-sign_and_finalize_age_withdraw (
- struct MHD_Connection *connection,
- const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
- const uint32_t num_coins,
- const struct TALER_BlindedPlanchet *coin_evs,
- const struct TEH_DenominationKey *denom_keys,
- MHD_RESULT *result)
-{
- enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
- struct TEH_CoinSignData csds[num_coins];
- struct TALER_BlindedDenominationSignature bds[num_coins];
- struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin awrcs[num_coins];
- enum GNUNET_DB_QueryStatus qs;
-
- for (uint32_t i = 0; i<num_coins; i++)
- {
- csds[i].h_denom_pub = &denom_keys[i].h_denom_pub;
- csds[i].bp = &coin_evs[i];
- }
-
- /* Sign the the blinded coins first */
- {
- enum TALER_ErrorCode ec;
- ec = TEH_keys_denomination_batch_sign (csds,
- num_coins,
- false,
- bds);
- if (TALER_EC_NONE != ec)
- {
- GNUNET_break (0);
- *result = TALER_MHD_reply_with_ec (connection,
- ec,
- NULL);
- return GNUNET_SYSERR;
- }
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Signatures ready, starting DB interaction\n");
-
- /* Prepare the data for insertion */
- for (uint32_t i = 0; i<num_coins; i++)
- {
- TALER_coin_ev_hash (&coin_evs[i],
- csds[i].h_denom_pub,
- &awrcs[i].h_coin_ev);
- awrcs[i].h_denom_pub = *csds[i].h_denom_pub;
- awrcs[i].coin_sig = bds[i];
- }
-
- /* Persist operation result in DB, transactionally */
- for (unsigned int r = 0; r < MAX_TRANSACTION_COMMIT_RETRIES; r++)
- {
- bool changed = false;
-
- /* Transaction start */
- if (GNUNET_OK !=
- TEH_plugin->start (TEH_plugin->cls,
- "insert_age_withdraw_reveal batch"))
- {
- GNUNET_break (0);
- ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_START_FAILED,
- NULL);
- goto cleanup;
- }
-
- qs = TEH_plugin->insert_age_withdraw_reveal (TEH_plugin->cls,
- h_commitment,
- num_coins,
- awrcs);
-
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- TEH_plugin->rollback (TEH_plugin->cls);
- continue;
- }
- else if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls);
- ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "insert_age_withdraw_reveal");
- goto cleanup;
- }
-
- changed = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
-
- /* Commit the transaction */
- qs = TEH_plugin->commit (TEH_plugin->cls);
- if (qs >= 0)
- {
- if (changed)
- TEH_METRICS_num_success[TEH_MT_SUCCESS_AGE_WITHDRAW_REVEAL]++;
-
- break; /* success */
-
- }
- else if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls);
- ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_COMMIT_FAILED,
- NULL);
- goto cleanup;
- }
- else
- {
- TEH_plugin->rollback (TEH_plugin->cls);
- }
- } /* end of retry */
-
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls);
- ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_SOFT_FAILURE,
- NULL);
- goto cleanup;
- }
-
- /* Generate final (positive) response */
- ret = reply_age_withdraw_reveal_success (connection,
- num_coins,
- awrcs);
-cleanup:
- GNUNET_break (GNUNET_OK != ret);
-
- /* Free resources */
- for (unsigned int i = 0; i<num_coins; i++)
- TALER_blinded_denom_sig_free (&awrcs[i].coin_sig);
- return ret;
-}
-
-
MHD_RESULT
TEH_handler_age_withdraw_reveal (
struct TEH_RequestContext *rc,
@@ -984,16 +527,10 @@ TEH_handler_age_withdraw_reveal (
MHD_RESULT result = MHD_NO;
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
struct AgeRevealContext actx = {0};
- const json_t *j_denoms_h;
- const json_t *j_coin_evs;
const json_t *j_disclosed_coin_secrets;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("reserve_pub",
&actx.reserve_pub),
- GNUNET_JSON_spec_array_const ("denoms_h",
- &j_denoms_h),
- GNUNET_JSON_spec_array_const ("coin_evs",
- &j_coin_evs),
GNUNET_JSON_spec_array_const ("disclosed_coin_secrets",
&j_disclosed_coin_secrets),
GNUNET_JSON_spec_end ()
@@ -1017,8 +554,6 @@ TEH_handler_age_withdraw_reveal (
if (GNUNET_OK !=
parse_age_withdraw_reveal_json (
rc->connection,
- j_denoms_h,
- j_coin_evs,
j_disclosed_coin_secrets,
&actx,
&result))
@@ -1034,49 +569,24 @@ TEH_handler_age_withdraw_reveal (
&result))
break;
- /* Ensure validity of denoms and the sum of amounts and fees */
- if (GNUNET_OK !=
- are_denominations_valid (
- rc->connection,
- actx.num_coins,
- actx.denoms_h,
- actx.coin_evs,
- &actx.denom_keys,
- &actx.commitment.amount_with_fee,
- &actx.total_amount,
- &actx.total_fee,
- &result))
- break;
-
/* Verify the computed h_commitment equals the committed one and that coins
* have a maximum age group corresponding max_age (age-mask dependent) */
if (GNUNET_OK !=
verify_commitment_and_max_age (
rc->connection,
- &actx.commitment.h_commitment,
- actx.commitment.max_age,
- actx.commitment.noreveal_index,
- actx.num_coins,
- actx.coin_evs,
- actx.denom_keys,
+ actx.commitment,
actx.disclosed_coin_secrets,
- &result))
- break;
-
- /* Finally, sign and persist the coins */
- if (GNUNET_OK !=
- sign_and_finalize_age_withdraw (
- rc->connection,
- &actx.commitment.h_commitment,
actx.num_coins,
- actx.coin_evs,
- actx.denom_keys,
&result))
break;
+ /* Finally, return the signatures */
+ result = reply_age_withdraw_reveal_success (rc->connection,
+ actx.commitment);
+
} while(0);
- age_reveal_context_free (&actx);
+ GNUNET_free (actx.disclosed_coin_secrets);
return result;
}