diff --git a/library/bignum.c b/library/bignum.c index d3a1b00d52..2421c1a3ec 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -1136,7 +1136,8 @@ int mbedtls_mpi_mul_mpi(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi MPI_VALIDATE_RET(A != NULL); MPI_VALIDATE_RET(B != NULL); - mbedtls_mpi_init(&TA); mbedtls_mpi_init(&TB); + mbedtls_mpi_init(&TA); + mbedtls_mpi_init(&TB); if (X == A) { MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TA, A)); A = &TA; @@ -1166,13 +1167,7 @@ int mbedtls_mpi_mul_mpi(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, i + j)); MBEDTLS_MPI_CHK(mbedtls_mpi_lset(X, 0)); - for (size_t k = 0; k < j; k++) { - /* We know that there cannot be any carry-out since we're - * iterating from bottom to top. */ - (void) mbedtls_mpi_core_mla(X->p + k, i + 1, - A->p, i, - B->p[k]); - } + mbedtls_mpi_core_mul(X->p, A->p, i, B->p, j); /* If the result is 0, we don't shortcut the operation, which reduces * but does not eliminate side channels leaking the zero-ness. We do diff --git a/library/bignum_core.c b/library/bignum_core.c index e50f043c51..1ba4142c7e 100644 --- a/library/bignum_core.c +++ b/library/bignum_core.c @@ -448,6 +448,17 @@ mbedtls_mpi_uint mbedtls_mpi_core_mla(mbedtls_mpi_uint *d, size_t d_len, return c; } +void mbedtls_mpi_core_mul(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, size_t A_limbs, + const mbedtls_mpi_uint *B, size_t B_limbs) +{ + memset(X, 0, (A_limbs + B_limbs) * ciL); + + for (size_t i = 0; i < B_limbs; i++) { + (void) mbedtls_mpi_core_mla(X + i, A_limbs + 1, A, A_limbs, B[i]); + } +} + /* * Fast Montgomery initialization (thanks to Tom St Denis). */ diff --git a/library/bignum_core.h b/library/bignum_core.h index 05bc923d27..7a0311ad17 100644 --- a/library/bignum_core.h +++ b/library/bignum_core.h @@ -398,6 +398,26 @@ mbedtls_mpi_uint mbedtls_mpi_core_mla(mbedtls_mpi_uint *X, size_t X_limbs, const mbedtls_mpi_uint *A, size_t A_limbs, mbedtls_mpi_uint b); +/** + * \brief Perform a known-size multiplication + * + * \p X may not be aliased to any of the inputs for this function. + * \p A may be aliased to \p B. + * + * \param[out] X The pointer to the (little-endian) array to receive + * the product of \p A_limbs and \p B_limbs. + * This must be of length \p A_limbs + \p B_limbs. + * \param[in] A The pointer to the (little-endian) array + * representing the first factor. + * \param A_limbs The number of limbs in \p A. + * \param[in] B The pointer to the (little-endian) array + * representing the second factor. + * \param B_limbs The number of limbs in \p B. + */ +void mbedtls_mpi_core_mul(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, size_t A_limbs, + const mbedtls_mpi_uint *B, size_t B_limbs); + /** * \brief Calculate initialisation value for fast Montgomery modular * multiplication diff --git a/scripts/mbedtls_dev/bignum_common.py b/scripts/mbedtls_dev/bignum_common.py index 5319ec68b2..aa2cd25886 100644 --- a/scripts/mbedtls_dev/bignum_common.py +++ b/scripts/mbedtls_dev/bignum_common.py @@ -68,7 +68,8 @@ def bound_mpi_limbs(limbs: int, bits_in_limb: int) -> int: def limbs_mpi(val: int, bits_in_limb: int) -> int: """Return the number of limbs required to store value.""" - return (val.bit_length() + bits_in_limb - 1) // bits_in_limb + bit_length = max(val.bit_length(), 1) + return (bit_length + bits_in_limb - 1) // bits_in_limb def combination_pairs(values: List[T]) -> List[Tuple[T, T]]: """Return all pair combinations from input values.""" diff --git a/scripts/mbedtls_dev/bignum_core.py b/scripts/mbedtls_dev/bignum_core.py index 24d37cbc72..e914ae72d2 100644 --- a/scripts/mbedtls_dev/bignum_core.py +++ b/scripts/mbedtls_dev/bignum_core.py @@ -230,6 +230,31 @@ class BignumCoreMLA(BignumCoreTarget, bignum_common.OperationCommon): yield cur_op.create_test_case() +class BignumCoreMul(BignumCoreTarget, bignum_common.OperationCommon): + """Test cases for bignum core multiplication.""" + count = 0 + input_style = "arch_split" + symbol = "*" + test_function = "mpi_core_mul" + test_name = "mbedtls_mpi_core_mul" + arity = 2 + unique_combinations_only = True + + def format_arg(self, val: str) -> str: + return val + + def format_result(self, res: int) -> str: + res_str = '{:x}'.format(res) + a_limbs = bignum_common.limbs_mpi(self.int_a, self.bits_in_limb) + b_limbs = bignum_common.limbs_mpi(self.int_b, self.bits_in_limb) + hex_digits = bignum_common.hex_digits_for_limb(a_limbs + b_limbs, self.bits_in_limb) + return bignum_common.quote_str(self.format_arg(res_str).zfill(hex_digits)) + + def result(self) -> List[str]: + result = self.int_a * self.int_b + return [self.format_result(result)] + + class BignumCoreMontmul(BignumCoreTarget, test_data_generation.BaseTest): """Test cases for Montgomery multiplication.""" count = 0 diff --git a/tests/suites/test_suite_bignum_core.function b/tests/suites/test_suite_bignum_core.function index 408eb0be4f..2f87ea959f 100644 --- a/tests/suites/test_suite_bignum_core.function +++ b/tests/suites/test_suite_bignum_core.function @@ -1057,6 +1057,72 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void mpi_core_mul(char *input_A, + char *input_B, + char *result) +{ + mbedtls_mpi_uint *A = NULL; + mbedtls_mpi_uint *A_orig = NULL; + mbedtls_mpi_uint *B = NULL; + mbedtls_mpi_uint *B_orig = NULL; + mbedtls_mpi_uint *R = NULL; + mbedtls_mpi_uint *X = NULL; + size_t A_limbs, B_limbs, R_limbs; + + TEST_EQUAL(mbedtls_test_read_mpi_core(&A, &A_limbs, input_A), 0); + TEST_EQUAL(mbedtls_test_read_mpi_core(&B, &B_limbs, input_B), 0); + TEST_EQUAL(mbedtls_test_read_mpi_core(&R, &R_limbs, result), 0); + + TEST_EQUAL(R_limbs, A_limbs + B_limbs); + + const size_t X_limbs = A_limbs + B_limbs; + const size_t X_bytes = X_limbs * sizeof(mbedtls_mpi_uint); + ASSERT_ALLOC(X, X_limbs); + + const size_t A_bytes = A_limbs * sizeof(mbedtls_mpi_uint); + ASSERT_ALLOC(A_orig, A_limbs); + memcpy(A_orig, A, A_bytes); + + const size_t B_bytes = B_limbs * sizeof(mbedtls_mpi_uint); + ASSERT_ALLOC(B_orig, B_limbs); + memcpy(B_orig, B, B_bytes); + + /* Set result to something that is unlikely to be correct */ + memset(X, '!', X_bytes); + + /* 1. X = A * B - result should be correct, A and B unchanged */ + mbedtls_mpi_core_mul(X, A, A_limbs, B, B_limbs); + ASSERT_COMPARE(X, X_bytes, R, X_bytes); + ASSERT_COMPARE(A, A_bytes, A_orig, A_bytes); + ASSERT_COMPARE(B, B_bytes, B_orig, B_bytes); + + /* 2. A == B: alias A and B - result should be correct, A and B unchanged */ + if (A_bytes == B_bytes && memcmp(A, B, A_bytes) == 0) { + memset(X, '!', X_bytes); + mbedtls_mpi_core_mul(X, A, A_limbs, A, A_limbs); + ASSERT_COMPARE(X, X_bytes, R, X_bytes); + ASSERT_COMPARE(A, A_bytes, A_orig, A_bytes); + } + /* 3. X = B * A - result should be correct, A and B unchanged */ + else { + memset(X, '!', X_bytes); + mbedtls_mpi_core_mul(X, B, B_limbs, A, A_limbs); + ASSERT_COMPARE(X, X_bytes, R, X_bytes); + ASSERT_COMPARE(A, A_bytes, A_orig, A_bytes); + ASSERT_COMPARE(B, B_bytes, B_orig, B_bytes); + } + +exit: + mbedtls_free(A); + mbedtls_free(A_orig); + mbedtls_free(B); + mbedtls_free(B_orig); + mbedtls_free(R); + mbedtls_free(X); +} +/* END_CASE */ + /* BEGIN MERGE SLOT 1 */ /* BEGIN_CASE */