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 f65d51f02449637aca9afd427a3969f7c8d0b006
parent fc4ec3c1b44a87c009f2d67cc6b7e606abad3442
Author: Jared Tobin <jared@jtobin.io>
Date:   Wed, 16 Oct 2024 19:55:52 +0400

lib: misc bigint optimisations

Diffstat:
Mlib/Crypto/Curve/Secp256k1.hs | 80++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 47 insertions(+), 33 deletions(-)

diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs @@ -69,6 +69,10 @@ import GHC.Generics import GHC.Natural import qualified GHC.Num.Integer as I +-- note the use of GHC.Num.Integer-qualified functions throughout +-- this module; in some cases explicit use of these functions yields +-- tremendous speedups compared to more general versions + -- keystroke savers & other utilities ----------------------------------------- fi :: (Integral a, Num b) => a -> b @@ -76,13 +80,11 @@ fi = fromIntegral {-# INLINE fi #-} -- generic modular exponentiation --- https://gist.github.com/trevordixon/6788535 -modexp :: Integer -> Integer -> Integer -> Integer -modexp b e m - | e == 0 = 1 - | otherwise = - let t = if B.testBit e 0 then b `mod` m else 1 - in t * modexp ((b * b) `mod` m) (B.shiftR e 1) m `mod` m +-- b ^ e mod m +modexp :: Integer -> Natural -> Natural -> Integer +modexp b (fi -> e) m = case I.integerPowMod# b e m of + (# fi -> n | #) -> n + (# | _ #) -> error "negative power impossible" {-# INLINE modexp #-} -- generic modular inverse @@ -131,12 +133,12 @@ lift :: Integer -> Maybe Affine lift x | not (fe x) = Nothing | otherwise = - let c = modP (modexp x 3 _CURVE_P + 7) - y = modexp c ((_CURVE_P + 1) `div` 4) _CURVE_P - y_p - | y `rem` 2 == 0 = y - | otherwise = _CURVE_P - y - in if c /= modexp y 2 _CURVE_P + let c = remP (modexp x 3 (fi _CURVE_P) + 7) -- modexp always nonnegative + e = (_CURVE_P + 1) `I.integerQuot` 4 + y = modexp c (fi e) (fi _CURVE_P) + y_p | B.testBit y 0 = _CURVE_P - y + | otherwise = y + in if c /= modexp y 2 (fi _CURVE_P) then Nothing else Just $! (Affine x y_p) @@ -240,7 +242,7 @@ _ZERO = Projective 0 1 0 -- secp256k1 in prime order j-invariant 0 form (i.e. a == 0). weierstrass :: Integer -> Integer -weierstrass x = modP (modP (x * x) * x + _CURVE_B) +weierstrass x = remP (remP (x * x) * x + _CURVE_B) {-# INLINE weierstrass #-} -- field, group operations ---------------------------------------------------- @@ -250,11 +252,23 @@ modP :: Integer -> Integer modP a = I.integerMod a _CURVE_P {-# INLINE modP #-} +-- Division modulo secp256k1 field prime, when argument is nonnegative. +-- (more efficient than modP) +remP :: Integer -> Integer +remP a = I.integerRem a _CURVE_P +{-# INLINE remP #-} + -- Division modulo secp256k1 group order. modQ :: Integer -> Integer modQ a = I.integerMod a _CURVE_Q {-# INLINE modQ #-} +-- Division modulo secp256k1 group order, when argument is nonnegative. +-- (more efficient than modQ) +remQ :: Integer -> Integer +remQ a = I.integerRem a _CURVE_Q +{-# INLINE remQ #-} + -- Is field element? fe :: Integer -> Bool fe n = 0 < n && n < _CURVE_P @@ -272,22 +286,22 @@ modsqrt :: Integer -> Maybe Integer modsqrt n = runST $ do r <- newSTRef 1 num <- newSTRef n - e <- newSTRef ((_CURVE_P + 1) `div` 4) + e <- newSTRef ((_CURVE_P + 1) `I.integerQuot` 4) let loop = do ev <- readSTRef e when (ev > 0) $ do - when (B.testBit ev 0) $ do + when (I.integerTestBit ev 0) $ do numv <- readSTRef num - modifySTRef' r (\rv -> (rv * numv) `rem` _CURVE_P) - modifySTRef' num (\numv -> (numv * numv) `rem` _CURVE_P) + modifySTRef' r (\rv -> (rv * numv) `I.integerRem` _CURVE_P) + modifySTRef' num (\numv -> (numv * numv) `I.integerRem` _CURVE_P) modifySTRef' e (`I.integerShiftR` 1) loop loop rv <- readSTRef r pure $ - if modP (rv * rv) == n + if remP (rv * rv) == n then Just $! rv else Nothing @@ -313,7 +327,7 @@ add_proj (Projective x1 y1 z1) (Projective x2 y2 z2) = runST $ do x3 <- newSTRef 0 y3 <- newSTRef 0 z3 <- newSTRef 0 - let b3 = modP (_CURVE_B * 3) + let b3 = remP (_CURVE_B * 3) t0 <- newSTRef (modP (x1 * x2)) -- 1 t1 <- newSTRef (modP (y1 * y2)) t2 <- newSTRef (modP (z1 * z2)) @@ -385,7 +399,7 @@ add_mixed (Projective x1 y1 z1) (Projective x2 y2 z2) x3 <- newSTRef 0 y3 <- newSTRef 0 z3 <- newSTRef 0 - let b3 = modP (_CURVE_B * 3) + let b3 = remP (_CURVE_B * 3) t0 <- newSTRef (modP (x1 * x2)) -- 1 t1 <- newSTRef (modP (y1 * y2)) t3 <- newSTRef (modP (x2 + y2)) @@ -440,7 +454,7 @@ double (Projective x y z) = runST $ do x3 <- newSTRef 0 y3 <- newSTRef 0 z3 <- newSTRef 0 - let b3 = modP (_CURVE_B * 3) + let b3 = remP (_CURVE_B * 3) t0 <- newSTRef (modP (y * y)) -- 1 readSTRef t0 >>= \r0 -> writeSTRef z3 (modP (r0 + r0)) @@ -517,7 +531,7 @@ _parse_compressed h (roll -> x) | not (fe x) = Nothing | otherwise = do y <- modsqrt (weierstrass x) - let yodd = B.testBit y 0 + let yodd = I.integerTestBit y 0 hodd = B.testBit h 0 pure $! if hodd /= yodd @@ -556,8 +570,8 @@ sign_schnorr d' m a | otherwise = let p_proj = mul _CURVE_G d' Affine x_p y_p = affine p_proj - d | y_p `rem` 2 == 0 = d' -- d' group element assures p nonzero - | otherwise = _CURVE_Q - d' + d | I.integerTestBit y_p 0 = _CURVE_Q - d' + | otherwise = d' bytes_d = unroll32 d h_a = hash_tagged "BIP0340/aux" a @@ -572,8 +586,8 @@ sign_schnorr d' m a then error "ppad-secp256k1 (sign_schnorr): invalid k" else let Affine x_r y_r = affine (mul _CURVE_G k') - k | y_r `rem` 2 == 0 = k' -- k' nonzero per above - | otherwise = _CURVE_Q - k' + k | I.integerTestBit y_r 0 = _CURVE_Q - k' + | otherwise = k' bytes_r = unroll32 x_r e = modQ . roll . hash_tagged "BIP0340/challenge" @@ -606,7 +620,7 @@ verify_schnorr m (affine -> Affine x_p _) sig = case lift x_p of in if dif == _ZERO then False else let Affine x_R y_R = affine dif - in not (y_R `rem` 2 /= 0 || x_R /= r) + in not (I.integerTestBit y_R 0 || x_R /= r) -- ecdsa ---------------------------------------------------------------------- -- see https://www.rfc-editor.org/rfc/rfc6979, https://secg.org/sec1-v2.pdf @@ -707,7 +721,7 @@ _sign_ecdsa ty hf x m Hash -> SHA256.hash m NoHash -> m - h_modQ = modQ (bits2int h) + h_modQ = remQ (bits2int h) -- bits2int yields nonnegative sign_loop g = do k <- gen_k g @@ -715,7 +729,7 @@ _sign_ecdsa ty hf x m Affine (modQ -> r) _ = affine kg s = case modinv k (fi _CURVE_Q) of Nothing -> error "ppad-secp256k1 (sign_ecdsa): bad k value" - Just kinv -> modQ (modQ (h_modQ + modQ (x * r)) * kinv) + Just kinv -> remQ (remQ (h_modQ + remQ (x * r)) * kinv) if r == 0 -- negligible probability then sign_loop g else let !sig = ECDSA r s @@ -764,14 +778,14 @@ verify_ecdsa_unrestricted (SHA256.hash -> h) p (ECDSA r s) -- SEC1-v2 4.1.4 | not (ge r) || not (ge s) = False | otherwise = - let e = modQ (bits2int h) + let e = remQ (bits2int h) s_inv = case modinv s (fi _CURVE_Q) of -- 'ge s' assures existence of inverse Nothing -> error "ppad-secp256k1 (verify_ecdsa_unrestricted): no inverse" Just si -> si - u1 = modQ (e * s_inv) - u2 = modQ (r * s_inv) + u1 = remQ (e * s_inv) + u2 = remQ (r * s_inv) capR = add (mul _CURVE_G u1) (mul p u2) in if capR == _ZERO then False