Compare commits

...

4 Commits

Author SHA1 Message Date
1b1a6c142a
[WIP] hash and signature verification of /keys works again
- 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

So far: test_exchange_api runs, including withdraw_age!

However, test_auditor_api fails and another is in a endless loop!
2022-02-08 00:00:24 +01:00
632d17f642
[WIP] moving towards withdrawal with age restriction
Age_mask now taken into account when denominations are being setup.
However, tests fail, because denoms can't be found!?  Probably because
on initial generation of the denominations, the age mask is not setup,
yet, because age restriction hasn't been enabled yet!?
2022-02-07 18:39:58 +01:00
d02b5e213a
[WIP] exchangedb adjustments for denominations
- all prepared statements re: denominations now handle age_mask

- signatures parameters adjusted.

Now compiles and Tests run but fail.

- good: we find denoms[] and age_restricted_denoms[] filled correctly in
  output to /keys

- bad: fails at exchange_api_handle.c:882, signature verification of
  denom.
2022-02-07 13:56:25 +01:00
f8b1c3f8db
Debugging session of withdraw with age restriction 2022-02-07 09:06:51 +01:00
20 changed files with 260 additions and 94 deletions

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.
@ -3037,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;
@ -3197,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.
* *
@ -3285,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)
@ -3519,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)
@ -3780,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

@ -157,6 +157,12 @@ TEH_extensions_init ()
it = it->next) it = it->next)
extension_update_event_cb (NULL, &it->type, sizeof(it->type)); extension_update_event_cb (NULL, &it->type, sizeof(it->type));
/* 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);
return GNUNET_OK; return GNUNET_OK;
} }

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
@ -1771,7 +1760,7 @@ 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)
@ -1857,9 +1846,11 @@ 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 age_restriction_active = bool age_restriction_active =
TALER_extensions_is_enabled_type (TALER_Extension_AgeRestriction); TALER_extensions_is_enabled_type (TALER_Extension_AgeRestriction);
bool have_age_restricted_denoms = false;
sctx.signkeys = json_array (); sctx.signkeys = json_array ();
GNUNET_assert (NULL != sctx.signkeys); GNUNET_assert (NULL != sctx.signkeys);
@ -1884,19 +1875,24 @@ 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
those hashes afterwards.*/
if (age_restriction_active) if (age_restriction_active)
{ {
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;
@ -1938,14 +1934,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 (
@ -1972,19 +1968,25 @@ 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
* hash for the corresponding array. */
if (age_restriction_active && if (age_restriction_active &&
(0 != dk->denom_pub.age_mask.mask)) (0 != dk->denom_pub.age_mask.mask))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, have_age_restricted_denoms = true;
"got agerestricted denom %p with mask %d\n",
&dk->denom_pub,
dk->denom_pub.age_mask.mask);
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 ==
@ -2000,8 +2002,21 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
{ {
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 (age_restriction_active && 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,
@ -2672,7 +2687,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;
} }
@ -2799,7 +2816,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.
* *
@ -295,6 +294,10 @@ TEH_handler_management_post_extensions (
NULL, NULL,
0); 0);
/* FIXME-oec. Because of a change of extensions, the key might */
TEH_keys_update_states ();
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

@ -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';

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),
@ -2071,7 +2075,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"
@ -2088,6 +2091,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;",
@ -2390,10 +2394,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"
@ -3096,9 +3101,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));
@ -3172,6 +3180,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
}; };
@ -3258,12 +3268,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,
@ -3272,6 +3285,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
@ -3357,10 +3377,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",
@ -3387,6 +3407,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
}; };
@ -3398,6 +3420,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,
@ -10193,6 +10219,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
}; };
@ -10236,6 +10264,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

@ -224,6 +224,9 @@ age_restriction_load_taler_config (
if (GNUNET_OK == ret) if (GNUNET_OK == ret)
{ {
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.mask.mask = mask.mask;
_config.num_groups = __builtin_popcount (mask.mask) - 1; /* no underflow, first bit always set */ _config.num_groups = __builtin_popcount (mask.mask) - 1; /* no underflow, first bit always set */
this->config = &_config; this->config = &_config;
@ -277,6 +280,10 @@ age_restriction_load_json_config (
this->config_json = jconfig; 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;
} }
@ -350,6 +357,13 @@ TALER_extensions_age_restriction_enabled ()
} }
struct TALER_AgeMask
TALER_extensions_age_restriction_ageMask ()
{
return _config.mask;
}
size_t size_t
TALER_extensions_age_restriction_num_groups () TALER_extensions_age_restriction_num_groups ()
{ {

View File

@ -159,13 +159,6 @@ struct TALER_EXCHANGE_DenomPublicKey
* revoked by the exchange. * revoked by the exchange.
*/ */
bool revoked; bool revoked;
/**
* If age_mask non-zero, the denomination is age-restricted, with the age
* groups as defined in the mask.
*/
struct TALER_AgeMask age_mask;
bool age_restricted;
}; };

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;
}; };
@ -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;
}; };

View File

