From 72fcc98d2361330411b8c6ccce346d5cc3c9e962 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 23 Mar 2021 22:31:31 +0100 Subject: [PATCH] mbedtls_ecp_gen_privkey: create subfunctions for each curve type Put the Montgomery and short Weierstrass implementations of mbedtls_ecp_gen_privkey into their own function which can be tested independently, but will not be part of the public ABI/API. Signed-off-by: Gilles Peskine --- library/ecp.c | 171 +++++++++++++++++++++++------------------ library/ecp_invasive.h | 52 +++++++++++++ 2 files changed, 147 insertions(+), 76 deletions(-) diff --git a/library/ecp.c b/library/ecp.c index 858dd5ed8..506f0cbaf 100644 --- a/library/ecp.c +++ b/library/ecp.c @@ -3057,6 +3057,97 @@ int mbedtls_ecp_check_privkey( const mbedtls_ecp_group *grp, return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); } +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_gen_privkey_mx( size_t n_bits, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + size_t b; + size_t n_bytes = ( n_bits + 7 ) / 8; + + /* [Curve25519] page 5 */ + do { + MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( d, n_bytes, f_rng, p_rng ) ); + } while( mbedtls_mpi_bitlen( d ) == 0); + + /* Make sure the most significant bit is n_bits */ + b = mbedtls_mpi_bitlen( d ) - 1; /* mbedtls_mpi_bitlen is one-based */ + if( b > n_bits ) + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( d, b - n_bits ) ); + else + MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, n_bits, 1 ) ); + + /* Make sure the last two bits are unset for Curve448, three bits for + Curve25519 */ + MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 0, 0 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 1, 0 ) ); + if( n_bits == 254 ) + { + MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 2, 0 ) ); + } + +cleanup: + return( ret ); +} +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_gen_privkey_sw( const mbedtls_mpi *N, size_t n_bits, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + /* SEC1 3.2.1: Generate d such that 1 <= n < N */ + int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + int count = 0; + unsigned cmp = 0; + size_t n_bytes = ( n_bits + 7 ) / 8; + + /* + * Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA) + * when f_rng is a suitably parametrized instance of HMAC_DRBG: + * - use the same byte ordering; + * - keep the leftmost n_bits bits of the generated octet string; + * - try until result is in the desired range. + * This also avoids any bias, which is especially important for ECDSA. + */ + do + { + MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( d, n_bytes, f_rng, p_rng ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( d, 8 * n_bytes - n_bits ) ); + + /* + * Each try has at worst a probability 1/2 of failing (the msb has + * a probability 1/2 of being 0, and then the result will be < N), + * so after 30 tries failure probability is a most 2**(-30). + * + * For most curves, 1 try is enough with overwhelming probability, + * since N starts with a lot of 1s in binary, but some curves + * such as secp224k1 are actually very close to the worst case. + */ + if( ++count > 30 ) + { + ret = MBEDTLS_ERR_ECP_RANDOM_FAILED; + goto cleanup; + } + + ret = mbedtls_mpi_lt_mpi_ct( d, N, &cmp ); + if( ret != 0 ) + { + goto cleanup; + } + } + while( mbedtls_mpi_cmp_int( d, 1 ) < 0 || cmp != 1 ); + +cleanup: + return( ret ); +} +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + /* * Generate a private key */ @@ -3065,94 +3156,22 @@ int mbedtls_ecp_gen_privkey( const mbedtls_ecp_group *grp, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) { - int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; - size_t n_bits; - const mbedtls_mpi *N = NULL; - ECP_VALIDATE_RET( grp != NULL ); ECP_VALIDATE_RET( d != NULL ); ECP_VALIDATE_RET( f_rng != NULL ); - N = &grp->N; - n_bits = grp->nbits; - #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) if( mbedtls_ecp_get_type( grp ) == MBEDTLS_ECP_TYPE_MONTGOMERY ) - { - size_t b; - size_t n_bytes = ( n_bits + 7 ) / 8; - - /* [Curve25519] page 5 */ - do { - MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( d, n_bytes, f_rng, p_rng ) ); - } while( mbedtls_mpi_bitlen( d ) == 0); - - /* Make sure the most significant bit is n_bits */ - b = mbedtls_mpi_bitlen( d ) - 1; /* mbedtls_mpi_bitlen is one-based */ - if( b > n_bits ) - MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( d, b - n_bits ) ); - else - MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, n_bits, 1 ) ); - - /* Make sure the last two bits are unset for Curve448, three bits for - Curve25519 */ - MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 0, 0 ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 1, 0 ) ); - if( n_bits == 254 ) - { - MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( d, 2, 0 ) ); - } - } + return( mbedtls_ecp_gen_privkey_mx( grp->nbits, d, f_rng, p_rng ) ); #endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) if( mbedtls_ecp_get_type( grp ) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS ) - { - /* SEC1 3.2.1: Generate d such that 1 <= n < N */ - int count = 0; - unsigned cmp = 0; - size_t n_bytes = ( n_bits + 7 ) / 8; - - /* - * Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA) - * when f_rng is a suitably parametrized instance of HMAC_DRBG: - * - use the same byte ordering; - * - keep the leftmost n_bits bits of the generated octet string; - * - try until result is in the desired range. - * This also avoids any bias, which is especially important for ECDSA. - */ - do - { - MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( d, n_bytes, f_rng, p_rng ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( d, 8 * n_bytes - n_bits ) ); - - /* - * Each try has at worst a probability 1/2 of failing (the msb has - * a probability 1/2 of being 0, and then the result will be < N), - * so after 30 tries failure probability is a most 2**(-30). - * - * For most curves, 1 try is enough with overwhelming probability, - * since N starts with a lot of 1s in binary, but some curves - * such as secp224k1 are actually very close to the worst case. - */ - if( ++count > 30 ) - { - ret = MBEDTLS_ERR_ECP_RANDOM_FAILED; - goto cleanup; - } - - ret = mbedtls_mpi_lt_mpi_ct( d, N, &cmp ); - if( ret != 0 ) - { - goto cleanup; - } - } - while( mbedtls_mpi_cmp_int( d, 1 ) < 0 || cmp != 1 ); - } + return( mbedtls_ecp_gen_privkey_sw( &grp->N, grp->nbits, d, + f_rng, p_rng ) ); #endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ -cleanup: - return( ret ); + return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); } /* diff --git a/library/ecp_invasive.h b/library/ecp_invasive.h index b5239676f..2895b19e1 100644 --- a/library/ecp_invasive.h +++ b/library/ecp_invasive.h @@ -27,6 +27,7 @@ #define MBEDTLS_ECP_INVASIVE_H #include "common.h" +#include "mbedtls/bignum.h" #include "mbedtls/ecp.h" #if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_ECP_C) @@ -46,6 +47,57 @@ void mbedtls_ecp_fix_negative( mbedtls_mpi *N, signed char c, size_t bits ); #endif +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) +/** Generate a private key on a Montgomery curve (Curve25519 or Curve448). + * + * This function implements key generation for the set of secret keys + * specified in [Curve25519] p. 5 and in [Curve448]. The resulting value + * has the lower bits masked but is not necessarily canonical. + * + * \note - [Curve25519] http://cr.yp.to/ecdh/curve25519-20060209.pdf + * - [RFC7748] https://tools.ietf.org/html/rfc7748 + * + * \p n_bits The position of the high-order bit of the key to generate. + * This is the bit-size of the key minus 1: + * 254 for Curve25519 or 447 for Curve448. + * \param d The randomly generated key. This is a number of size + * exactly \p n_bits + 1 bits, with the least significant bits + * masked as specified in [Curve25519] and in [RFC7748] §5. + * \param f_rng The RNG function. + * \param p_rng The RNG context to be passed to \p f_rng. + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_ECP_xxx or MBEDTLS_ERR_MPI_xxx on failure. + */ +int mbedtls_ecp_gen_privkey_mx( size_t n_bits, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +/** Generate a private key on a short Weierstrass curve. + * + * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA) + * when the RNG is a suitably parametrized instance of HMAC_DRBG. + * + * \p N The upper bound of the range. + * \p n_bits The size of \p N in bits. This value must be correct, + * otherwise the result is unpredictable. + * \param d A random number, uniformly generated in the range [1, N-1]. + * \param f_rng The RNG function. + * \param p_rng The RNG context to be passed to \p f_rng. + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_ECP_xxx or MBEDTLS_ERR_MPI_xxx on failure. + */ +int mbedtls_ecp_gen_privkey_sw( const mbedtls_mpi *N, size_t n_bits, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + #endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_ECP_C */ #endif /* MBEDTLS_ECP_INVASIVE_H */