summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/exchange/taler-exchange-httpd_age-withdraw_reveal.c96
-rw-r--r--src/include/taler_crypto_lib.h48
-rw-r--r--src/util/age_restriction.c268
-rw-r--r--src/util/crypto.c35
4 files changed, 342 insertions, 105 deletions
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
index cdb8115f8..b378e0e2b 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
@@ -35,7 +35,8 @@ struct AgeRevealContext
{
/**
- * Commitment for the age-withdraw operation.
+ * Commitment for the age-withdraw operation, previously called by the
+ * client.
*/
struct TALER_AgeWithdrawCommitmentHashP ach;
@@ -63,11 +64,6 @@ struct AgeRevealContext
struct TEH_DenominationKey *denom_keys;
/**
- * #num_coins hases of blinded coins.
- */
- struct TALER_BlindedCoinHashP *coin_evs;
-
- /**
* Total sum of all denominations' values
**/
struct TALER_Amount total_amount;
@@ -78,6 +74,11 @@ struct AgeRevealContext
struct TALER_Amount total_fee;
/**
+ * #num_coins hashes of blinded coins.
+ */
+ struct TALER_BlindedCoinHashP *coin_evs;
+
+ /**
* #num_coins*(kappa - 1) disclosed coins.
*/
struct GNUNET_CRYPTO_EddsaPrivateKey *disclosed_coins;
@@ -343,17 +344,15 @@ denomination_is_valid (
connection,
result);
- /* Does the denomination exist? */
if (NULL == dks)
{
+ /* The denomination doesn't exist */
GNUNET_assert (result != NULL);
/* Note: a HTTP-response has been queued and result has been set by
* TEH_keys_denominations_by_hash2 */
return false;
}
- /* Is the denomation still and already valid? */
-
if (GNUNET_TIME_absolute_is_past (dks->meta.expire_withdraw.abs_time))
{
/* This denomination is past the expiration time for withdraws */
@@ -504,7 +503,7 @@ are_denominations_valid (
if (0 != TALER_amount_cmp (&sum, amount_with_fee))
{
- GNUNET_break (0);
+ GNUNET_break_op (0);
*result = TALER_MHD_reply_with_ec (connection,
TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_INCORRECT,
NULL);
@@ -516,6 +515,62 @@ are_denominations_valid (
}
+/**
+ * Checks the validity of the disclosed coins as follows:
+ * - Derives and calculates the disclosed coins'
+ * - public keys,
+ * - nonces (if applicable),
+ * - age commitments,
+ * - blindings
+ * - blinded hashes
+ * - Computes h_commitment with those calculated and the undisclosed hashes
+ * - Compares h_commitment with the value from the original commitment
+ * - Verifies that all public keys in indices larger than max_age_group are
+ * derived from the constant public key.
+ *
+ * The derivation of the blindings, (potential) nonces and age-commitment from
+ * a coin's private keys is defined in
+ * https://docs.taler.net/design-documents/024-age-restriction.html#withdraw
+ *
+ * @param connection HTTP-connection to the client
+ * @param h_commitment_orig Original commitment
+ * @param max_age_group Maximum age group allowed for the age restriction
+ * @param noreveal_idx Index that was given to the client in response to the age-withdraw request
+ * @param num_coins Number of coins
+ * @param coin_evs The Hashes of the undisclosed, blinded coins
+ * @param discloded_coins The private keys of the disclosed coins
+ * @param denom_keys The array of denomination keys. Needed to detect Clause-Schnorr-based denominations
+ * @param[out] result On error, a HTTP-response will be queued and result set accordingly
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+static enum GNUNET_GenericReturnValue
+verify_commitment_and_max_age (
+ struct MHD_Connection *connection,
+ const struct TALER_AgeWithdrawCommitmentHashP *h_commitment_orig,
+ uint32_t max_age_group,
+ uint32_t noreveal_idx,
+ uint32_t num_coins,
+ const struct TALER_BlindedCoinHashP *coin_evs,
+ const struct GNUNET_CRYPTO_EddsaPrivateKey *disclosed_coins,
+ const struct TEH_DenominationKey *denom_keys,
+ MHD_RESULT *result)
+{
+ struct TALER_BlindedCoinHashP *disclosed_evs; /* Will contain all calculated hashes */
+
+ disclosed_evs = GNUNET_new_array (
+ num_coins * (TALER_CNC_KAPPA - 1),
+ struct TALER_BlindedCoinHashP);
+
+ for (uint32_t i = 0; i < num_coins; i++)
+ {
+ /* FIXME:oec: Calculate new coins and blinded hashes */
+ /*TALER_age_restriction_commit_from_base(); */
+ }
+
+ return GNUNET_SYSERR;
+}
+
+
MHD_RESULT
TEH_handler_age_withdraw_reveal (
struct TEH_RequestContext *rc,
@@ -583,15 +638,24 @@ TEH_handler_age_withdraw_reveal (
&result))
break;
+ /* Verify the computed h_commitment equals the committed one and that
+ * coins have a maximum age group of max_age_group */
+ if (GNUNET_OK != verify_commitment_and_max_age (
+ rc->connection,
+ &actx.commitment.h_commitment,
+ actx.commitment.max_age_group,
+ actx.commitment.noreveal_index,
+ actx.num_coins,
+ actx.coin_evs,
+ actx.disclosed_coins,
+ actx.denom_keys,
+ &result))
+ break;
+
+ /* TODO:oec: sign the coins */
} while(0);
- /* TODO:oec: compute the disclosed blinded coins */
- /* TODO:oec: generate h_commitment_comp */
- /* TODO:oec: compare h_commitment_comp against h_commitment */
- /* TODO:oec: sign the coins */
- /* TODO:oec: send response */
-
age_reveal_context_free (&actx);
GNUNET_JSON_parse_free (spec);
return result;
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index db50efa10..f7462d093 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -18,6 +18,7 @@
* @brief taler-specific crypto functions
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
* @author Christian Grothoff <christian@grothoff.org>
+ * @author Özgür Kesim <oec-taler@kesim.org>
*/
#if ! defined (__TALER_UTIL_LIB_H_INSIDE__)
#error "Only <taler_util.h> can be included directly."
@@ -1309,13 +1310,8 @@ struct TALER_AgeAttestation
#endif
};
-
-extern const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash;
-#define TALER_AgeCommitmentHash_isNullOrZero(ph) \
- ((NULL == ph) || \
- (0 == memcmp (ph, \
- &TALER_ZeroAgeCommitmentHash, \
- sizeof(struct TALER_AgeCommitmentHash))))
+#define TALER_AgeCommitmentHash_isNullOrZero(ph) ((NULL == ph) || \
+ GNUNET_is_zero (ph))
/**
* @brief Type of public signing keys for verifying blindly signed coins.
@@ -5931,4 +5927,42 @@ void
TALER_age_commitment_proof_free (
struct TALER_AgeCommitmentProof *p);
+
+/**
+ * @brief For age-withdraw, clients have to prove that the public keys for all
+ * age groups larger than the allowed maximum age group are derived by scalar
+ * multiplication from this Edx25519 public key (in Crockford Base32 encoding):
+ *
+ * DZJRF6HXN520505XDAWM8NMH36QV9J3VH77265WQ09EBQ76QSKCG
+ *
+ * Its private key was chosen randomly and then deleted.
+ */
+extern struct
+#ifndef AGE_RESTRICTION_WITH_ECDSA
+GNUNET_CRYPTO_Edx25519PublicKey
+#else
+GNUNET_CRYPTO_EcdsaPublicKey
+#endif
+TALER_age_commitment_base_public_key;
+
+/**
+ * @brief Similiar to TALER_age_restriction_commit, but takes the coin's
+ * private key as seed input and calculates the public keys in the slots larger
+ * than the given age as derived from TALER_age_commitment_base_public_key.
+ *
+ * See https://docs.taler.net/core/api-exchange.html#withdraw-with-age-restriction
+ *
+ * @param mask The age mask, defining the age groups
+ * @param max_age The maximum age for this coin.
+ * @param coin_priv The private key of the coin from which we derive the age restriction
+ * @param[out] comm_proof The commitment and proof for age restriction for age @a max_age
+ */
+enum GNUNET_GenericReturnValue
+TALER_age_restriction_commit_from_base (
+ const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+ const struct TALER_AgeMask *mask,
+ uint8_t max_age,
+ struct TALER_AgeCommitmentProof *comm_proof);
+
+
#endif
diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c
index f4ac9abea..594e1d464 100644
--- a/src/util/age_restriction.c
+++ b/src/util/age_restriction.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -24,6 +24,19 @@
#include <gnunet/gnunet_json_lib.h>
#include <gcrypt.h>
+struct
+#ifndef AGE_RESTRICTION_WITH_ECDSA
+GNUNET_CRYPTO_Edx25519PublicKey
+#else
+GNUNET_CRYPTO_EcdsaPublicKey
+#endif
+TALER_age_commitment_base_public_key = {
+ .q_y = { 0x6f, 0xe5, 0x87, 0x9a, 0x3d, 0xa9, 0x44, 0x20,
+ 0x80, 0xbd, 0x6a, 0xb9, 0x44, 0x56, 0x91, 0x19,
+ 0xaf, 0xb4, 0xc8, 0x7b, 0x89, 0xce, 0x23, 0x17,
+ 0x97, 0x20, 0x5c, 0xbb, 0x9c, 0xd7, 0xcc, 0xd9},
+};
+
void
TALER_age_commitment_hash (
const struct TALER_AgeCommitment *commitment,
@@ -82,36 +95,78 @@ get_age_group (
}
+#ifdef AGE_RESTRICTION_WITH_ECDSA
+/* @brief Helper function to generate a ECDSA private key
+ *
+ * @param seed Input seed
+ * @param size Size of the seed in bytes
+ * @param[out] pkey ECDSA private key
+ * @return GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+ecdsa_create_from_seed (
+ const void *seed,
+ size_t seed_size,
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *key)
+{
+ enum GNUNET_GenericReturnValue ret;
+ ret = GNUNET_CRYPTO_kdf (key,
+ sizeof (*key),
+ &seed,
+ seed_size,
+ "age commitment",
+ sizeof ("age commitment") - 1,
+ NULL, 0);
+ if (GNUNET_OK != ret)
+ return ret;
+
+ /* See GNUNET_CRYPTO_ecdsa_key_create */
+ key->d[0] &= 248;
+ key->d[31] &= 127;
+ key->d[31] |= 64;
+
+ return GNUNET_OK;
+}
+
+
+#endif
+
+
enum GNUNET_GenericReturnValue
TALER_age_restriction_commit (
const struct TALER_AgeMask *mask,
const uint8_t age,
const struct GNUNET_HashCode *seed,
- struct TALER_AgeCommitmentProof *new)
+ struct TALER_AgeCommitmentProof *ncp)
{
struct GNUNET_HashCode seed_i;
- uint8_t num_pub = __builtin_popcount (mask->bits) - 1;
- uint8_t num_priv = get_age_group (mask, age);
+ uint8_t num_pub;
+ uint8_t num_priv;
size_t i;
+ GNUNET_assert (NULL != mask);
GNUNET_assert (NULL != seed);
- GNUNET_assert (NULL != new);
+ GNUNET_assert (NULL != ncp);
GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
+
+ num_pub = __builtin_popcount (mask->bits) - 1;
+ num_priv = get_age_group (mask, age);
+
GNUNET_assert (31 > num_priv);
GNUNET_assert (num_priv <= num_pub);
seed_i = *seed;
- new->commitment.mask.bits = mask->bits;
- new->commitment.num = num_pub;
- new->proof.num = num_priv;
- new->proof.keys = NULL;
+ ncp->commitment.mask.bits = mask->bits;
+ ncp->commitment.num = num_pub;
+ ncp->proof.num = num_priv;
+ ncp->proof.keys = NULL;
- new->commitment.keys = GNUNET_new_array (
+ ncp->commitment.keys = GNUNET_new_array (
num_pub,
struct TALER_AgeCommitmentPublicKeyP);
if (0 < num_priv)
- new->proof.keys = GNUNET_new_array (
+ ncp->proof.keys = GNUNET_new_array (
num_priv,
struct TALER_AgeCommitmentPrivateKeyP);
@@ -126,47 +181,33 @@ TALER_age_restriction_commit (
/* Only save the private keys for age groups less than num_priv */
if (i < num_priv)
- pkey = &new->proof.keys[i];
+ pkey = &ncp->proof.keys[i];
#ifndef AGE_RESTRICTION_WITH_ECDSA
GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
sizeof(seed_i),
&pkey->priv);
GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
- &new->commitment.keys[i].pub);
- seed_i.bits[0] += 1;
- }
-
- return GNUNET_OK;
+ &ncp->commitment.keys[i].pub);
#else
- if (GNUNET_OK !=
- GNUNET_CRYPTO_kdf (pkey,
- sizeof (*pkey),
- &salti,
- sizeof (salti),
- "age commitment",
- strlen ("age commitment"),
- NULL, 0))
- goto FAIL;
-
- /* See GNUNET_CRYPTO_ecdsa_key_create */
- pkey->priv.d[0] &= 248;
- pkey->priv.d[31] &= 127;
- pkey->priv.d[31] |= 64;
+ if (GNUNET_OK !=
+ ecdsa_create_from_seed (&seed_i,
+ sizeof(seed_i),
+ &pkey->priv))
+ {
+ GNUNET_free (ncp->commitment.keys);
+ GNUNET_free (ncp->proof.keys);
+ return GNUNET_SYSERR;
+ }
GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
- &new->commitment.keys[i].pub);
+ &ncp->commitment.keys[i].pub);
+#endif
+ seed_i.bits[0] += 1;
}
return GNUNET_OK;
-
-FAIL:
- GNUNET_free (new->commitment.keys);
- if (NULL != new->proof.keys)
- GNUNET_free (new->proof.keys);
- return GNUNET_SYSERR;
-#endif
}
@@ -216,33 +257,30 @@ TALER_age_commitment_derive (
&newacp->proof.keys[i].priv);
}
#else
- char label[sizeof(uint64_t) + 1] = {0};
-
- /* Because GNUNET_CRYPTO_ecdsa_public_key_derive expects char * (and calls
- * strlen on it), we must avoid 0's in the label. */
- uint64_t nz_salt = salt | 0x8040201008040201;
- memcpy (label, &nz_salt, sizeof(nz_salt));
-
- /* 1. Derive the public keys */
- for (size_t i = 0; i < orig->commitment.num; i++)
{
- GNUNET_CRYPTO_ecdsa_public_key_derive (
- &orig->commitment.keys[i].pub,
- label,
- "age commitment derive",
- &newacp->commitment.keys[i].pub);
- }
+ const char *label = GNUNET_h2s (salt);
- /* 2. Derive the private keys */
- for (size_t i = 0; i < orig->proof.num; i++)
- {
- struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
- priv = GNUNET_CRYPTO_ecdsa_private_key_derive (
- &orig->proof.keys[i].priv,
- label,
- "age commitment derive");
- newacp->proof.keys[i].priv = *priv;
- GNUNET_free (priv);
+ /* 1. Derive the public keys */
+ for (size_t i = 0; i < orig->commitment.num; i++)
+ {
+ GNUNET_CRYPTO_ecdsa_public_key_derive (
+ &orig->commitment.keys[i].pub,
+ label,
+ "age commitment derive",
+ &newacp->commitment.keys[i].pub);
+ }
+
+ /* 2. Derive the private keys */
+ for (size_t i = 0; i < orig->proof.num; i++)
+ {
+ struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
+ priv = GNUNET_CRYPTO_ecdsa_private_key_derive (
+ &orig->proof.keys[i].priv,
+ label,
+ "age commitment derive");
+ newacp->proof.keys[i].priv = *priv;
+ GNUNET_free (priv);
+ }
}
#endif
@@ -546,4 +584,106 @@ TALER_age_mask_to_string (
}
+enum GNUNET_GenericReturnValue
+TALER_age_restriction_commit_from_base (
+ const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+ const struct TALER_AgeMask *mask,
+ uint8_t max_age,
+ struct TALER_AgeCommitmentProof *ncp)
+{
+ struct GNUNET_HashCode seed_i = {0};
+ uint8_t num_pub;
+ uint8_t num_priv;
+
+ GNUNET_assert (NULL != mask);
+ GNUNET_assert (NULL != coin_priv);
+ GNUNET_assert (NULL != ncp);
+ GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
+
+ num_pub = __builtin_popcount (mask->bits) - 1;
+ num_priv = get_age_group (mask, max_age);
+
+ GNUNET_assert (31 > num_priv);
+ GNUNET_assert (num_priv <= num_pub);
+
+ ncp->commitment.mask.bits = mask->bits;
+ ncp->commitment.num = num_pub;
+ ncp->proof.num = num_priv;
+ ncp->proof.keys = NULL;
+
+ ncp->commitment.keys = GNUNET_new_array (
+ num_pub,
+ struct TALER_AgeCommitmentPublicKeyP);
+
+ if (0 < num_priv)
+ ncp->proof.keys = GNUNET_new_array (
+ num_priv,
+ struct TALER_AgeCommitmentPrivateKeyP);
+
+ /* Create as many private keys as allow with max_age and derive the
+ * corresponding public keys. The rest of the needed public keys are created
+ * by scalar mulitplication with the TALER_age_commitment_base_public_key. */
+ for (size_t i = 0; i < num_pub; i++)
+ {
+ enum GNUNET_GenericReturnValue ret;
+ const char *label = i < num_priv ? "age-commitment" : "age-factor";
+
+ ret = GNUNET_CRYPTO_kdf (&seed_i, sizeof(seed_i),
+ coin_priv, sizeof(*coin_priv),
+ label, strlen (label),
+ &i, sizeof(i),
+ NULL, 0);
+ GNUNET_assert (GNUNET_OK == ret);
+
+ /* Only generate and save the private keys and public keys for age groups
+ * less than num_priv */
+ if (i < num_priv)
+ {
+ struct TALER_AgeCommitmentPrivateKeyP *pkey = &ncp->proof.keys[i];
+
+#ifndef AGE_RESTRICTION_WITH_ECDSA
+ GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
+ sizeof(seed_i),
+ &pkey->priv);
+ GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
+ &ncp->commitment.keys[i].pub);
+#else
+ if (GNUNET_OK != ecdsa_create_from_seed (&seed_i,
+ sizeof(seed_i),
+ &pkey->priv))
+ {
+ GNUNET_free (ncp->commitment.keys);
+ GNUNET_free (ncp->proof.keys);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
+ &ncp->commitment.keys[i].pub);
+#endif
+ }
+ else
+ {
+ /* For all indices larger than num_priv, derive a public key from
+ * TALER_age_commitment_base_public_key by scalar multiplication */
+#ifndef AGE_RESTRICTION_WITH_ECDSA
+ GNUNET_CRYPTO_edx25519_public_key_derive (
+ &TALER_age_commitment_base_public_key,
+ &seed_i,
+ sizeof(seed_i),
+ &ncp->commitment.keys[i].pub);
+#else
+
+ GNUNET_CRYPTO_ecdsa_public_key_derive (
+ &TALER_age_commitment_base_public_key,
+ GNUNET_h2s (&seed_i),
+ "age withdraw",
+ &ncp->commitment.keys[i].pub);
+#endif
+ }
+ }
+
+ return GNUNET_OK;
+
+}
+
+
/* end util/age_restriction.c */
diff --git a/src/util/crypto.c b/src/util/crypto.c
index 5cbba8135..bb14b6cdc 100644
--- a/src/util/crypto.c
+++ b/src/util/crypto.c
@@ -27,11 +27,6 @@
#include <gcrypt.h>
/**
- * Used in TALER_AgeCommitmentHash_isNullOrZero for comparison
- */
-const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash = {0};
-
-/**
* Function called by libgcrypt on serious errors.
* Prints an error message and aborts the process.
*
@@ -426,19 +421,23 @@ 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_AgeCommitmentHash);
- char data[key_s + age_s];
-
- GNUNET_memcpy (&data[0],
- &coin_pub->eddsa_pub,
- key_s);
- GNUNET_memcpy (&data[key_s],
- ach,
- age_s);
- GNUNET_CRYPTO_hash (&data,
- key_s + age_s,
- &coin_h->hash);
+ struct GNUNET_HashContext *hash_context;
+
+ hash_context = GNUNET_CRYPTO_hash_context_start ();
+
+ GNUNET_CRYPTO_hash_context_read (
+ hash_context,
+ &coin_pub->eddsa_pub,
+ sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
+
+ GNUNET_CRYPTO_hash_context_read (
+ hash_context,
+ ach,
+ sizeof(struct TALER_AgeCommitmentHash));
+
+ GNUNET_CRYPTO_hash_context_finish (
+ hash_context,
+ &coin_h->hash);
}
}