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 70d13a6fd3e9549507f83ee1e9168b135d03bee3
parent 9a388fbe19567ab3b2e43a807a9474b4bdf05130
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri, 18 Oct 2024 14:57:30 +0400

lib: constant time schnorr, ecdsa

benchmarking schnorr/sign_schnorr (small secret)
time                 5.425 ms   (5.381 ms .. 5.499 ms)
                     0.999 R²   (0.999 R² .. 1.000 R²)
mean                 5.513 ms   (5.489 ms .. 5.536 ms)
std dev              71.91 μs   (59.05 μs .. 96.65 μs)

benchmarking schnorr/sign_schnorr (large secret)
time                 5.406 ms   (5.289 ms .. 5.547 ms)
                     0.997 R²   (0.995 R² .. 0.999 R²)
mean                 5.500 ms   (5.460 ms .. 5.573 ms)
std dev              160.8 μs   (106.9 μs .. 259.6 μs)
variance introduced by outliers: 12% (moderately inflated)

benchmarking ecdsa/sign_ecdsa (small)
time                 1.685 ms   (1.670 ms .. 1.700 ms)
                     0.999 R²   (0.999 R² .. 1.000 R²)
mean                 1.716 ms   (1.706 ms .. 1.739 ms)
std dev              47.25 μs   (27.10 μs .. 79.26 μs)
variance introduced by outliers: 14% (moderately inflated)

benchmarking ecdsa/sign_ecdsa (large)
time                 1.707 ms   (1.689 ms .. 1.726 ms)
                     0.999 R²   (0.999 R² .. 1.000 R²)
mean                 1.729 ms   (1.720 ms .. 1.741 ms)
std dev              33.58 μs   (27.07 μs .. 41.45 μs)

Diffstat:
Mbench/Main.hs | 26++++++++++++++++++--------
Mlib/Crypto/Curve/Secp256k1.hs | 18+++++++++---------
2 files changed, 27 insertions(+), 17 deletions(-)

diff --git a/bench/Main.hs b/bench/Main.hs @@ -63,16 +63,26 @@ mul = env setup $ \x -> "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" schnorr :: Benchmark -schnorr = bgroup "schnorr" [ - bench "sign_schnorr" $ nf (S.sign_schnorr s_sk s_msg) s_aux - , bench "verify_schnorr" $ nf (S.verify_schnorr s_msg s_pk) s_sig - ] +schnorr = env setup $ \big -> + bgroup "schnorr" [ + bench "sign_schnorr (small secret)" $ nf (S.sign_schnorr 2 s_msg) s_aux + , bench "sign_schnorr (large secret)" $ nf (S.sign_schnorr big s_msg) s_aux + , bench "verify_schnorr" $ nf (S.verify_schnorr s_msg s_pk) s_sig + ] + where + setup = pure . S.parse_int256 $ B16.decodeLenient + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" ecdsa :: Benchmark -ecdsa = bgroup "ecdsa" [ - bench "sign_ecdsa" $ nf (S.sign_ecdsa s_sk) s_msg - -- , bench "verify_ecdsa" $ nf (S.verify_ecdsa e_msg t) e_sig -- XX inputs - ] +ecdsa = env setup $ \big -> + 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 + ] + where + setup = pure . S.parse_int256 $ B16.decodeLenient + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" p_bs :: BS.ByteString p_bs = B16.decodeLenient diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs @@ -644,13 +644,13 @@ sign_schnorr -> BS.ByteString -- ^ message -> BS.ByteString -- ^ 32 bytes of auxilliary random data -> BS.ByteString -- ^ 64-byte Schnorr signature -sign_schnorr d' m a - | not (ge d') = error "ppad-secp256k1 (sign_schnorr): invalid secret key" +sign_schnorr _SECRET m a + | not (ge _SECRET) = error "ppad-secp256k1 (sign_schnorr): invalid secret key" | otherwise = - let p_proj = mul _CURVE_G d' + let p_proj = mul _CURVE_G _SECRET Affine x_p y_p = affine p_proj - d | I.integerTestBit y_p 0 = _CURVE_Q - d' - | otherwise = d' + d | I.integerTestBit y_p 0 = _CURVE_Q - _SECRET + | otherwise = _SECRET bytes_d = unroll32 d h_a = hash_tagged "BIP0340/aux" a @@ -802,11 +802,11 @@ _sign_ecdsa_no_hash _sign_ecdsa_no_hash = _sign_ecdsa LowS NoHash _sign_ecdsa :: SigType -> HashFlag -> Integer -> BS.ByteString -> ECDSA -_sign_ecdsa ty hf x m - | not (ge x) = error "ppad-secp256k1 (sign_ecdsa): invalid secret key" +_sign_ecdsa ty hf _SECRET m + | not (ge _SECRET) = error "ppad-secp256k1 (sign_ecdsa): invalid secret key" | otherwise = runST $ do -- RFC6979 sec 3.3a - let entropy = int2octets x -- XX timing concern + let entropy = int2octets _SECRET -- XX timing concern nonce = bits2octets h drbg <- DRBG.new SHA256.hmac entropy nonce mempty -- RFC6979 sec 2.4 @@ -825,7 +825,7 @@ _sign_ecdsa ty hf x m s = case modinv k (fi _CURVE_Q) of Nothing -> error "ppad-secp256k1 (sign_ecdsa): bad k value" -- XX timing concern - Just kinv -> remQ (remQ (h_modQ + remQ (x * r)) * kinv) + Just kinv -> remQ (remQ (h_modQ + remQ (_SECRET * r)) * kinv) if r == 0 -- negligible probability then sign_loop g else let !sig = ECDSA r s