From ce3f3485a121c100f58f36d700cb35b060f6e866 Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Tue, 14 Jul 2026 16:26:44 +0800 Subject: [PATCH] fix(bignum): make constant-time prime sieve opt-in via config macro mbedtls 4.1.1 replaced the trial-division small-factor test in prime generation with a constant-time GCD against the 1380-bit small-primes product (same change as upstream mbedtls commit 7a29052238 in 3.6.7). The GCD runs a fixed (A_limbs + N_limbs) * biL iterations per prime candidate, making RSA key generation ~5x slower on 64-bit hosts and ~10x slower on ESP chips, and its non-yielding software loop starves the FreeRTOS idle task, tripping the task watchdog. Restore the pre-3.6.7 variable-time trial division and keep the constant-time implementation available behind the new MBEDTLS_MPI_PRIME_SIEVE_VARIABLE_TIME macro (defined by ESP-IDF's esp_config.h unless CONFIG_MBEDTLS_CONSTANT_TIME_PRIME_GEN is enabled). The trial-division path keeps the 4.x input contract (X <= 997 returns MBEDTLS_ERR_MPI_BAD_INPUT_DATA). Builds without the macro keep upstream behavior unchanged. --- tf-psa-crypto/drivers/builtin/src/bignum.c | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tf-psa-crypto/drivers/builtin/src/bignum.c b/tf-psa-crypto/drivers/builtin/src/bignum.c index d7ba1dd755..28078aef20 100644 --- a/tf-psa-crypto/drivers/builtin/src/bignum.c +++ b/tf-psa-crypto/drivers/builtin/src/bignum.c @@ -2085,6 +2085,90 @@ int mbedtls_mpi_inv_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi * value of nbits. */ static const mbedtls_mpi_sint small_primes_limit = 997; + +#if defined(MBEDTLS_MPI_PRIME_SIEVE_VARIABLE_TIME) +/* Use the variable-time trial-division small-factor test from mbedtls 3.6.6 + * instead of the constant-time GCD against the small-primes product. + * + * The constant-time GCD always runs (A_limbs + N_limbs) * biL iterations of + * full-width limb operations and is executed for every prime candidate + * (hundreds per generated prime), making RSA key generation an order of + * magnitude slower on 32-bit MCUs. It also runs entirely in software, so on + * targets where modular exponentiation is offloaded to hardware the CPU is + * kept busy for seconds without yielding, starving the idle task. + * + * The timing side channel closed upstream (variable-latency division leaking + * candidate-dependent information) requires an attacker able to observe the + * timing of individual operations during key generation, which is outside + * the threat model of devices that run no untrusted co-resident code. + */ + +/* Gaps between primes, starting at 3. https://oeis.org/A001223 */ +static const unsigned char small_prime_gaps[] = { + 2, 2, 4, 2, 4, 2, 4, 6, + 2, 6, 4, 2, 4, 6, 6, 2, + 6, 4, 2, 6, 4, 6, 8, 4, + 2, 4, 2, 4, 14, 4, 6, 2, + 10, 2, 6, 6, 4, 6, 6, 2, + 10, 2, 4, 2, 12, 12, 4, 2, + 4, 6, 2, 10, 6, 6, 6, 2, + 6, 4, 2, 10, 14, 4, 2, 4, + 14, 6, 10, 2, 4, 6, 8, 6, + 6, 4, 6, 8, 4, 8, 10, 2, + 10, 2, 6, 4, 6, 8, 4, 2, + 4, 12, 8, 4, 8, 4, 6, 12, + 2, 18, 6, 10, 6, 6, 2, 6, + 10, 6, 6, 2, 6, 6, 4, 2, + 12, 10, 2, 4, 6, 6, 2, 12, + 4, 6, 8, 10, 8, 10, 8, 6, + 6, 4, 8, 6, 4, 8, 4, 14, + 10, 12, 2, 10, 2, 4, 2, 10, + 14, 4, 2, 4, 14, 4, 2, 4, + 20, 4, 8, 10, 8, 4, 6, 6, + 14, 4, 6, 6, 8, 6, /*reaches 997*/ + 0 /* the last entry is effectively unused */ +}; + +/* + * Small divisors test (X must be positive) + * + * Return values: + * 0: no small factor (possible prime, more tests needed) + * MBEDTLS_ERR_MPI_NOT_ACCEPTABLE: certain non-prime + * MBEDTLS_ERR_MPI_BAD_INPUT_DATA: input too small + * other negative: error + */ +static int mpi_check_small_factors(const mbedtls_mpi *X) +{ + int ret = 0; + size_t i; + mbedtls_mpi_uint r; + unsigned p = 3; /* The first odd prime */ + + /* Same input contract as the constant-time implementation below: + * inputs no larger than the largest tested prime are rejected. */ + if (mbedtls_mpi_cmp_int(X, small_primes_limit) <= 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if ((X->p[0] & 1) == 0) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + + /* X > small_primes_limit, so any zero remainder proves X composite. */ + for (i = 0; i < sizeof(small_prime_gaps); p += small_prime_gaps[i], i++) { + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_int(&r, X, p)); + if (r == 0) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + } + +cleanup: + return ret; +} + +#else /* MBEDTLS_MPI_PRIME_SIEVE_VARIABLE_TIME */ + /* Product of small primes up to small_primes_limit included */ static const mbedtls_mpi_uint small_primes_product_limbs[] = { MBEDTLS_BYTES_TO_T_UINT_8(0x4b, 0x13, 0x6a, 0x97, 0xbb, 0xd0, 0xdf, 0x95), @@ -2161,6 +2245,8 @@ cleanup: return ret; } +#endif /* MBEDTLS_MPI_PRIME_SIEVE_VARIABLE_TIME */ + /* * Miller-Rabin pseudo-primality test (HAC 4.24) */