From cf7a80aa015a7554f03b9b569dbfc6b7ceb88869 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Sat, 13 Jun 2026 19:42:56 +0530 Subject: [PATCH 1/2] fix(mbedtls): Enable hardware CRT for RSA-4096 via base reduction Perform modulo reduction on the base before size checks to allow RSA-4096 CRT (2048-bit exponentiations) to use the hardware accelerator instead of falling back to software. Fix input validation, negative zero sign issues, and early memory cleanup paths in esp_mpi_exp_mod() --- components/mbedtls/port/bignum/esp_bignum.c | 63 ++++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/components/mbedtls/port/bignum/esp_bignum.c b/components/mbedtls/port/bignum/esp_bignum.c index 50b60bddebf..1f9645cef89 100644 --- a/components/mbedtls/port/bignum/esp_bignum.c +++ b/components/mbedtls/port/bignum/esp_bignum.c @@ -6,7 +6,7 @@ * * SPDX-License-Identifier: Apache-2.0 * - * SPDX-FileContributor: 2016-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2016-2026 Espressif Systems (Shanghai) CO LTD */ #include #include @@ -359,12 +359,44 @@ cleanup2: static int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M, mbedtls_mpi *_Rinv ) { int ret = 0; + mbedtls_mpi X_temp; + const mbedtls_mpi *X_ptr = X; mbedtls_mpi Rinv_new; /* used if _Rinv == NULL */ mbedtls_mpi *Rinv; /* points to _Rinv (if not NULL) otherwise &RR_new */ mbedtls_mpi_uint Mprime; - size_t x_words = mpi_words(X); + mbedtls_mpi_init(&X_temp); + mbedtls_mpi_init(&Rinv_new); + + /* Validate modulus M and exponent Y first to avoid passing invalid inputs to reduction */ + if (mbedtls_mpi_cmp_int(M, 0) <= 0 || (M->MBEDTLS_PRIVATE(p[0]) & 1) == 0) { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + + if (mbedtls_mpi_cmp_int(Y, 0) < 0) { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + + if (mbedtls_mpi_cmp_int(Y, 0) == 0) { + ret = mbedtls_mpi_lset(Z, 1); + goto cleanup; + } + + /* Perform base reduction if absolute value of base X is larger than modulus M */ + if (mbedtls_mpi_cmp_abs(X, M) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&X_temp, X)); + X_temp.MBEDTLS_PRIVATE(s) = 1; + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&X_temp, &X_temp, M)); + if (mbedtls_mpi_cmp_int(&X_temp, 0) != 0) { + X_temp.MBEDTLS_PRIVATE(s) = X->MBEDTLS_PRIVATE(s); + } + X_ptr = &X_temp; + } + + size_t x_words = mpi_words(X_ptr); size_t y_words = mpi_words(Y); size_t m_words = mpi_words(M); @@ -374,25 +406,13 @@ static int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_ size_t num_words = mpi_hal_calc_hardware_words(MAX(m_words, MAX(x_words, y_words))); if (num_words * 32 > SOC_RSA_MAX_BIT_LEN) { - return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - } - - if (mbedtls_mpi_cmp_int(M, 0) <= 0 || (M->MBEDTLS_PRIVATE(p[0]) & 1) == 0) { - return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; - } - - if (mbedtls_mpi_cmp_int(Y, 0) < 0) { - return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; - } - - if (mbedtls_mpi_cmp_int(Y, 0) == 0) { - return mbedtls_mpi_lset(Z, 1); + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; } /* Determine RR pointer, either _RR for cached value or local RR_new */ if (_Rinv == NULL) { - mbedtls_mpi_init(&Rinv_new); Rinv = &Rinv_new; } else { Rinv = _Rinv; @@ -405,7 +425,7 @@ static int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_ // Montgomery exponentiation: Z = X ^ Y mod M (HAC 14.94) #ifdef ESP_MPI_USE_MONT_EXP - ret = mpi_montgomery_exp_calc(Z, X, Y, M, Rinv, num_words, Mprime) ; + ret = mpi_montgomery_exp_calc(Z, X_ptr, Y, M, Rinv, num_words, Mprime) ; MBEDTLS_MPI_CHK(ret); #else esp_mpi_enable_hardware_hw_op(); @@ -418,7 +438,7 @@ static int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_ } #endif - esp_mpi_exp_mpi_mod_hw_op(X, Y, M, Rinv, Mprime, num_words); + esp_mpi_exp_mpi_mod_hw_op(X_ptr, Y, M, Rinv, Mprime, num_words); ret = mbedtls_mpi_grow(Z, m_words); if (ret != 0) { esp_mpi_disable_hardware_hw_op(); @@ -440,7 +460,7 @@ static int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_ #endif // Compensate for negative X - if (X->MBEDTLS_PRIVATE(s) == -1 && (Y->MBEDTLS_PRIVATE(p[0]) & 1) != 0) { + if (X_ptr->MBEDTLS_PRIVATE(s) == -1 && (Y->MBEDTLS_PRIVATE(p[0]) & 1) != 0) { Z->MBEDTLS_PRIVATE(s) = -1; MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(Z, M, Z)); } else { @@ -448,9 +468,8 @@ static int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_ } cleanup: - if (_Rinv == NULL) { - mbedtls_mpi_free(&Rinv_new); - } + mbedtls_mpi_free(&Rinv_new); + mbedtls_mpi_free(&X_temp); return ret; } From 66752f0942420f83d25a701318fa7400d2c98f62 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Mon, 15 Jun 2026 11:39:01 +0530 Subject: [PATCH 2/2] fix(mbedtls): Fix cached Rinv size mismatch under private exponent blinding Prevent signature verification failures on targets that do not round hardware words to 16-word boundaries (e.g. ESP32-S3, ESP32-C6, and ESP32-P4), where exponent blinding can cause `num_words` to vary between calls, leading to reuse of an incorrectly sized cached `Rinv`. --- components/mbedtls/port/bignum/esp_bignum.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/mbedtls/port/bignum/esp_bignum.c b/components/mbedtls/port/bignum/esp_bignum.c index 1f9645cef89..42392c6e844 100644 --- a/components/mbedtls/port/bignum/esp_bignum.c +++ b/components/mbedtls/port/bignum/esp_bignum.c @@ -134,6 +134,7 @@ static int calculate_rinv(mbedtls_mpi *Rinv, const mbedtls_mpi *M, int num_words mbedtls_mpi_init(&RR); MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&RR, num_bits * 2, 1)); MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(Rinv, &RR, M)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shrink(Rinv, num_words)); cleanup: mbedtls_mpi_free(&RR); @@ -417,7 +418,10 @@ static int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_ } else { Rinv = _Rinv; } - if (Rinv->MBEDTLS_PRIVATE(p) == NULL) { + /* Rinv depends on num_words, which may vary with blinded exponents. + calculate_rinv() stores Rinv with exactly num_words limbs, so the + allocation size is used here as the cache tag. */ + if (Rinv->MBEDTLS_PRIVATE(p) == NULL || Rinv->MBEDTLS_PRIVATE(n) != num_words) { MBEDTLS_MPI_CHK(calculate_rinv(Rinv, M, num_words)); }