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 41b8bc9f75f7d54247b823731a44c62703b93e1d
parent 9fa71722fa32446e0c457ebe77a5acd7eec0c055
Author: Jared Tobin <jared@jtobin.io>
Date:   Thu,  7 Nov 2024 10:44:57 +0400

lib: add parse_sig

Diffstat:
Mlib/Crypto/Curve/Secp256k1.hs | 52++++++++++++++++++++++++++++++++--------------------
Mtest/Noble.hs | 6+++---
2 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs @@ -27,6 +27,7 @@ module Crypto.Curve.Secp256k1 ( -- * Parsing , parse_int256 , parse_point + , parse_sig -- * BIP0340 Schnorr signatures , sign_schnorr @@ -342,26 +343,26 @@ ge n = 0 < n && n < _CURVE_Q -- For a, return x such that a = x x mod _CURVE_P. modsqrtP :: Integer -> Maybe Integer modsqrtP n = runST $ do - r <- newSTRef 1 - num <- newSTRef n - e <- newSTRef ((_CURVE_P + 1) `I.integerQuot` 4) - - let loop = do - ev <- readSTRef e - when (ev > 0) $ do - when (I.integerTestBit ev 0) $ do - numv <- readSTRef num - 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 remP (rv * rv) == n - then Just $! rv - else Nothing + r <- newSTRef 1 + num <- newSTRef n + e <- newSTRef ((_CURVE_P + 1) `I.integerQuot` 4) + + let loop = do + ev <- readSTRef e + when (ev > 0) $ do + when (I.integerTestBit ev 0) $ do + numv <- readSTRef num + 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 remP (rv * rv) == n + then Just $! rv + else Nothing -- ec point operations -------------------------------------------------------- @@ -659,6 +660,17 @@ _parse_uncompressed h (BS.splitAt _CURVE_Q_BYTES -> (roll32 -> x, roll32 -> y)) then Just $! p else Nothing +-- | Parse an ECDSA signature encoded in 64-byte "compact" form. +-- +-- >>> parse_sig <64-byte compact signature> +-- "<ecdsa signature>" +parse_sig :: BS.ByteString -> Maybe ECDSA +parse_sig bs + | BS.length bs /= 64 = Nothing + | otherwise = pure $ + let (roll -> r, roll -> s) = BS.splitAt 32 bs + in ECDSA r s + -- schnorr -------------------------------------------------------------------- -- see https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki diff --git a/test/Noble.hs b/test/Noble.hs @@ -100,9 +100,9 @@ instance A.FromJSON ValidTest where <*> fmap toBS (m .: "signature") parse_compact :: BS.ByteString -> ECDSA -parse_compact bs = - let (roll -> r, roll -> s) = BS.splitAt 32 bs - in ECDSA r s +parse_compact bs = case parse_sig bs of + Nothing -> error "bang" + Just s -> s data InvalidTest = InvalidTest { iv_sign :: ![(Int, InvalidSignTest)]