From a56a05b0152f4f669d7f1ece9271ed23be34c944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 10 Jul 2025 21:40:15 +0200 Subject: [PATCH 01/12] RSA: use CT gcd-modinv in prepare_blinding() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While at it, draw the blinding value uniformly in the permissible range. Signed-off-by: Manuel Pégourié-Gonnard --- library/rsa.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 557faaf363..b7df690fb5 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -1304,33 +1304,16 @@ static int rsa_prepare_blinding(mbedtls_rsa_context *ctx, } /* Unblinding value: Vf = random number, invertible mod N */ + mbedtls_mpi_lset(&R, 0); do { if (count++ > 10) { ret = MBEDTLS_ERR_RSA_RNG_FAILED; goto cleanup; } - MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&ctx->Vf, ctx->len - 1, f_rng, p_rng)); - - /* Compute Vf^-1 as R * (R Vf)^-1 to avoid leaks from inv_mod. */ - MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&R, ctx->len - 1, f_rng, p_rng)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vi, &ctx->Vf, &R)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vi, &ctx->Vi, &ctx->N)); - - /* At this point, Vi is invertible mod N if and only if both Vf and R - * are invertible mod N. If one of them isn't, we don't need to know - * which one, we just loop and choose new values for both of them. - * (Each iteration succeeds with overwhelming probability.) */ - ret = mbedtls_mpi_inv_mod(&ctx->Vi, &ctx->Vi, &ctx->N); - if (ret != 0 && ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { - goto cleanup; - } - - } while (ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE); - - /* Finish the computation of Vf^-1 = R * (R Vf)^-1 */ - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vi, &ctx->Vi, &R)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vi, &ctx->Vi, &ctx->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_random(&ctx->Vf, 1, &ctx->N, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(&R, &ctx->Vi, &ctx->Vf, &ctx->N)); + } while (mbedtls_mpi_cmp_int(&R, 1) != 0); /* Blinding value: Vi = Vf^(-e) mod N * (Vi already contains Vf^-1 at this point) */ From c2d210ea0db99bb30bfc574537b223e8573df53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 10 Jul 2025 21:48:41 +0200 Subject: [PATCH 02/12] DHM: use CT modinv for blinding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/dhm.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/library/dhm.c b/library/dhm.c index bcc07f5441..3c65bc8076 100644 --- a/library/dhm.c +++ b/library/dhm.c @@ -18,6 +18,7 @@ #if defined(MBEDTLS_DHM_C) #include "mbedtls/dhm.h" +#include "bignum_internal.h" #include "mbedtls/platform_util.h" #include "mbedtls/error.h" @@ -381,16 +382,8 @@ static int dhm_update_blinding(mbedtls_dhm_context *ctx, /* Vi = random( 2, P-2 ) */ MBEDTLS_MPI_CHK(dhm_random_below(&ctx->Vi, &ctx->P, f_rng, p_rng)); - /* Vf = Vi^-X mod P - * First compute Vi^-1 = R * (R Vi)^-1, (avoiding leaks from inv_mod), - * then elevate to the Xth power. */ - MBEDTLS_MPI_CHK(dhm_random_below(&R, &ctx->P, f_rng, p_rng)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vi, &R)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->P)); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&ctx->Vf, &ctx->Vf, &ctx->P)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vf, &R)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->P)); - + /* Vf = Vi^-X mod P */ + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, &ctx->Vf, &ctx->Vi, &ctx->P)); MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->Vf, &ctx->Vf, &ctx->X, &ctx->P, &ctx->RP)); cleanup: From f35d30799cc9ec07d46265d31384139a6bb6c09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 10 Jul 2025 21:54:38 +0200 Subject: [PATCH 03/12] ECP: use CT modinv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A function that was previously called in multiple places is now called only once, hence more susceptible to being inlined, hence the test fix. Signed-off-by: Manuel Pégourié-Gonnard --- library/ecp.c | 30 ++----------------- .../components-configuration-crypto.sh | 3 +- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/library/ecp.c b/library/ecp.c index fdd00a59c5..6af516c0ac 100644 --- a/library/ecp.c +++ b/library/ecp.c @@ -68,6 +68,7 @@ #include "mbedtls/error.h" #include "bn_mul.h" +#include "bignum_internal.h" #include "ecp_invasive.h" #include @@ -1173,7 +1174,7 @@ cleanup: MBEDTLS_MPI_CHK(mbedtls_mpi_mul_int_mod(grp, X, A, c)) #define MPI_ECP_INV(dst, src) \ - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod((dst), (src), &grp->P)) + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, (dst), (src), &grp->P)) #define MPI_ECP_MOV(X, A) \ MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, A)) @@ -2201,21 +2202,6 @@ static int ecp_mul_comb_after_precomp(const mbedtls_ecp_group *grp, final_norm: MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_INV); #endif - /* - * Knowledge of the jacobian coordinates may leak the last few bits of the - * scalar [1], and since our MPI implementation isn't constant-flow, - * inversion (used for coordinate normalization) may leak the full value - * of its input via side-channels [2]. - * - * [1] https://eprint.iacr.org/2003/191 - * [2] https://eprint.iacr.org/2020/055 - * - * Avoid the leak by randomizing coordinates before we normalize them. - */ - if (f_rng != 0) { - MBEDTLS_MPI_CHK(ecp_randomize_jac(grp, RR, f_rng, p_rng)); - } - MBEDTLS_MPI_CHK(ecp_normalize_jac(grp, RR)); #if defined(MBEDTLS_ECP_RESTARTABLE) @@ -2594,18 +2580,6 @@ static int ecp_mul_mxz(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, MPI_ECP_COND_SWAP(&R->Z, &RP.Z, b); } - /* - * Knowledge of the projective coordinates may leak the last few bits of the - * scalar [1], and since our MPI implementation isn't constant-flow, - * inversion (used for coordinate normalization) may leak the full value - * of its input via side-channels [2]. - * - * [1] https://eprint.iacr.org/2003/191 - * [2] https://eprint.iacr.org/2020/055 - * - * Avoid the leak by randomizing coordinates before we normalize them. - */ - MBEDTLS_MPI_CHK(ecp_randomize_mxz(grp, R, f_rng, p_rng)); MBEDTLS_MPI_CHK(ecp_normalize_mxz(grp, R)); cleanup: diff --git a/tests/scripts/components-configuration-crypto.sh b/tests/scripts/components-configuration-crypto.sh index 04c38f6c36..11a37059c3 100644 --- a/tests/scripts/components-configuration-crypto.sh +++ b/tests/scripts/components-configuration-crypto.sh @@ -1293,7 +1293,8 @@ common_test_psa_crypto_config_accel_ecc_some_curves () { ALG_SHA3_224 ALG_SHA3_256 ALG_SHA3_384 ALG_SHA3_512" helper_libtestdriver1_make_drivers "$loc_accel_list" "$loc_extra_list" - helper_libtestdriver1_make_main "$loc_accel_list" + # For grep to work below we need less inlining in ecp.c + ASAN_CFLAGS="$ASAN_CFLAGS -O0" helper_libtestdriver1_make_main "$loc_accel_list" # We expect ECDH to be re-enabled for the missing curves grep mbedtls_ecdh_ library/ecdh.o From 0d73de5ee0ec65ff8b60d073b7d1d369ee0a00e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 10 Jul 2025 22:59:39 +0200 Subject: [PATCH 04/12] ecdsa: use CT modinv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/ecdsa.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/library/ecdsa.c b/library/ecdsa.c index 2f7a996a7e..0d73c65425 100644 --- a/library/ecdsa.c +++ b/library/ecdsa.c @@ -17,6 +17,7 @@ #include "mbedtls/ecdsa.h" #include "mbedtls/asn1write.h" +#include "bignum_internal.h" #include @@ -340,21 +341,11 @@ modn: MBEDTLS_MPI_CHK(derive_mpi(grp, &e, buf, blen)); /* - * Generate a random value to blind inv_mod in next step, - * avoiding a potential timing leak. - */ - MBEDTLS_MPI_CHK(mbedtls_ecp_gen_privkey(grp, &t, f_rng_blind, - p_rng_blind)); - - /* - * Step 6: compute s = (e + r * d) / k = t (e + rd) / (kt) mod n + * Step 6: compute s = (e + r * d) / k */ MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(s, pr, d)); MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&e, &e, s)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&e, &e, &t)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(pk, pk, &t)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(pk, pk, &grp->N)); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(s, pk, &grp->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, s, pk, &grp->N)); MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(s, s, &e)); MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(s, s, &grp->N)); } while (mbedtls_mpi_cmp_int(s, 0) == 0); @@ -540,7 +531,7 @@ int mbedtls_ecdsa_verify_restartable(mbedtls_ecp_group *grp, */ ECDSA_BUDGET(MBEDTLS_ECP_OPS_CHK + MBEDTLS_ECP_OPS_INV + 2); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&s_inv, s, &grp->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, &s_inv, s, &grp->N)); MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(pu1, &e, &s_inv)); MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(pu1, pu1, &grp->N)); From 7dcfd7373170ef718863c0c61cda4ff3e5e25299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 10 Jul 2025 09:57:29 +0200 Subject: [PATCH 05/12] RSA: use constant-time GCD in deduce_primes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/rsa_alt_helpers.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/rsa_alt_helpers.c b/library/rsa_alt_helpers.c index 5c265a9921..feb7874b8b 100644 --- a/library/rsa_alt_helpers.c +++ b/library/rsa_alt_helpers.c @@ -12,6 +12,7 @@ #include "mbedtls/rsa.h" #include "mbedtls/bignum.h" +#include "bignum_internal.h" #include "rsa_alt_helpers.h" /* @@ -117,7 +118,7 @@ int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&K, primes[attempt])); /* Check if gcd(K,N) = 1 */ - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(P, &K, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(P, NULL, &K, N)); if (mbedtls_mpi_cmp_int(P, 1) != 0) { continue; } @@ -136,7 +137,7 @@ int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, } MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&K, &K, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(P, &K, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(P, NULL, &K, N)); if (mbedtls_mpi_cmp_int(P, 1) == 1 && mbedtls_mpi_cmp_mpi(P, N) == -1) { From 630148e67f0cd3d337308321f72f444ca4ef1c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Wed, 13 Aug 2025 13:57:35 +0200 Subject: [PATCH 06/12] RSA: use constant-time modinv in deduce_crt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/bignum.c | 6 +++--- library/bignum_internal.h | 18 ++++++++++++++++++ library/rsa_alt_helpers.c | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/library/bignum.c b/library/bignum.c index f7ec35a9df..00aa79ca2f 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -1924,9 +1924,9 @@ int mbedtls_mpi_random(mbedtls_mpi *X, /* * Modular inverse: X = A^-1 mod N with N odd (and A any range) */ -static int mbedtls_mpi_inv_mod_odd(mbedtls_mpi *X, - const mbedtls_mpi *A, - const mbedtls_mpi *N) +int mbedtls_mpi_inv_mod_odd(mbedtls_mpi *X, + const mbedtls_mpi *A, + const mbedtls_mpi *N) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; mbedtls_mpi T, G; diff --git a/library/bignum_internal.h b/library/bignum_internal.h index f3f6fcbc8d..a947497007 100644 --- a/library/bignum_internal.h +++ b/library/bignum_internal.h @@ -80,4 +80,22 @@ int mbedtls_mpi_gcd_modinv_odd(mbedtls_mpi *G, const mbedtls_mpi *A, const mbedtls_mpi *N); +/** + * \brief Modular inverse: X = A^-1 mod N with N odd + * + * \param[out] X The inverse of \p A modulo \p N on success, + * indeterminate otherwise. + * \param[in] A The number to invert. + * \param[in] N The modulus. Must be odd and greater than 1. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if preconditions were not + * met. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if A is not invertible mod N. + */ +int mbedtls_mpi_inv_mod_odd(mbedtls_mpi *X, + const mbedtls_mpi *A, + const mbedtls_mpi *N); + #endif /* bignum_internal.h */ diff --git a/library/rsa_alt_helpers.c b/library/rsa_alt_helpers.c index feb7874b8b..d91949af12 100644 --- a/library/rsa_alt_helpers.c +++ b/library/rsa_alt_helpers.c @@ -245,7 +245,7 @@ int mbedtls_rsa_deduce_crt(const mbedtls_mpi *P, const mbedtls_mpi *Q, /* QP = Q^{-1} mod P */ if (QP != NULL) { - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(QP, Q, P)); + MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod_odd(QP, Q, P)); } cleanup: From a4bf680e92dc5be758a730daaad7bf6eac5d7353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 10 Jul 2025 10:48:23 +0200 Subject: [PATCH 07/12] RSA: refactor: avoid code duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/rsa.c | 35 ++++++++++++----------------------- library/rsa_alt_helpers.c | 5 ++++- library/rsa_alt_helpers.h | 13 ++++++++----- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index b7df690fb5..08267dbfce 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -1047,7 +1047,7 @@ int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, unsigned int nbits, int exponent) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - mbedtls_mpi H, G, L; + mbedtls_mpi H; int prime_quality = 0; /* @@ -1060,8 +1060,6 @@ int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, } mbedtls_mpi_init(&H); - mbedtls_mpi_init(&G); - mbedtls_mpi_init(&L); if (exponent < 3 || nbits % 2 != 0) { ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA; @@ -1099,35 +1097,28 @@ int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, mbedtls_mpi_swap(&ctx->P, &ctx->Q); } - /* Temporarily replace P,Q by P-1, Q-1 */ - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&ctx->P, &ctx->P, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&ctx->Q, &ctx->Q, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&H, &ctx->P, &ctx->Q)); - - /* check GCD( E, (P-1)*(Q-1) ) == 1 (FIPS 186-4 §B.3.1 criterion 2(a)) */ - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&G, &ctx->E, &H)); - if (mbedtls_mpi_cmp_int(&G, 1) != 0) { + /* Compute D = E^-1 mod LCM(P-1, Q-1) (FIPS 186-4 §B.3.1 criterion 3(b)) + * if it exists (FIPS 186-4 §B.3.1 criterion 2(a)) */ + ret = mbedtls_rsa_deduce_private_exponent(&ctx->P, &ctx->Q, &ctx->E, &ctx->D); + if (ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { + mbedtls_mpi_lset(&ctx->D, 0); /* needed for the next call */ continue; } + if (ret != 0) { + goto cleanup; + } - /* compute smallest possible D = E^-1 mod LCM(P-1, Q-1) (FIPS 186-4 §B.3.1 criterion 3(b)) */ - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&G, &ctx->P, &ctx->Q)); - MBEDTLS_MPI_CHK(mbedtls_mpi_div_mpi(&L, NULL, &H, &G)); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&ctx->D, &ctx->E, &L)); - - if (mbedtls_mpi_bitlen(&ctx->D) <= ((nbits + 1) / 2)) { // (FIPS 186-4 §B.3.1 criterion 3(a)) + /* (FIPS 186-4 §B.3.1 criterion 3(a)) */ + if (mbedtls_mpi_bitlen(&ctx->D) <= ((nbits + 1) / 2)) { continue; } break; } while (1); - /* Restore P,Q */ - MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&ctx->P, &ctx->P, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&ctx->Q, &ctx->Q, 1)); + /* N = P * Q */ MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->N, &ctx->P, &ctx->Q)); - ctx->len = mbedtls_mpi_size(&ctx->N); #if !defined(MBEDTLS_RSA_NO_CRT) @@ -1146,8 +1137,6 @@ int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, cleanup: mbedtls_mpi_free(&H); - mbedtls_mpi_free(&G); - mbedtls_mpi_free(&L); if (ret != 0) { mbedtls_rsa_free(ctx); diff --git a/library/rsa_alt_helpers.c b/library/rsa_alt_helpers.c index d91949af12..08adbe3eb8 100644 --- a/library/rsa_alt_helpers.c +++ b/library/rsa_alt_helpers.c @@ -212,7 +212,10 @@ int mbedtls_rsa_deduce_private_exponent(mbedtls_mpi const *P, MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&K, &K, &L)); MBEDTLS_MPI_CHK(mbedtls_mpi_div_mpi(&K, NULL, &K, D)); - /* Compute modular inverse of E in LCM(P-1, Q-1) */ + /* Compute modular inverse of E mod LCM(P-1, Q-1) + * This is FIPS 186-4 §B.3.1 criterion 3(b). + * This will return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if E is not coprime to + * (P-1)(Q-1), also validating FIPS 186-4 §B.3.1 criterion 2(a). */ MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(D, E, &K)); cleanup: diff --git a/library/rsa_alt_helpers.h b/library/rsa_alt_helpers.h index 052b02491e..580ffb2fba 100644 --- a/library/rsa_alt_helpers.h +++ b/library/rsa_alt_helpers.h @@ -89,12 +89,15 @@ int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, mbedtls_mpi const *E, * \param P First prime factor of RSA modulus * \param Q Second prime factor of RSA modulus * \param E RSA public exponent - * \param D Pointer to MPI holding the private exponent on success. + * \param D Pointer to MPI holding the private exponent on success, + * ie the modular inverse of E modulo LCM(P-1,Q-1). * - * \return - * - 0 if successful. In this case, D is set to a simultaneous - * modular inverse of E modulo both P-1 and Q-1. - * - A non-zero error code otherwise. + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if E is not coprime to P-1 + * and Q-1, that is, if GCD( E, (P-1)*(Q-1) ) != 1. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if inputs are otherwise + * invalid. * * \note This function does not check whether P and Q are primes. * From 9e1c532847af26ea6504720c6755b668b4b1d0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Wed, 13 Aug 2025 14:14:19 +0200 Subject: [PATCH 08/12] RSA: use CT gcd-modinv in deduce_private_exponent() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/bignum.c | 6 +++--- library/bignum_internal.h | 20 ++++++++++++++++++++ library/rsa_alt_helpers.c | 6 +++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/library/bignum.c b/library/bignum.c index 00aa79ca2f..f6b8f99981 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -1963,9 +1963,9 @@ cleanup: * * Return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the inverse doesn't exist. */ -static int mbedtls_mpi_inv_mod_even_in_range(mbedtls_mpi *X, - mbedtls_mpi const *A, - mbedtls_mpi const *N) +int mbedtls_mpi_inv_mod_even_in_range(mbedtls_mpi *X, + mbedtls_mpi const *A, + mbedtls_mpi const *N) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; mbedtls_mpi I, G; diff --git a/library/bignum_internal.h b/library/bignum_internal.h index a947497007..341346222d 100644 --- a/library/bignum_internal.h +++ b/library/bignum_internal.h @@ -98,4 +98,24 @@ int mbedtls_mpi_inv_mod_odd(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *N); +/** + * \brief Modular inverse: X = A^-1 mod N with N even, + * A odd and 1 < A < N. + * + * \param[out] X The inverse of \p A modulo \p N on success, + * indeterminate otherwise. + * \param[in] A The number to invert. Must be odd, greated than 1 + * and less than \p N. + * \param[in] N The modulus. Must be even and greater than 1. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if preconditions were not + * met. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if A is not invertible mod N. + */ +int mbedtls_mpi_inv_mod_even_in_range(mbedtls_mpi *X, + mbedtls_mpi const *A, + mbedtls_mpi const *N); + #endif /* bignum_internal.h */ diff --git a/library/rsa_alt_helpers.c b/library/rsa_alt_helpers.c index 08adbe3eb8..50a5c4e0d7 100644 --- a/library/rsa_alt_helpers.c +++ b/library/rsa_alt_helpers.c @@ -198,6 +198,10 @@ int mbedtls_rsa_deduce_private_exponent(mbedtls_mpi const *P, return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; } + if (mbedtls_mpi_get_bit(E, 0) != 1) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + mbedtls_mpi_init(&K); mbedtls_mpi_init(&L); @@ -216,7 +220,7 @@ int mbedtls_rsa_deduce_private_exponent(mbedtls_mpi const *P, * This is FIPS 186-4 §B.3.1 criterion 3(b). * This will return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if E is not coprime to * (P-1)(Q-1), also validating FIPS 186-4 §B.3.1 criterion 2(a). */ - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(D, E, &K)); + MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod_even_in_range(D, E, &K)); cleanup: From 6ab0f519b8c9ede36b034e93fc191b14e8843891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 26 Aug 2025 11:31:52 +0200 Subject: [PATCH 09/12] dhm: remove unused variable (and improve comment) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/dhm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/library/dhm.c b/library/dhm.c index 3c65bc8076..941a89da80 100644 --- a/library/dhm.c +++ b/library/dhm.c @@ -345,9 +345,6 @@ static int dhm_update_blinding(mbedtls_dhm_context *ctx, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) { int ret; - mbedtls_mpi R; - - mbedtls_mpi_init(&R); /* * Don't use any blinding the first time a particular X is used, @@ -382,13 +379,11 @@ static int dhm_update_blinding(mbedtls_dhm_context *ctx, /* Vi = random( 2, P-2 ) */ MBEDTLS_MPI_CHK(dhm_random_below(&ctx->Vi, &ctx->P, f_rng, p_rng)); - /* Vf = Vi^-X mod P */ + /* Vf = Vi^-X = (Vi^-1)^X mod P */ MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, &ctx->Vf, &ctx->Vi, &ctx->P)); MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->Vf, &ctx->Vf, &ctx->X, &ctx->P, &ctx->RP)); cleanup: - mbedtls_mpi_free(&R); - return ret; } From b46432930eb8ecc3ed8953df56c77952c99e8f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 26 Aug 2025 11:33:12 +0200 Subject: [PATCH 10/12] ecdsa: rm unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/ecdsa.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/ecdsa.c b/library/ecdsa.c index 0d73c65425..a2d1eea6de 100644 --- a/library/ecdsa.c +++ b/library/ecdsa.c @@ -252,7 +252,7 @@ int mbedtls_ecdsa_sign_restartable(mbedtls_ecp_group *grp, int ret, key_tries, sign_tries; int *p_sign_tries = &sign_tries, *p_key_tries = &key_tries; mbedtls_ecp_point R; - mbedtls_mpi k, e, t; + mbedtls_mpi k, e; mbedtls_mpi *pk = &k, *pr = r; /* Fail cleanly on curves such as Curve25519 that can't be used for ECDSA */ @@ -266,7 +266,7 @@ int mbedtls_ecdsa_sign_restartable(mbedtls_ecp_group *grp, } mbedtls_ecp_point_init(&R); - mbedtls_mpi_init(&k); mbedtls_mpi_init(&e); mbedtls_mpi_init(&t); + mbedtls_mpi_init(&k); mbedtls_mpi_init(&e); ECDSA_RS_ENTER(sig); @@ -358,7 +358,7 @@ modn: cleanup: mbedtls_ecp_point_free(&R); - mbedtls_mpi_free(&k); mbedtls_mpi_free(&e); mbedtls_mpi_free(&t); + mbedtls_mpi_free(&k); mbedtls_mpi_free(&e); ECDSA_RS_LEAVE(sig); From c18eea6d43bf5d24e3f83945245e9fe31139228e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 26 Aug 2025 11:34:45 +0200 Subject: [PATCH 11/12] Minor grammar fix in comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/rsa_alt_helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/rsa_alt_helpers.h b/library/rsa_alt_helpers.h index 580ffb2fba..8ff74a02bf 100644 --- a/library/rsa_alt_helpers.h +++ b/library/rsa_alt_helpers.h @@ -90,7 +90,7 @@ int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, mbedtls_mpi const *E, * \param Q Second prime factor of RSA modulus * \param E RSA public exponent * \param D Pointer to MPI holding the private exponent on success, - * ie the modular inverse of E modulo LCM(P-1,Q-1). + * i.e. the modular inverse of E modulo LCM(P-1,Q-1). * * \return \c 0 if successful. * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. From c6b28b31ef5ad106e34102bf084b5c81cedc61a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 11 Sep 2025 09:58:45 +0200 Subject: [PATCH 12/12] Be explicit about modinv output range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- library/bignum_internal.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/library/bignum_internal.h b/library/bignum_internal.h index 341346222d..ba1c69d6b1 100644 --- a/library/bignum_internal.h +++ b/library/bignum_internal.h @@ -63,7 +63,8 @@ int mbedtls_mpi_exp_mod_unsafe(mbedtls_mpi *X, const mbedtls_mpi *A, * \param[out] G The GCD of \p A and \p N. * This may be NULL, to only compute I. * \param[out] I The inverse of \p A modulo \p N if it exists (that is, - * if \p G above is 1 on exit); indeterminate otherwise. + * if \p G above is 1 on exit), in the range [1, \p N); + * indeterminate otherwise. * This may be NULL, to only compute G. * \param[in] A The 1st operand of GCD and number to invert. * This value must be less than or equal to \p N. @@ -83,8 +84,8 @@ int mbedtls_mpi_gcd_modinv_odd(mbedtls_mpi *G, /** * \brief Modular inverse: X = A^-1 mod N with N odd * - * \param[out] X The inverse of \p A modulo \p N on success, - * indeterminate otherwise. + * \param[out] X The inverse of \p A modulo \p N in the range [1, \p N) + * on success; indeterminate otherwise. * \param[in] A The number to invert. * \param[in] N The modulus. Must be odd and greater than 1. * @@ -102,8 +103,8 @@ int mbedtls_mpi_inv_mod_odd(mbedtls_mpi *X, * \brief Modular inverse: X = A^-1 mod N with N even, * A odd and 1 < A < N. * - * \param[out] X The inverse of \p A modulo \p N on success, - * indeterminate otherwise. + * \param[out] X The inverse of \p A modulo \p N in the range [1, \p N) + * on success; indeterminate otherwise. * \param[in] A The number to invert. Must be odd, greated than 1 * and less than \p N. * \param[in] N The modulus. Must be even and greater than 1.