fix(bignum): make constant-time prime sieve opt-in via config macro

mbedtls 3.6.7 replaced the trial-division small-factor test in prime
generation with a constant-time GCD against the 1380-bit small-primes
product (upstream commit 7a29052238). 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
(RSA-2048: seconds -> ~86 s on ESP32-S3), 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).
Builds without the macro keep upstream behavior unchanged.
This commit is contained in:
Ashish Sharma
2026-07-14 16:14:31 +08:00
parent 64c8e14bff
commit 2b96dd8eeb
+82
View File
@@ -2089,6 +2089,86 @@ int mbedtls_mpi_inv_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi
#if defined(MBEDTLS_GENPRIME)
#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)
* 1: certain prime
* MBEDTLS_ERR_MPI_NOT_ACCEPTABLE: certain non-prime
* 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 */
if ((X->p[0] & 1) == 0) {
return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE;
}
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) {
if (mbedtls_mpi_cmp_int(X, p) == 0) {
return 1;
} else {
return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE;
}
}
}
cleanup:
return ret;
}
#else /* MBEDTLS_MPI_PRIME_SIEVE_VARIABLE_TIME */
static const mbedtls_mpi_sint small_primes_limit = 997;
/* Product of small primes up to small_primes_limit included */
static const mbedtls_mpi_uint small_primes_product_limbs[] = {
@@ -2175,6 +2255,8 @@ cleanup:
return ret;
}
#endif /* MBEDTLS_MPI_PRIME_SIEVE_VARIABLE_TIME */
/*
* Miller-Rabin pseudo-primality test (HAC 4.24)
*/