Merge pull request #6450 from gilles-peskine-arm/bignum-core-fill_random

Bignum core: fill_random
diff --git a/library/bignum.c b/library/bignum.c
index d33f07c..521787d 100644
--- a/library/bignum.c
+++ b/library/bignum.c
@@ -1931,39 +1931,11 @@
     return( ret );
 }
 
-/* Fill X with n_bytes random bytes.
- * X must already have room for those bytes.
- * The ordering of the bytes returned from the RNG is suitable for
- * deterministic ECDSA (see RFC 6979 §3.3 and mbedtls_mpi_random()).
- * The size and sign of X are unchanged.
- * n_bytes must not be 0.
- */
-static int mpi_fill_random_internal(
-    mbedtls_mpi *X, size_t n_bytes,
-    int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
-{
-    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
-    const size_t limbs = CHARS_TO_LIMBS( n_bytes );
-    const size_t overhead = ( limbs * ciL ) - n_bytes;
-
-    if( X->n < limbs )
-        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
-
-    memset( X->p, 0, overhead );
-    memset( (unsigned char *) X->p + limbs * ciL, 0, ( X->n - limbs ) * ciL );
-    MBEDTLS_MPI_CHK( f_rng( p_rng, (unsigned char *) X->p + overhead, n_bytes ) );
-    mbedtls_mpi_core_bigendian_to_host( X->p, limbs );
-
-cleanup:
-    return( ret );
-}
-
 /*
  * Fill X with size bytes of random.
- *
- * Use a temporary bytes representation to make sure the result is the same
- * regardless of the platform endianness (useful when f_rng is actually
- * deterministic, eg for tests).
+ * The bytes returned from the RNG are used in a specific order which
+ * is suitable for deterministic ECDSA (see the specification of
+ * mbedtls_mpi_random() and the implementation in mbedtls_mpi_fill_random()).
  */
 int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size,
                      int (*f_rng)(void *, unsigned char *, size_t),
@@ -1980,7 +1952,7 @@
     if( size == 0 )
         return( 0 );
 
-    ret = mpi_fill_random_internal( X, size, f_rng, p_rng );
+    ret = mbedtls_mpi_core_fill_random( X->p, X->n, size, f_rng, p_rng );
 
 cleanup:
     return( ret );
@@ -2042,7 +2014,9 @@
      */
     do
     {
-        MBEDTLS_MPI_CHK( mpi_fill_random_internal( X, n_bytes, f_rng, p_rng ) );
+        MBEDTLS_MPI_CHK( mbedtls_mpi_core_fill_random( X->p, X->n,
+                                                       n_bytes,
+                                                       f_rng, p_rng ) );
         MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( X, 8 * n_bytes - n_bits ) );
 
         if( --count == 0 )
diff --git a/library/bignum_core.c b/library/bignum_core.c
index b3bb3bc..b990a8d 100644
--- a/library/bignum_core.c
+++ b/library/bignum_core.c
@@ -553,4 +553,32 @@
     }
 }
 
+
+/* Fill X with n_bytes random bytes.
+ * X must already have room for those bytes.
+ * The ordering of the bytes returned from the RNG is suitable for
+ * deterministic ECDSA (see RFC 6979 §3.3 and the specification of
+ * mbedtls_mpi_core_random()).
+ */
+int mbedtls_mpi_core_fill_random(
+    mbedtls_mpi_uint *X, size_t X_limbs,
+    size_t n_bytes,
+    int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    const size_t limbs = CHARS_TO_LIMBS( n_bytes );
+    const size_t overhead = ( limbs * ciL ) - n_bytes;
+
+    if( X_limbs < limbs )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+    memset( X, 0, overhead );
+    memset( (unsigned char *) X + limbs * ciL, 0, ( X_limbs - limbs ) * ciL );
+    MBEDTLS_MPI_CHK( f_rng( p_rng, (unsigned char *) X + overhead, n_bytes ) );
+    mbedtls_mpi_core_bigendian_to_host( X, limbs );
+
+cleanup:
+    return( ret );
+}
+
 #endif /* MBEDTLS_BIGNUM_C */
diff --git a/library/bignum_core.h b/library/bignum_core.h
index ccccebb..624eaf4 100644
--- a/library/bignum_core.h
+++ b/library/bignum_core.h
@@ -470,4 +470,28 @@
                                             size_t count,
                                             size_t index );
 
+/**
+ * \brief          Fill an integer with a number of random bytes.
+ *
+ * \param X        The destination MPI.
+ * \param X_limbs  The number of limbs of \p X.
+ * \param bytes    The number of random bytes to generate.
+ * \param f_rng    The RNG function to use. This must not be \c NULL.
+ * \param p_rng    The RNG parameter to be passed to \p f_rng. This may be
+ *                 \c NULL if \p f_rng doesn't need a context argument.
+ *
+ * \return         \c 0 if successful.
+ * \return         #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p X does not have
+ *                 enough room for \p bytes bytes.
+ * \return         A negative error code on RNG failure.
+ *
+ * \note           The bytes obtained from the RNG are interpreted
+ *                 as a big-endian representation of an MPI; this can
+ *                 be relevant in applications like deterministic ECDSA.
+ */
+int mbedtls_mpi_core_fill_random( mbedtls_mpi_uint *X, size_t X_limbs,
+                                  size_t bytes,
+                                  int (*f_rng)(void *, unsigned char *, size_t),
+                                  void *p_rng );
+
 #endif /* MBEDTLS_BIGNUM_CORE_H */
