summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2023-07-24 20:23:42 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2023-07-24 20:29:38 +0200
commit3e29bdfb8bfda133b7c25a36160a3533e836e0b8 (patch)
tree5d2271678e7b0e3175c340c8a033e3950050a11f
parente9f7ad3742bc16e1f0afa2162564c6faa4f3036f (diff)
downloadexchange-3e29bdfb8bfda133b7c25a36160a3533e836e0b8.tar.gz
exchange-3e29bdfb8bfda133b7c25a36160a3533e836e0b8.tar.bz2
exchange-3e29bdfb8bfda133b7c25a36160a3533e836e0b8.zip
[age-withdraw] first tests pass
age-withdraw successfully tested (no reveal yet): 1. reserve filled with amount large enough to trigger kyc 2. kyc oauth2 test daemon sets birthday to 2015-00-00 3. usual withdraw fails with CONFLICT and AGE_RESTRICTION_REQUIRED 4. age-withdraw with loo large of an maximum age fails 5. age-withdraw with appropriate maximum age succeeds
-rw-r--r--src/exchange/taler-exchange-httpd_age-withdraw.c178
-rw-r--r--src/exchangedb/exchange_do_age_withdraw.sql34
-rw-r--r--src/exchangedb/pg_do_age_withdraw.c12
-rw-r--r--src/exchangedb/pg_do_age_withdraw.h2
-rw-r--r--src/include/taler_exchange_service.h2
-rw-r--r--src/include/taler_exchangedb_plugin.h2
-rw-r--r--src/include/taler_testing_lib.h36
-rw-r--r--src/lib/exchange_api_age_withdraw.c49
-rw-r--r--src/testing/test_exchange_api_age_restriction.c12
-rw-r--r--src/testing/testing_api_cmd_age_withdraw.c23
-rw-r--r--src/util/age_restriction.c8
11 files changed, 222 insertions, 136 deletions
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw.c b/src/exchange/taler-exchange-httpd_age-withdraw.c
index 106feb01a..60bc5fec2 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw.c
@@ -113,7 +113,7 @@ free_age_withdraw_context_resources (struct AgeWithdrawContext *awc)
static enum GNUNET_GenericReturnValue
parse_age_withdraw_json (
struct MHD_Connection *connection,
- const json_t *j_denoms_h,
+ const json_t *j_denom_hs,
const json_t *j_blinded_coin_evs,
struct AgeWithdrawContext *awc,
MHD_RESULT *mhd_ret)
@@ -135,9 +135,9 @@ parse_age_withdraw_json (
/* Verify JSON-structure consistency */
{
- uint32_t num_coins = json_array_size (j_denoms_h);
+ uint32_t num_coins = json_array_size (j_denom_hs);
- if (! json_is_array (j_denoms_h))
+ if (! json_is_array (j_denom_hs))
error = "denoms_h must be an array";
else if (! json_is_array (j_blinded_coin_evs))
error = "coin_evs must be an array";
@@ -168,7 +168,7 @@ parse_age_withdraw_json (
awc->denom_hs = GNUNET_new_array (awc->num_coins,
struct TALER_DenominationHashP);
- json_array_foreach (j_denoms_h, idx, value) {
+ json_array_foreach (j_denom_hs, idx, value) {
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto (NULL, &awc->denom_hs[idx]),
GNUNET_JSON_spec_end ()
@@ -196,24 +196,17 @@ parse_age_withdraw_json (
/* Parse blinded envelopes. */
json_array_foreach (j_blinded_coin_evs, idx, value) {
- const json_t *j_kappa_coin_evs;
- struct GNUNET_JSON_Specification aspec[] = {
- GNUNET_JSON_spec_array_const (NULL, &j_kappa_coin_evs),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value, aspec, NULL, NULL))
+ const json_t *j_kappa_coin_evs = value;
+ if (! json_is_array (j_kappa_coin_evs))
{
GNUNET_snprintf (buf,
sizeof(buf),
- "couldn't parse entry no. %d in array coin_evs",
+ "enxtry %d in array blinded_coin_evs is not an array",
idx + 1);
error = buf;
goto EXIT;
}
-
- if (TALER_CNC_KAPPA != json_array_size (j_kappa_coin_evs))
+ else if (TALER_CNC_KAPPA != json_array_size (j_kappa_coin_evs))
{
GNUNET_snprintf (buf,
sizeof(buf),
@@ -223,28 +216,47 @@ parse_age_withdraw_json (
goto EXIT;
}
- /* Now parse the individual kappa envelopes */
+ /* Now parse the individual kappa envelopes and calculate the hash of
+ * the commitment along the way. */
{
size_t off = idx * TALER_CNC_KAPPA;
- size_t kappa = 0;
+ unsigned int kappa = 0;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_HashContext *hash_context;
+
+ hash_context = GNUNET_CRYPTO_hash_context_start ();
json_array_foreach (j_kappa_coin_evs, kappa, value) {
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &awc->coin_evs[off + kappa]),
+ TALER_JSON_spec_blinded_planchet (NULL, &awc->coin_evs[off + kappa]),
GNUNET_JSON_spec_end ()
};
-
if (GNUNET_OK !=
GNUNET_JSON_parse (value, spec, NULL, NULL))
{
GNUNET_snprintf (buf,
sizeof(buf),
- "couldn't parse array no. %d in coin_evs",
+ "couldn't parse array no. %d in blinded_coin_evs[%d]",
+ kappa + 1,
idx + 1);
error = buf;
goto EXIT;
}
+ /* Continue to hash of the coin candidates */
+ {
+ struct TALER_BlindedCoinHashP bch;
+ ret = TALER_coin_ev_hash (&awc->coin_evs[off + kappa],
+ &awc->denom_hs[idx],
+ &bch);
+
+ GNUNET_assert (GNUNET_OK == ret);
+
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &bch,
+ sizeof(bch));
+ }
+
/* Check for duplicate planchets
* FIXME: is this needed?
*/
@@ -258,39 +270,15 @@ parse_age_withdraw_json (
}
}
}
+
+ /* Finally, calculate the h_commitment from all blinded envelopes */
+ GNUNET_CRYPTO_hash_context_finish (hash_context,
+ &awc->commitment.h_commitment.hash);
}
}; /* json_array_foreach over j_blinded_coin_evs */
- /* We successfully parsed denoms_h and blinded_coins_evs */
GNUNET_assert (NULL == error);
- /* Finally, calculate the h_commitment from all blinded envelopes */
- {
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_HashContext *hash_context;
-
- hash_context = GNUNET_CRYPTO_hash_context_start ();
-
- for (size_t c = 0;
- c < TALER_CNC_KAPPA * awc->num_coins;
- c++)
- {
- struct TALER_BlindedCoinHashP bch;
-
- ret = TALER_coin_ev_hash (&awc->coin_evs[c],
- &awc->denom_hs[c],
- &bch);
-
- GNUNET_assert (GNUNET_OK == ret);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &bch,
- sizeof(bch));
- }
-
- GNUNET_CRYPTO_hash_context_finish (hash_context,
- &awc->commitment.h_commitment.hash);
- }
-
EXIT:
if (NULL != error)
@@ -513,7 +501,6 @@ verify_reserve_signature (
const struct TALER_EXCHANGEDB_AgeWithdraw *commitment,
enum MHD_Result *mhd_ret)
{
-
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
TALER_wallet_age_withdraw_verify (&commitment->h_commitment,
@@ -741,6 +728,7 @@ age_withdraw_transaction (void *cls,
bool age_ok = false;
bool conflict = false;
uint16_t allowed_maximum_age = 0;
+ uint32_t reserve_birthday = 0;
qs = TEH_plugin->do_age_withdraw (TEH_plugin->cls,
&awc->commitment,
@@ -749,7 +737,29 @@ age_withdraw_transaction (void *cls,
&balance_ok,
&age_ok,
&allowed_maximum_age,
+ &reserve_birthday,
&conflict);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "XXXXXXX got from do_age_withdraw:"
+ "\n\tqs: %d"
+ "\n\tcommitment: %s"
+ "\n\tmax_age: %d"
+ "\n\tfound: %d"
+ "\n\tbalance_ok: %d"
+ "\n\tage_ok: %d"
+ "\n\tallowed_maximum_age: %d"
+ "\n\treserve_birthday: %d"
+ "\n\tconflict: %d\n",
+ qs,
+ GNUNET_h2s (&awc->commitment.h_commitment.hash),
+ awc->commitment.max_age,
+ found,
+ balance_ok,
+ age_ok,
+ allowed_maximum_age,
+ reserve_birthday,
+ conflict);
+
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
@@ -765,18 +775,6 @@ age_withdraw_transaction (void *cls,
NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- else if (! balance_ok)
- {
- TEH_plugin->rollback (TEH_plugin->cls);
-
- *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
- connection,
- TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS,
- &awc->commitment.amount_with_fee,
- &awc->commitment.reserve_pub);
-
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
else if (! age_ok)
{
enum TALER_ErrorCode ec =
@@ -785,10 +783,24 @@ age_withdraw_transaction (void *cls,
*mhd_ret =
TALER_MHD_REPLY_JSON_PACK (
connection,
- TALER_ErrorCode_get_http_status_safe (ec),
+ MHD_HTTP_CONFLICT,
TALER_MHD_PACK_EC (ec),
GNUNET_JSON_pack_uint64 ("allowed_maximum_age",
- allowed_maximum_age));
+ allowed_maximum_age),
+ GNUNET_JSON_pack_uint64 ("reserve_birthday",
+ reserve_birthday));
+
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ else if (! balance_ok)
+ {
+ TEH_plugin->rollback (TEH_plugin->cls);
+
+ *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
+ connection,
+ TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS,
+ &awc->commitment.amount_with_fee,
+ &awc->commitment.reserve_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
}
@@ -802,6 +814,7 @@ age_withdraw_transaction (void *cls,
GNUNET_assert (ok);
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
+ *mhd_ret = -1;
}
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
@@ -842,10 +855,11 @@ sign_and_do_age_withdraw (
awc->now = GNUNET_TIME_timestamp_get ();
/* Pick the challenge */
- awc->commitment.noreveal_index =
- noreveal_index =
- GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
- TALER_CNC_KAPPA);
+ noreveal_index =
+ GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+ TALER_CNC_KAPPA);
+
+ awc->commitment.noreveal_index = noreveal_index;
/* Choose and sign the coins */
{
@@ -893,21 +907,11 @@ sign_and_do_age_withdraw (
result,
&age_withdraw_transaction,
awc);
-
- if (GNUNET_OK != ret)
- {
- GNUNET_break (0);
- *result = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR,
- NULL);
- }
-
/* Free resources */
- awc->commitment.h_coin_evs = NULL;
- awc->commitment.denom_sigs = NULL;
for (unsigned int i = 0; i<awc->num_coins; i++)
TALER_blinded_denom_sig_free (&denom_sigs[i]);
+ awc->commitment.h_coin_evs = NULL;
+ awc->commitment.denom_sigs = NULL;
return ret;
}
@@ -918,14 +922,14 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
const json_t *root)
{
MHD_RESULT mhd_ret;
- const json_t *j_denoms_h;
- const json_t *j_blinded_coins_evs;
+ const json_t *j_denom_hs;
+ const json_t *j_blinded_coin_evs;
struct AgeWithdrawContext awc = {0};
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("denoms_h",
- &j_denoms_h),
- GNUNET_JSON_spec_array_const ("blinded_coins_evs",
- &j_blinded_coins_evs),
+ GNUNET_JSON_spec_array_const ("denom_hs",
+ &j_denom_hs),
+ GNUNET_JSON_spec_array_const ("blinded_coin_evs",
+ &j_blinded_coin_evs),
GNUNET_JSON_spec_uint16 ("max_age",
&awc.commitment.max_age),
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
@@ -957,8 +961,8 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
/* Parse denoms_h and blinded_coins_evs, partially fill awc */
if (GNUNET_OK !=
parse_age_withdraw_json (rc->connection,
- j_denoms_h,
- j_blinded_coins_evs,
+ j_denom_hs,
+ j_blinded_coin_evs,
&awc,
&mhd_ret))
break;
diff --git a/src/exchangedb/exchange_do_age_withdraw.sql b/src/exchangedb/exchange_do_age_withdraw.sql
index d6ae118ae..2230d4bff 100644
--- a/src/exchangedb/exchange_do_age_withdraw.sql
+++ b/src/exchangedb/exchange_do_age_withdraw.sql
@@ -32,6 +32,7 @@ CREATE OR REPLACE FUNCTION exchange_do_age_withdraw(
OUT balance_ok BOOLEAN,
OUT age_ok BOOLEAN,
OUT required_age INT2, -- in years ϵ [0,1..)
+ OUT reserve_birthday INT4,
OUT conflict BOOLEAN)
LANGUAGE plpgsql
AS $$
@@ -39,7 +40,6 @@ DECLARE
reserve_gc INT8;
reserve_val INT8;
reserve_frac INT4;
- reserve_birthday INT4;
not_before date;
earliest_date date;
BEGIN
@@ -64,23 +64,20 @@ SELECT
IF NOT FOUND
THEN
- -- reserve unknown
reserve_found=FALSE;
- balance_ok=FALSE;
- age_ok=FALSE;
- required_age=0;
+ age_ok = FALSE;
+ required_age=-1;
conflict=FALSE;
+ balance_ok=FALSE;
RETURN;
END IF;
+reserve_found = TRUE;
+conflict=FALSE; -- not really yet determined
-- Check age requirements
-IF ((maximum_age_committed = 0) OR (reserve_birthday = 0))
+IF (reserve_birthday <> 0)
THEN
- -- No commitment to a non-zero age was provided or the reserve is marked as
- -- having no age restriction. We can simply pass.
- age_ok = OK;
-ELSE
not_before=date '1970-01-01' + reserve_birthday;
earliest_date = current_date - make_interval(maximum_age_committed);
--
@@ -95,14 +92,18 @@ ELSE
--
IF (earliest_date < not_before)
THEN
- reserve_found = TRUE;
- balance_ok = FALSE;
+ required_age = extract(year from age(current_date, not_before));
age_ok = FALSE;
- required_age = extract(year from age(not_before, current_date)) + 1;
+ balance_ok=TRUE; -- NOT REALLY
RETURN;
END IF;
END IF;
+age_ok = TRUE;
+required_age=0;
+
+
+
-- Check reserve balance is sufficient.
IF (reserve_val > amount_val)
THEN
@@ -125,6 +126,8 @@ ELSE
END IF;
END IF;
+balance_ok=TRUE;
+
-- Calculate new expiration dates.
min_reserve_gc=GREATEST(min_reserve_gc,reserve_gc);
@@ -136,9 +139,6 @@ UPDATE reserves SET
WHERE
reserves.reserve_pub=rpub;
-reserve_found=TRUE;
-balance_ok=TRUE;
-
-- Write the commitment into the age-withdraw table
INSERT INTO exchange.age_withdraw
(h_commitment
@@ -146,7 +146,7 @@ INSERT INTO exchange.age_withdraw
,reserve_pub
,reserve_sig
,noreveal_index
- ,denomination_serials
+ ,denom_serials
,h_blind_evs
,denom_sigs)
VALUES
diff --git a/src/exchangedb/pg_do_age_withdraw.c b/src/exchangedb/pg_do_age_withdraw.c
index 8a93ef8df..c79b2b3de 100644
--- a/src/exchangedb/pg_do_age_withdraw.c
+++ b/src/exchangedb/pg_do_age_withdraw.c
@@ -38,8 +38,8 @@ TEH_PG_do_age_withdraw (
bool *balance_ok,
bool *age_ok,
uint16_t *required_age,
- bool *conflict,
- uint64_t *ruuid)
+ uint32_t *reserve_birthday,
+ bool *conflict)
{
struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Timestamp gc;
@@ -72,10 +72,10 @@ TEH_PG_do_age_withdraw (
age_ok),
GNUNET_PQ_result_spec_uint16 ("required_age",
required_age),
+ GNUNET_PQ_result_spec_uint32 ("reserve_birthday",
+ reserve_birthday),
GNUNET_PQ_result_spec_bool ("conflict",
conflict),
- GNUNET_PQ_result_spec_uint64 ("ruuid",
- ruuid),
GNUNET_PQ_result_spec_end
};
@@ -93,9 +93,9 @@ TEH_PG_do_age_withdraw (
",balance_ok"
",age_ok"
",required_age"
+ ",reserve_birthday"
",conflict"
- ",ruuid"
- " FROM exchange_do_batch_withdraw"
+ " FROM exchange_do_age_withdraw"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_age_withdraw",
diff --git a/src/exchangedb/pg_do_age_withdraw.h b/src/exchangedb/pg_do_age_withdraw.h
index 8f42bfb57..71376022d 100644
--- a/src/exchangedb/pg_do_age_withdraw.h
+++ b/src/exchangedb/pg_do_age_withdraw.h
@@ -36,6 +36,7 @@
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] age_ok set to true if no age requirements are present on the reserve
* @param[out] required_age if @e age_ok is false, set to the maximum allowed age when withdrawing from this reserve
+ * @param[out] reserve_birthday if @e age_ok is false, set to the birthday of the reserve
* @param[out] conflict set to true if there already is an entry in the database for the given pair (h_commitment, reserve_pub)
* @return query execution status
*/
@@ -48,6 +49,7 @@ TEH_PG_do_age_withdraw (
bool *balance_ok,
bool *age_ok,
uint16_t *required_age,
+ uint32_t *reserve_birthday,
bool *conflict);
#endif
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index 7bd1b3248..e8d789162 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -2966,6 +2966,7 @@ struct TALER_EXCHANGE_AgeWithdrawBlindedHandle;
* @param curl_ctx The curl context to use
* @param exchange_url The base-URL of the exchange
* @param keys The /keys material from the exchange
+ * @param max_age The maximum age that the coins are committed to.
* @param num_input number of entries in the @a blinded_input array
* @param blinded_input array of planchet details of the planchet to withdraw
* @param reserve_priv private key of the reserve to withdraw from
@@ -2981,6 +2982,7 @@ TALER_EXCHANGE_age_withdraw_blinded (
struct TALER_EXCHANGE_Keys *keys,
const char *exchange_url,
const struct TALER_ReservePrivateKeyP *reserve_priv,
+ uint8_t max_age,
unsigned int num_input,
const struct TALER_EXCHANGE_AgeWithdrawBlindedInput blinded_input[static
num_input],
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index c4b894e20..f5fdd7f11 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -3863,6 +3863,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] age_ok set to true if age requirements were met
* @param[out] allowed_maximum_age if @e age_ok is FALSE, this is set to the allowed maximum age
+ * @param[out] reserve_birthday if @e age_ok is FALSE, this is set to the reserve's birthday
* @return query execution status
*/
enum GNUNET_DB_QueryStatus
@@ -3874,6 +3875,7 @@ struct TALER_EXCHANGEDB_Plugin
bool *balance_ok,
bool *age_ok,
uint16_t *allowed_maximum_age,
+ uint32_t *reserve_birthday,
bool *conflict);
/**
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index c28937695..a514ad2dd 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -1130,6 +1130,42 @@ TALER_TESTING_cmd_batch_withdraw (const char *label,
const char *amount,
...);
+/**
+ * Create an age-withdraw command, letting the caller specify
+ * the maximum agend and desired amounts as string. Takes a variable,
+ * non-empty list of the denomination amounts via VARARGS, similar to
+ * #TALER_TESTING_cmd_withdraw_amount(), just using a batch withdraw.
+ *
+ * @param label command label.
+ * @param reserve_reference command providing us with a reserve to withdraw from
+ * @param max_age maximum allowed age, same for each coin
+ * @param expected_response_code which HTTP response code
+ * we expect from the exchange.
+ * @param amount how much we withdraw for the first coin
+ * @param ... NULL-terminated list of additional amounts to withdraw (one per coin)
+ * @return the withdraw command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_age_withdraw (const char *label,
+ const char *reserve_reference,
+ uint8_t max_age,
+ unsigned int expected_response_code,
+ const char *amount,
+ ...);
+
+/**
+ * Create a "age-withdraw reveal" command.
+ *
+ * @param label command label.
+ * @param age_withdraw_reference reference to a "age-withdraw" command.
+ * @param expected_response_code expected HTTP response code.
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_age_withdraw_reveal (
+ const char *label,
+ const char *age_withdraw_reference,
+ unsigned int expected_response_code);
/**
* Create a withdraw command, letting the caller specify
diff --git a/src/lib/exchange_api_age_withdraw.c b/src/lib/exchange_api_age_withdraw.c
index c68fe67d2..bd84dcb32 100644
--- a/src/lib/exchange_api_age_withdraw.c
+++ b/src/lib/exchange_api_age_withdraw.c
@@ -29,12 +29,14 @@
#include <gnunet/gnunet_curl_lib.h>
#include <sys/wait.h>
#include "taler_curl_lib.h"
+#include "taler_error_codes.h"
#include "taler_json_lib.h"
#include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "exchange_api_curl_defaults.h"
+#include "taler_util.h"
/**
* A CoinCandidate is populated from a master secret
@@ -315,12 +317,13 @@ reserve_age_withdraw_ok (
};
struct TALER_ExchangeSignatureP exchange_sig;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_uint8 ("noreaveal_index",
+ GNUNET_JSON_spec_uint8 ("noreveal_index",
&response.details.ok.noreveal_index),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
- &response.details.ok.exchange_pub)
+ &response.details.ok.exchange_pub),
+ GNUNET_JSON_spec_end ()
};
if (GNUNET_OK!=
@@ -538,6 +541,14 @@ handle_reserve_age_withdraw_blinded_finished (
awbr.hr.hint = TALER_JSON_get_error_hint (j_response);
break;
case MHD_HTTP_CONFLICT:
+ /* The age requirements might not have been met */
+ awbr.hr.ec = TALER_JSON_get_error_code (j_response);
+ if (TALER_EC_EXCHANGE_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE == awbr.hr.ec)
+ {
+ awbr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
+ }
+
/* The exchange says that the reserve has insufficient funds;
check the signatures in the history... */
if (GNUNET_OK !=
@@ -611,6 +622,9 @@ perform_protocol (
json_t *j_request_body = NULL;
CURL *curlh = NULL;
+ GNUNET_assert (0 < awbh->num_input);
+ awbh->age_mask = awbh->blinded_input[0].denom_pub->key.age_mask;
+
FAIL_IF (GNUNET_OK !=
TALER_amount_set_zero (awbh->keys->currency,
&awbh->amount_with_fee));
@@ -649,12 +663,17 @@ perform_protocol (
{
/* Build the denomination array */
{
- const struct TALER_EXCHANGE_DenomPublicKey *denom =
+ const struct TALER_EXCHANGE_DenomPublicKey *denom_pub =
awbh->blinded_input[i].denom_pub;
- json_t *jdenom = GNUNET_JSON_PACK (
- TALER_JSON_pack_denom_pub (NULL,
- &denom->key));
+ const struct TALER_DenominationHashP *denom_h = &denom_pub->h_key;
+ json_t *jdenom;
+
+ /* The mask must be the same for all coins */
+ FAIL_IF (awbh->age_mask.bits != denom_pub->key.age_mask.bits);
+ jdenom = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto (NULL,
+ denom_h));
FAIL_IF (NULL == jdenom);
FAIL_IF (0 < json_array_append_new (j_denoms,
jdenom));
@@ -686,6 +705,9 @@ perform_protocol (
&bch,
sizeof(bch));
}
+
+ FAIL_IF (0 < json_array_append_new (j_array_candidates,
+ j_can));
}
}
}
@@ -702,9 +724,17 @@ perform_protocol (
awbh->reserve_priv,
&awbh->reserve_sig);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_wallet_age_withdraw_verify (&awbh->h_commitment,
+ &awbh->amount_with_fee,
+ &awbh->age_mask,
+ awbh->max_age,
+ &awbh->reserve_pub,
+ &awbh->reserve_sig));
+
/* Initiate the POST-request */
j_request_body = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_array_steal ("denoms_h", j_denoms),
+ GNUNET_JSON_pack_array_steal ("denom_hs", j_denoms),
GNUNET_JSON_pack_array_steal ("blinded_coin_evs", j_array_candidates),
GNUNET_JSON_pack_uint64 ("max_age", awbh->max_age),
GNUNET_JSON_pack_data_auto ("reserve_sig", &awbh->reserve_sig));
@@ -813,6 +843,7 @@ call_age_withdraw_blinded (
awh->keys,
awh->exchange_url,
awh->reserve_priv,
+ awh->max_age,
awh->num_coins,
blinded_input,
copy_results,
@@ -1064,7 +1095,7 @@ prepare_coins (
&cd->denom_pub,
&planchet->blinded_planchet.details.cs_blinded_planchet.nonce,
&csr_withdraw_done,
- &cls);
+ cls);
FAIL_IF (NULL == cls->csr_withdraw_handle);
awh->csr.pending++;
@@ -1163,6 +1194,7 @@ TALER_EXCHANGE_age_withdraw_blinded (
struct TALER_EXCHANGE_Keys *keys,
const char *exchange_url,
const struct TALER_ReservePrivateKeyP *reserve_priv,
+ uint8_t max_age,
unsigned int num_input,
const struct TALER_EXCHANGE_AgeWithdrawBlindedInput blinded_input[static
num_input],
@@ -1179,6 +1211,7 @@ TALER_EXCHANGE_age_withdraw_blinded (
awbh->reserve_priv = reserve_priv;
awbh->callback = res_cb;
awbh->callback_cls = res_cb_cls;
+ awbh->max_age = max_age;
GNUNET_CRYPTO_eddsa_key_get_public (&awbh->reserve_priv->eddsa_priv,
&awbh->reserve_pub.eddsa_pub);
diff --git a/src/testing/test_exchange_api_age_restriction.c b/src/testing/test_exchange_api_age_restriction.c
index 93bd28bf9..b3f7357f7 100644
--- a/src/testing/test_exchange_api_age_restriction.c
+++ b/src/testing/test_exchange_api_age_restriction.c
@@ -284,6 +284,18 @@ run (void *cls,
"EUR:10",
0, /* age restriction off */
MHD_HTTP_CONFLICT),
+ TALER_TESTING_cmd_age_withdraw ("age-withdraw-coin-1-too-low",
+ "create-reserve-kyc-1",
+ 18, /* Too high */
+ MHD_HTTP_CONFLICT,
+ "EUR:10",
+ NULL),
+ TALER_TESTING_cmd_age_withdraw ("age-withdraw-coin-1",
+ "create-reserve-kyc-1",
+ 8,
+ MHD_HTTP_OK,
+ "EUR:10",
+ NULL),
TALER_TESTING_cmd_end (),
};
diff --git a/src/testing/testing_api_cmd_age_withdraw.c b/src/testing/testing_api_cmd_age_withdraw.c
index ea3249c09..9b7bfd209 100644
--- a/src/testing/testing_api_cmd_age_withdraw.c
+++ b/src/testing/testing_api_cmd_age_withdraw.c
@@ -209,21 +209,15 @@ age_withdraw_cb (
break;
case MHD_HTTP_CONFLICT:
/* TODO[oec]: Add this to the response-type and handle it here */
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Age withdraw test command does not YET support status code %u\n",
- response->hr.http_status);
break;
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
- /* TODO[oec]: Add this to response-type and handle it here */
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Age withdraw test command does not YET support status code %u\n",
- response->hr.http_status);
- break;
default:
/* Unsupported status code (by test harness) */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Age withdraw test command does not support status code %u\n",
- response->hr.http_status);
+ "test command for age-withdraw not support status code %u, body:\n"
+ ">>%s<<\n",
+ response->hr.http_status,
+ json_dumps (response->hr.reply, JSON_INDENT (2)));
GNUNET_break (0);
break;
}
@@ -366,12 +360,13 @@ age_withdraw_cleanup (
struct TALER_EXCHANGE_AgeWithdrawCoinInput *in = &aws->coin_inputs[n];
struct CoinOutputState *out = &aws->coin_outputs[n];
- if (NULL != in->denom_pub)
+ if (NULL != in && NULL != in->denom_pub)
{
TALER_EXCHANGE_destroy_denomination_key (in->denom_pub);
in->denom_pub = NULL;
}
- TALER_age_commitment_proof_free (&out->details.age_commitment_proof);
+ if (NULL != out)
+ TALER_age_commitment_proof_free (&out->details.age_commitment_proof);
}
GNUNET_free (aws->coin_inputs);
GNUNET_free (aws->coin_outputs);
@@ -490,10 +485,10 @@ TALER_TESTING_cmd_age_withdraw (const char *label,
label);
GNUNET_assert (0);
}
+ /* move on to next vararg! */
+ amount = va_arg (ap, const char *);
}
- /* move on to next vararg! */
- amount = va_arg (ap, const char *);
GNUNET_assert (NULL == amount);
va_end (ap);
diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c
index eec0c834c..d8c6e4da4 100644
--- a/src/util/age_restriction.c
+++ b/src/util/age_restriction.c
@@ -32,10 +32,10 @@ GNUNET_CRYPTO_Edx25519PublicKey
GNUNET_CRYPTO_EcdsaPublicKey
#endif
TALER_age_commitment_base_public_key = {
- .q_y = { 0x6f, 0xe5, 0x87, 0x9a, 0x3d, 0xa9, 0x44, 0x20,
- 0x80, 0xbd, 0x6a, 0xb9, 0x44, 0x56, 0x91, 0x19,
- 0xaf, 0xb4, 0xc8, 0x7b, 0x89, 0xce, 0x23, 0x17,
- 0x97, 0x20, 0x5c, 0xbb, 0x9c, 0xd7, 0xcc, 0xd9},
+ .q_y = { 0x64, 0x41, 0xb9, 0xbd, 0xbf, 0x14, 0x39, 0x8e,
+ 0x46, 0xeb, 0x5c, 0x1d, 0x34, 0xd3, 0x9b, 0x2f,
+ 0x9b, 0x7d, 0xc8, 0x18, 0xeb, 0x9c, 0x09, 0xfb,
+ 0x43, 0xad, 0x16, 0x64, 0xbc, 0x18, 0x49, 0xb5},
};
void