From 3e29bdfb8bfda133b7c25a36160a3533e836e0b8 Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Mon, 24 Jul 2023 20:23:42 +0200 Subject: [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 --- src/exchange/taler-exchange-httpd_age-withdraw.c | 178 ++++++++++++----------- src/exchangedb/exchange_do_age_withdraw.sql | 34 ++--- src/exchangedb/pg_do_age_withdraw.c | 12 +- src/exchangedb/pg_do_age_withdraw.h | 2 + src/include/taler_exchange_service.h | 2 + src/include/taler_exchangedb_plugin.h | 2 + src/include/taler_testing_lib.h | 36 +++++ src/lib/exchange_api_age_withdraw.c | 49 ++++++- src/testing/test_exchange_api_age_restriction.c | 12 ++ src/testing/testing_api_cmd_age_withdraw.c | 23 ++- src/util/age_restriction.c | 8 +- 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; inum_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 #include #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 -- cgit v1.2.3