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 <Gilles.Peskine@arm.com>
diff --git a/library/ecp.c b/library/ecp.c
index 2b6bcc8..b0fad0b 100644
--- a/library/ecp.c
+++ b/library/ecp.c
@@ -3040,6 +3040,97 @@
     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
  */
@@ -3048,94 +3139,22 @@
                      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 b523967..2895b19 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 */