From: Lukas Gerlach Date: Wed, 15 Apr 2026 13:52:46 +0000 (+0200) Subject: Fix constant-time violation in ossl_curve448_scalar_halve X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=3e8e5cb67f563ecb292df35e16e90af418753d49;p=thirdparty%2Fopenssl.git Fix constant-time violation in ossl_curve448_scalar_halve Add a value barrier to the mask variable in ossl_curve448_scalar_halve() to prevent LLVM SimpleLoopUnswitchPass from introducing a secret-dependent branch. When compiled with Clang >= 17 at -O3, the mask which is static during the loop (derived from the secret scalar LSB) is used by SimpleLoopUnswitchPass to clone the loop body into two versions guarded by a branch on the secret bit. This produces a side-channel that leaks nonce parity. The value barrier forces the compiler to treat the mask as opaque, preventing loop unswitching while maintaining identical performance. A portable value_barrier_c448 macro is added to word.h to select the appropriate barrier width (32 or 64 bit) based on C448_WORD_BITS. Reviewed-by: Kurt Roeckx Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Thu Apr 16 16:43:22 2026 (Merged from https://github.com/openssl/openssl/pull/30845) --- diff --git a/crypto/ec/curve448/scalar.c b/crypto/ec/curve448/scalar.c index 191b0b4fd2c..f70b24843f9 100644 --- a/crypto/ec/curve448/scalar.c +++ b/crypto/ec/curve448/scalar.c @@ -210,6 +210,7 @@ void ossl_curve448_scalar_encode(unsigned char ser[C448_SCALAR_BYTES], void ossl_curve448_scalar_halve(curve448_scalar_t out, const curve448_scalar_t a) { c448_word_t mask = 0 - (a->limb[0] & 1); + mask = value_barrier_c448(mask); c448_dword_t chain = 0; unsigned int i; diff --git a/crypto/ec/curve448/word.h b/crypto/ec/curve448/word.h index 8137be6abb8..a697879eeb3 100644 --- a/crypto/ec/curve448/word.h +++ b/crypto/ec/curve448/word.h @@ -18,6 +18,7 @@ #include #include #include "curve448utils.h" +#include "internal/constant_time.h" #ifdef INT128_MAX #include "arch_64/arch_intrinsics.h" @@ -53,6 +54,12 @@ typedef int64_t dsword_t; #error "For now we only support 32- and 64-bit architectures." #endif +#if C448_WORD_BITS == 64 +#define value_barrier_c448(x) value_barrier_64(x) +#elif C448_WORD_BITS == 32 +#define value_barrier_c448(x) value_barrier_32(x) +#endif + /* * The plan on booleans: The external interface uses c448_bool_t, but this * might be a different size than our particular arch's word_t (and thus