[PATCH 03/12] rsa, elgamal: avoid logical not operator in constant-time code

Jussi Kivilinna jussi.kivilinna at iki.fi
Thu Nov 2 19:01:07 CET 2023


* cipher/elgamal.c (elg_decrypt): Replace ! operator with calls to
ct_is_not_zero/ct_is_zero/ct_ulong_select.
* cipher/rsa-common.c (_gcry_rsa_pkcs1_decode_for_enc): Replace !
operator with call to ct_is_zero.
* cipher/rsa.c (rsa_decrypt): Replace ! operator with calls to
ct_is_not_zero/ct_is_zero/ct_ulong_select.
* src/const-time.c (_gcry_ct_vzero, _gcry_ct_vone): New.
* src/const-time.h (_gcry_ct_vzero, _gcry_ct_vone): New.
(ct_is_not_zero, ct_is_zero, DEFINE_CT_TYPE_SELECT_FUNC)
(ct_uintptr_select, ct_ulong_select): New.
(sexp_null_cond): Use ct_uintptr_select.
--

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 cipher/elgamal.c    | 22 +++++++++++---------
 cipher/rsa-common.c |  2 +-
 cipher/rsa.c        | 16 +++++++--------
 src/const-time.c    |  8 ++++++++
 src/const-time.h    | 49 +++++++++++++++++++++++++++++++++++++++++----
 5 files changed, 74 insertions(+), 23 deletions(-)

diff --git a/cipher/elgamal.c b/cipher/elgamal.c
index 06881cb6..540ecb02 100644
--- a/cipher/elgamal.c
+++ b/cipher/elgamal.c
@@ -931,26 +931,28 @@ elg_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     {
     case PUBKEY_ENC_PKCS1:
       rc = _gcry_rsa_pkcs1_decode_for_enc (&unpad, &unpadlen, ctx.nbits, plain);
-      mpi_free (plain); plain = NULL;
+      mpi_free (plain);
+      plain = NULL;
       rc_sexp = sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
-      *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);
-      if (!rc && rc_sexp)
-        rc = rc_sexp;
+      rc = ct_ulong_select (rc_sexp, rc,
+			    ct_is_zero (rc) & ct_is_not_zero (rc_sexp));
       break;
 
     case PUBKEY_ENC_OAEP:
       rc = _gcry_rsa_oaep_decode (&unpad, &unpadlen,
                                   ctx.nbits, ctx.hash_algo, plain,
                                   ctx.label, ctx.labellen);
-      mpi_free (plain); plain = NULL;
+      mpi_free (plain);
+      plain = NULL;
       rc_sexp = sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
-      *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);
-      if (!rc && rc_sexp)
-        rc = rc_sexp;
+      rc = ct_ulong_select (rc_sexp, rc,
+			    ct_is_zero (rc) & ct_is_not_zero (rc_sexp));
       break;
 
     default:
diff --git a/cipher/rsa-common.c b/cipher/rsa-common.c
index 74eb6341..1920eedd 100644
--- a/cipher/rsa-common.c
+++ b/cipher/rsa-common.c
@@ -246,7 +246,7 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen,
     }
 
   failed |= not_found;
-  n0 += !not_found; /* Skip the zero byte.  */
+  n0 += ct_is_zero (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.  */
diff --git a/cipher/rsa.c b/cipher/rsa.c
index 383e2474..c7a809f4 100644
--- a/cipher/rsa.c
+++ b/cipher/rsa.c
@@ -1516,11 +1516,11 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       mpi_free (plain);
       plain = NULL;
       rc_sexp = sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
-      *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);
-      if (!rc && rc_sexp)
-        rc = rc_sexp;
+      rc = ct_ulong_select (rc_sexp, rc,
+			    ct_is_zero (rc) & ct_is_not_zero (rc_sexp));
       break;
 
     case PUBKEY_ENC_OAEP:
@@ -1530,11 +1530,11 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       mpi_free (plain);
       plain = NULL;
       rc_sexp = sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad);
-      *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);
-      if (!rc && rc_sexp)
-        rc = rc_sexp;
+      rc = ct_ulong_select (rc_sexp, rc,
+			    ct_is_zero (rc) & ct_is_not_zero (rc_sexp));
       break;
 
     default:
diff --git a/src/const-time.c b/src/const-time.c
index fb787a02..908c0ee9 100644
--- a/src/const-time.c
+++ b/src/const-time.c
@@ -23,6 +23,14 @@
 #include "g10lib.h"
 #include "const-time.h"
 
+
+/* These variables are used to generate masks from conditional operation
+ * flag parameters.  Use of volatile prevents compiler optimizations from
+ * converting AND-masking to conditional branches.  */
+volatile unsigned int _gcry_ct_vzero = 0;
+volatile unsigned int _gcry_ct_vone = 1;
+
+
 /*
  * Compare byte arrays of length LEN, return 1 if it's not same,
  * 0, otherwise.
diff --git a/src/const-time.h b/src/const-time.h
index 4f14f86b..3a229ddb 100644
--- a/src/const-time.h
+++ b/src/const-time.h
@@ -23,6 +23,34 @@
 #include "types.h"
 
 
+extern volatile unsigned int _gcry_ct_vzero;
+extern volatile unsigned int _gcry_ct_vone;
+
+
+/*
+ * Return 0 if A is 0 and return 1 otherwise.
+ */
+static inline unsigned int
+ct_is_not_zero (unsigned int a)
+{
+  /* Sign bit set if A != 0. */
+  a = a | (-a);
+
+  return a >> (sizeof(unsigned int) * 8 - 1);
+}
+
+/*
+ * Return 1 if A is 0 and return 0 otherwise.
+ */
+static inline unsigned int
+ct_is_zero (unsigned int a)
+{
+  /* Sign bit set if A == 0. */
+  a = ~a & ~(-a);
+
+  return a >> (sizeof(unsigned int) * 8 - 1);
+}
+
 /*
  * Return 1 if it's not same, 0 if same.
  */
@@ -47,6 +75,21 @@ unsigned int ct_not_memequal (const void *b1, const void *b2, size_t len);
    any structure.  */
 unsigned int ct_memequal (const void *b1, const void *b2, size_t len);
 
+/*
+ *  Return A when OP_ENABLED=1
+ *  otherwise, return B
+ */
+#define DEFINE_CT_TYPE_SELECT_FUNC(name, type) \
+  static inline type \
+  ct_##name##_select (type a, type b, unsigned long op_enable) \
+  { \
+    type mask_b = (type)op_enable - (type)_gcry_ct_vone; \
+    type mask_a = (type)_gcry_ct_vzero - (type)op_enable; \
+    return (mask_a & a) | (mask_b & b); \
+  }
+DEFINE_CT_TYPE_SELECT_FUNC(uintptr, uintptr_t)
+DEFINE_CT_TYPE_SELECT_FUNC(ulong, unsigned long)
+
 /*
  *  Return NULL when OP_ENABLED=1
  *  otherwise, return W
@@ -54,10 +97,8 @@ unsigned int ct_memequal (const void *b1, const void *b2, size_t len);
 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);
+  uintptr_t o = ct_uintptr_select((uintptr_t)NULL, (uintptr_t)w, op_enable);
+  return (gcry_sexp_t)(void *)o;
 }
 
 /*
-- 
2.40.1




More information about the Gcrypt-devel mailing list