Merge pull request #1411 from mpg/bypass-wrappers

[3.6] Bypass GCD/modinv wrappers when possible
This commit is contained in:
Manuel Pégourié-Gonnard
2025-09-11 12:25:23 +02:00
committed by GitHub
9 changed files with 97 additions and 121 deletions

View File

@@ -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;
@@ -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;

View File

@@ -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.
@@ -80,4 +81,42 @@ 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 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.
*
* \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);
/**
* \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 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.
*
* \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 */

View File

@@ -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"
@@ -344,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,
@@ -381,21 +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
* 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 = (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;
}

View File

@@ -17,6 +17,7 @@
#include "mbedtls/ecdsa.h"
#include "mbedtls/asn1write.h"
#include "bignum_internal.h"
#include <string.h>
@@ -251,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 */
@@ -265,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);
@@ -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);
@@ -367,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);
@@ -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));

View File

@@ -68,6 +68,7 @@
#include "mbedtls/error.h"
#include "bn_mul.h"
#include "bignum_internal.h"
#include "ecp_invasive.h"
#include <string.h>
@@ -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:

View File

@@ -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);
@@ -1304,33 +1293,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) */

View File

@@ -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) {
@@ -197,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);
@@ -211,8 +216,11 @@ 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) */
MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(D, E, &K));
/* 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_even_in_range(D, E, &K));
cleanup:
@@ -244,7 +252,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:

View File

@@ -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,
* i.e. 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.
*

View File

@@ -1331,7 +1331,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