commit 34c20427926010d6fa95b1666e4b1b60f60a8742
Author: NIIBE Yutaka <gniibe@fsij.org>
Date:   Fri Oct 27 14:03:40 2023 +0900

    rsa: Fix decoding of PKCS#1 v1.5 and OAEP padding.
    
    * src/Makefile.am (libgcrypt_la_SOURCES): Add const-time.h and
    const-time.c.
    * src/const-time.h (ct_not_equal_byte, sexp_null_cond): New.
    (ct_memequal): New from NetBSD, modified return type and name.
    * src/const-time.c: New.
    * cipher/rsa-common.c (_gcry_rsa_pkcs1_decode_for_enc): Examine whole
    sequence of the byte-array.  Use N0 to find the separator position, with
    ct_not_equal_byte.  Return the MPI even when the case of an error.
    * cipher/rsa-common.c (_gcry_rsa_oaep_decode): Use ct_memequal to
    check LHASH.  Examine all the sequence of the byte-array.  Use N1 to
    find the separator of 0x01.  Return the MPI even when the case of an
    error.
    * cipher/rsa.c (rsa_decrypt): Always build a SEXP.
    
    --
    
    Note: For architecture(s) which may result branch in comparison of
    byte, configure script should emit POSSIBLE_BRANCH_IN_BYTE_COMPARISON.
    
    Reported-by: Hubert Kario <hkario@redhat.com>
    Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>

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
@@ -27,6 +27,7 @@
 #include "mpi.h"
 #include "cipher.h"
 #include "pubkey-internal.h"
+#include "const-time.h"
 
 
 /* Turn VALUE into an octet string and store it in an allocated buffer
@@ -161,6 +162,35 @@ _gcry_rsa_pkcs1_encode_for_enc (gcry_mpi
 }
 
 
+/*
+ *                    <--len-->
+ * DST-------v       v-------------SRC
+ *           [.................]
+ *            <---- buflen --->
+ *
+ * Copy the memory area SRC with LEN into another memory area DST.
+ * Conditions met:
+ *         the address SRC > DST
+ *         DST + BUFLEN == SRC + LEN.
+ *
+ * Memory access doesn't depends on LEN, but always done independently,
+ * sliding memory area by 1, 2, 4 ... until done.
+ */
+static void
+memmov_independently (void *dst, const void *src, size_t len, size_t buflen)
+{
+  size_t offset = (size_t)((char *)src - (char *)dst);
+  size_t shift;
+
+  (void)len; /* No dependency.  */
+  for (shift = 1; shift < buflen; shift <<= 1)
+    {
+      ct_memmov_cond (dst, (char *)dst + shift, buflen - shift, (offset&1));
+      offset >>= 1;
+    }
+}
+
+
 /* 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
@@ -172,7 +202,9 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned
   gcry_error_t err;
   unsigned char *frame = NULL;
   size_t nframe = (nbits+7) / 8;
-  size_t n;
+  size_t n, n0;
+  unsigned int failed = 0;
+  unsigned int not_found = 1;
 
   *r_result = NULL;
 
@@ -203,33 +235,31 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned
   n = 0;
   if (!frame[0])
     n++;
-  if (frame[n++] != 0x02)
-    {
-      xfree (frame);
-      return GPG_ERR_ENCODING_PROBLEM;  /* Wrong block type.  */
-    }
+  failed |= ct_not_equal_byte (frame[n++], 0x02);
 
-  /* Skip the non-zero random bytes and the terminating zero byte.  */
-  for (; n < nframe && frame[n] != 0x00; n++)
-    ;
-  if (n+1 >= nframe)
+  /* Find the terminating zero byte.  */
+  n0 = n;
+  for (; n < nframe; n++)
     {
-      xfree (frame);
-      return GPG_ERR_ENCODING_PROBLEM; /* No zero byte.  */
+      not_found &= ct_not_equal_byte (frame[n], 0x00);
+      n0 += not_found;
     }
-  n++; /* Skip the zero byte.  */
+
+  failed |= not_found;
+  n0 += !not_found; /* Skip the zero byte.  */
 
   /* To avoid an extra allocation we reuse the frame buffer.  The only
      caller of this function will anyway free the result soon.  */
-  memmove (frame, frame + n, nframe - n);
+  memmov_independently (frame, frame + n0, nframe - n0, nframe);
+
   *r_result = frame;
-  *r_resultlen = nframe - n;
+  *r_resultlen = nframe - n0;
 
   if (DBG_CIPHER)
     log_printhex ("value extracted from PKCS#1 block type 2 encoded data",
                   *r_result, *r_resultlen);
 
-  return 0;
+  return (0U - failed) & GPG_ERR_ENCODING_PROBLEM;
 }
 
 
