summaryrefslogtreecommitdiff
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.c826
1 files changed, 310 insertions, 516 deletions
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
index 50d524a2f..c9aca8e99 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
@@ -19,9 +19,13 @@
* @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"
#include "taler-exchange-httpd_age-withdraw_reveal.h"
@@ -48,60 +52,28 @@ 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 coins.
- */
- struct TALER_BlindedCoinHashP *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;
};
-/**
- * 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
@@ -109,8 +81,6 @@ age_reveal_context_free (struct AgeRevealContext *actx)
* memory for those.
*
* @param connection The MHD connection to handle
- * @param j_denoms_h Array of hashes of the denominations for the withdrawal, in JSON format
- * @param j_coin_evs The blinded envelopes in JSON format for the coins that are not revealed and will be signed on success
* @param j_disclosed_coin_secrets The n*(kappa-1) disclosed coins' private keys in JSON format, from which all other attributes (age restriction, blinding, nonce) will be derived from
* @param[out] actx The context of the operation, only partially built at call time
* @param[out] mhd_ret The result if a reply is queued for MHD
@@ -119,140 +89,95 @@ 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)
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";
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 * (TALER_CNC_KAPPA - 1);
+ actx->num_coins = num_entries;
+
}
/* Continue parsing the parts */
{
unsigned int idx = 0;
+ unsigned int k = 0;
+ json_t *array = NULL;
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 ()
- };
+ /* Parse diclosed keys */
+ actx->disclosed_coin_secrets =
+ GNUNET_new_array (actx->num_secrets,
+ struct TALER_PlanchetMasterSecretP);
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value, spec, NULL, NULL))
+ json_array_foreach (j_disclosed_coin_secrets, idx, array) {
+ if (! json_is_array (array) ||
+ (TALER_CNC_KAPPA - 1 != json_array_size (array)))
{
char msg[256] = {0};
GNUNET_snprintf (msg,
sizeof(msg),
- "couldn't parse entry no. %d in array denoms_h",
+ "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;
- }
- };
-
- /* Parse blinded envelopes */
- actx->coin_evs = GNUNET_new_array (actx->num_coins,
- struct TALER_BlindedCoinHashP);
-
- json_array_foreach (j_coin_evs, idx, value) {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto (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;
}
- };
-
- /* Parse diclosed keys */
- actx->disclosed_coin_secrets = GNUNET_new_array (
- actx->num_coins * (TALER_CNC_KAPPA - 1),
- struct TALER_PlanchetMasterSecretP);
-
- json_array_foreach (j_disclosed_coin_secrets, idx, value) {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &actx->disclosed_coin_secrets[idx]),
- GNUNET_JSON_spec_end ()
- };
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value, spec, NULL, NULL))
+ json_array_foreach (array, k, value)
{
- char msg[256] = {0};
- GNUNET_snprintf (msg,
- 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);
- goto EXIT;
+ struct TALER_PlanchetMasterSecretP *secret =
+ &actx->disclosed_coin_secrets[2 * idx + k];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL, secret),
+ 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 disclosed_coin_secrets[%d]",
+ k + 1,
+ idx + 1);
+ *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;
@@ -269,254 +194,181 @@ EXIT:
* @param reserve_pub Reserve public key used in the original age-withdraw request
* @param[out] commitment Data from the original age-withdraw request
* @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 withdraw request has been found,
- * GNUNET_SYSERROR if we did not find the request in the DB
+ * @return #GNUNET_OK if the withdraw request has been found,
+ * #GNUNET_SYSERR if we did not find the request in the DB
*/
static enum GNUNET_GenericReturnValue
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 */
-
- 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_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); /* should be impossible */
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- NULL);
+ 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);
+ return GNUNET_SYSERR;
+ 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 unsuccessful 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 keys 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] bch 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))
+ 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)
{
- /* 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;
+ GNUNET_break_op (0);
+ *result = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ NULL);
+ return GNUNET_SYSERR;
}
- if (dks->recoup_possible)
+ /* calculate age commitment hash */
{
- /* This denomination has been revoked */
- *result = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_GONE,
- TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
- NULL);
- return false;
+ struct TALER_AgeCommitmentProof acp;
+
+ TALER_age_restriction_from_secret (secret,
+ &denom_key->denom_pub.age_mask,
+ max_age,
+ &acp);
+ TALER_age_commitment_hash (&acp.commitment,
+ &ach);
+ TALER_age_commitment_proof_free (&acp);
}
- if (0 == dks->denom_pub.age_mask.bits)
+ /* Next: calculate planchet */
{
- /* 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;
- }
-
- 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[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,
- 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);
-
- 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;
- }
-
- *dks = GNUNET_new_array (len, struct TEH_DenominationKey);
- TALER_amount_set_zero (TEH_currency, total_amount);
- TALER_amount_set_zero (TEH_currency, total_fee);
+ struct TALER_CoinPubHashP c_hash;
+ struct TALER_PlanchetDetail detail = {0};
+ struct TALER_CoinSpendPrivateKeyP coin_priv;
+ union GNUNET_CRYPTO_BlindingSecretP bks;
+ struct GNUNET_CRYPTO_BlindingInputValues bi = {
+ .cipher = denom_key->denom_pub.bsign_pub_key->cipher
+ };
+ struct TALER_ExchangeWithdrawValues alg_values = {
+ .blinding_inputs = &bi
+ };
+ union GNUNET_CRYPTO_BlindSessionNonce nonce;
+ union GNUNET_CRYPTO_BlindSessionNonce *noncep = NULL;
- for (uint32_t i = 0; i < len; i++)
- {
- if (! denomination_is_valid (connection,
- ksh,
- &denoms_h[i],
- dks[i],
- result))
+ // FIXME: add logic to denom.c to do this!
+ if (GNUNET_CRYPTO_BSA_CS == bi.cipher)
{
- return GNUNET_SYSERR;
- }
+ struct TEH_CsDeriveData cdd = {
+ .h_denom_pub = &denom_key->h_denom_pub,
+ .nonce = &nonce.cs_nonce,
+ };
- /* Accumulate the values */
- if (0 > TALER_amount_add (
- total_amount,
- total_amount,
- &dks[i]->meta.value))
- {
- GNUNET_break (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW,
- "amount");
- return GNUNET_SYSERR;
+ TALER_cs_withdraw_nonce_derive (secret,
+ &nonce.cs_nonce);
+ noncep = &nonce;
+ GNUNET_assert (TALER_EC_NONE ==
+ TEH_keys_denomination_cs_r_pub (
+ &cdd,
+ false,
+ &bi.details.cs_values));
}
-
- /* Accumulate the withdraw fees */
- if (0 > TALER_amount_add (
- total_fee,
- total_fee,
- &dks[i]->meta.fees.withdraw))
+ TALER_planchet_blinding_secret_create (secret,
+ &alg_values,
+ &bks);
+ TALER_planchet_setup_coin_priv (secret,
+ &alg_values,
+ &coin_priv);
+ ret = TALER_planchet_prepare (&denom_key->denom_pub,
+ &alg_values,
+ &bks,
+ noncep,
+ &coin_priv,
+ &ach,
+ &c_hash,
+ &detail);
+ if (GNUNET_OK != ret)
{
GNUNET_break (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW,
- "fee");
- return GNUNET_SYSERR;
+ *result = TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ "{ss}",
+ "details",
+ "failed to prepare planchet from base key");
+ return ret;
}
- }
-
- /* Compare the committed amount against the totals */
- {
- struct TALER_Amount sum;
- TALER_amount_set_zero (TEH_currency, &sum);
-
- GNUNET_assert (0 < TALER_amount_add (
- &sum,
- total_amount,
- total_fee));
- if (0 != TALER_amount_cmp (&sum, amount_with_fee))
- {
- GNUNET_break_op (0);
- *result = TALER_MHD_reply_with_ec (connection,
- TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_INCORRECT,
- NULL);
- return GNUNET_SYSERR;
- }
+ TALER_coin_ev_hash (&detail.blinded_planchet,
+ &denom_key->h_denom_pub,
+ bch);
+ TALER_blinded_planchet_free (&detail.blinded_planchet);
}
- return GNUNET_OK;
+ return ret;
}
/**
- * 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),
@@ -533,169 +385,90 @@ 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 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 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_BlindedCoinHashP *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<3; idx++)
+ for (size_t k = 0; k<TALER_CNC_KAPPA; k++)
{
- if (idx == (size_t) noreveal_idx)
+ if (k == (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 = 2 * 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 (num_coins * (TALER_CNC_KAPPA - 1) > j);
+ GNUNET_assert (2>i);
+ 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_BlindedCoinHashP bch;
- 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);
-
-#pragma message ("FIXME:oec: return value of needs handling!")
- /* FIXME:oec: 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_read (hash_context,
- &detail.blinded_planchet,
- sizeof(detail.blinded_planchet));
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ return GNUNET_SYSERR;
}
+
+ /* Continue the running hash of all coin hashes with the calculated
+ * hash-value of the current, disclosed coin */
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &bch,
+ sizeof(bch));
}
}
}
@@ -706,7 +479,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);
@@ -717,8 +490,40 @@ verify_commitment_and_max_age (
}
}
+ return GNUNET_OK;
+}
- return ret;
+
+/**
+ * @brief Send a response for "/age-withdraw/$RCH/reveal"
+ *
+ * @param connection The http connection to the client to send the response to
+ * @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,
+ const struct TALER_EXCHANGEDB_AgeWithdraw *commitment)
+{
+ json_t *list = json_array ();
+ GNUNET_assert (NULL != list);
+
+ for (unsigned int i = 0; i < commitment->num_coins; i++)
+ {
+ json_t *obj = GNUNET_JSON_PACK (
+ TALER_JSON_pack_blinded_denom_sig (NULL,
+ &commitment->denom_sigs[i]));
+ GNUNET_assert (0 ==
+ json_array_append_new (list,
+ obj));
+ }
+
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_array_steal ("ev_sigs",
+ list));
}
@@ -731,45 +536,41 @@ TEH_handler_age_withdraw_reveal (
MHD_RESULT result = MHD_NO;
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
struct AgeRevealContext actx = {0};
- json_t *j_denoms_h;
- json_t *j_coin_evs;
- json_t *j_disclosed_coin_secrets;
+ 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_json ("denoms_h", &j_denoms_h),
- GNUNET_JSON_spec_json ("coin_evs", &j_coin_evs),
- GNUNET_JSON_spec_json ("disclosed_coin_secrets", &j_disclosed_coin_secrets),
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &actx.reserve_pub),
+ GNUNET_JSON_spec_array_const ("disclosed_coin_secrets",
+ &j_disclosed_coin_secrets),
GNUNET_JSON_spec_end ()
};
actx.ach = *ach;
/* Parse JSON body*/
+ ret = TALER_MHD_parse_json_data (rc->connection,
+ root,
+ spec);
+ if (GNUNET_OK != ret)
{
- ret = TALER_MHD_parse_json_data (rc->connection,
- root,
- spec);
- if (GNUNET_OK != ret)
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
- }
+ GNUNET_break_op (0);
+ return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
}
do {
/* Extract denominations, blinded and disclosed coins */
- if (GNUNET_OK != parse_age_withdraw_reveal_json (
+ if (GNUNET_OK !=
+ parse_age_withdraw_reveal_json (
rc->connection,
- j_denoms_h,
- j_coin_evs,
j_disclosed_coin_secrets,
&actx,
&result))
break;
/* Find original commitment */
- if (GNUNET_OK != find_original_commitment (
+ if (GNUNET_OK !=
+ find_original_commitment (
rc->connection,
&actx.ach,
&actx.reserve_pub,
@@ -777,38 +578,31 @@ 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.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 (
+ /* 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,
+ actx.num_coins,
&result))
break;
- /* TODO:oec: sign the coins */
+ /* Finally, return the signatures */
+ result = reply_age_withdraw_reveal_success (rc->connection,
+ &actx.commitment);
- } while(0);
+ } while (0);
- age_reveal_context_free (&actx);
GNUNET_JSON_parse_free (spec);
+ if (NULL != actx.commitment.denom_sigs)
+ for (unsigned int i = 0; i<actx.num_coins; i++)
+ TALER_blinded_denom_sig_free (&actx.commitment.denom_sigs[i]);
+ GNUNET_free (actx.commitment.denom_sigs);
+ GNUNET_free (actx.commitment.denom_pub_hashes);
+ GNUNET_free (actx.commitment.denom_serials);
+ GNUNET_free (actx.disclosed_coin_secrets);
return result;
}