From c8b87b0d88a80a0b37a4da8d243471ea423bb1fc Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 28 Jul 2025 11:21:32 +0200 Subject: [PATCH 1/7] Remove redundant public-after-private After doing an RSA private-key operation, we do the public-key operation. This protects against a glitch attack (Lenstra 1996) against signatures when using CRT coefficients to optimize the private-key operation. Glitch attacks are normally outside our threat model, but this one is especially easy to exploit so we defend against it. For historical reasons we ended up having this protection twice: once inside `mbedtls_rsa_private()` (before unblinding), and another one in `mbedtls_rsa_rsassa_pkcs1_v15_sign()` (but not in PSS signature). Keep only the one that's done systematically in `mbedtls_rsa_private()`. Signed-off-by: Gilles Peskine --- library/rsa.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 557faaf36354..95e9e5a99eaf 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -2470,12 +2470,6 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign(mbedtls_rsa_context *ctx, } MBEDTLS_MPI_CHK(mbedtls_rsa_private(ctx, f_rng, p_rng, sig, sig_try)); - MBEDTLS_MPI_CHK(mbedtls_rsa_public(ctx, sig_try, verif)); - - if (mbedtls_ct_memcmp(verif, sig, ctx->len) != 0) { - ret = MBEDTLS_ERR_RSA_PRIVATE_FAILED; - goto cleanup; - } memcpy(sig, sig_try, ctx->len); From 94ca138c114608bc889d916e4abe4cc578fe489c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 28 Jul 2025 11:31:03 +0200 Subject: [PATCH 2/7] Explain why we verify the result of an RSA private-key operation It's protection against Lenstra's glitch attack on CRT exponentiation. Signed-off-by: Gilles Peskine --- library/rsa.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/library/rsa.c b/library/rsa.c index 95e9e5a99eaf..9b6da909bd59 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -1554,7 +1554,17 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&T, &TQ, &TP)); #endif /* MBEDTLS_RSA_NO_CRT */ - /* Verify the result to prevent glitching attacks. */ + /* Verify the result to prevent a glitching attack: Arjen Lenstra, + * Memo on RSA signature generation in the presence of faults, 1996. + * https://infoscience.epfl.ch/record/164524/files/nscan20.PDF + * + * The attack is mostly on signature (the attacker needs to see + * the output), but could be relevant against decryption if the + * attacker gets to see the raw decryption result or maybe even part + * of it, possibly through a side channel in padding verification. + * We do the verification before unblinding, to reduce the risk of + * leaking information about the input. + */ MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&check_result_blinded, &T, &ctx->E, &ctx->N, &ctx->RN)); if (mbedtls_mpi_cmp_mpi(&check_result_blinded, &input_blinded) != 0) { From 1d745cd85974e55c11ac83dd783df7895e1cd79b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 28 Jul 2025 11:32:36 +0200 Subject: [PATCH 3/7] Use exponent-leaking exponentiation to verify RSA private-key operation Slight performance improvement. Signed-off-by: Gilles Peskine --- library/rsa.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 9b6da909bd59..1b201c8a3732 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -1564,9 +1564,12 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, * of it, possibly through a side channel in padding verification. * We do the verification before unblinding, to reduce the risk of * leaking information about the input. + * We use the "unsafe" version of exponentiation, which leaks the + * public exponent, for a slight performance improvement. */ - MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&check_result_blinded, &T, &ctx->E, - &ctx->N, &ctx->RN)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod_unsafe(&check_result_blinded, + &T, &ctx->E, + &ctx->N, &ctx->RN)); if (mbedtls_mpi_cmp_mpi(&check_result_blinded, &input_blinded) != 0) { ret = MBEDTLS_ERR_RSA_VERIFY_FAILED; goto cleanup; From f50e620a8bfb5788a639b7a853229ff66584f64d Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 28 Jul 2025 11:34:42 +0200 Subject: [PATCH 4/7] Only verify RSA private-key operation when using CRT The glitch attack it protects against is specifically on the algorithm that uses the CRT coefficients. Slight performance improvement when `MBEDTLS_RSA_NO_CRT` is enabled. Signed-off-by: Gilles Peskine --- library/rsa.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/rsa.c b/library/rsa.c index 1b201c8a3732..74aa5611a8a7 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -1554,6 +1554,7 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&T, &TQ, &TP)); #endif /* MBEDTLS_RSA_NO_CRT */ +#if !defined(MBEDTLS_RSA_NO_CRT) /* Verify the result to prevent a glitching attack: Arjen Lenstra, * Memo on RSA signature generation in the presence of faults, 1996. * https://infoscience.epfl.ch/record/164524/files/nscan20.PDF @@ -1574,6 +1575,7 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, ret = MBEDTLS_ERR_RSA_VERIFY_FAILED; goto cleanup; } +#endif /* * Unblind From bb87a039957761ae448fb56e396a7b40be939b22 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 30 Jul 2025 15:38:44 +0200 Subject: [PATCH 5/7] Remove obsolete comment Signed-off-by: Gilles Peskine --- library/rsa.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 74aa5611a8a7..e512738e55c2 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -2468,9 +2468,6 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign(mbedtls_rsa_context *ctx, } /* Private key operation - * - * In order to prevent Lenstra's attack, make the signature in a - * temporary buffer and check it before returning it. */ sig_try = mbedtls_calloc(1, ctx->len); From 7303ada0cee493221c49562d78b2a638a4cefbe3 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 30 Jul 2025 15:38:56 +0200 Subject: [PATCH 6/7] Remove verif, which is now unused Signed-off-by: Gilles Peskine --- library/rsa.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index e512738e55c2..8e57bd078efb 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -2448,7 +2448,7 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign(mbedtls_rsa_context *ctx, unsigned char *sig) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - unsigned char *sig_try = NULL, *verif = NULL; + unsigned char *sig_try = NULL; if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; @@ -2475,19 +2475,12 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign(mbedtls_rsa_context *ctx, return MBEDTLS_ERR_MPI_ALLOC_FAILED; } - verif = mbedtls_calloc(1, ctx->len); - if (verif == NULL) { - mbedtls_free(sig_try); - return MBEDTLS_ERR_MPI_ALLOC_FAILED; - } - MBEDTLS_MPI_CHK(mbedtls_rsa_private(ctx, f_rng, p_rng, sig, sig_try)); memcpy(sig, sig_try, ctx->len); cleanup: mbedtls_zeroize_and_free(sig_try, ctx->len); - mbedtls_zeroize_and_free(verif, ctx->len); if (ret != 0) { memset(sig, '!', ctx->len); From 2deca06c33dc31b536caf80014f79cff3a3647df Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 30 Jul 2025 15:39:56 +0200 Subject: [PATCH 7/7] Remove sig_try, which is now useless `mbedtls_rsa_private()` is ok with aliasing the output with the input. Signed-off-by: Gilles Peskine --- library/rsa.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 8e57bd078efb..bffeb2569d10 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -2448,7 +2448,6 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign(mbedtls_rsa_context *ctx, unsigned char *sig) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - unsigned char *sig_try = NULL; if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; @@ -2469,19 +2468,9 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign(mbedtls_rsa_context *ctx, /* Private key operation */ - - sig_try = mbedtls_calloc(1, ctx->len); - if (sig_try == NULL) { - return MBEDTLS_ERR_MPI_ALLOC_FAILED; - } - - MBEDTLS_MPI_CHK(mbedtls_rsa_private(ctx, f_rng, p_rng, sig, sig_try)); - - memcpy(sig, sig_try, ctx->len); + MBEDTLS_MPI_CHK(mbedtls_rsa_private(ctx, f_rng, p_rng, sig, sig)); cleanup: - mbedtls_zeroize_and_free(sig_try, ctx->len); - if (ret != 0) { memset(sig, '!', ctx->len); }