@@ -551,7 +581,8 @@ _gcry_rsa_oaep_decode (unsigned char **r
   size_t db_len;               /* Length of DB and masked_db.  */
   size_t nkey = (nbits+7)/8;   /* Length of the key in bytes.  */
   int failed = 0;              /* Error indicator.  */
-  size_t n;
+  size_t n, n1;
+  unsigned int not_found = 1;
 
   *r_result = NULL;
 
@@ -623,51 +654,43 @@ _gcry_rsa_oaep_decode (unsigned char **r
   db_len      = nframe - 1 - hlen;
 
   /* Step 3c and 3d: seed = maskedSeed ^ mgf(maskedDB, hlen).  */
-  if (mgf1 (seed, hlen, masked_db, db_len, algo))
-    failed = 1;
+  failed |= (mgf1 (seed, hlen, masked_db, db_len, algo) != 0);
   for (n = 0; n < hlen; n++)
     seed[n] ^= masked_seed[n];
 
   /* Step 3e and 3f: db = maskedDB ^ mgf(seed, db_len).  */
-  if (mgf1 (db, db_len, seed, hlen, algo))
-    failed = 1;
+  failed |= (mgf1 (db, db_len, seed, hlen, algo) != 0);
   for (n = 0; n < db_len; n++)
     db[n] ^= masked_db[n];
 
   /* Step 3g: Check lhash, an possible empty padding string terminated
      by 0x01 and the first byte of EM being 0.  */
-  if (memcmp (lhash, db, hlen))
-    failed = 1;
-  for (n = hlen; n < db_len; n++)
-    if (db[n] == 0x01)
-      break;
-  if (n == db_len)
-    failed = 1;
-  if (frame[0])
-    failed = 1;
+  failed |= !ct_memequal (lhash, db, hlen);
+  for (n = n1 = hlen; n < db_len; n++)
+    {
+      not_found &= ct_not_equal_byte (db[n], 0x01);
+      n1 += not_found;
+    }
+  failed |= not_found;
+  failed |= ct_not_equal_byte (frame[0], 0x00);
 
   xfree (lhash);
   xfree (frame);
-  if (failed)
-    {
-      xfree (seed);
-      return GPG_ERR_ENCODING_PROBLEM;
-    }
 
   /* Step 4: Output M.  */
   /* To avoid an extra allocation we reuse the seed buffer.  The only
      caller of this function will anyway free the result soon.  */
-  n++;
-  memmove (seed, db + n, db_len - n);
+  n1 += !not_found;
+  memmov_independently (seed, db + n1, db_len - n1, nframe - 1);
   *r_result = seed;
-  *r_resultlen = db_len - n;
+  *r_resultlen = db_len - n1;
   seed = NULL;
 
   if (DBG_CIPHER)
     log_printhex ("value extracted from OAEP encoded data",
                   *r_result, *r_resultlen);
 
-  return 0;
+  return (0U - failed) & GPG_ERR_ENCODING_PROBLEM;
 }
 
 
Index: libgcrypt-1.6.1/cipher/rsa.c
===================================================================
--- libgcrypt-1.6.1.orig/cipher/rsa.c
+++ libgcrypt-1.6.1/cipher/rsa.c
@@ -33,6 +33,7 @@
 #include "mpi.h"
 #include "cipher.h"
 #include "pubkey-internal.h"
+#include "const-time.h"
 
 
 typedef struct
@@ -1405,6 +1406,8 @@ 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;
+  gcry_sexp_t result = NULL;
+  gcry_sexp_t dummy = NULL;
 
   _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT,
                                    rsa_get_nbits (keyparms));
@@ -1504,8 +1507,10 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_
       rc = _gcry_rsa_pkcs1_decode_for_enc (&unpad, &unpadlen, ctx.nbits, plain);
       mpi_free (plain);
       plain = NULL;
-      if (!rc)
-        rc = sexp_build (r_plain, NULL, "(value %b)", (int)unpadlen, unpad);
+      sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
+      *r_plain = sexp_null_cond (result, !!rc);
+      dummy = sexp_null_cond (result,!rc);
+      sexp_release (dummy);
       break;
 
     case PUBKEY_ENC_OAEP:
@@ -1514,8 +1519,10 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_
                                   plain, ctx.label, ctx.labellen);
       mpi_free (plain);
       plain = NULL;
-      if (!rc)
-        rc = sexp_build (r_plain, NULL, "(value %b)", (int)unpadlen, unpad);
+      sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
+      *r_plain = sexp_null_cond (result, !!rc);
+      dummy = sexp_null_cond (result, !rc);
+      sexp_release (dummy);
       break;
 
     default:
