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 e4a20ca095bcb28cbd34874d8ccf957c36dbc9d5
parent fb2f9db3671f511e3f903b444b99969f34be6747
Author: Jared Tobin <jared@jtobin.io>
Date:   Wed,  4 Feb 2026 23:39:45 +0400

lib: primitive mul_vartime#

Diffstat:
Mlib/Crypto/Curve/Secp256k1.hs | 40+++++++++++++++++++++++++---------------
1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs @@ -136,9 +136,6 @@ type Limb4 = (# Limb, Limb, Limb, Limb #) -- Unboxed Projective synonym. type Proj = (# Limb4, Limb4, Limb4 #) -pattern Zero :: Wider -pattern Zero = Wider Z - pattern Z :: Limb4 pattern Z = (# Limb 0##, Limb 0##, Limb 0##, Limb 0## #) @@ -581,6 +578,28 @@ mul# (# px, py, pz #) s in loop (succ j) nacc nf nd nm {-# INLINE mul# #-} +mul_vartime# :: Proj -> Limb4 -> (# () | Proj #) +mul_vartime# (# px, py, pz #) s + | zero# s = + let !(P zx zy zz) = _CURVE_ZERO + in (# | (# zx, zy, zz #) #) + | CT.decide (CT.not (ge# s)) = (# () | #) + | otherwise = + let !(P zx zy zz) = _CURVE_ZERO + in (# | loop (# zx, zy, zz #) (# px, py, pz #) s #) + where + zero# (# Limb a, Limb b, Limb c, Limb d #) = Exts.isTrue# + ((a `Exts.or#` b `Exts.or#` c `Exts.or#` d) `Exts.eqWord#` 0##) + + loop !r !d !m + | zero# m = r + | otherwise = + let !nd = double# d + !(# nm, lsb_set #) = W.shr1_c# m + !nr = if CT.decide lsb_set then add_proj# r d else r + in loop nr nd nm +{-# INLINE mul_vartime# #-} + ge# :: Limb4 -> CT.Choice ge# n = let !(Wider q) = _CURVE_Q @@ -720,18 +739,9 @@ mul (P x y z) (Wider s) = case mul# (# x, y, z #) s of -- -- Don't use this function if the scalar could potentially be a secret. mul_vartime :: Projective -> Wider -> Maybe Projective -mul_vartime p = \case - Zero -> pure _CURVE_ZERO - n | not (ge n) -> Nothing - | otherwise -> pure $! loop _CURVE_ZERO p n - where - loop !r !d = \case - Zero -> r - m -> - let !nd = double d - !(# nm, lsb_set #) = W.shr1_c m - !nr = if CT.decide lsb_set then add r d else r - in loop nr nd nm +mul_vartime (P x y z) (Wider s) = case mul_vartime# (# x, y, z #) s of + (# () | #) -> Nothing + (# | (# px, py, pz #) #) -> Just $! P px py pz -- | Precomputed multiples of the secp256k1 base or generator point. data Context = Context {