diff --git a/tests/suites/test_suite_bignum.misc.data b/tests/suites/test_suite_bignum.misc.data
index 78afcb6..29ba4ab 100644
--- a/tests/suites/test_suite_bignum.misc.data
+++ b/tests/suites/test_suite_bignum.misc.data
@@ -1766,16 +1766,16 @@
 mpi_random_many:1:"0c":1000
 
 MPI random in range: 1..255
-mpi_random_many:1:"ff":100
+mpi_random_many:1:"ff":200
 
 MPI random in range: 1..256
-mpi_random_many:1:"0100":100
+mpi_random_many:1:"0100":200
 
 MPI random in range: 1..257
-mpi_random_many:1:"0101":100
+mpi_random_many:1:"0101":200
 
 MPI random in range: 1..272
-mpi_random_many:1:"0110":100
+mpi_random_many:1:"0110":200
 
 MPI random in range: 1..2^64-1
 mpi_random_many:1:"ffffffffffffffff":100
diff --git a/tests/suites/test_suite_bignum_core.function b/tests/suites/test_suite_bignum_core.function
index d5d58d8..745a8af 100644
--- a/tests/suites/test_suite_bignum_core.function
+++ b/tests/suites/test_suite_bignum_core.function
@@ -992,3 +992,56 @@
     mbedtls_free(dest);
 }
 /* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_core_fill_random( int wanted_bytes_arg, int extra_rng_bytes,
+                           int extra_limbs, int before, int expected_ret )
+{
+    size_t wanted_bytes = wanted_bytes_arg;
+    mbedtls_mpi_uint *X = NULL;
+    size_t X_limbs = CHARS_TO_LIMBS( wanted_bytes ) + extra_limbs;
+    size_t rng_bytes = wanted_bytes + extra_rng_bytes;
+    unsigned char *rnd_data = NULL;
+    mbedtls_test_rnd_buf_info rnd_info = {NULL, rng_bytes, NULL, NULL};
+    int ret;
+
+    /* Prepare an RNG with known output, limited to rng_bytes. */
+    ASSERT_ALLOC( rnd_data, rng_bytes );
+    TEST_EQUAL( 0, mbedtls_test_rnd_std_rand( NULL, rnd_data, rng_bytes ) );
+    rnd_info.buf = rnd_data;
+
+    /* Allocate an MPI with room for wanted_bytes plus extra_limbs.
+     * extra_limbs may be negative but the total limb count must be positive.
+     * Fill the MPI with the byte value in before. */
+    TEST_LE_U( 1, X_limbs );
+    ASSERT_ALLOC( X, X_limbs );
+    memset( X, before, X_limbs * sizeof( *X ) );
+
+    ret = mbedtls_mpi_core_fill_random( X, X_limbs, wanted_bytes,
+                                        mbedtls_test_rnd_buffer_rand,
+                                        &rnd_info );
+    TEST_EQUAL( expected_ret, ret );
+
+    if( expected_ret == 0 )
+    {
+        /* mbedtls_mpi_core_fill_random is documented to use bytes from the
+         * RNG as a big-endian representation of the number. We used an RNG
+         * with known output, so check that the output contains the
+         * expected value. Bytes above wanted_bytes must be zero. */
+        for( size_t i = 0; i < wanted_bytes; i++ )
+        {
+            mbedtls_test_set_step( i );
+            TEST_EQUAL( GET_BYTE( X, i ), rnd_data[wanted_bytes - 1 - i] );
+        }
+        for( size_t i = wanted_bytes; i < X_limbs * ciL; i++ )
+        {
+            mbedtls_test_set_step( i );
+            TEST_EQUAL( GET_BYTE( X, i ), 0 );
+        }
+    }
+
+exit:
+    mbedtls_free( rnd_data );
+    mbedtls_free( X );
+}
+/* END_CASE */
diff --git a/tests/suites/test_suite_bignum_core.misc.data b/tests/suites/test_suite_bignum_core.misc.data
index a8fe9ab..743bb3e 100644
--- a/tests/suites/test_suite_bignum_core.misc.data
+++ b/tests/suites/test_suite_bignum_core.misc.data
@@ -364,3 +364,69 @@
 
 mbedtls_mpi_core_get_mont_r2_unsafe #11
 mpi_core_get_mont_r2_unsafe:"d1cece570f2f991013f26dd5b03c4c5b65f97be5905f36cb4664f2c78ff80aa8135a4aaf57ccb8a0aca2f394909a74cef1ef6758a64d11e2c149c393659d124bfc94196f0ce88f7d7d567efa5a649e2deefaa6e10fdc3deac60d606bf63fc540ac95294347031aefd73d6a9ee10188aaeb7a90d920894553cb196881691cadc51808715a07e8b24fcb1a63df047c7cdf084dd177ba368c806f3d51ddb5d3898c863e687ecaf7d649a57a46264a582f94d3c8f2edaf59f77a7f6bdaf83c991e8f06abe220ec8507386fce8c3da84c6c3903ab8f3ad4630a204196a7dbcbd9bcca4e40ec5cc5c09938d49f5e1e6181db8896f33bb12e6ef73f12ec5c5ea7a8a337":"12d7243d92ebc8338221f6dcec8ad8a2ec64c10a98339c8721beb1cb79e629253a7aa35e25d5421e6c2b43ddc4310cf4443875c070a7a5a5cc2c4c3eefa8a133af2e477fb7bb5b5058c6120946a7f9f08f2fab51e2f243b9ba206d2bfd62e4ef647dda49100d7004794f28172be2d715905fbd2e9ab8588c774523c0e096b49b6855a10e5ce0d8498370949a29d71d293788bf10a71e2447d4b2f11959a72f7290e2950772d14c83f15532468745fa58a83fca8883b0b6169a27ec0cf922c4f39d283bb20fca5ff1de01d9c66b8a710108b951af634d56c843d9505bf2edd5a7b8f0b72a5c95672151e60075a78084e83fbe284617a90c74c8335cce38bb012e":"12d7243d92ebc8338221f6dcec8ad8a2ec64c10a98339c8721beb1cb79e629253a7aa35e25d5421e6c2b43ddc4310cf4443875c070a7a5a5cc2c4c3eefa8a133af2e477fb7bb5b5058c6120946a7f9f08f2fab51e2f243b9ba206d2bfd62e4ef647dda49100d7004794f28172be2d715905fbd2e9ab8588c774523c0e096b49b6855a10e5ce0d8498370949a29d71d293788bf10a71e2447d4b2f11959a72f7290e2950772d14c83f15532468745fa58a83fca8883b0b6169a27ec0cf922c4f39d283bb20fca5ff1de01d9c66b8a710108b951af634d56c843d9505bf2edd5a7b8f0b72a5c95672151e60075a78084e83fbe284617a90c74c8335cce38bb012e"