Index: libgcrypt-1.6.1/src/Makefile.am
===================================================================
--- libgcrypt-1.6.1.orig/src/Makefile.am
+++ libgcrypt-1.6.1/src/Makefile.am
@@ -61,6 +61,7 @@ libgcrypt_la_SOURCES = \
 	stdmem.c stdmem.h secmem.c secmem.h \
 	mpi.h missing-string.c fips.c \
 	hmac256.c hmac256.h context.c context.h \
+	const-time.h const-time.c \
 	ec-context.h \
 	ath.h ath.c
 
Index: libgcrypt-1.6.1/src/const-time.c
===================================================================
--- /dev/null
+++ libgcrypt-1.6.1/src/const-time.c
@@ -0,0 +1,52 @@
+/* const-time.c  -  Constant-time functions
+ *      Copyright (C) 2023  g10 Code GmbH
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "g10lib.h"
+#include "const-time.h"
+
+/* Originally in NetBSD.
+ *
+ * Written by Matthias Drochner <drochner@NetBSD.org>.
+ * Public domain.
+ *
+ * Modified the function name and return type to unsigned.
+ */
+unsigned int
+ct_memequal (const void *b1, const void *b2, size_t len)
+{
+	const unsigned char *c1 = b1, *c2 = b2;
+	unsigned int res = 0;
+
+	while (len--)
+		res |= *c1++ ^ *c2++;
+
+	/*
+	 * Map 0 to 1 and [1, 256) to 0 using only constant-time
+	 * arithmetic.
+	 *
+	 * This is not simply `!res' because although many CPUs support
+	 * branchless conditional moves and many compilers will take
+	 * advantage of them, certain compilers generate branches on
+	 * certain CPUs for `!res'.
+	 */
+	return (1 & ((res - 1) >> 8));
+}
Index: libgcrypt-1.6.1/src/const-time.h
===================================================================
--- /dev/null
+++ libgcrypt-1.6.1/src/const-time.h
@@ -0,0 +1,53 @@
+/* const-time.h  -  Constant-time functions
+ *      Copyright (C) 2023  g10 Code GmbH
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Return 1 if it's not same, 0 if same.
+ */
+static inline unsigned int
+ct_not_equal_byte (unsigned char b0, unsigned char b1)
+{
+#ifdef POSSIBLE_BRANCH_IN_BYTE_COMPARISON
+  unsigned int diff;
+
+  diff = b0;
+  diff ^= b1;
+
+  return (0U - diff) >> (sizeof (unsigned int)*8 - 1);
+#else
+  return b0 != b1;
+#endif
+}
+
+/*
+ *  Return NULL when OP_ENABLED=1
+ *  otherwise, return W
+ */
+static inline gcry_sexp_t
+sexp_null_cond (gcry_sexp_t w, unsigned long op_enable)
+{
+  static volatile uintptr_t vone = 1;
+  size_t mask = (uintptr_t)op_enable - vone;
+
+  return (gcry_sexp_t)(void *)((uintptr_t)w & mask);
+}
+
+/* Compare byte-arrays of length LEN, return 1 if it's same, 0 otherwise.
+   We use pointer of void *, so that it can be used with any structure.  */
+unsigned int ct_memequal (const void *b1, const void *b2, size_t len);
Index: libgcrypt-1.6.1/src/types.h
===================================================================
--- libgcrypt-1.6.1.orig/src/types.h
+++ libgcrypt-1.6.1/src/types.h
@@ -41,6 +41,17 @@
 #include <sys/types.h>
 
 
+#include <sys/types.h>
+
+/* Provide uintptr_t */
+#ifdef HAVE_STDINT_H
+# include <stdint.h> /* uintptr_t */
+#elif defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+#else
+/* In this case, uintptr_t is provided by config.h. */
+#endif
+
 #ifndef HAVE_BYTE_TYPEDEF
 #undef byte	    /* maybe there is a macro with this name */
 /* Windows typedefs byte in the rpc headers.  Avoid warning about
Index: libgcrypt-1.6.1/src/Makefile.in
===================================================================
--- libgcrypt-1.6.1.orig/src/Makefile.in
+++ libgcrypt-1.6.1/src/Makefile.in
@@ -413,7 +413,7 @@ libgcrypt_la_SOURCES = \
 	stdmem.c stdmem.h secmem.c secmem.h \
 	mpi.h missing-string.c fips.c \
 	hmac256.c hmac256.h context.c context.h \
-	ec-context.h \
+	ec-context.h const-time.h const-time.c \
 	ath.h ath.c
 
 EXTRA_libgcrypt_la_SOURCES = hwf-x86.c hwf-arm.c
