packages feed

secp256k1-haskell 1.2.0 → 1.3.0

raw patch · 7 files changed

+239/−1 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Crypto.Secp256k1: data Bip340Sig
+ Crypto.Secp256k1: data Rand32
+ Crypto.Secp256k1: data XOnlyPubKey
+ Crypto.Secp256k1: deriveXOnlyPubKey :: Ctx -> PubKey -> XOnlyPubKey
+ Crypto.Secp256k1: mkRand32 :: ByteString -> Maybe Rand32
+ Crypto.Secp256k1: signBip340 :: Ctx -> SecKey -> Msg -> Maybe Rand32 -> Maybe Bip340Sig
+ Crypto.Secp256k1: verifyBip340 :: Ctx -> XOnlyPubKey -> Msg -> Bip340Sig -> Bool
+ Crypto.Secp256k1.Internal.Base: Bip340Sig :: ByteString -> Bip340Sig
+ Crypto.Secp256k1.Internal.Base: Rand32 :: ByteString -> Rand32
+ Crypto.Secp256k1.Internal.Base: XOnlyPubKey :: ByteString -> XOnlyPubKey
+ Crypto.Secp256k1.Internal.Base: [$sel:get:Bip340Sig] :: Bip340Sig -> ByteString
+ Crypto.Secp256k1.Internal.Base: [$sel:get:Rand32] :: Rand32 -> ByteString
+ Crypto.Secp256k1.Internal.Base: [$sel:get:XOnlyPubKey] :: XOnlyPubKey -> ByteString
+ Crypto.Secp256k1.Internal.Base: deriveXOnlyPubKey :: Ctx -> PubKey -> XOnlyPubKey
+ Crypto.Secp256k1.Internal.Base: exportXOnlyPubKey :: Ctx -> XOnlyPubKey -> ByteString
+ Crypto.Secp256k1.Internal.Base: importXOnlyPubKey :: Ctx -> ByteString -> Maybe XOnlyPubKey
+ Crypto.Secp256k1.Internal.Base: instance Data.Hashable.Class.Hashable Crypto.Secp256k1.Internal.Base.XOnlyPubKey
+ Crypto.Secp256k1.Internal.Base: instance GHC.Classes.Eq Crypto.Secp256k1.Internal.Base.Bip340Sig
+ Crypto.Secp256k1.Internal.Base: instance GHC.Classes.Eq Crypto.Secp256k1.Internal.Base.XOnlyPubKey
+ Crypto.Secp256k1.Internal.Base: instance GHC.Show.Show Crypto.Secp256k1.Internal.Base.Bip340Sig
+ Crypto.Secp256k1.Internal.Base: instance GHC.Show.Show Crypto.Secp256k1.Internal.Base.XOnlyPubKey
+ Crypto.Secp256k1.Internal.Base: mkRand32 :: ByteString -> Maybe Rand32
+ Crypto.Secp256k1.Internal.Base: newtype Bip340Sig
+ Crypto.Secp256k1.Internal.Base: newtype Rand32
+ Crypto.Secp256k1.Internal.Base: newtype XOnlyPubKey
+ Crypto.Secp256k1.Internal.Base: signBip340 :: Ctx -> SecKey -> Msg -> Maybe Rand32 -> Maybe Bip340Sig
+ Crypto.Secp256k1.Internal.Base: verifyBip340 :: Ctx -> XOnlyPubKey -> Msg -> Bip340Sig -> Bool
+ Crypto.Secp256k1.Internal.BaseOps: keyPairCreate :: Ptr LCtx -> Ptr KeyPair -> Ptr SecKey32 -> IO CInt
+ Crypto.Secp256k1.Internal.BaseOps: schnorrSign :: Ptr LCtx -> Ptr Sig64 -> Ptr Msg32 -> Ptr KeyPair -> Ptr CUChar -> IO CInt
+ Crypto.Secp256k1.Internal.BaseOps: schnorrVerify :: Ptr LCtx -> Ptr Sig64 -> Ptr Msg32 -> CSize -> Ptr XOPubKey32 -> IO CInt
+ Crypto.Secp256k1.Internal.BaseOps: xOnlyPubKeyFromPubKey :: Ptr LCtx -> Ptr XOPubKey32 -> Ptr CInt -> Ptr PubKey64 -> IO CInt
+ Crypto.Secp256k1.Internal.BaseOps: xOnlyPubKeyParse :: Ptr LCtx -> Ptr XOPubKey32 -> Ptr CUChar -> IO CInt
+ Crypto.Secp256k1.Internal.BaseOps: xOnlyPubKeySerialize :: Ptr LCtx -> Ptr CUChar -> Ptr XOPubKey32 -> IO CInt
+ Crypto.Secp256k1.Internal.ForeignTypes: data KeyPair
+ Crypto.Secp256k1.Internal.ForeignTypes: data XOPubKey32

