[age restriction] progress 14/n - withdraw and deposit

Age restriction support for
  - withdraw is done and tested
  - deposit is done and tested
  - melt is done, untested
  - reveal started
  - link started

Added functions
 - TALER_age_commitment_hash
 - TALER_age_restriction_commit
 - TALER_age_commitment_derive
 - 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
  - WithdrawState carries age_commitment and its hash
  - withdraw_run derives new age commitment, if applicable
  - Added age parameter to testing (13 as example)
  - TALER_TESTING_find_pk takes boolean age_restricted
  - struct RefreshMeltState carries age commitment of melted coin
  - melt_run calls TALER_age_commitment_derive, if necessary

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
This commit is contained in:
Özgür Kesim 2022-01-23 18:27:31 +01:00
parent 8684a9bfea
commit 327361adda
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
59 changed files with 1882 additions and 365 deletions

@ -1 +1 @@
Subproject commit b0dd85e8187f33a1f92dd5eb31082050d333e168 Subproject commit 94b3d826de2aa442a985d03a52eaf526ef950d83

View File

@ -1227,6 +1227,7 @@ static int
refresh_session_cb (void *cls, refresh_session_cb (void *cls,
uint64_t rowid, uint64_t rowid,
const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *amount_with_fee,
@ -1289,6 +1290,7 @@ refresh_session_cb (void *cls,
&fee_refresh, &fee_refresh,
rc, rc,
&h_denom_pub, &h_denom_pub,
h_age_commitment,
coin_pub, coin_pub,
coin_sig)) coin_sig))
{ {
@ -1615,6 +1617,7 @@ deposit_cb (void *cls,
struct TALER_MerchantWireHash h_wire; struct TALER_MerchantWireHash h_wire;
struct TALER_DenominationHash h_denom_pub; struct TALER_DenominationHash h_denom_pub;
struct TALER_Amount deposit_fee; struct TALER_Amount deposit_fee;
struct TALER_AgeCommitmentHash *h_age_commitment = NULL; /* FIXME-oec */
TALER_denom_pub_hash (denom_pub, TALER_denom_pub_hash (denom_pub,
&h_denom_pub); &h_denom_pub);
@ -1631,6 +1634,7 @@ deposit_cb (void *cls,
&deposit_fee, &deposit_fee,
&h_wire, &h_wire,
&deposit->h_contract_terms, &deposit->h_contract_terms,
h_age_commitment, /* FIXME-oec */
NULL /* h_extensions! */, NULL /* h_extensions! */,
&h_denom_pub, &h_denom_pub,
deposit->timestamp, deposit->timestamp,

View File

@ -300,7 +300,7 @@ add_deposit (const struct Merchant *m)
struct TALER_EXCHANGEDB_Deposit deposit; struct TALER_EXCHANGEDB_Deposit deposit;
uint64_t known_coin_id; uint64_t known_coin_id;
struct TALER_DenominationHash dph; struct TALER_DenominationHash dph;
struct TALER_AgeHash agh; struct TALER_AgeCommitmentHash agh;
RANDOMIZE (&d.coin.coin_pub); RANDOMIZE (&d.coin.coin_pub);
d.coin.denom_pub_hash = h_denom_pub; d.coin.denom_pub_hash = h_denom_pub;

View File

@ -366,6 +366,7 @@ run (void *cls,
(TALER_TESTING_cmd_withdraw_amount (wl, (TALER_TESTING_cmd_withdraw_amount (wl,
create_reserve_label, create_reserve_label,
amount_5, amount_5,
0, /* age restriction off */
MHD_HTTP_OK)); MHD_HTTP_OK));
unit[1] = unit[1] =
TALER_TESTING_cmd_deposit_with_retry TALER_TESTING_cmd_deposit_with_retry

View File

@ -152,6 +152,10 @@ static char *currency;
*/ */
static char *CFG_exchange_url; static char *CFG_exchange_url;
/**
* If age restriction is enabled, the age mask to be used
*/
static struct TALER_AgeMask age_mask = {0};
/** /**
* A subcommand supported by this program. * A subcommand supported by this program.
@ -1924,6 +1928,7 @@ trigger_upload (const char *exchange_url)
if (0 == strcasecmp (key, if (0 == strcasecmp (key,
uhs[i].key)) uhs[i].key))
{ {
found = true; found = true;
uhs[i].cb (exchange_url, uhs[i].cb (exchange_url,
index, index,
@ -3036,6 +3041,7 @@ do_show (char *const *args)
keys = parse_keys_input ("show"); keys = parse_keys_input ("show");
if (NULL == keys) if (NULL == keys)
return; return;
if (GNUNET_OK != if (GNUNET_OK !=
load_offline_key (GNUNET_NO)) load_offline_key (GNUNET_NO))
return; return;
@ -3196,6 +3202,43 @@ sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
} }
/**
* Looks up the AGE_RESTRICTED setting for a denomination in the config and
* returns the age restriction (mask) accordingly.
*
* @param section_name Section in the configuration for the particular
* denomination.
*/
static struct TALER_AgeMask
load_age_mask (const char*section_name)
{
static const struct TALER_AgeMask null_mask = {0};
enum GNUNET_GenericReturnValue ret;
if (age_mask.mask == 0)
return null_mask;
if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
kcfg,
section_name,
"AGE_RESTRICTED")))
return null_mask;
ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg,
section_name,
"AGE_RESTRICTED");
if (GNUNET_YES == ret)
return age_mask;
if (GNUNET_SYSERR == ret)
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section_name,
"AGE_RESTRICTED",
"Value must be YES or NO\n");
return null_mask;
}
/** /**
* Sign @a denomkeys with offline key. * Sign @a denomkeys with offline key.
* *
@ -3284,7 +3327,10 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
duration = GNUNET_TIME_absolute_get_difference ( duration = GNUNET_TIME_absolute_get_difference (
stamp_start.abs_time, stamp_start.abs_time,
stamp_expire_withdraw.abs_time); stamp_expire_withdraw.abs_time);
// FIXME-Oec: setup age mask here?
/* Load the age mask, if applicable to this denomination */
denom_pub.age_mask = load_age_mask (section_name);
TALER_denom_pub_hash (&denom_pub, TALER_denom_pub_hash (&denom_pub,
&h_denom_pub); &h_denom_pub);
switch (denom_pub.cipher) switch (denom_pub.cipher)
@ -3518,14 +3564,6 @@ do_extensions_show (char *const *args)
json_t *exts = json_object (); json_t *exts = json_object ();
const struct TALER_Extension *it; const struct TALER_Extension *it;
TALER_extensions_init ();
if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"error while loading taler config for extensions\n");
return;
}
for (it = TALER_extensions_get_head (); for (it = TALER_extensions_get_head ();
NULL != it; NULL != it;
it = it->next) it = it->next)
@ -3779,6 +3817,17 @@ run (void *cls,
global_ret = EXIT_NOTCONFIGURED; global_ret = EXIT_NOTCONFIGURED;
return; return;
} }
/* load age mask, if age restriction is enabled */
TALER_extensions_init ();
if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"error while loading taler config for extensions\n");
return;
}
age_mask = TALER_extensions_age_restriction_ageMask ();
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc); &rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx); rc = GNUNET_CURL_gnunet_rc_create (ctx);

View File

@ -126,6 +126,12 @@ char *TEH_currency;
*/ */
char *TEH_base_url; 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. * Default timeout in seconds for HTTP requests.
*/ */
@ -736,6 +742,12 @@ handle_post_management (struct TEH_RequestContext *rc,
return TEH_handler_management_post_wire_fees (rc->connection, return TEH_handler_management_post_wire_fees (rc->connection,
root); root);
} }
if (0 == strcmp (args[0],
"extensions"))
{
return TEH_handler_management_post_extensions (rc->connection,
root);
}
GNUNET_break_op (0); GNUNET_break_op (0);
return r404 (rc->connection, return r404 (rc->connection,
"/management/*"); "/management/*");

View File

@ -186,6 +186,12 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
*/ */
extern char *TEH_currency; 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. * Our (externally visible) base URL.
*/ */

View File

@ -50,7 +50,7 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
{ {
enum TALER_EXCHANGEDB_CoinKnownStatus cks; enum TALER_EXCHANGEDB_CoinKnownStatus cks;
struct TALER_DenominationHash h_denom_pub; struct TALER_DenominationHash h_denom_pub;
struct TALER_AgeHash age_hash; struct TALER_AgeCommitmentHash age_hash;
/* make sure coin is 'known' in database */ /* make sure coin is 'known' in database */
cks = TEH_plugin->ensure_coin_known (TEH_plugin->cls, cks = TEH_plugin->ensure_coin_known (TEH_plugin->cls,

View File

@ -239,6 +239,9 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&deposit.merchant_pub), &deposit.merchant_pub),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms", GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&deposit.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", GNUNET_JSON_spec_fixed_auto ("coin_sig",
&deposit.csig), &deposit.csig),
GNUNET_JSON_spec_timestamp ("timestamp", GNUNET_JSON_spec_timestamp ("timestamp",
@ -387,6 +390,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&deposit.deposit_fee, &deposit.deposit_fee,
&h_wire, &h_wire,
&deposit.h_contract_terms, &deposit.h_contract_terms,
&deposit.coin.age_commitment_hash,
NULL /* h_extensions! */, NULL /* h_extensions! */,
&deposit.coin.denom_pub_hash, &deposit.coin.denom_pub_hash,
deposit.timestamp, deposit.timestamp,

View File

@ -127,6 +127,16 @@ extension_update_event_cb (void *cls,
GNUNET_break (0); 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; 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 */ /* Trigger the initial load of configuration from the db */
for (const struct TALER_Extension *it = TALER_extensions_get_head (); for (const struct TALER_Extension *it = TALER_extensions_get_head ();
NULL != it->next; NULL != it->next;

View File

@ -743,43 +743,30 @@ static struct TALER_AgeMask
load_age_mask (const char*section_name) load_age_mask (const char*section_name)
{ {
static const struct TALER_AgeMask null_mask = {0}; static const struct TALER_AgeMask null_mask = {0};
struct TALER_AgeMask age_mask = {0}; enum GNUNET_GenericReturnValue ret;
/* TODO: optimize by putting this into global? */ struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask ();
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 (age_mask.mask == 0) if (age_mask.mask == 0)
{
/* Age restriction support is not enabled. Ignore the AGE_RESTRICTED field
* for the particular denomination and simply return the null_mask
*/
return null_mask; return null_mask;
}
if (GNUNET_OK == (GNUNET_CONFIGURATION_have_value ( if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
TEH_cfg, TEH_cfg,
section_name, section_name,
"AGE_RESTRICTED"))) "AGE_RESTRICTED")))
{ return null_mask;
enum GNUNET_GenericReturnValue ret;
if (GNUNET_SYSERR == (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
section_name,
"AGE_RESTRICTED")))
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section_name,
"AGE_RESTRICTED",
"Value must be YES or NO\n");
return null_mask;
}
}
return age_mask; ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
section_name,
"AGE_RESTRICTED");
if (GNUNET_YES == ret)
return age_mask;
if (GNUNET_SYSERR == ret)
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section_name,
"AGE_RESTRICTED",
"Value must be YES or NO\n");
return null_mask;
} }
@ -1187,6 +1174,8 @@ denomination_info_cb (
dk->meta = *meta; dk->meta = *meta;
dk->master_sig = *master_sig; dk->master_sig = *master_sig;
dk->recoup_possible = recoup_possible; dk->recoup_possible = recoup_possible;
dk->denom_pub.age_mask = meta->age_mask;
GNUNET_assert ( GNUNET_assert (
GNUNET_OK == GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (ksh->denomkey_map, GNUNET_CONTAINER_multihashmap_put (ksh->denomkey_map,
@ -1601,7 +1590,7 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
* @a recoup and @a denoms. * @a recoup and @a denoms.
* *
* @param[in,out] ksh key state handle we build @a krd for * @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 last_cpd timestamp to use
* @param signkeys list of sign keys to return * @param signkeys list of sign keys to return
* @param recoup list of revoked keys to return * @param recoup list of revoked keys to return
@ -1719,7 +1708,8 @@ create_krd (struct TEH_KeyStateHandle *ksh,
int r; int r;
/* skip if not configured == disabled */ /* skip if not configured == disabled */
if (NULL == extension->config) if (NULL == extension->config ||
NULL == extension->config_json)
continue; continue;
/* flag our findings so far */ /* flag our findings so far */
@ -1755,7 +1745,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
json_t *sig; json_t *sig;
int r; int r;
r = json_object_set_new ( r = json_object_set (
keys, keys,
"extensions", "extensions",
extensions); extensions);
@ -1771,14 +1761,14 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_assert (0 == r); GNUNET_assert (0 == r);
} }
// 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. // age-restricted denominations.
if (age_restriction_enabled && if (age_restriction_enabled &&
NULL != age_restricted_denoms) NULL != age_restricted_denoms)
{ {
GNUNET_assert ( GNUNET_assert (
0 == 0 ==
json_object_set_new ( json_object_set (
keys, keys,
"age_restricted_denoms", "age_restricted_denoms",
age_restricted_denoms)); age_restricted_denoms));
@ -1857,7 +1847,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
json_t *age_restricted_denoms = NULL; json_t *age_restricted_denoms = NULL;
struct GNUNET_TIME_Timestamp last_cpd; struct GNUNET_TIME_Timestamp last_cpd;
struct GNUNET_CONTAINER_Heap *heap; 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 (); sctx.signkeys = json_array ();
GNUNET_assert (NULL != sctx.signkeys); GNUNET_assert (NULL != sctx.signkeys);
@ -1882,19 +1874,23 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
= GNUNET_TIME_relative_min (dkc.min_dk_frequency, = GNUNET_TIME_relative_min (dkc.min_dk_frequency,
sctx.min_sk_frequency); sctx.min_sk_frequency);
} }
denoms = json_array (); denoms = json_array ();
GNUNET_assert (NULL != denoms); GNUNET_assert (NULL != denoms);
hash_context = GNUNET_CRYPTO_hash_context_start ();
// If age restriction is enabled, initialize the array of age restricted denoms. /* If age restriction is enabled, initialize the array of age restricted
/* TODO: optimize by putting this into global? */ denoms and prepare a hash for them, separate from the others. We will join
if (TALER_extensions_is_enabled_type (TALER_Extension_AgeRestriction)) those hashes afterwards.*/
if (TEH_age_restriction_enabled)
{ {
age_restricted_denoms = json_array (); age_restricted_denoms = json_array ();
GNUNET_assert (NULL != age_restricted_denoms); GNUNET_assert (NULL != age_restricted_denoms);
hash_context_restricted = GNUNET_CRYPTO_hash_context_start ();
} }
last_cpd = GNUNET_TIME_UNIT_ZERO_TS; last_cpd = GNUNET_TIME_UNIT_ZERO_TS;
hash_context = GNUNET_CRYPTO_hash_context_start ();
{ {
struct TEH_DenominationKey *dk; struct TEH_DenominationKey *dk;
@ -1908,6 +1904,11 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
{ {
struct GNUNET_HashCode hc; 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_finish (
GNUNET_CRYPTO_hash_context_copy (hash_context), GNUNET_CRYPTO_hash_context_copy (hash_context),
&hc); &hc);
@ -1936,14 +1937,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
} }
last_cpd = dk->meta.start; 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 *denom;
json_t *array; json_t *array;
struct GNUNET_HashContext *hc;
denom = denom =
GNUNET_JSON_PACK ( GNUNET_JSON_PACK (
@ -1970,18 +1971,26 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
TALER_JSON_pack_amount ("fee_refund", TALER_JSON_pack_amount ("fee_refund",
&dk->meta.fee_refund)); &dk->meta.fee_refund));
/* Put the denom into the correct array - denoms or age_restricted_denoms - /* Put the denom into the correct array depending on the settings and
* depending on the settings and the properties of the denomination */ * the properties of the denomination. Also, we build up the right
if (NULL != age_restricted_denoms && * hash for the corresponding array. */
0 != dk->meta.age_restrictions.mask) if (TEH_age_restriction_enabled &&
(0 != dk->denom_pub.age_mask.mask))
{ {
have_age_restricted_denoms = true;
array = age_restricted_denoms; array = age_restricted_denoms;
hc = hash_context_restricted;
} }
else else
{ {
array = denoms; array = denoms;
hc = hash_context;
} }
GNUNET_CRYPTO_hash_context_read (hc,
&dk->h_denom_pub,
sizeof (struct GNUNET_HashCode));
GNUNET_assert ( GNUNET_assert (
0 == 0 ==
json_array_append_new ( json_array_append_new (
@ -1990,13 +1999,27 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
} }
} }
} }
GNUNET_CONTAINER_heap_destroy (heap); GNUNET_CONTAINER_heap_destroy (heap);
if (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)) if (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time))
{ {
struct GNUNET_HashCode hc; 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, GNUNET_CRYPTO_hash_context_finish (hash_context,
&hc); &hc);
if (GNUNET_OK != if (GNUNET_OK !=
create_krd (ksh, create_krd (ksh,
&hc, &hc,
@ -2010,7 +2033,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
"Failed to generate key response data for %s\n", "Failed to generate key response data for %s\n",
GNUNET_TIME_timestamp2s (last_cpd)); GNUNET_TIME_timestamp2s (last_cpd));
json_decref (denoms); json_decref (denoms);
if (NULL != age_restricted_denoms) if (TEH_age_restriction_enabled && NULL != age_restricted_denoms)
json_decref (age_restricted_denoms); json_decref (age_restricted_denoms);
json_decref (sctx.signkeys); json_decref (sctx.signkeys);
json_decref (recoup); json_decref (recoup);
@ -2667,7 +2690,9 @@ load_extension_data (const char *section_name,
TEH_currency); TEH_currency);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
meta->age_restrictions = load_age_mask (section_name);
meta->age_mask = load_age_mask (section_name);
return GNUNET_OK; return GNUNET_OK;
} }
@ -2794,7 +2819,7 @@ add_future_denomkey_cb (void *cls,
struct FutureBuilderContext *fbc = cls; struct FutureBuilderContext *fbc = cls;
struct HelperDenomination *hd = value; struct HelperDenomination *hd = value;
struct TEH_DenominationKey *dk; struct TEH_DenominationKey *dk;
struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; struct TALER_EXCHANGEDB_DenominationKeyMetaData meta = {0};
dk = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map, dk = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map,
h_denom_pub); h_denom_pub);

View File

