File libgcrypt-CVE-2024-2236_11.patch of Package libgcrypt.39917
From 81f8b7357395a8c10fc3fcbf177db6267097c507 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 31 May 2024 15:03:29 +0200
Subject: [PATCH 11/11] cipher: Use the constant time conversion also for OAEP
--
* cipher/rsa-common.c (mpi_to_string): New function
(_gcry_rsa_pkcs1_decode_for_enc): Use new function for MPI to string
conversion.
(_gcry_rsa_oaep_decode): Use the new function for MPI to string
conversion.
* cipher/rsa.c (rsa_decrypt): Use constant time conversion to S-exp for
OAEP padding.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
cipher/rsa-common.c | 77 +++++++++++++++++++++++++++++++--------------
cipher/rsa.c | 4 +++
2 files changed, 57 insertions(+), 24 deletions(-)
Index: libgcrypt-1.6.1/cipher/rsa-common.c
===================================================================
--- libgcrypt-1.6.1.orig/cipher/rsa-common.c
+++ libgcrypt-1.6.1/cipher/rsa-common.c
@@ -191,32 +191,26 @@ memmov_independently (void *dst, const v
}
}
-
-/* Decode a plaintext in VALUE assuming pkcs#1 block type 2 padding.
- NBITS is the size of the secret key. On success the result is
- stored as a newly allocated buffer at R_RESULT and its valid length at
- R_RESULTLEN. On error NULL is stored at R_RESULT. */
-gpg_err_code_t
-_gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen,
- unsigned int nbits, gcry_mpi_t value)
+static unsigned char *
+mpi_to_string(gcry_mpi_t value, size_t nframe)
{
unsigned char *frame = NULL;
- size_t nframe = (nbits+7) / 8;
- size_t n, n0;
- unsigned int failed = 0;
- unsigned int not_found = 1;
+ unsigned char *p;
+ size_t noff;
+ /* Allocate memory to fit the whole MPI limbs and allow moving it later to the right place */
+ size_t alloc_len = value->nlimbs * BYTES_PER_MPI_LIMB;
- *r_result = NULL;
+ /* for too short input, we need to allocate at least modulus length (which might still be valid in case of OAEP) */
+ noff = (alloc_len < nframe) ? nframe - alloc_len : 0;
+ alloc_len = alloc_len + noff;
- {
-#ifdef WITH_MARVIN_WORKAROUND
- /* Allocate more to fit the whole MPI and allow moving it later to the right place */
- size_t alloc_len = value->nlimbs * BYTES_PER_MPI_LIMB;
if ( !(frame = xtrymalloc_secure (alloc_len)))
- return gpg_err_code_from_syserror ();
+ return NULL;
+ if (noff)
+ memset (frame, 0, noff);
+ p = frame + noff;
/* shovel the MPI content to the buffer as it is */
- unsigned char *p = frame;
int i;
for (i = value->nlimbs - 1; i >= 0; i--)
{
@@ -231,9 +225,34 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned
# error please implement for this limb size.
#endif
}
- /* Remove leading zeroes, but not all! Keep the buffer to the nframe length!
- * -- valid PKCS#1.5 padding will never have different lengths, than modulus */
+ /* Move the MPI to the right place -- with the least significant bytes aligned to the modulus length
+ * -- this might keep some leading zeroes at the beginning of the buffer, but this might be valid for OAEP */
memmov_independently (frame, frame + (alloc_len - nframe), nframe, alloc_len);
+ return frame;
+}
+
+/* Decode a plaintext in VALUE assuming pkcs#1 block type 2 padding.
+ NBITS is the size of the secret key. On success the result is
+ stored as a newly allocated buffer at R_RESULT and its valid length at
+ R_RESULTLEN. On error NULL is stored at R_RESULT. */
+gpg_err_code_t
+_gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen,
+ unsigned int nbits, gcry_mpi_t value)
+{
+ unsigned char *frame = NULL;
+ size_t nframe = (nbits+7) / 8;
+ size_t n, n0;
+ unsigned int failed = 0;
+ unsigned int not_found = 1;
+
+ *r_result = NULL;
+
+ {
+#ifdef WITH_MARVIN_WORKAROUND
+ frame = mpi_to_string(value, nframe);
+ if (frame == NULL)
+ return gpg_err_code_from_syserror ();
+
n = 0;
failed |= ct_not_equal_byte (frame[n++], 0x00);
#else
@@ -266,7 +285,7 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned
n = 0;
if (!frame[0])
n++;
-#endif
+#endif /* WITH_MARVIN_WORKAROUND */
}
failed |= ct_not_equal_byte (frame[n++], 0x02);
@@ -645,14 +664,23 @@ _gcry_rsa_oaep_decode (unsigned char **r
happen due to the leading zero in OAEP frames and due to the
following random octets (seed^mask) which may have leading zero
bytes. This all is needed to cope with our leading zeroes
- suppressing MPI implementation. The code implictly implements
+ suppressing MPI implementation. The code implicitly implements
Step 1b (bail out if NFRAME != N). */
+#ifdef WITH_MARVIN_WORKAROUND
+ frame = mpi_to_string(value, nkey);
+ if (frame == NULL)
+ {
+ xfree (lhash);
+ return gpg_err_code_from_syserror ();
+ }
+#else
rc = octet_string_from_mpi (&frame, NULL, value, nkey);
if (rc)
{
xfree (lhash);
return GPG_ERR_ENCODING_PROBLEM;
}
+#endif /* WITH_MARVIN_WORKAROUND */
nframe = nkey;
/* Step 1c: Check that the key is long enough. */
@@ -667,7 +695,7 @@ _gcry_rsa_oaep_decode (unsigned char **r
gcry_mpi_aprint above. */
/* Allocate space for SEED and DB. */
- seed = xtrymalloc_secure (nframe - 1);
+ seed = xtrymalloc_secure (nframe);
if (!seed)
{
rc = gpg_err_code_from_syserror ();
Index: libgcrypt-1.6.1/cipher/rsa.c
===================================================================
--- libgcrypt-1.6.1.orig/cipher/rsa.c
+++ libgcrypt-1.6.1/cipher/rsa.c
@@ -362,6 +362,17 @@ generate_std (RSA_secret_key *sk, unsign
return 0;
}
+/* Check the RSA key length is acceptable for key generation or usage */
+static gpg_err_code_t
+rsa_check_keysize (unsigned int nbits)
+{
+ if (fips_mode() && nbits < 2048)
+ return GPG_ERR_INV_VALUE;
+
+ return GPG_ERR_NO_ERROR;
+}
+
+
/* Helper for generate_x931. */
static gcry_mpi_t
@@ -1395,7 +1406,7 @@ static gcry_err_code_t
rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
{
- gpg_err_code_t rc;
+ gpg_err_code_t rc, rc_sexp;
struct pk_encoding_ctx ctx;
gcry_sexp_t l1 = NULL;
gcry_mpi_t data = NULL;
@@ -1406,9 +1417,14 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_
gcry_mpi_t bldata = NULL;/* Blinded data to decrypt. */
unsigned char *unpad = NULL;
size_t unpadlen = 0;
+ unsigned int nbits = rsa_get_nbits (keyparms);
gcry_sexp_t result = NULL;
gcry_sexp_t dummy = NULL;
+ rc = rsa_check_keysize (nbits);
+ if (rc)
+ return rc;
+
_gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT,
rsa_get_nbits (keyparms));
@@ -1512,13 +1528,15 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_
mpi_free (plain);
plain = NULL;
#ifdef WITH_MARVIN_WORKAROUND
- rc_sexp = sexp_build (&result, NULL, "(value %c)", (int)unpadlen, unpad, (nbits + 7) / 8);
+ rc_sexp = sexp_build (&result, NULL, "(value %c)", (int)unpadlen, unpad, (ctx.nbits + 7) / 8);
#else
sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
#endif /* WITH_MARVIN_WORKAROUND */
- *r_plain = sexp_null_cond (result, !!rc);
- dummy = sexp_null_cond (result,!rc);
+ *r_plain = sexp_null_cond (result, ct_is_not_zero (rc));
+ dummy = sexp_null_cond (result, ct_is_zero (rc));
sexp_release (dummy);
+ rc = ct_ulong_select (rc_sexp, rc,
+ ct_is_zero (rc) & ct_is_not_zero (rc_sexp));
break;
case PUBKEY_ENC_OAEP:
@@ -1527,12 +1545,20 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_
plain, ctx.label, ctx.labellen);
mpi_free (plain);
plain = NULL;
- sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
- *r_plain = sexp_null_cond (result, !!rc);
- dummy = sexp_null_cond (result, !rc);
+
+#ifdef WITH_MARVIN_WORKAROUND
+ rc_sexp = sexp_build (&result, NULL, "(value %c)", (int)unpadlen, unpad, (ctx.nbits + 7) / 8);
+#else
+ rc_sexp = sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
+#endif /* WITH_MARVIN_WORKAROUND */
+ *r_plain = sexp_null_cond (result, ct_is_not_zero (rc));
+ dummy = sexp_null_cond (result, ct_is_zero (rc));
sexp_release (dummy);
+ rc = ct_ulong_select (rc_sexp, rc,
+ ct_is_zero (rc) & ct_is_not_zero (rc_sexp));
break;
+
default:
/* Raw format. For backward compatibility we need to assume a
signed mpi by using the sexp format string "%m". */
Index: libgcrypt-1.6.1/mpi/mpi-internal.h
===================================================================
--- libgcrypt-1.6.1.orig/mpi/mpi-internal.h
+++ libgcrypt-1.6.1/mpi/mpi-internal.h
@@ -50,6 +50,7 @@
#endif /*BITS_PER_MPI_LIMB*/
#include "mpi.h"
+#include "const-time.h"
/* If KARATSUBA_THRESHOLD is not already defined, define it to a
* value which is good on most machines. */
@@ -282,6 +283,28 @@ mpi_ptr_t _gcry_mpih_mod (mpi_ptr_t vp,
mpi_ptr_t up, mpi_size_t usize);
int _gcry_mpih_cmp_ui (mpi_ptr_t up, mpi_size_t usize, unsigned long v);
+DEFINE_CT_TYPE_GEN_MASK(limb, mpi_limb_t)
+DEFINE_CT_TYPE_GEN_INV_MASK(limb, mpi_limb_t)
+
+
+static inline int
+mpih_limb_is_zero (mpi_limb_t a)
+{
+ /* Sign bit set if A == 0. */
+ a = ~a & ~(-a);
+
+ return a >> (BITS_PER_MPI_LIMB - 1);
+}
+
+static inline int
+mpih_limb_is_not_zero (mpi_limb_t a)
+{
+ /* Sign bit set if A != 0. */
+ a = a | (-a);
+
+ return a >> (BITS_PER_MPI_LIMB - 1);
+}
+
/* Define stuff for longlong.h. */