aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2022-02-16 22:01:05 +0100
committerÖzgür Kesim <oec-taler@kesim.org>2022-02-16 22:01:05 +0100
commit8bdf6ab19df70c16d335ecf82f2c3b2117eeb70e (patch)
treefe38fc98807feb6892052ee091b2b5f0a70ab17a /src/util
parentb73be40ccd9ad0ef4a985f252099c867f698896d (diff)
[age restriction] progress 14/n - withdraw and deposit
Age restriction support for - withdraw is done and tested - deposit is done and tested TODOs: - melt/refresh/reveal - link ------ Added functions - TALER_age_restriction_commit - TALER_age_commitment_derive - TALER_age_commitment_hash - TALER_age_restriction_commitment_free_inside - Hash of age commitment passed around API boundaries Exchangedb adjustments for denominations - all prepared statements re: denominations now handle age_mask - signature parameters adjusted Hash and signature verification of /keys adjusted - Hashes of (normal) denominations and age-restricted denominations are calculated seperately - The hash of the age-restricted ones will then be added to the other hash - The total hash is signed/verified Tests for withdraw with age restriction added - TALER_EXCHANGE_DenomPublickey now carries age_mask - TALER_TESTING_cmd_withdraw_amount* takes age parameter - TALER_TESTING_find_pk takes boolean age_restricted - WithdrawState carries age_commitment and its hash - withdraw_run derives new age commitment, if applicable - Added age parameter to testing (13 as example) Various Fixes and changes - Fixes of post handler for /management/extensions - Fixes for offline tool extensions signing - Slight refactoring of extensions - Age restriction extension simplified - config is now global to extension - added global TEH_age_restriction_enabled and TEH_age_mask in taler-exchange-httpd - helper functions and macros introduced
Diffstat (limited to 'src/util')
-rw-r--r--src/util/crypto.c302
-rw-r--r--src/util/denom.c4
-rw-r--r--src/util/test_crypto.c10
-rw-r--r--src/util/test_helper_rsa.c11
-rw-r--r--src/util/wallet_signatures.c28
5 files changed, 336 insertions, 19 deletions
diff --git a/src/util/crypto.c b/src/util/crypto.c
index 13b9188c5..6bea984f3 100644
--- a/src/util/crypto.c
+++ b/src/util/crypto.c
@@ -20,11 +20,16 @@
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
+ * @author Özgür Kesim
*/
#include "platform.h"
#include "taler_util.h"
#include <gcrypt.h>
+/**
+ * Used in TALER_AgeCommitmentHash_isNullOrZero for comparison
+ */
+const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash = {0};
/**
* Function called by libgcrypt on serious errors.
@@ -83,12 +88,11 @@ TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info,
GNUNET_memcmp (&d_hash,
&coin_public_info->denom_pub_hash));
#endif
- // FIXME-Oec: replace with function that
- // also hashes the age vector if we have
- // one!
- GNUNET_CRYPTO_hash (&coin_public_info->coin_pub,
- sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
- &c_hash.hash);
+
+ TALER_coin_pub_hash (&coin_public_info->coin_pub,
+ &coin_public_info->age_commitment_hash,
+ &c_hash);
+
if (GNUNET_OK !=
TALER_denom_pub_verify (denom_pub,
&coin_public_info->denom_sig,
@@ -251,6 +255,7 @@ TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
const struct TALER_ExchangeWithdrawValues *alg_values,
const union TALER_DenominationBlindingKeyP *bks,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+ const struct TALER_AgeCommitmentHash *ach,
struct TALER_CoinPubHash *c_hash,
struct TALER_PlanchetDetail *pd
)
@@ -263,7 +268,7 @@ TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
if (GNUNET_OK !=
TALER_denom_blind (dk,
bks,
- NULL, /* FIXME-Oec */
+ ach,
&coin_pub,
alg_values,
c_hash,
@@ -291,6 +296,7 @@ TALER_planchet_to_coin (
const struct TALER_BlindedDenominationSignature *blind_sig,
const union TALER_DenominationBlindingKeyP *bks,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+ const struct TALER_AgeCommitmentHash *ach,
const struct TALER_CoinPubHash *c_hash,
const struct TALER_ExchangeWithdrawValues *alg_values,
struct TALER_FreshCoin *coin)
@@ -321,7 +327,9 @@ TALER_planchet_to_coin (
TALER_denom_sig_free (&coin->sig);
return GNUNET_SYSERR;
}
+
coin->coin_priv = *coin_priv;
+ coin->h_age_commitment = ach;
return GNUNET_OK;
}
@@ -396,10 +404,10 @@ TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc,
void
TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_AgeHash *age_commitment_hash,
+ const struct TALER_AgeCommitmentHash *ach,
struct TALER_CoinPubHash *coin_h)
{
- if (NULL == age_commitment_hash)
+ if (TALER_AgeCommitmentHash_isNullOrZero (ach))
{
/* No age commitment was set */
GNUNET_CRYPTO_hash (&coin_pub->eddsa_pub,
@@ -411,14 +419,14 @@ TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
/* Coin comes with age commitment. Take the hash of the age commitment
* into account */
const size_t key_s = sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey);
- const size_t age_s = sizeof(struct TALER_AgeHash);
+ const size_t age_s = sizeof(struct TALER_AgeCommitmentHash);
char data[key_s + age_s];
GNUNET_memcpy (&data[0],
&coin_pub->eddsa_pub,
key_s);
GNUNET_memcpy (&data[key_s],
- age_commitment_hash,
+ ach,
age_s);
GNUNET_CRYPTO_hash (&data,
key_s + age_s,
@@ -427,4 +435,276 @@ TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub,
}
+void
+TALER_age_commitment_hash (
+ const struct TALER_AgeCommitment *commitment,
+ struct TALER_AgeCommitmentHash *ahash)
+{
+ struct GNUNET_HashContext *hash_context;
+ struct GNUNET_HashCode hash;
+
+ GNUNET_assert (NULL != ahash);
+ if (NULL == commitment)
+ {
+ memset (ahash, 0, sizeof(struct TALER_AgeCommitmentHash));
+ return;
+ }
+
+ GNUNET_assert (__builtin_popcount (commitment->mask.mask) - 1 ==
+ commitment->num_pub);
+
+ hash_context = GNUNET_CRYPTO_hash_context_start ();
+
+ for (size_t i = 0; i < commitment->num_pub; i++)
+ {
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &commitment->pub[i],
+ sizeof(struct
+ GNUNET_CRYPTO_EddsaPublicKey));
+ }
+
+ GNUNET_CRYPTO_hash_context_finish (hash_context,
+ &hash);
+ GNUNET_memcpy (&ahash->shash.bits,
+ &hash.bits,
+ sizeof(ahash->shash.bits));
+}
+
+
+/* To a given age value between 0 and 31, returns the index of the age group
+ * defined by the given mask.
+ */
+static uint8_t
+get_age_group (
+ const struct TALER_AgeMask *mask,
+ uint8_t age)
+{
+ uint32_t m = mask->mask;
+ uint8_t i = 0;
+
+ while (m > 0)
+ {
+ if (0 >= age)
+ break;
+ m = m >> 1;
+ i += m & 1;
+ age--;
+ }
+ return i;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_age_restriction_commit (
+ const struct TALER_AgeMask *mask,
+ const uint8_t age,
+ const uint32_t seed,
+ struct TALER_AgeCommitment *new)
+{
+ uint8_t num_pub = __builtin_popcount (mask->mask) - 1;
+ uint8_t num_priv = get_age_group (mask, age) - 1;
+ size_t i;
+
+ GNUNET_assert (NULL != new);
+ GNUNET_assert (mask->mask & 1); /* fist bit must have been set */
+ GNUNET_assert (0 <= num_priv);
+ GNUNET_assert (31 > num_priv);
+
+ new->mask.mask = mask->mask;
+ new->num_pub = num_pub;
+ new->num_priv = num_priv;
+
+ new->pub = GNUNET_new_array (
+ num_pub,
+ struct TALER_AgeCommitmentPublicKeyP);
+ new->priv = GNUNET_new_array (
+ num_priv,
+ struct TALER_AgeCommitmentPrivateKeyP);
+
+ /* Create as many private keys as we need */
+ for (i = 0; i < num_priv; i++)
+ {
+ uint32_t seedBE = htonl (seed + i);
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_kdf (&new->priv[i],
+ sizeof (new->priv[i]),
+ &seedBE,
+ sizeof (seedBE),
+ "taler-age-commitment-derivation",
+ strlen (
+ "taler-age-commitment-derivation"),
+ NULL, 0))
+ goto FAIL;
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&new->priv[i].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:
+ GNUNET_free (new->pub);
+ GNUNET_free (new->priv);
+ return GNUNET_SYSERR;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_age_commitment_derive (
+ const struct TALER_AgeCommitment *orig,
+ const uint32_t seed,
+ struct TALER_AgeCommitment *new)
+{
+ struct GNUNET_CRYPTO_EccScalar val;
+
+ /*
+ * age commitment consists of GNUNET_CRYPTO_Eddsa{Private,Public}Key
+ *
+ * GNUNET_CRYPTO_EddsaPrivateKey is a
+ * unsigned char d[256 / 8];
+ *
+ * GNUNET_CRYPTO_EddsaPublicKey is a
+ * unsigned char q_y[256 / 8];
+ *
+ * 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
+ * 1. a scalar to multiply the public keys with
+ * 2. a factor to multiply the private key with
+ *
+ * Invariants:
+ * point*scalar == public(private*factor)
+ *
+ * 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
+ * GNUNET_CRYPTO_EccScalar which is a
+ * unsigned car v[256 / 8];
+ * */
+
+ GNUNET_assert (NULL != new);
+ GNUNET_assert (orig->num_pub == __builtin_popcount (orig->mask.mask) - 1);
+ GNUNET_assert (orig->num_priv <= orig->num_pub);
+
+ new->mask = orig->mask;
+ new->num_pub = orig->num_pub;
+ new->num_priv = orig->num_priv;
+ new->pub = GNUNET_new_array (
+ new->num_pub,
+ struct TALER_AgeCommitmentPublicKeyP);
+ new->priv = GNUNET_new_array (
+ 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++)
+ {
+ /* We shift all keys by the same scalar */
+ struct GNUNET_CRYPTO_EccPoint *p = (struct
+ GNUNET_CRYPTO_EccPoint *) &orig->pub[i];
+ struct GNUNET_CRYPTO_EccPoint *np = (struct
+ GNUNET_CRYPTO_EccPoint *) &new->pub[i];
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_ecc_pmul_mpi (
+ p,
+ &val,
+ np))
+ goto FAIL;
+
+ }
+
+ /* 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++)
+ {
+
+ /* 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));
+
+ /* TODO:
+ * make sure that the calculated private key generate the same public
+ * keys */
+ }
+
+ gcry_mpi_release (f);
+ gcry_ctx_release (ctx);
+ }
+
+ return GNUNET_OK;
+
+FAIL:
+ GNUNET_free (new->pub);
+ GNUNET_free (new->priv);
+ return GNUNET_SYSERR;
+}
+
+
+void
+TALER_age_restriction_commmitment_free_inside (
+ struct TALER_AgeCommitment *commitment)
+{
+ if (NULL == commitment)
+ return;
+
+ if (NULL != commitment->priv)
+ {
+ GNUNET_CRYPTO_zero_keys (
+ commitment->priv,
+ sizeof(*commitment->priv) * commitment->num_priv);
+
+ GNUNET_free (commitment->priv);
+ commitment->priv = NULL;
+ }
+
+ if (NULL != commitment->pub)
+ {
+ GNUNET_free (commitment->pub);
+ commitment->priv = NULL;
+ }
+
+ /* Caller is responsible for commitment itself */
+}
+
+
/* end of crypto.c */
diff --git a/src/util/denom.c b/src/util/denom.c
index 783e9a364..7c2c42c9e 100644
--- a/src/util/denom.c
+++ b/src/util/denom.c
@@ -297,14 +297,14 @@ enum GNUNET_GenericReturnValue
TALER_denom_blind (
const struct TALER_DenominationPublicKey *dk,
const union TALER_DenominationBlindingKeyP *coin_bks,
- const struct TALER_AgeHash *age_commitment_hash,
+ const struct TALER_AgeCommitmentHash *ach,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_ExchangeWithdrawValues *alg_values,
struct TALER_CoinPubHash *c_hash,
struct TALER_BlindedPlanchet *blinded_planchet)
{
TALER_coin_pub_hash (coin_pub,
- age_commitment_hash,
+ ach,
c_hash);
switch (dk->cipher)
{
diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c
index fbf30e3a4..cda17d9b6 100644
--- a/src/util/test_crypto.c
+++ b/src/util/test_crypto.c
@@ -152,6 +152,7 @@ test_planchets_rsa (void)
&alg_values,
&bks,
&coin_priv,
+ NULL, /* no age commitment */
&c_hash,
&pd));
GNUNET_assert (GNUNET_OK ==
@@ -164,6 +165,7 @@ test_planchets_rsa (void)
&blind_sig,
&bks,
&coin_priv,
+ NULL, /* no age commitment */
&c_hash,
&alg_values,
&coin));
@@ -175,6 +177,8 @@ test_planchets_rsa (void)
}
+/** FIXME-oec: Add test for planchets with age commitment hash */
+
/**
* @brief Function for CS signatures to derive public R_0 and R_1
*
@@ -392,10 +396,12 @@ main (int argc,
return 1;
if (0 != test_planchets ())
return 2;
- if (0 != test_exchange_sigs ())
+ if (0 != test_planchets_with_age_commitment ())
return 3;
- if (0 != test_merchant_sigs ())
+ if (0 != test_exchange_sigs ())
return 4;
+ if (0 != test_merchant_sigs ())
+ return 5;
return 0;
}
diff --git a/src/util/test_helper_rsa.c b/src/util/test_helper_rsa.c
index 679f5d7f4..2ead8a6e1 100644
--- a/src/util/test_helper_rsa.c
+++ b/src/util/test_helper_rsa.c
@@ -269,6 +269,7 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh)
bool success = false;
struct TALER_PlanchetMasterSecretP ps;
struct TALER_ExchangeWithdrawValues alg_values;
+ struct TALER_AgeCommitmentHash ach;
struct TALER_CoinPubHash c_hash;
struct TALER_CoinSpendPrivateKeyP coin_priv;
union TALER_DenominationBlindingKeyP bks;
@@ -280,6 +281,9 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh)
alg_values.cipher = TALER_DENOMINATION_RSA;
TALER_planchet_setup_coin_priv (&ps, &alg_values, &coin_priv);
TALER_planchet_blinding_secret_create (&ps, &alg_values, &bks);
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ &ach,
+ sizeof(ach));
for (unsigned int i = 0; i<MAX_KEYS; i++)
{
@@ -296,6 +300,7 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh)
&alg_values,
&bks,
&coin_priv,
+ &ach,
&c_hash,
&pd));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -440,6 +445,7 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
struct GNUNET_TIME_Relative duration;
struct TALER_PlanchetMasterSecretP ps;
struct TALER_CoinSpendPrivateKeyP coin_priv;
+ struct TALER_AgeCommitmentHash ach;
union TALER_DenominationBlindingKeyP bks;
struct TALER_ExchangeWithdrawValues alg_values;
@@ -447,7 +453,9 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
alg_values.cipher = TALER_DENOMINATION_RSA;
TALER_planchet_setup_coin_priv (&ps, &alg_values, &coin_priv);
TALER_planchet_blinding_secret_create (&ps, &alg_values, &bks);
-
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ &ach,
+ sizeof(ach));
duration = GNUNET_TIME_UNIT_ZERO;
TALER_CRYPTO_helper_rsa_poll (dh);
for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
@@ -477,6 +485,7 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh,
&alg_values,
&bks,
&coin_priv,
+ &ach,
&c_hash,
&pd));
/* use this key as long as it works */
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
index 01f33ae83..88cd8de06 100644
--- a/src/util/wallet_signatures.c
+++ b/src/util/wallet_signatures.c
@@ -29,6 +29,7 @@ TALER_wallet_deposit_sign (
const struct TALER_Amount *deposit_fee,
const struct TALER_MerchantWireHash *h_wire,
const struct TALER_PrivateContractHash *h_contract_terms,
+ const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_ExtensionContractHash *h_extensions,
const struct TALER_DenominationHash *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp,
@@ -48,8 +49,12 @@ TALER_wallet_deposit_sign (
.merchant = *merchant_pub
};
+ if (NULL != h_age_commitment)
+ dr.h_age_commitment = *h_age_commitment;
+
if (NULL != h_extensions)
dr.h_extensions = *h_extensions;
+
TALER_amount_hton (&dr.amount_with_fee,
amount);
TALER_amount_hton (&dr.deposit_fee,
@@ -66,6 +71,7 @@ TALER_wallet_deposit_verify (
const struct TALER_Amount *deposit_fee,
const struct TALER_MerchantWireHash *h_wire,
const struct TALER_PrivateContractHash *h_contract_terms,
+ const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_ExtensionContractHash *h_extensions,
const struct TALER_DenominationHash *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp,
@@ -82,11 +88,17 @@ TALER_wallet_deposit_verify (
.h_denom_pub = *h_denom_pub,
.wallet_timestamp = GNUNET_TIME_timestamp_hton (wallet_timestamp),
.refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline),
- .merchant = *merchant_pub
+ .merchant = *merchant_pub,
+ .h_age_commitment = {{{0}}},
+ .h_extensions = {{{0}}}
};
+ if (NULL != h_age_commitment)
+ dr.h_age_commitment = *h_age_commitment;
+
if (NULL != h_extensions)
dr.h_extensions = *h_extensions;
+
TALER_amount_hton (&dr.amount_with_fee,
amount);
TALER_amount_hton (&dr.deposit_fee,
@@ -131,6 +143,7 @@ TALER_wallet_link_verify (
const struct TALER_TransferPublicKeyP *transfer_pub,
const struct TALER_BlindedCoinHash *h_coin_ev,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
+ const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
struct TALER_LinkDataPS ldp = {
@@ -138,9 +151,13 @@ TALER_wallet_link_verify (
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK),
.h_denom_pub = *h_denom_pub,
.transfer_pub = *transfer_pub,
- .coin_envelope_hash = *h_coin_ev
+ .coin_envelope_hash = *h_coin_ev,
+ .h_age_commitment = {{{0}}}
};
+ if (NULL != h_age_commitment)
+ ldp.h_age_commitment = *h_age_commitment;
+
return
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
&ldp,
@@ -263,6 +280,7 @@ TALER_wallet_melt_verify (
const struct TALER_Amount *melt_fee,
const struct TALER_RefreshCommitmentP *rc,
const struct TALER_DenominationHash *h_denom_pub,
+ const struct TALER_AgeCommitmentHash *h_age_commitment,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
@@ -270,9 +288,13 @@ TALER_wallet_melt_verify (
.purpose.size = htonl (sizeof (melt)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT),
.rc = *rc,
- .h_denom_pub = *h_denom_pub
+ .h_denom_pub = *h_denom_pub,
+ .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,