diff options
author | Özgür Kesim <oec-taler@kesim.org> | 2022-02-16 22:01:05 +0100 |
---|---|---|
committer | Özgür Kesim <oec-taler@kesim.org> | 2022-02-16 22:01:05 +0100 |
commit | 8bdf6ab19df70c16d335ecf82f2c3b2117eeb70e (patch) | |
tree | fe38fc98807feb6892052ee091b2b5f0a70ab17a /src/exchange | |
parent | b73be40ccd9ad0ef4a985f252099c867f698896d (diff) | |
download | exchange-8bdf6ab19df70c16d335ecf82f2c3b2117eeb70e.tar.gz exchange-8bdf6ab19df70c16d335ecf82f2c3b2117eeb70e.tar.bz2 exchange-8bdf6ab19df70c16d335ecf82f2c3b2117eeb70e.zip |
[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
Diffstat (limited to 'src/exchange')
-rw-r--r-- | src/exchange/taler-exchange-httpd.c | 12 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd.h | 6 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_db.c | 2 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_deposit.c | 4 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_extensions.c | 16 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_keys.c | 100 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_management_extensions.c | 110 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_management_post_keys.c | 2 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_melt.c | 8 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_recoup-refresh.c | 2 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_recoup.c | 2 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_refreshes_reveal.c | 38 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_responses.c | 12 |
13 files changed, 227 insertions, 87 deletions
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 06ad7ca9d..a0d0aa3b6 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -128,6 +128,12 @@ char *TEH_currency; char *TEH_base_url; /** + * Age restriction flags and mask + */ +bool TEH_age_restriction_enabled = false; +struct TALER_AgeMask TEH_age_mask = {0}; + +/** * Default timeout in seconds for HTTP requests. */ static unsigned int connection_timeout = 30; @@ -737,6 +743,12 @@ handle_post_management (struct TEH_RequestContext *rc, return TEH_handler_management_post_wire_fees (rc->connection, root); } + if (0 == strcmp (args[0], + "extensions")) + { + return TEH_handler_management_post_extensions (rc->connection, + root); + } GNUNET_break_op (0); return r404 (rc->connection, "/management/*"); diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index d3b1ba84a..ffbce0e9b 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -186,6 +186,12 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin; */ extern char *TEH_currency; +/* + * Age restriction extension state + */ +extern bool TEH_age_restriction_enabled; +extern struct TALER_AgeMask TEH_age_mask; + /** * Our (externally visible) base URL. */ diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index 3600d7931..f331e17d2 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -50,7 +50,7 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin, { enum TALER_EXCHANGEDB_CoinKnownStatus cks; struct TALER_DenominationHash h_denom_pub; - struct TALER_AgeHash age_hash; + struct TALER_AgeCommitmentHash age_hash; /* make sure coin is 'known' in database */ cks = TEH_plugin->ensure_coin_known (TEH_plugin->cls, diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 053552f2a..d750ec70e 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -240,6 +240,9 @@ TEH_handler_deposit (struct MHD_Connection *connection, &deposit.merchant_pub), GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &deposit.h_contract_terms), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("h_age_commitment", + &deposit.coin.age_commitment_hash)), GNUNET_JSON_spec_fixed_auto ("coin_sig", &deposit.csig), GNUNET_JSON_spec_timestamp ("timestamp", @@ -397,6 +400,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, &deposit.deposit_fee, &h_wire, &deposit.h_contract_terms, + &deposit.coin.age_commitment_hash, NULL /* h_extensions! */, &deposit.coin.denom_pub_hash, deposit.timestamp, diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c index 8edb24d40..6894a0762 100644 --- a/src/exchange/taler-exchange-httpd_extensions.c +++ b/src/exchange/taler-exchange-httpd_extensions.c @@ -127,6 +127,16 @@ extension_update_event_cb (void *cls, GNUNET_break (0); } } + + /* Special case age restriction: Update global flag and mask */ + if (TALER_Extension_AgeRestriction == type) + { + TEH_age_mask.mask = 0; + TEH_age_restriction_enabled = + TALER_extensions_age_restriction_is_enabled (); + if (TEH_age_restriction_enabled) + TEH_age_mask = TALER_extensions_age_restriction_ageMask (); + } } @@ -151,6 +161,12 @@ TEH_extensions_init () return GNUNET_SYSERR; } + /* FIXME: shall we load the extensions from the config right away? + * We do have to for now, as otherwise denominations with age restriction + * will not have the age mask set right upon initial generation. + */ + TALER_extensions_load_taler_config (TEH_cfg); + /* Trigger the initial load of configuration from the db */ for (const struct TALER_Extension *it = TALER_extensions_get_head (); NULL != it->next; diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 4b1a6213c..d1dfb28b9 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -795,27 +795,17 @@ static struct TALER_AgeMask load_age_mask (const char*section_name) { static const struct TALER_AgeMask null_mask = {0}; - struct TALER_AgeMask age_mask = {0}; - /* TODO: optimize by putting this into global? */ - const struct TALER_Extension *age_ext = - TALER_extensions_get_by_type (TALER_Extension_AgeRestriction); - - // Get the age mask from the extension, if configured - /* TODO: optimize by putting this into global? */ - if (TALER_extensions_is_enabled (age_ext)) - age_mask = *(struct TALER_AgeMask *) age_ext->config; - if (0 == age_mask.mask) - { - /* Age restriction support is not enabled. Ignore the AGE_RESTRICTED field - * for the particular denomination and simply return the null_mask - */ + struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask (); + + if (age_mask.mask == 0) return null_mask; - } - if (GNUNET_OK == (GNUNET_CONFIGURATION_have_value ( + if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( TEH_cfg, section_name, "AGE_RESTRICTED"))) + return null_mask; + { enum GNUNET_GenericReturnValue ret; @@ -1331,6 +1321,8 @@ denomination_info_cb ( dk->meta = *meta; dk->master_sig = *master_sig; dk->recoup_possible = recoup_possible; + dk->denom_pub.age_mask = meta->age_mask; + GNUNET_assert ( GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (ksh->denomkey_map, @@ -1745,7 +1737,7 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh, * @a recoup and @a denoms. * * @param[in,out] ksh key state handle we build @a krd for - * @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms + * @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms and age_restricted_denoms * @param last_cpd timestamp to use * @param signkeys list of sign keys to return * @param recoup list of revoked keys to return @@ -1863,7 +1855,8 @@ create_krd (struct TEH_KeyStateHandle *ksh, int r; /* skip if not configured == disabled */ - if (NULL == extension->config) + if (NULL == extension->config || + NULL == extension->config_json) continue; /* flag our findings so far */ @@ -1899,7 +1892,7 @@ create_krd (struct TEH_KeyStateHandle *ksh, json_t *sig; int r; - r = json_object_set_new ( + r = json_object_set ( keys, "extensions", extensions); @@ -1919,14 +1912,14 @@ create_krd (struct TEH_KeyStateHandle *ksh, json_decref (extensions); } - // Special case for age restrictions: if enabled, provide the lits of + // Special case for age restrictions: if enabled, provide the list of // age-restricted denominations. if (age_restriction_enabled && NULL != age_restricted_denoms) { GNUNET_assert ( 0 == - json_object_set_new ( + json_object_set ( keys, "age_restricted_denoms", age_restricted_denoms)); @@ -2005,7 +1998,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) json_t *age_restricted_denoms = NULL; struct GNUNET_TIME_Timestamp last_cpd; struct GNUNET_CONTAINER_Heap *heap; - struct GNUNET_HashContext *hash_context; + struct GNUNET_HashContext *hash_context = NULL; + struct GNUNET_HashContext *hash_context_restricted = NULL; + bool have_age_restricted_denoms = false; sctx.signkeys = json_array (); GNUNET_assert (NULL != sctx.signkeys); @@ -2030,19 +2025,23 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) = GNUNET_TIME_relative_min (dkc.min_dk_frequency, sctx.min_sk_frequency); } + denoms = json_array (); GNUNET_assert (NULL != denoms); + hash_context = GNUNET_CRYPTO_hash_context_start (); - // If age restriction is enabled, initialize the array of age restricted denoms. - /* TODO: optimize by putting this into global? */ - if (TALER_extensions_is_enabled_type (TALER_Extension_AgeRestriction)) + /* If age restriction is enabled, initialize the array of age restricted + denoms and prepare a hash for them, separate from the others. We will join + those hashes afterwards.*/ + if (TEH_age_restriction_enabled) { age_restricted_denoms = json_array (); GNUNET_assert (NULL != age_restricted_denoms); + hash_context_restricted = GNUNET_CRYPTO_hash_context_start (); } last_cpd = GNUNET_TIME_UNIT_ZERO_TS; - hash_context = GNUNET_CRYPTO_hash_context_start (); + { struct TEH_DenominationKey *dk; @@ -2056,6 +2055,11 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) { struct GNUNET_HashCode hc; + /* FIXME-oec: Do we need to take hash_context_restricted into account + * in this if-branch!? Current tests suggests: no, (they don't fail). + * But something seems to be odd about only finishing hash_context. + */ + GNUNET_CRYPTO_hash_context_finish ( GNUNET_CRYPTO_hash_context_copy (hash_context), &hc); @@ -2084,14 +2088,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) return GNUNET_SYSERR; } } + last_cpd = dk->meta.start; - GNUNET_CRYPTO_hash_context_read (hash_context, - &dk->h_denom_pub, - sizeof (struct GNUNET_HashCode)); { json_t *denom; json_t *array; + struct GNUNET_HashContext *hc; + denom = GNUNET_JSON_PACK ( @@ -2118,18 +2122,26 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) TALER_JSON_pack_amount ("fee_refund", &dk->meta.fee_refund)); - /* Put the denom into the correct array - denoms or age_restricted_denoms - - * depending on the settings and the properties of the denomination */ - if (NULL != age_restricted_denoms && - 0 != dk->meta.age_restrictions.mask) + /* Put the denom into the correct array depending on the settings and + * the properties of the denomination. Also, we build up the right + * hash for the corresponding array. */ + if (TEH_age_restriction_enabled && + (0 != dk->denom_pub.age_mask.mask)) { + have_age_restricted_denoms = true; array = age_restricted_denoms; + hc = hash_context_restricted; } else { array = denoms; + hc = hash_context; } + GNUNET_CRYPTO_hash_context_read (hc, + &dk->h_denom_pub, + sizeof (struct GNUNET_HashCode)); + GNUNET_assert ( 0 == json_array_append_new ( @@ -2138,13 +2150,27 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) } } } + GNUNET_CONTAINER_heap_destroy (heap); if (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)) { struct GNUNET_HashCode hc; + /* If age restriction is active and we had at least one denomination of + * that sort, we simply add the hash of all age restricted denominations at + * the end of the others. */ + if (TEH_age_restriction_enabled && have_age_restricted_denoms) + { + 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, &hc); + if (GNUNET_OK != create_krd (ksh, &hc, @@ -2158,7 +2184,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) "Failed to generate key response data for %s\n", GNUNET_TIME_timestamp2s (last_cpd)); json_decref (denoms); - if (NULL != age_restricted_denoms) + if (TEH_age_restriction_enabled && NULL != age_restricted_denoms) json_decref (age_restricted_denoms); json_decref (sctx.signkeys); json_decref (recoup); @@ -2849,7 +2875,9 @@ load_extension_data (const char *section_name, TEH_currency); return GNUNET_SYSERR; } - meta->age_restrictions = load_age_mask (section_name); + + meta->age_mask = load_age_mask (section_name); + return GNUNET_OK; } @@ -2976,7 +3004,7 @@ add_future_denomkey_cb (void *cls, struct FutureBuilderContext *fbc = cls; struct HelperDenomination *hd = value; struct TEH_DenominationKey *dk; - struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; + struct TALER_EXCHANGEDB_DenominationKeyMetaData meta = {0}; dk = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map, h_denom_pub); diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c index 17b000067..ab0287e33 100644 --- a/src/exchange/taler-exchange-httpd_management_extensions.c +++ b/src/exchange/taler-exchange-httpd_management_extensions.c @@ -31,7 +31,6 @@ #include "taler_extensions.h" #include "taler_dbevents.h" - /** * Extension carries the necessary data for a particular extension. * @@ -91,6 +90,8 @@ set_extensions (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } + GNUNET_assert (NULL != ext->config); + config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS); if (NULL == config) { @@ -140,6 +141,57 @@ set_extensions (void *cls, } +static enum GNUNET_GenericReturnValue +verify_extensions_from_json ( + json_t *extensions, + struct SetExtensionsContext *sec) +{ + const char*name; + const struct TALER_Extension *extension; + size_t i = 0; + json_t *blob; + + GNUNET_assert (NULL != extensions); + GNUNET_assert (json_is_object (extensions)); + + sec->num_extensions = json_object_size (extensions); + sec->extensions = GNUNET_new_array (sec->num_extensions, + struct Extension); + + json_object_foreach (extensions, name, blob) + { + int critical = 0; + json_t *config; + const char *version = NULL; + + /* load and verify criticality, version, etc. */ + extension = TALER_extensions_get_by_name (name); + if (NULL == extension) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "no such extension: %s\n", name); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + TALER_extensions_is_json_config ( + blob, &critical, &version, &config)) + return GNUNET_SYSERR; + + if (critical != extension->critical + || 0 != strcmp (version, extension->version) // TODO: libtool compare? + || NULL == config + || GNUNET_OK != extension->test_json_config (config)) + return GNUNET_SYSERR; + + sec->extensions[i].type = extension->type; + sec->extensions[i].config = config; + } + + return GNUNET_OK; +} + + MHD_RESULT TEH_handler_management_post_extensions ( struct MHD_Connection *connection, @@ -204,57 +256,18 @@ TEH_handler_management_post_extensions ( GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received /management/extensions\n"); - sec.num_extensions = json_object_size (extensions); - sec.extensions = GNUNET_new_array (sec.num_extensions, - struct Extension); - /* Now parse individual extensions and signatures from those objects. */ + if (GNUNET_OK != + verify_extensions_from_json (extensions, &sec)) { - const struct TALER_Extension *extension = NULL; - const char *name; - json_t *config; - int idx = 0; - - json_object_foreach (extensions, name, config){ - - /* 1. Make sure name refers to a supported extension */ - extension = TALER_extensions_get_by_name (name); - if (NULL == extension) - { - ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "invalid extension type"); - goto CLEANUP; - } - - sec.extensions[idx].config = config; - sec.extensions[idx].type = extension->type; - - /* 2. Make sure the config is sound */ - if (GNUNET_OK != - extension->test_json_config ( - sec.extensions[idx].config)) - { - ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "invalid configuration for extension"); - goto CLEANUP; - } - - /* We have a validly signed JSON object for the extension. Increment its - * refcount. - */ - json_incref (sec.extensions[idx].config); - idx++; - - } /* json_object_foreach */ + GNUNET_JSON_parse_free (top_spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "invalid object"); } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %u extensions\n", sec.num_extensions); @@ -281,6 +294,7 @@ TEH_handler_management_post_extensions ( NULL, 0); + CLEANUP: for (unsigned int i = 0; i < sec.num_extensions; i++) { diff --git a/src/exchange/taler-exchange-httpd_management_post_keys.c b/src/exchange/taler-exchange-httpd_management_post_keys.c index f0c3f1f39..c353a9959 100644 --- a/src/exchange/taler-exchange-httpd_management_post_keys.c +++ b/src/exchange/taler-exchange-httpd_management_post_keys.c @@ -204,6 +204,7 @@ add_keys (void *cls, TALER_denom_pub_free (&denom_pub); return GNUNET_DB_STATUS_HARD_ERROR; } + if (is_active) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -211,6 +212,7 @@ add_keys (void *cls, GNUNET_h2s (&d->h_denom_pub.hash)); continue; /* skip, already known */ } + qs = TEH_plugin->add_denomination_key ( TEH_plugin->cls, &d->h_denom_pub, diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c index 021b629b3..8bfdf8cef 100644 --- a/src/exchange/taler-exchange-httpd_melt.c +++ b/src/exchange/taler-exchange-httpd_melt.c @@ -279,6 +279,7 @@ check_melt_valid (struct MHD_Connection *connection, &mret); if (NULL == dk) return mret; + if (GNUNET_TIME_absolute_is_past (dk->meta.expire_legal.abs_time)) { /* Way too late now, even zombies have expired */ @@ -288,6 +289,7 @@ check_melt_valid (struct MHD_Connection *connection, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, "MELT"); } + if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) { /* This denomination is not yet valid */ @@ -300,9 +302,11 @@ check_melt_valid (struct MHD_Connection *connection, rmc->coin_refresh_fee = dk->meta.fee_refresh; rmc->coin_value = dk->meta.value; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Melted coin's denomination is worth %s\n", TALER_amount2s (&dk->meta.value)); + /* sanity-check that "total melt amount > melt fee" */ if (0 < TALER_amount_cmp (&rmc->coin_refresh_fee, @@ -332,6 +336,7 @@ check_melt_valid (struct MHD_Connection *connection, &rmc->coin_refresh_fee, &rmc->refresh_session.rc, &rmc->refresh_session.coin.denom_pub_hash, + &rmc->refresh_session.coin.age_commitment_hash, &rmc->refresh_session.coin.coin_pub, &rmc->refresh_session.coin_sig)) { @@ -407,6 +412,9 @@ TEH_handler_melt (struct MHD_Connection *connection, &rmc.refresh_session.coin.denom_sig), GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", &rmc.refresh_session.coin.denom_pub_hash), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("age_commitment_hash", + &rmc.refresh_session.coin.age_commitment_hash)), GNUNET_JSON_spec_fixed_auto ("confirm_sig", &rmc.refresh_session.coin_sig), TALER_JSON_spec_amount ("value_with_fee", diff --git a/src/exchange/taler-exchange-httpd_recoup-refresh.c b/src/exchange/taler-exchange-httpd_recoup-refresh.c index 829e2cbd7..bbf6defee 100644 --- a/src/exchange/taler-exchange-httpd_recoup-refresh.c +++ b/src/exchange/taler-exchange-httpd_recoup-refresh.c @@ -251,7 +251,7 @@ verify_and_execute_recoup_refresh ( if (GNUNET_OK != TALER_denom_blind (&dk->denom_pub, coin_bks, - NULL, /* FIXME-Oec: TALER_AgeHash * */ + NULL, /* FIXME-Oec: TALER_AgeCommitmentHash * */ &coin->coin_pub, exchange_vals, &c_hash, diff --git a/src/exchange/taler-exchange-httpd_recoup.c b/src/exchange/taler-exchange-httpd_recoup.c index ea319d11c..4ac997e9c 100644 --- a/src/exchange/taler-exchange-httpd_recoup.c +++ b/src/exchange/taler-exchange-httpd_recoup.c @@ -256,7 +256,7 @@ verify_and_execute_recoup ( if (GNUNET_OK != TALER_denom_blind (&dk->denom_pub, coin_bks, - NULL, /* FIXME-Oec: TALER_AgeHash * */ + NULL, /* FIXME-Oec: TALER_AgeCommitmentHash * */ &coin->coin_pub, exchange_vals, &c_hash, diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index 0d8f7bf9b..1f0782aaa 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -281,6 +281,7 @@ check_commitment (struct RevealContext *rctx, alg_value, &bks, &coin_priv, + NULL, /* FIXME-Oec, struct TALER_AgeCommitmentHash * */ &c_hash, &pd)); if (TALER_DENOMINATION_CS == dk->cipher) @@ -380,6 +381,7 @@ check_commitment (struct RevealContext *rctx, * @param rctx context for the operation, partially built at this time * @param link_sigs_json link signatures in JSON format * @param new_denoms_h_json requests for fresh coins to be created + * @param old_age_commitment_json age commitment that went into the withdrawal, maybe NULL * @param coin_evs envelopes of gamma-selected coins to be signed * @return MHD result code */ @@ -388,6 +390,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, struct RevealContext *rctx, const json_t *link_sigs_json, const json_t *new_denoms_h_json, + const json_t *old_age_commitment_json, const json_t *coin_evs) { unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); @@ -412,6 +415,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, NULL); } + /* Parse denomination key hashes */ for (unsigned int i = 0; i<num_fresh_coins; i++) { @@ -537,6 +541,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, goto cleanup; } } + /* Parse link signatures array */ for (unsigned int i = 0; i<num_fresh_coins; i++) { @@ -554,6 +559,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, -1); if (GNUNET_OK != res) return (GNUNET_NO == res) ? MHD_YES : MHD_NO; + /* Check signature */ if (GNUNET_OK != TALER_wallet_link_verify ( @@ -561,6 +567,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, &rctx->gamma_tp, &rrcs[i].coin_envelope_hash, &rctx->melt.session.coin.coin_pub, + NULL, // TODO-oec: calculate the correct h_age_commitment &rrcs[i].orig_coin_link_sig)) { GNUNET_break_op (0); @@ -592,6 +599,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, goto cleanup; } } + rctx->dks = dks; rctx->rcds = rcds; rctx->rrcs = rrcs; @@ -604,6 +612,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating %u signatures\n", (unsigned int) rctx->num_fresh_coins); + /* create fresh coin signatures */ for (unsigned int i = 0; i<rctx->num_fresh_coins; i++) { @@ -622,8 +631,10 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, goto cleanup; } } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signatures ready, starting DB interaction\n"); + /* Persist operation result in DB */ { enum GNUNET_DB_QueryStatus qs; @@ -678,11 +689,18 @@ cleanup: * revealed information is valid then returns the signed refreshed * coins. * + * If the denomination has age restriction support, the array of EDDSA public + * keys, one for each age group that was activated during the withdrawal + * by the parent/ward, must be provided in old_age_commitment. The hash of + * this array must be the same as the h_age_commitment of the persisted reveal + * request. + * * @param connection the MHD connection to handle * @param rctx context for the operation, partially built at this time * @param tp_json private transfer keys in JSON format * @param link_sigs_json link signatures in JSON format * @param new_denoms_h_json requests for fresh coins to be created + * @param old_age_commitment_json array of EDDSA public keys in JSON, used for age restriction, maybe NULL * @param coin_evs envelopes of gamma-selected coins to be signed * @return MHD result code */ @@ -692,6 +710,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection, const json_t *tp_json, const json_t *link_sigs_json, const json_t *new_denoms_h_json, + const json_t *old_age_commitment_json, const json_t *coin_evs) { unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); @@ -727,6 +746,19 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection, "new_denoms/link_sigs"); } + /* Sanity check of age commitment: If it was provided, it _must_ be an array + * of the size the # of age groups */ + if (NULL != old_age_commitment_json + && TALER_extensions_age_restriction_num_groups () != + json_array_size (old_age_commitment_json)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID, + "old_age_commitment"); + } + /* Parse transfer private keys array */ for (unsigned int i = 0; i<num_tprivs; i++) { @@ -750,6 +782,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection, rctx, link_sigs_json, new_denoms_h_json, + old_age_commitment_json, coin_evs); } @@ -763,6 +796,7 @@ TEH_handler_reveal (struct TEH_RequestContext *rc, json_t *transfer_privs; json_t *link_sigs; json_t *new_denoms_h; + json_t *old_age_commitment = NULL; struct RevealContext rctx; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("transfer_pub", @@ -775,6 +809,9 @@ TEH_handler_reveal (struct TEH_RequestContext *rc, &coin_evs), GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("old_age_commitment", + &old_age_commitment)), GNUNET_JSON_spec_end () }; @@ -836,6 +873,7 @@ TEH_handler_reveal (struct TEH_RequestContext *rc, transfer_privs, link_sigs, new_denoms_h, + old_age_commitment, coin_evs); GNUNET_JSON_parse_free (spec); return res; diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 55b230444..00f047172 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -73,6 +73,7 @@ TEH_RESPONSE_compile_transaction_history ( &deposit->deposit_fee, &h_wire, &deposit->h_contract_terms, + NULL, /* h_age_commitment, FIXME-oec */ NULL /* h_extensions! */, &deposit->h_denom_pub, deposit->timestamp, @@ -122,6 +123,7 @@ TEH_RESPONSE_compile_transaction_history ( { const struct TALER_EXCHANGEDB_MeltListEntry *melt = pos->details.melt; + const struct TALER_AgeCommitmentHash *phac = NULL; #if ENABLE_SANITY_CHECKS if (GNUNET_OK != @@ -129,6 +131,7 @@ TEH_RESPONSE_compile_transaction_history ( &melt->melt_fee, &melt->rc, &melt->h_denom_pub, + &melt->h_age_commitment, coin_pub, &melt->coin_sig)) { @@ -137,6 +140,12 @@ TEH_RESPONSE_compile_transaction_history ( return NULL; } #endif + + /* Age restriction is optional. We communicate a NULL value to + * JSON_PACK below */ + if (! TALER_AgeCommitmentHash_isNullOrZero (&melt->h_age_commitment)) + phac = &melt->h_age_commitment; + if (0 != json_array_append_new ( history, @@ -151,6 +160,9 @@ TEH_RESPONSE_compile_transaction_history ( &melt->rc), GNUNET_JSON_pack_data_auto ("h_denom_pub", &melt->h_denom_pub), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_data_auto ("h_age_commitment", + phac)), GNUNET_JSON_pack_data_auto ("coin_sig", &melt->coin_sig)))) { |