From 8fe127eb2e6b4cb1efe12515638e9456accbb7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sun, 23 Jan 2022 22:25:41 +0100 Subject: [PATCH] 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 --- .../taler-exchange-httpd_refreshes_reveal.c | 37 +++++++++++++ src/exchangedb/plugin_exchangedb_postgres.c | 11 ++++ src/extensions/extension_age_restriction.c | 54 ++++++++++++++----- src/include/taler_crypto_lib.h | 10 ++-- src/include/taler_exchangedb_plugin.h | 12 ++++- src/include/taler_extensions.h | 12 +++++ src/include/taler_signatures.h | 5 ++ src/lib/exchange_api_link.c | 11 ++++ src/util/crypto.c | 13 ++--- src/util/wallet_signatures.c | 8 +++ 10 files changed, 147 insertions(+), 26 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index 30a7294c1..bae5ba55c 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -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; igamma_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; inum_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; isession.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; } diff --git a/src/extensions/extension_age_restriction.c b/src/extensions/extension_age_restriction.c index a9ffb7f1a..80474a9fc 100644 --- a/src/extensions/extension_age_restriction.c +++ b/src/extensions/extension_age_restriction.c @@ -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 */ diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 50df036fd..026a0fda9 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -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); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 48bacebb8..8e5f72df3 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -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; diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h index f00f3ed56..be28c9618 100644 --- a/src/include/taler_extensions.h +++ b/src/include/taler_extensions.h @@ -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 diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index c7016e53c..e93c59487 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -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. */ diff --git a/src/lib/exchange_api_link.c b/src/lib/exchange_api_link.c index ec085b533..262af1d15 100644 --- a/src/lib/exchange_api_link.c +++ b/src/lib/exchange_api_link.c @@ -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); diff --git a/src/util/crypto.c b/src/util/crypto.c index 0ef191765..12b2cc748 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -25,6 +25,10 @@ #include "taler_util.h" #include +/** + * 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 */ diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c index ebeab74d4..b42716417 100644 --- a/src/util/wallet_signatures.c +++ b/src/util/wallet_signatures.c @@ -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,