@ -238,6 +238,13 @@ TALER_age_mask_to_string (
bool bool
TALER_extensions_age_restriction_enabled (); TALER_extensions_age_restriction_enabled ();
/**
* Returns the currently set age mask
*/
struct TALER_AgeMask
TALER_extensions_age_restriction_ageMask ();
/** /**
* Returns the amount of age groups defined. 0 means no age restriction * Returns the amount of age groups defined. 0 means no age restriction
* enabled. * enabled.

View File

@ -67,7 +67,7 @@
/** /**
* Set to 1 for extra debug logging. * Set to 1 for extra debug logging.
*/ */
#define DEBUG 1 /* FIXME-oec */ #define DEBUG 0
/** /**
* Log error related to CURL operations. * Log error related to CURL operations.
@ -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,10 +846,15 @@ decode_keys_json (const json_t *resp_obj,
*/ */
struct struct
{ char *name; { char *name;
struct GNUNET_HashContext *hc;
bool is_optional_age_restriction;} bool is_optional_age_restriction;}
hive[2] = { hive[2] = {
{ "denoms", false }, { "denoms",
{ "age_restricted_denoms", true } 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++)
@ -873,6 +885,8 @@ decode_keys_json (const json_t *resp_obj,
struct TALER_EXCHANGE_DenomPublicKey dk; struct TALER_EXCHANGE_DenomPublicKey dk;
bool found = false; bool found = false;
have_age_restricted_denom = true;
memset (&dk, memset (&dk,
0, 0,
sizeof (dk)); sizeof (dk));
@ -881,14 +895,15 @@ 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, /* Mark age restriction according where we got this denomination from,
* "denoms" or "age_restricted_denoms" */ * "denoms" or "age_restricted_denoms" */
if (hive[s].is_optional_age_restriction) if (hive[s].is_optional_age_restriction)
{ {
dk.age_restricted = true; // dk.age_restricted = true;
dk.age_mask.mask = key_data->age_mask.mask; // dk.age_mask.mask = key_data->age_mask.mask;
GNUNET_assert (0 != key_data->age_mask.mask);
} }
for (unsigned int j = 0; for (unsigned int j = 0;
@ -1048,6 +1063,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

@ -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

@ -355,6 +355,7 @@ TALER_EXCHANGE_refresh_prepare (
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];
@ -372,6 +373,7 @@ 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, TALER_age_commitment_hash (age_commitment,
&md.melted_coin.h_age_commitment); &md.melted_coin.h_age_commitment);

View File

@ -184,6 +184,7 @@ TALER_EXCHANGE_withdraw (
{ {
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;
@ -193,8 +194,8 @@ TALER_EXCHANGE_withdraw (
wh->ps = *ps; wh->ps = *ps;
wh->ach = ach; wh->ach = ach;
GNUNET_assert ( (pk->age_restricted && (NULL != ach)) || GNUNET_assert ( (age_restricted && (NULL != ach)) ||
(! pk->age_restricted && (NULL == ach)) ); (! age_restricted && (NULL == ach)));
if (GNUNET_OK != if (GNUNET_OK !=
TALER_planchet_prepare (&pk->key, TALER_planchet_prepare (&pk->key,

View File

@ -209,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

@ -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
@ -983,12 +984,12 @@ 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,
false), false),
TALER_TESTING_cmd_exec_offline_sign_extensions ("offline-sign-extensions",
CONFIG_FILE),
TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
CONFIG_FILE), CONFIG_FILE),
TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees", TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees",
@ -1042,6 +1043,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,

View File

@ -997,6 +997,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
@ -1026,6 +1027,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,
@ -1035,9 +1037,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;
@ -1055,7 +1059,7 @@ melt_run (void *cls,
} }
fresh_pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange), fresh_pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
&fresh_amount, &fresh_amount,
melt_denom_pub->age_restricted); age_restricted);
if (NULL == fresh_pk) if (NULL == fresh_pk)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1080,8 +1084,7 @@ melt_run (void *cls,
{ {
struct TALER_AgeCommitment *ac = NULL; struct TALER_AgeCommitment *ac = NULL;
GNUNET_assert (melt_denom_pub->age_restricted == GNUNET_assert (age_restricted == (NULL != rms->age_commitment));
(NULL != rms->age_commitment));
if (NULL != rms->age_commitment) if (NULL != rms->age_commitment)
{ {

View File

@ -464,12 +464,14 @@ withdraw_run (void *cls,
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_age_restriction_commit ( TALER_age_restriction_commit (
&ws->pk->age_mask, &ws->pk->key.age_mask,
ws->age, ws->age,
seed, seed,
ac)); ac));
ws->age_commitment = ac; ws->age_commitment = ac;
ws->h_age_commitment = GNUNET_malloc (sizeof(struct
TALER_AgeCommitmentHash));
TALER_age_commitment_hash ( TALER_age_commitment_hash (
ac, ac,
ws->h_age_commitment); ws->h_age_commitment);

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"
/** /**
@ -440,7 +441,7 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
(GNUNET_TIME_timestamp_cmp (now, (GNUNET_TIME_timestamp_cmp (now,
<, <,
pk->withdraw_valid_until)) && pk->withdraw_valid_until)) &&
(age_restricted == pk->age_restricted) ) (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
@ -457,7 +458,7 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
GNUNET_TIME_timestamp_cmp (now, GNUNET_TIME_timestamp_cmp (now,
>, >,
pk->withdraw_valid_until) ) && pk->withdraw_valid_until) ) &&
(age_restricted == pk->age_restricted) ) (age_restricted == (0 != pk->key.age_mask.mask)) )
{ {
GNUNET_log GNUNET_log
(GNUNET_ERROR_TYPE_WARNING, (GNUNET_ERROR_TYPE_WARNING,