]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Fix constant-time violation in ossl_curve448_scalar_halve
authorLukas Gerlach <s8lugerl@stud.uni-saarland.de>
Wed, 15 Apr 2026 13:52:46 +0000 (15:52 +0200)
committerTomas Mraz <tomas@openssl.foundation>
Thu, 16 Apr 2026 16:43:04 +0000 (18:43 +0200)
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 <kurt@roeckx.be>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Thu Apr 16 16:43:22 2026
(Merged from https://github.com/openssl/openssl/pull/30845)

crypto/ec/curve448/scalar.c
crypto/ec/curve448/word.h

index 191b0b4fd2cf988114bdc11c7b5b08de11d8d6a5..f70b24843f9539d04640d6af880274d63007ed22 100644 (file)
@@ -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;
 
index 8137be6abb8ef193106adbffb8498b0f99ad77de..a697879eeb3d63878772156cd5b7a4a4e85a1382 100644 (file)
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <openssl/e_os2.h>
 #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