diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 38f91a8e0..a8dc4b0d7 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -869,7 +869,7 @@ struct TALER_AgeCommitmentHash */ struct TALER_AgeAttestation { - struct GNUNET_CRYPTO_EddsaSignature attest; + struct GNUNET_CRYPTO_EddsaSignature eddsa_signature; }; extern const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash; diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 72150ca65..3758792ae 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -271,6 +271,12 @@ */ #define TALER_SIGNATURE_WALLET_COIN_RECOUP_REFRESH 1206 +/** + * Signature using a age restriction key for attestation of a particular + * age/age-group. + */ +#define TALER_SIGNATURE_WALLET_AGE_ATTESTATION 1207 + /******************************/ /* Security module signatures */ @@ -1704,6 +1710,27 @@ struct TALER_MerchantPaySessionSigPS }; +/** + * Used for attestation of a particular age + */ +struct TALER_AgeAttestationPS +{ + /** + * Purpose must be #TALER_SIGNATURE_WALLET_AGE_ATTESTATION. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Age mask that defines the underlying age groups + */ + struct TALER_AgeMask mask; + + /** + * The particular age that this attestation is for + */ + uint8_t age; +}; + GNUNET_NETWORK_STRUCT_END diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 997b49f29..022d0611c 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -75,6 +75,7 @@ lib_LTLIBRARIES = \ libtalerutil.la libtalerutil_la_SOURCES = \ + age_restriction.c \ amount.c \ auditor_signatures.c \ config.c \ diff --git a/src/util/crypto.c b/src/util/crypto.c index 8e6c89aa9..974566dc0 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -443,317 +443,6 @@ 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); - - hash_context = GNUNET_CRYPTO_hash_context_start (); - - for (size_t i = 0; i < commitment->num; 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 uint64_t salt, - struct TALER_AgeCommitmentProof *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); - GNUNET_assert (num_priv <= num_pub); - - new->commitment.mask.mask = mask->mask; - new->commitment.num = num_pub; - new->proof.num = num_priv; - - new->commitment.pub = GNUNET_new_array ( - num_pub, - struct TALER_AgeCommitmentPublicKeyP); - new->proof.priv = GNUNET_new_array ( - num_priv, - struct TALER_AgeCommitmentPrivateKeyP); - - /* 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++) - { - 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->proof.priv[i]; - - if (GNUNET_OK != - 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 (&priv->eddsa_priv, - &new->commitment.pub[i].eddsa_pub); - } - - return GNUNET_OK; - -FAIL: - GNUNET_free (new->commitment.pub); - GNUNET_free (new->proof.priv); - return GNUNET_SYSERR; -} - - -enum GNUNET_GenericReturnValue -TALER_age_commitment_derive ( - const struct TALER_AgeCommitmentProof *orig, - const uint64_t salt, - struct TALER_AgeCommitmentProof *new) -{ - 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 - * - * 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 salt 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 scalar for use in point multiplications is a - * GNUNET_CRYPTO_EccScalar which is a - * unsigned char v[256 / 8]; - * */ - - GNUNET_assert (NULL != new); - GNUNET_assert (orig->commitment.num== __builtin_popcount ( - orig->commitment.mask.mask) - 1); - GNUNET_assert (orig->proof.num <= orig->commitment.num); - - new->commitment.mask = orig->commitment.mask; - new->commitment.num = orig->commitment.num; - new->proof.num = orig->proof.num; - new->commitment.pub = GNUNET_new_array ( - new->commitment.num, - struct TALER_AgeCommitmentPublicKeyP); - new->proof.priv = GNUNET_new_array ( - new->proof.num, - struct TALER_AgeCommitmentPrivateKeyP); - - /* scalar multiply the public keys on the curve */ - for (size_t i = 0; i < orig->commitment.num; i++) - { - /* We shift all keys by the same scalar */ - struct GNUNET_CRYPTO_EccPoint *p = (struct - GNUNET_CRYPTO_EccPoint *) &orig-> - commitment.pub[i]; - struct GNUNET_CRYPTO_EccPoint *np = (struct - GNUNET_CRYPTO_EccPoint *) &new-> - commitment.pub[i]; - if (GNUNET_OK != - GNUNET_CRYPTO_ecc_pmul_mpi ( - p, - &scalar, - np)) - goto FAIL; - - } - - /* multiply the private keys */ - /* we borough ideas from GNUNET_CRYPTO_ecdsa_private_key_derive */ - { - for (size_t i = 0; i < orig->proof.num; 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)); - - for (size_t j = 0; j < 32; j++) - dc[i] = orig->proof.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); - GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d); - - for (size_t j = 0; j <32; j++) - new->proof.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: add test to make sure that the calculated private key generate - * the same public keys */ - } - - } - - return GNUNET_OK; - -FAIL: - GNUNET_free (new->commitment.pub); - GNUNET_free (new->proof.priv); - return GNUNET_SYSERR; -} - - -void -TALER_age_commitment_free ( - struct TALER_AgeCommitment *commitment) -{ - if (NULL == commitment) - return; - - if (NULL != commitment->pub) - { - GNUNET_free (commitment->pub); - commitment->pub = NULL; - } - GNUNET_free (commitment); -} - - -void -TALER_age_proof_free ( - struct TALER_AgeProof *proof) -{ - if (NULL != proof->priv) - { - GNUNET_CRYPTO_zero_keys ( - proof->priv, - sizeof(*proof->priv) * proof->num); - - GNUNET_free (proof->priv); - proof->priv = NULL; - } - GNUNET_free (proof); -} - - -void -TALER_age_commitment_proof_free ( - struct TALER_AgeCommitmentProof *cp) -{ - if (NULL != cp->proof.priv) - { - GNUNET_CRYPTO_zero_keys ( - cp->proof.priv, - sizeof(*cp->proof.priv) * cp->proof.num); - - GNUNET_free (cp->proof.priv); - cp->proof.priv = NULL; - } - - if (NULL != cp->commitment.pub) - { - GNUNET_free (cp->commitment.pub); - cp->commitment.pub = NULL; - } -} - - enum GNUNET_GenericReturnValue TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet, const struct TALER_DenominationHashP *denom_hash,