diff --git a/src/extensions/extension_age_restriction.c b/src/extensions/extension_age_restriction.c index 80474a9fc..704161e56 100644 --- a/src/extensions/extension_age_restriction.c +++ b/src/extensions/extension_age_restriction.c @@ -225,7 +225,7 @@ age_restriction_load_taler_config ( if (GNUNET_OK == ret) { _config.mask.mask = mask.mask; - _config.num_groups = __builtin_popcount (mask.mask); + _config.num_groups = __builtin_popcount (mask.mask) - 1; /* no underflow, first bit always set */ this->config = &_config; } @@ -258,9 +258,18 @@ age_restriction_load_json_config ( if (TALER_Extension_AgeRestriction != this->type) return GNUNET_SYSERR; - _config.mask.mask = mask.mask; - _config.num_groups = __builtin_popcount (mask.mask); + _config.num_groups = 0; + + if (mask.mask > 0) + { + /* if the mask is not zero, the first bit MUST be set */ + if (0 == (mask.mask & 1)) + return GNUNET_SYSERR; + + _config.num_groups = __builtin_popcount (mask.mask) - 1; + } + this->config = &_config; if (NULL != this->config_json) diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index d157bccb3..b1a27c063 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -2613,17 +2613,18 @@ struct TALER_AgeCommitment struct TALER_AgeMask mask; /* The number of public keys, which must be the same as the number of - * groups in the mask + * groups in the mask. */ - size_t num_pub_keys; + size_t num_pub; - /* A NULL-terminated list of public keys. - * The list must be exactly of length @a num_pub_keys, i. e. the same as the - * number of age groups defined in the mask. + /* The list of #num_pub public keys. In must have same size as the number of + * age groups defined in the mask. * * A hash of this list is the hashed commitment that goes into FDC * calculation during the withdraw and refresh operations for new coins. That * way, the particular age commitment becomes mandatory and bound to a coin. + * + * The list has been allocated via GNUNET_malloc. */ struct TALER_AgeCommitmentPublicKeyP *pub; @@ -2631,31 +2632,19 @@ struct TALER_AgeCommitment * this number corresponds to the largest age group that is supported with * this age commitment. */ - size_t num_priv_keys; + size_t num_priv; - /* A NULL-terminated list of private keys. + /* List of #num_priv private keys. + * * Note that the list can be _smaller_ than the corresponding list of public - * keys! In that case, the wallet can sign off only for a subset of the age + * keys. In that case, the wallet can sign off only for a subset of the age * groups. + * + * The list has been allocated via GNUNET_malloc. */ struct TALER_AgeCommitmentPrivateKeyP *priv; }; -/* - * @brief Generates an age commitent for the given age. - * - * @param mask The age mask the defines the age groups - * @param age The age for which an age commitment is generated - * @param seed The seed that goes into the key generation. MUST be choosen uniformly random. - * @param commitment[out] The generated age commitment, allocated via GNUNET_malloc - */ -void -TALER_age_restriction_commit ( - const struct TALER_AgeMask *mask, - const uint8_t age, - const uint32_t seed, - struct TALER_AgeCommitment *commitment); - /* * @brief Generates a hash of the public keys in the age commitment. * @@ -2667,18 +2656,36 @@ TALER_age_commitment_hash ( const struct TALER_AgeCommitment *commitment, struct TALER_AgeCommitmentHash *hash); + +/* + * @brief Generates an age commitent for the given age. + * + * @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 commitment[out] The generated age commitment, allocated via GNUNET_malloc on success + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + */ +enum GNUNET_GenericReturnValue +TALER_age_restriction_commit ( + const struct TALER_AgeMask *mask, + const uint8_t age, + const uint32_t seed, + 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 derived[out] The resulting age commitment, allocated via GNUNET_malloc. + * @param derived[out] The resulting age commitment, allocated via GNUNET_malloc on success. + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ -void +enum GNUNET_GenericReturnValue TALER_age_restriction_derive ( const struct TALER_AgeCommitment *orig, const uint32_t seed, - const struct TALER_AgeCommitment *derived); + struct TALER_AgeCommitment **derived); #endif diff --git a/src/util/crypto.c b/src/util/crypto.c index d80661640..3e02b71d6 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -381,12 +381,12 @@ TALER_age_commitment_hash ( return; } - GNUNET_assert (__builtin_popcount (commitment->mask.mask) == - commitment->num_pub_keys); + 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_keys; i++) + for (size_t i = 0; i < commitment->num_pub; i++) { GNUNET_CRYPTO_hash_context_read (hash_context, &commitment->pub[i], @@ -402,24 +402,215 @@ TALER_age_commitment_hash ( } -void +enum GNUNET_GenericReturnValue TALER_age_restriction_derive ( const struct TALER_AgeCommitment *orig, const uint32_t seed, - const struct TALER_AgeCommitment *derived) + struct TALER_AgeCommitment **derived) { - /* TODO oec */ + /* + * 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]; + * */ + struct GNUNET_CRYPTO_EccScalar val; + struct TALER_AgeCommitment *new; + + GNUNET_assert (orig->num_pub == __builtin_popcount (orig->mask.mask) -1); + GNUNET_assert (orig->num_priv <= orig->num_pub); + + *derived = NULL; + + new = GNUNET_malloc (sizeof(struct TALER_AgeCommitment)); + 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); + } + + *derived = new; + return GNUNET_OK; + +FAIL: + GNUNET_free (new->pub); + GNUNET_free (new->priv); + GNUNET_free (new); + return GNUNET_SYSERR; } -void +/* 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 *commitment) + struct TALER_AgeCommitment **commitment) { - /* TODO oec */ + 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 (mask->mask & 1); /* fist bit must have been set */ + GNUNET_assert (0 <= num_priv); + GNUNET_assert (31 > num_priv); + + new = GNUNET_malloc (sizeof(struct TALER_AgeCommitment)); + 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 (; ipub[i], + sizeof(new->pub[i])); + + *commitment = new; + return GNUNET_OK; + +FAIL: + GNUNET_free (new->pub); + GNUNET_free (new->priv); + GNUNET_free (new); + return GNUNET_SYSERR; }