@ -31,7 +31,6 @@
#include "taler_extensions.h" #include "taler_extensions.h"
#include "taler_dbevents.h" #include "taler_dbevents.h"
/** /**
* Extension carries the necessary data for a particular extension. * Extension carries the necessary data for a particular extension.
* *
@ -91,6 +90,8 @@ set_extensions (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
GNUNET_assert (NULL != ext->config);
config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS); config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS);
if (NULL == config) 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 MHD_RESULT
TEH_handler_management_post_extensions ( TEH_handler_management_post_extensions (
struct MHD_Connection *connection, struct MHD_Connection *connection,
@ -204,57 +256,18 @@ TEH_handler_management_post_extensions (
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received /management/extensions\n"); "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. */ /* Now parse individual extensions and signatures from those objects. */
if (GNUNET_OK !=
verify_extensions_from_json (extensions, &sec))
{ {
const struct TALER_Extension *extension = NULL; GNUNET_JSON_parse_free (top_spec);
const char *name; return TALER_MHD_reply_with_error (
json_t *config; connection,
int idx = 0; MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
json_object_foreach (extensions, name, config){ "invalid object");
/* 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_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received %u extensions\n", "Received %u extensions\n",
sec.num_extensions); sec.num_extensions);
@ -281,6 +294,7 @@ TEH_handler_management_post_extensions (
NULL, NULL,
0); 0);
CLEANUP: CLEANUP:
for (unsigned int i = 0; i < sec.num_extensions; i++) for (unsigned int i = 0; i < sec.num_extensions; i++)
{ {

View File

@ -204,6 +204,7 @@ add_keys (void *cls,
TALER_denom_pub_free (&denom_pub); TALER_denom_pub_free (&denom_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if (is_active) if (is_active)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -211,6 +212,7 @@ add_keys (void *cls,
GNUNET_h2s (&d->h_denom_pub.hash)); GNUNET_h2s (&d->h_denom_pub.hash));
continue; /* skip, already known */ continue; /* skip, already known */
} }
qs = TEH_plugin->add_denomination_key ( qs = TEH_plugin->add_denomination_key (
TEH_plugin->cls, TEH_plugin->cls,
&d->h_denom_pub, &d->h_denom_pub,

View File

@ -278,6 +278,7 @@ check_melt_valid (struct MHD_Connection *connection,
&mret); &mret);
if (NULL == dk) if (NULL == dk)
return mret; return mret;
if (GNUNET_TIME_absolute_is_past (dk->meta.expire_legal.abs_time)) if (GNUNET_TIME_absolute_is_past (dk->meta.expire_legal.abs_time))
{ {
/* Way too late now, even zombies have expired */ /* Way too late now, even zombies have expired */
@ -287,6 +288,7 @@ check_melt_valid (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
"MELT"); "MELT");
} }
if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
{ {
/* This denomination is not yet valid */ /* This denomination is not yet valid */
@ -299,6 +301,7 @@ check_melt_valid (struct MHD_Connection *connection,
rmc->coin_refresh_fee = dk->meta.fee_refresh; rmc->coin_refresh_fee = dk->meta.fee_refresh;
rmc->coin_value = dk->meta.value; rmc->coin_value = dk->meta.value;
/* sanity-check that "total melt amount > melt fee" */ /* sanity-check that "total melt amount > melt fee" */
if (0 < if (0 <
TALER_amount_cmp (&rmc->coin_refresh_fee, TALER_amount_cmp (&rmc->coin_refresh_fee,
@ -328,6 +331,7 @@ check_melt_valid (struct MHD_Connection *connection,
&rmc->coin_refresh_fee, &rmc->coin_refresh_fee,
&rmc->refresh_session.rc, &rmc->refresh_session.rc,
&rmc->refresh_session.coin.denom_pub_hash, &rmc->refresh_session.coin.denom_pub_hash,
&rmc->refresh_session.coin.age_commitment_hash,
&rmc->refresh_session.coin.coin_pub, &rmc->refresh_session.coin.coin_pub,
&rmc->refresh_session.coin_sig)) &rmc->refresh_session.coin_sig))
{ {
@ -403,6 +407,9 @@ TEH_handler_melt (struct MHD_Connection *connection,
&rmc.refresh_session.coin.denom_sig), &rmc.refresh_session.coin.denom_sig),
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&rmc.refresh_session.coin.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", GNUNET_JSON_spec_fixed_auto ("confirm_sig",
&rmc.refresh_session.coin_sig), &rmc.refresh_session.coin_sig),
TALER_JSON_spec_amount ("value_with_fee", TALER_JSON_spec_amount ("value_with_fee",

View File

@ -248,7 +248,7 @@ verify_and_execute_recoup_refresh (
if (GNUNET_OK != if (GNUNET_OK !=
TALER_denom_blind (&dk->denom_pub, TALER_denom_blind (&dk->denom_pub,
coin_bks, coin_bks,
NULL, /* FIXME-Oec: TALER_AgeHash * */ NULL, /* FIXME-Oec: TALER_AgeCommitmentHash * */
&coin->coin_pub, &coin->coin_pub,
&c_hash, &c_hash,
&coin_ev, &coin_ev,

View File

@ -250,7 +250,7 @@ verify_and_execute_recoup (
if (GNUNET_OK != if (GNUNET_OK !=
TALER_denom_blind (&dk->denom_pub, TALER_denom_blind (&dk->denom_pub,
coin_bks, coin_bks,
NULL, /* FIXME-Oec: TALER_AgeHash * */ NULL, /* FIXME-Oec: TALER_AgeCommitmentHash * */
&coin->coin_pub, &coin->coin_pub,
&c_hash, &c_hash,
&coin_ev, &coin_ev,

View File

@ -191,6 +191,7 @@ check_commitment (struct RevealContext *rctx,
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_planchet_prepare (rcd->dk, TALER_planchet_prepare (rcd->dk,
&ps, &ps,
NULL, /* FIXME-Oec, struct TALER_AgeCommitmentHash * */
&c_hash, &c_hash,
&pd)); &pd));
rcd->coin_ev = pd.coin_ev; rcd->coin_ev = pd.coin_ev;
@ -285,6 +286,7 @@ check_commitment (struct RevealContext *rctx,
* @param rctx context for the operation, partially built at this time * @param rctx context for the operation, partially built at this time
* @param link_sigs_json link signatures 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 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 * @param coin_evs envelopes of gamma-selected coins to be signed
* @return MHD result code * @return MHD result code
*/ */
@ -293,6 +295,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
struct RevealContext *rctx, struct RevealContext *rctx,
const json_t *link_sigs_json, const json_t *link_sigs_json,
const json_t *new_denoms_h_json, const json_t *new_denoms_h_json,
const json_t *old_age_commitment_json,
const json_t *coin_evs) const json_t *coin_evs)
{ {
unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
@ -317,6 +320,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
NULL); NULL);
} }
/* Parse denomination key hashes */ /* Parse denomination key hashes */
for (unsigned int i = 0; i<num_fresh_coins; i++) for (unsigned int i = 0; i<num_fresh_coins; i++)
{ {
@ -443,6 +447,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
goto cleanup; goto cleanup;
} }
} }
/* Parse link signatures array */ /* Parse link signatures array */
for (unsigned int i = 0; i<num_fresh_coins; i++) for (unsigned int i = 0; i<num_fresh_coins; i++)
{ {
@ -460,6 +465,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
-1); -1);
if (GNUNET_OK != res) if (GNUNET_OK != res)
return (GNUNET_NO == res) ? MHD_YES : MHD_NO; return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
/* Check signature */ /* Check signature */
if (GNUNET_OK != if (GNUNET_OK !=
TALER_wallet_link_verify ( TALER_wallet_link_verify (
@ -467,6 +473,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
&rctx->gamma_tp, &rctx->gamma_tp,
&rrcs[i].coin_envelope_hash, &rrcs[i].coin_envelope_hash,
&rctx->melt.session.coin.coin_pub, &rctx->melt.session.coin.coin_pub,
NULL, // TODO-oec: calculate the correct h_age_commitment
&rrcs[i].orig_coin_link_sig)) &rrcs[i].orig_coin_link_sig))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
@ -489,6 +496,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
rcd->coin_ev_size = rrc->coin_ev_size; rcd->coin_ev_size = rrc->coin_ev_size;
rcd->dk = &dks[i]->denom_pub; rcd->dk = &dks[i]->denom_pub;
} }
rctx->dks = dks; rctx->dks = dks;
rctx->rcds = rcds; rctx->rcds = rcds;
if (GNUNET_OK != if (GNUNET_OK !=
@ -500,6 +508,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Creating %u signatures\n", "Creating %u signatures\n",
(unsigned int) rctx->num_fresh_coins); (unsigned int) rctx->num_fresh_coins);
/* create fresh coin signatures */ /* create fresh coin signatures */
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++) for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
{ {
@ -520,8 +529,10 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
goto cleanup; goto cleanup;
} }
} }
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Signatures ready, starting DB interaction\n"); "Signatures ready, starting DB interaction\n");
/* Persist operation result in DB */ /* Persist operation result in DB */
{ {
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
@ -577,11 +588,18 @@ cleanup:
* revealed information is valid then returns the signed refreshed * revealed information is valid then returns the signed refreshed
* coins. * 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 connection the MHD connection to handle
* @param rctx context for the operation, partially built at this time * @param rctx context for the operation, partially built at this time
* @param tp_json private transfer keys in JSON format * @param tp_json private transfer keys in JSON format
* @param link_sigs_json link signatures 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 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 * @param coin_evs envelopes of gamma-selected coins to be signed
* @return MHD result code * @return MHD result code
*/ */
@ -591,6 +609,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
const json_t *tp_json, const json_t *tp_json,
const json_t *link_sigs_json, const json_t *link_sigs_json,
const json_t *new_denoms_h_json, const json_t *new_denoms_h_json,
const json_t *old_age_commitment_json,
const json_t *coin_evs) const json_t *coin_evs)
{ {
unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
@ -626,6 +645,19 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
"new_denoms/link_sigs"); "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 */ /* Parse transfer private keys array */
for (unsigned int i = 0; i<num_tprivs; i++) for (unsigned int i = 0; i<num_tprivs; i++)
{ {
@ -649,6 +681,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
rctx, rctx,
link_sigs_json, link_sigs_json,
new_denoms_h_json, new_denoms_h_json,
old_age_commitment_json,
coin_evs); coin_evs);
} }
@ -662,6 +695,7 @@ TEH_handler_reveal (struct TEH_RequestContext *rc,
json_t *transfer_privs; json_t *transfer_privs;
json_t *link_sigs; json_t *link_sigs;
json_t *new_denoms_h; json_t *new_denoms_h;
json_t *old_age_commitment = NULL;
struct RevealContext rctx; struct RevealContext rctx;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("transfer_pub", GNUNET_JSON_spec_fixed_auto ("transfer_pub",
@ -674,6 +708,9 @@ TEH_handler_reveal (struct TEH_RequestContext *rc,
&coin_evs), &coin_evs),
GNUNET_JSON_spec_json ("new_denoms_h", GNUNET_JSON_spec_json ("new_denoms_h",
&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 () GNUNET_JSON_spec_end ()
}; };
@ -735,6 +772,7 @@ TEH_handler_reveal (struct TEH_RequestContext *rc,
transfer_privs, transfer_privs,
link_sigs, link_sigs,
new_denoms_h, new_denoms_h,
old_age_commitment,
coin_evs); coin_evs);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return res; return res;

View File

@ -73,6 +73,7 @@ TEH_RESPONSE_compile_transaction_history (
&deposit->deposit_fee, &deposit->deposit_fee,
&h_wire, &h_wire,
&deposit->h_contract_terms, &deposit->h_contract_terms,
NULL, /* h_age_commitment, FIXME-oec */
NULL /* h_extensions! */, NULL /* h_extensions! */,
&deposit->h_denom_pub, &deposit->h_denom_pub,
deposit->timestamp, deposit->timestamp,
@ -122,6 +123,7 @@ TEH_RESPONSE_compile_transaction_history (
{ {
const struct TALER_EXCHANGEDB_MeltListEntry *melt = const struct TALER_EXCHANGEDB_MeltListEntry *melt =
pos->details.melt; pos->details.melt;
const struct TALER_AgeCommitmentHash *phac = NULL;
#if ENABLE_SANITY_CHECKS #if ENABLE_SANITY_CHECKS
if (GNUNET_OK != if (GNUNET_OK !=
@ -129,6 +131,7 @@ TEH_RESPONSE_compile_transaction_history (
&melt->melt_fee, &melt->melt_fee,
&melt->rc, &melt->rc,
&melt->h_denom_pub, &melt->h_denom_pub,
&melt->h_age_commitment,
coin_pub, coin_pub,
&melt->coin_sig)) &melt->coin_sig))
{ {
@ -137,6 +140,12 @@ TEH_RESPONSE_compile_transaction_history (
return NULL; return NULL;
} }
#endif #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 != if (0 !=
json_array_append_new ( json_array_append_new (
history, history,
@ -151,6 +160,9 @@ TEH_RESPONSE_compile_transaction_history (
&melt->rc), &melt->rc),
GNUNET_JSON_pack_data_auto ("h_denom_pub", GNUNET_JSON_pack_data_auto ("h_denom_pub",
&melt->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", GNUNET_JSON_pack_data_auto ("coin_sig",
&melt->coin_sig)))) &melt->coin_sig))))
{ {

View File

@ -25,7 +25,7 @@ CREATE TABLE IF NOT EXISTS denominations
(denominations_serial BIGSERIAL UNIQUE (denominations_serial BIGSERIAL UNIQUE
,denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64) ,denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)
,denom_type INT4 NOT NULL DEFAULT (1) -- 1 == RSA (for now, remove default later!) ,denom_type INT4 NOT NULL DEFAULT (1) -- 1 == RSA (for now, remove default later!)
,age_restrictions INT4 NOT NULL DEFAULT (0) ,age_mask INT4 NOT NULL DEFAULT (0)
,denom_pub BYTEA NOT NULL ,denom_pub BYTEA NOT NULL
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64) ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
,valid_from INT8 NOT NULL ,valid_from INT8 NOT NULL
@ -47,7 +47,7 @@ COMMENT ON TABLE denominations
IS 'Main denominations table. All the valid denominations the exchange knows about.'; IS 'Main denominations table. All the valid denominations the exchange knows about.';
COMMENT ON COLUMN denominations.denom_type COMMENT ON COLUMN denominations.denom_type
IS 'determines cipher type for blind signatures used with this denomination; 0 is for RSA'; IS 'determines cipher type for blind signatures used with this denomination; 0 is for RSA';
COMMENT ON COLUMN denominations.age_restrictions COMMENT ON COLUMN denominations.age_mask
IS 'bitmask with the age restrictions that are being used for this denomination; 0 if denomination does not support the use of age restrictions'; IS 'bitmask with the age restrictions that are being used for this denomination; 0 if denomination does not support the use of age restrictions';
COMMENT ON COLUMN denominations.denominations_serial COMMENT ON COLUMN denominations.denominations_serial
IS 'needed for exchange-auditor replication logic'; IS 'needed for exchange-auditor replication logic';
@ -342,6 +342,7 @@ CREATE TABLE IF NOT EXISTS refresh_commitments
(melt_serial_id BIGSERIAL -- UNIQUE (melt_serial_id BIGSERIAL -- UNIQUE
,rc BYTEA PRIMARY KEY CHECK (LENGTH(rc)=64) ,rc BYTEA PRIMARY KEY CHECK (LENGTH(rc)=64)
,old_coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE ,old_coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE
,h_age_commitment BYTEA CHECK(LENGTH(h_age_commitment)=32)
,old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64) ,old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)
,amount_with_fee_val INT8 NOT NULL ,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL ,amount_with_fee_frac INT4 NOT NULL
@ -356,6 +357,8 @@ COMMENT ON COLUMN refresh_commitments.rc
IS 'Commitment made by the client, hash over the various client inputs in the cut-and-choose protocol'; IS 'Commitment made by the client, hash over the various client inputs in the cut-and-choose protocol';
COMMENT ON COLUMN refresh_commitments.old_coin_pub COMMENT ON COLUMN refresh_commitments.old_coin_pub
IS 'Coin being melted in the refresh process.'; IS 'Coin being melted in the refresh process.';
COMMENT ON COLUMN refresh_commitments.h_age_commitment
IS '(optional) age commitment that was involved in the minting process of the coin, may be NULL.';
CREATE TABLE IF NOT EXISTS refresh_commitments_default CREATE TABLE IF NOT EXISTS refresh_commitments_default
PARTITION OF refresh_commitments PARTITION OF refresh_commitments
FOR VALUES WITH (MODULUS 1, REMAINDER 0); FOR VALUES WITH (MODULUS 1, REMAINDER 0);

View File

@ -231,10 +231,11 @@ prepare_statements (struct PostgresClosure *pg)
",fee_refresh_frac" ",fee_refresh_frac"
",fee_refund_val" ",fee_refund_val"
",fee_refund_frac" ",fee_refund_frac"
",age_mask"
") VALUES " ") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10," "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
" $11, $12, $13, $14, $15, $16, $17);", " $11, $12, $13, $14, $15, $16, $17, $18);",
17), 18),
/* Used in #postgres_iterate_denomination_info() */ /* Used in #postgres_iterate_denomination_info() */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"denomination_iterate", "denomination_iterate",
@ -255,6 +256,7 @@ prepare_statements (struct PostgresClosure *pg)
",fee_refund_val" ",fee_refund_val"
",fee_refund_frac" ",fee_refund_frac"
",denom_pub" ",denom_pub"
",age_mask"
" FROM denominations;", " FROM denominations;",
0), 0),
/* Used in #postgres_iterate_denominations() */ /* Used in #postgres_iterate_denominations() */
@ -278,6 +280,7 @@ prepare_statements (struct PostgresClosure *pg)
",fee_refund_val" ",fee_refund_val"
",fee_refund_frac" ",fee_refund_frac"
",denom_pub" ",denom_pub"
",age_mask"
" FROM denominations" " FROM denominations"
" LEFT JOIN " " LEFT JOIN "
" denomination_revocations USING (denominations_serial);", " denomination_revocations USING (denominations_serial);",
@ -341,6 +344,7 @@ prepare_statements (struct PostgresClosure *pg)
",fee_refresh_frac" ",fee_refresh_frac"
",fee_refund_val" ",fee_refund_val"
",fee_refund_frac" ",fee_refund_frac"
",age_mask"
" FROM denominations" " FROM denominations"
" WHERE denom_pub_hash=$1;", " WHERE denom_pub_hash=$1;",
1), 1),
@ -830,6 +834,7 @@ prepare_statements (struct PostgresClosure *pg)
",denoms.fee_refresh_frac" ",denoms.fee_refresh_frac"
",old_coin_pub" ",old_coin_pub"
",old_coin_sig" ",old_coin_sig"
",h_age_commitment"
",amount_with_fee_val" ",amount_with_fee_val"
",amount_with_fee_frac" ",amount_with_fee_frac"
",noreveal_index" ",noreveal_index"
@ -848,6 +853,7 @@ prepare_statements (struct PostgresClosure *pg)
"SELECT" "SELECT"
" denom.denom_pub" " denom.denom_pub"
",kc.coin_pub AS old_coin_pub" ",kc.coin_pub AS old_coin_pub"
",h_age_commitment"
",old_coin_sig" ",old_coin_sig"
",amount_with_fee_val" ",amount_with_fee_val"
",amount_with_fee_frac" ",amount_with_fee_frac"
@ -1842,6 +1848,7 @@ prepare_statements (struct PostgresClosure *pg)
",fee_refresh_frac" ",fee_refresh_frac"
",fee_refund_val" ",fee_refund_val"
",fee_refund_frac" ",fee_refund_frac"
",age_mask"
" FROM denominations" " FROM denominations"
" WHERE denom_pub_hash=$1;", " WHERE denom_pub_hash=$1;",
1), 1),
@ -2069,7 +2076,6 @@ prepare_statements (struct PostgresClosure *pg)
"SELECT" "SELECT"
" denominations_serial AS serial" " denominations_serial AS serial"
",denom_type" ",denom_type"
",age_restrictions"
",denom_pub" ",denom_pub"
",master_sig" ",master_sig"
",valid_from" ",valid_from"
@ -2086,6 +2092,7 @@ prepare_statements (struct PostgresClosure *pg)
",fee_refresh_frac" ",fee_refresh_frac"
",fee_refund_val" ",fee_refund_val"
",fee_refund_frac" ",fee_refund_frac"
",age_mask"
" FROM denominations" " FROM denominations"
" WHERE denominations_serial > $1" " WHERE denominations_serial > $1"
" ORDER BY denominations_serial ASC;", " ORDER BY denominations_serial ASC;",
@ -2388,10 +2395,11 @@ prepare_statements (struct PostgresClosure *pg)
",fee_refresh_frac" ",fee_refresh_frac"
",fee_refund_val" ",fee_refund_val"
",fee_refund_frac" ",fee_refund_frac"
",age_mask"
") VALUES " ") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10," "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
" $11, $12, $13, $14, $15, $16, $17, $18);", " $11, $12, $13, $14, $15, $16, $17, $18, $19);",
18), 19),
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"insert_into_table_denomination_revocations", "insert_into_table_denomination_revocations",
"INSERT INTO denomination_revocations" "INSERT INTO denomination_revocations"
@ -3094,9 +3102,12 @@ postgres_insert_denomination_info (
TALER_PQ_query_param_amount_nbo (&issue->properties.fee_deposit), TALER_PQ_query_param_amount_nbo (&issue->properties.fee_deposit),
TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refresh), TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refresh),
TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refund), TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refund),
GNUNET_PQ_query_param_uint32 (&denom_pub->age_mask.mask),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
GNUNET_assert (denom_pub->age_mask.mask == issue->age_mask.mask);
GNUNET_assert (! GNUNET_TIME_absolute_is_zero ( GNUNET_assert (! GNUNET_TIME_absolute_is_zero (
GNUNET_TIME_timestamp_ntoh ( GNUNET_TIME_timestamp_ntoh (
issue->properties.start).abs_time)); issue->properties.start).abs_time));
@ -3170,6 +3181,8 @@ postgres_get_denomination_info (
&issue->properties.fee_refresh), &issue->properties.fee_refresh),
TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_refund", TALER_PQ_RESULT_SPEC_AMOUNT_NBO ("fee_refund",
&issue->properties.fee_refund), &issue->properties.fee_refund),
GNUNET_PQ_result_spec_uint32 ("age_mask",
&issue->age_mask.mask),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
@ -3256,12 +3269,15 @@ domination_cb_helper (void *cls,
&issue.properties.fee_refund), &issue.properties.fee_refund),
TALER_PQ_result_spec_denom_pub ("denom_pub", TALER_PQ_result_spec_denom_pub ("denom_pub",
&denom_pub), &denom_pub),
GNUNET_PQ_result_spec_uint32 ("age_mask",
&issue.age_mask.mask),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
memset (&issue.properties.master, memset (&issue.properties.master,
0, 0,
sizeof (issue.properties.master)); sizeof (issue.properties.master));
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_PQ_extract_result (result, GNUNET_PQ_extract_result (result,
rs, rs,
@ -3270,6 +3286,13 @@ domination_cb_helper (void *cls,
GNUNET_break (0); GNUNET_break (0);
return; return;
} }
/* Unfortunately we have to carry the age mask in both, the
* TALER_DenominationPublicKey and
* TALER_EXCHANGEDB_DenominationKeyInformationP at different times.
* Here we use _both_ so let's make sure the values are the same. */
denom_pub.age_mask = issue.age_mask;
issue.properties.purpose.size issue.properties.purpose.size
= htonl (sizeof (struct TALER_DenominationKeyValidityPS)); = htonl (sizeof (struct TALER_DenominationKeyValidityPS));
issue.properties.purpose.purpose issue.properties.purpose.purpose
@ -3355,10 +3378,10 @@ dominations_cb_helper (void *cls,
for (unsigned int i = 0; i<num_results; i++) for (unsigned int i = 0; i<num_results; i++)
{ {
struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; struct TALER_EXCHANGEDB_DenominationKeyMetaData meta = {0};
struct TALER_DenominationPublicKey denom_pub; struct TALER_DenominationPublicKey denom_pub = {0};
struct TALER_MasterSignatureP master_sig; struct TALER_MasterSignatureP master_sig = {0};
struct TALER_DenominationHash h_denom_pub; struct TALER_DenominationHash h_denom_pub = {0};
bool revoked; bool revoked;
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("master_sig", GNUNET_PQ_result_spec_auto_from_type ("master_sig",
@ -3385,6 +3408,8 @@ dominations_cb_helper (void *cls,
&meta.fee_refund), &meta.fee_refund),
TALER_PQ_result_spec_denom_pub ("denom_pub", TALER_PQ_result_spec_denom_pub ("denom_pub",
&denom_pub), &denom_pub),
GNUNET_PQ_result_spec_uint32 ("age_mask",
&meta.age_mask.mask),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
@ -3396,6 +3421,10 @@ dominations_cb_helper (void *cls,
GNUNET_break (0); GNUNET_break (0);
return; return;
} }
/* make sure the mask information is the same */
denom_pub.age_mask = meta.age_mask;
TALER_denom_pub_hash (&denom_pub, TALER_denom_pub_hash (&denom_pub,
&h_denom_pub); &h_denom_pub);
dic->cb (dic->cb_cls, dic->cb (dic->cb_cls,
@ -5730,11 +5759,13 @@ postgres_ensure_coin_known (void *cls,
const struct TALER_CoinPublicInfo *coin, const struct TALER_CoinPublicInfo *coin,
uint64_t *known_coin_id, uint64_t *known_coin_id,
struct TALER_DenominationHash *denom_hash, struct TALER_DenominationHash *denom_hash,
struct TALER_AgeHash *age_hash) struct TALER_AgeCommitmentHash *age_hash)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
bool existed; bool existed;
bool is_denom_pub_hash_null = false;
bool is_age_hash_null = false;
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub), GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
GNUNET_PQ_query_param_auto_from_type (&coin->denom_pub_hash), GNUNET_PQ_query_param_auto_from_type (&coin->denom_pub_hash),
@ -5742,24 +5773,22 @@ postgres_ensure_coin_known (void *cls,
TALER_PQ_query_param_denom_sig (&coin->denom_sig), TALER_PQ_query_param_denom_sig (&coin->denom_sig),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
bool is_null = false;
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("existed", GNUNET_PQ_result_spec_bool ("existed",
&existed), &existed),
GNUNET_PQ_result_spec_uint64 ("known_coin_id", GNUNET_PQ_result_spec_uint64 ("known_coin_id",
known_coin_id), known_coin_id),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_auto_from_type ("age_hash",
age_hash),
&is_null),
GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
denom_hash), denom_hash),
&is_null), &is_denom_pub_hash_null),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_auto_from_type ("age_hash",
age_hash),
&is_age_hash_null),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
GNUNET_break (GNUNET_is_zero (&coin->age_commitment_hash)); // FIXME-OEC
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"insert_known_coin", "insert_known_coin",
params, params,
@ -5779,21 +5808,24 @@ postgres_ensure_coin_known (void *cls,
return TALER_EXCHANGEDB_CKS_ADDED; return TALER_EXCHANGEDB_CKS_ADDED;
break; /* continued below */ break; /* continued below */
} }
if ( (! is_null) &&
(0 != GNUNET_memcmp (age_hash, if ( (! is_denom_pub_hash_null) &&
&coin->age_commitment_hash)) ) (0 != GNUNET_memcmp (&denom_hash->hash,
{ &coin->denom_pub_hash.hash)) )
GNUNET_break (GNUNET_is_zero (age_hash)); // FIXME-OEC
GNUNET_break_op (0);
return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
}
if ( (! is_null) &&
(0 != GNUNET_memcmp (denom_hash,
&coin->denom_pub_hash)) )
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT; return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
} }
if ( (! is_age_hash_null) &&
(0 != GNUNET_memcmp (age_hash,
&coin->age_commitment_hash)) )
{
GNUNET_break (GNUNET_is_zero (age_hash));
GNUNET_break_op (0);
return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
}
return TALER_EXCHANGEDB_CKS_PRESENT; return TALER_EXCHANGEDB_CKS_PRESENT;
} }
@ -6019,6 +6051,7 @@ postgres_get_melt (void *cls,
uint64_t *melt_serial_id) uint64_t *melt_serial_id)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
bool h_age_commitment_is_null;
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (rc), GNUNET_PQ_query_param_auto_from_type (rc),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
@ -6035,6 +6068,10 @@ postgres_get_melt (void *cls,
&melt->session.coin.coin_pub), &melt->session.coin.coin_pub),
GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig", GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
&melt->session.coin_sig), &melt->session.coin_sig),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_auto_from_type ("h_age_commitment",
&melt->session.h_age_commitment),
&h_age_commitment_is_null),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&melt->session.amount_with_fee), &melt->session.amount_with_fee),
GNUNET_PQ_result_spec_uint64 ("melt_serial_id", GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
@ -6050,6 +6087,11 @@ postgres_get_melt (void *cls,
"get_melt", "get_melt",
params, params,
rs); rs);
if (h_age_commitment_is_null)
memset (&melt->session.h_age_commitment,
0,
sizeof(melt->session.h_age_commitment));
melt->session.rc = *rc; melt->session.rc = *rc;
return qs; return qs;
} }
@ -8202,6 +8244,8 @@ refreshs_serial_helper_cb (void *cls,
struct TALER_DenominationPublicKey denom_pub; struct TALER_DenominationPublicKey denom_pub;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_CoinSpendSignatureP coin_sig; struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_AgeCommitmentHash h_age_commitment;
bool ac_isnull;
struct TALER_Amount amount_with_fee; struct TALER_Amount amount_with_fee;
uint32_t noreveal_index; uint32_t noreveal_index;
uint64_t rowid; uint64_t rowid;
@ -8209,6 +8253,10 @@ refreshs_serial_helper_cb (void *cls,
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_denom_pub ("denom_pub", TALER_PQ_result_spec_denom_pub ("denom_pub",
&denom_pub), &denom_pub),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_auto_from_type ("h_age_commitment",
&h_age_commitment),
&ac_isnull),
GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub", GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
&coin_pub), &coin_pub),
GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig", GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
@ -8234,9 +8282,11 @@ refreshs_serial_helper_cb (void *cls,
rsc->status = GNUNET_SYSERR; rsc->status = GNUNET_SYSERR;
return; return;
} }
ret = rsc->cb (rsc->cb_cls, ret = rsc->cb (rsc->cb_cls,
rowid, rowid,
&denom_pub, &denom_pub,
ac_isnull ? NULL : &h_age_commitment,
&coin_pub, &coin_pub,
&coin_sig, &coin_sig,
&amount_with_fee, &amount_with_fee,
@ -10173,6 +10223,8 @@ postgres_lookup_denomination_key (
&meta->fee_refresh), &meta->fee_refresh),
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund", TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
&meta->fee_refund), &meta->fee_refund),
GNUNET_PQ_result_spec_uint32 ("age_mask",
&meta->age_mask.mask),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
@ -10216,6 +10268,7 @@ postgres_add_denomination_key (
TALER_PQ_query_param_amount (&meta->fee_deposit), TALER_PQ_query_param_amount (&meta->fee_deposit),
TALER_PQ_query_param_amount (&meta->fee_refresh), TALER_PQ_query_param_amount (&meta->fee_refresh),
TALER_PQ_query_param_amount (&meta->fee_refund), TALER_PQ_query_param_amount (&meta->fee_refund),
GNUNET_PQ_query_param_uint32 (&meta->age_mask.mask),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };

View File

@ -459,6 +459,7 @@ static unsigned int auditor_row_cnt;
* @param cls closure * @param cls closure
* @param rowid unique serial ID for the refresh session in our DB * @param rowid unique serial ID for the refresh session in our DB
* @param denom_pub denomination of the @a coin_pub * @param denom_pub denomination of the @a coin_pub
* @param h_age_commitment hash of age commitment that went into the minting, may be NULL
* @param coin_pub public key of the coin * @param coin_pub public key of the coin
* @param coin_sig signature from the coin * @param coin_sig signature from the coin
* @param amount_with_fee amount that was deposited including fee * @param amount_with_fee amount that was deposited including fee
@ -471,6 +472,8 @@ static enum GNUNET_GenericReturnValue
audit_refresh_session_cb (void *cls, audit_refresh_session_cb (void *cls,
uint64_t rowid, uint64_t rowid,
const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_DenominationPublicKey *denom_pub,
const struct
TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *amount_with_fee,
@ -1467,8 +1470,8 @@ run (void *cls)
{ {
struct TALER_PlanchetDetail pd; struct TALER_PlanchetDetail pd;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_AgeHash age_hash; struct TALER_AgeCommitmentHash age_hash;
struct TALER_AgeHash *p_ah[2] = {NULL, &age_hash}; struct TALER_AgeCommitmentHash *p_ah[2] = {NULL, &age_hash};
/* Call TALER_denom_blind()/TALER_denom_sign_blinded() twice, once without /* Call TALER_denom_blind()/TALER_denom_sign_blinded() twice, once without
* age_hash, once with age_hash */ * age_hash, once with age_hash */
@ -1576,7 +1579,7 @@ run (void *cls)
deadline = GNUNET_TIME_timestamp_get (); deadline = GNUNET_TIME_timestamp_get ();
{ {
struct TALER_DenominationHash dph; struct TALER_DenominationHash dph;
struct TALER_AgeHash agh; struct TALER_AgeCommitmentHash agh;
FAILIF (TALER_EXCHANGEDB_CKS_ADDED != FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
plugin->ensure_coin_known (plugin->cls, plugin->ensure_coin_known (plugin->cls,
@ -1819,7 +1822,7 @@ run (void *cls)
uint64_t new_known_coin_id; uint64_t new_known_coin_id;
struct TALER_CoinPublicInfo new_coin; struct TALER_CoinPublicInfo new_coin;
struct TALER_DenominationHash dph; struct TALER_DenominationHash dph;
struct TALER_AgeHash agh; struct TALER_AgeCommitmentHash agh;
bool recoup_ok; bool recoup_ok;
bool internal_failure; bool internal_failure;
@ -2171,7 +2174,7 @@ run (void *cls)
{ {
uint64_t known_coin_id; uint64_t known_coin_id;
struct TALER_DenominationHash dph; struct TALER_DenominationHash dph;
struct TALER_AgeHash agh; struct TALER_AgeCommitmentHash agh;
FAILIF (TALER_EXCHANGEDB_CKS_ADDED != FAILIF (TALER_EXCHANGEDB_CKS_ADDED !=
plugin->ensure_coin_known (plugin->cls, plugin->ensure_coin_known (plugin->cls,

View File

@ -23,6 +23,19 @@
#include "taler_extensions.h" #include "taler_extensions.h"
#include "stdint.h" #include "stdint.h"
/**
* Carries all the information we need for age restriction
*/
struct age_restriction_config
{
struct TALER_AgeMask mask;
size_t num_groups;
};
/**
* Global config for this extension
*/
static struct age_restriction_config _config = {0};
/** /**
* @param groups String representation of the age groups. Must be of the form * @param groups String representation of the age groups. Must be of the form
@ -146,6 +159,9 @@ age_restriction_disable (
json_decref (this->config_json); json_decref (this->config_json);
this->config_json = NULL; this->config_json = NULL;
} }
_config.mask.mask = 0;
_config.num_groups = 0;
} }
@ -197,7 +213,6 @@ age_restriction_load_taler_config (
mask.mask = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK; mask.mask = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
ret = GNUNET_OK; ret = GNUNET_OK;
if (groups != NULL) if (groups != NULL)
@ -208,7 +223,19 @@ age_restriction_load_taler_config (
} }
if (GNUNET_OK == ret) if (GNUNET_OK == ret)
this->config = (void *) (size_t) mask.mask; {
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"setting age mask to %x with #groups: %d\n", mask.mask,
__builtin_popcount (mask.mask) - 1);
_config.mask.mask = mask.mask;
_config.num_groups = __builtin_popcount (mask.mask) - 1; /* no underflow, first bit always set */
this->config = &_config;
/* Note: we do now have _config set, however this->config_json is NOT set,
* i.e. the extension is not yet active! For age restriction to become
* active, load_json_config must have been called. */
}
GNUNET_free (groups); GNUNET_free (groups);
return ret; return ret;
@ -223,12 +250,12 @@ age_restriction_load_taler_config (
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
age_restriction_load_json_config ( age_restriction_load_json_config (
struct TALER_Extension *this, struct TALER_Extension *this,
json_t *config) json_t *jconfig)
{ {
struct TALER_AgeMask mask = {0}; struct TALER_AgeMask mask = {0};
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
ret = TALER_JSON_parse_agemask (config, &mask); ret = TALER_JSON_parse_age_groups (jconfig, &mask);
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
return ret; return ret;
@ -239,16 +266,28 @@ age_restriction_load_json_config (
if (TALER_Extension_AgeRestriction != this->type) if (TALER_Extension_AgeRestriction != this->type)
return GNUNET_SYSERR; return GNUNET_SYSERR;
if (NULL != this->config) _config.mask.mask = mask.mask;
GNUNET_free (this->config); _config.num_groups = 0;
this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask)); if (mask.mask > 0)
GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask)); {
/* if the mask is not zero, the first bit MUST be set */
if (0 == (mask.mask & 1))
return GNUNET_SYSERR;
_config.num_groups = __builtin_popcount (mask.mask) - 1;
}
this->config = &_config;
if (NULL != this->config_json) if (NULL != this->config_json)
json_decref (this->config_json); json_decref (this->config_json);
this->config_json = config; this->config_json = jconfig;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"loaded new age restriction config with age groups: %s\n",
TALER_age_mask_to_string (&mask));
return GNUNET_OK; return GNUNET_OK;
} }
@ -263,7 +302,6 @@ json_t *
age_restriction_config_to_json ( age_restriction_config_to_json (
const struct TALER_Extension *this) const struct TALER_Extension *this)
{ {
struct TALER_AgeMask mask;
char *mask_str; char *mask_str;
json_t *conf; json_t *conf;
@ -275,8 +313,7 @@ age_restriction_config_to_json (
return json_copy (this->config_json); return json_copy (this->config_json);
} }
mask.mask = (uint32_t) (size_t) this->config; mask_str = TALER_age_mask_to_string (&_config.mask);
mask_str = TALER_age_mask_to_string (&mask);
conf = GNUNET_JSON_PACK ( conf = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("age_groups", mask_str) GNUNET_JSON_pack_string ("age_groups", mask_str)
); );
@ -298,7 +335,7 @@ age_restriction_test_json_config (
{ {
struct TALER_AgeMask mask = {0}; struct TALER_AgeMask mask = {0};
return TALER_JSON_parse_agemask (config, &mask); return TALER_JSON_parse_age_groups (config, &mask);
} }
@ -318,4 +355,50 @@ struct TALER_Extension _extension_age_restriction = {
.load_taler_config = &age_restriction_load_taler_config, .load_taler_config = &age_restriction_load_taler_config,
}; };
bool
TALER_extensions_age_restriction_is_configured ()
{
return (0 != _config.mask.mask);
}
struct TALER_AgeMask
TALER_extensions_age_restriction_ageMask ()
{
return _config.mask;
}
size_t
TALER_extensions_age_restriction_num_groups ()
{
return _config.num_groups;
}
enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask)
{
enum GNUNET_GenericReturnValue ret;
const char *str;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("age_groups",
&str),
GNUNET_JSON_spec_end ()
};
ret = GNUNET_JSON_parse (root,
spec,
NULL,
NULL);
if (GNUNET_OK == ret)
TALER_parse_age_group_string (str, mask);
GNUNET_JSON_parse_free (spec);
return ret;
}
/* end of extension_age_restriction.c */ /* end of extension_age_restriction.c */

View File

@ -247,27 +247,31 @@ TALER_extensions_load_taler_config (
} }
static enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
is_json_extension_config ( TALER_extensions_is_json_config (
json_t *obj, json_t *obj,
int *critical, int *critical,
const char **version, const char **version,
json_t **config) json_t **config)
{ {
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
json_t *cfg;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_boolean ("critical", GNUNET_JSON_spec_boolean ("critical",
critical), critical),
GNUNET_JSON_spec_string ("version", GNUNET_JSON_spec_string ("version",
version), version),
GNUNET_JSON_spec_json ("config", GNUNET_JSON_spec_json ("config",
config), &cfg),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
ret = GNUNET_JSON_parse (obj, spec, NULL, NULL); ret = GNUNET_JSON_parse (obj, spec, NULL, NULL);
if (GNUNET_OK == ret) if (GNUNET_OK == ret)
{
*config = json_copy (cfg);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
}
return ret; return ret;
} }
@ -300,7 +304,7 @@ TALER_extensions_load_json_config (
/* load and verify criticality, version, etc. */ /* load and verify criticality, version, etc. */
if (GNUNET_OK != if (GNUNET_OK !=
is_json_extension_config ( TALER_extensions_is_json_config (
blob, &critical, &version, &config)) blob, &critical, &version, &config))
return GNUNET_SYSERR; return GNUNET_SYSERR;
@ -330,4 +334,16 @@ TALER_extensions_load_json_config (
} }
bool
TALER_extensions_age_restriction_is_enabled ()
{
const struct TALER_Extension *age =
TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
return (NULL != age &&
NULL != age->config_json &&
TALER_extensions_age_restriction_is_configured ());
}
/* end of extensions.c */ /* end of extensions.c */

View File

@ -280,37 +280,6 @@ struct TALER_MasterSignatureP
struct GNUNET_CRYPTO_EddsaSignature eddsa_signature; struct GNUNET_CRYPTO_EddsaSignature eddsa_signature;
}; };
/*
* @brief Type of a list of age groups, represented as bit mask.
*
* The bits set in the mask mark the edges at the beginning of a next age
* group. F.e. for the age groups
* 0-7, 8-9, 10-11, 12-14, 14-15, 16-17, 18-21, 21-*
* the following bits are set:
*
* 31 24 16 8 0
* | | | | |
* oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
*
* A value of 0 means that the exchange does not support the extension for
* age-restriction.
*/
struct TALER_AgeMask
{
uint32_t mask;
};
/**
* @brief Age restriction commitment of a coin.
*/
struct TALER_AgeHash
{
/**
* The commitment is a SHA-256 hash code.
*/
struct GNUNET_ShortHashCode shash;
};
/** /**
* @brief Type of public keys for Taler coins. The same key material is used * @brief Type of public keys for Taler coins. The same key material is used
@ -338,6 +307,29 @@ struct TALER_CoinSpendPrivateKeyP
struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv; struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
}; };
/**
* @brief Type of private keys for age commitment in coins.
*/
struct TALER_AgeCommitmentPrivateKeyP
{
/**
* Taler uses EdDSA for coins when signing age verification attestation.
*/
struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
};
/**
* @brief Type of public keys for age commitment in coins.
*/
struct TALER_AgeCommitmentPublicKeyP
{
/**
* Taler uses EdDSA for coins when signing age verification attestation.
*/
struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
};
/** /**
* @brief Type of signatures made with Taler coins. * @brief Type of signatures made with Taler coins.
@ -635,6 +627,46 @@ struct TALER_BlindedDenominationSignature
}; };
/* *************** Age Restriction *********************************** */
/*
* @brief Type of a list of age groups, represented as bit mask.
*
* The bits set in the mask mark the edges at the beginning of a next age
* group. F.e. for the age groups
* 0-7, 8-9, 10-11, 12-14, 14-15, 16-17, 18-21, 21-*
* the following bits are set:
*
* 31 24 16 8 0
* | | | | |
* oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
*
* A value of 0 means that the exchange does not support the extension for
* age-restriction.
*/
struct TALER_AgeMask
{
uint32_t mask;
};
/**
* @brief Age commitment of a coin.
*/
struct TALER_AgeCommitmentHash
{
/**
* The commitment is a SHA-256 hash code.
*/
struct GNUNET_ShortHashCode shash;
};
extern const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash;
#define TALER_AgeCommitmentHash_isNullOrZero(ph) ((NULL == ph) || \
(0 == memcmp (ph, \
& \
TALER_ZeroAgeCommitmentHash, \
sizeof(struct \
TALER_AgeCommitmentHash))))
/** /**
* @brief Type of public signing keys for verifying blindly signed coins. * @brief Type of public signing keys for verifying blindly signed coins.
@ -712,9 +744,10 @@ struct TALER_CoinPublicInfo
struct TALER_DenominationHash denom_pub_hash; struct TALER_DenominationHash denom_pub_hash;
/** /**
* Hash of the age commitment. * Hash of the age commitment. If no age commitment was provided, it must be
* set to all zeroes.
*/ */
struct TALER_AgeHash age_commitment_hash; struct TALER_AgeCommitmentHash age_commitment_hash;
/** /**
* (Unblinded) signature over @e coin_pub with @e denom_pub, * (Unblinded) signature over @e coin_pub with @e denom_pub,
@ -824,7 +857,7 @@ TALER_denom_sig_free (struct TALER_DenominationSignature *denom_sig);
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_denom_blind (const struct TALER_DenominationPublicKey *dk, TALER_denom_blind (const struct TALER_DenominationPublicKey *dk,
const union TALER_DenominationBlindingKeyP *coin_bks, const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_AgeHash *age_commitment_hash, const struct TALER_AgeCommitmentHash *age_commitment_hash,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_CoinPubHash *c_hash, struct TALER_CoinPubHash *c_hash,
void **coin_ev, void **coin_ev,
@ -1024,7 +1057,7 @@ TALER_coin_ev_hash (const void *coin_ev,
*/ */
void void
TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub, TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_AgeHash *age_commitment_hash, const struct TALER_AgeCommitmentHash *age_commitment_hash,
struct TALER_CoinPubHash *coin_h); struct TALER_CoinPubHash *coin_h);
@ -1110,8 +1143,9 @@ struct TALER_FreshCoin
struct TALER_CoinSpendPrivateKeyP coin_priv; struct TALER_CoinSpendPrivateKeyP coin_priv;
/** /**
* FIXME-Oec: Age-verification vector, as pointer: Dyn alloc! * Optional hash of an age commitment bound to this coin, maybe NULL.
*/ */
const struct TALER_AgeCommitmentHash *h_age_commitment;
}; };
@ -1232,6 +1266,7 @@ TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps);
* *
* @param dk denomination key for the coin to be created * @param dk denomination key for the coin to be created
* @param ps secret planchet internals (for #TALER_planchet_to_coin) * @param ps secret planchet internals (for #TALER_planchet_to_coin)
* @param ach (optional) hash of age commitment to bind to this coin, maybe NULL
* @param[out] c_hash set to the hash of the public key of the coin (needed later) * @param[out] c_hash set to the hash of the public key of the coin (needed later)
* @param[out] pd set to the planchet detail for TALER_MERCHANT_tip_pickup() and * @param[out] pd set to the planchet detail for TALER_MERCHANT_tip_pickup() and
* other withdraw operations * other withdraw operations
@ -1240,6 +1275,7 @@ TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps);
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk, TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
const struct TALER_PlanchetSecretsP *ps, const struct TALER_PlanchetSecretsP *ps,
const struct TALER_AgeCommitmentHash *ach,
struct TALER_CoinPubHash *c_hash, struct TALER_CoinPubHash *c_hash,
struct TALER_PlanchetDetail *pd); struct TALER_PlanchetDetail *pd);
@ -1251,6 +1287,7 @@ TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
* @param dk denomination key, must match what was given to #TALER_planchet_prepare() * @param dk denomination key, must match what was given to #TALER_planchet_prepare()
* @param blind_sig blind signature from the exchange * @param blind_sig blind signature from the exchange
* @param ps secrets from #TALER_planchet_prepare() * @param ps secrets from #TALER_planchet_prepare()
* @param ach (optional) hash of age commitment that is bound to this coin, maybe NULL
* @param c_hash hash of the coin's public key for verification of the signature * @param c_hash hash of the coin's public key for verification of the signature
* @param[out] coin set to the details of the fresh coin * @param[out] coin set to the details of the fresh coin
* @return #GNUNET_OK on success * @return #GNUNET_OK on success
@ -1260,6 +1297,7 @@ TALER_planchet_to_coin (
const struct TALER_DenominationPublicKey *dk, const struct TALER_DenominationPublicKey *dk,
const struct TALER_BlindedDenominationSignature *blind_sig, const struct TALER_BlindedDenominationSignature *blind_sig,
const struct TALER_PlanchetSecretsP *ps, const struct TALER_PlanchetSecretsP *ps,
const struct TALER_AgeCommitmentHash *ach,
const struct TALER_CoinPubHash *c_hash, const struct TALER_CoinPubHash *c_hash,
struct TALER_FreshCoin *coin); struct TALER_FreshCoin *coin);
@ -1682,6 +1720,7 @@ TALER_exchange_deposit_confirm_verify (
* @param deposit_fee the deposit fee we expect to pay * @param deposit_fee the deposit fee we expect to pay
* @param h_wire hash of the merchants account details * @param h_wire hash of the merchants account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange) * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_age_commitment hash over the age commitment, if applicable to the denomination (maybe NULL)
* @param h_extensions hash over the extensions * @param h_extensions hash over the extensions
* @param h_denom_pub hash of the coin denomination's public key * @param h_denom_pub hash of the coin denomination's public key
* @param coin_priv coins private key * @param coin_priv coins private key
@ -1696,6 +1735,7 @@ TALER_wallet_deposit_sign (
const struct TALER_Amount *deposit_fee, const struct TALER_Amount *deposit_fee,
const struct TALER_MerchantWireHash *h_wire, const struct TALER_MerchantWireHash *h_wire,
const struct TALER_PrivateContractHash *h_contract_terms, const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_ExtensionContractHash *h_extensions, const struct TALER_ExtensionContractHash *h_extensions,
const struct TALER_DenominationHash *h_denom_pub, const struct TALER_DenominationHash *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp, struct GNUNET_TIME_Timestamp wallet_timestamp,
@ -1712,6 +1752,7 @@ TALER_wallet_deposit_sign (
* @param deposit_fee the deposit fee we expect to pay * @param deposit_fee the deposit fee we expect to pay
* @param h_wire hash of the merchants account details * @param h_wire hash of the merchants account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange) * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_age_commitment hash over the age commitment (maybe all zeroes, if not applicable to the denomination)
* @param h_extensions hash over the extensions * @param h_extensions hash over the extensions
* @param h_denom_pub hash of the coin denomination's public key * @param h_denom_pub hash of the coin denomination's public key
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future * @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
@ -1727,6 +1768,7 @@ TALER_wallet_deposit_verify (
const struct TALER_Amount *deposit_fee, const struct TALER_Amount *deposit_fee,
const struct TALER_MerchantWireHash *h_wire, const struct TALER_MerchantWireHash *h_wire,
const struct TALER_PrivateContractHash *h_contract_terms, const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_commitment_hash,
const struct TALER_ExtensionContractHash *h_extensions, const struct TALER_ExtensionContractHash *h_extensions,
const struct TALER_DenominationHash *h_denom_pub, const struct TALER_DenominationHash *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp, struct GNUNET_TIME_Timestamp wallet_timestamp,
@ -1763,6 +1805,7 @@ TALER_wallet_melt_sign (
* @param melt_fee the melt fee we expect to pay * @param melt_fee the melt fee we expect to pay
* @param rc refresh session we are committed to * @param rc refresh session we are committed to
* @param h_denom_pub hash of the coin denomination's public key * @param h_denom_pub hash of the coin denomination's public key
* @param h_age_commitment hash of the age commitment (may be NULL)
* @param coin_pub coins public key * @param coin_pub coins public key
* @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_MELT * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_MELT
* @return #GNUNET_OK if the signature is valid * @return #GNUNET_OK if the signature is valid
@ -1773,6 +1816,7 @@ TALER_wallet_melt_verify (
const struct TALER_Amount *melt_fee, const struct TALER_Amount *melt_fee,
const struct TALER_RefreshCommitmentP *rc, const struct TALER_RefreshCommitmentP *rc,
const struct TALER_DenominationHash *h_denom_pub, const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig); const struct TALER_CoinSpendSignatureP *coin_sig);
@ -1803,6 +1847,7 @@ TALER_wallet_link_sign (const struct TALER_DenominationHash *h_denom_pub,
* @param transfer_pub transfer public key * @param transfer_pub transfer public key
* @param h_coin_ev hash of the coin envelope * @param h_coin_ev hash of the coin envelope
* @param old_coin_pub old coin key that the link signature is for * @param old_coin_pub old coin key that the link signature is for
* @param h_age_commitment hash of age commitment. Maybe NULL, if not applicable.
* @param coin_sig resulting signature * @param coin_sig resulting signature
* @return #GNUNET_OK if the signature is valid * @return #GNUNET_OK if the signature is valid
*/ */
@ -1812,6 +1857,7 @@ TALER_wallet_link_verify (
const struct TALER_TransferPublicKeyP *transfer_pub, const struct TALER_TransferPublicKeyP *transfer_pub,
const struct TALER_BlindedCoinHash *h_coin_ev, const struct TALER_BlindedCoinHash *h_coin_ev,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub, const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendSignatureP *coin_sig); const struct TALER_CoinSpendSignatureP *coin_sig);
@ -2557,5 +2603,100 @@ TALER_exchange_offline_extension_config_hash_verify (
const struct TALER_MasterSignatureP *master_sig const struct TALER_MasterSignatureP *master_sig
); );
/*
* @brief Representation of an age commitment: one public key per age group.
*
* The number of keys must be be the same as the number of bits set in the
* corresponding age mask.
*/
struct TALER_AgeCommitment
{
/* The age mask defines the age groups that were a parameter during the
* generation of this age commitment */
struct TALER_AgeMask mask;
/* The number of public keys, which must be the same as the number of
* groups in the mask.
*/
size_t num_pub;
/* The list of #num_pub public keys. In must have same size as the number of
* age groups defined in the mask.
*
* A hash of this list is the hashed commitment that goes into FDC
* calculation during the withdraw and refresh operations for new coins. That
* way, the particular age commitment becomes mandatory and bound to a coin.
*
* The list has been allocated via GNUNET_malloc.
*/
struct TALER_AgeCommitmentPublicKeyP *pub;
/* The number of private keys, which must be at most num_pub_keys. One minus
* this number corresponds to the largest age group that is supported with
* this age commitment.
*/
size_t num_priv;
/* List of #num_priv private keys.
*
* Note that the list can be _smaller_ than the corresponding list of public
* keys. In that case, the wallet can sign off only for a subset of the age
* groups.
*
* The list has been allocated via GNUNET_malloc.
*/
struct TALER_AgeCommitmentPrivateKeyP *priv;
};
/*
* @brief Generates a hash of the public keys in the age commitment.
*
* @param commitment the age commitment - one public key per age group
* @param[out] hash resulting hash
*/
void
TALER_age_commitment_hash (
const struct TALER_AgeCommitment *commitment,
struct TALER_AgeCommitmentHash *hash);
/*
* @brief Generates an age commitent for the given age.
*
* @param mask The age mask the defines the age groups
* @param age The actual age for which an age commitment is generated
* @param seed The seed that goes into the key generation. MUST be choosen uniformly random.
* @param commitment[out] The generated age commitment, ->priv and ->pub allocated via GNUNET_malloc on success
* @return GNUNET_OK on success, GNUNET_SYSERR otherwise
*/
enum GNUNET_GenericReturnValue
TALER_age_restriction_commit (
const struct TALER_AgeMask *mask,
const uint8_t age,
const uint32_t seed,
struct TALER_AgeCommitment *commitment);
/*
* @brief Derives another, equivalent age commitment for a given one.
*
* @param orig Original age commitment
* @param seed Used to move the points on the elliptic curve in order to generate another, equivalent commitment.
* @param derived[out] The resulting age commitment, ->priv and ->pub allocated via GNUNET_malloc on success.
* @return GNUNET_OK on success, GNUNET_SYSERR otherwise
*/
enum GNUNET_GenericReturnValue
TALER_age_commitment_derive (
const struct TALER_AgeCommitment *orig,
const uint32_t seed,
struct TALER_AgeCommitment *derived);
/*
* @brief helper function to free memory inside a struct TALER_AgeCommitment
* @param cmt the commitment from which internal memory should be freed. Note
* that cmt itself is NOT freed!
*/
void
TALER_age_restriction_commitment_free_inside (
struct TALER_AgeCommitment *cmt);
#endif #endif

View File

@ -159,11 +159,6 @@ struct TALER_EXCHANGE_DenomPublicKey
* revoked by the exchange. * revoked by the exchange.
*/ */
bool revoked; bool revoked;
/**
* Is the denomination age-restricted?
*/
bool age_restricted;
}; };
@ -785,6 +780,7 @@ TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh);
* @param h_extensions hash over the extensions * @param h_extensions hash over the extensions
* @param h_denom_pub hash of the coin denomination's public key * @param h_denom_pub hash of the coin denomination's public key
* @param coin_priv coins private key * @param coin_priv coins private key
* @param age_commitment age commitment that went into the making of the coin, might be NULL
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future * @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
* @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests) * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
* @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline * @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline
@ -799,6 +795,7 @@ TALER_EXCHANGE_deposit_permission_sign (
const struct TALER_ExtensionContractHash *h_extensions, const struct TALER_ExtensionContractHash *h_extensions,
const struct TALER_DenominationHash *h_denom_pub, const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_CoinSpendPrivateKeyP *coin_priv, const struct TALER_CoinSpendPrivateKeyP *coin_priv,
const struct TALER_AgeCommitment *age_commitment,
struct GNUNET_TIME_Timestamp wallet_timestamp, struct GNUNET_TIME_Timestamp wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
struct GNUNET_TIME_Timestamp refund_deadline, struct GNUNET_TIME_Timestamp refund_deadline,
@ -924,6 +921,7 @@ TALER_EXCHANGE_deposit (
const char *merchant_payto_uri, const char *merchant_payto_uri,
const struct TALER_WireSalt *wire_salt, const struct TALER_WireSalt *wire_salt,
const struct TALER_PrivateContractHash *h_contract_terms, const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const json_t *extension_details, const json_t *extension_details,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_DenominationSignature *denom_sig, const struct TALER_DenominationSignature *denom_sig,
@ -1359,6 +1357,7 @@ typedef void
* @param reserve_priv private key of the reserve to withdraw from * @param reserve_priv private key of the reserve to withdraw from
* @param ps secrets of the planchet * @param ps secrets of the planchet
* caller must have committed this value to disk before the call (with @a pk) * caller must have committed this value to disk before the call (with @a pk)
* @param ach (optional) hash of the age commitment that should be bound to this coin. Maybe NULL.
* @param res_cb the callback to call when the final result for this request is available * @param res_cb the callback to call when the final result for this request is available
* @param res_cb_cls closure for @a res_cb * @param res_cb_cls closure for @a res_cb
* @return NULL * @return NULL
@ -1371,6 +1370,7 @@ TALER_EXCHANGE_withdraw (
const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_ReservePrivateKeyP *reserve_priv, const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_PlanchetSecretsP *ps, const struct TALER_PlanchetSecretsP *ps,
const struct TALER_AgeCommitmentHash *ach,
TALER_EXCHANGE_WithdrawCallback res_cb, TALER_EXCHANGE_WithdrawCallback res_cb,
void *res_cb_cls); void *res_cb_cls);
@ -1479,6 +1479,8 @@ TALER_EXCHANGE_withdraw2_cancel (struct TALER_EXCHANGE_Withdraw2Handle *wh);
* @param melt_pk denomination key information * @param melt_pk denomination key information
* record corresponding to the @a melt_sig * record corresponding to the @a melt_sig
* validity of the keys * validity of the keys
* @param age_commitment (optional) age commitment that went into the original
* coin. Maybe NULL, if no age commitment was provided.
* @param fresh_pks_len length of the @a pks array * @param fresh_pks_len length of the @a pks array
* @param fresh_pks array of @a pks_len denominations of fresh coins to create * @param fresh_pks array of @a pks_len denominations of fresh coins to create
* @return NULL * @return NULL
@ -1493,6 +1495,7 @@ TALER_EXCHANGE_refresh_prepare (
const struct TALER_Amount *melt_amount, const struct TALER_Amount *melt_amount,
const struct TALER_DenominationSignature *melt_sig, const struct TALER_DenominationSignature *melt_sig,
const struct TALER_EXCHANGE_DenomPublicKey *melt_pk, const struct TALER_EXCHANGE_DenomPublicKey *melt_pk,
const struct TALER_AgeCommitment *age_commitment,
unsigned int fresh_pks_len, unsigned int fresh_pks_len,
const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks); const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks);

