summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/benchmark/taler-aggregator-benchmark.c6
-rw-r--r--src/exchange/taler-exchange-httpd_melt.c9
-rw-r--r--src/exchange/taler-exchange-httpd_refreshes_reveal.c29
-rw-r--r--src/exchangedb/exchange-0001.sql11
-rw-r--r--src/exchangedb/irbt_callbacks.c2
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c48
-rw-r--r--src/include/taler_crypto_lib.h10
-rw-r--r--src/include/taler_exchange_service.h12
-rw-r--r--src/include/taler_exchangedb_plugin.h8
-rw-r--r--src/lib/exchange_api_common.c30
-rw-r--r--src/lib/exchange_api_link.c36
-rw-r--r--src/lib/exchange_api_melt.c7
-rw-r--r--src/lib/exchange_api_refresh_common.c38
-rw-r--r--src/lib/exchange_api_refresh_common.h4
-rw-r--r--src/lib/exchange_api_refreshes_reveal.c21
-rw-r--r--src/testing/test_exchange_api.c121
-rw-r--r--src/testing/testing_api_cmd_deposit.c14
-rw-r--r--src/testing/testing_api_cmd_refresh.c66
-rw-r--r--src/testing/testing_api_cmd_withdraw.c10
-rw-r--r--src/util/crypto.c102
-rw-r--r--src/util/wallet_signatures.c8
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,