Files

CHANGELOG.md view
@@ -4,6 +4,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.2.0] - 2023-03-14++### Changed++- Make compatible with latest upstream LTS Haskell.+ ## [1.1.0] - 2023-11-01  ### Changed
secp256k1-haskell.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           secp256k1-haskell-version:        1.2.0+version:        1.3.0 synopsis:       Bindings for secp256k1 description:    Sign and verify signatures using the secp256k1 library. category:       Crypto
src/Crypto/Secp256k1.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DuplicateRecordFields #-}+ -- | -- Module      : Crypto.Secp256k1 -- License     : UNLICENSE@@ -57,6 +58,15 @@     tweakMulPubKey,     combinePubKeys,     tweakNegate,++    -- * BIP340 Support+    XOnlyPubKey,+    deriveXOnlyPubKey,+    Rand32,+    mkRand32,+    Bip340Sig,+    signBip340,+    verifyBip340,   ) where 
src/Crypto/Secp256k1/Internal/Base.hs view
@@ -40,6 +40,12 @@     ecdsaSignatureSerializeCompact,     ecdsaSignatureSerializeDer,     ecdsaVerify,+    keyPairCreate,+    schnorrSign,+    schnorrVerify,+    xOnlyPubKeyFromPubKey,+    xOnlyPubKeyParse,+    xOnlyPubKeySerialize,   ) import Crypto.Secp256k1.Internal.Context (Ctx (..)) import Crypto.Secp256k1.Internal.ForeignTypes@@ -57,6 +63,7 @@   ) import Data.ByteString (ByteString) import Data.ByteString qualified as BS+import Data.ByteString.Unsafe qualified as BU import Data.Hashable (Hashable (..)) import Data.Maybe (fromJust, fromMaybe, isJust) import Data.String (IsString (..))@@ -445,3 +452,122 @@       valid_bs = bs_gen `suchThat` isJust       bs_gen = secKey . BS.pack <$> replicateM 32 arbitraryBoundedRandom       gen_key = fromJust <$> valid_bs++-- | An x-only pubkey corresponds to the keys @(x,y)@ and @(x, -y)@.  The+-- equality test only checks the x-coordinate.  An x-only pubkey serializes to 32+-- bytes.+--+-- @since 1.3.0+newtype XOnlyPubKey = XOnlyPubKey {get :: ByteString}+  deriving (Eq, Hashable)++instance Show XOnlyPubKey where+  showsPrec _ = showsHex . (.get)++newtype Bip340Sig = Bip340Sig {get :: ByteString}+  deriving (Eq)++instance Show Bip340Sig where+  showsPrec _ = showsHex . (.get)++newtype Rand32 = Rand32 {get :: ByteString}++-- | Create a 'Rand32' value from a 32-byte 'ByteString'+mkRand32 :: ByteString -> Maybe Rand32+mkRand32 bytes+  | BS.length bytes == 32 = Just $ Rand32 bytes+  | otherwise = Nothing++-- | Encode an x-only pubkey to bytes+--+-- @since 1.3.0+exportXOnlyPubKey :: Ctx -> XOnlyPubKey -> ByteString+exportXOnlyPubKey (Ctx fctx) pubKey = unsafePerformIO $+  withForeignPtr fctx $ \ctx ->+    unsafeUseByteString pubKey.get $ \(pub_key, _) ->+      allocaBytes 32 $ \out_ptr -> do+        result <- xOnlyPubKeySerialize ctx out_ptr pub_key+        if isSuccess result+          then packByteString (out_ptr, 32)+          else free out_ptr >> error "Unable to serialize x-only pubkey"++-- |  Import an x-only pubkey from bytes+--+-- @since 1.3.0+importXOnlyPubKey :: Ctx -> ByteString -> Maybe XOnlyPubKey+importXOnlyPubKey (Ctx fctx) inputBytes+  | BS.length inputBytes == 32 = unsafePerformIO $+      withForeignPtr fctx $ \ctx ->+        unsafeUseByteString inputBytes $ \(input, _) -> do+          pub_key <- mallocBytes 64+          result <- xOnlyPubKeyParse ctx pub_key input+          if isSuccess result+            then Just . XOnlyPubKey <$> unsafePackByteString (pub_key, 64)+            else pure Nothing+  | otherwise = Nothing++-- | Derive an x-only key from a pub key+--+-- @since 1.3.0+deriveXOnlyPubKey :: Ctx -> PubKey -> XOnlyPubKey+deriveXOnlyPubKey (Ctx fctx) pubKey = unsafePerformIO $+  withForeignPtr fctx $ \ctx ->+    unsafeUseByteString pubKey.get $ \(pub_key, _) -> do+      xonly_pub_key <- mallocBytes 64+      result <- xOnlyPubKeyFromPubKey ctx xonly_pub_key nullPtr pub_key+      if isSuccess result+        then XOnlyPubKey <$> unsafePackByteString (xonly_pub_key, 64)+        else free xonly_pub_key >> error "Unable to derive x-only pubkey"++-- | Sign a message according to BIP 340+--+-- @since 1.3.0+signBip340 ::+  Ctx ->+  -- | Secret key+  SecKey ->+  -- | Message+  Msg ->+  -- | Randomness+  Maybe Rand32 ->+  -- | Signature+  Maybe Bip340Sig+signBip340 (Ctx fctx) theSecKey message rand32 = unsafePerformIO $+  withForeignPtr fctx $ \ctx ->+    unsafeUseByteString theSecKey.get $ \(sec_key, _) ->+      allocaBytes 96 $ \key_pair -> do+        -- The 'SecKey' API guarantees that the following call will succeed+        _ <- keyPairCreate ctx key_pair sec_key+        unsafeUseByteString message.get $ \(msg_ptr, _) -> do+          withRand32 $ \(rand_ptr, _) -> do+            sig_ptr <- mallocBytes 64+            result <- schnorrSign ctx sig_ptr msg_ptr key_pair rand_ptr+            if isSuccess result+              then Just . Bip340Sig <$> unsafePackByteString (sig_ptr, 64)+              else Nothing <$ free sig_ptr+  where+    withRand32 = maybe ($ (nullPtr, 0)) (unsafeUseByteString . (.get)) rand32++-- | Verify a message according to BIP 340+--+-- @since 1.3.0+verifyBip340 ::+  Ctx ->+  XOnlyPubKey ->+  -- | Message+  Msg ->+  -- | Signature+  Bip340Sig ->+  Bool+verifyBip340 (Ctx fctx) xOnlyPubKey message sig = unsafePerformIO $+  withForeignPtr fctx $ \ctx ->+    unsafeUseByteString xOnlyPubKey.get $ \(pub_key, _) ->+      unsafeUseByteString message.get $ \(msg_ptr, messageSize) ->+        unsafeUseByteString sig.get $ \(sig_ptr, _) ->+          isSuccess+            <$> schnorrVerify+              ctx+              sig_ptr+              msg_ptr+              (fromIntegral messageSize)+              pub_key
src/Crypto/Secp256k1/Internal/BaseOps.hs view
@@ -11,6 +11,7 @@  import Crypto.Secp256k1.Internal.ForeignTypes   ( Compact64,+    KeyPair,     LCtx,     Msg32,     NonceFun,@@ -21,6 +22,7 @@     SerFlags,     Sig64,     Tweak32,+    XOPubKey32,   ) import Foreign (FunPtr, Ptr) import Foreign.C@@ -170,3 +172,67 @@     -- | number of public keys     CInt ->     IO Ret++foreign import ccall unsafe "secp256k1.h secp256k1_schnorrsig_sign"+  schnorrSign ::+    Ptr LCtx ->+    -- | Signature+    Ptr Sig64 ->+    -- | Message+    Ptr Msg32 ->+    -- | Keypair+    Ptr KeyPair ->+    -- | Randomness+    Ptr CUChar ->+    IO CInt++foreign import ccall unsafe "secp256k1.h secp256k1_schnorrsig_verify"+  schnorrVerify ::+    Ptr LCtx ->+    -- | Signature+    Ptr Sig64 ->+    -- | Message+    Ptr Msg32 ->+    -- | Message length+    CSize ->+    -- | X-Only pubkey+    Ptr XOPubKey32 ->+    IO CInt++foreign import ccall unsafe "secp256k1.h secp256k1_keypair_create"+  keyPairCreate ::+    Ptr LCtx ->+    -- | Keypair+    Ptr KeyPair ->+    -- | Secret key+    Ptr SecKey32 ->+    IO CInt++foreign import ccall unsafe "secp256k1.h secp256k1_xonly_pubkey_parse"+  xOnlyPubKeyParse ::+    Ptr LCtx ->+    -- | Parsed X-Only pubkey+    Ptr XOPubKey32 ->+    -- | Input buffer+    Ptr CUChar ->+    IO CInt++foreign import ccall unsafe "secp256k1.h secp256k1_xonly_pubkey_serialize"+  xOnlyPubKeySerialize ::+    Ptr LCtx ->+    -- | Output buffer+    Ptr CUChar ->+    -- | X-Only pubkey+    Ptr XOPubKey32 ->+    IO CInt++foreign import ccall unsafe "secp256k1.h secp256k1_xonly_pubkey_from_pubkey"+  xOnlyPubKeyFromPubKey ::+    Ptr LCtx ->+    -- | X-only pubkey+    Ptr XOPubKey32 ->+    -- | Parity+    Ptr CInt ->+    -- | Pubkey+    Ptr PubKey64 ->+    IO CInt
src/Crypto/Secp256k1/Internal/ForeignTypes.hs view
@@ -16,6 +16,10 @@  data PubKey64 +data XOPubKey32++data KeyPair+ data Msg32  data Sig64
test/Crypto/Secp256k1Spec.hs view
@@ -72,6 +72,11 @@       property $ combinePubKeyEmptyListTest ctx     it "negates tweak" $ \ctx ->       property $ negateTweakTest ctx+  describe "BIP 340" $ do+    it "verifies a signed message (null rand32)" $ \ctx ->+      property $ bip340SigTestNull ctx+    it "verifies a signed message (not-null rand32)" $ \ctx ->+      property $ bip340SigTest ctx  hexToBytes :: String -> BS.ByteString hexToBytes = decodeBase16 . assertBase16 . B8.pack@@ -310,3 +315,24 @@     Just minusOneTwk = tweakNegate ctx oneTwk     Just twoKey = tweakAddSecKey ctx oneKey oneTwk     Just subtracted = tweakAddSecKey ctx twoKey minusOneTwk++bip340SigTestNull :: Ctx -> Assertion+bip340SigTestNull ctx =+  assertBool "verifies signature" $+    verifyBip340 ctx pk theMsg sig+  where+    Just sk = secKey $ BS.replicate 32 0x01+    pk = deriveXOnlyPubKey ctx $ derivePubKey ctx sk+    Just theMsg = msg $ BS.replicate 32 0x02+    Just sig = signBip340 ctx sk theMsg Nothing++bip340SigTest :: Ctx -> Assertion+bip340SigTest ctx =+  assertBool "verifies signature" $+    verifyBip340 ctx pk theMsg sig+  where+    Just sk = secKey $ BS.replicate 32 0x01+    pk = deriveXOnlyPubKey ctx $ derivePubKey ctx sk+    Just theMsg = msg $ BS.replicate 32 0x02+    Just r = mkRand32 $ BS.replicate 32 0x03+    Just sig = signBip340 ctx sk theMsg (Just r)