View File

@ -70,6 +70,12 @@ struct TALER_EXCHANGEDB_DenominationKeyInformationP
* Signed properties of the denomination key. * Signed properties of the denomination key.
*/ */
struct TALER_DenominationKeyValidityPS properties; struct TALER_DenominationKeyValidityPS properties;
/**
* If denomination was setup for age restriction, non-zero age mask.
* Note that the mask is not part of the signature.
*/
struct TALER_AgeMask age_mask;
}; };
@ -295,7 +301,7 @@ struct TALER_EXCHANGEDB_TableData
struct struct
{ {
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_AgeHash age_hash; struct TALER_AgeCommitmentHash age_hash;
uint64_t denominations_serial; uint64_t denominations_serial;
struct TALER_DenominationSignature denom_sig; struct TALER_DenominationSignature denom_sig;
} known_coins; } known_coins;
@ -643,7 +649,7 @@ struct TALER_EXCHANGEDB_DenominationKeyMetaData
* A value of 0 means that the denomination does not support the extension for * A value of 0 means that the denomination does not support the extension for
* age-restriction. * age-restriction.
*/ */
struct TALER_AgeMask age_restrictions; struct TALER_AgeMask age_mask;
}; };
@ -1260,6 +1266,13 @@ struct TALER_EXCHANGEDB_Refresh
*/ */
struct TALER_CoinSpendSignatureP coin_sig; struct TALER_CoinSpendSignatureP coin_sig;
/**
* Hash of the age commitment used to sign the coin, if age restriction was
* applicable to the denomination. May be all zeroes if no age restriction
* applies.
*/
struct TALER_AgeCommitmentHash h_age_commitment;
/** /**
* Refresh commitment this coin is melted into. * Refresh commitment this coin is melted into.
*/ */
@ -1305,6 +1318,13 @@ struct TALER_EXCHANGEDB_MeltListEntry
*/ */
struct TALER_DenominationHash h_denom_pub; struct TALER_DenominationHash h_denom_pub;
/**
* Hash of the age commitment used to sign the coin, if age restriction was
* applicable to the denomination. May be all zeroes if no age restriction
* applies.
*/
struct TALER_AgeCommitmentHash h_age_commitment;
/** /**
* How much value is being melted? This amount includes the fees, * How much value is being melted? This amount includes the fees,
* so the final amount contributed to the melt is this value minus * so the final amount contributed to the melt is this value minus
@ -1585,6 +1605,7 @@ typedef enum GNUNET_GenericReturnValue
* @param cls closure * @param cls closure
* @param rowid unique serial ID for the refresh session in our DB * @param rowid unique serial ID for the refresh session in our DB
* @param denom_pub denomination public key of @a coin_pub * @param denom_pub denomination public key of @a coin_pub
* @param h_age_commitment age commitment that went into the signing of the coin, may be NULL
* @param coin_pub public key of the coin * @param coin_pub public key of the coin
* @param coin_sig signature from the coin * @param coin_sig signature from the coin
* @param amount_with_fee amount that was deposited including fee * @param amount_with_fee amount that was deposited including fee
@ -1597,6 +1618,7 @@ typedef enum GNUNET_GenericReturnValue
void *cls, void *cls,
uint64_t rowid, uint64_t rowid,
const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *amount_with_fee,
@ -2735,7 +2757,7 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_CoinPublicInfo *coin, const struct TALER_CoinPublicInfo *coin,
uint64_t *known_coin_id, uint64_t *known_coin_id,
struct TALER_DenominationHash *denom_pub_hash, struct TALER_DenominationHash *denom_pub_hash,
struct TALER_AgeHash *age_hash); struct TALER_AgeCommitmentHash *age_hash);
/** /**

View File

@ -85,6 +85,31 @@ enum GNUNET_GenericReturnValue
TALER_extensions_load_taler_config ( TALER_extensions_load_taler_config (
const struct GNUNET_CONFIGURATION_Handle *cfg); const struct GNUNET_CONFIGURATION_Handle *cfg);
/*
* Check the given obj to be a valid extension object and fill the fields
* accordingly.
*/
enum GNUNET_GenericReturnValue
TALER_extensions_is_json_config (
json_t *obj,
int *critical,
const char **version,
json_t **config);
/*
* Sets the configuration of the extensions from a given JSON object.
*
* he JSON object must be of type ExchangeKeysResponse as described in
* https://docs.taler.net/design-documents/006-extensions.html#exchange
*
* @param cfg JSON object containting the configuration for all extensions
* @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
* or any particular configuration couldn't be parsed.
*/
enum GNUNET_GenericReturnValue
TALER_extensions_load_json_config (
json_t *cfg);
/* /*
* Returns the head of the linked list of extensions * Returns the head of the linked list of extensions
*/ */
@ -156,20 +181,6 @@ TALER_extensions_verify_json_config_signature (
struct TALER_MasterSignatureP *extensions_sig, struct TALER_MasterSignatureP *extensions_sig,
struct TALER_MasterPublicKeyP *master_pub); struct TALER_MasterPublicKeyP *master_pub);
/*
* Sets the configuration of the extensions from a given JSON object.
*
* The JSON object must be of type ExchangeKeysResponse as described in
* https://docs.taler.net/design-documents/006-extensions.html#exchange
*
* @param cfg Handle to the TALER configuration
* @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
* or any particular configuration couldn't be parsed.
*/
enum GNUNET_GenericReturnValue
TALER_extensions_load_json_config (
json_t *extensions);
/* /*
* TALER Age Restriction Extension * TALER Age Restriction Extension
@ -221,6 +232,45 @@ char *
TALER_age_mask_to_string ( TALER_age_mask_to_string (
const struct TALER_AgeMask *mask); const struct TALER_AgeMask *mask);
/**
* Returns true when age restriction is configured and enabled.
*/
bool
TALER_extensions_age_restriction_is_enabled ();
/**
* Returns true when age restriction is configured (might not be _enabled_,
* though).
*/
bool
TALER_extensions_age_restriction_is_configured ();
/**
* Returns the currently set age mask. Note that even if age restriction is
* not enabled, the age mask might be have a non-zero value.
*/
struct TALER_AgeMask
TALER_extensions_age_restriction_ageMask ();
/**
* Returns the amount of age groups defined. 0 means no age restriction
* enabled.
*/
size_t
TALER_extensions_age_restriction_num_groups ();
/**
* Parses a JSON object { "age_groups": "a:b:...y:z" }.
*
* @param root is the json object
* @param[out] mask on succes, will contain the age mask
* @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
*/
enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask);
/* /*
* TODO: Add Peer2Peer Extension * TODO: Add Peer2Peer Extension

View File

@ -552,17 +552,6 @@ enum GNUNET_GenericReturnValue
TALER_JSON_extensions_config_hash (const json_t *config, TALER_JSON_extensions_config_hash (const json_t *config,
struct TALER_ExtensionConfigHash *eh); struct TALER_ExtensionConfigHash *eh);
/**
* Parses a JSON object { "extension": "age_restriction", "mask": <uint32> }.
*
* @param root is the json object
* @param[out] mask on succes, will contain the age mask
* @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
*/
enum GNUNET_GenericReturnValue
TALER_JSON_parse_agemask (const json_t *root,
struct TALER_AgeMask *mask);
#endif /* TALER_JSON_LIB_H_ */ #endif /* TALER_JSON_LIB_H_ */
/* End of taler_json_lib.h */ /* End of taler_json_lib.h */

View File

@ -414,6 +414,11 @@ struct TALER_LinkDataPS
*/ */
struct TALER_TransferPublicKeyP transfer_pub; struct TALER_TransferPublicKeyP transfer_pub;
/**
* Hash of the age commitment, if applicable. Can be all zero
*/
struct TALER_AgeCommitmentHash h_age_commitment;
/** /**
* Hash of the blinded new coin. * Hash of the blinded new coin.
*/ */
@ -478,6 +483,12 @@ struct TALER_DepositRequestPS
*/ */
struct TALER_PrivateContractHash h_contract_terms GNUNET_PACKED; struct TALER_PrivateContractHash h_contract_terms GNUNET_PACKED;
/**
* Hash over the age commitment that went into the coin. Maybe all zero, if
* age commitment isn't applicable to the denomination.
*/
struct TALER_AgeCommitmentHash h_age_commitment GNUNET_PACKED;
/** /**
* Hash over extension attributes shared with the exchange. * Hash over extension attributes shared with the exchange.
*/ */
@ -711,6 +722,13 @@ struct TALER_RefreshMeltCoinAffirmationPS
*/ */
struct TALER_DenominationHash h_denom_pub GNUNET_PACKED; struct TALER_DenominationHash h_denom_pub GNUNET_PACKED;
/**
* If age commitment was provided during the withdrawal of the coin, this is
* the hash of the age commitment vector. It must be all zeroes if no age
* commitment was provided.
*/
struct TALER_AgeCommitmentHash h_age_commitment GNUNET_PACKED;
/** /**
* How much of the value of the coin should be melted? This amount * How much of the value of the coin should be melted? This amount
* includes the fees, so the final amount contributed to the melt is * includes the fees, so the final amount contributed to the melt is

View File

@ -66,11 +66,13 @@ TALER_TESTING_make_wire_details (const char *payto);
* *
* @param keys array of keys to search * @param keys array of keys to search
* @param amount coin value to look for * @param amount coin value to look for
* @param age_restricted must the denomination be age restricted?
* @return NULL if no matching key was found * @return NULL if no matching key was found
*/ */
const struct TALER_EXCHANGE_DenomPublicKey * const struct TALER_EXCHANGE_DenomPublicKey *
TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys, TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
const struct TALER_Amount *amount); const struct TALER_Amount *amount,
bool age_restricted);
/** /**
@ -1277,6 +1279,7 @@ TALER_TESTING_cmd_exec_transfer (const char *label,
* @param label command label. * @param label command label.
* @param reserve_reference command providing us with a reserve to withdraw from * @param reserve_reference command providing us with a reserve to withdraw from
* @param amount how much we withdraw. * @param amount how much we withdraw.
* @param age if > 0, age restriction applies
* @param expected_response_code which HTTP response code * @param expected_response_code which HTTP response code
* we expect from the exchange. * we expect from the exchange.
* @return the withdraw command to be executed by the interpreter. * @return the withdraw command to be executed by the interpreter.
@ -1285,6 +1288,7 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_withdraw_amount (const char *label, TALER_TESTING_cmd_withdraw_amount (const char *label,
const char *reserve_reference, const char *reserve_reference,
const char *amount, const char *amount,
uint8_t age,
unsigned int expected_response_code); unsigned int expected_response_code);
@ -1297,6 +1301,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
* @param label command label. * @param label command label.
* @param reserve_reference command providing us with a reserve to withdraw from * @param reserve_reference command providing us with a reserve to withdraw from
* @param amount how much we withdraw. * @param amount how much we withdraw.
* @param age if > 0, age restriction applies.
* @param coin_ref reference to (withdraw/reveal) command of a coin * @param coin_ref reference to (withdraw/reveal) command of a coin
* from which we should re-use the private key * from which we should re-use the private key
* @param expected_response_code which HTTP response code * @param expected_response_code which HTTP response code
@ -1308,6 +1313,7 @@ TALER_TESTING_cmd_withdraw_amount_reuse_key (
const char *label, const char *label,
const char *reserve_reference, const char *reserve_reference,
const char *amount, const char *amount,
uint8_t age,
const char *coin_ref, const char *coin_ref,
unsigned int expected_response_code); unsigned int expected_response_code);
@ -2138,6 +2144,19 @@ TALER_TESTING_cmd_wire_del (const char *label,
unsigned int expected_http_status, unsigned int expected_http_status,
bool bad_sig); bool bad_sig);
/**
* Sign all extensions that the exchange has to offer, f. e. the extension for
* age restriction. This has to be run before any withdrawal of age restricted
* can be performed.
*
* @param label command label.
* @param config_filename configuration filename.
* @return the command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_exec_offline_sign_extensions (const char *label,
const char *config_filename);
/** /**
* Sign all exchange denomination and online signing keys * Sign all exchange denomination and online signing keys
@ -2442,8 +2461,8 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
*/ */
#define TALER_TESTING_SIMPLE_TRAITS(op) \ #define TALER_TESTING_SIMPLE_TRAITS(op) \
op (bank_row, const uint64_t) \ op (bank_row, const uint64_t) \
op (reserve_priv, const struct TALER_ReservePrivateKeyP) \ op (reserve_priv, const struct TALER_ReservePrivateKeyP) \
op (reserve_pub, const struct TALER_ReservePublicKeyP) \ op (reserve_pub, const struct TALER_ReservePublicKeyP) \
op (merchant_priv, const struct TALER_MerchantPrivateKeyP) \ op (merchant_priv, const struct TALER_MerchantPrivateKeyP) \
op (merchant_pub, const struct TALER_MerchantPublicKeyP) \ op (merchant_pub, const struct TALER_MerchantPublicKeyP) \
op (merchant_sig, const struct TALER_MerchantSignatureP) \ op (merchant_sig, const struct TALER_MerchantSignatureP) \
@ -2456,8 +2475,8 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
op (exchange_bank_account_url, const char *) \ op (exchange_bank_account_url, const char *) \
op (taler_uri, const char *) \ op (taler_uri, const char *) \
op (payto_uri, const char *) \ op (payto_uri, const char *) \
op (kyc_url, const char *) \ op (kyc_url, const char *) \
op (web_url, const char *) \ op (web_url, const char *) \
op (row, const uint64_t) \ op (row, const uint64_t) \
op (payment_target_uuid, const uint64_t) \ op (payment_target_uuid, const uint64_t) \
op (array_length, const unsigned int) \ op (array_length, const unsigned int) \
@ -2482,6 +2501,8 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
#define TALER_TESTING_INDEXED_TRAITS(op) \ #define TALER_TESTING_INDEXED_TRAITS(op) \
op (denom_pub, const struct TALER_EXCHANGE_DenomPublicKey) \ op (denom_pub, const struct TALER_EXCHANGE_DenomPublicKey) \
op (denom_sig, const struct TALER_DenominationSignature) \ op (denom_sig, const struct TALER_DenominationSignature) \
op (age_commitment, struct TALER_AgeCommitment) \
op (h_age_commitment, struct TALER_AgeCommitmentHash) \
op (coin_priv, const struct TALER_CoinSpendPrivateKeyP) \ op (coin_priv, const struct TALER_CoinSpendPrivateKeyP) \
op (coin_pub, const struct TALER_CoinSpendPublicKeyP) \ op (coin_pub, const struct TALER_CoinSpendPublicKeyP) \
op (absolute_time, const struct GNUNET_TIME_Absolute) \ op (absolute_time, const struct GNUNET_TIME_Absolute) \

View File

