Compare commits

...

2 Commits

Author SHA1 Message Date
8fe127eb2e
age restriction progress
- age restriction extension simplified
  - its config is now global to extension
  - helper functions and macros introduced

- age restriction support for
  - melt is done
  - reveal continued
  - link started
2022-01-23 22:25:41 +01:00
ffe4cc06aa
gana update 2022-01-23 22:11:06 +01:00
11 changed files with 148 additions and 27 deletions

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

View File

@ -285,6 +285,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 +294,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 +319,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 +446,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 +464,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 +472,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 +495,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 +507,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 +528,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 +587,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 +608,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 +644,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 +680,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 +694,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 +707,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 +771,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

@ -830,6 +830,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"
@ -6020,6 +6021,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
@ -6036,6 +6038,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",
@ -6051,6 +6057,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;
} }

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,11 @@ age_restriction_load_taler_config (
} }
if (GNUNET_OK == ret) if (GNUNET_OK == ret)
this->config = (void *) (size_t) mask.mask; {
_config.mask.mask = mask.mask;
_config.num_groups = __builtin_popcount (mask.mask);
this->config = &_config;
}
GNUNET_free (groups); GNUNET_free (groups);
return ret; return ret;
@ -223,12 +242,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_agemask (jconfig, &mask);
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
return ret; return ret;
@ -239,16 +258,15 @@ 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)
GNUNET_free (this->config);
this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask)); _config.mask.mask = mask.mask;
GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask)); _config.num_groups = __builtin_popcount (mask.mask);
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;
return GNUNET_OK; return GNUNET_OK;
} }
@ -263,7 +281,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 +292,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)
); );
@ -318,4 +334,18 @@ 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_enabled ()
{
return (0 != _config.mask.mask);
}
size_t
TALER_extensions_age_restriction_num_groups ()
{
return _config.num_groups;
}
/* end of extension_age_restriction.c */ /* end of extension_age_restriction.c */

View File

@ -311,9 +311,11 @@ struct TALER_AgeHash
struct GNUNET_ShortHashCode shash; struct GNUNET_ShortHashCode shash;
}; };
bool extern const struct TALER_AgeHash TALER_AgeHash_zeroHash;
TALER_AgeHash_isZero ( #define TALER_AgeHash_isZero(ph) ((NULL == ph) || \
const struct TALER_AgeHash *hash); (0 == memcmp (ph, \
&TALER_AgeHash_zeroHash, \
sizeof(struct TALER_AgeHash))))
/** /**
* @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
@ -1809,6 +1811,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
*/ */
@ -1818,6 +1821,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_AgeHash *h_age_commitment,
const struct TALER_CoinSpendSignatureP *coin_sig); const struct TALER_CoinSpendSignatureP *coin_sig);

View File

@ -1260,6 +1260,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_AgeHash h_age_commitment;
/** /**
* Refresh commitment this coin is melted into. * Refresh commitment this coin is melted into.
*/ */
@ -1306,8 +1313,9 @@ 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. May be all zeroes if no * Hash of the age commitment used to sign the coin, if age restriction was
* age restriction applies. * applicable to the denomination. May be all zeroes if no age restriction
* applies.
*/ */
struct TALER_AgeHash h_age_commitment; struct TALER_AgeHash h_age_commitment;

View File

@ -221,6 +221,18 @@ 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 enabled
*/
bool
TALER_extensions_age_restriction_enabled ();
/**
* Returns the amount of age groups defined. 0 means no age restriction
* enabled.
*/
size_t
TALER_extensions_age_restriction_num_groups ();
/* /*
* TODO: Add Peer2Peer Extension * TODO: Add Peer2Peer Extension

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_AgeHash h_age_commitment;
/** /**
* Hash of the blinded new coin. * Hash of the blinded new coin.
*/ */

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_AgeHash h_age_commitment = {0}; // TODO, see below.
/* parse reply */ /* parse reply */
if (GNUNET_OK != if (GNUNET_OK !=
@ -156,11 +157,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

@ -25,6 +25,10 @@
#include "taler_util.h" #include "taler_util.h"
#include <gcrypt.h> #include <gcrypt.h>
/**
* Used in TALER_AgeHash_isZero for comparison
*/
const struct TALER_AgeHash TALER_AgeHash_zeroHash = {0};
/** /**
* Function called by libgcrypt on serious errors. * Function called by libgcrypt on serious errors.
@ -352,13 +356,4 @@ TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
} }
bool
TALER_AgeHash_isZero (
const struct TALER_AgeHash *hash)
{
static struct TALER_AgeHash zeroAgeHash = {0};
return (0 == memcmp (hash, &zeroAgeHash, sizeof(struct TALER_AgeHash)));
}
/* end of crypto.c */ /* end of crypto.c */

View File

@ -135,6 +135,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_AgeHash *h_age_commitment,
const struct TALER_CoinSpendSignatureP *coin_sig) const struct TALER_CoinSpendSignatureP *coin_sig)
{ {
struct TALER_LinkDataPS ldp = { struct TALER_LinkDataPS ldp = {
@ -145,6 +146,13 @@ TALER_wallet_link_verify (
.coin_envelope_hash = *h_coin_ev .coin_envelope_hash = *h_coin_ev
}; };
if (NULL == h_age_commitment)
memset (&ldp.h_age_commitment,
0,
sizeof(*h_age_commitment));
else
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,