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 132ef47e2561f28f401583132658dae17a62852f
parent 8305a24530f35a6d6c9f50012ef50a0a6e1ba97c
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri, 18 Oct 2024 16:55:43 +0400

lib: s/derive_public/derive_pub, bench stuff

Diffstat:
MREADME.md | 37+++++++++++++++++++++++++++++++++++++
Mbench/Main.hs | 25+++++++++++++++----------
Mlib/Crypto/Curve/Secp256k1.hs | 12++++++------
3 files changed, 58 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md @@ -38,6 +38,43 @@ A sample GHCi session: Haddocks (API documentation, etc.) are hosted at [docs.ppad.tech/secp256k1][hadoc]. +## Performance + +The aim is best-in-class performance for pure, highly-auditable Haskell +code. + +Current benchmark figures on my mid-2020 MacBook Air look like (use +`cabal bench` to run the benchmark suite): + +``` + benchmarking schnorr/sign_schnorr + time 5.663 ms (5.618 ms .. 5.714 ms) + 0.999 R² (0.999 R² .. 1.000 R²) + mean 5.683 ms (5.652 ms .. 5.715 ms) + std dev 98.56 μs (78.45 μs .. 127.0 μs) + + benchmarking schnorr/verify_schnorr + time 2.323 ms (2.301 ms .. 2.360 ms) + 0.999 R² (0.997 R² .. 0.999 R²) + mean 2.342 ms (2.328 ms .. 2.363 ms) + std dev 57.68 μs (43.66 μs .. 86.22 μs) + variance introduced by outliers: 11% (moderately inflated) + + benchmarking ecdsa/sign_ecdsa + time 1.756 ms (1.741 ms .. 1.774 ms) + 0.999 R² (0.998 R² .. 1.000 R²) + mean 1.773 ms (1.760 ms .. 1.788 ms) + std dev 45.40 μs (35.58 μs .. 57.52 μs) + variance introduced by outliers: 13% (moderately inflated) + + benchmarking ecdsa/verify_ecdsa + time 2.300 ms (2.270 ms .. 2.331 ms) + 0.998 R² (0.997 R² .. 0.999 R²) + mean 2.318 ms (2.297 ms .. 2.345 ms) + std dev 81.45 μs (65.15 μs .. 105.2 μs) + variance introduced by outliers: 21% (moderately inflated) +``` + ## Security This library aims at the maximum security achievable in a diff --git a/bench/Main.hs b/bench/Main.hs @@ -20,7 +20,7 @@ main = defaultMain [ parse_point , add , mul - , derive_public + , derive_pub , schnorr , ecdsa ] @@ -63,11 +63,11 @@ mul = env setup $ \x -> setup = pure . S.parse_int256 $ B16.decodeLenient "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" -derive_public :: Benchmark -derive_public = env setup $ \x -> - bgroup "derive_public" [ - bench "sk = 2" $ nf S.derive_public 2 - , bench "sk = 2 ^ 255 - 19" $ nf S.derive_public x +derive_pub :: Benchmark +derive_pub = env setup $ \x -> + bgroup "derive_pub" [ + bench "sk = 2" $ nf S.derive_pub 2 + , bench "sk = 2 ^ 255 - 19" $ nf S.derive_pub x ] where setup = pure . S.parse_int256 $ B16.decodeLenient @@ -85,15 +85,20 @@ schnorr = env setup $ \big -> "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" ecdsa :: Benchmark -ecdsa = env setup $ \big -> +ecdsa = env setup $ \ ~(big, pub, msg, sig) -> bgroup "ecdsa" [ bench "sign_ecdsa (small)" $ nf (S.sign_ecdsa 2) s_msg , bench "sign_ecdsa (large)" $ nf (S.sign_ecdsa big) s_msg - -- , bench "verify_ecdsa" $ nf (S.verify_ecdsa e_msg t) e_sig -- XX inputs + , bench "verify_ecdsa" $ nf (S.verify_ecdsa msg pub) sig ] where - setup = pure . S.parse_int256 $ B16.decodeLenient - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" + setup = do + let big = S.parse_int256 $ B16.decodeLenient + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" + pub = S.derive_pub big + msg = "i approve of this message" + sig = S.sign_ecdsa big s_msg + pure (big, pub, msg, sig) p_bs :: BS.ByteString p_bs = B16.decodeLenient diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs @@ -43,7 +43,7 @@ module Crypto.Curve.Secp256k1 ( , double , mul , mul_unsafe - , derive_public + , derive_pub -- Coordinate systems and transformations , Affine(..) @@ -580,15 +580,15 @@ mul_unsafe p n -- -- >>> import qualified System.Entropy as E -- >>> sk <- fmap parse_int256 (E.getEntropy 32) --- >>> derive_public sk +-- >>> derive_pub sk -- "<secp256k1 point>" -derive_public :: Integer -> Pub -derive_public _SECRET +derive_pub :: Integer -> Pub +derive_pub _SECRET | not (ge _SECRET) = - error "ppad-secp256k1 (derive_public): invalid secret key" + error "ppad-secp256k1 (derive_pub): invalid secret key" | otherwise = mul _CURVE_G _SECRET -{-# NOINLINE derive_public #-} +{-# NOINLINE derive_pub #-} -- parsing --------------------------------------------------------------------