Merge pull request #6747 from gilles-peskine-arm/bignum-mod-random

Bignum mod random
This commit is contained in:
Manuel Pégourié-Gonnard 2022-12-23 10:36:22 +01:00 committed by GitHub
commit 2fcb4c1d06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 909 additions and 175 deletions

View File

@ -303,6 +303,17 @@ int mbedtls_mpi_mod_add( mbedtls_mpi_mod_residue *X,
/* BEGIN MERGE SLOT 6 */ /* 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 */ /* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */ /* BEGIN MERGE SLOT 7 */
@ -326,8 +337,7 @@ int mbedtls_mpi_mod_read( mbedtls_mpi_mod_residue *r,
r->limbs = m->limbs; r->limbs = m->limbs;
if( m->int_rep == MBEDTLS_MPI_MOD_REP_MONTGOMERY ) ret = mbedtls_mpi_mod_raw_canonical_to_modulus_rep( r->p, m );
ret = mbedtls_mpi_mod_raw_to_mont_rep( r->p, m );
cleanup: cleanup:
return ( ret ); return ( ret );

View File

@ -87,12 +87,23 @@
#include "mbedtls/bignum.h" #include "mbedtls/bignum.h"
#endif #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 typedef enum
{ {
/** Representation not chosen (makes the modulus structure invalid). */
MBEDTLS_MPI_MOD_REP_INVALID = 0, 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_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; } mbedtls_mpi_mod_rep_selector;
/* Make mbedtls_mpi_mod_rep_selector and mbedtls_mpi_mod_ext_rep disjoint to /* 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 mbedtls_mpi_mod_rep_selector int_rep; // selector to signal the active member of the union
union rep union rep
{ {
/* if int_rep == #MBEDTLS_MPI_MOD_REP_MONTGOMERY */
mbedtls_mpi_mont_struct mont; mbedtls_mpi_mont_struct mont;
/* if int_rep == #MBEDTLS_MPI_MOD_REP_OPT_RED */
mbedtls_mpi_opt_red_struct ored; mbedtls_mpi_opt_red_struct ored;
} rep; } rep;
} mbedtls_mpi_mod_modulus; } mbedtls_mpi_mod_modulus;
@ -319,6 +332,39 @@ int mbedtls_mpi_mod_add( mbedtls_mpi_mod_residue *X,
/* BEGIN MERGE SLOT 6 */ /* 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 */ /* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */ /* BEGIN MERGE SLOT 7 */

View File

@ -186,6 +186,48 @@ void mbedtls_mpi_mod_raw_add( mbedtls_mpi_uint *X,
/* BEGIN MERGE SLOT 6 */ /* 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 */ /* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */ /* BEGIN MERGE SLOT 7 */

View File

@ -336,6 +336,74 @@ void mbedtls_mpi_mod_raw_add( mbedtls_mpi_uint *X,
/* BEGIN MERGE SLOT 6 */ /* 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 */ /* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */ /* BEGIN MERGE SLOT 7 */

View File

@ -15,6 +15,7 @@
# limitations under the License. # limitations under the License.
from abc import abstractmethod from abc import abstractmethod
import enum
from typing import Iterator, List, Tuple, TypeVar, Any from typing import Iterator, List, Tuple, TypeVar, Any
from itertools import chain from itertools import chain
@ -53,7 +54,7 @@ def hex_to_int(val: str) -> int:
return 0 return 0
return int(val, 16) return int(val, 16)
def quote_str(val) -> str: def quote_str(val: str) -> str:
return "\"{}\"".format(val) return "\"{}\"".format(val)
def bound_mpi(val: int, bits_in_limb: int) -> int: 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: def hex_digits(self) -> int:
return 2 * (self.limbs * self.bits_in_limb // 8) 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: if self.input_style not in self.input_styles:
raise ValueError("Unknown input style!") raise ValueError("Unknown input style!")
if self.input_style == "variable": if self.input_style == "variable":
@ -147,7 +148,7 @@ class OperationCommon(test_data_generation.BaseTest):
else: else:
return val.zfill(self.hex_digits) return val.zfill(self.hex_digits)
def format_result(self, res) -> str: def format_result(self, res: int) -> str:
res_str = '{:x}'.format(res) res_str = '{:x}'.format(res)
return quote_str(self.format_arg(res_str)) 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): class ModOperationCommon(OperationCommon):
#pylint: disable=abstract-method #pylint: disable=abstract-method
"""Target for bignum mod_raw test case generation.""" """Target for bignum mod_raw test case generation."""
@ -266,6 +284,17 @@ class ModOperationCommon(OperationCommon):
def from_montgomery(self, val: int) -> int: def from_montgomery(self, val: int) -> int:
return (val * self.r_inv) % self.int_n 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 @property
def boundary(self) -> int: def boundary(self) -> int:
return self.int_n return self.int_n
@ -282,6 +311,9 @@ class ModOperationCommon(OperationCommon):
def arg_n(self) -> str: def arg_n(self) -> str:
return self.format_arg(self.val_n) 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]: def arguments(self) -> List[str]:
return [quote_str(self.arg_n)] + super().arguments() return [quote_str(self.arg_n)] + super().arguments()

View File

@ -14,8 +14,9 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # 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 test_data_generation
from . import bignum_common from . import bignum_common
from .bignum_data import ONLY_PRIME_MODULI from .bignum_data import ONLY_PRIME_MODULI
@ -116,6 +117,88 @@ class BignumModRawAdd(bignum_common.ModOperationCommon,
# BEGIN MERGE SLOT 6 # 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 # END MERGE SLOT 6
# BEGIN MERGE SLOT 7 # BEGIN MERGE SLOT 7

View File

@ -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 */

View File

@ -215,6 +215,17 @@ void mbedtls_test_hexify( unsigned char *obuf,
const unsigned char *ibuf, const unsigned char *ibuf,
int len ); 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. * Allocate and zeroize a buffer.
* *
@ -269,60 +280,4 @@ void mbedtls_test_err_add_check( int high, int low,
const char *file, int line); const char *file, int line);
#endif #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 */ #endif /* TEST_HELPERS_H */

View File

@ -60,7 +60,6 @@ from abc import ABCMeta
from typing import List from typing import List
import scripts_path # pylint: disable=unused-import import scripts_path # pylint: disable=unused-import
from mbedtls_dev import test_case
from mbedtls_dev import test_data_generation from mbedtls_dev import test_data_generation
from mbedtls_dev import bignum_common from mbedtls_dev import bignum_common
# Import modules containing additional test classes # Import modules containing additional test classes

142
tests/src/bignum_helpers.c Normal file
View File

@ -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 */

View File

@ -48,7 +48,7 @@ void mbedtls_test_platform_teardown( void )
#endif /* MBEDTLS_PLATFORM_C */ #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' ) ) if( ( c >= '0' ) && ( c <= '9' ) )
*uc = c - '0'; *uc = c - '0';
@ -207,10 +207,10 @@ int mbedtls_test_unhexify( unsigned char *obuf,
while( *ibuf != 0 ) while( *ibuf != 0 )
{ {
if ( ascii2uc( *(ibuf++), &uc ) != 0 ) if ( mbedtls_test_ascii2uc( *(ibuf++), &uc ) != 0 )
return( -1 ); return( -1 );
if ( ascii2uc( *(ibuf++), &uc2 ) != 0 ) if ( mbedtls_test_ascii2uc( *(ibuf++), &uc2 ) != 0 )
return( -1 ); return( -1 );
*(obuf++) = ( uc << 4 ) | uc2; *(obuf++) = ( uc << 4 ) | uc2;
@ -350,84 +350,3 @@ void mbedtls_test_err_add_check( int high, int low,
} }
} }
#endif /* MBEDTLS_TEST_HOOKS */ #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

View File

@ -5,6 +5,7 @@
#include <test/helpers.h> #include <test/helpers.h>
#include <test/macros.h> #include <test/macros.h>
#include <test/random.h> #include <test/random.h>
#include <test/bignum_helpers.h>
#include <test/psa_crypto_helpers.h> #include <test/psa_crypto_helpers.h>
#include <stdlib.h> #include <stdlib.h>

View File

@ -619,7 +619,59 @@ exit:
/* END MERGE SLOT 5 */ /* END MERGE SLOT 5 */
/* BEGIN MERGE SLOT 6 */ /* 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 */ /* END MERGE SLOT 6 */
/* BEGIN MERGE SLOT 7 */ /* BEGIN MERGE SLOT 7 */

View File

@ -17,31 +17,55 @@ MPI core random basic: 2^30..2^129
mpi_core_random_basic:0x40000000:"0200000000000000000000000000000000":0 mpi_core_random_basic:0x40000000:"0200000000000000000000000000000000":0
# Use the same data values for mpi_core_random_basic->NOT_ACCEPTABLE # 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 # and for mpi_XXX_random_values where we want to return NOT_ACCEPTABLE
# this isn't checked at runtime. # but this isn't checked at runtime.
MPI core random basic: 2^28-1..2^28 (NOT_ACCEPTABLE) MPI core random basic: 2^28-1..2^28+1 (NOT_ACCEPTABLE)
mpi_core_random_basic:0x0fffffff:"10000000":MBEDTLS_ERR_MPI_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 legacy=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE)
mpi_random_values:0x0fffffff:"10000000" mpi_legacy_random_values:0x0fffffff:"10000001"
MPI core random basic: 2^29-1..2^29 (NOT_ACCEPTABLE) MPI random mod=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE) (Mont)
mpi_core_random_basic:0x1fffffff:"20000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE mpi_mod_random_values:0x0fffffff:"10000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
MPI random legacy=core: 2^29-1..2^29 (NOT_ACCEPTABLE) MPI random mod=core: 2^28-1..2^28+1 (NOT_ACCEPTABLE) (canon)
mpi_random_values:0x1fffffff:"20000000" 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: 2^29-1..2^29+1 (NOT_ACCEPTABLE)
mpi_core_random_basic:0x3fffffff:"40000000":MBEDTLS_ERR_MPI_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 legacy=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE)
mpi_random_values:0x3fffffff:"40000000" mpi_legacy_random_values:0x1fffffff:"20000001"
MPI core random basic: 2^31-1..2^31 (NOT_ACCEPTABLE) MPI random mod=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE) (Mont)
mpi_core_random_basic:0x7fffffff:"80000000":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE mpi_mod_random_values:0x1fffffff:"20000001":MBEDTLS_MPI_MOD_REP_MONTGOMERY
MPI random legacy=core: 2^31-1..2^31 (NOT_ACCEPTABLE) MPI random mod=core: 2^29-1..2^29+1 (NOT_ACCEPTABLE) (canon)
mpi_random_values:0x7fffffff:"80000000" 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 in range: 1..2
mpi_random_many:1:"02":1000 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_fail:2:"000000000000000001":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
MPI random legacy=core: 0..1 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 legacy=core: 0..2
mpi_random_values:0:"02" mpi_legacy_random_values:0:"02"
MPI random legacy=core: 1..2 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 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 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 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 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

