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 link_sigs_json link signatures in JSON format
* @param new_denoms_h_json requests for fresh coins to be created
* @param old_age_commitment_json age commitment that went into the withdrawal, maybe NULL
* @param coin_evs envelopes of gamma-selected coins to be signed
* @return MHD result code
*/
@ -293,6 +294,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
struct RevealContext *rctx,
const json_t *link_sigs_json,
const json_t *new_denoms_h_json,
const json_t *old_age_commitment_json,
const json_t *coin_evs)
{
unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
@ -317,6 +319,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
NULL);
}
/* Parse denomination key hashes */
for (unsigned int i = 0; i<num_fresh_coins; i++)
{
@ -443,6 +446,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
goto cleanup;
}
}
/* Parse link signatures array */
for (unsigned int i = 0; i<num_fresh_coins; i++)
{
@ -460,6 +464,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
-1);
if (GNUNET_OK != res)
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
/* Check signature */
if (GNUNET_OK !=
TALER_wallet_link_verify (
@ -467,6 +472,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
&rctx->gamma_tp,
&rrcs[i].coin_envelope_hash,
&rctx->melt.session.coin.coin_pub,
NULL, // TODO-oec: calculate the correct h_age_commitment
&rrcs[i].orig_coin_link_sig))
{
GNUNET_break_op (0);
@ -489,6 +495,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
rcd->coin_ev_size = rrc->coin_ev_size;
rcd->dk = &dks[i]->denom_pub;
}
rctx->dks = dks;
rctx->rcds = rcds;
if (GNUNET_OK !=
@ -500,6 +507,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Creating %u signatures\n",
(unsigned int) rctx->num_fresh_coins);
/* create fresh coin signatures */
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
{
@ -520,8 +528,10 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
goto cleanup;
}
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Signatures ready, starting DB interaction\n");
/* Persist operation result in DB */
{
enum GNUNET_DB_QueryStatus qs;
@ -577,11 +587,18 @@ cleanup:
* revealed information is valid then returns the signed refreshed
* coins.
*
* If the denomination has age restriction support, the array of EDDSA public
* keys, one for each age group that was activated during the withdrawal
* by the parent/ward, must be provided in old_age_commitment. The hash of
* this array must be the same as the h_age_commitment of the persisted reveal
* request.
*
* @param connection the MHD connection to handle
* @param rctx context for the operation, partially built at this time
* @param tp_json private transfer keys in JSON format
* @param link_sigs_json link signatures in JSON format
* @param new_denoms_h_json requests for fresh coins to be created
* @param old_age_commitment_json array of EDDSA public keys in JSON, used for age restriction, maybe NULL
* @param coin_evs envelopes of gamma-selected coins to be signed
* @return MHD result code
*/
@ -591,6 +608,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
const json_t *tp_json,
const json_t *link_sigs_json,
const json_t *new_denoms_h_json,
const json_t *old_age_commitment_json,
const json_t *coin_evs)
{
unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
@ -626,6 +644,19 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
"new_denoms/link_sigs");
}
/* Sanity check of age commitment: If it was provided, it _must_ be an array
* of the size the # of age groups */
if (NULL != old_age_commitment_json
&& TALER_extensions_age_restriction_num_groups () !=
json_array_size (old_age_commitment_json))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
"old_age_commitment");
}
/* Parse transfer private keys array */
for (unsigned int i = 0; i<num_tprivs; i++)
{
@ -649,6 +680,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
rctx,
link_sigs_json,
new_denoms_h_json,
old_age_commitment_json,
coin_evs);
}
@ -662,6 +694,7 @@ TEH_handler_reveal (struct TEH_RequestContext *rc,
json_t *transfer_privs;
json_t *link_sigs;
json_t *new_denoms_h;
json_t *old_age_commitment = NULL;
struct RevealContext rctx;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("transfer_pub",
@ -674,6 +707,9 @@ TEH_handler_reveal (struct TEH_RequestContext *rc,
&coin_evs),
GNUNET_JSON_spec_json ("new_denoms_h",
&new_denoms_h),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("old_age_commitment",
&old_age_commitment)),
GNUNET_JSON_spec_end ()
};
@ -735,6 +771,7 @@ TEH_handler_reveal (struct TEH_RequestContext *rc,
transfer_privs,
link_sigs,
new_denoms_h,
old_age_commitment,
coin_evs);
GNUNET_JSON_parse_free (spec);
return res;

View File

