diff --git a/library/bignum_core.c b/library/bignum_core.c index b401fa36cb..b3bb3bcb88 100644 --- a/library/bignum_core.c +++ b/library/bignum_core.c @@ -540,4 +540,17 @@ cleanup: return( ret ); } +void mbedtls_mpi_core_ct_uint_table_lookup( mbedtls_mpi_uint *dest, + const mbedtls_mpi_uint *table, + size_t limbs, + size_t count, + size_t index ) +{ + for( size_t i = 0; i < count; i++, table += limbs ) + { + unsigned char assign = mbedtls_ct_size_bool_eq( i, index ); + mbedtls_mpi_core_cond_assign( dest, table, limbs, assign ); + } +} + #endif /* MBEDTLS_BIGNUM_C */ diff --git a/library/bignum_core.h b/library/bignum_core.h index 9a5b89fc6e..ccccebbffa 100644 --- a/library/bignum_core.h +++ b/library/bignum_core.h @@ -452,4 +452,22 @@ void mbedtls_mpi_core_montmul( mbedtls_mpi_uint *X, int mbedtls_mpi_core_get_mont_r2_unsafe( mbedtls_mpi *X, const mbedtls_mpi *N ); +/** + * Copy an MPI from a table without leaking the index. + * + * \param dest The destination buffer. This must point to a writable + * buffer of at least \p limbs limbs. + * \param table The address of the table. This must point to a readable + * array of \p count elements of \p limbs limbs each. + * \param limbs The number of limbs in each table entry. + * \param count The number of entries in \p table. + * \param index The (secret) table index to look up. This must be in the + * range `0 .. count-1`. + */ +void mbedtls_mpi_core_ct_uint_table_lookup( mbedtls_mpi_uint *dest, + const mbedtls_mpi_uint *table, + size_t limbs, + size_t count, + size_t index ); + #endif /* MBEDTLS_BIGNUM_CORE_H */ diff --git a/scripts/mbedtls_dev/bignum_core.py b/scripts/mbedtls_dev/bignum_core.py index 0d238e714b..9929e13fa3 100644 --- a/scripts/mbedtls_dev/bignum_core.py +++ b/scripts/mbedtls_dev/bignum_core.py @@ -69,6 +69,43 @@ class BignumCoreShiftR(BignumCoreTarget, metaclass=ABCMeta): for count in counts: yield cls(input_hex, descr, count).create_test_case() +class BignumCoreCTLookup(BignumCoreTarget, metaclass=ABCMeta): + """Test cases for mbedtls_mpi_core_ct_uint_table_lookup().""" + test_function = "mpi_core_ct_uint_table_lookup" + test_name = "Constant time MPI table lookup" + + bitsizes = [ + (32, "One limb"), + (192, "Smallest curve sized"), + (512, "Largest curve sized"), + (2048, "Small FF/RSA sized"), + (4096, "Large FF/RSA sized"), + ] + + window_sizes = [0, 1, 2, 3, 4, 5, 6] + + def __init__(self, + bitsize: int, descr: str, window_size: int) -> None: + self.bitsize = bitsize + self.bitsize_description = descr + self.window_size = window_size + + def arguments(self) -> List[str]: + return [str(self.bitsize), str(self.window_size)] + + def description(self) -> str: + return '{} - {} MPI with {} bit window'.format( + BignumCoreCTLookup.test_name, + self.bitsize_description, + self.window_size + ) + + @classmethod + def generate_function_tests(cls) -> Iterator[test_case.TestCase]: + for bitsize, bitsize_description in cls.bitsizes: + for window_size in cls.window_sizes: + yield (cls(bitsize, bitsize_description, window_size) + .create_test_case()) class BignumCoreOperation(bignum_common.OperationCommon, BignumCoreTarget, metaclass=ABCMeta): #pylint: disable=abstract-method diff --git a/tests/suites/test_suite_bignum_core.function b/tests/suites/test_suite_bignum_core.function index 57c2f37387..d5d58d870b 100644 --- a/tests/suites/test_suite_bignum_core.function +++ b/tests/suites/test_suite_bignum_core.function @@ -941,3 +941,54 @@ exit: mbedtls_mpi_free( &RR_REF ); } /* END_CASE */ + +/* BEGIN_CASE */ +void mpi_core_ct_uint_table_lookup( int bitlen, int window_size ) +{ + size_t limbs = BITS_TO_LIMBS( bitlen ); + size_t count = ( (size_t) 1 ) << window_size; + + mbedtls_mpi_uint *table = NULL; + mbedtls_mpi_uint *dest = NULL; + + ASSERT_ALLOC( table, limbs * count ); + ASSERT_ALLOC( dest, limbs ); + + /* + * Fill the table with a unique counter so that differences are easily + * detected. (And have their relationship to the index relatively non-trivial just + * to be sure.) + */ + for( size_t i = 0; i < count * limbs; i++ ) + { + table[i] = ~i - 1; + } + + for( size_t i = 0; i < count; i++ ) + { + mbedtls_mpi_uint *current = table + i * limbs; + memset( dest, 0x00, limbs * sizeof( *dest ) ); + + /* + * We shouldn't leak anything through timing. + * We need to set these in every loop as we need to make the loop + * variable public for the loop head and the buffers for comparison. + */ + TEST_CF_SECRET( &i, sizeof( i ) ); + TEST_CF_SECRET( dest, limbs * sizeof( *dest ) ); + TEST_CF_SECRET( table, count * limbs * sizeof( *table ) ); + + mbedtls_mpi_core_ct_uint_table_lookup( dest, table, limbs, count, i ); + + TEST_CF_PUBLIC( dest, limbs * sizeof( *dest ) ); + TEST_CF_PUBLIC( table, count * limbs * sizeof( *table ) ); + ASSERT_COMPARE( dest, limbs * sizeof( *dest ), + current, limbs * sizeof( *current ) ); + TEST_CF_PUBLIC( &i, sizeof( i ) ); + } + +exit: + mbedtls_free(table); + mbedtls_free(dest); +} +/* END_CASE */