Merge pull request #6747 from gilles-peskine-arm/bignum-mod-random
Bignum mod random
diff --git a/library/bignum_mod.c b/library/bignum_mod.c
index 31e18e7..c9efb33 100644
--- a/library/bignum_mod.c
+++ b/library/bignum_mod.c
@@ -303,6 +303,17 @@
/* BEGIN MERGE SLOT 6 */
+int mbedtls_mpi_mod_random( mbedtls_mpi_mod_residue *X,
+ mbedtls_mpi_uint min,
+ const mbedtls_mpi_mod_modulus *N,
+ int (*f_rng)(void *, unsigned char *, size_t),
+ void *p_rng )
+{
+ if( X->limbs != N->limbs )
+ return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+ return( mbedtls_mpi_mod_raw_random( X->p, min, N, f_rng, p_rng ) );
+}
+
/* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */
@@ -326,8 +337,7 @@
r->limbs = m->limbs;
- if( m->int_rep == MBEDTLS_MPI_MOD_REP_MONTGOMERY )
- ret = mbedtls_mpi_mod_raw_to_mont_rep( r->p, m );
+ ret = mbedtls_mpi_mod_raw_canonical_to_modulus_rep( r->p, m );
cleanup:
return ( ret );
diff --git a/library/bignum_mod.h b/library/bignum_mod.h
index 95aaacc..2ee219f 100644
--- a/library/bignum_mod.h
+++ b/library/bignum_mod.h
@@ -87,12 +87,23 @@
#include "mbedtls/bignum.h"
#endif
-/* Skip 1 as it is slightly easier to accidentally pass to functions. */
+/** How residues associated with a modulus are represented.
+ *
+ * This also determines which fields of the modulus structure are valid and
+ * what their contents are (see #mbedtls_mpi_mod_modulus).
+ */
typedef enum
{
+ /** Representation not chosen (makes the modulus structure invalid). */
MBEDTLS_MPI_MOD_REP_INVALID = 0,
+ /* Skip 1 as it is slightly easier to accidentally pass to functions. */
+ /** Montgomery representation. */
MBEDTLS_MPI_MOD_REP_MONTGOMERY = 2,
- MBEDTLS_MPI_MOD_REP_OPT_RED
+ /** TODO: document this.
+ *
+ * Residues are in canonical representation.
+ */
+ MBEDTLS_MPI_MOD_REP_OPT_RED,
} mbedtls_mpi_mod_rep_selector;
/* Make mbedtls_mpi_mod_rep_selector and mbedtls_mpi_mod_ext_rep disjoint to
@@ -124,7 +135,9 @@
mbedtls_mpi_mod_rep_selector int_rep; // selector to signal the active member of the union
union rep
{
+ /* if int_rep == #MBEDTLS_MPI_MOD_REP_MONTGOMERY */
mbedtls_mpi_mont_struct mont;
+ /* if int_rep == #MBEDTLS_MPI_MOD_REP_OPT_RED */
mbedtls_mpi_opt_red_struct ored;
} rep;
} mbedtls_mpi_mod_modulus;
@@ -319,6 +332,39 @@
/* BEGIN MERGE SLOT 6 */
+/** Generate a random number uniformly in a range.
+ *
+ * This function generates a random number between \p min inclusive and
+ * \p N exclusive.
+ *
+ * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA)
+ * when the RNG is a suitably parametrized instance of HMAC_DRBG
+ * and \p min is \c 1.
+ *
+ * \note There are `N - min` possible outputs. The lower bound
+ * \p min can be reached, but the upper bound \p N cannot.
+ *
+ * \param X The destination residue.
+ * \param min The minimum value to return. It must be strictly smaller
+ * than \b N.
+ * \param N The modulus.
+ * This is the upper bound of the output range, exclusive.
+ * \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.
+ *
+ * \return \c 0 if successful.
+ * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was
+ * unable to find a suitable value within a limited number
+ * of attempts. This has a negligible probability if \p N
+ * is significantly larger than \p min, which is the case
+ * for all usual cryptographic applications.
+ */
+int mbedtls_mpi_mod_random( mbedtls_mpi_mod_residue *X,
+ mbedtls_mpi_uint min,
+ const mbedtls_mpi_mod_modulus *N,
+ int (*f_rng)(void *, unsigned char *, size_t),
+ void *p_rng );
+
/* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */
diff --git a/library/bignum_mod_raw.c b/library/bignum_mod_raw.c
index 5950ff6..18599c3 100644
--- a/library/bignum_mod_raw.c
+++ b/library/bignum_mod_raw.c
@@ -186,6 +186,48 @@
/* BEGIN MERGE SLOT 6 */
+int mbedtls_mpi_mod_raw_canonical_to_modulus_rep(
+ mbedtls_mpi_uint *X,
+ const mbedtls_mpi_mod_modulus *N )
+{
+ switch( N->int_rep )
+ {
+ case MBEDTLS_MPI_MOD_REP_MONTGOMERY:
+ return( mbedtls_mpi_mod_raw_to_mont_rep( X, N ) );
+ case MBEDTLS_MPI_MOD_REP_OPT_RED:
+ return( 0 );
+ default:
+ return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+ }
+}
+
+int mbedtls_mpi_mod_raw_modulus_to_canonical_rep(
+ mbedtls_mpi_uint *X,
+ const mbedtls_mpi_mod_modulus *N )
+{
+ switch( N->int_rep )
+ {
+ case MBEDTLS_MPI_MOD_REP_MONTGOMERY:
+ return( mbedtls_mpi_mod_raw_from_mont_rep( X, N ) );
+ case MBEDTLS_MPI_MOD_REP_OPT_RED:
+ return( 0 );
+ default:
+ return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+ }
+}
+
+int mbedtls_mpi_mod_raw_random( mbedtls_mpi_uint *X,
+ mbedtls_mpi_uint min,
+ const mbedtls_mpi_mod_modulus *N,
+ int (*f_rng)(void *, unsigned char *, size_t),
+ void *p_rng )
+{
+ int ret = mbedtls_mpi_core_random( X, min, N->p, N->limbs, f_rng, p_rng );
+ if( ret != 0 )
+ return( ret );
+ return( mbedtls_mpi_mod_raw_canonical_to_modulus_rep( X, N ) );
+}
+
/* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */
diff --git a/library/bignum_mod_raw.h b/library/bignum_mod_raw.h
index 0fac6f8..ea3207f 100644
--- a/library/bignum_mod_raw.h
+++ b/library/bignum_mod_raw.h
@@ -336,6 +336,74 @@
/* BEGIN MERGE SLOT 6 */
+/** Convert an MPI from canonical representation (little-endian limb array)
+ * to the representation associated with the modulus.
+ *
+ * \param[in,out] X The limb array to convert.
+ * It must have as many limbs as \p N.
+ * It is converted in place.
+ * If this function returns an error, the content of \p X
+ * is unspecified.
+ * \param[in] N The modulus structure.
+ *
+ *\ return \c 0 if successful.
+ * Otherwise an \c MBEDTLS_ERR_MPI_xxx error code.
+ */
+int mbedtls_mpi_mod_raw_canonical_to_modulus_rep(
+ mbedtls_mpi_uint *X,
+ const mbedtls_mpi_mod_modulus *N );
+
+/** Convert an MPI from the representation associated with the modulus
+ * to canonical representation (little-endian limb array).
+ *
+ * \param[in,out] X The limb array to convert.
+ * It must have as many limbs as \p N.
+ * It is converted in place.
+ * If this function returns an error, the content of \p X
+ * is unspecified.
+ * \param[in] N The modulus structure.
+ *
+ *\ return \c 0 if successful.
+ * Otherwise an \c MBEDTLS_ERR_MPI_xxx error code.
+ */
+int mbedtls_mpi_mod_raw_modulus_to_canonical_rep(
+ mbedtls_mpi_uint *X,
+ const mbedtls_mpi_mod_modulus *N );
+
+/** Generate a random number uniformly in a range.
+ *
+ * This function generates a random number between \p min inclusive and
+ * \p N exclusive.
+ *
+ * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA)
+ * when the RNG is a suitably parametrized instance of HMAC_DRBG
+ * and \p min is \c 1.
+ *
+ * \note There are `N - min` possible outputs. The lower bound
+ * \p min can be reached, but the upper bound \p N cannot.
+ *
+ * \param X The destination MPI, in canonical representation modulo \p N.
+ * It must not be aliased with \p N or otherwise overlap it.
+ * \param min The minimum value to return. It must be strictly smaller
+ * than \b N.
+ * \param N The modulus.
+ * This is the upper bound of the output range, exclusive.
+ * \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.
+ *
+ * \return \c 0 if successful.
+ * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was
+ * unable to find a suitable value within a limited number
+ * of attempts. This has a negligible probability if \p N
+ * is significantly larger than \p min, which is the case
+ * for all usual cryptographic applications.
+ */
+int mbedtls_mpi_mod_raw_random( mbedtls_mpi_uint *X,
+ mbedtls_mpi_uint min,
+ const mbedtls_mpi_mod_modulus *N,
+ int (*f_rng)(void *, unsigned char *, size_t),
+ void *p_rng );
+
/* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */
diff --git a/scripts/mbedtls_dev/bignum_common.py b/scripts/mbedtls_dev/bignum_common.py
index c4efabf..2422175 100644
--- a/scripts/mbedtls_dev/bignum_common.py
+++ b/scripts/mbedtls_dev/bignum_common.py
@@ -15,6 +15,7 @@
# limitations under the License.
from abc import abstractmethod
+import enum
from typing import Iterator, List, Tuple, TypeVar, Any
from itertools import chain
@@ -53,7 +54,7 @@
return 0
return int(val, 16)
-def quote_str(val) -> str:
+def quote_str(val: str) -> str:
return "\"{}\"".format(val)
def bound_mpi(val: int, bits_in_limb: int) -> int:
@@ -139,7 +140,7 @@
def hex_digits(self) -> int:
return 2 * (self.limbs * self.bits_in_limb // 8)
- def format_arg(self, val) -> str:
+ def format_arg(self, val: str) -> str:
if self.input_style not in self.input_styles:
raise ValueError("Unknown input style!")
if self.input_style == "variable":
@@ -147,7 +148,7 @@
else:
return val.zfill(self.hex_digits)
- def format_result(self, res) -> str:
+ def format_result(self, res: int) -> str:
res_str = '{:x}'.format(res)
return quote_str(self.format_arg(res_str))
@@ -245,6 +246,23 @@
)
+class ModulusRepresentation(enum.Enum):
+ """Representation selector of a modulus."""
+ # Numerical values aligned with the type mbedtls_mpi_mod_rep_selector
+ INVALID = 0
+ MONTGOMERY = 2
+ OPT_RED = 3
+
+ def symbol(self) -> str:
+ """The C symbol for this representation selector."""
+ return 'MBEDTLS_MPI_MOD_REP_' + self.name
+
+ @classmethod
+ def supported_representations(cls) -> List['ModulusRepresentation']:
+ """Return all representations that are supported in positive test cases."""
+ return [cls.MONTGOMERY, cls.OPT_RED]
+
+
class ModOperationCommon(OperationCommon):
#pylint: disable=abstract-method
"""Target for bignum mod_raw test case generation."""
@@ -266,6 +284,17 @@
def from_montgomery(self, val: int) -> int:
return (val * self.r_inv) % self.int_n
+ def convert_from_canonical(self, canonical: int,
+ rep: ModulusRepresentation) -> int:
+ """Convert values from canonical representation to the given representation."""
+ if rep is ModulusRepresentation.MONTGOMERY:
+ return self.to_montgomery(canonical)
+ elif rep is ModulusRepresentation.OPT_RED:
+ return canonical
+ else:
+ raise ValueError('Modulus representation not supported: {}'
+ .format(rep.name))
+
@property
def boundary(self) -> int:
return self.int_n
@@ -282,6 +311,9 @@
def arg_n(self) -> str:
return self.format_arg(self.val_n)
+ def format_arg(self, val: str) -> str:
+ return super().format_arg(val).zfill(self.hex_digits)
+
def arguments(self) -> List[str]:
return [quote_str(self.arg_n)] + super().arguments()
diff --git a/scripts/mbedtls_dev/bignum_mod_raw.py b/scripts/mbedtls_dev/bignum_mod_raw.py
index 09bbbee..f9d9899 100644
--- a/scripts/mbedtls_dev/bignum_mod_raw.py
+++ b/scripts/mbedtls_dev/bignum_mod_raw.py
@@ -14,8 +14,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from typing import Dict, List
+from typing import Iterator, List
+from . import test_case
from . import test_data_generation
from . import bignum_common
from .bignum_data import ONLY_PRIME_MODULI
@@ -116,6 +117,88 @@
# BEGIN MERGE SLOT 6
+class BignumModRawConvertRep(bignum_common.ModOperationCommon,
+ BignumModRawTarget):
+ # This is an abstract class, it's ok to have unimplemented methods.
+ #pylint: disable=abstract-method
+ """Test cases for representation conversion."""
+ symbol = ""
+ input_style = "arch_split"
+ arity = 1
+ rep = bignum_common.ModulusRepresentation.INVALID
+
+ def set_representation(self, r: bignum_common.ModulusRepresentation) -> None:
+ self.rep = r
+
+ def arguments(self) -> List[str]:
+ return ([bignum_common.quote_str(self.arg_n), self.rep.symbol(),
+ bignum_common.quote_str(self.arg_a)] +
+ self.result())
+
+ def description(self) -> str:
+ base = super().description()
+ mod_with_rep = 'mod({})'.format(self.rep.name)
+ return base.replace('mod', mod_with_rep, 1)
+
+ @classmethod
+ def test_cases_for_values(cls, rep: bignum_common.ModulusRepresentation,
+ n: str, a: str) -> Iterator[test_case.TestCase]:
+ """Emit test cases for the given values (if any).
+
+ This may emit no test cases if a isn't valid for the modulus n,
+ or multiple test cases if rep requires different data depending
+ on the limb size.
+ """
+ for bil in cls.limb_sizes:
+ test_object = cls(n, a, bits_in_limb=bil)
+ test_object.set_representation(rep)
+ # The class is set to having separate test cases for each limb
+ # size, because the Montgomery representation requires it.
+ # But other representations don't require it. So for other
+ # representations, emit a single test case with no dependency
+ # on the limb size.
+ if rep is not bignum_common.ModulusRepresentation.MONTGOMERY:
+ test_object.dependencies = \
+ [dep for dep in test_object.dependencies
+ if not dep.startswith('MBEDTLS_HAVE_INT')]
+ if test_object.is_valid:
+ yield test_object.create_test_case()
+ if rep is not bignum_common.ModulusRepresentation.MONTGOMERY:
+ # A single test case (emitted, or skipped due to invalidity)
+ # is enough, since this test case doesn't depend on the
+ # limb size.
+ break
+
+ # The parent class doesn't support non-bignum parameters. So we override
+ # test generation, in order to have the representation as a parameter.
+ @classmethod
+ def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
+
+ for rep in bignum_common.ModulusRepresentation.supported_representations():
+ for n in cls.moduli:
+ for a in cls.input_values:
+ yield from cls.test_cases_for_values(rep, n, a)
+
+class BignumModRawCanonicalToModulusRep(BignumModRawConvertRep):
+ """Test cases for mpi_mod_raw_canonical_to_modulus_rep."""
+ test_function = "mpi_mod_raw_canonical_to_modulus_rep"
+ test_name = "Rep canon->mod"
+
+ def result(self) -> List[str]:
+ return [self.format_result(self.convert_from_canonical(self.int_a, self.rep))]
+
+class BignumModRawModulusToCanonicalRep(BignumModRawConvertRep):
+ """Test cases for mpi_mod_raw_modulus_to_canonical_rep."""
+ test_function = "mpi_mod_raw_modulus_to_canonical_rep"
+ test_name = "Rep mod->canon"
+
+ @property
+ def arg_a(self) -> str:
+ return self.format_arg("{:x}".format(self.convert_from_canonical(self.int_a, self.rep)))
+
+ def result(self) -> List[str]:
+ return [self.format_result(self.int_a)]
+
# END MERGE SLOT 6
# BEGIN MERGE SLOT 7
diff --git a/tests/include/test/bignum_helpers.h b/tests/include/test/bignum_helpers.h
new file mode 100644
index 0000000..164017e
--- /dev/null
+++ b/tests/include/test/bignum_helpers.h
@@ -0,0 +1,118 @@
+/**
+ * \file bignum_helpers.h
+ *
+ * \brief This file contains the prototypes of helper functions for
+ * bignum-related testing.
+ */
+
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEST_BIGNUM_HELPERS_H
+#define TEST_BIGNUM_HELPERS_H
+
+#include <mbedtls/build_info.h>
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+#include <mbedtls/bignum.h>
+#include <bignum_mod.h>
+
+/** Allocate and populate a core MPI from a test case argument.
+ *
+ * This function allocates exactly as many limbs as necessary to fit
+ * the length of the input. In other words, it preserves leading zeros.
+ *
+ * The limb array is allocated with mbedtls_calloc() and must later be
+ * freed with mbedtls_free().
+ *
+ * \param[in,out] pX The address where a pointer to the allocated limb
+ * array will be stored.
+ * \c *pX must be null on entry.
+ * On exit, \c *pX is null on error or if the number
+ * of limbs is 0.
+ * \param[out] plimbs The address where the number of limbs will be stored.
+ * \param[in] input The test argument to read.
+ * It is interpreted as a hexadecimal representation
+ * of a non-negative integer.
+ *
+ * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
+ */
+int mbedtls_test_read_mpi_core( mbedtls_mpi_uint **pX, size_t *plimbs,
+ const char *input );
+
+/** Read a modulus from a hexadecimal string.
+ *
+ * This function allocates exactly as many limbs as necessary to fit
+ * the length of the input. In other words, it preserves leading zeros.
+ *
+ * The limb array is allocated with mbedtls_calloc() and must later be
+ * freed with mbedtls_free(). You can do that by calling
+ * mbedtls_test_mpi_mod_modulus_free_with_limbs().
+ *
+ * \param[in,out] N A modulus structure. It must be initialized, but
+ * not set up.
+ * \param[in] s The null-terminated hexadecimal string to read from.
+ * \param int_rep The desired representation of residues.
+ *
+ * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
+ */
+int mbedtls_test_read_mpi_modulus( mbedtls_mpi_mod_modulus *N,
+ const char *s,
+ mbedtls_mpi_mod_rep_selector int_rep );
+
+/** Free a modulus and its limbs.
+ *
+ * \param[in] N A modulus structure such that there is no other
+ * reference to `N->p`.
+ */
+void mbedtls_test_mpi_mod_modulus_free_with_limbs( mbedtls_mpi_mod_modulus *N );
+
+/** Read an MPI from a hexadecimal string.
+ *
+ * Like mbedtls_mpi_read_string(), but with tighter guarantees around
+ * edge cases.
+ *
+ * - This function guarantees that if \p s begins with '-' then the sign
+ * bit of the result will be negative, even if the value is 0.
+ * When this function encounters such a "negative 0", it
+ * increments #mbedtls_test_case_uses_negative_0.
+ * - The size of the result is exactly the minimum number of limbs needed
+ * to fit the digits in the input. In particular, this function constructs
+ * a bignum with 0 limbs for an empty string, and a bignum with leading 0
+ * limbs if the string has sufficiently many leading 0 digits.
+ * This is important so that the "0 (null)" and "0 (1 limb)" and
+ * "leading zeros" test cases do what they claim.
+ *
+ * \param[out] X The MPI object to populate. It must be initialized.
+ * \param[in] s The null-terminated hexadecimal string to read from.
+ *
+ * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
+ */
+int mbedtls_test_read_mpi( mbedtls_mpi *X, const char *s );
+
+/** Nonzero if the current test case had an input parsed with
+ * mbedtls_test_read_mpi() that is a negative 0 (`"-"`, `"-0"`, `"-00"`, etc.,
+ * constructing a result with the sign bit set to -1 and the value being
+ * all-limbs-0, which is not a valid representation in #mbedtls_mpi but is
+ * tested for robustness).
+ */
+extern unsigned mbedtls_test_case_uses_negative_0;
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+#endif /* TEST_BIGNUM_HELPERS_H */
diff --git a/tests/include/test/helpers.h b/tests/include/test/helpers.h
index 5f9bde6..b64bfcb 100644
--- a/tests/include/test/helpers.h
+++ b/tests/include/test/helpers.h
@@ -216,6 +216,17 @@
int len );
/**
+ * \brief Convert hexadecimal digit to an integer.
+ *
+ * \param c The digit to convert (`'0'` to `'9'`, `'A'` to `'F'` or
+ * `'a'` to `'f'`).
+ * \param[out] uc On success, the value of the digit (0 to 15).
+ *
+ * \return 0 on success, -1 if \p c is not a hexadecimal digit.
+ */
+int mbedtls_test_ascii2uc(const char c, unsigned char *uc);
+
+/**
* Allocate and zeroize a buffer.
*
* If the size if zero, a pointer to a zeroized 1-byte buffer is returned.
@@ -269,60 +280,4 @@
const char *file, int line);
#endif
-#if defined(MBEDTLS_BIGNUM_C)
-/** Allocate and populate a core MPI from a test case argument.
- *
- * This function allocates exactly as many limbs as necessary to fit
- * the length of the input. In other words, it preserves leading zeros.
- *
- * The limb array is allocated with mbedtls_calloc() and must later be
- * freed with mbedtls_free().
- *
- * \param[in,out] pX The address where a pointer to the allocated limb
- * array will be stored.
- * \c *pX must be null on entry.
- * On exit, \c *pX is null on error or if the number
- * of limbs is 0.
- * \param[out] plimbs The address where the number of limbs will be stored.
- * \param[in] input The test argument to read.
- * It is interpreted as a hexadecimal representation
- * of a non-negative integer.
- *
- * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
- */
-int mbedtls_test_read_mpi_core( mbedtls_mpi_uint **pX, size_t *plimbs,
- const char *input );
-
-/** Read an MPI from a hexadecimal string.
- *
- * Like mbedtls_mpi_read_string(), but with tighter guarantees around
- * edge cases.
- *
- * - This function guarantees that if \p s begins with '-' then the sign
- * bit of the result will be negative, even if the value is 0.
- * When this function encounters such a "negative 0", it
- * increments #mbedtls_test_case_uses_negative_0.
- * - The size of the result is exactly the minimum number of limbs needed
- * to fit the digits in the input. In particular, this function constructs
- * a bignum with 0 limbs for an empty string, and a bignum with leading 0
- * limbs if the string has sufficiently many leading 0 digits.
- * This is important so that the "0 (null)" and "0 (1 limb)" and
- * "leading zeros" test cases do what they claim.
- *
- * \param[out] X The MPI object to populate. It must be initialized.
- * \param[in] s The null-terminated hexadecimal string to read from.
- *
- * \return \c 0 on success, an \c MBEDTLS_ERR_MPI_xxx error code otherwise.
- */
-int mbedtls_test_read_mpi( mbedtls_mpi *X, const char *s );
-
-/** Nonzero if the current test case had an input parsed with
- * mbedtls_test_read_mpi() that is a negative 0 (`"-"`, `"-0"`, `"-00"`, etc.,
- * constructing a result with the sign bit set to -1 and the value being
- * all-limbs-0, which is not a valid representation in #mbedtls_mpi but is
- * tested for robustness).
- */
-extern unsigned mbedtls_test_case_uses_negative_0;
-#endif /* MBEDTLS_BIGNUM_C */
-
#endif /* TEST_HELPERS_H */
diff --git a/tests/scripts/generate_bignum_tests.py b/tests/scripts/generate_bignum_tests.py
index 0b84711..6ee6ab3 100755
--- a/tests/scripts/generate_bignum_tests.py
+++ b/tests/scripts/generate_bignum_tests.py
@@ -60,7 +60,6 @@
from typing import List
import scripts_path # pylint: disable=unused-import
-from mbedtls_dev import test_case
from mbedtls_dev import test_data_generation
from mbedtls_dev import bignum_common
# Import modules containing additional test classes
diff --git a/tests/src/bignum_helpers.c b/tests/src/bignum_helpers.c
new file mode 100644
index 0000000..d6ec9bd
--- /dev/null
+++ b/tests/src/bignum_helpers.c
@@ -0,0 +1,142 @@
+/**
+ * \file bignum_helpers.c
+ *
+ * \brief This file contains the prototypes of helper functions for
+ * bignum-related testing.
+ */
+
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define MBEDTLS_ALLOW_PRIVATE_ACCESS
+#include <test/bignum_helpers.h>
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <mbedtls/bignum.h>
+#include <bignum_core.h>
+#include <bignum_mod.h>
+#include <bignum_mod_raw.h>
+
+#include <test/helpers.h>
+#include <test/macros.h>
+
+int mbedtls_test_read_mpi_core( mbedtls_mpi_uint **pX, size_t *plimbs,
+ const char *input )
+{
+ /* Sanity check */
+ if( *pX != NULL )
+ return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+ size_t hex_len = strlen( input );
+ size_t byte_len = ( hex_len + 1 ) / 2;
+ *plimbs = CHARS_TO_LIMBS( byte_len );
+
+ /* A core bignum is not allowed to be empty. Forbid it as test data,
+ * this way static analyzers have a chance of knowing we don't expect
+ * the bignum functions to support empty inputs. */
+ if( *plimbs == 0 )
+ return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+ *pX = mbedtls_calloc( *plimbs, sizeof( **pX ) );
+ if( *pX == NULL )
+ return( MBEDTLS_ERR_MPI_ALLOC_FAILED );
+
+ unsigned char *byte_start = ( unsigned char * ) *pX;
+ if( byte_len % sizeof( mbedtls_mpi_uint ) != 0 )
+ {
+ byte_start += sizeof( mbedtls_mpi_uint ) - byte_len % sizeof( mbedtls_mpi_uint );
+ }
+ if( ( hex_len & 1 ) != 0 )
+ {
+ /* mbedtls_test_unhexify wants an even number of hex digits */
+ TEST_ASSERT( mbedtls_test_ascii2uc( *input, byte_start ) == 0 );
+ ++byte_start;
+ ++input;
+ --byte_len;
+ }
+ TEST_ASSERT( mbedtls_test_unhexify( byte_start,
+ byte_len,
+ input,
+ &byte_len ) == 0 );
+
+ mbedtls_mpi_core_bigendian_to_host( *pX, *plimbs );
+ return( 0 );
+
+exit:
+ mbedtls_free( *pX );
+ return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+}
+
+int mbedtls_test_read_mpi_modulus( mbedtls_mpi_mod_modulus *N,
+ const char *s,
+ mbedtls_mpi_mod_rep_selector int_rep )
+{
+ mbedtls_mpi_uint *p = NULL;
+ size_t limbs = 0;
+ if( N->limbs != 0 )
+ return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+ int ret = mbedtls_test_read_mpi_core( &p, &limbs, s );
+ if( ret != 0 )
+ return( ret );
+ ret = mbedtls_mpi_mod_modulus_setup( N, p, limbs, int_rep );
+ if( ret != 0 )
+ mbedtls_free( p );
+ return( ret );
+}
+
+void mbedtls_test_mpi_mod_modulus_free_with_limbs( mbedtls_mpi_mod_modulus *N )
+{
+ mbedtls_free( (mbedtls_mpi_uint*) N->p );
+ mbedtls_mpi_mod_modulus_free( N );
+}
+
+int mbedtls_test_read_mpi( mbedtls_mpi *X, const char *s )
+{
+ int negative = 0;
+ /* Always set the sign bit to -1 if the input has a minus sign, even for 0.
+ * This creates an invalid representation, which mbedtls_mpi_read_string()
+ * avoids but we want to be able to create that in test data. */
+ if( s[0] == '-' )
+ {
+ ++s;
+ negative = 1;
+ }
+ /* mbedtls_mpi_read_string() currently retains leading zeros.
+ * It always allocates at least one limb for the value 0. */
+ if( s[0] == 0 )
+ {
+ mbedtls_mpi_free( X );
+ return( 0 );
+ }
+ int ret = mbedtls_mpi_read_string( X, 16, s );
+ if( ret != 0 )
+ return( ret );
+ if( negative )
+ {
+ if( mbedtls_mpi_cmp_int( X, 0 ) == 0 )
+ ++mbedtls_test_case_uses_negative_0;
+ X->s = -1;
+ }
+ return( 0 );
+}
+
+#endif /* MBEDTLS_BIGNUM_C */
+
diff --git a/tests/src/helpers.c b/tests/src/helpers.c
index 7c83714..be5c465 100644
--- a/tests/src/helpers.c
+++ b/tests/src/helpers.c
@@ -48,7 +48,7 @@
#endif /* MBEDTLS_PLATFORM_C */
}
-static int ascii2uc(const char c, unsigned char *uc)
+int mbedtls_test_ascii2uc(const char c, unsigned char *uc)
{
if( ( c >= '0' ) && ( c <= '9' ) )
*uc = c - '0';
@@ -207,10 +207,10 @@
while( *ibuf != 0 )
{
- if ( ascii2uc( *(ibuf++), &uc ) != 0 )
+ if ( mbedtls_test_ascii2uc( *(ibuf++), &uc ) != 0 )
return( -1 );
- if ( ascii2uc( *(ibuf++), &uc2 ) != 0 )
+ if ( mbedtls_test_ascii2uc( *(ibuf++), &uc2 ) != 0 )
return( -1 );
*(obuf++) = ( uc << 4 ) | uc2;
@@ -350,84 +350,3 @@
}
}
#endif /* MBEDTLS_TEST_HOOKS */
-
-#if defined(MBEDTLS_BIGNUM_C)
-#include "bignum_core.h"
-
-int mbedtls_test_read_mpi_core( mbedtls_mpi_uint **pX, size_t *plimbs,
- const char *input )
-{
- /* Sanity check */
- if( *pX != NULL )
- return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
-
- size_t hex_len = strlen( input );
- size_t byte_len = ( hex_len + 1 ) / 2;
- *plimbs = CHARS_TO_LIMBS( byte_len );
-
- /* A core bignum is not allowed to be empty. Forbid it as test data,
- * this way static analyzers have a chance of knowing we don't expect
- * the bignum functions to support empty inputs. */
- if( *plimbs == 0 )
- return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
-
- *pX = mbedtls_calloc( *plimbs, sizeof( **pX ) );
- if( *pX == NULL )
- return( MBEDTLS_ERR_MPI_ALLOC_FAILED );
-
- unsigned char *byte_start = ( unsigned char * ) *pX;
- if( byte_len % sizeof( mbedtls_mpi_uint ) != 0 )
- {
- byte_start += sizeof( mbedtls_mpi_uint ) - byte_len % sizeof( mbedtls_mpi_uint );
- }
- if( ( hex_len & 1 ) != 0 )
- {
- /* mbedtls_test_unhexify wants an even number of hex digits */
- TEST_ASSERT( ascii2uc( *input, byte_start ) == 0 );
- ++byte_start;
- ++input;
- --byte_len;
- }
- TEST_ASSERT( mbedtls_test_unhexify( byte_start,
- byte_len,
- input,
- &byte_len ) == 0 );
-
- mbedtls_mpi_core_bigendian_to_host( *pX, *plimbs );
- return( 0 );
-
-exit:
- mbedtls_free( *pX );
- return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
-}
-
-int mbedtls_test_read_mpi( mbedtls_mpi *X, const char *s )
-{
- int negative = 0;
- /* Always set the sign bit to -1 if the input has a minus sign, even for 0.
- * This creates an invalid representation, which mbedtls_mpi_read_string()
- * avoids but we want to be able to create that in test data. */
- if( s[0] == '-' )
- {
- ++s;
- negative = 1;
- }
- /* mbedtls_mpi_read_string() currently retains leading zeros.
- * It always allocates at least one limb for the value 0. */
- if( s[0] == 0 )
- {
- mbedtls_mpi_free( X );
- return( 0 );
- }
- int ret = mbedtls_mpi_read_string( X, 16, s );
- if( ret != 0 )
- return( ret );
- if( negative )
- {
- if( mbedtls_mpi_cmp_int( X, 0 ) == 0 )
- ++mbedtls_test_case_uses_negative_0;
- X->s = -1;
- }
- return( 0 );
-}
-#endif
diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function
index fe33f9b..8249564 100644
--- a/tests/suites/helpers.function
+++ b/tests/suites/helpers.function
@@ -5,6 +5,7 @@
#include <test/helpers.h>
#include <test/macros.h>
#include <test/random.h>
+#include <test/bignum_helpers.h>
#include <test/psa_crypto_helpers.h>
#include <stdlib.h>
diff --git a/tests/suites/test_suite_bignum_mod_raw.function b/tests/suites/test_suite_bignum_mod_raw.function
index 461a18e..4a658e1 100644
--- a/tests/suites/test_suite_bignum_mod_raw.function
+++ b/tests/suites/test_suite_bignum_mod_raw.function
@@ -619,7 +619,59 @@
/* END MERGE SLOT 5 */
/* BEGIN MERGE SLOT 6 */
+/* BEGIN_CASE */
+void mpi_mod_raw_canonical_to_modulus_rep( const char *input_N, int rep,
+ const char *input_A,
+ const char *input_X )
+{
+ mbedtls_mpi_mod_modulus N;
+ mbedtls_mpi_mod_modulus_init( &N );
+ mbedtls_mpi_uint *A = NULL;
+ size_t A_limbs = 0;;
+ mbedtls_mpi_uint *X = NULL;
+ size_t X_limbs = 0;
+ TEST_EQUAL( 0, mbedtls_test_read_mpi_modulus( &N, input_N, rep ) );
+ TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &A, &A_limbs, input_A ) );
+ TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &X, &X_limbs, input_X ) );
+
+ TEST_EQUAL( 0, mbedtls_mpi_mod_raw_canonical_to_modulus_rep( A, &N ) );
+ ASSERT_COMPARE( A, A_limbs * sizeof( mbedtls_mpi_uint ),
+ X, X_limbs * sizeof( mbedtls_mpi_uint ) );
+
+exit:
+ mbedtls_test_mpi_mod_modulus_free_with_limbs( &N );
+ mbedtls_free( A );
+ mbedtls_free( X );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void mpi_mod_raw_modulus_to_canonical_rep( const char *input_N, int rep,
+ const char *input_A,
+ const char *input_X )
+{
+ mbedtls_mpi_mod_modulus N;
+ mbedtls_mpi_mod_modulus_init( &N );
+ mbedtls_mpi_uint *A = NULL;
+ size_t A_limbs = 0;
+ mbedtls_mpi_uint *X = NULL;
+ size_t X_limbs = 0;
+
+ TEST_EQUAL( 0, mbedtls_test_read_mpi_modulus( &N, input_N, rep ) );
+ TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &A, &A_limbs, input_A ) );
+ TEST_EQUAL( 0, mbedtls_test_read_mpi_core( &X, &X_limbs, input_X ) );
+
+ TEST_EQUAL( 0, mbedtls_mpi_mod_raw_modulus_to_canonical_rep( A, &N ) );
+ ASSERT_COMPARE( A, A_limbs * sizeof( mbedtls_mpi_uint ),
+ X, X_limbs * sizeof( mbedtls_mpi_uint ) );
+
+exit:
+ mbedtls_test_mpi_mod_modulus_free_with_limbs( &N );
+ mbedtls_free( A );
+ mbedtls_free( X );
+}
+/* END_CASE */
/* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */
diff --git a/tests/suites/test_suite_bignum_random.data b/tests/suites/test_suite_bignum_random.data
index fe29053..ee5e397 100644
--- a/tests/suites/test_suite_bignum_random.data
+++ b/tests/suites/test_suite_bignum_random.data
@@ -17,31 +17,55 @@
mpi_core_random_basic:0x40000000:"0200000000000000000000000000000000":0
# Use the same data values for mpi_core_random_basic->NOT_ACCEPTABLE
-# and for mpi_random_values where we want to return NOT_ACCEPTABLE but
-# this isn't checked at runtime.
-MPI core random basic: 2^28-1..2^28 (NOT_ACCEPTABLE)
-mpi_core_random_basic:0x0fffffff:"10000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+# and for mpi_XXX_random_values where we want to return NOT_ACCEPTABLE
+# but this isn't checked at runtime.
+MPI core random basic: 2^28-1..2^28+1 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x0fffffff:"10000001":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
-MPI random legacy=core: 2^28-1..2^28 (NOT_ACCEPTABLE)
-mpi_random_values:0x0fffffff:"10000000"
+MPI random legacy=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE)
+mpi_legacy_random_values:0x0fffffff:"10000001"
-MPI core random basic: 2^29-1..2^29 (NOT_ACCEPTABLE)
-mpi_core_random_basic:0x1fffffff:"20000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+MPI random mod=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE) (Mont)
+mpi_mod_random_values:0x0fffffff:"10000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
-MPI random legacy=core: 2^29-1..2^29 (NOT_ACCEPTABLE)
-mpi_random_values:0x1fffffff:"20000000"
+MPI random mod=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE) (canon)
+mpi_mod_random_values:0x0fffffff:"10000001":MBEDTLS_MPI_MOD_REP_OPT_RED
-MPI core random basic: 2^30-1..2^30 (NOT_ACCEPTABLE)
-mpi_core_random_basic:0x3fffffff:"40000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+MPI core random basic: 2^29-1..2^29+1 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x1fffffff:"20000001":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
-MPI random legacy=core: 2^30-1..2^30 (NOT_ACCEPTABLE)
-mpi_random_values:0x3fffffff:"40000000"
+MPI random legacy=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE)
+mpi_legacy_random_values:0x1fffffff:"20000001"
-MPI core random basic: 2^31-1..2^31 (NOT_ACCEPTABLE)
-mpi_core_random_basic:0x7fffffff:"80000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+MPI random mod=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE) (Mont)
+mpi_mod_random_values:0x1fffffff:"20000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
-MPI random legacy=core: 2^31-1..2^31 (NOT_ACCEPTABLE)
-mpi_random_values:0x7fffffff:"80000000"
+MPI random mod=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE) (canon)
+mpi_mod_random_values:0x1fffffff:"20000001":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI core random basic: 2^30-1..2^30+1 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x3fffffff:"40000001":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+
+MPI random legacy=core: 2^30-1..2^30+1 (NOT_ACCEPTABLE)
+mpi_legacy_random_values:0x3fffffff:"40000001"
+
+MPI random mod=core: 2^30-1..2^30+1 (NOT_ACCEPTABLE) (Mont)
+mpi_mod_random_values:0x3fffffff:"40000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 2^30-1..2^30+1 (NOT_ACCEPTABLE) (canon)
+mpi_mod_random_values:0x3fffffff:"40000001":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI core random basic: 2^31-1..2^31+1 (NOT_ACCEPTABLE)
+mpi_core_random_basic:0x7fffffff:"80000001":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
+
+MPI random legacy=core: 2^31-1..2^31+1 (NOT_ACCEPTABLE)
+mpi_legacy_random_values:0x7fffffff:"80000001"
+
+MPI random mod=core: 2^31-1..2^31+1 (NOT_ACCEPTABLE) (Mont)
+mpi_mod_random_values:0x7fffffff:"80000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 2^31-1..2^31+1 (NOT_ACCEPTABLE) (canon)
+mpi_mod_random_values:0x7fffffff:"80000001":MBEDTLS_MPI_MOD_REP_OPT_RED
MPI random in range: 1..2
mpi_random_many:1:"02":1000
@@ -214,22 +238,103 @@
mpi_random_fail:2:"000000000000000001":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
MPI random legacy=core: 0..1
-mpi_random_values:0:"01"
+mpi_legacy_random_values:0:"01"
MPI random legacy=core: 0..2
-mpi_random_values:0:"02"
+mpi_legacy_random_values:0:"02"
MPI random legacy=core: 1..2
-mpi_random_values:1:"02"
+mpi_legacy_random_values:1:"02"
MPI random legacy=core: 2^30..2^31
-mpi_random_values:0x40000000:"80000000"
+mpi_legacy_random_values:0x40000000:"80000000"
MPI random legacy=core: 2^31-1..2^32-1
-mpi_random_values:0x7fffffff:"ffffffff"
+mpi_legacy_random_values:0x7fffffff:"ffffffff"
MPI random legacy=core: 0..2^256
-mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000000"
+mpi_legacy_random_values:0:"010000000000000000000000000000000000000000000000000000000000000000"
MPI random legacy=core: 0..2^256+1
-mpi_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001"
+mpi_legacy_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001"
+
+MPI random mod=core: 0..1 (Mont)
+mpi_mod_random_values:0:"01":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 0..1 (canon)
+mpi_mod_random_values:0:"01":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 0..3 (Mont)
+mpi_mod_random_values:0:"03":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 0..3 (canon)
+mpi_mod_random_values:0:"03":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 1..3 (Mont)
+mpi_mod_random_values:1:"03":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 1..3 (canon)
+mpi_mod_random_values:1:"03":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 2^30..2^31-1 (Mont)
+mpi_mod_random_values:0x40000000:"7fffffff":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 2^30..2^31-1 (canon)
+mpi_mod_random_values:0x40000000:"7fffffff":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 2^31-1..2^32-1 (Mont)
+mpi_mod_random_values:0x7fffffff:"ffffffff":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 2^31-1..2^32-1 (canon)
+mpi_mod_random_values:0x7fffffff:"ffffffff":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod=core: 0..2^256+1 (Mont)
+mpi_mod_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
+
+MPI random mod=core: 0..2^256+1 (canon)
+mpi_mod_random_values:0:"010000000000000000000000000000000000000000000000000000000000000001":MBEDTLS_MPI_MOD_REP_OPT_RED
+
+MPI random mod validation: 1 limb, good, 0..1
+mpi_mod_random_validation:0:"1":0:0
+
+MPI random mod validation: 1 limb, good, 1..3
+mpi_mod_random_validation:1:"3":0:0
+
+MPI random mod validation: 1 limb, good, 2..3
+mpi_mod_random_validation:2:"3":0:0
+
+MPI random mod validation: 1 limb, good, 3..5
+mpi_mod_random_validation:3:"5":0:0
+
+MPI random mod validation: 1 limb, good, 4..5
+mpi_mod_random_validation:4:"5":0:0
+
+MPI random mod validation: 1 limb, good, 5..7
+mpi_mod_random_validation:5:"7":0:0
+
+MPI random mod validation: 1 limb, good, 6..7
+mpi_mod_random_validation:6:"7":0:0
+
+MPI random mod validation: 1 limb, good, 0..0x123
+mpi_mod_random_validation:0:"123":0:0
+
+MPI random mod validation: 2+ limbs, good
+mpi_mod_random_validation:0:"01234567890123456789":0:0
+
+MPI random mod validation: 1 limb, output null
+mpi_mod_random_validation:0:"123":-1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: 1 limb, output too large
+mpi_mod_random_validation:0:"123":1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: 2+ limbs, output too small
+mpi_mod_random_validation:0:"01234567890123456789":-1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: 2+ limbs, output too large
+mpi_mod_random_validation:0:"01234567890123456789":1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: min == upper bound
+mpi_mod_random_validation:0x123:"123":-1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
+
+MPI random mod validation: min > upper bound
+mpi_mod_random_validation:0x124:"123":-1:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
diff --git a/tests/suites/test_suite_bignum_random.function b/tests/suites/test_suite_bignum_random.function
index 184de5a..4709148 100644
--- a/tests/suites/test_suite_bignum_random.function
+++ b/tests/suites/test_suite_bignum_random.function
@@ -3,11 +3,44 @@
* functions. Due to the complexity of how these functions are tested,
* we test all the layers in a single test suite, unlike the way other
* functions are tested with each layer in its own test suite.
+ *
+ * Test strategy
+ * =============
+ *
+ * There are three main goals for testing random() functions:
+ * - Parameter validation.
+ * - Correctness of outputs (well-formed, in range).
+ * - Distribution of outputs.
+ *
+ * We test parameter validation in a standard way, with unit tests with
+ * positive and negative cases:
+ * - mbedtls_mpi_core_random(): negative cases for mpi_core_random_basic.
+ * - mbedtls_mpi_mod_raw_random(), mbedtls_mpi_mod_random(): negative
+ * cases for mpi_mod_random_validation.
+ * - mbedtls_mpi_random(): mpi_random_fail.
+ *
+ * We test the correctness of outputs in positive tests:
+ * - mbedtls_mpi_core_random(): positive cases for mpi_core_random_basic,
+ * and mpi_random_many.
+ * - mbedtls_mpi_mod_raw_random(), mbedtls_mpi_mod_random(): tested indirectly
+ * via mpi_mod_random_values.
+ * - mbedtls_mpi_random(): mpi_random_sizes, plus indirectly via
+ * mpi_random_values.
+ *
+ * We test the distribution of outputs only for mbedtls_mpi_core_random(),
+ * in mpi_random_many, which runs the function multiple times. This also
+ * helps in validating the output range, through test cases with a small
+ * range where any output out of range would be very likely to lead to a
+ * test failure. For the other functions, we validate the distribution
+ * indirectly by testing that these functions consume the random generator
+ * in the same way as mbedtls_mpi_core_random(). This is done in
+ * mpi_mod_random_values and mpi_legacy_random_values.
*/
#include "mbedtls/bignum.h"
#include "mbedtls/entropy.h"
#include "bignum_core.h"
+#include "bignum_mod_raw.h"
#include "constant_time_internal.h"
/* This test suite only manipulates non-negative bignums. */
@@ -110,7 +143,7 @@
/* END_CASE */
/* BEGIN_CASE */
-void mpi_random_values( int min, char *max_hex )
+void mpi_legacy_random_values( int min, char *max_hex )
{
/* Same RNG as in mpi_core_random_basic */
mbedtls_test_rnd_pseudo_info rnd_core = rnd_pseudo_seed;
@@ -159,6 +192,77 @@
/* END_CASE */
/* BEGIN_CASE */
+void mpi_mod_random_values( int min, char *max_hex, int rep )
+{
+ /* Same RNG as in mpi_core_random_basic */
+ mbedtls_test_rnd_pseudo_info rnd_core = rnd_pseudo_seed;
+ mbedtls_test_rnd_pseudo_info rnd_mod_raw;
+ memcpy( &rnd_mod_raw, &rnd_core, sizeof( rnd_core ) );
+ mbedtls_test_rnd_pseudo_info rnd_mod;
+ memcpy( &rnd_mod, &rnd_core, sizeof( rnd_core ) );
+ mbedtls_mpi_uint *R_core = NULL;
+ mbedtls_mpi_uint *R_mod_raw = NULL;
+ mbedtls_mpi_uint *R_mod_digits = NULL;
+ mbedtls_mpi_mod_residue R_mod;
+ mbedtls_mpi_mod_modulus N;
+ mbedtls_mpi_mod_modulus_init( &N );
+
+ TEST_EQUAL( mbedtls_test_read_mpi_modulus( &N, max_hex, rep ), 0 );
+ ASSERT_ALLOC( R_core, N.limbs );
+ ASSERT_ALLOC( R_mod_raw, N.limbs );
+ ASSERT_ALLOC( R_mod_digits, N.limbs );
+ TEST_EQUAL( mbedtls_mpi_mod_residue_setup( &R_mod, &N,
+ R_mod_digits, N.limbs ),
+ 0 );
+
+ /* Call the core and mod random() functions with the same random stream. */
+ int core_ret = mbedtls_mpi_core_random( R_core,
+ min, N.p, N.limbs,
+ mbedtls_test_rnd_pseudo_rand,
+ &rnd_core );
+ int mod_raw_ret = mbedtls_mpi_mod_raw_random( R_mod_raw,
+ min, &N,
+ mbedtls_test_rnd_pseudo_rand,
+ &rnd_mod_raw );
+ int mod_ret = mbedtls_mpi_mod_random( &R_mod,
+ min, &N,
+ mbedtls_test_rnd_pseudo_rand,
+ &rnd_mod );
+
+ /* They must return the same status, and, on success, output the
+ * same number, with the same limb count. */
+ TEST_EQUAL( core_ret, mod_raw_ret );
+ TEST_EQUAL( core_ret, mod_ret );
+ if( core_ret == 0 )
+ {
+ TEST_EQUAL( mbedtls_mpi_mod_raw_modulus_to_canonical_rep( R_mod_raw, &N ),
+ 0 );
+ ASSERT_COMPARE( R_core, N.limbs * ciL,
+ R_mod_raw, N.limbs * ciL );
+ TEST_EQUAL( mbedtls_mpi_mod_raw_modulus_to_canonical_rep( R_mod_digits, &N ),
+ 0 );
+ ASSERT_COMPARE( R_core, N.limbs * ciL,
+ R_mod_digits, N.limbs * ciL );
+ }
+
+ /* Also check that they have consumed the RNG in the same way. */
+ /* This may theoretically fail on rare platforms with padding in
+ * the structure! If this is a problem in practice, change to a
+ * field-by-field comparison. */
+ ASSERT_COMPARE( &rnd_core, sizeof( rnd_core ),
+ &rnd_mod_raw, sizeof( rnd_mod_raw ) );
+ ASSERT_COMPARE( &rnd_core, sizeof( rnd_core ),
+ &rnd_mod, sizeof( rnd_mod ) );
+
+exit:
+ mbedtls_test_mpi_mod_modulus_free_with_limbs( &N );
+ mbedtls_free( R_core );
+ mbedtls_free( R_mod_raw );
+ mbedtls_free( R_mod_digits );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
void mpi_random_many( int min, char *bound_hex, int iterations )
{
/* Generate numbers in the range 1..bound-1. Do it iterations times.
@@ -312,6 +416,64 @@
/* END_CASE */
/* BEGIN_CASE */
+void mpi_mod_random_validation( int min, char *bound_hex,
+ int result_limbs_delta,
+ int expected_ret )
+{
+ mbedtls_mpi_uint *result_digits = NULL;
+ mbedtls_mpi_mod_modulus N;
+ mbedtls_mpi_mod_modulus_init( &N );
+
+ TEST_EQUAL( mbedtls_test_read_mpi_modulus( &N, bound_hex,
+ MBEDTLS_MPI_MOD_REP_OPT_RED ),
+ 0 );
+ size_t result_limbs = N.limbs + result_limbs_delta;
+ ASSERT_ALLOC( result_digits, result_limbs );
+ /* Build a reside that might not match the modulus, to test that
+ * the library function rejects that as expected. */
+ mbedtls_mpi_mod_residue result = {result_digits, result_limbs};
+
+ TEST_EQUAL( mbedtls_mpi_mod_random( &result, min, &N,
+ mbedtls_test_rnd_std_rand, NULL ),
+ expected_ret );
+ if( expected_ret == 0 )
+ {
+ /* Success should only be expected when the result has the same
+ * size as the modulus, otherwise it's a mistake in the test data. */
+ TEST_EQUAL( result_limbs, N.limbs );
+ /* Sanity check: check that the result is in range */
+ TEST_EQUAL( mbedtls_mpi_core_lt_ct( result_digits, N.p, N.limbs ),
+ 1 );
+ /* Check result >= min (changes result) */
+ TEST_EQUAL( mbedtls_mpi_core_sub_int( result_digits, result_digits, min,
+ result_limbs ),
+ 0 );
+ }
+
+ /* When the result has the right number of limbs, also test mod_raw
+ * (for which this is an unchecked precondition). */
+ if( result_limbs_delta == 0 )
+ {
+ TEST_EQUAL( mbedtls_mpi_mod_raw_random( result_digits, min, &N,
+ mbedtls_test_rnd_std_rand, NULL ),
+ expected_ret );
+ if( expected_ret == 0 )
+ {
+ TEST_EQUAL( mbedtls_mpi_core_lt_ct( result_digits, N.p, N.limbs ),
+ 1 );
+ TEST_EQUAL( mbedtls_mpi_core_sub_int( result_digits, result.p, min,
+ result_limbs ),
+ 0 );
+ }
+ }
+
+exit:
+ mbedtls_test_mpi_mod_modulus_free_with_limbs( &N );
+ mbedtls_free( result_digits );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
void mpi_random_fail( int min, data_t *bound_bytes, int expected_ret )
{
mbedtls_mpi upper_bound;