[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