+
+Fill random core: 0 bytes
+mpi_core_fill_random:0:0:1:0:0
+
+Fill random core: 1 byte, RNG stops at 0
+mpi_core_fill_random:1:-1:0:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED
+
+Fill random core: 1 byte, RNG just sufficient
+mpi_core_fill_random:1:0:0:0:0
+
+Fill random core: 1 byte, RNG not exhausted
+mpi_core_fill_random:1:1:0:0:0
+
+Fill random core: 1 byte, prior content nonzero
+mpi_core_fill_random:1:0:0:0xba:0
+
+Fill random core: 1 byte, 1 extra limb
+mpi_core_fill_random:1:0:1:0:0
+
+Fill random core: 1 byte, 1 extra limb, prior content nonzero
+mpi_core_fill_random:1:0:1:0xba:0
+
+Fill random core: 8 bytes, RNG stops before
+mpi_core_fill_random:8:-1:0:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED
+
+Fill random core: 8 bytes, RNG just sufficient
+mpi_core_fill_random:8:0:0:0:0
+
+Fill random core: 8 bytes, RNG not exhausted
+mpi_core_fill_random:8:1:0:0:0
+
+Fill random core: 8 bytes, prior content nonzero
+mpi_core_fill_random:8:0:0:0xba:0
+
+Fill random core: 8 bytes, 1 extra limb
+mpi_core_fill_random:8:0:1:0:0
+
+Fill random core: 8 bytes, 1 extra limb, prior content nonzero
+mpi_core_fill_random:8:0:1:0xba:0
+
+Fill random core: 9 bytes, 1 missing limb
+mpi_core_fill_random:9:0:-1:0:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+Fill random core: 42 bytes, RNG stops before
+mpi_core_fill_random:42:-1:0:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED
+
+Fill random core: 42 bytes, RNG just sufficient
+mpi_core_fill_random:42:0:0:0:0
+
+Fill random core: 42 bytes, RNG not exhausted
+mpi_core_fill_random:42:1:0:0:0
+
+Fill random core: 42 bytes, prior content nonzero
+mpi_core_fill_random:42:0:0:0xba:0
+
+Fill random core: 42 bytes, 1 extra limb
+mpi_core_fill_random:42:0:1:0:0
+
+Fill random core: 42 bytes, 1 extra limb, prior content nonzero
+mpi_core_fill_random:42:0:1:0xba:0
+
+Fill random core: 42 bytes, 1 missing limb
+mpi_core_fill_random:42:0:-1:0:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+Fill random core: 42 bytes, 5 missing limbs
+mpi_core_fill_random:42:0:-5:0:MBEDTLS_ERR_MPI_BAD_INPUT_DATA