@ -659,36 +659,4 @@ TALER_JSON_spec_i18n_str (const char *name,
} }
enum GNUNET_GenericReturnValue
TALER_JSON_parse_agemask (const json_t *root,
struct TALER_AgeMask *mask)
{
const char *name;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("extension",
&name),
GNUNET_JSON_spec_uint32 ("mask",
&mask->mask),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK != GNUNET_JSON_parse (root,
spec,
NULL,
NULL))
{
return GNUNET_SYSERR;
}
if (! strncmp (name,
"age_restriction",
sizeof("age_restriction")))
{
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/* end of json/json_helper.c */ /* end of json/json_helper.c */

View File

@ -482,6 +482,7 @@ TALER_EXCHANGE_verify_coin_history (
struct TALER_MerchantPublicKeyP merchant_pub; struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_TIME_Timestamp refund_deadline = {0}; struct GNUNET_TIME_Timestamp refund_deadline = {0};
struct TALER_CoinSpendSignatureP sig; struct TALER_CoinSpendSignatureP sig;
struct TALER_AgeCommitmentHash *hac = NULL;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_sig", GNUNET_JSON_spec_fixed_auto ("coin_sig",
&sig), &sig),
@ -516,6 +517,7 @@ TALER_EXCHANGE_verify_coin_history (
&fee, &fee,
&h_wire, &h_wire,
&h_contract_terms, &h_contract_terms,
hac,
NULL /* h_extensions! */, NULL /* h_extensions! */,
h_denom_pub, h_denom_pub,
wallet_timestamp, wallet_timestamp,
@ -548,6 +550,7 @@ TALER_EXCHANGE_verify_coin_history (
{ {
struct TALER_CoinSpendSignatureP sig; struct TALER_CoinSpendSignatureP sig;
struct TALER_RefreshCommitmentP rc; struct TALER_RefreshCommitmentP rc;
struct TALER_AgeCommitmentHash h_age_commitment = {0};
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_sig", GNUNET_JSON_spec_fixed_auto ("coin_sig",
&sig), &sig),
@ -555,6 +558,9 @@ TALER_EXCHANGE_verify_coin_history (
&rc), &rc),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub", GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
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", TALER_JSON_spec_amount_any ("melt_fee",
&fee), &fee),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
@ -568,6 +574,7 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (NULL != dk) if (NULL != dk)
{ {
/* check that melt fee matches our expectations from /keys! */ /* check that melt fee matches our expectations from /keys! */
@ -582,16 +589,25 @@ TALER_EXCHANGE_verify_coin_history (
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
} }
if (GNUNET_OK !=
TALER_wallet_melt_verify (&amount,
&fee,
&rc,
h_denom_pub,
coin_pub,
&sig))
{ {
GNUNET_break_op (0); const struct TALER_AgeCommitmentHash *ahc = &h_age_commitment;
return GNUNET_SYSERR;
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; add = GNUNET_YES;
} }

View File

@ -462,6 +462,7 @@ handle_deposit_finished (void *cls,
* @param h_wire hash of the merchants account details * @param h_wire hash of the merchants account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange) * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param coin_pub coins public key * @param coin_pub coins public key
* @param h_age_commitment coins hash of age commitment, might be NULL
* @param denom_sig exchanges unblinded signature of the coin * @param denom_sig exchanges unblinded signature of the coin
* @param denom_pub denomination key with which the coin is signed * @param denom_pub denomination key with which the coin is signed
* @param denom_pub_hash hash of @a denom_pub * @param denom_pub_hash hash of @a denom_pub
@ -478,6 +479,7 @@ verify_signatures (const struct TALER_EXCHANGE_DenomPublicKey *dki,
const struct TALER_PrivateContractHash *h_contract_terms, const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_ExtensionContractHash *ech, const struct TALER_ExtensionContractHash *ech,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_DenominationSignature *denom_sig, const struct TALER_DenominationSignature *denom_sig,
const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_DenominationHash *denom_pub_hash, const struct TALER_DenominationHash *denom_pub_hash,
@ -491,6 +493,7 @@ verify_signatures (const struct TALER_EXCHANGE_DenomPublicKey *dki,
&dki->fee_deposit, &dki->fee_deposit,
h_wire, h_wire,
h_contract_terms, h_contract_terms,
h_age_commitment,
ech, ech,
denom_pub_hash, denom_pub_hash,
timestamp, timestamp,
@ -514,8 +517,12 @@ verify_signatures (const struct TALER_EXCHANGE_DenomPublicKey *dki,
.coin_pub = *coin_pub, .coin_pub = *coin_pub,
.denom_pub_hash = *denom_pub_hash, .denom_pub_hash = *denom_pub_hash,
.denom_sig = *denom_sig, .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 != if (GNUNET_YES !=
TALER_test_coin_valid (&coin_info, TALER_test_coin_valid (&coin_info,
@ -547,6 +554,7 @@ TALER_EXCHANGE_deposit (
const char *merchant_payto_uri, const char *merchant_payto_uri,
const struct TALER_WireSalt *wire_salt, const struct TALER_WireSalt *wire_salt,
const struct TALER_PrivateContractHash *h_contract_terms, const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const json_t *extension_details, const json_t *extension_details,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_DenominationSignature *denom_sig, const struct TALER_DenominationSignature *denom_sig,
@ -599,11 +607,14 @@ TALER_EXCHANGE_deposit (
} }
GNUNET_assert (GNUNET_YES == GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange)); TEAH_handle_is_ready (exchange));
/* initialize h_wire */ /* initialize h_wire */
TALER_merchant_wire_signature_hash (merchant_payto_uri, TALER_merchant_wire_signature_hash (merchant_payto_uri,
wire_salt, wire_salt,
&h_wire); &h_wire);
key_state = TALER_EXCHANGE_get_keys (exchange); key_state = TALER_EXCHANGE_get_keys (exchange);
dki = TALER_EXCHANGE_get_denomination_key (key_state, dki = TALER_EXCHANGE_get_denomination_key (key_state,
denom_pub); denom_pub);
if (NULL == dki) if (NULL == dki)
@ -612,6 +623,7 @@ TALER_EXCHANGE_deposit (
GNUNET_break_op (0); GNUNET_break_op (0);
return NULL; return NULL;
} }
if (0 > if (0 >
TALER_amount_subtract (&amount_without_fee, TALER_amount_subtract (&amount_without_fee,
amount, amount,
@ -621,17 +633,18 @@ TALER_EXCHANGE_deposit (
GNUNET_break_op (0); GNUNET_break_op (0);
return NULL; return NULL;
} }
TALER_denom_pub_hash (denom_pub, TALER_denom_pub_hash (denom_pub,
&denom_pub_hash); &denom_pub_hash);
if (GNUNET_OK != if (GNUNET_OK !=
verify_signatures (dki, verify_signatures (dki,
amount, amount,
&h_wire, &h_wire,
h_contract_terms, h_contract_terms,
(NULL != extension_details) (NULL != extension_details) ? &ech : NULL,
? &ech
: NULL,
coin_pub, coin_pub,
h_age_commitment,
denom_sig, denom_sig,
denom_pub, denom_pub,
&denom_pub_hash, &denom_pub_hash,
@ -654,6 +667,9 @@ TALER_EXCHANGE_deposit (
wire_salt), wire_salt),
GNUNET_JSON_pack_data_auto ("h_contract_terms", GNUNET_JSON_pack_data_auto ("h_contract_terms",
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", GNUNET_JSON_pack_data_auto ("denom_pub_hash",
&denom_pub_hash), &denom_pub_hash),
TALER_JSON_pack_denom_sig ("ub_sig", TALER_JSON_pack_denom_sig ("ub_sig",

View File

@ -667,7 +667,9 @@ decode_keys_json (const json_t *resp_obj,
enum TALER_EXCHANGE_VersionCompatibility *vc) enum TALER_EXCHANGE_VersionCompatibility *vc)
{ {
struct TALER_ExchangeSignatureP sig; 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; struct TALER_ExchangePublicKeyP pub;
const char *currency; const char *currency;
struct GNUNET_JSON_Specification mspec[] = { struct GNUNET_JSON_Specification mspec[] = {
@ -746,7 +748,6 @@ decode_keys_json (const json_t *resp_obj,
key_data->version = GNUNET_strdup (ver); key_data->version = GNUNET_strdup (ver);
} }
hash_context = NULL;
EXITIF (GNUNET_OK != EXITIF (GNUNET_OK !=
GNUNET_JSON_parse (resp_obj, GNUNET_JSON_parse (resp_obj,
(check_sig) ? mspec : &mspec[2], (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 */ /* parse the master public key and issue date of the response */
if (check_sig) if (check_sig)
{
hash_context = GNUNET_CRYPTO_hash_context_start (); hash_context = GNUNET_CRYPTO_hash_context_start ();
hash_context_restricted = GNUNET_CRYPTO_hash_context_start ();
}
/* parse the signing keys */ /* parse the signing keys */
{ {
@ -829,6 +833,9 @@ decode_keys_json (const json_t *resp_obj,
EXITIF (GNUNET_OK != EXITIF (GNUNET_OK !=
TALER_extensions_load_json_config (extensions)); 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 /* parse the denomination keys, merging with the
@ -839,9 +846,15 @@ decode_keys_json (const json_t *resp_obj,
*/ */
struct struct
{ char *name; { char *name;
bool is_optional_age_restriction;} hive[2] = { struct GNUNET_HashContext *hc;
{ "denoms", false }, bool is_optional_age_restriction;}
{ "age_restricted_denoms", true }, 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++) 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, denom_keys_array = json_object_get (resp_obj,
hive[s].name); hive[s].name);
EXITIF (NULL == denom_keys_array && if (NULL == denom_keys_array)
! hive[s].is_optional_age_restriction);
if (NULL == denom_keys_array &&
hive[s].is_optional_age_restriction)
continue; 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)); EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
json_array_foreach (denom_keys_array, index, denom_key_obj) { json_array_foreach (denom_keys_array, index, denom_key_obj) {
struct TALER_EXCHANGE_DenomPublicKey dk; struct TALER_EXCHANGE_DenomPublicKey dk;
bool found = false; 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, memset (&dk,
0, 0,
sizeof (dk)); sizeof (dk));
@ -880,12 +887,7 @@ decode_keys_json (const json_t *resp_obj,
check_sig, check_sig,
denom_key_obj, denom_key_obj,
&key_data->master_pub, &key_data->master_pub,
hash_context)); hive[s].hc));
/* 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;
for (unsigned int j = 0; for (unsigned int j = 0;
j<key_data->num_denom_keys; j<key_data->num_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) .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, GNUNET_CRYPTO_hash_context_finish (hash_context,
&ks.hc); &ks.hc);
hash_context = NULL; hash_context = NULL;

View File

@ -105,6 +105,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
}; };
struct TALER_TransferSecretP secret; struct TALER_TransferSecretP secret;
struct TALER_PlanchetSecretsP fc; struct TALER_PlanchetSecretsP fc;
struct TALER_AgeCommitmentHash h_age_commitment = {0}; // TODO, see below.
/* parse reply */ /* parse reply */
if (GNUNET_OK != if (GNUNET_OK !=
@ -145,6 +146,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
if (GNUNET_OK != if (GNUNET_OK !=
TALER_planchet_prepare (&rpub, TALER_planchet_prepare (&rpub,
&fc, &fc,
NULL, /* FIXME-oec. struct TALER_AgeCommitmentHash * */
&c_hash, &c_hash,
&pd)) &pd))
{ {
@ -156,11 +158,21 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
pd.coin_ev_size, pd.coin_ev_size,
&coin_envelope_hash.hash); &coin_envelope_hash.hash);
/*
* 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?
*/
if (GNUNET_OK != if (GNUNET_OK !=
TALER_wallet_link_verify (&pd.denom_pub_hash, TALER_wallet_link_verify (&pd.denom_pub_hash,
trans_pub, trans_pub,
&coin_envelope_hash, &coin_envelope_hash,
&old_coin_pub, &old_coin_pub,
&h_age_commitment,
&link_sig)) &link_sig))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);

View File

@ -32,7 +32,7 @@
/** /**
* Set to 1 for extra debug logging. * Set to 1 for extra debug logging.
*/ */
#define DEBUG 0 #define DEBUG 1 /* FIXME-oec */
/** /**

View File

@ -151,7 +151,7 @@ TALER_EXCHANGE_management_post_extensions (
body = GNUNET_JSON_PACK ( body = GNUNET_JSON_PACK (
GNUNET_JSON_pack_object_steal ("extensions", GNUNET_JSON_pack_object_steal ("extensions",
ped->extensions), ped->extensions),
GNUNET_JSON_pack_data_auto ("extensions_sigs", GNUNET_JSON_pack_data_auto ("extensions_sig",
&ped->extensions_sig)); &ped->extensions_sig));
eh = curl_easy_init (); eh = curl_easy_init ();
@ -168,7 +168,7 @@ TALER_EXCHANGE_management_post_extensions (
return NULL; return NULL;
} }
json_decref (body); json_decref (body);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Requesting URL '%s'\n", "Requesting URL '%s'\n",
ph->url); ph->url);
GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, GNUNET_assert (CURLE_OK == curl_easy_setopt (eh,

View File

@ -79,6 +79,8 @@ serialize_melted_coin (const struct MeltedCoin *mc)
return GNUNET_JSON_PACK ( return GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("coin_priv", GNUNET_JSON_pack_data_auto ("coin_priv",
&mc->coin_priv), &mc->coin_priv),
GNUNET_JSON_pack_data_auto ("h_age_commitment",
&mc->h_age_commitment),
TALER_JSON_pack_denom_sig ("denom_sig", TALER_JSON_pack_denom_sig ("denom_sig",
&mc->sig), &mc->sig),
TALER_JSON_pack_denom_pub ("denom_pub", TALER_JSON_pack_denom_pub ("denom_pub",
@ -113,6 +115,9 @@ deserialize_melted_coin (struct MeltedCoin *mc,
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_priv", GNUNET_JSON_spec_fixed_auto ("coin_priv",
&mc->coin_priv), &mc->coin_priv),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&mc->h_age_commitment)),
TALER_JSON_spec_denom_sig ("denom_sig", TALER_JSON_spec_denom_sig ("denom_sig",
&mc->sig), &mc->sig),
TALER_JSON_spec_denom_pub ("denom_pub", TALER_JSON_spec_denom_pub ("denom_pub",
@ -343,12 +348,14 @@ TALER_EXCHANGE_refresh_prepare (
const struct TALER_Amount *melt_amount, const struct TALER_Amount *melt_amount,
const struct TALER_DenominationSignature *melt_sig, const struct TALER_DenominationSignature *melt_sig,
const struct TALER_EXCHANGE_DenomPublicKey *melt_pk, const struct TALER_EXCHANGE_DenomPublicKey *melt_pk,
const struct TALER_AgeCommitment *age_commitment,
unsigned int fresh_pks_len, unsigned int fresh_pks_len,
const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks) const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks)
{ {
struct MeltData md; struct MeltData md;
json_t *ret; json_t *ret;
struct TALER_Amount total; struct TALER_Amount total;
struct TALER_AgeCommitmentHash ach = {0};
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA]; struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA];
struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA];
@ -366,6 +373,10 @@ TALER_EXCHANGE_refresh_prepare (
md.melted_coin.original_value = melt_pk->value; md.melted_coin.original_value = melt_pk->value;
md.melted_coin.expire_deposit md.melted_coin.expire_deposit
= melt_pk->expire_deposit; = melt_pk->expire_deposit;
md.melted_coin.h_age_commitment = ach;
TALER_age_commitment_hash (age_commitment,
&md.melted_coin.h_age_commitment);
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (melt_amount->currency, TALER_amount_set_zero (melt_amount->currency,
&total)); &total));
@ -375,6 +386,7 @@ TALER_EXCHANGE_refresh_prepare (
melt_sig); melt_sig);
md.fresh_pks = GNUNET_new_array (fresh_pks_len, md.fresh_pks = GNUNET_new_array (fresh_pks_len,
struct TALER_DenominationPublicKey); struct TALER_DenominationPublicKey);
for (unsigned int i = 0; i<fresh_pks_len; i++) for (unsigned int i = 0; i<fresh_pks_len; i++)
{ {
TALER_denom_pub_deep_copy (&md.fresh_pks[i], TALER_denom_pub_deep_copy (&md.fresh_pks[i],
@ -393,6 +405,7 @@ TALER_EXCHANGE_refresh_prepare (
return NULL; return NULL;
} }
} }
/* verify that melt_amount is above total cost */ /* verify that melt_amount is above total cost */
if (1 == if (1 ==
TALER_amount_cmp (&total, TALER_amount_cmp (&total,
@ -416,23 +429,62 @@ TALER_EXCHANGE_refresh_prepare (
TALER_link_derive_transfer_secret (melt_priv, TALER_link_derive_transfer_secret (melt_priv,
&md.melted_coin.transfer_priv[i], &md.melted_coin.transfer_priv[i],
&trans_sec[i]); &trans_sec[i]);
md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len, md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len,
struct TALER_PlanchetSecretsP); struct TALER_PlanchetSecretsP);
rce[i].new_coins = GNUNET_new_array (fresh_pks_len, rce[i].new_coins = GNUNET_new_array (fresh_pks_len,
struct TALER_RefreshCoinData); struct TALER_RefreshCoinData);
md.fresh_ach[i] = GNUNET_new_array (fresh_pks_len,
struct TALER_AgeCommitmentHash);
for (unsigned int j = 0; j<fresh_pks_len; j++) for (unsigned int j = 0; j<fresh_pks_len; j++)
{ {
struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j]; struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j];
struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j]; struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
struct TALER_AgeCommitmentHash *ach = &md.fresh_ach[i][j];
struct TALER_PlanchetDetail pd; struct TALER_PlanchetDetail pd;
struct TALER_CoinPubHash c_hash; struct TALER_CoinPubHash c_hash;
/* Handle age commitment, if present */
if (NULL == age_commitment)
{
memset (ach, 0, sizeof(struct TALER_AgeCommitmentHash));
ach = NULL;
}
else
{
struct TALER_AgeCommitment new_ac;
uint32_t seed;
/* we use the first 4 bytes of the trans_sec to generate a new age
* commitment */
seed = *(uint32_t *) trans_sec[i].key.bits;
if (GNUNET_OK != TALER_age_commitment_derive (
age_commitment,
seed,
&new_ac))
{
GNUNET_break_op (0);
TALER_EXCHANGE_free_melt_data_ (&md);
return NULL;
}
TALER_age_commitment_hash (
&new_ac,
&md.fresh_ach[i][j]);
}
TALER_planchet_setup_refresh (&trans_sec[i], TALER_planchet_setup_refresh (&trans_sec[i],
j, j,
fc); fc);
if (GNUNET_OK != if (GNUNET_OK !=
TALER_planchet_prepare (&md.fresh_pks[j], TALER_planchet_prepare (&md.fresh_pks[j],
fc, fc,
ach,
&c_hash, &c_hash,
&pd)) &pd))
{ {
@ -453,6 +505,7 @@ TALER_EXCHANGE_refresh_prepare (
rce, rce,
&coin_pub, &coin_pub,
melt_amount); melt_amount);
/* finally, serialize everything */ /* finally, serialize everything */
ret = serialize_melt_data (&md); ret = serialize_melt_data (&md);
for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
@ -461,6 +514,7 @@ TALER_EXCHANGE_refresh_prepare (
GNUNET_free (rce[i].new_coins[j].coin_ev); GNUNET_free (rce[i].new_coins[j].coin_ev);
GNUNET_free (rce[i].new_coins); GNUNET_free (rce[i].new_coins);
} }
TALER_EXCHANGE_free_melt_data_ (&md); TALER_EXCHANGE_free_melt_data_ (&md);
return ret; return ret;
} }

View File

@ -52,6 +52,12 @@ struct MeltedCoin
*/ */
struct TALER_Amount original_value; 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;
/** /**
* Transfer private keys for each cut-and-choose dimension. * Transfer private keys for each cut-and-choose dimension.
*/ */
@ -107,6 +113,14 @@ struct MeltData
* coins to be created, for each cut-and-choose dimension. * coins to be created, for each cut-and-choose dimension.
*/ */
struct TALER_PlanchetSecretsP *fresh_coins[TALER_CNC_KAPPA]; struct TALER_PlanchetSecretsP *fresh_coins[TALER_CNC_KAPPA];
/**
* Arrays of @e num_fresh_coins with information about the hashes of age
* commitments coins to be created, 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_AgeCommitmentHash *fresh_ach[TALER_CNC_KAPPA];
}; };

View File

@ -136,6 +136,7 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
{ {
const struct TALER_PlanchetSecretsP *fc; const struct TALER_PlanchetSecretsP *fc;
struct TALER_DenominationPublicKey *pk; struct TALER_DenominationPublicKey *pk;
struct TALER_AgeCommitmentHash *ach = NULL;
json_t *jsonai; json_t *jsonai;
struct TALER_BlindedDenominationSignature blind_sig; struct TALER_BlindedDenominationSignature blind_sig;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
@ -152,6 +153,12 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
jsonai = json_array_get (jsona, i); jsonai = json_array_get (jsona, i);
GNUNET_assert (NULL != jsonai); GNUNET_assert (NULL != jsonai);
if (! TALER_AgeCommitmentHash_isNullOrZero (
&rrh->md->melted_coin.h_age_commitment))
{
ach = &rrh->md->fresh_ach[rrh->noreveal_index][i];
}
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (jsonai, GNUNET_JSON_parse (jsonai,
spec, spec,
@ -166,14 +173,14 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
hence recomputing it here... */ hence recomputing it here... */
GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
&coin_pub.eddsa_pub); &coin_pub.eddsa_pub);
/* FIXME-Oec: Age commitment hash. */
TALER_coin_pub_hash (&coin_pub, TALER_coin_pub_hash (&coin_pub,
NULL, /* FIXME-Oec */ ach,
&coin_hash); &coin_hash);
if (GNUNET_OK != if (GNUNET_OK !=
TALER_planchet_to_coin (pk, TALER_planchet_to_coin (pk,
&blind_sig, &blind_sig,
fc, fc,
ach,
&coin_hash, &coin_hash,
&coin)) &coin))
{ {
@ -359,6 +366,7 @@ TALER_EXCHANGE_refreshes_reveal (
if (GNUNET_OK != if (GNUNET_OK !=
TALER_planchet_prepare (&md->fresh_pks[i], TALER_planchet_prepare (&md->fresh_pks[i],
&md->fresh_coins[noreveal_index][i], &md->fresh_coins[noreveal_index][i],
NULL, /* FIXME-oec */
&c_hash, &c_hash,
&pd)) &pd))
{ {

View File

@ -203,6 +203,7 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
struct TALER_Amount deposit_fee; struct TALER_Amount deposit_fee;
struct TALER_MerchantWireHash h_wire; struct TALER_MerchantWireHash h_wire;
struct TALER_PrivateContractHash h_contract_terms; struct TALER_PrivateContractHash h_contract_terms;
struct TALER_AgeCommitmentHash h_age_commitment = {{{0}}};
// struct TALER_ExtensionContractHash h_extensions; // FIXME! // struct TALER_ExtensionContractHash h_extensions; // FIXME!
struct TALER_DenominationHash h_denom_pub; struct TALER_DenominationHash h_denom_pub;
struct GNUNET_TIME_Timestamp wallet_timestamp; struct GNUNET_TIME_Timestamp wallet_timestamp;
@ -218,6 +219,9 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
&h_wire), &h_wire),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub", GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&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", GNUNET_JSON_spec_timestamp ("timestamp",
&wallet_timestamp), &wallet_timestamp),
GNUNET_JSON_spec_timestamp ("refund_deadline", GNUNET_JSON_spec_timestamp ("refund_deadline",
@ -243,6 +247,7 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
&deposit_fee, &deposit_fee,
&h_wire, &h_wire,
&h_contract_terms, &h_contract_terms,
&h_age_commitment,
NULL /* h_extensions! */, NULL /* h_extensions! */,
&h_denom_pub, &h_denom_pub,
wallet_timestamp, wallet_timestamp,

View File

@ -63,6 +63,11 @@ struct TALER_EXCHANGE_WithdrawHandle
*/ */
struct TALER_PlanchetSecretsP ps; struct TALER_PlanchetSecretsP ps;
/**
* Hash of the age commitment for this coin, if applicable. Maybe NULL
*/
const struct TALER_AgeCommitmentHash *ach;
/** /**
* Denomination key we are withdrawing. * Denomination key we are withdrawing.
*/ */
@ -106,6 +111,7 @@ handle_reserve_withdraw_finished (
TALER_planchet_to_coin (&wh->pk.key, TALER_planchet_to_coin (&wh->pk.key,
blind_sig, blind_sig,
&wh->ps, &wh->ps,
wh->ach,
&wh->c_hash, &wh->c_hash,
&fc)) &fc))
{ {
@ -159,6 +165,7 @@ handle_reserve_withdraw_finished (
* @param reserve_priv private key of the reserve to withdraw from * @param reserve_priv private key of the reserve to withdraw from
* @param ps secrets of the planchet * @param ps secrets of the planchet
* caller must have committed this value to disk before the call (with @a pk) * caller must have committed this value to disk before the call (with @a pk)
* @param ach (optional) hash of the age commitment that should be bound to this coin. Maybe NULL.
* @param res_cb the callback to call when the final result for this request is available * @param res_cb the callback to call when the final result for this request is available
* @param res_cb_cls closure for the above callback * @param res_cb_cls closure for the above callback
* @return handle for the operation on success, NULL on error, i.e. * @return handle for the operation on success, NULL on error, i.e.
@ -171,11 +178,13 @@ TALER_EXCHANGE_withdraw (
const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_ReservePrivateKeyP *reserve_priv, const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_PlanchetSecretsP *ps, const struct TALER_PlanchetSecretsP *ps,
const struct TALER_AgeCommitmentHash *ach,
TALER_EXCHANGE_WithdrawCallback res_cb, TALER_EXCHANGE_WithdrawCallback res_cb,
void *res_cb_cls) void *res_cb_cls)
{ {
struct TALER_PlanchetDetail pd; struct TALER_PlanchetDetail pd;
struct TALER_EXCHANGE_WithdrawHandle *wh; struct TALER_EXCHANGE_WithdrawHandle *wh;
bool age_restricted = (0 != pk->key.age_mask.mask);
wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle); wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
wh->exchange = exchange; wh->exchange = exchange;
@ -183,9 +192,14 @@ TALER_EXCHANGE_withdraw (
wh->cb_cls = res_cb_cls; wh->cb_cls = res_cb_cls;
wh->pk = *pk; wh->pk = *pk;
wh->ps = *ps; wh->ps = *ps;
wh->ach = ach;
GNUNET_assert (age_restricted == (NULL != ach));
if (GNUNET_OK != if (GNUNET_OK !=
TALER_planchet_prepare (&pk->key, TALER_planchet_prepare (&pk->key,
ps, ps,
ach,
&wh->c_hash, &wh->c_hash,
&pd)) &pd))
{ {

View File

@ -68,6 +68,7 @@ libtalertesting_la_SOURCES = \
testing_api_cmd_oauth.c \ testing_api_cmd_oauth.c \
testing_api_cmd_offline_sign_fees.c \ testing_api_cmd_offline_sign_fees.c \
testing_api_cmd_offline_sign_keys.c \ testing_api_cmd_offline_sign_keys.c \
testing_api_cmd_offline_sign_extensions.c \
testing_api_cmd_set_wire_fee.c \ testing_api_cmd_set_wire_fee.c \
testing_api_cmd_recoup.c \ testing_api_cmd_recoup.c \
testing_api_cmd_recoup_refresh.c \ testing_api_cmd_recoup_refresh.c \
@ -208,6 +209,7 @@ test_exchange_api_LDADD = \
-lgnunetcurl \ -lgnunetcurl \
-lgnunetutil \ -lgnunetutil \
-ljansson \ -ljansson \
-ltalerextensions \
$(XLIB) $(XLIB)
test_exchange_management_api_SOURCES = \ test_exchange_management_api_SOURCES = \

View File

@ -128,6 +128,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
"create-reserve-1", "create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };
@ -168,6 +169,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-1", TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-1",
"refresh-create-reserve-1", "refresh-create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/** /**
* Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in * Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in
@ -315,6 +317,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-unaggregated", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-unaggregated",
"create-reserve-unaggregated", "create-reserve-unaggregated",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_deposit ("deposit-unaggregated", TALER_TESTING_cmd_deposit ("deposit-unaggregated",
"withdraw-coin-unaggregated", "withdraw-coin-unaggregated",
@ -347,6 +350,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1",
"create-reserve-r1", "create-reserve-r1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/** /**
* Spend 5 EUR of the 5 EUR coin (in full). Merchant would * Spend 5 EUR of the 5 EUR coin (in full). Merchant would
@ -402,6 +406,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1",
"recoup-create-reserve-1", "recoup-create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_revoke ("revoke-1", TALER_TESTING_cmd_revoke ("revoke-1",
MHD_HTTP_OK, MHD_HTTP_OK,
@ -417,6 +422,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2",
"recoup-create-reserve-1", "recoup-create-reserve-1",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/** /**
* These commands should close the reserve because the aggregator * These commands should close the reserve because the aggregator
@ -447,6 +453,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2a", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2a",
"recoup-create-reserve-2", "recoup-create-reserve-2",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/** /**
* Withdraw a 1 EUR coin, at fee of 1 ct * Withdraw a 1 EUR coin, at fee of 1 ct
@ -454,6 +461,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2b", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2b",
"recoup-create-reserve-2", "recoup-create-reserve-2",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_deposit ("recoup-deposit-partial", TALER_TESTING_cmd_deposit ("recoup-deposit-partial",
"recoup-withdraw-coin-2a", "recoup-withdraw-coin-2a",
@ -491,42 +499,52 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-1", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-1",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-2", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-2",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-3", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-3",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-4", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-4",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-5", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-5",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-6", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-6",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-7", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-7",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-8", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-8",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-9", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-9",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-10", TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-10",
"massive-reserve", "massive-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_deposit ( TALER_TESTING_cmd_deposit (
"massive-deposit-1", "massive-deposit-1",
@ -708,7 +726,7 @@ main (int argc,
GNUNET_break (0); GNUNET_break (0);
return 1; return 1;
case GNUNET_NO: case GNUNET_NO:
return 77; return 78;
case GNUNET_OK: case GNUNET_OK:
if (GNUNET_OK != if (GNUNET_OK !=
/* Set up event loop and reschedule context, plus /* Set up event loop and reschedule context, plus
@ -718,11 +736,11 @@ main (int argc,
TALER_TESTING_auditor_setup (&run, TALER_TESTING_auditor_setup (&run,
NULL, NULL,
CONFIG_FILE)) CONFIG_FILE))
return 1; return 2;
break; break;
default: default:
GNUNET_break (0); GNUNET_break (0);
return 1; return 3;
} }
return 0; return 0;
} }

View File

@ -33,6 +33,7 @@
#include "taler_bank_service.h" #include "taler_bank_service.h"
#include "taler_fakebank_lib.h" #include "taler_fakebank_lib.h"
#include "taler_testing_lib.h" #include "taler_testing_lib.h"
#include "taler_extensions.h"
/** /**
* Configuration file we use. One (big) configuration is used * Configuration file we use. One (big) configuration is used
@ -138,6 +139,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
"create-reserve-1", "create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/** /**
* Withdraw EUR:1 using the SAME private coin key as for the previous coin * Withdraw EUR:1 using the SAME private coin key as for the previous coin
@ -146,6 +148,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount_reuse_key ("withdraw-coin-1x", TALER_TESTING_cmd_withdraw_amount_reuse_key ("withdraw-coin-1x",
"create-reserve-1", "create-reserve-1",
"EUR:1", "EUR:1",
0, /* age restriction off */
"withdraw-coin-1", "withdraw-coin-1",
MHD_HTTP_OK), MHD_HTTP_OK),
/** /**
@ -161,6 +164,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
"create-reserve-1", "create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_CONFLICT), MHD_HTTP_CONFLICT),
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };
@ -254,6 +258,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-1", TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-1",
"refresh-create-reserve-1", "refresh-create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/* Try to partially spend (deposit) 1 EUR of the 5 EUR coin /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin
* (in full) (merchant would receive EUR:0.99 due to 1 ct * (in full) (merchant would receive EUR:0.99 due to 1 ct
@ -330,6 +335,61 @@ run (void *cls,
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };
/**
* Test withdrawal with age restriction. Success is expected, so it MUST be
* called _after_ TALER_TESTING_cmd_exec_offline_sign_extensions is called,
* i. e. age restriction is activated in the exchange!
*
* TODO: create a test that tries to withdraw coins with age restriction but
* (expectedly) fails because the exchange doesn't support age restriction
* yet.
*/
struct TALER_TESTING_Command withdraw_age[] = {
/**
* Move money to the exchange's bank account.
*/
CMD_TRANSFER_TO_EXCHANGE ("create-reserve-age",
"EUR:5.02"),
TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-age",
"EUR:5.02",
bc.user42_payto,
bc.exchange_payto,
"create-reserve-age"),
/**
* Make a reserve exist, according to the previous
* transfer.
*/
CMD_EXEC_WIREWATCH ("wirewatch-age"),
/**
* Withdraw EUR:5.
*/
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-age-1",
"create-reserve-age",
"EUR:5",
13,
MHD_HTTP_OK),
TALER_TESTING_cmd_end ()
};
struct TALER_TESTING_Command spend_age[] = {
/**
* Spend the coin.
*/
TALER_TESTING_cmd_deposit ("deposit-simple-age",
"withdraw-coin-age-1",
0,
bc.user42_payto,
"{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
GNUNET_TIME_UNIT_ZERO,
"EUR:5",
MHD_HTTP_OK),
TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay-age",
"deposit-simple-age",
MHD_HTTP_OK),
TALER_TESTING_cmd_end ()
};
struct TALER_TESTING_Command track[] = { struct TALER_TESTING_Command track[] = {
/* Try resolving a deposit's WTID, as we never triggered /* Try resolving a deposit's WTID, as we never triggered
* execution of transactions, the answer should be that * execution of transactions, the answer should be that
@ -372,6 +432,11 @@ run (void *cls,
"EUR:4.98", "EUR:4.98",
bc.exchange_payto, bc.exchange_payto,
bc.user42_payto), bc.user42_payto),
TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-499c2",
ec.exchange_url,
"EUR:4.98",
bc.exchange_payto,
bc.user42_payto),
TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c1", TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c1",
ec.exchange_url, ec.exchange_url,
"EUR:0.98", "EUR:0.98",
@ -426,6 +491,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-unaggregated", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-unaggregated",
"create-reserve-unaggregated", "create-reserve-unaggregated",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_deposit ("deposit-unaggregated", TALER_TESTING_cmd_deposit ("deposit-unaggregated",
"withdraw-coin-unaggregated", "withdraw-coin-unaggregated",
@ -464,6 +530,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-aggtest", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-aggtest",
"create-reserve-aggtest", "create-reserve-aggtest",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_deposit ("deposit-aggtest-1", TALER_TESTING_cmd_deposit ("deposit-aggtest-1",
"withdraw-coin-aggtest", "withdraw-coin-aggtest",
@ -512,6 +579,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1",
"create-reserve-r1", "create-reserve-r1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/** /**
* Spend 5 EUR of the 5 EUR coin (in full) (merchant would * Spend 5 EUR of the 5 EUR coin (in full) (merchant would
@ -612,6 +680,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-rb", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-rb",
"create-reserve-rb", "create-reserve-rb",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_deposit ("deposit-refund-1b", TALER_TESTING_cmd_deposit ("deposit-refund-1b",
"withdraw-coin-rb", "withdraw-coin-rb",
@ -661,11 +730,13 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1",
"recoup-create-reserve-1", "recoup-create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/* Withdraw a 10 EUR coin, at fee of 1 ct */ /* Withdraw a 10 EUR coin, at fee of 1 ct */
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1b", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1b",
"recoup-create-reserve-1", "recoup-create-reserve-1",
"EUR:10", "EUR:10",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/* melt 10 EUR coin to get 5 EUR refreshed coin */ /* melt 10 EUR coin to get 5 EUR refreshed coin */
TALER_TESTING_cmd_melt ("recoup-melt-coin-1b", TALER_TESTING_cmd_melt ("recoup-melt-coin-1b",
@ -756,6 +827,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2",
"recoup-create-reserve-1", "recoup-create-reserve-1",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/** /**
* This withdrawal will test the logic to create a "recoup" * This withdrawal will test the logic to create a "recoup"
@ -764,6 +836,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2-over", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2-over",
"recoup-create-reserve-1", "recoup-create-reserve-1",
"EUR:10", "EUR:10",
0, /* age restriction off */
MHD_HTTP_CONFLICT), MHD_HTTP_CONFLICT),
TALER_TESTING_cmd_status ("recoup-reserve-status-2", TALER_TESTING_cmd_status ("recoup-reserve-status-2",
"recoup-create-reserve-1", "recoup-create-reserve-1",
@ -796,6 +869,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("expired-withdraw", TALER_TESTING_cmd_withdraw_amount ("expired-withdraw",
"short-lived-reserve", "short-lived-reserve",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_CONFLICT), MHD_HTTP_CONFLICT),
TALER_TESTING_cmd_check_bank_transfer ("check_bank_short-lived_reimburse", TALER_TESTING_cmd_check_bank_transfer ("check_bank_short-lived_reimburse",
ec.exchange_url, ec.exchange_url,
@ -820,11 +894,13 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2a", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2a",
"recoup-create-reserve-2", "recoup-create-reserve-2",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/* Withdraw a 1 EUR coin, at fee of 1 ct */ /* Withdraw a 1 EUR coin, at fee of 1 ct */
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2b", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2b",
"recoup-create-reserve-2", "recoup-create-reserve-2",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_deposit ("recoup-deposit-partial", TALER_TESTING_cmd_deposit ("recoup-deposit-partial",
"recoup-withdraw-coin-2a", "recoup-withdraw-coin-2a",
@ -885,6 +961,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-3-revoked", TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-3-revoked",
"recoup-create-reserve-3", "recoup-create-reserve-3",
"EUR:1", "EUR:1",
0, /* age restriction off */
MHD_HTTP_GONE), MHD_HTTP_GONE),
/* check that we are empty before the rejection test */ /* check that we are empty before the rejection test */
TALER_TESTING_cmd_check_bank_empty ("check-empty-again"), TALER_TESTING_cmd_check_bank_empty ("check-empty-again"),
@ -931,6 +1008,8 @@ run (void *cls,
TALER_TESTING_cmd_auditor_add ("add-auditor-OK", TALER_TESTING_cmd_auditor_add ("add-auditor-OK",
MHD_HTTP_NO_CONTENT, MHD_HTTP_NO_CONTENT,
false), false),
TALER_TESTING_cmd_exec_offline_sign_extensions ("offline-sign-extensions",
CONFIG_FILE),
TALER_TESTING_cmd_wire_add ("add-wire-account", TALER_TESTING_cmd_wire_add ("add-wire-account",
"payto://x-taler-bank/localhost/2", "payto://x-taler-bank/localhost/2",
MHD_HTTP_NO_CONTENT, MHD_HTTP_NO_CONTENT,
@ -951,6 +1030,10 @@ run (void *cls,
spend), spend),
TALER_TESTING_cmd_batch ("refresh", TALER_TESTING_cmd_batch ("refresh",
refresh), refresh),
TALER_TESTING_cmd_batch ("withdraw-age",
withdraw_age),
TALER_TESTING_cmd_batch ("spend-age",
spend_age),
TALER_TESTING_cmd_batch ("track", TALER_TESTING_cmd_batch ("track",
track), track),
TALER_TESTING_cmd_batch ("unaggregation", TALER_TESTING_cmd_batch ("unaggregation",
@ -986,6 +1069,9 @@ main (int argc,
GNUNET_log_setup ("test-exchange-api", GNUNET_log_setup ("test-exchange-api",
"INFO", "INFO",
NULL); NULL);
TALER_extensions_init ();
/* Check fakebank port is available and get config */ /* Check fakebank port is available and get config */
if (GNUNET_OK != if (GNUNET_OK !=
TALER_TESTING_prepare_fakebank (CONFIG_FILE, TALER_TESTING_prepare_fakebank (CONFIG_FILE,
@ -1004,7 +1090,7 @@ main (int argc,
GNUNET_break (0); GNUNET_break (0);
return 1; return 1;
case GNUNET_NO: case GNUNET_NO:
return 77; return 78;
case GNUNET_OK: case GNUNET_OK:
if (GNUNET_OK != if (GNUNET_OK !=
/* Set up event loop and reschedule context, plus /* Set up event loop and reschedule context, plus
@ -1014,11 +1100,11 @@ main (int argc,
TALER_TESTING_setup_with_exchange (&run, TALER_TESTING_setup_with_exchange (&run,
NULL, NULL,
CONFIG_FILE)) CONFIG_FILE))
return 1; return 2;
break; break;
default: default:
GNUNET_break (0); GNUNET_break (0);
return 1; return 3;
} }
return 0; return 0;
} }

View File

@ -150,7 +150,7 @@ fee_deposit = EUR:0.00
fee_refresh = EUR:0.01 fee_refresh = EUR:0.01
fee_refund = EUR:0.01 fee_refund = EUR:0.01
rsa_keysize = 1024 rsa_keysize = 1024
age_restricted = true age_restricted = YES
[coin_eur_ct_10_age_restricted] [coin_eur_ct_10_age_restricted]
value = EUR:0.10 value = EUR:0.10
@ -162,7 +162,7 @@ fee_deposit = EUR:0.01
fee_refresh = EUR:0.03 fee_refresh = EUR:0.03
fee_refund = EUR:0.01 fee_refund = EUR:0.01
rsa_keysize = 1024 rsa_keysize = 1024
age_restricted = true age_restricted = YES
[coin_eur_1_age_restricted] [coin_eur_1_age_restricted]
value = EUR:1 value = EUR:1
@ -174,7 +174,7 @@ fee_deposit = EUR:0.01
fee_refresh = EUR:0.03 fee_refresh = EUR:0.03
fee_refund = EUR:0.01 fee_refund = EUR:0.01
rsa_keysize = 1024 rsa_keysize = 1024
age_restricted = true age_restricted = YES
[coin_eur_5_age_restricted] [coin_eur_5_age_restricted]
value = EUR:5 value = EUR:5
@ -186,7 +186,7 @@ fee_deposit = EUR:0.01
fee_refresh = EUR:0.03 fee_refresh = EUR:0.03
fee_refund = EUR:0.01 fee_refund = EUR:0.01
rsa_keysize = 1024 rsa_keysize = 1024
age_restricted = true age_restricted = YES
[coin_eur_10_age_restricted] [coin_eur_10_age_restricted]
value = EUR:10 value = EUR:10
@ -198,4 +198,4 @@ fee_deposit = EUR:0.01
fee_refresh = EUR:0.03 fee_refresh = EUR:0.03
fee_refund = EUR:0.01 fee_refund = EUR:0.01
rsa_keysize = 1024 rsa_keysize = 1024
age_restricted = true age_restricted = YES

View File

@ -95,11 +95,13 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-revocation-coin-1", TALER_TESTING_cmd_withdraw_amount ("withdraw-revocation-coin-1",
"create-reserve-1", "create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/* Withdraw another 5 EUR coin, at fee of 1 ct */ /* Withdraw another 5 EUR coin, at fee of 1 ct */
TALER_TESTING_cmd_withdraw_amount ("withdraw-revocation-coin-2", TALER_TESTING_cmd_withdraw_amount ("withdraw-revocation-coin-2",
"create-reserve-1", "create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
/* Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in full) /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in full)
* (merchant would receive EUR:0.99 due to 1 ct deposit fee) */// * (merchant would receive EUR:0.99 due to 1 ct deposit fee) *///

View File

@ -105,10 +105,12 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-no-kyc", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-no-kyc",
"create-reserve-1", "create-reserve-1",
"EUR:10", "EUR:10",
0, /* age restriction off */
MHD_HTTP_ACCEPTED), MHD_HTTP_ACCEPTED),
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
"create-reserve-1", "create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };
@ -120,6 +122,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-lacking-kyc", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-lacking-kyc",
"create-reserve-1", "create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_ACCEPTED), MHD_HTTP_ACCEPTED),
TALER_TESTING_cmd_proof_kyc ("proof-kyc", TALER_TESTING_cmd_proof_kyc ("proof-kyc",
"withdraw-coin-1-lacking-kyc", "withdraw-coin-1-lacking-kyc",
@ -129,6 +132,7 @@ run (void *cls,
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-with-kyc", TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-with-kyc",
"create-reserve-1", "create-reserve-1",
"EUR:5", "EUR:5",
0, /* age restriction off */
MHD_HTTP_OK), MHD_HTTP_OK),
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };

View File

@ -287,6 +287,8 @@ deposit_run (void *cls,
const struct TALER_TESTING_Command *coin_cmd; const struct TALER_TESTING_Command *coin_cmd;
const struct TALER_CoinSpendPrivateKeyP *coin_priv; const struct TALER_CoinSpendPrivateKeyP *coin_priv;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_AgeCommitment *age_commitment = NULL;
struct TALER_AgeCommitmentHash h_age_commitment = {0};
const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
const struct TALER_DenominationSignature *denom_pub_sig; const struct TALER_DenominationSignature *denom_pub_sig;
struct TALER_CoinSpendSignatureP coin_sig; struct TALER_CoinSpendSignatureP coin_sig;
@ -382,6 +384,10 @@ deposit_run (void *cls,
TALER_TESTING_get_trait_coin_priv (coin_cmd, TALER_TESTING_get_trait_coin_priv (coin_cmd,
ds->coin_index, ds->coin_index,
&coin_priv)) || &coin_priv)) ||
(GNUNET_OK !=
TALER_TESTING_get_trait_age_commitment (coin_cmd,
ds->coin_index,
&age_commitment)) ||
(GNUNET_OK != (GNUNET_OK !=
TALER_TESTING_get_trait_denom_pub (coin_cmd, TALER_TESTING_get_trait_denom_pub (coin_cmd,
ds->coin_index, ds->coin_index,
@ -398,6 +404,12 @@ deposit_run (void *cls,
TALER_TESTING_interpreter_fail (is); TALER_TESTING_interpreter_fail (is);
return; return;
} }
if (NULL != age_commitment)
{
TALER_age_commitment_hash (age_commitment, &h_age_commitment);
}
ds->deposit_fee = denom_pub->fee_deposit; ds->deposit_fee = denom_pub->fee_deposit;
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin_pub.eddsa_pub); &coin_pub.eddsa_pub);
@ -431,7 +443,8 @@ deposit_run (void *cls,
&denom_pub->fee_deposit, &denom_pub->fee_deposit,
&h_wire, &h_wire,
&h_contract_terms, &h_contract_terms,
NULL, /* FIXME: extension hash! */ &h_age_commitment,
NULL, /* FIXME: add hash of extensions */
&denom_pub->h_key, &denom_pub->h_key,
ds->wallet_timestamp, ds->wallet_timestamp,
&merchant_pub, &merchant_pub,
@ -445,7 +458,8 @@ deposit_run (void *cls,
payto_uri, payto_uri,
&wire_salt, &wire_salt,
&h_contract_terms, &h_contract_terms,
NULL, /* FIXME: extension object */ &h_age_commitment,
NULL, /* FIXME: add hash of extensions */
&coin_pub, &coin_pub,
denom_pub_sig, denom_pub_sig,
&denom_pub->key, &denom_pub->key,
@ -520,6 +534,7 @@ deposit_traits (void *cls,
const struct TALER_TESTING_Command *coin_cmd; const struct TALER_TESTING_Command *coin_cmd;
/* Will point to coin cmd internals. */ /* Will point to coin cmd internals. */
const struct TALER_CoinSpendPrivateKeyP *coin_spent_priv; const struct TALER_CoinSpendPrivateKeyP *coin_spent_priv;
struct TALER_AgeCommitment *age_commitment;
if (GNUNET_YES != ds->command_initialized) if (GNUNET_YES != ds->command_initialized)
{ {
@ -540,7 +555,11 @@ deposit_traits (void *cls,
if (GNUNET_OK != if (GNUNET_OK !=
TALER_TESTING_get_trait_coin_priv (coin_cmd, TALER_TESTING_get_trait_coin_priv (coin_cmd,
ds->coin_index, ds->coin_index,
&coin_spent_priv)) &coin_spent_priv) ||
(GNUNET_OK !=
TALER_TESTING_get_trait_age_commitment (coin_cmd,
ds->coin_index,
&age_commitment)))
{ {
GNUNET_break (0); GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
@ -555,6 +574,8 @@ deposit_traits (void *cls,
/* These traits are always available */ /* These traits are always available */
TALER_TESTING_make_trait_coin_priv (0, TALER_TESTING_make_trait_coin_priv (0,
coin_spent_priv), coin_spent_priv),
TALER_TESTING_make_trait_age_commitment (0,
age_commitment),
TALER_TESTING_make_trait_wire_details (ds->wire_details), TALER_TESTING_make_trait_wire_details (ds->wire_details),
TALER_TESTING_make_trait_contract_terms (ds->contract_terms), TALER_TESTING_make_trait_contract_terms (ds->contract_terms),
TALER_TESTING_make_trait_merchant_priv (&ds->merchant_priv), TALER_TESTING_make_trait_merchant_priv (&ds->merchant_priv),

View File

@ -238,7 +238,7 @@ insert_deposit_run (void *cls,
{ {
uint64_t known_coin_id; uint64_t known_coin_id;
struct TALER_DenominationHash dph; struct TALER_DenominationHash dph;
struct TALER_AgeHash agh; struct TALER_AgeCommitmentHash agh;
if ( (GNUNET_OK != if ( (GNUNET_OK !=
ids->dbc->plugin->start (ids->dbc->plugin->cls, ids->dbc->plugin->start (ids->dbc->plugin->cls,

View File

@ -0,0 +1,164 @@
/*
This file is part of TALER
Copyright (C) 2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3, or (at your
option) any later version.
TALER is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not,
see <http://www.gnu.org/licenses/>
*/
/**
* @file testing/testing_api_cmd_offline_sign_extensions.c
* @brief run the taler-exchange-offline command to sign extensions (and therefore activate them)
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "taler_signatures.h"
#include "taler_testing_lib.h"
/**
* State for a "extensionssign" CMD.
*/
struct ExtensionsSignState
{
/**
* Process for the "extensionssign" command.
*/
struct GNUNET_OS_Process *extensionssign_proc;
/**
* Configuration file used by the command.
*/
const char *config_filename;
};
/**
* Run the command; calls the `taler-exchange-offline' program.
*
* @param cls closure.
* @param cmd the commaind being run.
* @param is interpreter state.
*/
static void
extensionssign_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct ExtensionsSignState *ks = cls;
ks->extensionssign_proc
= GNUNET_OS_start_process (
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-offline",
"taler-exchange-offline",
"-c", ks->config_filename,
"-L", "INFO",
"extensions",
"sign",
"upload",
NULL);
if (NULL == ks->extensionssign_proc)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_wait_for_sigchld (is);
}
/**
* Free the state of a "extensionssign" CMD, and possibly kills its
* process if it did not terminate correctly.
*
* @param cls closure.
* @param cmd the command being freed.
*/
static void
extensionssign_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct ExtensionsSignState *ks = cls;
(void) cmd;
if (NULL != ks->extensionssign_proc)
{
GNUNET_break (0 ==
GNUNET_OS_process_kill (ks->extensionssign_proc,
SIGKILL));
GNUNET_OS_process_wait (ks->extensionssign_proc);
GNUNET_OS_process_destroy (ks->extensionssign_proc);
ks->extensionssign_proc = NULL;
}
GNUNET_free (ks);
}
/**
* Offer "extensionssign" CMD internal data to other commands.
*
* @param cls closure.
* @param[out] ret result
* @param trait name of the trait.
* @param index index number of the object to offer.
* @return #GNUNET_OK on success.
*/
static enum GNUNET_GenericReturnValue
extensionssign_traits (void *cls,
const void **ret,
const char *trait,
unsigned int index)
{
struct ExtensionsSignState *ks = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_process (&ks->extensionssign_proc),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
struct TALER_TESTING_Command
TALER_TESTING_cmd_exec_offline_sign_extensions (const char *label,
const char *config_filename)
{
struct ExtensionsSignState *ks;
ks = GNUNET_new (struct ExtensionsSignState);
ks->config_filename = config_filename;
{
struct TALER_TESTING_Command cmd = {
.cls = ks,
.label = label,
.run = &extensionssign_run,
.cleanup = &extensionssign_cleanup,
.traits = &extensionssign_traits
};
return cmd;
}
}
/* end of testing_api_cmd_exec_offline_sign_extensions.c */

View File

@ -70,6 +70,11 @@ struct TALER_TESTING_FreshCoinData
*/ */
struct TALER_CoinSpendPrivateKeyP coin_priv; struct TALER_CoinSpendPrivateKeyP coin_priv;
/*
* Age commitment for the coin, NULL if not applicable.
*/
struct TALER_AgeCommitment *age_commitment;
/** /**
* The blinding key (needed for recoup operations). * The blinding key (needed for recoup operations).
*/ */
@ -121,6 +126,11 @@ struct RefreshMeltState
*/ */
const struct TALER_CoinSpendPrivateKeyP *melt_priv; const struct TALER_CoinSpendPrivateKeyP *melt_priv;
/*
* Age commitment for the coin, NULL if not applicable.
*/
struct TALER_AgeCommitment *age_commitment;
/** /**
* Task scheduled to try later. * Task scheduled to try later.
*/ */
@ -992,6 +1002,7 @@ melt_run (void *cls,
const struct TALER_DenominationSignature *melt_sig; const struct TALER_DenominationSignature *melt_sig;
const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub; const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
const struct TALER_TESTING_Command *coin_command; const struct TALER_TESTING_Command *coin_command;
bool age_restricted;
if (NULL == (coin_command if (NULL == (coin_command
= TALER_TESTING_interpreter_lookup_command = TALER_TESTING_interpreter_lookup_command
@ -1012,6 +1023,16 @@ melt_run (void *cls,
return; return;
} }
if (GNUNET_OK !=
TALER_TESTING_get_trait_age_commitment (coin_command,
0,
&rms->age_commitment))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
if (GNUNET_OK != if (GNUNET_OK !=
TALER_TESTING_get_trait_denom_sig (coin_command, TALER_TESTING_get_trait_denom_sig (coin_command,
0, 0,
@ -1021,6 +1042,7 @@ melt_run (void *cls,
TALER_TESTING_interpreter_fail (rms->is); TALER_TESTING_interpreter_fail (rms->is);
return; return;
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_TESTING_get_trait_denom_pub (coin_command, TALER_TESTING_get_trait_denom_pub (coin_command,
0, 0,
@ -1030,9 +1052,11 @@ melt_run (void *cls,
TALER_TESTING_interpreter_fail (rms->is); TALER_TESTING_interpreter_fail (rms->is);
return; return;
} }
/* Melt amount starts with the melt fee of the old coin; we'll add the /* Melt amount starts with the melt fee of the old coin; we'll add the
values and withdraw fees of the fresh coins next */ values and withdraw fees of the fresh coins next */
melt_amount = melt_denom_pub->fee_refresh; melt_amount = melt_denom_pub->fee_refresh;
age_restricted = melt_denom_pub->key.age_mask.mask != 0;
for (unsigned int i = 0; i<num_fresh_coins; i++) for (unsigned int i = 0; i<num_fresh_coins; i++)
{ {
const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk; const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
@ -1048,8 +1072,9 @@ melt_run (void *cls,
TALER_TESTING_interpreter_fail (rms->is); TALER_TESTING_interpreter_fail (rms->is);
return; return;
} }
fresh_pk = TALER_TESTING_find_pk fresh_pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
(TALER_EXCHANGE_get_keys (is->exchange), &fresh_amount); &fresh_amount,
age_restricted);
if (NULL == fresh_pk) if (NULL == fresh_pk)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1071,13 +1096,33 @@ melt_run (void *cls,
&fresh_pk->key); &fresh_pk->key);
} /* end for */ } /* end for */
GNUNET_assert (NULL == rms->refresh_data); GNUNET_assert (NULL == rms->refresh_data);
rms->refresh_data {
= TALER_EXCHANGE_refresh_prepare (rms->melt_priv, struct TALER_AgeCommitment *ac = NULL;
&melt_amount,
melt_sig, GNUNET_assert (age_restricted == (NULL != rms->age_commitment));
melt_denom_pub,
num_fresh_coins, if (NULL != rms->age_commitment)
rms->fresh_pks); {
uint32_t seed = GNUNET_CRYPTO_random_u32 (
GNUNET_CRYPTO_QUALITY_WEAK,
UINT32_MAX);
GNUNET_assert (GNUNET_OK ==
TALER_age_commitment_derive (
rms->age_commitment,
seed,
ac));
}
rms->refresh_data
= TALER_EXCHANGE_refresh_prepare (rms->melt_priv,
&melt_amount,
melt_sig,
melt_denom_pub,
ac,
num_fresh_coins,
rms->fresh_pks);
}
if (NULL == rms->refresh_data) if (NULL == rms->refresh_data)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1168,6 +1213,8 @@ melt_traits (void *cls,
&rms->fresh_pks[index]), &rms->fresh_pks[index]),
TALER_TESTING_make_trait_coin_priv (0, TALER_TESTING_make_trait_coin_priv (0,
rms->melt_priv), rms->melt_priv),
TALER_TESTING_make_trait_age_commitment (index,
rms->age_commitment),
TALER_TESTING_trait_end () TALER_TESTING_trait_end ()
}; };
@ -1326,6 +1373,9 @@ refresh_reveal_traits (void *cls,
TALER_TESTING_make_trait_coin_priv ( TALER_TESTING_make_trait_coin_priv (
index, index,
&rrs->fresh_coins[index].coin_priv), &rrs->fresh_coins[index].coin_priv),
TALER_TESTING_make_trait_age_commitment (
index,
rrs->fresh_coins[index].age_commitment),
TALER_TESTING_make_trait_denom_pub ( TALER_TESTING_make_trait_denom_pub (
index, index,
rrs->fresh_coins[index].pk), rrs->fresh_coins[index].pk),

View File

@ -27,6 +27,7 @@
#include <microhttpd.h> #include <microhttpd.h>
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "taler_signatures.h" #include "taler_signatures.h"
#include "taler_extensions.h"
#include "taler_testing_lib.h" #include "taler_testing_lib.h"
#include "backoff.h" #include "backoff.h"
@ -115,6 +116,18 @@ struct WithdrawState
*/ */
struct TALER_PlanchetSecretsP ps; struct TALER_PlanchetSecretsP ps;
/**
* An age > 0 signifies age restriction is required
*/
uint8_t age;
/**
* If age > 0, put here the corresponding age commitment and its hash,
* respectivelly, NULL otherwise.
*/
struct TALER_AgeCommitment *age_commitment;
struct TALER_AgeCommitmentHash *h_age_commitment;
/** /**
* Reserve history entry that corresponds to this operation. * Reserve history entry that corresponds to this operation.
* Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL. * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
@ -363,12 +376,14 @@ withdraw_run (void *cls,
= TALER_TESTING_interpreter_lookup_command ( = TALER_TESTING_interpreter_lookup_command (
is, is,
ws->reserve_reference); ws->reserve_reference);
if (NULL == create_reserve) if (NULL == create_reserve)
{ {
GNUNET_break (0); GNUNET_break (0);
TALER_TESTING_interpreter_fail (is); TALER_TESTING_interpreter_fail (is);
return; return;
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_TESTING_get_trait_reserve_priv (create_reserve, TALER_TESTING_get_trait_reserve_priv (create_reserve,
&rp)) &rp))
@ -377,6 +392,7 @@ withdraw_run (void *cls,
TALER_TESTING_interpreter_fail (is); TALER_TESTING_interpreter_fail (is);
return; return;
} }
if (NULL == ws->exchange_url) if (NULL == ws->exchange_url)
ws->exchange_url ws->exchange_url
= GNUNET_strdup (TALER_EXCHANGE_get_base_url (is->exchange)); = GNUNET_strdup (TALER_EXCHANGE_get_base_url (is->exchange));
@ -386,6 +402,7 @@ withdraw_run (void *cls,
ws->reserve_payto_uri ws->reserve_payto_uri
= TALER_payto_from_reserve (ws->exchange_url, = TALER_payto_from_reserve (ws->exchange_url,
&ws->reserve_pub); &ws->reserve_pub);
if (NULL == ws->reuse_coin_key_ref) if (NULL == ws->reuse_coin_key_ref)
{ {
TALER_planchet_setup_random (&ws->ps); TALER_planchet_setup_random (&ws->ps);
@ -412,10 +429,12 @@ withdraw_run (void *cls,
TALER_planchet_setup_random (&ws->ps); TALER_planchet_setup_random (&ws->ps);
ws->ps.coin_priv = *coin_priv; ws->ps.coin_priv = *coin_priv;
} }
if (NULL == ws->pk) if (NULL == ws->pk)
{ {
dpk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange), dpk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
&ws->amount); &ws->amount,
ws->age > 0);
if (NULL == dpk) if (NULL == dpk)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -433,18 +452,24 @@ withdraw_run (void *cls,
{ {
ws->amount = ws->pk->value; ws->amount = ws->pk->value;
} }
ws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL; ws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL;
GNUNET_assert (0 <= GNUNET_assert (0 <=
TALER_amount_add (&ws->reserve_history.amount, TALER_amount_add (&ws->reserve_history.amount,
&ws->amount, &ws->amount,
&ws->pk->fee_withdraw)); &ws->pk->fee_withdraw));
ws->reserve_history.details.withdraw.fee = ws->pk->fee_withdraw; ws->reserve_history.details.withdraw.fee = ws->pk->fee_withdraw;
ws->wsh = TALER_EXCHANGE_withdraw (is->exchange,
ws->pk, {
rp,
&ws->ps, ws->wsh = TALER_EXCHANGE_withdraw (is->exchange,
&reserve_withdraw_cb, ws->pk,
ws); rp,
&ws->ps,
ws->h_age_commitment,
&reserve_withdraw_cb,
ws);
}
if (NULL == ws->wsh) if (NULL == ws->wsh)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -486,6 +511,16 @@ withdraw_cleanup (void *cls,
TALER_EXCHANGE_destroy_denomination_key (ws->pk); TALER_EXCHANGE_destroy_denomination_key (ws->pk);
ws->pk = NULL; ws->pk = NULL;
} }
if (NULL != ws->age_commitment)
{
GNUNET_free (ws->age_commitment);
ws->age_commitment = NULL;
}
if (NULL != ws->h_age_commitment)
{
GNUNET_free (ws->h_age_commitment);
ws->h_age_commitment = NULL;
}
GNUNET_free (ws->exchange_url); GNUNET_free (ws->exchange_url);
GNUNET_free (ws->reserve_payto_uri); GNUNET_free (ws->reserve_payto_uri);
GNUNET_free (ws); GNUNET_free (ws);
@ -512,13 +547,13 @@ withdraw_traits (void *cls,
struct TALER_TESTING_Trait traits[] = { struct TALER_TESTING_Trait traits[] = {
/* history entry MUST be first due to response code logic below! */ /* history entry MUST be first due to response code logic below! */
TALER_TESTING_make_trait_reserve_history (&ws->reserve_history), TALER_TESTING_make_trait_reserve_history (&ws->reserve_history),
TALER_TESTING_make_trait_coin_priv (0 /* only one coin */, TALER_TESTING_make_trait_coin_priv (index /* only one coin */,
&ws->ps.coin_priv), &ws->ps.coin_priv),
TALER_TESTING_make_trait_blinding_key (0 /* only one coin */, TALER_TESTING_make_trait_blinding_key (index /* only one coin */,
&ws->ps.blinding_key), &ws->ps.blinding_key),
TALER_TESTING_make_trait_denom_pub (0 /* only one coin */, TALER_TESTING_make_trait_denom_pub (index /* only one coin */,
ws->pk), ws->pk),
TALER_TESTING_make_trait_denom_sig (0 /* only one coin */, TALER_TESTING_make_trait_denom_sig (index /* only one coin */,
&ws->sig), &ws->sig),
TALER_TESTING_make_trait_reserve_priv (&ws->reserve_priv), TALER_TESTING_make_trait_reserve_priv (&ws->reserve_priv),
TALER_TESTING_make_trait_reserve_pub (&ws->reserve_pub), TALER_TESTING_make_trait_reserve_pub (&ws->reserve_pub),
@ -528,6 +563,8 @@ withdraw_traits (void *cls,
(const char **) &ws->reserve_payto_uri), (const char **) &ws->reserve_payto_uri),
TALER_TESTING_make_trait_exchange_url ( TALER_TESTING_make_trait_exchange_url (
(const char **) &ws->exchange_url), (const char **) &ws->exchange_url),
TALER_TESTING_make_trait_age_commitment (index, ws->age_commitment),
TALER_TESTING_make_trait_h_age_commitment (index, ws->h_age_commitment),
TALER_TESTING_trait_end () TALER_TESTING_trait_end ()
}; };
@ -547,6 +584,7 @@ withdraw_traits (void *cls,
* @param label command label. * @param label command label.
* @param reserve_reference command providing us with a reserve to withdraw from * @param reserve_reference command providing us with a reserve to withdraw from
* @param amount how much we withdraw. * @param amount how much we withdraw.
* @param age if > 0, age restriction is activated
* @param expected_response_code which HTTP response code * @param expected_response_code which HTTP response code
* we expect from the exchange. * we expect from the exchange.
* @return the withdraw command to be executed by the interpreter. * @return the withdraw command to be executed by the interpreter.
@ -555,11 +593,45 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_withdraw_amount (const char *label, TALER_TESTING_cmd_withdraw_amount (const char *label,
const char *reserve_reference, const char *reserve_reference,
const char *amount, const char *amount,
const uint8_t age,
unsigned int expected_response_code) unsigned int expected_response_code)
{ {
struct WithdrawState *ws; struct WithdrawState *ws;
ws = GNUNET_new (struct WithdrawState); ws = GNUNET_new (struct WithdrawState);
ws->age = age;
if (0 < age)
{
struct TALER_AgeCommitment *ac;
struct TALER_AgeCommitmentHash *hac;
uint32_t seed;
struct TALER_AgeMask mask;
ac = GNUNET_new (struct TALER_AgeCommitment);
hac = GNUNET_new (struct TALER_AgeCommitmentHash);
seed = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX);
mask = TALER_extensions_age_restriction_ageMask ();
if (GNUNET_OK !=
TALER_age_restriction_commit (
&mask,
age,
seed,
ac))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to generate age commitment for age %d at %s\n",
age,
label);
GNUNET_assert (0);
}
TALER_age_commitment_hash (ac,hac);
ws->age_commitment = ac;
ws->h_age_commitment = hac;
}
ws->reserve_reference = reserve_reference; ws->reserve_reference = reserve_reference;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_string_to_amount (amount, TALER_string_to_amount (amount,
@ -595,6 +667,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
* @param label command label. * @param label command label.
* @param reserve_reference command providing us with a reserve to withdraw from * @param reserve_reference command providing us with a reserve to withdraw from
* @param amount how much we withdraw. * @param amount how much we withdraw.
* @param age if > 0, age restriction is activated
* @param coin_ref reference to (withdraw/reveal) command of a coin * @param coin_ref reference to (withdraw/reveal) command of a coin
* from which we should re-use the private key * from which we should re-use the private key
* @param expected_response_code which HTTP response code * @param expected_response_code which HTTP response code
@ -606,6 +679,7 @@ TALER_TESTING_cmd_withdraw_amount_reuse_key (
const char *label, const char *label,
const char *reserve_reference, const char *reserve_reference,
const char *amount, const char *amount,
uint8_t age,
const char *coin_ref, const char *coin_ref,
unsigned int expected_response_code) unsigned int expected_response_code)
{ {
@ -614,6 +688,7 @@ TALER_TESTING_cmd_withdraw_amount_reuse_key (
cmd = TALER_TESTING_cmd_withdraw_amount (label, cmd = TALER_TESTING_cmd_withdraw_amount (label,
reserve_reference, reserve_reference,
amount, amount,
age,
expected_response_code); expected_response_code);
{ {
struct WithdrawState *ws = cmd.cls; struct WithdrawState *ws = cmd.cls;

View File

@ -27,6 +27,7 @@
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "taler_signatures.h" #include "taler_signatures.h"
#include "taler_extensions.h"
#include "taler_testing_lib.h" #include "taler_testing_lib.h"
/** /**
@ -312,6 +313,9 @@ sign_keys_for_exchange (void *cls,
char *exchange_master_pub; char *exchange_master_pub;
int ret; int ret;
/* Load the age restriction mask from the configuration */
TALER_extensions_load_taler_config (cfg);
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg, GNUNET_CONFIGURATION_get_value_string (cfg,
"exchange", "exchange",
@ -416,11 +420,13 @@ TALER_TESTING_prepare_exchange (const char *config_filename,
* *
* @param keys array of keys to search * @param keys array of keys to search
* @param amount coin value to look for * @param amount coin value to look for
* @param age_restricted must denomination support age restriction?
* @return NULL if no matching key was found * @return NULL if no matching key was found
*/ */
const struct TALER_EXCHANGE_DenomPublicKey * const struct TALER_EXCHANGE_DenomPublicKey *
TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys, TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
const struct TALER_Amount *amount) const struct TALER_Amount *amount,
bool age_restricted)
{ {
struct GNUNET_TIME_Timestamp now; struct GNUNET_TIME_Timestamp now;
struct TALER_EXCHANGE_DenomPublicKey *pk; struct TALER_EXCHANGE_DenomPublicKey *pk;
@ -437,7 +443,8 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
pk->valid_from)) && pk->valid_from)) &&
(GNUNET_TIME_timestamp_cmp (now, (GNUNET_TIME_timestamp_cmp (now,
<, <,
pk->withdraw_valid_until)) ) pk->withdraw_valid_until)) &&
(age_restricted == (0 != pk->key.age_mask.mask)) )
return pk; return pk;
} }
/* do 2nd pass to check if expiration times are to blame for /* do 2nd pass to check if expiration times are to blame for
@ -453,7 +460,8 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
pk->valid_from) || pk->valid_from) ||
GNUNET_TIME_timestamp_cmp (now, GNUNET_TIME_timestamp_cmp (now,
>, >,
pk->withdraw_valid_until) ) ) pk->withdraw_valid_until) ) &&
(age_restricted == (0 != pk->key.age_mask.mask)) )
{ {
GNUNET_log GNUNET_log
(GNUNET_ERROR_TYPE_WARNING, (GNUNET_ERROR_TYPE_WARNING,

View File

@ -20,11 +20,16 @@
* @author Florian Dold * @author Florian Dold
* @author Benedikt Mueller * @author Benedikt Mueller
* @author Christian Grothoff * @author Christian Grothoff
* @author Özgür Kesim
*/ */
#include "platform.h" #include "platform.h"
#include "taler_util.h" #include "taler_util.h"
#include <gcrypt.h> #include <gcrypt.h>
/**
* Used in TALER_AgeCommitmentHash_isNullOrZero for comparison
*/
const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash = {0};
/** /**
* Function called by libgcrypt on serious errors. * Function called by libgcrypt on serious errors.
@ -83,12 +88,11 @@ TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info,
GNUNET_memcmp (&d_hash, GNUNET_memcmp (&d_hash,
&coin_public_info->denom_pub_hash)); &coin_public_info->denom_pub_hash));
#endif #endif
// FIXME-Oec: replace with function that
// also hashes the age vector if we have TALER_coin_pub_hash (&coin_public_info->coin_pub,
// one! &coin_public_info->age_commitment_hash,
GNUNET_CRYPTO_hash (&coin_public_info->coin_pub, &c_hash);
sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
&c_hash.hash);
if (GNUNET_OK != if (GNUNET_OK !=
TALER_denom_pub_verify (denom_pub, TALER_denom_pub_verify (denom_pub,
&coin_public_info->denom_sig, &coin_public_info->denom_sig,
@ -178,6 +182,7 @@ TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps)
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk, TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
const struct TALER_PlanchetSecretsP *ps, const struct TALER_PlanchetSecretsP *ps,
const struct TALER_AgeCommitmentHash *ach,
struct TALER_CoinPubHash *c_hash, struct TALER_CoinPubHash *c_hash,
struct TALER_PlanchetDetail *pd) struct TALER_PlanchetDetail *pd)
{ {
@ -188,7 +193,7 @@ TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
if (GNUNET_OK != if (GNUNET_OK !=
TALER_denom_blind (dk, TALER_denom_blind (dk,
&ps->blinding_key, &ps->blinding_key,
NULL, /* FIXME-Oec */ ach,
&coin_pub, &coin_pub,
c_hash, c_hash,
&pd->coin_ev, &pd->coin_ev,
@ -208,6 +213,7 @@ TALER_planchet_to_coin (
const struct TALER_DenominationPublicKey *dk, const struct TALER_DenominationPublicKey *dk,
const struct TALER_BlindedDenominationSignature *blind_sig, const struct TALER_BlindedDenominationSignature *blind_sig,
const struct TALER_PlanchetSecretsP *ps, const struct TALER_PlanchetSecretsP *ps,
const struct TALER_AgeCommitmentHash *ach,
const struct TALER_CoinPubHash *c_hash, const struct TALER_CoinPubHash *c_hash,
struct TALER_FreshCoin *coin) struct TALER_FreshCoin *coin)
{ {
@ -233,6 +239,7 @@ TALER_planchet_to_coin (
} }
coin->sig = sig; coin->sig = sig;
coin->coin_priv = ps->coin_priv; coin->coin_priv = ps->coin_priv;
coin->h_age_commitment = ach;
return GNUNET_OK; return GNUNET_OK;
} }
@ -319,10 +326,10 @@ TALER_coin_ev_hash (const void *coin_ev,
void void
TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub, TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_AgeHash *age_commitment_hash, const struct TALER_AgeCommitmentHash *ach,
struct TALER_CoinPubHash *coin_h) struct TALER_CoinPubHash *coin_h)
{ {
if (NULL == age_commitment_hash) if (TALER_AgeCommitmentHash_isNullOrZero (ach))
{ {
/* No age commitment was set */ /* No age commitment was set */
GNUNET_CRYPTO_hash (&coin_pub->eddsa_pub, GNUNET_CRYPTO_hash (&coin_pub->eddsa_pub,
@ -334,7 +341,7 @@ TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
/* Coin comes with age commitment. Take the hash of the age commitment /* Coin comes with age commitment. Take the hash of the age commitment
* into account */ * into account */
const size_t key_s = sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey); const size_t key_s = sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey);
const size_t age_s = sizeof(struct TALER_AgeHash); const size_t age_s = sizeof(struct TALER_AgeCommitmentHash);
char data[key_s + age_s]; char data[key_s + age_s];
GNUNET_memcpy (&data[0], GNUNET_memcpy (&data[0],
@ -342,7 +349,7 @@ TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
key_s); key_s);
GNUNET_memcpy (&data[key_s], GNUNET_memcpy (&data[key_s],
age_commitment_hash, ach,
age_s); age_s);
GNUNET_CRYPTO_hash (&data, GNUNET_CRYPTO_hash (&data,
@ -352,4 +359,276 @@ TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
} }
void
TALER_age_commitment_hash (
const struct TALER_AgeCommitment *commitment,
struct TALER_AgeCommitmentHash *ahash)
{
struct GNUNET_HashContext *hash_context;
struct GNUNET_HashCode hash;
GNUNET_assert (NULL != ahash);
if (NULL == commitment)
{
memset (ahash, 0, sizeof(struct TALER_AgeCommitmentHash));
return;
}
GNUNET_assert (__builtin_popcount (commitment->mask.mask) - 1 ==
commitment->num_pub);
hash_context = GNUNET_CRYPTO_hash_context_start ();
for (size_t i = 0; i < commitment->num_pub; i++)
{
GNUNET_CRYPTO_hash_context_read (hash_context,
&commitment->pub[i],
sizeof(struct
GNUNET_CRYPTO_EddsaPublicKey));
}
GNUNET_CRYPTO_hash_context_finish (hash_context,
&hash);
GNUNET_memcpy (&ahash->shash.bits,
&hash.bits,
sizeof(ahash->shash.bits));
}
/* To a given age value between 0 and 31, returns the index of the age group
* defined by the given mask.
*/
static uint8_t
get_age_group (
const struct TALER_AgeMask *mask,
uint8_t age)
{
uint32_t m = mask->mask;
uint8_t i = 0;
while (m > 0)
{
if (0 >= age)
break;
m = m >> 1;
i += m & 1;
age--;
}
return i;
}
enum GNUNET_GenericReturnValue
TALER_age_restriction_commit (
const struct TALER_AgeMask *mask,
const uint8_t age,
const uint32_t seed,
struct TALER_AgeCommitment *new)
{
uint8_t num_pub = __builtin_popcount (mask->mask) - 1;
uint8_t num_priv = get_age_group (mask, age) - 1;
size_t i;
GNUNET_assert (NULL != new);
GNUNET_assert (mask->mask & 1); /* fist bit must have been set */
GNUNET_assert (0 <= num_priv);
GNUNET_assert (31 > num_priv);
new->mask.mask = mask->mask;
new->num_pub = num_pub;
new->num_priv = num_priv;
new->pub = GNUNET_new_array (
num_pub,
struct TALER_AgeCommitmentPublicKeyP);
new->priv = GNUNET_new_array (
num_priv,
struct TALER_AgeCommitmentPrivateKeyP);
/* Create as many private keys as we need */
for (i = 0; i < num_priv; i++)
{
uint32_t seedBE = htonl (seed + i);
if (GNUNET_OK !=
GNUNET_CRYPTO_kdf (&new->priv[i],
sizeof (new->priv[i]),
&seedBE,
sizeof (seedBE),
"taler-age-commitment-derivation",
strlen (
"taler-age-commitment-derivation"),
NULL, 0))
goto FAIL;
GNUNET_CRYPTO_eddsa_key_get_public (&new->priv[i].eddsa_priv,
&new->pub[i].eddsa_pub);
}
/* Fill the rest of the public keys with random values */
for (; i<num_pub; i++)
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&new->pub[i],
sizeof(new->pub[i]));
return GNUNET_OK;
FAIL:
GNUNET_free (new->pub);
GNUNET_free (new->priv);
return GNUNET_SYSERR;
}
enum GNUNET_GenericReturnValue
TALER_age_commitment_derive (
const struct TALER_AgeCommitment *orig,
const uint32_t seed,
struct TALER_AgeCommitment *new)
{
struct GNUNET_CRYPTO_EccScalar val;
/*
* age commitment consists of GNUNET_CRYPTO_Eddsa{Private,Public}Key
*
* GNUNET_CRYPTO_EddsaPrivateKey is a
* unsigned char d[256 / 8];
*
* GNUNET_CRYPTO_EddsaPublicKey is a
* unsigned char q_y[256 / 8];
*
* We want to multiply, both, the Private Key by an integer factor and the
* public key (point on curve) with the equivalent scalar.
*
* From the seed we will derive
* 1. a scalar to multiply the public keys with
* 2. a factor to multiply the private key with
*
* Invariants:
* point*scalar == public(private*factor)
*
* A point on a curve is GNUNET_CRYPTO_EccPoint which is
* unsigned char v[256 / 8];
*
* A ECC scaler for use in point multiplications is a
* GNUNET_CRYPTO_EccScalar which is a
* unsigned car v[256 / 8];
* */
GNUNET_assert (NULL != new);
GNUNET_assert (orig->num_pub == __builtin_popcount (orig->mask.mask) - 1);
GNUNET_assert (orig->num_priv <= orig->num_pub);
new->mask = orig->mask;
new->num_pub = orig->num_pub;
new->num_priv = orig->num_priv;
new->pub = GNUNET_new_array (
new->num_pub,
struct TALER_AgeCommitmentPublicKeyP);
new->priv = GNUNET_new_array (
new->num_priv,
struct TALER_AgeCommitmentPrivateKeyP);
GNUNET_CRYPTO_ecc_scalar_from_int (seed, &val);
/* scalar multiply the public keys on the curve */
for (size_t i = 0; i < orig->num_pub; i++)
{
/* We shift all keys by the same scalar */
struct GNUNET_CRYPTO_EccPoint *p = (struct
GNUNET_CRYPTO_EccPoint *) &orig->pub[i];
struct GNUNET_CRYPTO_EccPoint *np = (struct
GNUNET_CRYPTO_EccPoint *) &new->pub[i];
if (GNUNET_OK !=
GNUNET_CRYPTO_ecc_pmul_mpi (
p,
&val,
np))
goto FAIL;
}
/* multiply the private keys */
/* we borough ideas from GNUNET_CRYPTO_ecdsa_private_key_derive */
{
uint32_t seedBE;
uint8_t dc[32];
gcry_mpi_t f, x, d, n;
gcry_ctx_t ctx;
GNUNET_assert (0==gcry_mpi_ec_new (&ctx,NULL, "Ed25519"));
n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
/* make the seed big endian */
seedBE = GNUNET_htonll (seed);
GNUNET_CRYPTO_mpi_scan_unsigned (&f, &seedBE, sizeof(seedBE));
for (size_t i = 0; i < orig->num_priv; i++)
{
/* convert to big endian for libgrypt */
for (size_t j = 0; j < 32; j++)
dc[i] = orig->priv[i].eddsa_priv.d[31 - j];
GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc));
d = gcry_mpi_new (256);
gcry_mpi_mulm (d, f, x, n);
gcry_mpi_release (x);
gcry_mpi_release (d);
gcry_mpi_release (n);
gcry_mpi_release (d);
GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
for (size_t j = 0; j <32; j++)
new->priv[i].eddsa_priv.d[j] = dc[31 - 1];
sodium_memzero (dc, sizeof(dc));
/* TODO:
* make sure that the calculated private key generate the same public
* keys */
}
gcry_mpi_release (f);
gcry_ctx_release (ctx);
}
return GNUNET_OK;
FAIL:
GNUNET_free (new->pub);
GNUNET_free (new->priv);
return GNUNET_SYSERR;
}
void
TALER_age_restriction_commmitment_free_inside (
struct TALER_AgeCommitment *commitment)
{
if (NULL == commitment)
return;
if (NULL != commitment->priv)
{
GNUNET_CRYPTO_zero_keys (
commitment->priv,
sizeof(*commitment->priv) * commitment->num_priv);
GNUNET_free (commitment->priv);
commitment->priv = NULL;
}
if (NULL != commitment->pub)
{
GNUNET_free (commitment->pub);
commitment->priv = NULL;
}
/* Caller is responsible for commitment itself */
}
/* end of crypto.c */ /* end of crypto.c */

View File

@ -235,7 +235,7 @@ TALER_denom_priv_to_pub (const struct TALER_DenominationPrivateKey *denom_priv,
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_denom_blind (const struct TALER_DenominationPublicKey *dk, TALER_denom_blind (const struct TALER_DenominationPublicKey *dk,
const union TALER_DenominationBlindingKeyP *coin_bks, const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_AgeHash *age_commitment_hash, const struct TALER_AgeCommitmentHash *ach,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_CoinPubHash *c_hash, struct TALER_CoinPubHash *c_hash,
void **coin_ev, void **coin_ev,
@ -245,7 +245,7 @@ TALER_denom_blind (const struct TALER_DenominationPublicKey *dk,
{ {
case TALER_DENOMINATION_RSA: case TALER_DENOMINATION_RSA:
TALER_coin_pub_hash (coin_pub, TALER_coin_pub_hash (coin_pub,
age_commitment_hash, ach,
c_hash); c_hash);
if (GNUNET_YES != if (GNUNET_YES !=
GNUNET_CRYPTO_rsa_blind (&c_hash->hash, GNUNET_CRYPTO_rsa_blind (&c_hash->hash,

View File

@ -101,6 +101,7 @@ test_planchets (void)
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_planchet_prepare (&dk_pub, TALER_planchet_prepare (&dk_pub,
&ps, &ps,
NULL, /* no age commitment */
&c_hash, &c_hash,
&pd)); &pd));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
@ -112,6 +113,62 @@ test_planchets (void)
TALER_planchet_to_coin (&dk_pub, TALER_planchet_to_coin (&dk_pub,
&blind_sig, &blind_sig,
&ps, &ps,
NULL, /* no age commitment */
&c_hash,
&coin));
TALER_blinded_denom_sig_free (&blind_sig);
TALER_denom_sig_free (&coin.sig);
TALER_denom_priv_free (&dk_priv);
TALER_denom_pub_free (&dk_pub);
return 0;
}
/**
* Test the basic planchet functionality of creating a fresh planchet and
* extracting the respective signature, this time _with_ age commitment.
*
* @return 0 on success
*/
static int
test_planchets_with_age_commitment (void)
{
struct TALER_PlanchetSecretsP ps;
struct TALER_AgeCommitmentHash ach;
struct TALER_DenominationPrivateKey dk_priv;
struct TALER_DenominationPublicKey dk_pub;
struct TALER_PlanchetDetail pd;
struct TALER_BlindedDenominationSignature blind_sig;
struct TALER_FreshCoin coin;
struct TALER_CoinPubHash c_hash;
GNUNET_assert (GNUNET_OK ==
TALER_denom_priv_create (&dk_priv,
&dk_pub,
TALER_DENOMINATION_RSA,
1024));
TALER_planchet_setup_random (&ps);
GNUNET_CRYPTO_random_block (
GNUNET_CRYPTO_QUALITY_WEAK,
&ach,
sizeof(struct TALER_AgeCommitmentHash));
GNUNET_assert (GNUNET_OK ==
TALER_planchet_prepare (&dk_pub,
&ps,
&ach,
&c_hash,
&pd));
GNUNET_assert (GNUNET_OK ==
TALER_denom_sign_blinded (&blind_sig,
&dk_priv,
pd.coin_ev,
pd.coin_ev_size));
GNUNET_assert (GNUNET_OK ==
TALER_planchet_to_coin (&dk_pub,
&blind_sig,
&ps,
&ach,
&c_hash, &c_hash,
&coin)); &coin));
TALER_blinded_denom_sig_free (&blind_sig); TALER_blinded_denom_sig_free (&blind_sig);
@ -221,10 +278,12 @@ main (int argc,
return 1; return 1;
if (0 != test_planchets ()) if (0 != test_planchets ())
return 2; return 2;
if (0 != test_exchange_sigs ()) if (0 != test_planchets_with_age_commitment ())
return 3; return 3;
if (0 != test_merchant_sigs ()) if (0 != test_exchange_sigs ())
return 4; return 4;
if (0 != test_merchant_sigs ())
return 5;
return 0; return 0;
} }

View File

@ -268,9 +268,13 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh)
enum TALER_ErrorCode ec; enum TALER_ErrorCode ec;
bool success = false; bool success = false;
struct TALER_PlanchetSecretsP ps; struct TALER_PlanchetSecretsP ps;
struct TALER_AgeCommitmentHash ach;
struct TALER_CoinPubHash c_hash; struct TALER_CoinPubHash c_hash;
TALER_planchet_setup_random (&ps); TALER_planchet_setup_random (&ps);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&ach,
sizeof(ach));
for (unsigned int i = 0; i<MAX_KEYS; i++) for (unsigned int i = 0; i<MAX_KEYS; i++)
{ {
if (! keys[i].valid) if (! keys[i].valid)
@ -281,6 +285,7 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh)
GNUNET_assert (GNUNET_YES == GNUNET_assert (GNUNET_YES ==
TALER_planchet_prepare (&keys[i].denom_pub, TALER_planchet_prepare (&keys[i].denom_pub,
&ps, &ps,
&ach,
&c_hash, &c_hash,
&pd)); &pd));
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -418,8 +423,12 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
enum TALER_ErrorCode ec; enum TALER_ErrorCode ec;
struct GNUNET_TIME_Relative duration; struct GNUNET_TIME_Relative duration;
struct TALER_PlanchetSecretsP ps; struct TALER_PlanchetSecretsP ps;
struct TALER_AgeCommitmentHash ach;
TALER_planchet_setup_random (&ps); TALER_planchet_setup_random (&ps);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&ach,
sizeof(ach));
duration = GNUNET_TIME_UNIT_ZERO; duration = GNUNET_TIME_UNIT_ZERO;
TALER_CRYPTO_helper_rsa_poll (dh); TALER_CRYPTO_helper_rsa_poll (dh);
for (unsigned int j = 0; j<NUM_SIGN_PERFS;) for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
@ -445,6 +454,7 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
GNUNET_assert (GNUNET_YES == GNUNET_assert (GNUNET_YES ==
TALER_planchet_prepare (&keys[i].denom_pub, TALER_planchet_prepare (&keys[i].denom_pub,
&ps, &ps,
&ach,
&c_hash, &c_hash,
&pd)); &pd));
/* use this key as long as it works */ /* use this key as long as it works */

View File

@ -29,6 +29,7 @@ TALER_wallet_deposit_sign (
const struct TALER_Amount *deposit_fee, const struct TALER_Amount *deposit_fee,
const struct TALER_MerchantWireHash *h_wire, const struct TALER_MerchantWireHash *h_wire,
const struct TALER_PrivateContractHash *h_contract_terms, const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_ExtensionContractHash *h_extensions, const struct TALER_ExtensionContractHash *h_extensions,
const struct TALER_DenominationHash *h_denom_pub, const struct TALER_DenominationHash *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp, struct GNUNET_TIME_Timestamp wallet_timestamp,
@ -48,8 +49,12 @@ TALER_wallet_deposit_sign (
.merchant = *merchant_pub .merchant = *merchant_pub
}; };
if (NULL != h_age_commitment)
dr.h_age_commitment = *h_age_commitment;
if (NULL != h_extensions) if (NULL != h_extensions)
dr.h_extensions = *h_extensions; dr.h_extensions = *h_extensions;
TALER_amount_hton (&dr.amount_with_fee, TALER_amount_hton (&dr.amount_with_fee,
amount); amount);
TALER_amount_hton (&dr.deposit_fee, TALER_amount_hton (&dr.deposit_fee,
@ -66,6 +71,7 @@ TALER_wallet_deposit_verify (
const struct TALER_Amount *deposit_fee, const struct TALER_Amount *deposit_fee,
const struct TALER_MerchantWireHash *h_wire, const struct TALER_MerchantWireHash *h_wire,
const struct TALER_PrivateContractHash *h_contract_terms, const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_ExtensionContractHash *h_extensions, const struct TALER_ExtensionContractHash *h_extensions,
const struct TALER_DenominationHash *h_denom_pub, const struct TALER_DenominationHash *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp, struct GNUNET_TIME_Timestamp wallet_timestamp,
@ -82,11 +88,21 @@ TALER_wallet_deposit_verify (
.h_denom_pub = *h_denom_pub, .h_denom_pub = *h_denom_pub,
.wallet_timestamp = GNUNET_TIME_timestamp_hton (wallet_timestamp), .wallet_timestamp = GNUNET_TIME_timestamp_hton (wallet_timestamp),
.refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline), .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline),
.merchant = *merchant_pub .merchant = *merchant_pub,
.h_age_commitment = {{{0}}},
.h_extensions = {{{0}}}
}; };
if (NULL != h_age_commitment)
{
dr.h_age_commitment = *h_age_commitment;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"VFVFVFVF got NON-NULL h_age_commitment\n");
}
if (NULL != h_extensions) if (NULL != h_extensions)
dr.h_extensions = *h_extensions; dr.h_extensions = *h_extensions;
TALER_amount_hton (&dr.amount_with_fee, TALER_amount_hton (&dr.amount_with_fee,
amount); amount);
TALER_amount_hton (&dr.deposit_fee, TALER_amount_hton (&dr.deposit_fee,
@ -135,6 +151,7 @@ TALER_wallet_link_verify (
const struct TALER_TransferPublicKeyP *transfer_pub, const struct TALER_TransferPublicKeyP *transfer_pub,
const struct TALER_BlindedCoinHash *h_coin_ev, const struct TALER_BlindedCoinHash *h_coin_ev,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub, const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendSignatureP *coin_sig) const struct TALER_CoinSpendSignatureP *coin_sig)
{ {
struct TALER_LinkDataPS ldp = { struct TALER_LinkDataPS ldp = {
@ -142,9 +159,13 @@ TALER_wallet_link_verify (
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK),
.h_denom_pub = *h_denom_pub, .h_denom_pub = *h_denom_pub,
.transfer_pub = *transfer_pub, .transfer_pub = *transfer_pub,
.coin_envelope_hash = *h_coin_ev .coin_envelope_hash = *h_coin_ev,
.h_age_commitment = {{{0}}}
}; };
if (NULL != h_age_commitment)
ldp.h_age_commitment = *h_age_commitment;
return return
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
&ldp, &ldp,
@ -267,6 +288,7 @@ TALER_wallet_melt_verify (
const struct TALER_Amount *melt_fee, const struct TALER_Amount *melt_fee,
const struct TALER_RefreshCommitmentP *rc, const struct TALER_RefreshCommitmentP *rc,
const struct TALER_DenominationHash *h_denom_pub, const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig) const struct TALER_CoinSpendSignatureP *coin_sig)
{ {
@ -274,9 +296,13 @@ TALER_wallet_melt_verify (
.purpose.size = htonl (sizeof (melt)), .purpose.size = htonl (sizeof (melt)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT),
.rc = *rc, .rc = *rc,
.h_denom_pub = *h_denom_pub .h_denom_pub = *h_denom_pub,
.h_age_commitment = {{{0}}},
}; };
if (NULL != h_age_commitment)
melt.h_age_commitment = *h_age_commitment;
TALER_amount_hton (&melt.amount_with_fee, TALER_amount_hton (&melt.amount_with_fee,
amount_with_fee); amount_with_fee);
TALER_amount_hton (&melt.melt_fee, TALER_amount_hton (&melt.melt_fee,