diff --git a/crypto/curve25519/curve25519.c b/crypto/curve25519/curve25519.c index b87d1fe425..532baff8df 100644 --- a/crypto/curve25519/curve25519.c +++ b/crypto/curve25519/curve25519.c @@ -31,6 +31,25 @@ #include "../internal.h" #include "../fipsmodule/cpucap/internal.h" +// X25519 [1] and Ed25519 [2] is an ECDHE protocol and signature scheme, +// respectively. This file contains an implementation of both using two +// different backends: +// 1) One backend is a pure C backend that should work on any platform. +// 2) The other backend is machine-optimized using s2n-bignum [3] as backend. +// +// [1]: https://datatracker.ietf.org/doc/html/rfc7748 +// [2]: https://datatracker.ietf.org/doc/html/rfc8032 +// [3]: https://github.com/awslabs/s2n-bignum +// +// "Clamping": +// Both X25519 and Ed25519 contain "clamping" steps; bit-twiddling, masking or +// setting specific bits. Generally, the bit-twiddling is to avoid common +// implementation errors and weak instances. Details can be found through the +// following two references: +// * https://mailarchive.ietf.org/arch/msg/cfrg/pt2bt3fGQbNF8qdEcorp-rJSJrc/ +// * https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about + + // If (1) x86_64 or aarch64, (2) linux or apple, and (3) OPENSSL_NO_ASM is not // set, s2n-bignum path is capable. #if ((defined(OPENSSL_X86_64) && \ @@ -51,6 +70,11 @@ OPENSSL_INLINE int x25519_s2n_bignum_capable(void) { #endif } +// Return 0 until ED25519 lands in s2n-bignum +OPENSSL_INLINE int ed25519_s2n_bignum_capable(void) { + return 0; +} + // Stub functions if implementations are not compiled. // These functions have to abort, otherwise we risk applications assuming they // did work without actually doing anything. @@ -234,28 +258,53 @@ static void x25519_s2n_bignum_public_from_private( #endif } +// Stub function until ED25519 lands in s2n-bignum +static void ed25519_public_key_from_hashed_seed_s2n_bignum( + uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN], + uint8_t az[SHA512_DIGEST_LENGTH]) { + abort(); +} -void ED25519_keypair_from_seed(uint8_t out_public_key[32], - uint8_t out_private_key[64], - const uint8_t seed[ED25519_SEED_LEN]) { +void ED25519_keypair_from_seed(uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN], + uint8_t out_private_key[ED25519_PRIVATE_KEY_LEN], + const uint8_t seed[ED25519_SEED_LEN]) { + + // Step: rfc8032 5.1.5.1 + // Compute SHA512(seed). uint8_t az[SHA512_DIGEST_LENGTH]; SHA512(seed, ED25519_SEED_LEN, az); - az[0] &= 248; - az[31] &= 127; - az[31] |= 64; + // Step: rfc8032 5.1.5.2 + az[0] &= 248; // 11111000_2 + az[31] &= 127; // 01111111_2 + az[31] |= 64; // 01000000_2 - ge_p3 A; - x25519_ge_scalarmult_base(&A, az); - ge_p3_tobytes(out_public_key, &A); + // Step: rfc8032 5.1.5.[3,4] + // Compute [az]B and encode public key to a 32 byte octet. + if (ed25519_s2n_bignum_capable() == 1) { + ed25519_public_key_from_hashed_seed_s2n_bignum(out_public_key, az); + } else { + ed25519_public_key_from_hashed_seed_nohw(out_public_key, az); + } + // Encoded public key is a suffix in the private key. Avoids having to + // generate the public key from the private key when signing. + OPENSSL_STATIC_ASSERT(ED25519_PRIVATE_KEY_LEN == (ED25519_SEED_LEN + ED25519_PUBLIC_KEY_LEN), ed25519_parameter_length_mismatch) OPENSSL_memcpy(out_private_key, seed, ED25519_SEED_LEN); - OPENSSL_memcpy(out_private_key + ED25519_SEED_LEN, out_public_key, 32); + OPENSSL_memcpy(out_private_key + ED25519_SEED_LEN, out_public_key, + ED25519_PUBLIC_KEY_LEN); } -void ED25519_keypair(uint8_t out_public_key[32], uint8_t out_private_key[64]) { +void ED25519_keypair(uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN], + uint8_t out_private_key[ED25519_PRIVATE_KEY_LEN]) { + + // Ed25519 key generation: rfc8032 5.1.5 + // Private key is 32 octets of random data. uint8_t seed[ED25519_SEED_LEN]; RAND_bytes(seed, ED25519_SEED_LEN); + + // Public key generation is handled in a separate function. See function + // description why this is useful. ED25519_keypair_from_seed(out_public_key, out_private_key, seed); OPENSSL_cleanse(seed, ED25519_SEED_LEN); } diff --git a/crypto/curve25519/curve25519_nohw.c b/crypto/curve25519/curve25519_nohw.c index 3776c49c51..7e2d1545be 100644 --- a/crypto/curve25519/curve25519_nohw.c +++ b/crypto/curve25519/curve25519_nohw.c @@ -1968,3 +1968,10 @@ void x25519_public_from_private_nohw(uint8_t out_public_value[32], fe_tobytes(out_public_value, &zminusy_inv); CONSTTIME_DECLASSIFY(out_public_value, 32); } + +void ed25519_public_key_from_hashed_seed_nohw(uint8_t out_public_key[32], + uint8_t az[SHA512_DIGEST_LENGTH]) { + ge_p3 A; + x25519_ge_scalarmult_base(&A, az); + ge_p3_tobytes(out_public_key, &A); +} diff --git a/crypto/curve25519/internal.h b/crypto/curve25519/internal.h index 3458c3eb3d..3e9da57b36 100644 --- a/crypto/curve25519/internal.h +++ b/crypto/curve25519/internal.h @@ -20,6 +20,7 @@ extern "C" { #endif #include +#include #include "../internal.h" @@ -114,6 +115,9 @@ void x25519_scalar_mult_generic_nohw(uint8_t out[32], const uint8_t point[32]); void x25519_public_from_private_nohw(uint8_t out_public_value[32], const uint8_t private_key[32]); +void ed25519_public_key_from_hashed_seed_nohw( + uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN], + uint8_t az[SHA512_DIGEST_LENGTH]); // Port to internal linkage in curve25519_nohw.c when adding implementation // from s2n-bignum ed25519