View File

@ -3,11 +3,44 @@
* functions. Due to the complexity of how these functions are tested, * 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 * 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. * 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/bignum.h"
#include "mbedtls/entropy.h" #include "mbedtls/entropy.h"
#include "bignum_core.h" #include "bignum_core.h"
#include "bignum_mod_raw.h"
#include "constant_time_internal.h" #include "constant_time_internal.h"
/* This test suite only manipulates non-negative bignums. */ /* This test suite only manipulates non-negative bignums. */
@ -110,7 +143,7 @@ exit:
/* END_CASE */ /* END_CASE */
/* BEGIN_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 */ /* Same RNG as in mpi_core_random_basic */
mbedtls_test_rnd_pseudo_info rnd_core = rnd_pseudo_seed; mbedtls_test_rnd_pseudo_info rnd_core = rnd_pseudo_seed;
@ -158,6 +191,77 @@ exit:
} }
/* END_CASE */ /* 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 */ /* BEGIN_CASE */
void mpi_random_many( int min, char *bound_hex, int iterations ) void mpi_random_many( int min, char *bound_hex, int iterations )
{ {
@ -311,6 +415,64 @@ exit:
} }
/* END_CASE */ /* 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 */ /* BEGIN_CASE */
void mpi_random_fail( int min, data_t *bound_bytes, int expected_ret ) void mpi_random_fail( int min, data_t *bound_bytes, int expected_ret )
{ {