@ -830,6 +830,7 @@ prepare_statements (struct PostgresClosure *pg)
",denoms.fee_refresh_frac"
",old_coin_pub"
",old_coin_sig"
",h_age_commitment"
",amount_with_fee_val"
",amount_with_fee_frac"
",noreveal_index"
@ -6020,6 +6021,7 @@ postgres_get_melt (void *cls,
uint64_t *melt_serial_id)
{
struct PostgresClosure *pg = cls;
bool h_age_commitment_is_null;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (rc),
GNUNET_PQ_query_param_end
@ -6036,6 +6038,10 @@ postgres_get_melt (void *cls,
&melt->session.coin.coin_pub),
GNUNET_PQ_result_spec_auto_from_type ("old_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",
&melt->session.amount_with_fee),
GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
@ -6051,6 +6057,11 @@ postgres_get_melt (void *cls,
"get_melt",
params,
rs);
if (h_age_commitment_is_null)
memset (&melt->session.h_age_commitment,
0,
sizeof(melt->session.h_age_commitment));
melt->session.rc = *rc;
return qs;
}

View File

@ -23,6 +23,19 @@
#include "taler_extensions.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
@ -146,6 +159,9 @@ age_restriction_disable (
json_decref (this->config_json);
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;
ret = GNUNET_OK;
if (groups != NULL)
@ -208,7 +223,11 @@ age_restriction_load_taler_config (
}
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);
return ret;
@ -223,12 +242,12 @@ age_restriction_load_taler_config (
static enum GNUNET_GenericReturnValue
age_restriction_load_json_config (
struct TALER_Extension *this,
json_t *config)
json_t *jconfig)
{
struct TALER_AgeMask mask = {0};
enum GNUNET_GenericReturnValue ret;
ret = TALER_JSON_parse_agemask (config, &mask);
ret = TALER_JSON_parse_agemask (jconfig, &mask);
if (GNUNET_OK != ret)
return ret;
@ -239,16 +258,15 @@ age_restriction_load_json_config (
if (TALER_Extension_AgeRestriction != this->type)
return GNUNET_SYSERR;
if (NULL != this->config)
GNUNET_free (this->config);
this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask));
GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask));
_config.mask.mask = mask.mask;
_config.num_groups = __builtin_popcount (mask.mask);
this->config = &_config;
if (NULL != this->config_json)
json_decref (this->config_json);
this->config_json = config;
this->config_json = jconfig;
return GNUNET_OK;
}
@ -263,7 +281,6 @@ json_t *
age_restriction_config_to_json (
const struct TALER_Extension *this)
{
struct TALER_AgeMask mask;
char *mask_str;
json_t *conf;
@ -275,8 +292,7 @@ age_restriction_config_to_json (
return json_copy (this->config_json);
}
mask.mask = (uint32_t) (size_t) this->config;
mask_str = TALER_age_mask_to_string (&mask);
mask_str = TALER_age_mask_to_string (&_config.mask);
conf = GNUNET_JSON_PACK (
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,
};
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 */

View File

@ -311,9 +311,11 @@ struct TALER_AgeHash
struct GNUNET_ShortHashCode shash;
};
bool
TALER_AgeHash_isZero (
const struct TALER_AgeHash *hash);
extern const struct TALER_AgeHash TALER_AgeHash_zeroHash;
#define TALER_AgeHash_isZero(ph) ((NULL == ph) || \
(0 == memcmp (ph, \
&TALER_AgeHash_zeroHash, \
sizeof(struct TALER_AgeHash))))
/**
* @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 h_coin_ev hash of the coin envelope
* @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
* @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_BlindedCoinHash *h_coin_ev,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
const struct TALER_AgeHash *h_age_commitment,
const struct TALER_CoinSpendSignatureP *coin_sig);

View File

@ -1260,6 +1260,13 @@ struct TALER_EXCHANGEDB_Refresh
*/
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.
*/
@ -1306,8 +1313,9 @@ struct TALER_EXCHANGEDB_MeltListEntry
struct TALER_DenominationHash h_denom_pub;
/**
* Hash of the age commitment used to sign the coin. May be all zeroes if no
* age restriction applies.
* 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;

View File

@ -221,6 +221,18 @@ char *
TALER_age_mask_to_string (
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

View File

@ -414,6 +414,11 @@ struct TALER_LinkDataPS
*/
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.
*/

View File

@ -105,6 +105,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
};
struct TALER_TransferSecretP secret;
struct TALER_PlanchetSecretsP fc;
struct TALER_AgeHash h_age_commitment = {0}; // TODO, see below.
/* parse reply */
if (GNUNET_OK !=
@ -156,11 +157,21 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
pd.coin_ev_size,
&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 !=
TALER_wallet_link_verify (&pd.denom_pub_hash,
trans_pub,
&coin_envelope_hash,
&old_coin_pub,
&h_age_commitment,
&link_sig))
{
GNUNET_break_op (0);

View File

@ -25,6 +25,10 @@
#include "taler_util.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.
@ -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 */

View File

@ -135,6 +135,7 @@ TALER_wallet_link_verify (
const struct TALER_TransferPublicKeyP *transfer_pub,
const struct TALER_BlindedCoinHash *h_coin_ev,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
const struct TALER_AgeHash *h_age_commitment,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
struct TALER_LinkDataPS ldp = {
@ -145,6 +146,13 @@ TALER_wallet_link_verify (
.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
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
&ldp,