diff options
-rw-r--r-- | src/benchmark/taler-aggregator-benchmark.c | 6 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_melt.c | 9 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_refreshes_reveal.c | 29 | ||||
-rw-r--r-- | src/exchangedb/exchange-0001.sql | 11 | ||||
-rw-r--r-- | src/exchangedb/irbt_callbacks.c | 2 | ||||
-rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 48 | ||||
-rw-r--r-- | src/include/taler_crypto_lib.h | 10 | ||||
-rw-r--r-- | src/include/taler_exchange_service.h | 12 | ||||
-rw-r--r-- | src/include/taler_exchangedb_plugin.h | 8 | ||||
-rw-r--r-- | src/lib/exchange_api_common.c | 30 | ||||
-rw-r--r-- | src/lib/exchange_api_link.c | 36 | ||||
-rw-r--r-- | src/lib/exchange_api_melt.c | 7 | ||||
-rw-r--r-- | src/lib/exchange_api_refresh_common.c | 38 | ||||
-rw-r--r-- | src/lib/exchange_api_refresh_common.h | 4 | ||||
-rw-r--r-- | src/lib/exchange_api_refreshes_reveal.c | 21 | ||||
-rw-r--r-- | src/testing/test_exchange_api.c | 121 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_deposit.c | 14 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_refresh.c | 66 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_withdraw.c | 10 | ||||
-rw-r--r-- | src/util/crypto.c | 102 | ||||
-rw-r--r-- | src/util/wallet_signatures.c | 8 |
21 files changed, 381 insertions, 211 deletions
diff --git a/src/benchmark/taler-aggregator-benchmark.c b/src/benchmark/taler-aggregator-benchmark.c index 11ceec90e..6d5df1e64 100644 --- a/src/benchmark/taler-aggregator-benchmark.c +++ b/src/benchmark/taler-aggregator-benchmark.c @@ -528,14 +528,14 @@ run (void *cls, &bks); { - uint32_t seed; + uint64_t seed; struct TALER_AgeMask mask = { .mask = 1 || 1 << 8 || 1 << 12 || 1 << 16 || 1 << 18 }; struct TALER_AgeCommitment ac = {0}; - seed = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - UINT32_MAX); + seed = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX); GNUNET_assert (GNUNET_OK == TALER_age_restriction_commit ( diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c index 03075280d..049fd09a0 100644 --- a/src/exchange/taler-exchange-httpd_melt.c +++ b/src/exchange/taler-exchange-httpd_melt.c @@ -425,7 +425,7 @@ TEH_handler_melt (struct MHD_Connection *connection, 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 ("h_age_commitment", + GNUNET_JSON_spec_fixed_auto ("age_commitment_hash", &rmc.refresh_session.coin.h_age_commitment)), GNUNET_JSON_spec_fixed_auto ("confirm_sig", &rmc.refresh_session.coin_sig), @@ -440,10 +440,9 @@ TEH_handler_melt (struct MHD_Connection *connection, GNUNET_JSON_spec_end () }; - memset (&rmc, - 0, - sizeof (rmc)); + memset (&rmc, 0, sizeof (rmc)); rmc.refresh_session.coin.coin_pub = *coin_pub; + { enum GNUNET_GenericReturnValue ret; ret = TALER_MHD_parse_json_data (connection, @@ -452,8 +451,10 @@ TEH_handler_melt (struct MHD_Connection *connection, if (GNUNET_OK != ret) return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; } + rmc.have_rms = (NULL != json_object_get (root, "rms")); + { MHD_RESULT res; diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index bace776da..23620f87a 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -277,7 +277,7 @@ check_commitment (struct RevealContext *rctx, union TALER_DenominationBlindingKeyP bks; const struct TALER_ExchangeWithdrawValues *alg_value = &rctx->rrcs[j].exchange_vals; - struct TALER_PlanchetDetail pd; + struct TALER_PlanchetDetail pd = {0}; struct TALER_AgeCommitmentHash *hac = NULL; struct TALER_CoinPubHashP c_hash; struct TALER_PlanchetMasterSecretP ps; @@ -298,15 +298,16 @@ check_commitment (struct RevealContext *rctx, { struct TALER_AgeCommitment ac = {0}; struct TALER_AgeCommitmentHash h = {0}; + uint64_t seed = (uint64_t) ts.key.bits[0] + | (uint64_t) ts.key.bits[1] << 32; GNUNET_assert (GNUNET_OK == TALER_age_commitment_derive ( rctx->old_age_commitment, - ts.key.bits[0], + seed, &ac)); TALER_age_commitment_hash (&ac, &h); - hac = &h; } @@ -590,7 +591,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, if (TEH_age_restriction_enabled && ((NULL == old_age_commitment_json) != TALER_AgeCommitmentHash_isNullOrZero ( - &rctx->melt.session.h_age_commitment))) + &rctx->melt.session.coin.h_age_commitment))) { GNUNET_break (0); return MHD_NO; @@ -602,7 +603,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, (NULL != old_age_commitment_json)) { enum GNUNET_GenericReturnValue res; - struct TALER_AgeCommitment *oac = rctx->old_age_commitment; + struct TALER_AgeCommitment *oac; size_t ng = json_array_size (old_age_commitment_json); bool failed = true; @@ -610,7 +611,8 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, GNUNET_assert (ng == TALER_extensions_age_restriction_num_groups ()); - oac = GNUNET_new (struct TALER_AgeCommitment); + rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment); + oac = rctx->old_age_commitment; oac->mask = TEH_age_mask; oac->num_pub = ng; oac->num_priv = 0; /* no private keys are needed for the reveal phase */ @@ -630,7 +632,8 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, ac_spec, i, -1); - GNUNET_break (GNUNET_OK != res); + + GNUNET_break_op (GNUNET_OK == res); if (GNUNET_OK != res) goto clean_age; } @@ -640,12 +643,9 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, struct TALER_AgeCommitmentHash hac = {0}; TALER_age_commitment_hash (oac, &hac); if (0 != memcmp (&hac, - &rctx->melt.session.h_age_commitment, + &rctx->melt.session.coin.h_age_commitment, sizeof(struct TALER_AgeCommitmentHash))) - { - GNUNET_break (0); goto clean_age; - } } failed = false; @@ -654,7 +654,10 @@ clean_age: if (failed) { TALER_age_commitment_free (oac); - return (GNUNET_NO == res) ? MHD_YES : MHD_NO; + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID, + "old_age_commitment"); } } @@ -913,7 +916,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; + json_t *old_age_commitment; struct RevealContext rctx; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("transfer_pub", diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql index 3f9979c06..0cef7d0db 100644 --- a/src/exchangedb/exchange-0001.sql +++ b/src/exchangedb/exchange-0001.sql @@ -311,7 +311,7 @@ CREATE TABLE IF NOT EXISTS known_coins (known_coin_id BIGINT GENERATED BY DEFAULT AS IDENTITY -- UNIQUE ,denominations_serial INT8 NOT NULL REFERENCES denominations (denominations_serial) ON DELETE CASCADE ,coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (LENGTH(coin_pub)=32) - ,age_hash BYTEA CHECK (LENGTH(age_hash)=32) + ,age_commitment_hash BYTEA CHECK (LENGTH(age_commitment_hash)=32) ,denom_sig BYTEA NOT NULL ,remaining_val INT8 NOT NULL ,remaining_frac INT4 NOT NULL @@ -325,8 +325,8 @@ COMMENT ON COLUMN known_coins.coin_pub IS 'EdDSA public key of the coin'; COMMENT ON COLUMN known_coins.remaining_val IS 'Value of the coin that remains to be spent'; -COMMENT ON COLUMN known_coins.age_hash - IS 'Optional hash for age restrictions as per DD 24 (active if denom_type has the respective bit set)'; +COMMENT ON COLUMN known_coins.age_commitment_hash + IS 'Optional hash of the age commitment for age restrictions as per DD 24 (active if denom_type has the respective bit set)'; COMMENT ON COLUMN known_coins.denom_sig IS 'This is the signature of the exchange that affirms that the coin is a valid coin. The specific signature type depends on denom_type of the denomination.'; CREATE TABLE IF NOT EXISTS known_coins_default @@ -358,7 +358,7 @@ COMMENT ON COLUMN refresh_commitments.rc 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.'; + IS 'The (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); @@ -1259,6 +1259,7 @@ CREATE OR REPLACE FUNCTION exchange_do_melt( IN in_old_coin_pub BYTEA, IN in_old_coin_sig BYTEA, IN in_known_coin_id INT8, -- not used, but that's OK + IN in_h_age_commitment BYTEA, IN in_noreveal_index INT4, IN in_zombie_required BOOLEAN, OUT out_balance_ok BOOLEAN, @@ -1281,6 +1282,7 @@ INSERT INTO refresh_commitments ,old_coin_sig ,amount_with_fee_val ,amount_with_fee_frac + ,h_age_commitment ,noreveal_index ) VALUES @@ -1289,6 +1291,7 @@ INSERT INTO refresh_commitments ,in_old_coin_sig ,in_amount_with_fee_val ,in_amount_with_fee_frac + ,in_h_age_commitment ,in_noreveal_index) ON CONFLICT DO NOTHING; diff --git a/src/exchangedb/irbt_callbacks.c b/src/exchangedb/irbt_callbacks.c index cf0549420..3673c7be1 100644 --- a/src/exchangedb/irbt_callbacks.c +++ b/src/exchangedb/irbt_callbacks.c @@ -406,6 +406,8 @@ irbt_cb_table_refresh_commitments (struct PostgresClosure *pg, &td->details.refresh_commitments.noreveal_index), GNUNET_PQ_query_param_auto_from_type ( &td->details.refresh_commitments.old_coin_pub), + GNUNET_PQ_query_param_auto_from_type ( + &td->details.refresh_commitments.h_age_commitment), GNUNET_PQ_query_param_end }; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index b5bf71e50..2f59401c6 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -611,7 +611,7 @@ prepare_statements (struct PostgresClosure *pg) ",out_zombie_bad AS zombie_required" ",out_noreveal_index AS noreveal_index" " FROM exchange_do_melt" - " ($1,$2,$3,$4,$5,$6,$7,$8,$9);", + " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);", 9), /* Used in #postgres_do_refund() to refund a deposit. */ GNUNET_PQ_make_prepare ( @@ -730,7 +730,7 @@ prepare_statements (struct PostgresClosure *pg) "get_known_coin", "SELECT" " denominations.denom_pub_hash" - ",age_hash" + ",age_commitment_hash" ",denom_sig" " FROM known_coins" " JOIN denominations USING (denominations_serial)" @@ -784,7 +784,7 @@ prepare_statements (struct PostgresClosure *pg) " INSERT INTO known_coins " " (coin_pub" " ,denominations_serial" - " ,age_hash" + " ,age_commitment_hash" " ,denom_sig" " ,remaining_val" " ,remaining_frac" @@ -804,14 +804,14 @@ prepare_statements (struct PostgresClosure *pg) " FALSE AS existed" " ,known_coin_id" " ,NULL AS denom_pub_hash" - " ,NULL AS age_hash" + " ,NULL AS age_commitment_hash" " FROM ins " "UNION ALL " "SELECT " " TRUE AS existed" " ,known_coin_id" " ,denom_pub_hash" - " ,kc.age_hash" + " ,kc.age_commitment_hash" " FROM input_rows" " JOIN known_coins kc USING (coin_pub)" " JOIN denominations USING (denominations_serial)" @@ -873,6 +873,7 @@ prepare_statements (struct PostgresClosure *pg) ",denoms.denom_pub_hash" ",denoms.fee_refresh_val" ",denoms.fee_refresh_frac" + ",h_age_commitment" ",melt_serial_id" " FROM refresh_commitments" " JOIN known_coins kc" @@ -1188,7 +1189,7 @@ prepare_statements (struct PostgresClosure *pg) ",denoms.fee_deposit_val" ",denoms.fee_deposit_frac" ",denoms.denom_pub_hash" - ",kc.age_hash" + ",kc.age_commitment_hash" ",wallet_timestamp" ",refund_deadline" ",wire_deadline" @@ -2529,8 +2530,9 @@ prepare_statements (struct PostgresClosure *pg) ",amount_with_fee_frac" ",noreveal_index" ",old_coin_pub" + ",h_age_commitment" ") VALUES " - "($1, $2, $3, $4, $5, $6, $7);", + "($1, $2, $3, $4, $5, $6, $7, $8);", 7), GNUNET_PQ_make_prepare ( "insert_into_table_refresh_revealed_coins", @@ -4593,6 +4595,7 @@ postgres_do_melt ( GNUNET_PQ_query_param_auto_from_type (&refresh->coin.coin_pub), GNUNET_PQ_query_param_auto_from_type (&refresh->coin_sig), GNUNET_PQ_query_param_uint64 (&known_coin_id), + GNUNET_PQ_query_param_auto_from_type (&refresh->coin.h_age_commitment), GNUNET_PQ_query_param_uint32 (&refresh->noreveal_index), GNUNET_PQ_query_param_bool (*zombie_required), GNUNET_PQ_query_param_end @@ -5646,7 +5649,7 @@ postgres_get_known_coin (void *cls, GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", &coin_info->denom_pub_hash), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("age_hash", + GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash", &coin_info->h_age_commitment), &is_null), TALER_PQ_result_spec_denom_sig ("denom_sig", @@ -5747,7 +5750,7 @@ postgres_count_known_coins (void *cls, * @param[out] known_coin_id set to the unique row of the coin * @param[out] denom_hash set to the denomination hash of the existing * coin (for conflict error reporting) - * @param[out] age_hash set to the conflicting age hash on conflict + * @param[out] h_age_commitment set to the conflicting age commitment hash on conflict * @return database transaction status, non-negative on success */ static enum TALER_EXCHANGEDB_CoinKnownStatus @@ -5755,7 +5758,7 @@ postgres_ensure_coin_known (void *cls, const struct TALER_CoinPublicInfo *coin, uint64_t *known_coin_id, struct TALER_DenominationHashP *denom_hash, - struct TALER_AgeCommitmentHash *age_hash) + struct TALER_AgeCommitmentHash *h_age_commitment) { struct PostgresClosure *pg = cls; enum GNUNET_DB_QueryStatus qs; @@ -5779,8 +5782,8 @@ postgres_ensure_coin_known (void *cls, denom_hash), &is_denom_pub_hash_null), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("age_hash", - age_hash), + GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash", + h_age_commitment), &is_age_hash_null), GNUNET_PQ_result_spec_end }; @@ -5814,10 +5817,10 @@ postgres_ensure_coin_known (void *cls, } if ( (! is_age_hash_null) && - (0 != GNUNET_memcmp (age_hash, + (0 != GNUNET_memcmp (h_age_commitment, &coin->h_age_commitment)) ) { - GNUNET_break (GNUNET_is_zero (age_hash)); + GNUNET_break (GNUNET_is_zero (h_age_commitment)); GNUNET_break_op (0); return TALER_EXCHANGEDB_CKS_AGE_CONFLICT; } @@ -6066,7 +6069,7 @@ postgres_get_melt (void *cls, &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), + &melt->session.coin.h_age_commitment), &h_age_commitment_is_null), TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", &melt->session.amount_with_fee), @@ -6084,9 +6087,9 @@ postgres_get_melt (void *cls, params, rs); if (h_age_commitment_is_null) - memset (&melt->session.h_age_commitment, + memset (&melt->session.coin.h_age_commitment, 0, - sizeof(melt->session.h_age_commitment)); + sizeof(melt->session.coin.h_age_commitment)); melt->session.rc = *rc; return qs; @@ -6600,7 +6603,7 @@ add_coin_deposit (void *cls, GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", &deposit->h_denom_pub), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("age_hash", + GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash", &deposit->h_age_commitment), &is_null), GNUNET_PQ_result_spec_timestamp ("wallet_timestamp", @@ -6668,6 +6671,7 @@ add_coin_melt (void *cls, struct TALER_EXCHANGEDB_MeltListEntry *melt; struct TALER_EXCHANGEDB_TransactionList *tl; uint64_t serial_id; + bool hac_isnull; chc->have_deposit_or_melt = true; melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry); @@ -6684,6 +6688,10 @@ add_coin_melt (void *cls, &melt->amount_with_fee), TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh", &melt->melt_fee), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_auto_from_type ("h_age_commitment", + &melt->h_age_commitment), + &hac_isnull), GNUNET_PQ_result_spec_uint64 ("melt_serial_id", &serial_id), GNUNET_PQ_result_spec_end @@ -6699,6 +6707,10 @@ add_coin_melt (void *cls, chc->failed = true; return; } + + if (hac_isnull) + memset (&melt->h_age_commitment, 0, sizeof(melt->h_age_commitment)); + } tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList); tl->next = chc->head; diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 20b9ff303..a49b9eb5f 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -2419,6 +2419,7 @@ TALER_wallet_deposit_verify ( * @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_priv coin’s private key * @param[out] coin_sig set to the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_MELT */ @@ -2428,6 +2429,7 @@ TALER_wallet_melt_sign ( const struct TALER_Amount *melt_fee, const struct TALER_RefreshCommitmentP *rc, const struct TALER_DenominationHashP *h_denom_pub, + const struct TALER_AgeCommitmentHash *h_age_commitment, const struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_CoinSpendSignatureP *coin_sig); @@ -3346,7 +3348,7 @@ TALER_age_commitment_hash ( * * @param mask The age mask the defines the age groups * @param age The actual age for which an age commitment is generated - * @param seed The seed that goes into the key generation. MUST be choosen uniformly random. + * @param salt The salt that goes into the key generation. MUST be choosen uniformly random. * @param commitment[out] The generated age commitment, ->priv and ->pub allocated via GNUNET_malloc on success * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ @@ -3354,21 +3356,21 @@ enum GNUNET_GenericReturnValue TALER_age_restriction_commit ( const struct TALER_AgeMask *mask, const uint8_t age, - const uint32_t seed, + const uint64_t salt, struct TALER_AgeCommitment *commitment); /* * @brief Derives another, equivalent age commitment for a given one. * * @param orig Original age commitment - * @param seed Used to move the points on the elliptic curve in order to generate another, equivalent commitment. + * @param salt Salt to randomly move the points on the elliptic curve in order to generate another, equivalent commitment. * @param derived[out] The resulting age commitment, ->priv and ->pub allocated via GNUNET_malloc on success. * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ enum GNUNET_GenericReturnValue TALER_age_commitment_derive ( const struct TALER_AgeCommitment *orig, - const uint32_t seed, + const uint64_t salt, struct TALER_AgeCommitment *derived); /* diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 92e841ef2..fcb0ab7fb 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -1682,9 +1682,11 @@ struct TALER_EXCHANGE_RefreshData struct TALER_CoinSpendPrivateKeyP melt_priv; /* - * age commitment that went into the original coin, might be NULL + * age commitment and its hash that went into the original coin, might be + * NULL */ - struct TALER_AgeCommitment *age_commitment; + struct TALER_AgeCommitment *melt_age_commitment; + struct TALER_AgeCommitmentHash *melt_h_age_commitment; /** * amount specifying how much the coin will contribute to the melt @@ -1998,6 +2000,12 @@ struct TALER_EXCHANGE_LinkedCoinInfo struct TALER_CoinSpendPrivateKeyP coin_priv; /** + * Age commitment and its hash, if applicable. Might be NULL. + */ + struct TALER_AgeCommitment *age_commitment; + struct TALER_AgeCommitmentHash *h_age_commitment; + + /** * Master secret of this coin. */ struct TALER_PlanchetMasterSecretP ps; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 10ab1ac94..529d49431 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -309,6 +309,7 @@ struct TALER_EXCHANGEDB_TableData struct TALER_CoinSpendPublicKeyP old_coin_pub; struct TALER_CoinSpendSignatureP old_coin_sig; struct TALER_Amount amount_with_fee; + struct TALER_AgeCommitmentHash h_age_commitment; uint32_t noreveal_index; } refresh_commitments; @@ -1269,13 +1270,6 @@ 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_AgeCommitmentHash h_age_commitment; - - /** * Refresh commitment this coin is melted into. */ struct TALER_RefreshCommitmentP rc; diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index 17e00a813..b7a43bbc8 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -585,24 +585,20 @@ TALER_EXCHANGE_verify_coin_history ( } } - { - const struct TALER_AgeCommitmentHash *ahc = &h_age_commitment; - - if (TALER_AgeCommitmentHash_isNullOrZero (ahc)) - ahc = NULL; - if (GNUNET_OK != - TALER_wallet_melt_verify (&amount, - &fee, - &rc, - h_denom_pub, - ahc, - coin_pub, - &sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + if (GNUNET_OK != + TALER_wallet_melt_verify ( + &amount, + &fee, + &rc, + h_denom_pub, + TALER_AgeCommitmentHash_isNullOrZero (&h_age_commitment) ? + NULL : &h_age_commitment, + coin_pub, + &sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; } add = GNUNET_YES; } diff --git a/src/lib/exchange_api_link.c b/src/lib/exchange_api_link.c index 902f2b422..ac3fecdde 100644 --- a/src/lib/exchange_api_link.c +++ b/src/lib/exchange_api_link.c @@ -67,7 +67,8 @@ struct TALER_EXCHANGE_LinkHandle struct TALER_CoinSpendPrivateKeyP coin_priv; /** - * Age commitment of the coin, might be NULL, required to re-generate age commitments + * Age commitment of the original coin, might be NULL. + * Required to derive the new age commitment */ const struct TALER_AgeCommitment *age_commitment; @@ -118,7 +119,6 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, struct TALER_TransferSecretP secret; struct TALER_PlanchetDetail pd; struct TALER_CoinPubHashP c_hash; - struct TALER_AgeCommitmentHash *hac = NULL; /* parse reply */ memset (&nonce, @@ -145,28 +145,26 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, &alg_values, &bks); + lci->age_commitment = NULL; + lci->h_age_commitment = NULL; + /* Derive the age commitment and calculate the hash */ if (NULL != lh->age_commitment) { - struct TALER_AgeCommitment nac = {0}; - struct TALER_AgeCommitmentHash h = {0}; - uint32_t seed = secret.key.bits[0]; + uint64_t seed = (uint64_t) secret.key.bits[0] + | (uint64_t) secret.key.bits[1] << 32; + lci->age_commitment = GNUNET_new (struct TALER_AgeCommitment); + lci->h_age_commitment = GNUNET_new (struct TALER_AgeCommitmentHash); - if (GNUNET_OK != - TALER_age_commitment_derive ( - lh->age_commitment, - seed, - &nac)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + GNUNET_assert (GNUNET_OK == + TALER_age_commitment_derive ( + lh->age_commitment, + seed, + lci->age_commitment)); TALER_age_commitment_hash ( - &nac, - &h); - - hac = &h; + lci->age_commitment, + lci->h_age_commitment); } if (GNUNET_OK != @@ -174,7 +172,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, &alg_values, &bks, &lci->coin_priv, - hac, + lci->h_age_commitment, &c_hash, &pd)) { diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c index f7f770272..dbe77c7eb 100644 --- a/src/lib/exchange_api_melt.c +++ b/src/lib/exchange_api_melt.c @@ -478,6 +478,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh) &mh->md.melted_coin.fee_melt, &mh->md.rc, &h_denom_pub, + mh->md.melted_coin.h_age_commitment, &mh->md.melted_coin.coin_priv, &confirm_sig); GNUNET_CRYPTO_eddsa_key_get_public (&mh->md.melted_coin.coin_priv.eddsa_priv, @@ -494,6 +495,12 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh) GNUNET_JSON_pack_data_auto ("rc", &mh->md.rc), GNUNET_JSON_pack_allow_null ( + mh->md.melted_coin.h_age_commitment + ? GNUNET_JSON_pack_data_auto ("age_commitment_hash", + mh->md.melted_coin.h_age_commitment) + : GNUNET_JSON_pack_string ("age_commitment_hash", + NULL)), + GNUNET_JSON_pack_allow_null ( mh->send_rms ? GNUNET_JSON_pack_data_auto ("rms", &mh->rms) diff --git a/src/lib/exchange_api_refresh_common.c b/src/lib/exchange_api_refresh_common.c index 8e9e8da37..997d1fec8 100644 --- a/src/lib/exchange_api_refresh_common.c +++ b/src/lib/exchange_api_refresh_common.c @@ -78,7 +78,8 @@ TALER_EXCHANGE_get_melt_data_ ( md->melted_coin.fee_melt = rd->melt_pk.fees.refresh; md->melted_coin.original_value = rd->melt_pk.value; md->melted_coin.expire_deposit = rd->melt_pk.expire_deposit; - md->melted_coin.age_commitment = rd->age_commitment; + md->melted_coin.age_commitment = rd->melt_age_commitment; + md->melted_coin.h_age_commitment = rd->melt_h_age_commitment; GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (rd->melt_amount.currency, @@ -184,29 +185,23 @@ TALER_EXCHANGE_get_melt_data_ ( /* Handle age commitment, if present */ if (NULL != md->melted_coin.age_commitment) { - struct TALER_AgeCommitment new_ac; - struct TALER_AgeCommitmentHash hac; - - /* We use the first 4 bytes of the trans_sec to generate a new age + /* We use the first 8 bytes of the trans_sec to generate a new age * commitment */ - uint32_t age_seed = trans_sec.key.bits[0]; - - if (GNUNET_OK != - TALER_age_commitment_derive ( - md->melted_coin.age_commitment, - age_seed + j, - &new_ac)) - { - GNUNET_break_op (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } + uint64_t age_seed = (uint64_t) trans_sec.key.bits[0] + | (uint64_t) trans_sec.key.bits[1] << 32; - TALER_age_commitment_hash ( - &new_ac, - &hac); + fcd->age_commitment[i] = GNUNET_new (struct TALER_AgeCommitment); + ach = GNUNET_new (struct TALER_AgeCommitmentHash); + + GNUNET_assert (GNUNET_OK == + TALER_age_commitment_derive ( + md->melted_coin.age_commitment, + age_seed, + fcd->age_commitment[i])); - ach = &hac; + TALER_age_commitment_hash ( + fcd->age_commitment[i], + ach); } if (TALER_DENOMINATION_CS == alg_values[j].cipher) @@ -225,7 +220,6 @@ TALER_EXCHANGE_get_melt_data_ ( TALER_EXCHANGE_free_melt_data_ (md); return GNUNET_SYSERR; } - rcd->blinded_planchet = pd.blinded_planchet; rcd->dk = &fcd->fresh_pk; } diff --git a/src/lib/exchange_api_refresh_common.h b/src/lib/exchange_api_refresh_common.h index a3c3e2c02..8d7eb282e 100644 --- a/src/lib/exchange_api_refresh_common.h +++ b/src/lib/exchange_api_refresh_common.h @@ -56,8 +56,8 @@ struct MeltedCoin * The original age commitment and its hash. MUST be NULL if no age * commitment was set. */ - struct TALER_AgeCommitment *age_commitment; - struct TALER_AgeCommitmentHash *h_age_commitment; + const struct TALER_AgeCommitment *age_commitment; + const struct TALER_AgeCommitmentHash *h_age_commitment; /** * Timestamp indicating when coins of this denomination become invalid. diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c index 461432db7..881c7e731 100644 --- a/src/lib/exchange_api_refreshes_reveal.c +++ b/src/lib/exchange_api_refreshes_reveal.c @@ -336,6 +336,7 @@ TALER_EXCHANGE_refreshes_reveal ( json_t *coin_evs; json_t *reveal_obj; json_t *link_sigs; + json_t *old_age_commitment = NULL; CURL *eh; struct GNUNET_CURL_Context *ctx; struct MeltData md; @@ -427,6 +428,22 @@ TALER_EXCHANGE_refreshes_reveal ( &md.transfer_priv[j]))); } + /* build array of old age commitment, if applicable */ + GNUNET_assert ((NULL == rd->melt_age_commitment) == + (NULL == rd->melt_h_age_commitment)); + if (NULL != rd->melt_age_commitment) + { + GNUNET_assert (NULL != (old_age_commitment = json_array ())); + + for (size_t i = 0; i < rd->melt_age_commitment->num_pub; i++) + { + GNUNET_assert (0 == + json_array_append_new (old_age_commitment, + GNUNET_JSON_from_data_auto ( + &rd->melt_age_commitment->pub[i]))); + } + } + /* build main JSON request */ reveal_obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("transfer_pub", @@ -437,6 +454,9 @@ TALER_EXCHANGE_refreshes_reveal ( rms) : GNUNET_JSON_pack_string ("rms", NULL)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("old_age_commitment", + old_age_commitment)), GNUNET_JSON_pack_array_steal ("transfer_privs", transfer_privs), GNUNET_JSON_pack_array_steal ("link_sigs", @@ -480,6 +500,7 @@ TALER_EXCHANGE_refreshes_reveal ( GNUNET_free (rrh); return NULL; } + eh = TALER_EXCHANGE_curl_easy_get_ (rrh->url); if ( (NULL == eh) || (GNUNET_OK != diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c index 957e42e8a..b6dd39c87 100644 --- a/src/testing/test_exchange_api.c +++ b/src/testing/test_exchange_api.c @@ -377,9 +377,9 @@ run (void *cls, * Move money to the exchange's bank account. */ CMD_TRANSFER_TO_EXCHANGE ("create-reserve-age", - "EUR:5.01"), + "EUR:6.01"), TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-age", - "EUR:5.01", + "EUR:6.01", bc.user42_payto, bc.exchange_payto, "create-reserve-age"), @@ -475,7 +475,22 @@ run (void *cls, "EUR:0.98", bc.exchange_payto, bc.user42_payto), - TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c", + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c3", + ec.exchange_url, + "EUR:0.98", + bc.exchange_payto, + bc.user42_payto), + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c4", + ec.exchange_url, + "EUR:0.98", + bc.exchange_payto, + bc.user42_payto), + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-08c", + ec.exchange_url, + "EUR:0.08", + bc.exchange_payto, + bc.user43_payto), + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-08c2", ec.exchange_url, "EUR:0.08", bc.exchange_payto, @@ -548,6 +563,104 @@ run (void *cls, TALER_TESTING_cmd_end () }; + struct TALER_TESTING_Command refresh_age[] = { + /* Fill reserve with EUR:5, 1ct is for fees. */ + CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-age-1", + "EUR:6.01"), + TALER_TESTING_cmd_check_bank_admin_transfer ( + "ck-refresh-create-reserve-age-1", + "EUR:6.01", + bc.user42_payto, + bc.exchange_payto, + "refresh-create-reserve-age-1"), + /** + * Make previous command effective. + */ + CMD_EXEC_WIREWATCH ("wirewatch-age-2"), + /** + * Withdraw EUR:7 with age restriction for age 13. + */ + TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-age-1", + "refresh-create-reserve-age-1", + "EUR:5", + 13, + MHD_HTTP_OK), + /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin + * (in full) (merchant would receive EUR:0.99 due to 1 ct + * deposit fee) */// + TALER_TESTING_cmd_deposit ("refresh-deposit-partial-age", + "refresh-withdraw-coin-age-1", + 0, + bc.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:1", + MHD_HTTP_OK), + /** + * Melt the rest of the coin's value + * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */ + TALER_TESTING_cmd_melt_double ("refresh-melt-age-1", + "refresh-withdraw-coin-age-1", + MHD_HTTP_OK, + NULL), + /** + * Complete (successful) melt operation, and + * withdraw the coins + */ + TALER_TESTING_cmd_refresh_reveal ("refresh-reveal-age-1", + "refresh-melt-age-1", + MHD_HTTP_OK), + /** + * Do it again to check idempotency + */ + TALER_TESTING_cmd_refresh_reveal ("refresh-reveal-age-1-idempotency", + "refresh-melt-age-1", + MHD_HTTP_OK), + /** + * Test that /refresh/link works + */ + TALER_TESTING_cmd_refresh_link ("refresh-link-age-1", + "refresh-reveal-age-1", + MHD_HTTP_OK), + /** + * Try to spend a refreshed EUR:1 coin + */ + TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-age-1a", + "refresh-reveal-age-1-idempotency", + 0, + bc.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:1", + MHD_HTTP_OK), + /** + * Try to spend a refreshed EUR:0.1 coin + */ + TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-age-1b", + "refresh-reveal-age-1", + 3, + bc.user43_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:0.1", + MHD_HTTP_OK), +#if 0 /* FIXME oec */ + /* Test running a failing melt operation (same operation + * again must fail) */ + TALER_TESTING_cmd_melt ("refresh-melt-failing-age", + "refresh-withdraw-coin-age-1", + MHD_HTTP_CONFLICT, + NULL), + /* Test running a failing melt operation (on a coin that + was itself revealed and subsequently deposited) */ + TALER_TESTING_cmd_melt ("refresh-melt-failing-age-2", + "refresh-reveal-age-1", + MHD_HTTP_CONFLICT, + NULL), + +#endif + TALER_TESTING_cmd_end () + }; /** * This block exercises the aggretation logic by making two payments @@ -1073,6 +1186,8 @@ run (void *cls, withdraw_age), TALER_TESTING_cmd_batch ("spend-age", spend_age), + TALER_TESTING_cmd_batch ("refresh-age", + refresh_age), TALER_TESTING_cmd_batch ("track", track), TALER_TESTING_cmd_batch ("unaggregation", diff --git a/src/testing/testing_api_cmd_deposit.c b/src/testing/testing_api_cmd_deposit.c index a241c531b..ad1315b28 100644 --- a/src/testing/testing_api_cmd_deposit.c +++ b/src/testing/testing_api_cmd_deposit.c @@ -568,23 +568,23 @@ deposit_traits (void *cls, struct TALER_TESTING_Trait traits[] = { /* First two traits are only available if ds->traits is #GNUNET_YES */ - TALER_TESTING_make_trait_exchange_pub (0, &ds->exchange_pub), - TALER_TESTING_make_trait_exchange_sig (0, &ds->exchange_sig), + TALER_TESTING_make_trait_exchange_pub (index, &ds->exchange_pub), + TALER_TESTING_make_trait_exchange_sig (index, &ds->exchange_sig), /* These traits are always available */ - TALER_TESTING_make_trait_coin_priv (0, + TALER_TESTING_make_trait_coin_priv (index, coin_spent_priv), - TALER_TESTING_make_trait_age_commitment (0, + TALER_TESTING_make_trait_age_commitment (index, age_commitment), TALER_TESTING_make_trait_wire_details (ds->wire_details), TALER_TESTING_make_trait_contract_terms (ds->contract_terms), TALER_TESTING_make_trait_merchant_priv (&ds->merchant_priv), TALER_TESTING_make_trait_deposit_amount (&ds->amount), TALER_TESTING_make_trait_deposit_fee_amount (&ds->deposit_fee), - TALER_TESTING_make_trait_timestamp (0, + TALER_TESTING_make_trait_timestamp (index, &ds->exchange_timestamp), - TALER_TESTING_make_trait_wire_deadline (0, + TALER_TESTING_make_trait_wire_deadline (index, &ds->wire_deadline), - TALER_TESTING_make_trait_refund_deadline (0, + TALER_TESTING_make_trait_refund_deadline (index, &ds->refund_deadline), TALER_TESTING_trait_end () }; diff --git a/src/testing/testing_api_cmd_refresh.c b/src/testing/testing_api_cmd_refresh.c index 29ad9d2fa..f287681d8 100644 --- a/src/testing/testing_api_cmd_refresh.c +++ b/src/testing/testing_api_cmd_refresh.c @@ -71,9 +71,10 @@ struct TALER_TESTING_FreshCoinData struct TALER_CoinSpendPrivateKeyP coin_priv; /* - * Age commitment for the coin, NULL if not applicable. + * Fresh age commitment for the coin and its hash, NULL if not applicable. */ struct TALER_AgeCommitment *age_commitment; + struct TALER_AgeCommitmentHash *h_age_commitment; /** * The blinding key (needed for recoup operations). @@ -137,11 +138,6 @@ struct RefreshMeltState */ const struct TALER_CoinSpendPrivateKeyP *melt_priv; - /* - * Age commitment for the coin, NULL if not applicable. - */ - struct TALER_AgeCommitment *age_commitment; - /** * Task scheduled to try later. */ @@ -445,6 +441,8 @@ reveal_cb (void *cls, return; } fc->coin_priv = coin->coin_priv; + fc->age_commitment = coin->age_commitment; + fc->h_age_commitment = coin->h_age_commitment; TALER_denom_sig_deep_copy (&fc->sig, &coin->sig); @@ -836,7 +834,7 @@ refresh_link_run (void *cls, /* finally, use private key from withdraw sign command */ rls->rlh = TALER_EXCHANGE_link (is->exchange, coin_priv, - rms->age_commitment, + rms->refresh_data.melt_age_commitment, &link_cb, rls); @@ -1046,6 +1044,8 @@ melt_run (void *cls, { struct TALER_Amount melt_amount; struct TALER_Amount fresh_amount; + struct TALER_AgeCommitment *age_commitment; + struct TALER_AgeCommitmentHash *h_age_commitment; const struct TALER_DenominationSignature *melt_sig; const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub; const struct TALER_TESTING_Command *coin_command; @@ -1070,10 +1070,21 @@ melt_run (void *cls, TALER_TESTING_interpreter_fail (rms->is); return; } + if (GNUNET_OK != TALER_TESTING_get_trait_age_commitment (coin_command, 0, - &rms->age_commitment)) + &age_commitment)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (rms->is); + return; + } + + if (GNUNET_OK != + TALER_TESTING_get_trait_h_age_commitment (coin_command, + 0, + &h_age_commitment)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (rms->is); @@ -1148,31 +1159,13 @@ melt_run (void *cls, rms->refresh_data.melt_amount = melt_amount; rms->refresh_data.melt_sig = *melt_sig; rms->refresh_data.melt_pk = *melt_denom_pub; + rms->refresh_data.melt_age_commitment = age_commitment; + rms->refresh_data.melt_h_age_commitment = h_age_commitment; rms->refresh_data.fresh_pks = rms->fresh_pks; rms->refresh_data.fresh_pks_len = num_fresh_coins; - rms->refresh_data.age_commitment = NULL; GNUNET_assert (age_restricted == - (NULL != rms->age_commitment)); - - if (NULL != rms->age_commitment) - { - struct TALER_AgeCommitment *ac; - uint32_t seed; - - ac = GNUNET_new (struct TALER_AgeCommitment); - seed = GNUNET_CRYPTO_random_u32 ( - GNUNET_CRYPTO_QUALITY_WEAK, - UINT32_MAX); - - GNUNET_assert (GNUNET_OK == - TALER_age_commitment_derive ( - rms->age_commitment, - seed, - ac)); - - rms->refresh_data.age_commitment = ac; - } + (NULL != age_commitment)); rms->rmh = TALER_EXCHANGE_melt (is->exchange, &rms->rms, @@ -1256,10 +1249,14 @@ melt_traits (void *cls, struct TALER_TESTING_Trait traits[] = { TALER_TESTING_make_trait_denom_pub (index, &rms->fresh_pks[index]), - TALER_TESTING_make_trait_coin_priv (0, + TALER_TESTING_make_trait_coin_priv (index, rms->melt_priv), - TALER_TESTING_make_trait_age_commitment (index, - rms->age_commitment), + TALER_TESTING_make_trait_age_commitment ( + index, + rms->refresh_data.melt_age_commitment), + TALER_TESTING_make_trait_h_age_commitment ( + index, + rms->refresh_data.melt_h_age_commitment), TALER_TESTING_make_trait_exchange_wd_value (index, &rms->mbds[index].alg_value), TALER_TESTING_make_trait_refresh_secret (&rms->rms), @@ -1418,6 +1415,7 @@ refresh_reveal_traits (void *cls, if (index >= rrs->num_fresh_coins) return GNUNET_SYSERR; + { struct TALER_TESTING_Trait traits[] = { TALER_TESTING_make_trait_coin_priv ( @@ -1426,6 +1424,9 @@ refresh_reveal_traits (void *cls, TALER_TESTING_make_trait_age_commitment ( index, rrs->fresh_coins[index].age_commitment), + TALER_TESTING_make_trait_h_age_commitment ( + index, + rrs->fresh_coins[index].h_age_commitment), TALER_TESTING_make_trait_denom_pub ( index, rrs->fresh_coins[index].pk), @@ -1443,7 +1444,6 @@ refresh_reveal_traits (void *cls, &rrs->psa[index]), TALER_TESTING_trait_end () }; - return TALER_TESTING_get_trait (traits, ret, trait, diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c index 14015c497..3974a1057 100644 --- a/src/testing/testing_api_cmd_withdraw.c +++ b/src/testing/testing_api_cmd_withdraw.c @@ -526,7 +526,7 @@ withdraw_cleanup (void *cls, } if (NULL != ws->age_commitment) { - GNUNET_free (ws->age_commitment); + TALER_age_commitment_free (ws->age_commitment); ws->age_commitment = NULL; } if (NULL != ws->h_age_commitment) @@ -569,7 +569,7 @@ withdraw_traits (void *cls, &ws->exchange_vals), TALER_TESTING_make_trait_denom_pub (0 /* only one coin */, ws->pk), - TALER_TESTING_make_trait_denom_sig (index /* only one coin */, + TALER_TESTING_make_trait_denom_sig (0 /* only one coin */, &ws->sig), TALER_TESTING_make_trait_reserve_priv (&ws->reserve_priv), TALER_TESTING_make_trait_reserve_pub (&ws->reserve_pub), @@ -579,8 +579,8 @@ withdraw_traits (void *cls, (const char **) &ws->reserve_payto_uri), TALER_TESTING_make_trait_exchange_url ( (const char **) &ws->exchange_url), - TALER_TESTING_make_trait_age_commitment (index, ws->age_commitment), - TALER_TESTING_make_trait_h_age_commitment (index, ws->h_age_commitment), + TALER_TESTING_make_trait_age_commitment (0, ws->age_commitment), + TALER_TESTING_make_trait_h_age_commitment (0, ws->h_age_commitment), TALER_TESTING_trait_end () }; @@ -626,7 +626,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label, ac = GNUNET_new (struct TALER_AgeCommitment); hac = GNUNET_new (struct TALER_AgeCommitmentHash); - seed = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); + seed = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); mask = TALER_extensions_age_restriction_ageMask (); if (GNUNET_OK != diff --git a/src/util/crypto.c b/src/util/crypto.c index aced73f2a..bf91b6971 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -506,7 +506,7 @@ enum GNUNET_GenericReturnValue TALER_age_restriction_commit ( const struct TALER_AgeMask *mask, const uint8_t age, - const uint32_t seed, + const uint64_t salt, struct TALER_AgeCommitment *new) { uint8_t num_pub = __builtin_popcount (mask->mask) - 1; @@ -517,6 +517,7 @@ TALER_age_restriction_commit ( GNUNET_assert (mask->mask & 1); /* fist bit must have been set */ GNUNET_assert (0 <= num_priv); GNUNET_assert (31 > num_priv); + GNUNET_assert (num_priv <= num_pub); new->mask.mask = mask->mask; new->num_pub = num_pub; @@ -529,32 +530,35 @@ TALER_age_restriction_commit ( num_priv, struct TALER_AgeCommitmentPrivateKeyP); - /* Create as many private keys as we need */ - for (i = 0; i < num_priv; i++) + /* Create as many private keys as we need and fill the rest of the + * public keys with valid curve points. + * We need to make sure that the public keys are proper points on the + * elliptic curve, so we can't simply fill the struct with random values. */ + for (i = 0; i < num_pub; i++) { - uint32_t seedBE = htonl (seed + i); + uint64_t saltBE = htonl (salt + i); + struct TALER_AgeCommitmentPrivateKeyP key = {0}; + struct TALER_AgeCommitmentPrivateKeyP *priv = &key; + + /* Only save the private keys for age groups less than num_priv */ + if (i < num_priv) + priv = &new->priv[i]; if (GNUNET_OK != - GNUNET_CRYPTO_kdf (&new->priv[i], - sizeof (new->priv[i]), - &seedBE, - sizeof (seedBE), + GNUNET_CRYPTO_kdf (priv, + sizeof (*priv), + &saltBE, + sizeof (saltBE), "taler-age-commitment-derivation", strlen ( "taler-age-commitment-derivation"), NULL, 0)) goto FAIL; - GNUNET_CRYPTO_eddsa_key_get_public (&new->priv[i].eddsa_priv, + GNUNET_CRYPTO_eddsa_key_get_public (&priv->eddsa_priv, &new->pub[i].eddsa_pub); } - /* Fill the rest of the public keys with random values */ - for (; i<num_pub; i++) - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - &new->pub[i], - sizeof(new->pub[i])); - return GNUNET_OK; FAIL: @@ -567,10 +571,24 @@ FAIL: enum GNUNET_GenericReturnValue TALER_age_commitment_derive ( const struct TALER_AgeCommitment *orig, - const uint32_t seed, + const uint64_t salt, struct TALER_AgeCommitment *new) { - struct GNUNET_CRYPTO_EccScalar val; + struct GNUNET_CRYPTO_EccScalar scalar; + uint64_t saltBT = htonl (salt); + int64_t factor; + + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_kdf ( + &factor, + sizeof (factor), + &saltBT, + sizeof (saltBT), + "taler-age-restriction-derivation", + strlen ("taler-age-restriction-derivation"), + NULL, 0)); + + GNUNET_CRYPTO_ecc_scalar_from_int (factor, &scalar); /* * age commitment consists of GNUNET_CRYPTO_Eddsa{Private,Public}Key @@ -584,7 +602,7 @@ TALER_age_commitment_derive ( * We want to multiply, both, the Private Key by an integer factor and the * public key (point on curve) with the equivalent scalar. * - * From the seed we will derive + * From the salt we will derive * 1. a scalar to multiply the public keys with * 2. a factor to multiply the private key with * @@ -594,9 +612,9 @@ TALER_age_commitment_derive ( * A point on a curve is GNUNET_CRYPTO_EccPoint which is * unsigned char v[256 / 8]; * - * A ECC scaler for use in point multiplications is a + * A ECC scalar for use in point multiplications is a * GNUNET_CRYPTO_EccScalar which is a - * unsigned car v[256 / 8]; + * unsigned char v[256 / 8]; * */ GNUNET_assert (NULL != new); @@ -613,9 +631,6 @@ TALER_age_commitment_derive ( new->num_priv, struct TALER_AgeCommitmentPrivateKeyP); - - GNUNET_CRYPTO_ecc_scalar_from_int (seed, &val); - /* scalar multiply the public keys on the curve */ for (size_t i = 0; i < orig->num_pub; i++) { @@ -627,7 +642,7 @@ TALER_age_commitment_derive ( if (GNUNET_OK != GNUNET_CRYPTO_ecc_pmul_mpi ( p, - &val, + &scalar, np)) goto FAIL; @@ -636,47 +651,40 @@ TALER_age_commitment_derive ( /* multiply the private keys */ /* we borough ideas from GNUNET_CRYPTO_ecdsa_private_key_derive */ { - uint32_t seedBE; - uint8_t dc[32]; - gcry_mpi_t f, x, d, n; - gcry_ctx_t ctx; - - GNUNET_assert (0==gcry_mpi_ec_new (&ctx,NULL, "Ed25519")); - n = gcry_mpi_ec_get_mpi ("n", ctx, 1); - - /* make the seed big endian */ - seedBE = GNUNET_htonll (seed); - - GNUNET_CRYPTO_mpi_scan_unsigned (&f, &seedBE, sizeof(seedBE)); - for (size_t i = 0; i < orig->num_priv; i++) { + uint8_t dc[32]; + gcry_mpi_t f, x, d, n; + gcry_ctx_t ctx; + + GNUNET_assert (0==gcry_mpi_ec_new (&ctx, NULL, "Ed25519")); + n = gcry_mpi_ec_get_mpi ("n", ctx, 1); + + GNUNET_CRYPTO_mpi_scan_unsigned (&f, (unsigned char*) &factor, + sizeof(factor)); - /* convert to big endian for libgrypt */ for (size_t j = 0; j < 32; j++) dc[i] = orig->priv[i].eddsa_priv.d[31 - j]; GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc)); d = gcry_mpi_new (256); gcry_mpi_mulm (d, f, x, n); - gcry_mpi_release (x); - gcry_mpi_release (d); - gcry_mpi_release (n); - gcry_mpi_release (d); GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d); for (size_t j = 0; j <32; j++) new->priv[i].eddsa_priv.d[j] = dc[31 - 1]; sodium_memzero (dc, sizeof(dc)); + gcry_mpi_release (d); + gcry_mpi_release (x); + gcry_mpi_release (n); + gcry_mpi_release (f); + gcry_ctx_release (ctx); - /* TODO: - * make sure that the calculated private key generate the same public - * keys */ + /* TODO: add test to make sure that the calculated private key generate + * the same public keys */ } - gcry_mpi_release (f); - gcry_ctx_release (ctx); } return GNUNET_OK; diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c index a21c73bef..7d07c9e77 100644 --- a/src/util/wallet_signatures.c +++ b/src/util/wallet_signatures.c @@ -249,6 +249,7 @@ TALER_wallet_melt_sign ( const struct TALER_Amount *melt_fee, const struct TALER_RefreshCommitmentP *rc, const struct TALER_DenominationHashP *h_denom_pub, + const struct TALER_AgeCommitmentHash *h_age_commitment, const struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_CoinSpendSignatureP *coin_sig) { @@ -256,9 +257,14 @@ TALER_wallet_melt_sign ( .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT), .purpose.size = htonl (sizeof (melt)), .rc = *rc, - .h_denom_pub = *h_denom_pub + .h_denom_pub = *h_denom_pub, + .h_age_commitment = {{{0}}}, }; + 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, |