From 2b85559c0630ea7c4df721f0076aed2e77ed9078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sun, 23 Jan 2022 18:27:31 +0100 Subject: [PATCH] first steps towards age restriction support for coin refresh --- src/auditor/taler-helper-auditor-coins.c | 2 ++ src/exchange/taler-exchange-httpd_melt.c | 7 +++++++ src/exchange/taler-exchange-httpd_responses.c | 11 +++++++++++ src/exchangedb/exchange-0001.sql | 3 +++ src/exchangedb/plugin_exchangedb_postgres.c | 9 +++++++++ src/exchangedb/test_exchangedb.c | 2 ++ src/include/taler_crypto_lib.h | 8 +++++++- src/include/taler_exchangedb_plugin.h | 8 ++++++++ src/include/taler_signatures.h | 7 +++++++ src/lib/exchange_api_common.c | 8 ++++++++ src/util/crypto.c | 9 +++++++++ src/util/wallet_signatures.c | 7 ++++++- 12 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c index 3473a8284..bf97c50f2 100644 --- a/src/auditor/taler-helper-auditor-coins.c +++ b/src/auditor/taler-helper-auditor-coins.c @@ -1227,6 +1227,7 @@ static int refresh_session_cb (void *cls, uint64_t rowid, const struct TALER_DenominationPublicKey *denom_pub, + const struct TALER_AgeHash *h_age_commitment, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_Amount *amount_with_fee, @@ -1289,6 +1290,7 @@ refresh_session_cb (void *cls, &fee_refresh, rc, &h_denom_pub, + h_age_commitment, coin_pub, coin_sig)) { diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c index 54f1385d7..334ec1287 100644 --- a/src/exchange/taler-exchange-httpd_melt.c +++ b/src/exchange/taler-exchange-httpd_melt.c @@ -278,6 +278,7 @@ check_melt_valid (struct MHD_Connection *connection, &mret); if (NULL == dk) return mret; + if (GNUNET_TIME_absolute_is_past (dk->meta.expire_legal.abs_time)) { /* 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, "MELT"); } + if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) { /* 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_value = dk->meta.value; + /* sanity-check that "total melt amount > melt fee" */ if (0 < TALER_amount_cmp (&rmc->coin_refresh_fee, @@ -328,6 +331,7 @@ check_melt_valid (struct MHD_Connection *connection, &rmc->coin_refresh_fee, &rmc->refresh_session.rc, &rmc->refresh_session.coin.denom_pub_hash, + &rmc->refresh_session.coin.age_commitment_hash, &rmc->refresh_session.coin.coin_pub, &rmc->refresh_session.coin_sig)) { @@ -403,6 +407,9 @@ TEH_handler_melt (struct MHD_Connection *connection, &rmc.refresh_session.coin.denom_sig), GNUNET_JSON_spec_fixed_auto ("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", &rmc.refresh_session.coin_sig), TALER_JSON_spec_amount ("value_with_fee", diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 66a3b0af9..c2a4a6cab 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -122,6 +122,7 @@ TEH_RESPONSE_compile_transaction_history ( { const struct TALER_EXCHANGEDB_MeltListEntry *melt = pos->details.melt; + const struct TALER_AgeHash *phac = NULL; #if ENABLE_SANITY_CHECKS if (GNUNET_OK != @@ -129,6 +130,7 @@ TEH_RESPONSE_compile_transaction_history ( &melt->melt_fee, &melt->rc, &melt->h_denom_pub, + &melt->h_age_commitment, coin_pub, &melt->coin_sig)) { @@ -137,6 +139,12 @@ TEH_RESPONSE_compile_transaction_history ( return NULL; } #endif + + /* Age restriction is optional. We communicate a NULL value to + * JSON_PACK below */ + if (! TALER_AgeHash_isZero (&melt->h_age_commitment)) + phac = &melt->h_age_commitment; + if (0 != json_array_append_new ( history, @@ -151,6 +159,9 @@ TEH_RESPONSE_compile_transaction_history ( &melt->rc), GNUNET_JSON_pack_data_auto ("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", &melt->coin_sig)))) { diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql index a8e79335b..97739fe80 100644 --- a/src/exchangedb/exchange-0001.sql +++ b/src/exchangedb/exchange-0001.sql @@ -342,6 +342,7 @@ CREATE TABLE IF NOT EXISTS refresh_commitments (melt_serial_id BIGSERIAL -- UNIQUE ,rc BYTEA PRIMARY KEY CHECK (LENGTH(rc)=64) ,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) ,amount_with_fee_val INT8 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'; COMMENT ON COLUMN refresh_commitments.old_coin_pub 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 PARTITION OF refresh_commitments FOR VALUES WITH (MODULUS 1, REMAINDER 0); diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index f9f0ce412..9cdde4d81 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -848,6 +848,7 @@ prepare_statements (struct PostgresClosure *pg) "SELECT" " denom.denom_pub" ",kc.coin_pub AS old_coin_pub" + ",h_age_commitment" ",old_coin_sig" ",amount_with_fee_val" ",amount_with_fee_frac" @@ -8202,6 +8203,8 @@ refreshs_serial_helper_cb (void *cls, struct TALER_DenominationPublicKey denom_pub; struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendSignatureP coin_sig; + struct TALER_AgeHash h_age_commitment; + bool ac_isnull; struct TALER_Amount amount_with_fee; uint32_t noreveal_index; uint64_t rowid; @@ -8209,6 +8212,10 @@ refreshs_serial_helper_cb (void *cls, struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_result_spec_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", &coin_pub), GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig", @@ -8234,9 +8241,11 @@ refreshs_serial_helper_cb (void *cls, rsc->status = GNUNET_SYSERR; return; } + ret = rsc->cb (rsc->cb_cls, rowid, &denom_pub, + ac_isnull ? NULL : &h_age_commitment, &coin_pub, &coin_sig, &amount_with_fee, diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index cca7c3f47..d90ee3989 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -459,6 +459,7 @@ static unsigned int auditor_row_cnt; * @param cls closure * @param rowid unique serial ID for the refresh session in our DB * @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_sig signature from the coin * @param amount_with_fee amount that was deposited including fee @@ -471,6 +472,7 @@ static enum GNUNET_GenericReturnValue audit_refresh_session_cb (void *cls, uint64_t rowid, const struct TALER_DenominationPublicKey *denom_pub, + const struct TALER_AgeHash *h_age_commitment, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_Amount *amount_with_fee, diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 6a805b645..50df036fd 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -311,6 +311,9 @@ struct TALER_AgeHash struct GNUNET_ShortHashCode shash; }; +bool +TALER_AgeHash_isZero ( + const struct TALER_AgeHash *hash); /** * @brief Type of public keys for Taler coins. The same key material is used @@ -712,7 +715,8 @@ struct TALER_CoinPublicInfo 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; @@ -1763,6 +1767,7 @@ TALER_wallet_melt_sign ( * @param melt_fee the melt fee we expect to pay * @param rc refresh session we are committed to * @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 coin’s public key * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_MELT * @return #GNUNET_OK if the signature is valid @@ -1773,6 +1778,7 @@ TALER_wallet_melt_verify ( const struct TALER_Amount *melt_fee, const struct TALER_RefreshCommitmentP *rc, const struct TALER_DenominationHash *h_denom_pub, + const struct TALER_AgeHash *h_age_commitment, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index cd68e1edb..48bacebb8 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1305,6 +1305,12 @@ 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. + */ + struct TALER_AgeHash h_age_commitment; + /** * How much value is being melted? This amount includes the fees, * so the final amount contributed to the melt is this value minus @@ -1585,6 +1591,7 @@ typedef enum GNUNET_GenericReturnValue * @param cls closure * @param rowid unique serial ID for the refresh session in our DB * @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_sig signature from the coin * @param amount_with_fee amount that was deposited including fee @@ -1597,6 +1604,7 @@ typedef enum GNUNET_GenericReturnValue void *cls, uint64_t rowid, const struct TALER_DenominationPublicKey *denom_pub, + const struct TALER_AgeHash *h_age_commitment, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_Amount *amount_with_fee, diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 3ad1121ca..c7016e53c 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -711,6 +711,13 @@ struct TALER_RefreshMeltCoinAffirmationPS */ 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_AgeHash h_age_commitment GNUNET_PACKED; + /** * 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 diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index 399eb280a..a42cfa706 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -548,6 +548,7 @@ TALER_EXCHANGE_verify_coin_history ( { struct TALER_CoinSpendSignatureP sig; struct TALER_RefreshCommitmentP rc; + struct TALER_AgeHash h_age_commitment = {0}; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_sig", &sig), @@ -555,6 +556,9 @@ TALER_EXCHANGE_verify_coin_history ( &rc), GNUNET_JSON_spec_fixed_auto ("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", &fee), GNUNET_JSON_spec_end () @@ -568,6 +572,7 @@ TALER_EXCHANGE_verify_coin_history ( GNUNET_break_op (0); return GNUNET_SYSERR; } + if (NULL != dk) { /* check that melt fee matches our expectations from /keys! */ @@ -582,11 +587,14 @@ TALER_EXCHANGE_verify_coin_history ( return GNUNET_SYSERR; } } + if (GNUNET_OK != TALER_wallet_melt_verify (&amount, &fee, &rc, h_denom_pub, + TALER_AgeHash_isZero (&h_age_commitment) ? + NULL : &h_age_commitment, coin_pub, &sig)) { diff --git a/src/util/crypto.c b/src/util/crypto.c index 178db3aad..0ef191765 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -352,4 +352,13 @@ 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 bc4903e0e..ebeab74d4 100644 --- a/src/util/wallet_signatures.c +++ b/src/util/wallet_signatures.c @@ -267,6 +267,7 @@ TALER_wallet_melt_verify ( const struct TALER_Amount *melt_fee, const struct TALER_RefreshCommitmentP *rc, const struct TALER_DenominationHash *h_denom_pub, + const struct TALER_AgeHash *h_age_commitment, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig) { @@ -274,9 +275,13 @@ TALER_wallet_melt_verify ( .purpose.size = htonl (sizeof (melt)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT), .rc = *rc, - .h_denom_pub = *h_denom_pub + .h_denom_pub = *h_denom_pub, }; + memset (&melt.h_age_commitment, 0, sizeof(struct TALER_AgeHash)); + if (NULL != h_age_commitment) + melt.h_age_commitment = *h_age_commitment; + TALER_amount_hton (&melt.amount_with_fee, amount_with_fee); TALER_amount_hton (&melt.melt_fee,