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 aae76295f8d86754de5a4560e4d6c7f2213ac5e6
parent 5873f4b163896cd7b7197c9d9f5fb3683a9e8aaa
Author: Jared Tobin <jared@jtobin.io>
Date:   Tue, 17 Jun 2025 14:47:52 +0400

lib: make parse_int256 total

Diffstat:
Mbench/Main.hs | 23++++++++++++++---------
Mbench/Weight.hs | 11++++++++---
Mlib/Crypto/Curve/Secp256k1.hs | 9++++-----
3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/bench/Main.hs b/bench/Main.hs @@ -28,6 +28,11 @@ main = defaultMain [ , ecdh ] +parse_int256 :: BS.ByteString -> Integer +parse_int256 bs = case S.parse_int256 bs of + Nothing -> error "bang" + Just s -> s + remQ :: Benchmark remQ = env setup $ \x -> bgroup "remQ (remainder modulo _CURVE_Q)" [ @@ -35,7 +40,7 @@ remQ = env setup $ \x -> , bench "remQ (2 ^ 255 - 19)" $ nf S.remQ x ] where - setup = pure . S.parse_int256 $ B16.decodeLenient + setup = pure . parse_int256 $ B16.decodeLenient "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" parse_point :: Benchmark @@ -48,8 +53,8 @@ parse_point = bgroup "parse_point" [ parse_integer :: Benchmark parse_integer = env setup $ \ ~(small, big) -> bgroup "parse_int256" [ - bench "parse_int256 (small)" $ nf S.parse_int256 small - , bench "parse_int256 (big)" $ nf S.parse_int256 big + bench "parse_int256 (small)" $ nf parse_int256 small + , bench "parse_int256 (big)" $ nf parse_int256 big ] where setup = do @@ -73,7 +78,7 @@ mul = env setup $ \x -> , bench "(2 ^ 255 - 19) G" $ nf (S.mul S._CURVE_G) x ] where - setup = pure . S.parse_int256 $ B16.decodeLenient + setup = pure . parse_int256 $ B16.decodeLenient "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" precompute :: Benchmark @@ -88,7 +93,7 @@ mul_wnaf = env setup $ \ ~(tex, x) -> where setup = do let !tex = S.precompute - !int = S.parse_int256 $ B16.decodeLenient + !int = parse_int256 $ B16.decodeLenient "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" pure (tex, int) @@ -103,7 +108,7 @@ derive_pub = env setup $ \ ~(tex, x) -> where setup = do let !tex = S.precompute - !int = S.parse_int256 $ B16.decodeLenient + !int = parse_int256 $ B16.decodeLenient "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" pure (tex, int) @@ -120,7 +125,7 @@ schnorr = env setup $ \ ~(tex, big) -> where setup = do let !tex = S.precompute - !int = S.parse_int256 $ B16.decodeLenient + !int = parse_int256 $ B16.decodeLenient "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" pure (tex, int) @@ -137,7 +142,7 @@ ecdsa = env setup $ \ ~(tex, big, pub, msg, sig) -> where setup = do let !tex = S.precompute - big = S.parse_int256 $ B16.decodeLenient + big = parse_int256 $ B16.decodeLenient "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" Just pub = S.derive_pub big msg = "i approve of this message" @@ -203,7 +208,7 @@ t = case S.parse_point t_bs of Just !pt -> pt s_sk :: Integer -s_sk = S.parse_int256 . B16.decodeLenient $ +s_sk = parse_int256 . B16.decodeLenient $ "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF" s_sig :: BS.ByteString diff --git a/bench/Weight.hs b/bench/Weight.hs @@ -15,6 +15,11 @@ instance NFData S.Affine instance NFData S.ECDSA instance NFData S.Context +parse_int :: BS.ByteString -> Integer +parse_int bs = case S.parse_int256 bs of + Nothing -> error "bang" + Just v -> v + big :: Integer big = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed @@ -42,8 +47,8 @@ remQ = W.wgroup "remQ" $ do parse_int256 :: W.Weigh () parse_int256 = W.wgroup "parse_int256" $ do - W.func' "parse_int256 (small)" S.parse_int256 (BS.replicate 32 0x00) - W.func' "parse_int256 (big)" S.parse_int256 (BS.replicate 32 0xFF) + W.func' "parse_int (small)" parse_int (BS.replicate 32 0x00) + W.func' "parse_int (big)" parse_int (BS.replicate 32 0xFF) add :: W.Weigh () add = W.wgroup " add" $ do @@ -108,7 +113,7 @@ ecdh = W.wgroup "ecdh" $ do "bd02b9dfc8ef760708950bd972f2dc244893b61b6b46c3b19be1b2da7b034ac5" s_sk :: Integer -s_sk = S.parse_int256 . B16.decodeLenient $ +s_sk = parse_int . B16.decodeLenient $ "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF" s_sig :: BS.ByteString diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs @@ -703,11 +703,10 @@ derive_pub' = mul_wnaf -- >>> import qualified Data.ByteString as BS -- >>> parse_int256 (BS.replicate 32 0xFF) -- <2^256 - 1> -parse_int256 :: BS.ByteString -> Integer -parse_int256 bs - | BS.length bs /= 32 = - error "ppad-secp256k1 (parse_int256): requires exactly 32-byte input" - | otherwise = roll32 bs +parse_int256 :: BS.ByteString -> Maybe Integer +parse_int256 bs = do + guard (BS.length bs == 32) + pure $! roll32 bs -- | Parse compressed secp256k1 point (33 bytes), uncompressed point (65 -- bytes), or BIP0340-style point (32 bytes).