From jussi.kivilinna at iki.fi Wed Nov 1 19:26:05 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Wed, 1 Nov 2023 20:26:05 +0200 Subject: [PATCH] bench-slope: update auto-GHZ for alderlake-P Message-ID: <20231101182605.2648623-1-jussi.kivilinna@iki.fi> * tests/bench-slope.c (vone): New. (auto_ghz_bench): Remove memory barrier usage; Generate constant values from volatile variable. -- Alderlake P-core is able of fuse "immediate to register addition" instructions with other instructions. This feature broke auto-GHZ detection in bench-slope. Patch forces generation of "register to register addition" by generating constant values from volatile source, which prevents compiler from known the values in constant value variables. Signed-off-by: Jussi Kivilinna --- tests/bench-slope.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/bench-slope.c b/tests/bench-slope.c index b8818aed..71bc97d0 100644 --- a/tests/bench-slope.c +++ b/tests/bench-slope.c @@ -515,6 +515,8 @@ err_free: /********************************************* CPU frequency auto-detection. */ +static volatile size_t vone = 1; + static int auto_ghz_init (struct bench_obj *obj) { @@ -535,6 +537,9 @@ auto_ghz_free (struct bench_obj *obj) static void auto_ghz_bench (struct bench_obj *obj, void *buf, size_t buflen) { + size_t one = vone; + size_t two = one + vone; + (void)obj; (void)buf; @@ -544,21 +549,12 @@ auto_ghz_bench (struct bench_obj *obj, void *buf, size_t buflen) * function will give cycles/iteration result 1024.0 on high-end CPUs. * With turbo, result will be less and can be used detect turbo-clock. */ -#ifdef HAVE_GCC_ASM_VOLATILE_MEMORY - /* Auto-ghz operation takes two CPU cycles to perform. Memory barriers - * are used to prevent compiler from optimizing this loop away. */ - #define AUTO_GHZ_OPERATION \ - asm volatile ("":"+r"(buflen)::"memory"); \ - buflen ^= 1; \ - asm volatile ("":"+r"(buflen)::"memory"); \ - buflen -= 2 -#else - /* TODO: Needs alternative way of preventing compiler optimizations. - * Mix of XOR and subtraction appears to do the trick for now. */ - #define AUTO_GHZ_OPERATION \ - buflen ^= 1; \ - buflen -= 2 -#endif + /* Auto-ghz operation takes two CPU cycles to perform. Variables are + * generated through volatile object and therefore compiler is unable + * to optimize these operations away. */ +#define AUTO_GHZ_OPERATION \ + buflen ^= one; \ + buflen -= two #define AUTO_GHZ_OPERATION_2 \ AUTO_GHZ_OPERATION; \ -- 2.40.1 From gniibe at fsij.org Thu Nov 2 02:34:43 2023 From: gniibe at fsij.org (NIIBE Yutaka) Date: Thu, 02 Nov 2023 10:34:43 +0900 Subject: Fixes for RSA and ElGamal Message-ID: <871qd9q6oc.fsf@akagi.fsij.org> Hello, In master, I pushed my fixes for RSA and ElGamal. It is to minimize timing difference (between success case and failure case) in unpadding PKCS#1 v1.5 padding and OAEP padding. -- From jussi.kivilinna at iki.fi Thu Nov 2 19:01:05 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:05 +0200 Subject: [PATCH 01/12] Use single constant-time memory comparison implementation Message-ID: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> * src/const-time.c (ct_not_memequal): Use original 'buf_eq_const' implementation here. (ct_memequal): New. * cipher/bufhelp.h (buf_eq_const): Call to 'ct_memequal'. -- Signed-off-by: Jussi Kivilinna --- cipher/bufhelp.h | 19 +++---------------- src/const-time.c | 36 +++++++++++++++++++++++------------- src/const-time.h | 13 +++++++++++++ 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/cipher/bufhelp.h b/cipher/bufhelp.h index fa5b2e8e..6dcc622a 100644 --- a/cipher/bufhelp.h +++ b/cipher/bufhelp.h @@ -22,6 +22,7 @@ #include "g10lib.h" #include "bithelp.h" +#include "const-time.h" #undef BUFHELP_UNALIGNED_ACCESS @@ -362,23 +363,9 @@ buf_xor_n_copy(void *_dst_xor, void *_srcdst_cpy, const void *_src, size_t len) /* Constant-time compare of two buffers. Returns 1 if buffers are equal, and 0 if buffers differ. */ static inline int -buf_eq_const(const void *_a, const void *_b, size_t len) +buf_eq_const(const void *a, const void *b, size_t len) { - const byte *a = _a; - const byte *b = _b; - int ab, ba; - size_t i; - - /* Constant-time compare. */ - for (i = 0, ab = 0, ba = 0; i < len; i++) - { - /* If a[i] != b[i], either ab or ba will be negative. */ - ab |= a[i] - b[i]; - ba |= b[i] - a[i]; - } - - /* 'ab | ba' is negative when buffers are not equal. */ - return (ab | ba) >= 0; + return ct_memequal (a, b, len); } diff --git a/src/const-time.c b/src/const-time.c index 400af993..fb787a02 100644 --- a/src/const-time.c +++ b/src/const-time.c @@ -26,25 +26,35 @@ /* * Compare byte arrays of length LEN, return 1 if it's not same, * 0, otherwise. - * - * Originally in NetBSD as "consttime_memequal" which is: - * - * Written by Matthias Drochner . - * Public domain. - * - * Modified the function name, return type to unsigned, - * and return value (0 <-> 1). */ unsigned int ct_not_memequal (const void *b1, const void *b2, size_t len) { - const unsigned char *c1 = b1, *c2 = b2; - unsigned int res = 0; + const byte *a = b1; + const byte *b = b2; + int ab, ba; + size_t i; + + /* Constant-time compare. */ + for (i = 0, ab = 0, ba = 0; i < len; i++) + { + /* If a[i] != b[i], either ab or ba will be negative. */ + ab |= a[i] - b[i]; + ba |= b[i] - a[i]; + } - while (len--) - res |= *c1++ ^ *c2++; + /* 'ab | ba' is negative when buffers are not equal, extract sign bit. */ + return ((unsigned int)(ab | ba) >> (sizeof(unsigned int) * 8 - 1)) & 1; +} - return ct_not_equal_byte (res, 0); +/* + * Compare byte arrays of length LEN, return 0 if it's not same, + * 1, otherwise. + */ +unsigned int +ct_memequal (const void *b1, const void *b2, size_t len) +{ + return ct_not_memequal (b1, b2, len) ^ 1; } /* diff --git a/src/const-time.h b/src/const-time.h index 53d7dd2a..defe5ff4 100644 --- a/src/const-time.h +++ b/src/const-time.h @@ -17,6 +17,12 @@ * License along with this program; if not, see . */ +#ifndef GCRY_CONST_TIME_H +#define GCRY_CONST_TIME_H + +#include "types.h" + + /* * Return 1 if it's not same, 0 if same. */ @@ -40,6 +46,11 @@ ct_not_equal_byte (unsigned char b0, unsigned char b1) any structure. */ unsigned int ct_not_memequal (const void *b1, const void *b2, size_t len); +/* Compare byte-arrays of length LEN, return 0 if it's not same, 1 + 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); + /* * Return NULL when OP_ENABLED=1 * otherwise, return W @@ -60,3 +71,5 @@ sexp_null_cond (gcry_sexp_t w, unsigned long op_enable) */ void ct_memmov_cond (void *dst, const void *src, size_t len, unsigned long op_enable); + +#endif /*GCRY_CONST_TIME_H*/ -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:01:08 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:08 +0200 Subject: [PATCH 04/12] mpih_set_cond: restore EM leakage mitigation In-Reply-To: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> References: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180113.2946414-4-jussi.kivilinna@iki.fi> * mpi/mpih-const-time.c (_gcry_mpih_set_cond): Replace single mask + XOR with dual mask + AND/OR; Add comment about reason for dual mask usage. (_gcry_mpih_add_n_cond, _gcry_mpih_sub_n_cond, _gcry_mpih_swap_cond) (_gcry_mpih_abs_cond): Add comment about reason for dual mask usage. -- Signed-off-by: Jussi Kivilinna --- mpi/mpih-const-time.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/mpi/mpih-const-time.c b/mpi/mpih-const-time.c index 3f0440a9..388d2a91 100644 --- a/mpi/mpih-const-time.c +++ b/mpi/mpih-const-time.c @@ -39,11 +39,15 @@ void _gcry_mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long op_enable) { + /* Note: dual mask with AND/OR used for EM leakage mitigation */ + mpi_limb_t mask1 = vzero - op_enable; + mpi_limb_t mask2 = op_enable - vone; mpi_size_t i; - mpi_limb_t mask = vzero - op_enable; for (i = 0; i < usize; i++) - wp[i] ^= mask & (wp[i] ^ up[i]); + { + wp[i] = (wp[i] & mask2) | (up[i] & mask1); + } } @@ -55,10 +59,11 @@ mpi_limb_t _gcry_mpih_add_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { - mpi_size_t i; - mpi_limb_t cy; + /* Note: dual mask with AND/OR used for EM leakage mitigation */ mpi_limb_t mask1 = vzero - op_enable; mpi_limb_t mask2 = op_enable - vone; + mpi_size_t i; + mpi_limb_t cy; cy = 0; for (i = 0; i < usize; i++) @@ -86,10 +91,11 @@ mpi_limb_t _gcry_mpih_sub_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { - mpi_size_t i; - mpi_limb_t cy; + /* Note: dual mask with AND/OR used for EM leakage mitigation */ mpi_limb_t mask1 = vzero - op_enable; mpi_limb_t mask2 = op_enable - vone; + mpi_size_t i; + mpi_limb_t cy; cy = 0; for (i = 0; i < usize; i++) @@ -117,9 +123,10 @@ void _gcry_mpih_swap_cond (mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { - mpi_size_t i; + /* Note: dual mask with AND/OR used for EM leakage mitigation */ mpi_limb_t mask1 = vzero - op_enable; mpi_limb_t mask2 = op_enable - vone; + mpi_size_t i; for (i = 0; i < usize; i++) { @@ -139,10 +146,11 @@ void _gcry_mpih_abs_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long op_enable) { - mpi_size_t i; + /* Note: dual mask with AND/OR used for EM leakage mitigation */ mpi_limb_t mask1 = vzero - op_enable; mpi_limb_t mask2 = op_enable - vone; mpi_limb_t cy = op_enable; + mpi_size_t i; for (i = 0; i < usize; i++) { -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:01:09 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:09 +0200 Subject: [PATCH 05/12] const-time: prefix global symbols with _gcry_ In-Reply-To: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> References: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180113.2946414-5-jussi.kivilinna@iki.fi> * cipher/const-time.c (ct_not_memequal, ct_memequal) (ct_memmov_cond): Rename these to ... (_gcry_ct_not_memequal, _gcry_ct_memequal) (_gcry_ct_memmov_cond): ... these. * cipher/const-time.h (ct_not_memequal, ct_memequal) (ct_memmov_cond): Rename these to ... (_gcry_ct_not_memequal, _gcry_ct_memequal) (_gcry_ct_memmov_cond): ... these. (ct_not_memequal, ct_memequal, ct_memmov_cond): New macros. -- Signed-off-by: Jussi Kivilinna --- src/const-time.c | 10 +++++----- src/const-time.h | 13 +++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/const-time.c b/src/const-time.c index 908c0ee9..2066d48d 100644 --- a/src/const-time.c +++ b/src/const-time.c @@ -36,7 +36,7 @@ volatile unsigned int _gcry_ct_vone = 1; * 0, otherwise. */ unsigned int -ct_not_memequal (const void *b1, const void *b2, size_t len) +_gcry_ct_not_memequal (const void *b1, const void *b2, size_t len) { const byte *a = b1; const byte *b = b2; @@ -60,9 +60,9 @@ ct_not_memequal (const void *b1, const void *b2, size_t len) * 1, otherwise. */ unsigned int -ct_memequal (const void *b1, const void *b2, size_t len) +_gcry_ct_memequal (const void *b1, const void *b2, size_t len) { - return ct_not_memequal (b1, b2, len) ^ 1; + return _gcry_ct_not_memequal (b1, b2, len) ^ 1; } /* @@ -71,8 +71,8 @@ ct_memequal (const void *b1, const void *b2, size_t len) * DST > SRC, the memory areas must not overlap. */ void -ct_memmov_cond (void *dst, const void *src, size_t len, - unsigned long op_enable) +_gcry_ct_memmov_cond (void *dst, const void *src, size_t len, + unsigned long op_enable) { size_t i; unsigned char mask; diff --git a/src/const-time.h b/src/const-time.h index 3a229ddb..e324dcb7 100644 --- a/src/const-time.h +++ b/src/const-time.h @@ -23,6 +23,11 @@ #include "types.h" +#define ct_not_memequal _gcry_ct_not_memequal +#define ct_memequal _gcry_ct_memequal +#define ct_memmov_cond _gcry_ct_memmov_cond + + extern volatile unsigned int _gcry_ct_vzero; extern volatile unsigned int _gcry_ct_vone; @@ -68,12 +73,12 @@ ct_not_equal_byte (unsigned char b0, unsigned char b1) /* Compare byte-arrays of length LEN, return 1 if it's not same, 0 otherwise. We use pointer of void *, so that it can be used with any structure. */ -unsigned int ct_not_memequal (const void *b1, const void *b2, size_t len); +unsigned int _gcry_ct_not_memequal (const void *b1, const void *b2, size_t len); /* Compare byte-arrays of length LEN, return 0 if it's not same, 1 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); +unsigned int _gcry_ct_memequal (const void *b1, const void *b2, size_t len); /* * Return A when OP_ENABLED=1 @@ -106,7 +111,7 @@ sexp_null_cond (gcry_sexp_t w, unsigned long op_enable) * OP_ENABLED=1. When DST <= SRC, the memory areas may overlap. When * DST > SRC, the memory areas must not overlap. */ -void ct_memmov_cond (void *dst, const void *src, size_t len, - unsigned long op_enable); +void _gcry_ct_memmov_cond (void *dst, const void *src, size_t len, + unsigned long op_enable); #endif /*GCRY_CONST_TIME_H*/ -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:01:11 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:11 +0200 Subject: [PATCH 07/12] mpih-const-time: use global vzero/vone variable In-Reply-To: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> References: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180113.2946414-7-jussi.kivilinna@iki.fi> * mpi/mpih-const-time.c (vzero, vone): Remove. (_gcry_mpih_set_cond, _gcry_mpih_add_n_cond, _gcry_mpih_sub_n_cond) (_gcry_mpih_swap_cond, _gcry_mpih_abs_cond): Use _gcry_ct_vzero and _gcry_ct_vone. -- Signed-off-by: Jussi Kivilinna --- mpi/mpih-const-time.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/mpi/mpih-const-time.c b/mpi/mpih-const-time.c index 388d2a91..85dba389 100644 --- a/mpi/mpih-const-time.c +++ b/mpi/mpih-const-time.c @@ -22,14 +22,10 @@ #include #include "mpi-internal.h" #include "g10lib.h" +#include "const-time.h" #define A_LIMB_1 ((mpi_limb_t)1) -/* 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. */ -static volatile mpi_limb_t vzero = 0; -static volatile mpi_limb_t vone = 1; /* * W = U when OP_ENABLED=1 @@ -40,8 +36,8 @@ _gcry_mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = vzero - op_enable; - mpi_limb_t mask2 = op_enable - vone; + mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; + mpi_limb_t mask2 = op_enable - _gcry_ct_vone; mpi_size_t i; for (i = 0; i < usize; i++) @@ -60,8 +56,8 @@ _gcry_mpih_add_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = vzero - op_enable; - mpi_limb_t mask2 = op_enable - vone; + mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; + mpi_limb_t mask2 = op_enable - _gcry_ct_vone; mpi_size_t i; mpi_limb_t cy; @@ -92,8 +88,8 @@ _gcry_mpih_sub_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = vzero - op_enable; - mpi_limb_t mask2 = op_enable - vone; + mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; + mpi_limb_t mask2 = op_enable - _gcry_ct_vone; mpi_size_t i; mpi_limb_t cy; @@ -124,8 +120,8 @@ _gcry_mpih_swap_cond (mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = vzero - op_enable; - mpi_limb_t mask2 = op_enable - vone; + mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; + mpi_limb_t mask2 = op_enable - _gcry_ct_vone; mpi_size_t i; for (i = 0; i < usize; i++) @@ -147,8 +143,8 @@ _gcry_mpih_abs_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = vzero - op_enable; - mpi_limb_t mask2 = op_enable - vone; + mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; + mpi_limb_t mask2 = op_enable - _gcry_ct_vone; mpi_limb_t cy = op_enable; mpi_size_t i; -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:01:07 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:07 +0200 Subject: [PATCH 03/12] rsa, elgamal: avoid logical not operator in constant-time code In-Reply-To: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> References: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180113.2946414-3-jussi.kivilinna@iki.fi> * 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 --- 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 From jussi.kivilinna at iki.fi Thu Nov 2 19:01:10 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:10 +0200 Subject: [PATCH 06/12] const-time: ct_memmov_cond: switch to use dual mask approach In-Reply-To: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> References: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180113.2946414-6-jussi.kivilinna@iki.fi> * src/const-time.c (_gcry_ct_memmov_cond): Use dual mask + AND/OR instead of single mask + XOR. -- Signed-off-by: Jussi Kivilinna --- src/const-time.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/const-time.c b/src/const-time.c index 2066d48d..73bf8b40 100644 --- a/src/const-time.c +++ b/src/const-time.c @@ -74,12 +74,13 @@ void _gcry_ct_memmov_cond (void *dst, const void *src, size_t len, unsigned long op_enable) { - size_t i; - unsigned char mask; + /* Note: dual mask with AND/OR used for EM leakage mitigation */ + unsigned char mask1 = _gcry_ct_vzero - op_enable; + unsigned char mask2 = op_enable - _gcry_ct_vone; unsigned char *b_dst = dst; const unsigned char *b_src = src; + size_t i; - mask = -(unsigned char)op_enable; for (i = 0; i < len; i++) - b_dst[i] ^= mask & (b_dst[i] ^ b_src[i]); + b_dst[i] = (b_dst[i] & mask2) | (b_src[i] & mask1); } -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:01:12 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:12 +0200 Subject: [PATCH 08/12] mpiutil: use global vone and vzero In-Reply-To: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> References: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180113.2946414-8-jussi.kivilinna@iki.fi> * mpi/mpiutil.c (_gcry_mpi_set_cond, _gcry_mpi_swap_cond): Use _gcry_ct_vzero and _gcry_ct_vone. -- Signed-off-by: Jussi Kivilinna --- mpi/mpiutil.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/mpi/mpiutil.c b/mpi/mpiutil.c index d5a1b8a8..f7506718 100644 --- a/mpi/mpiutil.c +++ b/mpi/mpiutil.c @@ -27,6 +27,7 @@ #include "g10lib.h" #include "mpi-internal.h" #include "mod-source-info.h" +#include "const-time.h" #if SIZEOF_UNSIGNED_INT == 2 @@ -46,12 +47,6 @@ /* Constants allocated right away at startup. */ static gcry_mpi_t constants[MPI_NUMBER_OF_CONSTANTS]; -/* 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. */ -static volatile mpi_limb_t vzero = 0; -static volatile mpi_limb_t vone = 1; - const char * _gcry_mpi_get_hw_config (void) @@ -513,10 +508,11 @@ _gcry_mpi_set (gcry_mpi_t w, gcry_mpi_t u) gcry_mpi_t _gcry_mpi_set_cond (gcry_mpi_t w, const gcry_mpi_t u, unsigned long set) { + /* Note: dual mask with AND/OR used for EM leakage mitigation */ + mpi_limb_t mask1 = _gcry_ct_vzero - set; + mpi_limb_t mask2 = set - _gcry_ct_vone; mpi_size_t i; mpi_size_t nlimbs = u->alloced; - mpi_limb_t mask1 = vzero - set; - mpi_limb_t mask2 = set - vone; mpi_limb_t xu; mpi_limb_t xw; mpi_limb_t *uu = u->d; @@ -614,10 +610,11 @@ _gcry_mpi_swap (gcry_mpi_t a, gcry_mpi_t b) void _gcry_mpi_swap_cond (gcry_mpi_t a, gcry_mpi_t b, unsigned long swap) { + /* Note: dual mask with AND/OR used for EM leakage mitigation */ + mpi_limb_t mask1 = _gcry_ct_vzero - swap; + mpi_limb_t mask2 = swap - _gcry_ct_vone; mpi_size_t i; mpi_size_t nlimbs; - mpi_limb_t mask1 = vzero - swap; - mpi_limb_t mask2 = swap - vone; mpi_limb_t *ua = a->d; mpi_limb_t *ub = b->d; mpi_limb_t xa; -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:01:06 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:06 +0200 Subject: [PATCH 02/12] const-time: always avoid comparison operator for byte comparison In-Reply-To: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> References: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180113.2946414-2-jussi.kivilinna@iki.fi> * configure.ac: Remove POSSIBLE_CONDITIONAL_BRANCH_IN_BYTE_COMPARISON macro. * src/const-time.h (ct_not_equal_byte): Remove POSSIBLE_CONDITIONAL_BRANCH_IN_BYTE_COMPARISON ifdef. -- Performance impact of avoiding comparison is negligible, so remove the option for using comparison to make this easier maintain (tested on every arch). Signed-off-by: Jussi Kivilinna --- configure.ac | 5 ----- src/const-time.h | 4 ---- 2 files changed, 9 deletions(-) diff --git a/configure.ac b/configure.ac index aead8347..4f7c3a52 100644 --- a/configure.ac +++ b/configure.ac @@ -121,11 +121,6 @@ AH_BOTTOM([ properly prefixed. */ #define CAMELLIA_EXT_SYM_PREFIX _gcry_ -/* For some architectures or a specific compiler vendor, we need - to care about possible conditional branch in byte comparison. */ -#if defined(__vax__) || defined(AVR) || defined(_MSC_VER) -# define POSSIBLE_CONDITIONAL_BRANCH_IN_BYTE_COMPARISON 1 -#endif #endif /*_GCRYPT_CONFIG_H_INCLUDED*/ ]) diff --git a/src/const-time.h b/src/const-time.h index defe5ff4..4f14f86b 100644 --- a/src/const-time.h +++ b/src/const-time.h @@ -29,16 +29,12 @@ static inline unsigned int ct_not_equal_byte (unsigned char b0, unsigned char b1) { -#ifdef POSSIBLE_CONDITIONAL_BRANCH_IN_BYTE_COMPARISON unsigned int diff; diff = b0; diff ^= b1; return (0U - diff) >> (sizeof (unsigned int)*8 - 1); -#else - return b0 != b1; -#endif } /* Compare byte-arrays of length LEN, return 1 if it's not same, 0 -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:01:13 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:01:13 +0200 Subject: [PATCH 09/12] ec-nist: use global vone and vzero In-Reply-To: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> References: <20231102180113.2946414-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180113.2946414-9-jussi.kivilinna@iki.fi> * mpi/ec-nist.c (vzero, vone): Remove. (_gcry_mpi_ec_nist192_mod, _gcry_mpi_ec_nist224_mod) (_gcry_mpi_ec_nist256_mod, _gcry_mpi_ec_nist384_mod): Use _gcry_ct_vzero and _gcry_ct_vone. -- Signed-off-by: Jussi Kivilinna --- mpi/ec-nist.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/mpi/ec-nist.c b/mpi/ec-nist.c index 14e3c3ab..a822af76 100644 --- a/mpi/ec-nist.c +++ b/mpi/ec-nist.c @@ -32,13 +32,7 @@ #include "context.h" #include "ec-context.h" #include "ec-inline.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. */ -static volatile mpi_limb_t vzero = 0; -static volatile mpi_limb_t vone = 1; +#include "const-time.h" static inline @@ -147,8 +141,8 @@ _gcry_mpi_ec_nist192_mod (gcry_mpi_t w, mpi_ec_t ctx) s_is_negative = LO32_LIMB64(s[3]) >> 31; - mask2 = vzero - s_is_negative; - mask1 = s_is_negative - vone; + mask2 = _gcry_ct_vzero - s_is_negative; + mask1 = s_is_negative - _gcry_ct_vone; STORE64_COND(wp, 0, mask2, o[0], mask1, s[0]); STORE64_COND(wp, 1, mask2, o[1], mask1, s[1]); @@ -270,8 +264,8 @@ _gcry_mpi_ec_nist224_mod (gcry_mpi_t w, mpi_ec_t ctx) s_is_negative = (HI32_LIMB64(s[3]) >> 31); - mask2 = vzero - s_is_negative; - mask1 = s_is_negative - vone; + mask2 = _gcry_ct_vzero - s_is_negative; + mask1 = s_is_negative - _gcry_ct_vone; STORE64_COND(wp, 0, mask2, d[0], mask1, s[0]); STORE64_COND(wp, 1, mask2, d[1], mask1, s[1]); @@ -499,9 +493,9 @@ _gcry_mpi_ec_nist256_mod (gcry_mpi_t w, mpi_ec_t ctx) s_is_negative = LO32_LIMB64(s[4]) >> 31; d_is_negative = LO32_LIMB64(d[4]) >> 31; - mask3 = vzero - d_is_negative; - mask2 = (vzero - s_is_negative) & ~mask3; - mask1 = (s_is_negative - vone) & ~mask3; + mask3 = _gcry_ct_vzero - d_is_negative; + mask2 = (_gcry_ct_vzero - s_is_negative) & ~mask3; + mask1 = (s_is_negative - _gcry_ct_vone) & ~mask3; s[0] = LIMB_OR64(MASK_AND64(mask2, d[0]), MASK_AND64(mask1, s[0])); s[1] = LIMB_OR64(MASK_AND64(mask2, d[1]), MASK_AND64(mask1, s[1])); @@ -770,8 +764,8 @@ _gcry_mpi_ec_nist384_mod (gcry_mpi_t w, mpi_ec_t ctx) p_mult[0 + 3][1], p_mult[0 + 3][0]); s_is_negative = LO32_LIMB64(s[6]) >> 31; - mask2 = vzero - s_is_negative; - mask1 = s_is_negative - vone; + mask2 = _gcry_ct_vzero - s_is_negative; + mask1 = s_is_negative - _gcry_ct_vone; STORE64_COND(wp, 0, mask2, d[0], mask1, s[0]); STORE64_COND(wp, 1, mask2, d[1], mask1, s[1]); -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:06:37 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:06:37 +0200 Subject: [PATCH 10/12] mpih_cmp_ui: avoid unintentional conditional branch Message-ID: <20231102180639.2947240-1-jussi.kivilinna@iki.fi> * mpi/mpi-internal.h (mpih_limb_is_zero): New. * mpi/mpih-const-time.c (_gcry_mpih_cmp_ui): Use mpih_limb_is_zero instead of comparison. -- Signed-off-by: Jussi Kivilinna --- mpi/mpi-internal.h | 9 +++++++++ mpi/mpih-const-time.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mpi/mpi-internal.h b/mpi/mpi-internal.h index 79a6cce7..bb12e86c 100644 --- a/mpi/mpi-internal.h +++ b/mpi/mpi-internal.h @@ -267,6 +267,15 @@ mpi_limb_t _gcry_mpih_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, #define mpih_abs_cond(w,u,s,o) _gcry_mpih_abs_cond ((w),(u),(s),(o)) #define mpih_mod(v,vs,u,us) _gcry_mpih_mod ((v),(vs),(u),(us)) +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); +} + void _gcry_mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long op_enable); mpi_limb_t _gcry_mpih_add_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, diff --git a/mpi/mpih-const-time.c b/mpi/mpih-const-time.c index 85dba389..3d854e8c 100644 --- a/mpi/mpih-const-time.c +++ b/mpi/mpih-const-time.c @@ -202,7 +202,7 @@ _gcry_mpih_cmp_ui (mpi_ptr_t up, mpi_size_t usize, unsigned long v) mpi_size_t i; for (i = 1; i < usize; i++) - is_all_zero &= (up[i] == 0); + is_all_zero &= mpih_limb_is_zero (up[i]); if (is_all_zero) { -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:06:39 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:06:39 +0200 Subject: [PATCH 12/12] ec: avoid unintentional condition branches for 25519, 448 and 256k1 In-Reply-To: <20231102180639.2947240-1-jussi.kivilinna@iki.fi> References: <20231102180639.2947240-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180639.2947240-3-jussi.kivilinna@iki.fi> * mpi/ec.c (ec_addm_25519, ec_subm_25519, ec_mulm_25519, ec_addm_448) (ec_subm_448, ec_mulm_448, ec_secp256k1_mod): Use mpih_limb_is_zero and mpih_limb_is_not_zero instead of comparison to zero. -- Signed-off-by: Jussi Kivilinna --- mpi/ec.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mpi/ec.c b/mpi/ec.c index b0d0f56b..4b7a1afd 100644 --- a/mpi/ec.c +++ b/mpi/ec.c @@ -375,7 +375,7 @@ ec_addm_25519 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) _gcry_mpih_add_n (wp, up, vp, wsize); borrow = _gcry_mpih_sub_n (n, wp, ctx->p->d, wsize); - mpih_set_cond (wp, n, wsize, (borrow == 0UL)); + mpih_set_cond (wp, n, wsize, mpih_limb_is_zero (borrow)); wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB)); } @@ -396,7 +396,7 @@ ec_subm_25519 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) borrow = _gcry_mpih_sub_n (wp, up, vp, wsize); _gcry_mpih_add_n (n, wp, ctx->p->d, wsize); - mpih_set_cond (wp, n, wsize, (borrow != 0UL)); + mpih_set_cond (wp, n, wsize, mpih_limb_is_not_zero (borrow)); wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB)); } @@ -433,7 +433,7 @@ ec_mulm_25519 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) _gcry_mpih_add_n (wp, wp, n, wsize); cy = _gcry_mpih_sub_n (n, wp, ctx->p->d, wsize); - mpih_set_cond (wp, n, wsize, (cy == 0UL)); + mpih_set_cond (wp, n, wsize, mpih_limb_is_zero (cy)); } static void @@ -470,7 +470,7 @@ ec_addm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) cy = _gcry_mpih_add_n (wp, up, vp, wsize); _gcry_mpih_sub_n (n, wp, ctx->p->d, wsize); - mpih_set_cond (wp, n, wsize, (cy != 0UL)); + mpih_set_cond (wp, n, wsize, mpih_limb_is_not_zero (cy)); } static void @@ -490,7 +490,7 @@ ec_subm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) borrow = _gcry_mpih_sub_n (wp, up, vp, wsize); _gcry_mpih_add_n (n, wp, ctx->p->d, wsize); - mpih_set_cond (wp, n, wsize, (borrow != 0UL)); + mpih_set_cond (wp, n, wsize, mpih_limb_is_not_zero (borrow)); } static void @@ -561,7 +561,7 @@ ec_mulm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) _gcry_mpih_add_n (wp, wp, n, wsize); cy = _gcry_mpih_sub_n (n, wp, ctx->p->d, wsize); - mpih_set_cond (wp, n, wsize, (cy == 0UL)); + mpih_set_cond (wp, n, wsize, mpih_limb_is_zero (cy)); } static void @@ -622,7 +622,8 @@ ec_secp256k1_mod (gcry_mpi_t w, mpi_ec_t ctx) cy = _gcry_mpih_add_n (wp, wp, n, wsize); borrow = _gcry_mpih_sub_n (s, wp, ctx->p->d, wsize); - mpih_set_cond (wp, s, wsize, (cy != 0UL) | (borrow == 0UL)); + mpih_set_cond (wp, s, wsize, + mpih_limb_is_not_zero (cy) | mpih_limb_is_zero (borrow)); w->nlimbs = wsize; MPN_NORMALIZE (wp, w->nlimbs); -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 19:06:38 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 20:06:38 +0200 Subject: [PATCH 11/12] ec-nist: avoid unintentional conditional branch by comparison In-Reply-To: <20231102180639.2947240-1-jussi.kivilinna@iki.fi> References: <20231102180639.2947240-1-jussi.kivilinna@iki.fi> Message-ID: <20231102180639.2947240-2-jussi.kivilinna@iki.fi> * mpi/ec-nist.c (_gcry_mpi_ec_nist521_mod): Use mpih_limb_is_not_zero. * mpi/mpi-internal.h (mpih_limb_is_not_zero): New. -- Signed-off-by: Jussi Kivilinna --- mpi/ec-nist.c | 2 +- mpi/mpi-internal.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mpi/ec-nist.c b/mpi/ec-nist.c index a822af76..6dfaa1da 100644 --- a/mpi/ec-nist.c +++ b/mpi/ec-nist.c @@ -808,7 +808,7 @@ _gcry_mpi_ec_nist521_mod (gcry_mpi_t w, mpi_ec_t ctx) /* "mod p" */ cy = _gcry_mpih_sub_n (wp, wp, ctx->p->d, wsize); _gcry_mpih_add_n (s, wp, ctx->p->d, wsize); - mpih_set_cond (wp, s, wsize, (cy != 0UL)); + mpih_set_cond (wp, s, wsize, mpih_limb_is_not_zero (cy)); w->nlimbs = wsize; MPN_NORMALIZE (wp, w->nlimbs); diff --git a/mpi/mpi-internal.h b/mpi/mpi-internal.h index bb12e86c..70045037 100644 --- a/mpi/mpi-internal.h +++ b/mpi/mpi-internal.h @@ -276,6 +276,15 @@ mpih_limb_is_zero (mpi_limb_t 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); +} + void _gcry_mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long op_enable); mpi_limb_t _gcry_mpih_add_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 20:27:49 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 21:27:49 +0200 Subject: [PATCH 1/2] const-time: add functions for generating masks from 0/1 input Message-ID: <20231102192750.3021787-1-jussi.kivilinna@iki.fi> * mpi/ec-nist.c (ct_limb_gen_mask, ct_limb_gen_inv_mask): New. (_gcry_mpi_ec_nist192_mod, _gcry_mpi_ec_nist224_mod) (_gcry_mpi_ec_nist256_mod, _gcry_mpi_ec_nist384_mod): Use mask generating functions. * mpi/mpih-const-time.c (ct_limb_gen_mask, ct_limb_gen_inv_mask): New. (_gcry_mpih_set_cond, _gcry_mpih_add_n_cond, _gcry_mpih_sub_n_cond) (_gcry_mpih_sub_n_cond, _gcry_mpih_swap_cond): Use mask generating functions. * mpi/mpiutil.c (ct_limb_gen_mask, ct_limb_gen_inv_mask): New. (_gcry_mpi_set_cond, _gcry_mpi_swap_cond): Use mask generating functions. * src/const-time.h (DEFINE_CT_TYPE_GEN_MASK, ct_uintptr_gen_mask) (ct_ulong_gen_mask, DEFINE_CT_TYPE_GEN_INV_MASK, ct_uintptr_gen_inv_mask) (ct_ulong_gen_inv_mask): New. (DEFINE_CT_TYPE_SELECT_FUNC): Use mask generating functions. * src/const-time.c (_gcry_ct_memmov_cond): Use mask generating functions. -- Provide functions for generating mask for constant time operations. Signed-off-by: Jussi Kivilinna --- mpi/ec-nist.c | 22 ++++++++++-------- mpi/mpih-const-time.c | 24 +++++++++++--------- mpi/mpiutil.c | 12 ++++++---- src/const-time.c | 4 ++-- src/const-time.h | 52 +++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 87 insertions(+), 27 deletions(-) diff --git a/mpi/ec-nist.c b/mpi/ec-nist.c index 6dfaa1da..f3b3cd66 100644 --- a/mpi/ec-nist.c +++ b/mpi/ec-nist.c @@ -35,6 +35,10 @@ #include "const-time.h" +DEFINE_CT_TYPE_GEN_MASK(limb, mpi_limb_t) +DEFINE_CT_TYPE_GEN_INV_MASK(limb, mpi_limb_t) + + static inline void prefetch(const void *tab, size_t len) { @@ -141,8 +145,8 @@ _gcry_mpi_ec_nist192_mod (gcry_mpi_t w, mpi_ec_t ctx) s_is_negative = LO32_LIMB64(s[3]) >> 31; - mask2 = _gcry_ct_vzero - s_is_negative; - mask1 = s_is_negative - _gcry_ct_vone; + mask2 = ct_limb_gen_mask(s_is_negative); + mask1 = ct_limb_gen_inv_mask(s_is_negative); STORE64_COND(wp, 0, mask2, o[0], mask1, s[0]); STORE64_COND(wp, 1, mask2, o[1], mask1, s[1]); @@ -264,8 +268,8 @@ _gcry_mpi_ec_nist224_mod (gcry_mpi_t w, mpi_ec_t ctx) s_is_negative = (HI32_LIMB64(s[3]) >> 31); - mask2 = _gcry_ct_vzero - s_is_negative; - mask1 = s_is_negative - _gcry_ct_vone; + mask2 = ct_limb_gen_mask(s_is_negative); + mask1 = ct_limb_gen_inv_mask(s_is_negative); STORE64_COND(wp, 0, mask2, d[0], mask1, s[0]); STORE64_COND(wp, 1, mask2, d[1], mask1, s[1]); @@ -493,9 +497,9 @@ _gcry_mpi_ec_nist256_mod (gcry_mpi_t w, mpi_ec_t ctx) s_is_negative = LO32_LIMB64(s[4]) >> 31; d_is_negative = LO32_LIMB64(d[4]) >> 31; - mask3 = _gcry_ct_vzero - d_is_negative; - mask2 = (_gcry_ct_vzero - s_is_negative) & ~mask3; - mask1 = (s_is_negative - _gcry_ct_vone) & ~mask3; + mask3 = ct_limb_gen_mask(d_is_negative); + mask2 = ct_limb_gen_mask(s_is_negative) & ~mask3; + mask1 = ct_limb_gen_inv_mask(s_is_negative) & ~mask3; s[0] = LIMB_OR64(MASK_AND64(mask2, d[0]), MASK_AND64(mask1, s[0])); s[1] = LIMB_OR64(MASK_AND64(mask2, d[1]), MASK_AND64(mask1, s[1])); @@ -764,8 +768,8 @@ _gcry_mpi_ec_nist384_mod (gcry_mpi_t w, mpi_ec_t ctx) p_mult[0 + 3][1], p_mult[0 + 3][0]); s_is_negative = LO32_LIMB64(s[6]) >> 31; - mask2 = _gcry_ct_vzero - s_is_negative; - mask1 = s_is_negative - _gcry_ct_vone; + mask2 = ct_limb_gen_mask(s_is_negative); + mask1 = ct_limb_gen_inv_mask(s_is_negative); STORE64_COND(wp, 0, mask2, d[0], mask1, s[0]); STORE64_COND(wp, 1, mask2, d[1], mask1, s[1]); diff --git a/mpi/mpih-const-time.c b/mpi/mpih-const-time.c index 3d854e8c..4ebd072d 100644 --- a/mpi/mpih-const-time.c +++ b/mpi/mpih-const-time.c @@ -27,6 +27,10 @@ #define A_LIMB_1 ((mpi_limb_t)1) +DEFINE_CT_TYPE_GEN_MASK(limb, mpi_limb_t) +DEFINE_CT_TYPE_GEN_INV_MASK(limb, mpi_limb_t) + + /* * W = U when OP_ENABLED=1 * otherwise, W keeps old value @@ -36,8 +40,8 @@ _gcry_mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; - mpi_limb_t mask2 = op_enable - _gcry_ct_vone; + mpi_limb_t mask1 = ct_limb_gen_mask(op_enable); + mpi_limb_t mask2 = ct_limb_gen_inv_mask(op_enable); mpi_size_t i; for (i = 0; i < usize; i++) @@ -56,8 +60,8 @@ _gcry_mpih_add_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; - mpi_limb_t mask2 = op_enable - _gcry_ct_vone; + mpi_limb_t mask1 = ct_limb_gen_mask(op_enable); + mpi_limb_t mask2 = ct_limb_gen_inv_mask(op_enable); mpi_size_t i; mpi_limb_t cy; @@ -88,8 +92,8 @@ _gcry_mpih_sub_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; - mpi_limb_t mask2 = op_enable - _gcry_ct_vone; + mpi_limb_t mask1 = ct_limb_gen_mask(op_enable); + mpi_limb_t mask2 = ct_limb_gen_inv_mask(op_enable); mpi_size_t i; mpi_limb_t cy; @@ -120,8 +124,8 @@ _gcry_mpih_swap_cond (mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; - mpi_limb_t mask2 = op_enable - _gcry_ct_vone; + mpi_limb_t mask1 = ct_limb_gen_mask(op_enable); + mpi_limb_t mask2 = ct_limb_gen_inv_mask(op_enable); mpi_size_t i; for (i = 0; i < usize; i++) @@ -143,8 +147,8 @@ _gcry_mpih_abs_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = _gcry_ct_vzero - op_enable; - mpi_limb_t mask2 = op_enable - _gcry_ct_vone; + mpi_limb_t mask1 = ct_limb_gen_mask(op_enable); + mpi_limb_t mask2 = ct_limb_gen_inv_mask(op_enable); mpi_limb_t cy = op_enable; mpi_size_t i; diff --git a/mpi/mpiutil.c b/mpi/mpiutil.c index f7506718..954bb1ea 100644 --- a/mpi/mpiutil.c +++ b/mpi/mpiutil.c @@ -30,6 +30,10 @@ #include "const-time.h" +DEFINE_CT_TYPE_GEN_MASK(limb, mpi_limb_t) +DEFINE_CT_TYPE_GEN_INV_MASK(limb, mpi_limb_t) + + #if SIZEOF_UNSIGNED_INT == 2 # define MY_UINT_MAX 0xffff /* (visual check: 0123 ) */ @@ -509,8 +513,8 @@ gcry_mpi_t _gcry_mpi_set_cond (gcry_mpi_t w, const gcry_mpi_t u, unsigned long set) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = _gcry_ct_vzero - set; - mpi_limb_t mask2 = set - _gcry_ct_vone; + mpi_limb_t mask1 = ct_limb_gen_mask(set); + mpi_limb_t mask2 = ct_limb_gen_inv_mask(set); mpi_size_t i; mpi_size_t nlimbs = u->alloced; mpi_limb_t xu; @@ -611,8 +615,8 @@ void _gcry_mpi_swap_cond (gcry_mpi_t a, gcry_mpi_t b, unsigned long swap) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - mpi_limb_t mask1 = _gcry_ct_vzero - swap; - mpi_limb_t mask2 = swap - _gcry_ct_vone; + mpi_limb_t mask1 = ct_limb_gen_mask(swap); + mpi_limb_t mask2 = ct_limb_gen_inv_mask(swap); mpi_size_t i; mpi_size_t nlimbs; mpi_limb_t *ua = a->d; diff --git a/src/const-time.c b/src/const-time.c index 73bf8b40..55edd979 100644 --- a/src/const-time.c +++ b/src/const-time.c @@ -75,8 +75,8 @@ _gcry_ct_memmov_cond (void *dst, const void *src, size_t len, unsigned long op_enable) { /* Note: dual mask with AND/OR used for EM leakage mitigation */ - unsigned char mask1 = _gcry_ct_vzero - op_enable; - unsigned char mask2 = op_enable - _gcry_ct_vone; + unsigned char mask1 = ct_ulong_gen_mask(op_enable); + unsigned char mask2 = ct_ulong_gen_inv_mask(op_enable); unsigned char *b_dst = dst; const unsigned char *b_src = src; size_t i; diff --git a/src/const-time.h b/src/const-time.h index e324dcb7..cfb59f9a 100644 --- a/src/const-time.h +++ b/src/const-time.h @@ -80,6 +80,54 @@ unsigned int _gcry_ct_not_memequal (const void *b1, const void *b2, size_t len); any structure. */ unsigned int _gcry_ct_memequal (const void *b1, const void *b2, size_t len); +/* + * Return all bits set if A is 1 and return 0 otherwise. + */ +#ifdef HAVE_GCC_ASM_VOLATILE_MEMORY +# define DEFINE_CT_TYPE_GEN_MASK(name, type) \ + static inline type \ + ct_##name##_gen_mask (unsigned long op_enable) \ + { \ + type mask = -(type)op_enable; \ + asm volatile ("\n" :: "r" (mask) : "memory"); \ + return mask; \ + } +#else +# define DEFINE_CT_TYPE_GEN_MASK(name, type) \ + static inline type \ + ct_##name##_gen_mask (unsigned long op_enable) \ + { \ + type mask = (type)_gcry_ct_vzero - (type)op_enable; \ + return mask; \ + } +#endif +DEFINE_CT_TYPE_GEN_MASK(uintptr, uintptr_t) +DEFINE_CT_TYPE_GEN_MASK(ulong, unsigned long) + +/* + * Return all bits set if A is 0 and return 1 otherwise. + */ +#ifdef HAVE_GCC_ASM_VOLATILE_MEMORY +# define DEFINE_CT_TYPE_GEN_INV_MASK(name, type) \ + static inline type \ + ct_##name##_gen_inv_mask (unsigned long op_enable) \ + { \ + type mask = (type)op_enable - (type)1; \ + asm volatile ("\n" :: "r" (mask) : "memory"); \ + return mask; \ + } +#else +# define DEFINE_CT_TYPE_GEN_INV_MASK(name, type) \ + static inline type \ + ct_##name##_gen_inv_mask (unsigned long op_enable) \ + { \ + type mask = (type)op_enable - (type)_gcry_ct_vone; \ + return mask; \ + } +#endif +DEFINE_CT_TYPE_GEN_INV_MASK(uintptr, uintptr_t) +DEFINE_CT_TYPE_GEN_INV_MASK(ulong, unsigned long) + /* * Return A when OP_ENABLED=1 * otherwise, return B @@ -88,8 +136,8 @@ unsigned int _gcry_ct_memequal (const void *b1, const void *b2, size_t len); 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; \ + type mask_b = ct_##name##_gen_inv_mask(op_enable); \ + type mask_a = ct_##name##_gen_mask(op_enable); \ return (mask_a & a) | (mask_b & b); \ } DEFINE_CT_TYPE_SELECT_FUNC(uintptr, uintptr_t) -- 2.40.1 From jussi.kivilinna at iki.fi Thu Nov 2 20:27:50 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Thu, 2 Nov 2023 21:27:50 +0200 Subject: [PATCH 2/2] mpih-const-time: use constant-time comparisons conditional add/sub/abs In-Reply-To: <20231102192750.3021787-1-jussi.kivilinna@iki.fi> References: <20231102192750.3021787-1-jussi.kivilinna@iki.fi> Message-ID: <20231102192750.3021787-2-jussi.kivilinna@iki.fi> * mpi/mpih-const-time.c (mpih_ct_limb_greater_than) (mpih_ct_limb_less_than): New. (_gcry_mpih_add_n_cond, _gcry_mpih_sub_n_cond, _gcry_mpih_abs_cond): Use mpih_ct_limb_greater_than and mpih_ct_limb_less_than for comparisons. -- Signed-off-by: Jussi Kivilinna --- mpi/mpih-const-time.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/mpi/mpih-const-time.c b/mpi/mpih-const-time.c index 4ebd072d..2300c666 100644 --- a/mpi/mpih-const-time.c +++ b/mpi/mpih-const-time.c @@ -23,6 +23,7 @@ #include "mpi-internal.h" #include "g10lib.h" #include "const-time.h" +#include "longlong.h" #define A_LIMB_1 ((mpi_limb_t)1) @@ -31,6 +32,28 @@ DEFINE_CT_TYPE_GEN_MASK(limb, mpi_limb_t) DEFINE_CT_TYPE_GEN_INV_MASK(limb, mpi_limb_t) +/* + * Return 1 if X > Y and otherwise return 0. + */ +static mpi_limb_t +mpih_ct_limb_greater_than (mpi_limb_t x, mpi_limb_t y) +{ + mpi_limb_t diff_hi, diff_lo; + sub_ddmmss (diff_hi, diff_lo, 0, y, 0, x); + return diff_hi >> (BITS_PER_MPI_LIMB - 1); +} + + +/* + * Return 1 if X < Y and otherwise return 0. + */ +static mpi_limb_t +mpih_ct_limb_less_than (mpi_limb_t x, mpi_limb_t y) +{ + return mpih_ct_limb_greater_than (y, x); +} + + /* * W = U when OP_ENABLED=1 * otherwise, W keeps old value @@ -70,11 +93,11 @@ _gcry_mpih_add_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, { mpi_limb_t u = up[i]; mpi_limb_t x = u + vp[i]; - mpi_limb_t cy1 = x < u; + mpi_limb_t cy1 = mpih_ct_limb_less_than(x, u); mpi_limb_t cy2; x = x + cy; - cy2 = x < cy; + cy2 = mpih_ct_limb_less_than(x, cy); cy = cy1 | cy2; wp[i] = (u & mask2) | (x & mask1); } @@ -102,10 +125,10 @@ _gcry_mpih_sub_n_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_ptr_t vp, { mpi_limb_t u = up[i]; mpi_limb_t x = u - vp[i]; - mpi_limb_t cy1 = x > u; + mpi_limb_t cy1 = mpih_ct_limb_greater_than(x, u); mpi_limb_t cy2; - cy2 = x < cy; + cy2 = mpih_ct_limb_less_than(x, cy); x = x - cy; cy = cy1 | cy2; wp[i] = (u & mask2) | (x & mask1); @@ -157,7 +180,7 @@ _gcry_mpih_abs_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, mpi_limb_t u = up[i]; mpi_limb_t x = ~u + cy; - cy = (x < ~u); + cy = mpih_ct_limb_less_than(x, ~u); wp[i] = (u & mask2) | (x & mask1); } } -- 2.40.1 From jussi.kivilinna at iki.fi Fri Nov 3 20:49:20 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Fri, 3 Nov 2023 21:49:20 +0200 Subject: [PATCH] mpih_mod: avoid unintentional conditional branch Message-ID: <20231103194920.3253907-1-jussi.kivilinna@iki.fi> * mpi/mpih-const-time.c (_gcry_mpih_mod): Avoid conditional branch on the_bit extraction. -- Signed-off-by: Jussi Kivilinna --- mpi/mpih-const-time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpi/mpih-const-time.c b/mpi/mpih-const-time.c index 7246d548..74939400 100644 --- a/mpi/mpih-const-time.c +++ b/mpi/mpih-const-time.c @@ -204,7 +204,7 @@ _gcry_mpih_mod (mpi_ptr_t vp, mpi_size_t vsize, unsigned int limbno = j / BITS_PER_MPI_LIMB; unsigned int bitno = j % BITS_PER_MPI_LIMB; mpi_limb_t limb = vp[limbno]; - unsigned int the_bit = ((limb & (A_LIMB_1 << bitno)) ? 1 : 0); + unsigned int the_bit = (limb >> bitno) & 1; mpi_limb_t underflow; mpi_limb_t overflow; -- 2.40.1 From gniibe at fsij.org Tue Nov 7 02:24:07 2023 From: gniibe at fsij.org (NIIBE Yutaka) Date: Tue, 07 Nov 2023 10:24:07 +0900 Subject: Fixes for RSA and ElGamal In-Reply-To: <871qd9q6oc.fsf@akagi.fsij.org> References: <871qd9q6oc.fsf@akagi.fsij.org> Message-ID: <8734xis6dk.fsf@akagi.fsij.org> NIIBE Yutaka wrote: > In master, I pushed my fixes for RSA and ElGamal. And then, thanks to Jussi, more fixes are pushed for constant time operations and their use cases in the implementation. Those are applied to 1.10 branch, too. Once, I introduced POSSIBLE_CONDITIONAL_BRANCH_IN_BYTE_COMPARISON, but it was removed to prefer having same code for every architecture. For those who build libgcrypt with MSVC, you may see the difference. -- From falko.strenzke at mtg.de Tue Nov 14 16:27:12 2023 From: falko.strenzke at mtg.de (Falko Strenzke) Date: Tue, 14 Nov 2023 16:27:12 +0100 Subject: AVX implementation for PQC Message-ID: <6f0032ac-0f00-477f-8afe-3f27c6ce294d@mtg.de> In the course of our project for the integration of PQC into libgcrypt we also plan to provide an AVX2 implementation. As our implementation is generally based on the reference implementation, the AVX2 implementation would also closely follow the one provided in the reference implementation of the respective scheme, i.e. using immintrin.h. We plan, however, to create a more unified implementation in that we will keep common higher level functions with compile-time switches and for the lower level functions that are completely different for AVX2, we would create separate files. Please let me know if that approach sounds suitable for libcrypt and if you have any comments. - Falko -- *MTG AG* Dr. Falko Strenzke Executive System Architect Phone: +49 6151 8000 24 E-Mail: falko.strenzke at mtg.de Web: mtg.de Follow us ------------------------------------------------------------------------ MTG AG - Dolivostr. 11 - 64293 Darmstadt, Germany Commercial register: HRB 8901 Register Court: Amtsgericht Darmstadt Management Board: J?rgen Ruf (CEO), Tamer Kemer?z Chairman of the Supervisory Board: Dr. Thomas Milde This email may contain confidential and/or privileged information. If you are not the correct recipient or have received this email in error, please inform the sender immediately and delete this email.Unauthorised copying or distribution of this email is not permitted. Data protection information: Privacy policy -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: smime.p7s Type: application/pkcs7-signature Size: 4813 bytes Desc: Kryptografische S/MIME-Signatur URL: From jussi.kivilinna at iki.fi Tue Nov 14 18:41:13 2023 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Tue, 14 Nov 2023 19:41:13 +0200 Subject: AVX implementation for PQC In-Reply-To: <6f0032ac-0f00-477f-8afe-3f27c6ce294d@mtg.de> References: <6f0032ac-0f00-477f-8afe-3f27c6ce294d@mtg.de> Message-ID: Hello, On 14.11.2023 17.27, Falko Strenzke wrote: > In the course of our project for the integration of PQC into libgcrypt we also plan to provide an AVX2 implementation. As our implementation is generally based on the reference implementation, the AVX2 implementation would also closely follow the one provided in the reference implementation of the respective scheme, i.e. using immintrin.h. We plan, however, to create a more unified implementation in that we will keep common higher level functions with compile-time switches and for the lower level functions that are completely different for AVX2, we would create separate files. > So, if I understood right, higher level functions be compiled both without and with AVX2 using, for example, AVX2_ENABLED macro defined from Makefile. AVX2 implementation would be then selected run-time based on HWF flags and AVX2 would be compiled only if configure.ac detected that compiler supports "immintrin.h" intrinsics. > Please let me know if that approach sounds suitable for libcrypt and if you have any comments. > Sounds good to me. -Jussi From falko.strenzke at mtg.de Tue Nov 14 16:16:30 2023 From: falko.strenzke at mtg.de (Falko Strenzke) Date: Tue, 14 Nov 2023 16:16:30 +0100 Subject: Adding new public key KEM API In-Reply-To: <875y2wwn7x.fsf@akagi.fsij.org> References: <877cnlwyoy.fsf@akagi.fsij.org> <875y33xdud.fsf@akagi.fsij.org> <87mswfuitt.fsf@jacob.g10code.de> <875y2wwn7x.fsf@akagi.fsij.org> Message-ID: <1178687a-ca4f-4e09-8e30-8b979ec867f5@mtg.de> There is another point to consider for the design of a generic KEM API: the use of the public in the key derivation, which makes it necessary to pass the public key to the decapsulation function if one doesn't want to run the computation of the public key from the private key in the decapsulation function. We are using using this now for instance in our OpenPGP PQC draft (https://github.com/openpgp-pqc/draft-openpgp-pqc/pull/66 corrects the ECC-KEM function signature regarding that matter). - Falko Am 24.10.23 um 08:25 schrieb NIIBE Yutaka: > Werner Koch wrote: >> On Thu, 19 Oct 2023 16:37, NIIBE Yutaka said: >> >>> gcry_error_t gcry_kem_decap (int algo, >>> const void *seckey, >>> const void *ciphertext, >>> void *shared_secret); >> I still don't feel comfortable without a size argument. > Assumption here (for lower level API) is: > > It's caller side (user of libgcrypt) which does static > compile-time check against ALGO and the length of each > byte-array. > > If not static, caller side can do run-time check, if needed, > before the call. > > Having a size argument would mean, > > libgcrypt does run-time check of the length (for each call) > > I wonder if this kind of run-time check in libgcrypt is useful in lower > level API. > > I could imagine having an API offering static compile-time check. In > this case, it would provide a macro something like gcry_kem_decap_check > which has length arguments. The ABI is gcry_kem_decap. -- *MTG AG* Dr. Falko Strenzke Executive System Architect Phone: +49 6151 8000 24 E-Mail: falko.strenzke at mtg.de Web: mtg.de Follow us ------------------------------------------------------------------------ MTG AG - Dolivostr. 11 - 64293 Darmstadt, Germany Commercial register: HRB 8901 Register Court: Amtsgericht Darmstadt Management Board: J?rgen Ruf (CEO), Tamer Kemer?z Chairman of the Supervisory Board: Dr. Thomas Milde This email may contain confidential and/or privileged information. If you are not the correct recipient or have received this email in error, please inform the sender immediately and delete this email.Unauthorised copying or distribution of this email is not permitted. Data protection information: Privacy policy -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: HM2gwfch0I8R0uAq.png Type: image/png Size: 4018 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: NNDy9qKS7rLGbGcv.png Type: image/png Size: 14587 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: Q5roHJuK66RgXLHM.png Type: image/png Size: 13185 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: smime.p7s Type: application/pkcs7-signature Size: 4813 bytes Desc: Kryptografische S/MIME-Signatur URL: From gniibe at fsij.org Fri Nov 17 07:40:26 2023 From: gniibe at fsij.org (NIIBE Yutaka) Date: Fri, 17 Nov 2023 15:40:26 +0900 Subject: Adding new public key KEM API In-Reply-To: <1178687a-ca4f-4e09-8e30-8b979ec867f5@mtg.de> References: <877cnlwyoy.fsf@akagi.fsij.org> <875y33xdud.fsf@akagi.fsij.org> <87mswfuitt.fsf@jacob.g10code.de> <875y2wwn7x.fsf@akagi.fsij.org> <1178687a-ca4f-4e09-8e30-8b979ec867f5@mtg.de> Message-ID: <87ttpkg9wl.fsf@akagi.fsij.org> Hello, Falko Strenzke wrote: > There is another point to consider for the design of a generic KEM API: > the use of the public in the key derivation, which makes it necessary to > pass the public key to the decapsulation function if one doesn't want to > run the computation of the public key from the private key in the > decapsulation function. Thank you for the input. I encounter this exact issue when I did an experiment for DHKEM(X25519, HKDF-SHA256). Currently, it computes public key from secret key. My experiment is here: https://dev.gnupg.org/source/libgcrypt/history/gniibe%252Fkem2/ This is the branch on top of master. Last month, I created the gniibe/kem branch on top of 1.10 branch. I need HKDF function for the DHKEM, so, I did again on top of master. -- From simon at josefsson.org Mon Nov 20 09:28:49 2023 From: simon at josefsson.org (Simon Josefsson) Date: Mon, 20 Nov 2023 09:28:49 +0100 Subject: Adding new public key KEM API In-Reply-To: <1178687a-ca4f-4e09-8e30-8b979ec867f5@mtg.de> (Falko Strenzke's message of "Tue, 14 Nov 2023 16:16:30 +0100") References: <877cnlwyoy.fsf@akagi.fsij.org> <875y33xdud.fsf@akagi.fsij.org> <87mswfuitt.fsf@jacob.g10code.de> <875y2wwn7x.fsf@akagi.fsij.org> <1178687a-ca4f-4e09-8e30-8b979ec867f5@mtg.de> Message-ID: <877cmc25ha.fsf@kaka.sjd.se> One common solution to that problem is to include the public key in the data array holding the private key. The SNTRUP761 implementations I've worked with does that by default, and I thought it was a fairly common design pattern. /Simon Falko Strenzke writes: > There is another point to consider for the design of a generic KEM API: the use of the public in the > key derivation, which makes it necessary to pass the public key to the decapsulation function if one > doesn't want to run the computation of the public key from the private key in the decapsulation > function. > > We are using using this now for instance in our OpenPGP PQC draft > (https://github.com/openpgp-pqc/draft-openpgp-pqc/pull/66 corrects the ECC-KEM function > signature regarding that matter). > > - Falko > > Am 24.10.23 um 08:25 schrieb NIIBE Yutaka: > > Werner Koch wrote: > > On Thu, 19 Oct 2023 16:37, NIIBE Yutaka said: > > gcry_error_t gcry_kem_decap (int algo, > const void *seckey, > const void *ciphertext, > void *shared_secret); > > > I still don't feel comfortable without a size argument. > > > Assumption here (for lower level API) is: > > It's caller side (user of libgcrypt) which does static > compile-time check against ALGO and the length of each > byte-array. > > If not static, caller side can do run-time check, if needed, > before the call. > > Having a size argument would mean, > > libgcrypt does run-time check of the length (for each call) > > I wonder if this kind of run-time check in libgcrypt is useful in lower > level API. > > I could imagine having an API offering static compile-time check. In > this case, it would provide a macro something like gcry_kem_decap_check > which has length arguments. The ABI is gcry_kem_decap. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 255 bytes Desc: not available URL: From dkg at fifthhorseman.net Tue Nov 21 21:59:23 2023 From: dkg at fifthhorseman.net (Daniel Kahn Gillmor) Date: Tue, 21 Nov 2023 15:59:23 -0500 Subject: Adding new public key KEM API In-Reply-To: <877cmc25ha.fsf@kaka.sjd.se> References: <877cnlwyoy.fsf@akagi.fsij.org> <875y33xdud.fsf@akagi.fsij.org> <87mswfuitt.fsf@jacob.g10code.de> <875y2wwn7x.fsf@akagi.fsij.org> <1178687a-ca4f-4e09-8e30-8b979ec867f5@mtg.de> <877cmc25ha.fsf@kaka.sjd.se> Message-ID: <87il5uzutw.fsf@fifthhorseman.net> On Mon 2023-11-20 09:28:49 +0100, Simon Josefsson via Gcrypt-devel wrote: > One common solution to that problem is to include the public key in the > data array holding the private key. The SNTRUP761 implementations I've > worked with does that by default, and I thought it was a fairly common > design pattern. This doesn't seem like an unreasonable approach, but it also requires a well-documented serialization (and deserialization) convention for the public key material when aggregated with the private key. That might be as simple as mere concatenation of already well-defined, fixed-length byte arrays for public and private key material. but it's not necessarily the case, depending on the KEM. That said, an abstract KEM API that only works for KEMs that do have such well-defined serialization and aggregation conventions sounds like it hits a sweet spot to me: - The API surface is minimal - Most callers don't want to have to fiddle with thinking about sizes or relative strengths of parameterized algorithm choices anyway, and would prefer to treat the components as bytestrings in the first place. - And it aggressively selects for/prefers well-defined KEM abstractions in the first place. A simple, narrow API that makes it convenient to use reasonable algorithms is a net win, even (especially?) if it discourages users of the library from trying to adopt other more complex algorithms that require something more ungainly or idiosyncratic. --dkg -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 227 bytes Desc: not available URL: From gniibe at fsij.org Fri Nov 24 08:29:28 2023 From: gniibe at fsij.org (NIIBE Yutaka) Date: Fri, 24 Nov 2023 16:29:28 +0900 Subject: Adding new public key KEM API In-Reply-To: <87ttpkg9wl.fsf@akagi.fsij.org> References: <877cnlwyoy.fsf@akagi.fsij.org> <875y33xdud.fsf@akagi.fsij.org> <87mswfuitt.fsf@jacob.g10code.de> <875y2wwn7x.fsf@akagi.fsij.org> <1178687a-ca4f-4e09-8e30-8b979ec867f5@mtg.de> <87ttpkg9wl.fsf@akagi.fsij.org> Message-ID: <877cm7txrb.fsf@akagi.fsij.org> Hello, NIIBE Yutaka wrote: > I encounter this exact issue when I did an experiment for DHKEM(X25519, > HKDF-SHA256). Currently, it computes public key from secret key. > > My experiment is here: > > https://dev.gnupg.org/source/libgcrypt/history/gniibe%252Fkem2/ > > This is the branch on top of master. For next experiment, I added GCRY_KEM_OPENPGP_X25519. My target use case in mind is using this KEM for OpenPGP. For this use case, I need to supply KDF parameter to the API, so, I added optional argument for the API for this experiment. gcry_error_t gcry_kem_keypair (int algo, void *pubkey, void *seckey); gcry_error_t gcry_kem_encap (int algo, const void *pubkey, void *ciphertext, void *shared_secret, const void *optional); gcry_error_t gcry_kem_decap (int algo, const void *seckey, const void *ciphertext, void *shared_secret, const void *optional); In the tests/t-kem.c of my branch of experiment, it is used like: const uint8_t kdf_param[N_TESTS_OPENPGP][56] = { { /* Curve OID of Curve25519 in OpenPGP v4. */ 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01, /**/ 0x12, /* ECDH algo in OpenPGP */ /**/ 0x03, 0x01, 0x08 /*SHA256*/, 0x07 /*AES128*/, /**/ 0x41, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, 0x73, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, /* "Anonymous Sender " */ /**/ 0x25, 0xd4, 0x45, 0xfa, 0xc1, 0x96, 0x49, 0xc4, 0x6a, 0x6b, 0x2f, 0xb3, 0xcd, 0xfc, 0x22, 0x19, 0xc5, 0x53, 0xd3, 0x92 /* public key fingerprint */ } [...] err = gcry_kem_decap (GCRY_KEM_OPENPGP_X25519, seckey[testno], ciphertext[testno], kek2, kdf_param[testno]); I investigated how this API can be used in GnuPG. I realized that in the current GnuPG implementation, gcry_kem_decap with GCRY_KEM_OPENPGP_X25519 cannot be used directly; We need to change the demarcation between gpg and gpg-agent, beforehand. In the current GnuPG implementation: gpg-agent does: ECDH gpg does: KDF, key unwrap, and symmetric decryption If gcry_kem_decap with GCRY_KEM_OPENPGP_X25519 is used, it will be: gpg-agent does: KEM decapsulation (ECDH and KDF) gpg does: key unwrap and symmetric decryption This means that, gpg-agent will need to know (a part of) OpenPGP public key, to do gcry_kem_decap operation. Possibly, we need to enhance gpg-agent protocol so that PKDECRYPT command can optionally inquire (a part of) OpenPGP public key to gpg frontend. --