ppad-secp256k1 0.2.1 → 0.2.2
raw patch · 3 files changed
+61/−39 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Crypto.Curve.Secp256k1: _CURVE_G :: Projective
+ Crypto.Curve.Secp256k1: _CURVE_P :: Integer
+ Crypto.Curve.Secp256k1: _CURVE_Q :: Integer
+ Crypto.Curve.Secp256k1: _CURVE_ZERO :: Projective
+ Crypto.Curve.Secp256k1: modQ :: Integer -> Integer
+ Crypto.Curve.Secp256k1: remQ :: Integer -> Integer
Files
- CHANGELOG +3/−0
- lib/Crypto/Curve/Secp256k1.hs +57/−38
- ppad-secp256k1.cabal +1/−1
CHANGELOG view
@@ -1,5 +1,8 @@ # Changelog +- 0.2.2 (2025-02-16)+ * Exports the secp256k1 "point at infinity" as _CURVE_ZERO.+ - 0.2.1 (2024-12-18) * Adds 'serialize_point' for compressed-format serialization of secp256k1 points.
lib/Crypto/Curve/Secp256k1.hs view
@@ -21,10 +21,18 @@ -- "low-S" signatures) on the elliptic curve secp256k1. module Crypto.Curve.Secp256k1 (+ -- * Field and group parameters+ _CURVE_Q+ , _CURVE_P+ , remQ+ , modQ+ -- * secp256k1 points- Pub+ , Pub , derive_pub , derive_pub'+ , _CURVE_G+ , _CURVE_ZERO -- * Parsing , parse_int256@@ -74,11 +82,6 @@ -- for testing/benchmarking , _sign_ecdsa_no_hash , _sign_ecdsa_no_hash'- , _CURVE_P- , _CURVE_Q- , _CURVE_G- , remQ- , modQ ) where import Control.Monad (when)@@ -182,15 +185,6 @@ where l = BS.length u --- replacing the following w/a series of functions with the hashed tags--- hard-coded is possible, but there is virtually no performance benefit---- (bip0340) tagged hash function-hash_tagged :: BS.ByteString -> BS.ByteString -> BS.ByteString-hash_tagged tag x =- let !h = SHA256.hash tag- in SHA256.hash (h <> h <> x)- -- (bip0340) return point with x coordinate == x and with even y coordinate lift :: Integer -> Maybe Affine lift x@@ -237,7 +231,7 @@ -- Convert to affine coordinates. affine :: Projective -> Affine affine p@(Projective x y z)- | p == _ZERO = Affine 0 0+ | p == _CURVE_ZERO = Affine 0 0 | z == 1 = Affine x y | otherwise = case modinv z (fi _CURVE_P) of Nothing -> error "ppad-secp256k1 (affine): impossible point"@@ -246,7 +240,7 @@ -- Convert to projective coordinates. projective :: Affine -> Projective projective (Affine x y)- | x == 0 && y == 0 = _ZERO+ | x == 0 && y == 0 = _CURVE_ZERO | otherwise = Projective x y 1 -- Point is valid@@ -260,13 +254,13 @@ -- curve parameters ----------------------------------------------------------- -- see https://www.secg.org/sec2-v2.pdf for parameter specs --- secp256k1 field prime+-- | secp256k1 field prime. ----- = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1+-- = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 _CURVE_P :: Integer _CURVE_P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F --- secp256k1 group order+-- | secp256k1 group order. _CURVE_Q :: Integer _CURVE_Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 @@ -290,7 +284,7 @@ _CURVE_B :: Integer _CURVE_B = 7 --- secp256k1 generator+-- | secp256k1 generator point. -- -- = parse_point -- "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"@@ -299,9 +293,14 @@ x = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 y = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 +-- | secp256k1 zero point / point at infinity / monoidal identity.+_CURVE_ZERO :: Projective+_CURVE_ZERO = Projective 0 1 0+ -- secp256k1 zero point / point at infinity / monoidal identity _ZERO :: Projective _ZERO = Projective 0 1 0+{-# DEPRECATED _ZERO "use _CURVE_ZERO instead" #-} -- secp256k1 in prime order j-invariant 0 form (i.e. a == 0). weierstrass :: Integer -> Integer@@ -321,13 +320,12 @@ remP a = I.integerRem a _CURVE_P {-# INLINE remP #-} --- Division modulo secp256k1 group order.+-- | 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)+-- | Division modulo secp256k1 group order, when argument is nonnegative. remQ :: Integer -> Integer remQ a = I.integerRem a _CURVE_Q {-# INLINE remQ #-}@@ -356,8 +354,8 @@ 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' r (\rv -> remP (rv * numv))+ modifySTRef' num (\numv -> remP (numv * numv)) modifySTRef' e (`I.integerShiftR` 1) loop @@ -555,7 +553,7 @@ mul :: Projective -> Integer -> Projective mul p _SECRET | not (ge _SECRET) = error "ppad-secp256k1 (mul): scalar not in group"- | otherwise = loop (0 :: Int) _ZERO _CURVE_G p _SECRET+ | otherwise = loop (0 :: Int) _CURVE_ZERO _CURVE_G p _SECRET where loop !j !acc !f !d !m | j == _CURVE_Q_BITS = acc@@ -572,10 +570,10 @@ -- Don't use this function if the scalar could potentially be a secret. mul_unsafe :: Projective -> Integer -> Projective mul_unsafe p n- | n == 0 = _ZERO+ | n == 0 = _CURVE_ZERO | not (ge n) = error "ppad-secp256k1 (mul_unsafe): scalar not in group"- | otherwise = loop _ZERO p n+ | otherwise = loop _CURVE_ZERO p n where loop !r !d m | m <= 0 = r@@ -635,7 +633,7 @@ -- secp256k1 points. mul_wnaf :: Context -> Integer -> Projective mul_wnaf Context {..} _SECRET =- loop 0 _ZERO _CURVE_G _SECRET+ loop 0 _CURVE_ZERO _CURVE_G _SECRET where wins = 256 `quot` ctxW + 1 wsize = 2 ^ (ctxW - 1)@@ -779,8 +777,8 @@ -- | Serialize a secp256k1 point in 33-byte compressed form. ----- >>> serialize_point <secp256k1 point>--- "<33-byte bytestring>"+-- >>> serialize_point pub+-- "<33-byte compressed point>" serialize_point :: Projective -> BS.ByteString serialize_point (affine -> Affine x y) = BS.cons b (unroll32 x) where b | I.integerTestBit y 0 = 0x03@@ -845,11 +843,11 @@ | otherwise = _SECRET bytes_d = unroll32 d- h_a = hash_tagged "BIP0340/aux" a+ h_a = hash_aux a t = xor bytes_d h_a bytes_p = unroll32 x_p- rand = hash_tagged "BIP0340/nonce" (t <> bytes_p <> m)+ rand = hash_nonce (t <> bytes_p <> m) k' = modQ (roll32 rand) @@ -861,7 +859,7 @@ | otherwise = k' bytes_r = unroll32 x_r- e = modQ . roll32 . hash_tagged "BIP0340/challenge"+ e = modQ . roll32 . hash_challenge $ bytes_r <> bytes_p <> m bytes_ked = unroll32 (modQ (k + e * d))@@ -920,16 +918,37 @@ let (roll32 -> r, roll32 -> s) = BS.splitAt 32 sig in if r >= _CURVE_P || s >= _CURVE_Q then False- else let e = modQ . roll32 $ hash_tagged "BIP0340/challenge"+ else let e = modQ . roll32 $ hash_challenge (unroll32 r <> unroll32 x_P <> m) dif = add (_mul s) (neg (mul_unsafe (projective capP) e))- in if dif == _ZERO+ in if dif == _CURVE_ZERO then False else let Affine x_R y_R = affine dif in not (I.integerTestBit y_R 0 || x_R /= r) {-# INLINE _verify_schnorr #-} +-- hardcoded tag of BIP0340/aux+--+-- \x -> let h = SHA256.hash "BIP0340/aux"+-- in SHA256.hash (h <> h <> x)+hash_aux :: BS.ByteString -> BS.ByteString+hash_aux x = SHA256.hash $+ "\241\239N^\192c\202\218m\148\202\250\157\152~\160i&X9\236\193\US\151-w\165.\216\193\204\144\241\239N^\192c\202\218m\148\202\250\157\152~\160i&X9\236\193\US\151-w\165.\216\193\204\144" <> x+{-# INLINE hash_aux #-}++-- hardcoded tag of BIP0340/nonce+hash_nonce :: BS.ByteString -> BS.ByteString+hash_nonce x = SHA256.hash $+ "\aIw4\167\155\203\&5[\155\140}\ETXO\DC2\FS\244\&4\215>\247-\218\EM\135\NULa\251R\191\235/\aIw4\167\155\203\&5[\155\140}\ETXO\DC2\FS\244\&4\215>\247-\218\EM\135\NULa\251R\191\235/" <> x+{-# INLINE hash_nonce #-}++-- hardcoded tag of BIP0340/challenge+hash_challenge :: BS.ByteString -> BS.ByteString+hash_challenge x = SHA256.hash $+ "{\181-z\159\239X2>\177\191z@}\179\130\210\243\242\216\ESC\177\"OI\254Q\143mH\211|{\181-z\159\239X2>\177\191z@}\179\130\210\243\242\216\ESC\177\"OI\254Q\143mH\211|" <> x+{-# INLINE hash_challenge #-}+ -- ecdsa ---------------------------------------------------------------------- -- see https://www.rfc-editor.org/rfc/rfc6979, https://secg.org/sec1-v2.pdf @@ -1211,7 +1230,7 @@ u1 = remQ (e * s_inv) u2 = remQ (r * s_inv) capR = add (_mul u1) (mul_unsafe p u2)- in if capR == _ZERO+ in if capR == _CURVE_ZERO then False else let Affine (modQ -> v) _ = affine capR in v == r
ppad-secp256k1.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: ppad-secp256k1-version: 0.2.1+version: 0.2.2 synopsis: Schnorr signatures & ECDSA on the elliptic curve secp256k1 license: MIT license-file: LICENSE