From 1ad2fb331548adf635e9cff8786b468e54666371 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 6 Jan 2020 14:02:40 +0100 Subject: [PATCH] Fix ECDSA/ECDH key exchange Libgcrypt interprets the private key as little endian, while tweetnacl interprets it as big endian. This caused the key exchange to fail. --- src/util/.gitignore | 1 + src/util/crypto_ecc.c | 109 ++++++------------------------------ src/util/tweetnacl-gnunet.c | 21 ++++++- src/util/tweetnacl-gnunet.h | 4 ++ 4 files changed, 42 insertions(+), 93 deletions(-) diff --git a/src/util/.gitignore b/src/util/.gitignore index 01ebcc834..0495dcf8f 100644 --- a/src/util/.gitignore +++ b/src/util/.gitignore @@ -30,6 +30,7 @@ test_container_multihashmap32 test_container_multipeermap test_crypto_crc test_crypto_ecc_dlog +test_crypto_ecdh_ecdsa test_crypto_ecdh_eddsa test_crypto_ecdhe test_crypto_ecdsa diff --git a/src/util/crypto_ecc.c b/src/util/crypto_ecc.c index bd7c425d4..d4cfaa72c 100644 --- a/src/util/crypto_ecc.c +++ b/src/util/crypto_ecc.c @@ -173,22 +173,8 @@ GNUNET_CRYPTO_ecdsa_key_get_public ( const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, struct GNUNET_CRYPTO_EcdsaPublicKey *pub) { - gcry_sexp_t sexp; - gcry_ctx_t ctx; - gcry_mpi_t q; - BENCHMARK_START (ecdsa_key_get_public); - - sexp = decode_private_ecdsa_key (priv); - GNUNET_assert (NULL != sexp); - GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, sexp, NULL)); - gcry_sexp_release (sexp); - q = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0); - GNUNET_assert (NULL != q); - GNUNET_CRYPTO_mpi_print_unsigned (pub->q_y, sizeof(pub->q_y), q); - gcry_mpi_release (q); - gcry_ctx_release (ctx); - + GNUNET_TWEETNACL_scalarmult_le_ed25519_base (pub->q_y, priv->d); BENCHMARK_END (ecdsa_key_get_public); } @@ -1040,45 +1026,6 @@ GNUNET_CRYPTO_ecdsa_public_key_derive ( } -/** - * Take point from ECDH and convert it to key material. - * - * @param result point from ECDH - * @param ctx ECC context - * @param key_material[out] set to derived key material - * @return #GNUNET_OK on success - */ -static int -point_to_hash (gcry_mpi_point_t result, - gcry_ctx_t ctx, - struct GNUNET_HashCode *key_material) -{ - gcry_mpi_t result_x; - unsigned char xbuf[256 / 8]; - size_t rsize; - - /* finally, convert point to string for hashing */ - result_x = gcry_mpi_new (256); - if (gcry_mpi_ec_get_affine (result_x, NULL, result, ctx)) - { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "get_affine failed", 0); - return GNUNET_SYSERR; - } - - rsize = sizeof(xbuf); - GNUNET_assert (! gcry_mpi_get_flag (result_x, GCRYMPI_FLAG_OPAQUE)); - /* result_x can be negative here, so we do not use 'GNUNET_CRYPTO_mpi_print_unsigned' - as that does not include the sign bit; x should be a 255-bit - value, so with the sign it should fit snugly into the 256-bit - xbuf */ - GNUNET_assert ( - 0 == gcry_mpi_print (GCRYMPI_FMT_STD, xbuf, rsize, &rsize, result_x)); - GNUNET_CRYPTO_hash (xbuf, rsize, key_material); - gcry_mpi_release (result_x); - return GNUNET_OK; -} - - /** * @ingroup crypto * Derive key material from a ECDH public key and a private EdDSA key. @@ -1125,41 +1072,18 @@ GNUNET_CRYPTO_ecdsa_ecdh (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, const struct GNUNET_CRYPTO_EcdhePublicKey *pub, struct GNUNET_HashCode *key_material) { - gcry_mpi_point_t result; - gcry_mpi_point_t q; - gcry_mpi_t d; - gcry_ctx_t ctx; - gcry_sexp_t pub_sexpr; - int ret; + uint8_t p[GNUNET_TWEETNACL_SCALARMULT_BYTES]; + uint8_t d_rev[GNUNET_TWEETNACL_SCALARMULT_BYTES]; BENCHMARK_START (ecdsa_ecdh); - - /* first, extract the q = dP value from the public key */ - if (0 != gcry_sexp_build (&pub_sexpr, - NULL, - "(public-key(ecc(curve " CURVE ")(q %b)))", - (int) sizeof(pub->q_y), - pub->q_y)) - return GNUNET_SYSERR; - GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, pub_sexpr, NULL)); - gcry_sexp_release (pub_sexpr); - q = gcry_mpi_ec_get_point ("q", ctx, 0); - - /* second, extract the d value from our private key */ - GNUNET_CRYPTO_mpi_scan_unsigned (&d, priv->d, sizeof(priv->d)); - - /* then call the 'multiply' function, to compute the product */ - result = gcry_mpi_point_new (0); - gcry_mpi_ec_mul (result, d, q, ctx); - gcry_mpi_point_release (q); - gcry_mpi_release (d); - - /* finally, convert point to string for hashing */ - ret = point_to_hash (result, ctx, key_material); - gcry_mpi_point_release (result); - gcry_ctx_release (ctx); + for (size_t i = 0; i < 32; i++) + d_rev[i] = priv->d[31 - i]; + GNUNET_TWEETNACL_scalarmult_curve25519 (p, d_rev, pub->q_y); + GNUNET_CRYPTO_hash (p, + GNUNET_TWEETNACL_SCALARMULT_BYTES, + key_material); BENCHMARK_END (ecdsa_ecdh); - return ret; + return GNUNET_OK; } @@ -1191,7 +1115,7 @@ GNUNET_CRYPTO_ecdh_eddsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, /** * @ingroup crypto * Derive key material from a ECDSA public key and a private ECDH key. - * Dual to #GNUNET_CRRYPTO_eddsa_ecdh. + * Dual to #GNUNET_CRYPTO_ecdsa_ecdh. * * @param priv private key to use for the ECDH (y) * @param pub public key from ECDSA to use for the ECDH (X=h(x)G) @@ -1203,10 +1127,13 @@ GNUNET_CRYPTO_ecdh_ecdsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, const struct GNUNET_CRYPTO_EcdsaPublicKey *pub, struct GNUNET_HashCode *key_material) { - return GNUNET_CRYPTO_ecdh_eddsa (priv, - (const struct GNUNET_CRYPTO_EddsaPublicKey *) - pub, - key_material); + uint8_t p[GNUNET_TWEETNACL_SCALARMULT_BYTES]; + uint8_t curve25510_pk[GNUNET_TWEETNACL_SIGN_PUBLICBYTES]; + + GNUNET_TWEETNACL_sign_ed25519_pk_to_curve25519 (curve25510_pk, pub->q_y); + GNUNET_TWEETNACL_scalarmult_curve25519 (p, priv->d, curve25510_pk); + GNUNET_CRYPTO_hash (p, GNUNET_TWEETNACL_SCALARMULT_BYTES, key_material); + return GNUNET_OK; } diff --git a/src/util/tweetnacl-gnunet.c b/src/util/tweetnacl-gnunet.c index 1c27730a4..c3471ae66 100644 --- a/src/util/tweetnacl-gnunet.c +++ b/src/util/tweetnacl-gnunet.c @@ -424,8 +424,25 @@ GNUNET_TWEETNACL_sign_pk_from_seed (u8 *pk, const u8 *seed) d[31] &= 127; d[31] |= 64; - scalarbase (p,d); - pack (pk,p); + scalarbase (p, d); + pack (pk, p); +} + +void +GNUNET_TWEETNACL_scalarmult_le_ed25519_base (u8 *pk, const u8 *s) +{ + u8 d[64]; + gf p[4]; + + // Treat s as little endian. + for (u32 i = 0; i < 32; i++) + d[i] = s[31 - i]; + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + scalarbase (p, d); + pack (pk, p); } void diff --git a/src/util/tweetnacl-gnunet.h b/src/util/tweetnacl-gnunet.h index 239166ffc..2b2dc8e63 100644 --- a/src/util/tweetnacl-gnunet.h +++ b/src/util/tweetnacl-gnunet.h @@ -47,4 +47,8 @@ GNUNET_TWEETNACL_sign_detached (uint8_t *sig, const uint8_t *m, uint64_t n, const uint8_t *sk); + +void +GNUNET_TWEETNACL_scalarmult_le_ed25519_base (uint8_t *pk, const uint8_t *s); + #endif -- 2.25.1