From 8bdf6ab19df70c16d335ecf82f2c3b2117eeb70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Wed, 16 Feb 2022 22:01:05 +0100 Subject: [age restriction] progress 14/n - withdraw and deposit Age restriction support for - withdraw is done and tested - deposit is done and tested TODOs: - melt/refresh/reveal - link ------ Added functions - TALER_age_restriction_commit - TALER_age_commitment_derive - TALER_age_commitment_hash - TALER_age_restriction_commitment_free_inside - Hash of age commitment passed around API boundaries Exchangedb adjustments for denominations - all prepared statements re: denominations now handle age_mask - signature parameters adjusted Hash and signature verification of /keys adjusted - Hashes of (normal) denominations and age-restricted denominations are calculated seperately - The hash of the age-restricted ones will then be added to the other hash - The total hash is signed/verified Tests for withdraw with age restriction added - TALER_EXCHANGE_DenomPublickey now carries age_mask - TALER_TESTING_cmd_withdraw_amount* takes age parameter - TALER_TESTING_find_pk takes boolean age_restricted - WithdrawState carries age_commitment and its hash - withdraw_run derives new age commitment, if applicable - Added age parameter to testing (13 as example) Various Fixes and changes - Fixes of post handler for /management/extensions - Fixes for offline tool extensions signing - Slight refactoring of extensions - Age restriction extension simplified - config is now global to extension - added global TEH_age_restriction_enabled and TEH_age_mask in taler-exchange-httpd - helper functions and macros introduced --- src/lib/exchange_api_common.c | 34 +++++++++---- src/lib/exchange_api_deposit.c | 24 ++++++++-- src/lib/exchange_api_handle.c | 58 ++++++++++++++--------- src/lib/exchange_api_link.c | 12 +++++ src/lib/exchange_api_management_get_keys.c | 2 +- src/lib/exchange_api_management_post_extensions.c | 4 +- src/lib/exchange_api_refresh_common.c | 1 + src/lib/exchange_api_refresh_common.h | 13 +++++ src/lib/exchange_api_refreshes_reveal.c | 11 ++++- src/lib/exchange_api_refund.c | 5 ++ src/lib/exchange_api_withdraw.c | 10 ++++ 11 files changed, 134 insertions(+), 40 deletions(-) (limited to 'src/lib') diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index 53a75a934..d03409244 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -477,6 +477,7 @@ TALER_EXCHANGE_verify_coin_history ( struct TALER_MerchantPublicKeyP merchant_pub; struct GNUNET_TIME_Timestamp refund_deadline = {0}; struct TALER_CoinSpendSignatureP sig; + struct TALER_AgeCommitmentHash *hac = NULL; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_sig", &sig), @@ -511,6 +512,7 @@ TALER_EXCHANGE_verify_coin_history ( &fee, &h_wire, &h_contract_terms, + hac, NULL /* h_extensions! */, h_denom_pub, wallet_timestamp, @@ -543,6 +545,7 @@ TALER_EXCHANGE_verify_coin_history ( { struct TALER_CoinSpendSignatureP sig; struct TALER_RefreshCommitmentP rc; + struct TALER_AgeCommitmentHash h_age_commitment = {0}; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_sig", &sig), @@ -550,6 +553,9 @@ TALER_EXCHANGE_verify_coin_history ( &rc), GNUNET_JSON_spec_fixed_auto ("h_denom_pub", h_denom_pub), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("h_age_commitment", + &h_age_commitment)), TALER_JSON_spec_amount_any ("melt_fee", &fee), GNUNET_JSON_spec_end () @@ -563,6 +569,7 @@ TALER_EXCHANGE_verify_coin_history ( GNUNET_break_op (0); return GNUNET_SYSERR; } + if (NULL != dk) { /* check that melt fee matches our expectations from /keys! */ @@ -577,16 +584,25 @@ TALER_EXCHANGE_verify_coin_history ( return GNUNET_SYSERR; } } - if (GNUNET_OK != - TALER_wallet_melt_verify (&amount, - &fee, - &rc, - h_denom_pub, - coin_pub, - &sig)) + { - GNUNET_break_op (0); - return GNUNET_SYSERR; + const struct TALER_AgeCommitmentHash *ahc = &h_age_commitment; + + if (TALER_AgeCommitmentHash_isNullOrZero (ahc)) + ahc = NULL; + + if (GNUNET_OK != + TALER_wallet_melt_verify (&amount, + &fee, + &rc, + h_denom_pub, + ahc, + coin_pub, + &sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } } add = GNUNET_YES; } diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c index 7ff596518..2bfaaf6ce 100644 --- a/src/lib/exchange_api_deposit.c +++ b/src/lib/exchange_api_deposit.c @@ -463,6 +463,7 @@ handle_deposit_finished (void *cls, * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange) * @param ech hash over contract extensions * @param coin_pub coin’s public key + * @param h_age_commitment coin’s hash of age commitment, might be NULL * @param denom_sig exchange’s unblinded signature of the coin * @param denom_pub denomination key with which the coin is signed * @param denom_pub_hash hash of @a denom_pub @@ -479,6 +480,7 @@ verify_signatures (const struct TALER_EXCHANGE_DenomPublicKey *dki, const struct TALER_PrivateContractHash *h_contract_terms, const struct TALER_ExtensionContractHash *ech, const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_AgeCommitmentHash *h_age_commitment, const struct TALER_DenominationSignature *denom_sig, const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_DenominationHash *denom_pub_hash, @@ -492,6 +494,7 @@ verify_signatures (const struct TALER_EXCHANGE_DenomPublicKey *dki, &dki->fee_deposit, h_wire, h_contract_terms, + h_age_commitment, ech, denom_pub_hash, timestamp, @@ -515,8 +518,12 @@ verify_signatures (const struct TALER_EXCHANGE_DenomPublicKey *dki, .coin_pub = *coin_pub, .denom_pub_hash = *denom_pub_hash, .denom_sig = *denom_sig, - .age_commitment_hash = {{{0}}} /* FIXME-Oec */ + .age_commitment_hash = {{{0}}} }; + if (NULL != h_age_commitment) + { + coin_info.age_commitment_hash = *h_age_commitment; + } if (GNUNET_YES != TALER_test_coin_valid (&coin_info, @@ -548,6 +555,7 @@ TALER_EXCHANGE_deposit ( const char *merchant_payto_uri, const struct TALER_WireSaltP *wire_salt, const struct TALER_PrivateContractHash *h_contract_terms, + const struct TALER_AgeCommitmentHash *h_age_commitment, const json_t *extension_details, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_DenominationSignature *denom_sig, @@ -600,11 +608,14 @@ TALER_EXCHANGE_deposit ( } GNUNET_assert (GNUNET_YES == TEAH_handle_is_ready (exchange)); + /* initialize h_wire */ TALER_merchant_wire_signature_hash (merchant_payto_uri, wire_salt, &h_wire); + key_state = TALER_EXCHANGE_get_keys (exchange); + dki = TALER_EXCHANGE_get_denomination_key (key_state, denom_pub); if (NULL == dki) @@ -613,6 +624,7 @@ TALER_EXCHANGE_deposit ( GNUNET_break_op (0); return NULL; } + if (0 > TALER_amount_subtract (&amount_without_fee, amount, @@ -622,17 +634,18 @@ TALER_EXCHANGE_deposit ( GNUNET_break_op (0); return NULL; } + TALER_denom_pub_hash (denom_pub, &denom_pub_hash); + if (GNUNET_OK != verify_signatures (dki, amount, &h_wire, h_contract_terms, - (NULL != extension_details) - ? &ech - : NULL, + (NULL != extension_details) ? &ech : NULL, coin_pub, + h_age_commitment, denom_sig, denom_pub, &denom_pub_hash, @@ -655,6 +668,9 @@ TALER_EXCHANGE_deposit ( wire_salt), GNUNET_JSON_pack_data_auto ("h_contract_terms", h_contract_terms), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_data_auto ("h_age_commitment", + h_age_commitment)), GNUNET_JSON_pack_data_auto ("denom_pub_hash", &denom_pub_hash), TALER_JSON_pack_denom_sig ("ub_sig", diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index cf3d69d6a..3243f5e95 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -667,7 +667,9 @@ decode_keys_json (const json_t *resp_obj, enum TALER_EXCHANGE_VersionCompatibility *vc) { struct TALER_ExchangeSignatureP sig; - struct GNUNET_HashContext *hash_context; + struct GNUNET_HashContext *hash_context = NULL; + struct GNUNET_HashContext *hash_context_restricted = NULL; + bool have_age_restricted_denom = false; struct TALER_ExchangePublicKeyP pub; const char *currency; struct GNUNET_JSON_Specification mspec[] = { @@ -746,7 +748,6 @@ decode_keys_json (const json_t *resp_obj, key_data->version = GNUNET_strdup (ver); } - hash_context = NULL; EXITIF (GNUNET_OK != GNUNET_JSON_parse (resp_obj, (check_sig) ? mspec : &mspec[2], @@ -766,7 +767,10 @@ decode_keys_json (const json_t *resp_obj, /* parse the master public key and issue date of the response */ if (check_sig) + { hash_context = GNUNET_CRYPTO_hash_context_start (); + hash_context_restricted = GNUNET_CRYPTO_hash_context_start (); + } /* parse the signing keys */ { @@ -829,6 +833,9 @@ decode_keys_json (const json_t *resp_obj, EXITIF (GNUNET_OK != TALER_extensions_load_json_config (extensions)); } + + /* 4. assuming we might have now a new value for age_mask, set it in key_data */ + key_data->age_mask = TALER_extensions_age_restriction_ageMask (); } /* parse the denomination keys, merging with the @@ -839,9 +846,15 @@ decode_keys_json (const json_t *resp_obj, */ struct { char *name; - bool is_optional_age_restriction;} hive[2] = { - { "denoms", false }, - { "age_restricted_denoms", true }, + struct GNUNET_HashContext *hc; + bool is_optional_age_restriction;} + hive[2] = { + { "denoms", + hash_context, + false }, + { "age_restricted_denoms", + hash_context_restricted, + true } }; for (size_t s = 0; s < sizeof(hive) / sizeof(hive[0]); s++) @@ -853,25 +866,19 @@ decode_keys_json (const json_t *resp_obj, denom_keys_array = json_object_get (resp_obj, hive[s].name); - EXITIF (NULL == denom_keys_array && - ! hive[s].is_optional_age_restriction); - - if (NULL == denom_keys_array && - hive[s].is_optional_age_restriction) + if (NULL == denom_keys_array) continue; - /* if "age_restricted_denoms" exists, age-restriction better be enabled - * (that is: mask non-zero) */ - EXITIF (NULL != denom_keys_array && - hive[s].is_optional_age_restriction && - 0 == key_data->age_mask.mask); - EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); json_array_foreach (denom_keys_array, index, denom_key_obj) { struct TALER_EXCHANGE_DenomPublicKey dk; bool found = false; + /* mark that we have at least one age restricted denomination, needed + * for the hash calculation and signature verification below. */ + have_age_restricted_denom |= hive[s].is_optional_age_restriction; + memset (&dk, 0, sizeof (dk)); @@ -880,12 +887,7 @@ decode_keys_json (const json_t *resp_obj, check_sig, denom_key_obj, &key_data->master_pub, - hash_context)); - - /* Mark age restriction according where we got this denomination from, - * "denoms" or "age_restricted_denoms" */ - if (hive[s].is_optional_age_restriction) - dk.age_restricted = true; + hive[s].hc)); for (unsigned int j = 0; jnum_denom_keys; @@ -1044,6 +1046,18 @@ decode_keys_json (const json_t *resp_obj, .list_issue_date = GNUNET_TIME_timestamp_hton (key_data->list_issue_date) }; + /* If we had any age restricted denominations, add their hash to the end of + * the normal denominations. */ + if (have_age_restricted_denom) + { + struct GNUNET_HashCode hcr; + GNUNET_CRYPTO_hash_context_finish (hash_context_restricted, + &hcr); + GNUNET_CRYPTO_hash_context_read (hash_context, + &hcr, + sizeof(struct GNUNET_HashCode)); + } + GNUNET_CRYPTO_hash_context_finish (hash_context, &ks.hc); hash_context = NULL; diff --git a/src/lib/exchange_api_link.c b/src/lib/exchange_api_link.c index a44ccdcea..10ddd471d 100644 --- a/src/lib/exchange_api_link.c +++ b/src/lib/exchange_api_link.c @@ -113,6 +113,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, struct TALER_TransferSecretP secret; struct TALER_PlanchetDetail pd; struct TALER_CoinPubHash c_hash; + struct TALER_AgeCommitmentHash h_age_commitment = {0}; // TODO, see below. /* parse reply */ memset (&nonce, @@ -143,6 +144,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, &alg_values, &bks, &lci->coin_priv, + NULL, /* FIXME-oec. struct TALER_AgeCommitmentHash */ &c_hash, &pd)) { @@ -179,6 +181,15 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, GNUNET_CRYPTO_eddsa_key_get_public (&lh->coin_priv.eddsa_priv, &old_coin_pub.eddsa_pub); + /* + * TODO-oec: Derive the age commitment vector and hash it into + * h_age_commitment. + * Questions: + * - Where do we get the information about the support for age + * restriction of the denomination? + * - Where do we get the information bout the previous coin's age groups? + */ + TALER_coin_ev_hash (&pd.blinded_planchet, &pd.denom_pub_hash, &coin_envelope_hash); @@ -187,6 +198,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, trans_pub, &coin_envelope_hash, &old_coin_pub, + &h_age_commitment, &link_sig)) { GNUNET_break_op (0); diff --git a/src/lib/exchange_api_management_get_keys.c b/src/lib/exchange_api_management_get_keys.c index 4d6866338..ac419388f 100644 --- a/src/lib/exchange_api_management_get_keys.c +++ b/src/lib/exchange_api_management_get_keys.c @@ -32,7 +32,7 @@ /** * Set to 1 for extra debug logging. */ -#define DEBUG 0 +#define DEBUG 1 /* FIXME-oec */ /** diff --git a/src/lib/exchange_api_management_post_extensions.c b/src/lib/exchange_api_management_post_extensions.c index c0ab143f6..87b0e0be8 100644 --- a/src/lib/exchange_api_management_post_extensions.c +++ b/src/lib/exchange_api_management_post_extensions.c @@ -151,7 +151,7 @@ TALER_EXCHANGE_management_post_extensions ( body = GNUNET_JSON_PACK ( GNUNET_JSON_pack_object_steal ("extensions", ped->extensions), - GNUNET_JSON_pack_data_auto ("extensions_sigs", + GNUNET_JSON_pack_data_auto ("extensions_sig", &ped->extensions_sig)); eh = curl_easy_init (); @@ -168,7 +168,7 @@ TALER_EXCHANGE_management_post_extensions ( return NULL; } json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting URL '%s'\n", ph->url); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, diff --git a/src/lib/exchange_api_refresh_common.c b/src/lib/exchange_api_refresh_common.c index e944b79a1..89ee1e178 100644 --- a/src/lib/exchange_api_refresh_common.c +++ b/src/lib/exchange_api_refresh_common.c @@ -175,6 +175,7 @@ TALER_EXCHANGE_get_melt_data_ ( &alg_values[j], bks, coin_priv, + NULL, /* FIXME-oec: This needs to be setup !*/ &c_hash, &pd)) { diff --git a/src/lib/exchange_api_refresh_common.h b/src/lib/exchange_api_refresh_common.h index ab19ad7d1..b6926b51f 100644 --- a/src/lib/exchange_api_refresh_common.h +++ b/src/lib/exchange_api_refresh_common.h @@ -52,6 +52,12 @@ struct MeltedCoin */ struct TALER_Amount original_value; + /** + * The original age commitment hash. MUST be all zeroes, if no age + * commitment was set. + */ + struct TALER_AgeCommitmentHash h_age_commitment; + /** * Timestamp indicating when coins of this denomination become invalid. */ @@ -92,6 +98,13 @@ struct FreshCoinData */ struct TALER_CoinSpendPrivateKeyP coin_priv; + /** + * Arrays age commitments to be created, one for each cut-and-choose + * dimension. The entries in each list might be NULL and indicate no age + * commitment/restriction on the particular coin. + */ + struct TALER_AgeCommitment *age_commitment[TALER_CNC_KAPPA]; + /** * Blinding key secrets for the coins, depending on the * cut-and-choose. diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c index 08357c14e..8d04c279a 100644 --- a/src/lib/exchange_api_refreshes_reveal.c +++ b/src/lib/exchange_api_refreshes_reveal.c @@ -142,6 +142,7 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, &rcis[i]; const struct FreshCoinData *fcd = &rrh->md.fcds[i]; const struct TALER_DenominationPublicKey *pk; + struct TALER_AgeCommitmentHash *ach = NULL; json_t *jsonai; struct TALER_BlindedDenominationSignature blind_sig; struct TALER_CoinSpendPublicKeyP coin_pub; @@ -160,6 +161,12 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, jsonai = json_array_get (jsona, i); GNUNET_assert (NULL != jsonai); + if (! TALER_AgeCommitmentHash_isNullOrZero ( + &rrh->md.melted_coin.h_age_commitment)) + { + /* FIXME-oec: need to pull fresh_ach from somewhere */ + } + if (GNUNET_OK != GNUNET_JSON_parse (jsonai, spec, @@ -180,15 +187,15 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, hence recomputing it here... */ GNUNET_CRYPTO_eddsa_key_get_public (&rci->coin_priv.eddsa_priv, &coin_pub.eddsa_pub); - /* FIXME-Oec: Age commitment hash. */ TALER_coin_pub_hash (&coin_pub, - NULL, /* FIXME-Oec */ + ach, &coin_hash); if (GNUNET_OK != TALER_planchet_to_coin (pk, &blind_sig, &bks, &rci->coin_priv, + ach, &coin_hash, &rrh->alg_values[i], &coin)) diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c index 94909470a..a9510715b 100644 --- a/src/lib/exchange_api_refund.c +++ b/src/lib/exchange_api_refund.c @@ -203,6 +203,7 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh, struct TALER_Amount deposit_fee; struct TALER_MerchantWireHash h_wire; struct TALER_PrivateContractHash h_contract_terms; + struct TALER_AgeCommitmentHash h_age_commitment = {{{0}}}; // struct TALER_ExtensionContractHash h_extensions; // FIXME! struct TALER_DenominationHash h_denom_pub; struct GNUNET_TIME_Timestamp wallet_timestamp; @@ -218,6 +219,9 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh, &h_wire), GNUNET_JSON_spec_fixed_auto ("h_denom_pub", &h_denom_pub), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("h_age_commitment", + &h_age_commitment)), GNUNET_JSON_spec_timestamp ("timestamp", &wallet_timestamp), GNUNET_JSON_spec_timestamp ("refund_deadline", @@ -243,6 +247,7 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh, &deposit_fee, &h_wire, &h_contract_terms, + &h_age_commitment, NULL /* h_extensions! */, &h_denom_pub, wallet_timestamp, diff --git a/src/lib/exchange_api_withdraw.c b/src/lib/exchange_api_withdraw.c index c832699a2..efc8a99c2 100644 --- a/src/lib/exchange_api_withdraw.c +++ b/src/lib/exchange_api_withdraw.c @@ -88,6 +88,11 @@ struct TALER_EXCHANGE_WithdrawHandle */ struct TALER_ExchangeWithdrawValues alg_values; + /** + * Hash of the age commitment for this coin, if applicable. Maybe NULL + */ + const struct TALER_AgeCommitmentHash *ach; + /** * Denomination key we are withdrawing. */ @@ -137,6 +142,7 @@ handle_reserve_withdraw_finished ( blind_sig, &wh->bks, &wh->priv, + wh->ach, &wh->c_hash, &wh->alg_values, &fc)) @@ -222,6 +228,7 @@ withdraw_cs_stage_two_callback (void *cls, &wh->alg_values, &wh->bks, &wh->priv, + wh->ach, &wh->c_hash, &wh->pd)) { @@ -249,6 +256,7 @@ TALER_EXCHANGE_withdraw ( const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_ReservePrivateKeyP *reserve_priv, const struct TALER_PlanchetMasterSecretP *ps, + const struct TALER_AgeCommitmentHash *ach, TALER_EXCHANGE_WithdrawCallback res_cb, void *res_cb_cls) { @@ -260,6 +268,7 @@ TALER_EXCHANGE_withdraw ( wh->cb_cls = res_cb_cls; wh->reserve_priv = reserve_priv; wh->ps = *ps; + wh->ach = ach; wh->pk = *pk; TALER_denom_pub_deep_copy (&wh->pk.key, &pk->key); @@ -280,6 +289,7 @@ TALER_EXCHANGE_withdraw ( &wh->alg_values, &wh->bks, &wh->priv, + wh->ach, &wh->c_hash, &wh->pd)) { -- cgit v1.2.3