secp256k1

Pure Haskell Schnorr, ECDSA on the elliptic curve secp256k1 (docs.ppad.tech/secp256k1).
git clone git://git.ppad.tech/secp256k1.git
Log | Files | Refs | README | LICENSE

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:
Mlib/Crypto/Curve/Secp256k1.hs | 27++++++++++++++++++++++++---
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