diff --git a/library/bignum_mod.c b/library/bignum_mod.c index 31e18e741..c9efb3350 100644 --- a/library/bignum_mod.c +++ b/library/bignum_mod.c @@ -303,6 +303,17 @@ int mbedtls_mpi_mod_add( mbedtls_mpi_mod_residue *X, /* 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 @@ int mbedtls_mpi_mod_read( mbedtls_mpi_mod_residue *r, 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 95aaacc4d..2ee219f08 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 @@ typedef struct { 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 @@ int mbedtls_mpi_mod_add( mbedtls_mpi_mod_residue *X, /* 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 5950ff660..18599c353 100644 --- a/library/bignum_mod_raw.c +++ b/library/bignum_mod_raw.c @@ -186,6 +186,48 @@ void mbedtls_mpi_mod_raw_add( mbedtls_mpi_uint *X, /* 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 0fac6f874..ea3207fa4 100644 --- a/library/bignum_mod_raw.h +++ b/library/bignum_mod_raw.h @@ -336,6 +336,74 @@ void mbedtls_mpi_mod_raw_add( mbedtls_mpi_uint *X, /* 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 c4efabfcc..242217554 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 @@ def hex_to_int(val: str) -> int: 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 @@ class OperationCommon(test_data_generation.BaseTest): 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 @@ class OperationCommon(test_data_generation.BaseTest): 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 OperationCommon(test_data_generation.BaseTest): ) +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 @@ class ModOperationCommon(OperationCommon): 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 @@ class ModOperationCommon(OperationCommon): 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 09bbbee1d..f9d9899f6 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 @@ class BignumModRawAdd(bignum_common.ModOperationCommon, # 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 000000000..164017e69 --- /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 + +#if defined(MBEDTLS_BIGNUM_C) + +#include +#include + +/** 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 5f9bde697..b64bfcbce 100644 --- a/tests/include/test/helpers.h +++ b/tests/include/test/helpers.h @@ -215,6 +215,17 @@ void mbedtls_test_hexify( unsigned char *obuf, const unsigned char *ibuf, 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. * @@ -269,60 +280,4 @@ void mbedtls_test_err_add_check( int high, int low, 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 0b8471186..6ee6ab39a 100755 --- a/tests/scripts/generate_bignum_tests.py +++ b/tests/scripts/generate_bignum_tests.py @@ -60,7 +60,6 @@ from abc import ABCMeta 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 000000000..d6ec9bd3c --- /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 + +#if defined(MBEDTLS_BIGNUM_C) + +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 7c83714f1..be5c465fd 100644 --- a/tests/src/helpers.c +++ b/tests/src/helpers.c @@ -48,7 +48,7 @@ void mbedtls_test_platform_teardown( void ) #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 @@ int mbedtls_test_unhexify( unsigned char *obuf, 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 @@ void mbedtls_test_err_add_check( int high, int low, } } #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 fe33f9bf9..82495644e 100644 --- a/tests/suites/helpers.function +++ b/tests/suites/helpers.function @@ -5,6 +5,7 @@ #include #include #include +#include #include #include diff --git a/tests/suites/test_suite_bignum_mod_raw.function b/tests/suites/test_suite_bignum_mod_raw.function index 461a18e2e..4a658e1d5 100644 --- a/tests/suites/test_suite_bignum_mod_raw.function +++ b/tests/suites/test_suite_bignum_mod_raw.function @@ -619,7 +619,59 @@ exit: /* 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 fe290531a..ee5e39778 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: 2^30..2^129 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 bad arguments: min > N = 1, 0 limb in upper bound 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 184de5a40..470914890 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 @@ exit: /* 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; @@ -158,6 +191,77 @@ exit: } /* 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 ) { @@ -311,6 +415,64 @@ exit: } /* 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 ) {