commit 67e05f3aa8e323515882d4930385a40044f1f3b6
parent 9ccce108727ed5f82be65c3e57cf80e62a98a48c
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 27 Dec 2025 11:47:01 -0330
lib: more rigorous constant-time array index
The previous version indexed on a CT-selected offset, but that can
still leak secret bits via cache timing. This version does a proper
full-window scan, selecting an entry via mask.
Diffstat:
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs
@@ -609,9 +609,9 @@ mul_wnaf# ctxArray ctxW ls
!is_zero = CT.from_word_eq# b0 0##
!c0 = CT.from_word# (Exts.and# w 1##)
!off_nz = Exts.minusWord# (Exts.plusWord# off0 abs_b) 1##
- !off = CT.select_word# off0 off_nz (CT.not# is_zero)
+ !off = CT.select_word# off0 off_nz (CT.not is_zero)
- !pr = index_proj# ctxArray (Exts.word2Int# off)
+ !pr = ct_index_proj# ctxArray off0 s off
!neg_pr = neg# pr
!pt_zero = select_proj# pr neg_pr c0
!pt_nonzero = select_proj# pr neg_pr bor
@@ -642,6 +642,27 @@ index_proj# (ByteArray arr#) i# =
in (# x, y, z #)
{-# INLINE index_proj# #-}
+-- Constant-time table lookup within a window.
+--
+-- Unconditionally scans all entries from 'base' to 'base + size - 1',
+-- selecting the one where 'index' equals 'target'.
+ct_index_proj#
+ :: ByteArray
+ -> Exts.Word# -- ^ base index
+ -> Exts.Word# -- ^ size of window
+ -> Exts.Word# -- ^ target index
+ -> Proj
+ct_index_proj# arr base size target = loop 0## (# Z, Z, Z #) where
+ loop i acc
+ | Exts.isTrue# (i `Exts.geWord#` size) = acc
+ | otherwise =
+ let !idx = Exts.plusWord# base i
+ !pt = index_proj# arr (Exts.word2Int# idx)
+ !eq = CT.from_word_eq# idx target
+ !nacc = select_proj# acc pt eq
+ in loop (Exts.plusWord# i 1##) nacc
+{-# INLINE ct_index_proj# #-}
+
-- ec arithmetic --------------------------------------------------------------
-- Negate secp256k1 point.
@@ -723,7 +744,7 @@ instance Show Context where
-- >>> sign_ecdsa' tex sec msg
-- >>> sign_schnorr' tex sec msg aux
precompute :: Context
-precompute = _precompute 8
+precompute = _precompute 4
-- This is a highly-optimized version of a function originally
-- translated from noble-secp256k1's "precompute". Points are stored in