secp256k1-haskell 0.2.5 → 0.3.0
raw patch · 6 files changed
+24/−740 lines, 6 files
Files
- CHANGELOG.md +15/−0
- README.md +2/−0
- secp256k1-haskell.cabal +2/−38
- src/Crypto/Secp256k1.hs +3/−292
- src/Crypto/Secp256k1/Internal.hs +2/−232
- test/Crypto/Secp256k1Spec.hs +0/−178
CHANGELOG.md view
@@ -4,10 +4,25 @@ 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). +## 0.3.0+### Fixed+- Compiles with all flags now.++### Added+- Script to compile with all flags.++### Removed+- Remove ECDH support.+- Remove Schnorr support.+- Remove Recovery support.+ ## 0.2.5 ### Changed - Reuse context aggressively. - Generate context in a single thread.++### Fixed+- Memory deallocation bug. ## 0.2.4 ### Changed
README.md view
@@ -1,3 +1,5 @@+[](https://travis-ci.org/haskoin/secp256k1-haskell)+ # Haskell bindings for secp256k1 This project contains Haskell bindings for the
secp256k1-haskell.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: f64a6c7d68d58fab5fc141dd7bda78089fe9403dc39525641eba1ca0fd92593c+-- hash: d55c31829652e236145323f89ee1ec9c61e4f18712778a13011c688d36db8b19 name: secp256k1-haskell-version: 0.2.5+version: 0.3.0 synopsis: Bindings for secp256k1 description: Sign and verify signatures using the secp256k1 library. category: Crypto@@ -27,26 +27,6 @@ type: git location: https://github.com/haskoin/secp256k1-haskell.git -flag ecdh- description: Enable (experimental) ECDH APIs- manual: True- default: False--flag negate- description: Enable tweak negate- manual: True- default: False--flag recovery- description: Enable signature key recovery APIs- manual: True- default: False--flag schnorr- description: Enable BIP-340 (Schnorr) APIs- manual: True- default: False- library exposed-modules: Crypto.Secp256k1@@ -68,14 +48,6 @@ , entropy , hashable , string-conversions- if flag(ecdh)- cpp-options: -DECDH- if flag(schnorr)- cpp-options: -DSCHNORR- if flag(recovery)- cpp-options: -DRECOVERY- if flag(negate)- cpp-options: -DNEGATE default-language: Haskell2010 test-suite spec@@ -103,13 +75,5 @@ , mtl , secp256k1-haskell , string-conversions- if flag(ecdh)- cpp-options: -DECDH- if flag(schnorr)- cpp-options: -DSCHNORR- if flag(recovery)- cpp-options: -DRECOVERY- if flag(negate)- cpp-options: -DNEGATE default-language: Haskell2010 build-tool-depends: hspec-discover:hspec-discover
src/Crypto/Secp256k1.hs view
@@ -39,16 +39,6 @@ , CompactSig(..) , exportCompactSig , importCompactSig-#ifdef RECOVERY- -- ** Recovery- , RecSig- , CompactRecSig(..)- , importCompactRecSig- , exportCompactRecSig- , convertRecSig- , signRecMsg- , recover-#endif -- * Addition & Multiplication , Tweak@@ -59,29 +49,7 @@ , tweakAddPubKey , tweakMulPubKey , combinePubKeys-#ifdef NEGATE , tweakNegate-#endif--#ifdef ECDH- -- * Diffie Hellman- , ecdh-#endif--#ifdef SCHNORR- , XOnlyPubKey- , SchnorrSig- , signMsgSchnorr- , exportSchnorrSig- , importSchnorrSig- , exportXOnlyPubKey- , importXOnlyPubKey- , verifyMsgSchnorr- , deriveXOnlyPubKey- , schnorrTweakAddPubKey- , schnorrTweakAddSecKey- , testTweakXOnlyPubKey-#endif ) where import Control.Monad (replicateM, unless, (<=<))@@ -93,9 +61,8 @@ import Data.Maybe (fromJust, fromMaybe, isJust) import Data.String (IsString (..)) import Data.String.Conversions (ConvertibleStrings, cs)-import Foreign (ForeignPtr, alloca,- allocaArray, allocaBytes,- mallocForeignPtr,+import Foreign (ForeignPtr, alloca, allocaArray,+ allocaBytes, mallocForeignPtr, nullPtr, peek, poke, pokeArray, withForeignPtr) import System.IO.Unsafe (unsafePerformIO)@@ -113,10 +80,6 @@ newtype SecKey = SecKey (ForeignPtr SecKey32) newtype Tweak = Tweak (ForeignPtr Tweak32) newtype RecSig = RecSig (ForeignPtr RecSig65)-#ifdef SCHNORR-newtype XOnlyPubKey = XOnlyPubKey (ForeignPtr XOnlyPubKey64)-newtype SchnorrSig = SchnorrSig (ForeignPtr SchnorrSig64)-#endif instance NFData PubKey where rnf (PubKey p) = p `seq` ()@@ -185,30 +148,6 @@ instance Show Sig where showsPrec _ = shows . B16.encode . exportSig -#ifdef RECOVERY-recSigFromString :: String -> Maybe RecSig-recSigFromString str = do- bs <- decodeHex str- rs <- either (const Nothing) Just $ decode bs- importCompactRecSig rs--instance Hashable RecSig where- i `hashWithSalt` s = i `hashWithSalt` encode (exportCompactRecSig s)--instance Read RecSig where- readPrec = parens $ do- String str <- lexP- maybe pfail return $ recSigFromString str--instance IsString RecSig where- fromString = fromMaybe e . recSigFromString- where- e = error "Could not decode signature from hex string"--instance Show RecSig where- showsPrec _ = shows . B16.encode . encode . exportCompactRecSig-#endif- instance Read SecKey where readPrec = parens $ do String str <- lexP@@ -248,61 +187,12 @@ instance Eq Sig where fg1 == fg2 = exportCompactSig fg1 == exportCompactSig fg2 -#ifdef RECOVERY-instance Eq RecSig where- fg1 == fg2 = exportCompactRecSig fg1 == exportCompactRecSig fg2-#endif- instance Eq SecKey where fk1 == fk2 = getSecKey fk1 == getSecKey fk2 instance Eq Tweak where ft1 == ft2 = getTweak ft1 == getTweak ft2 -#ifdef SCHNORR-instance NFData SchnorrSig where- rnf (SchnorrSig p) = p `seq` ()--instance Show SchnorrSig where- showsPrec _ = shows . B16.encode . exportSchnorrSig--instance Read SchnorrSig where- readPrec = parens $ do- String str <- lexP- maybe pfail return $ importSchnorrSig =<< decodeHex str--instance Eq SchnorrSig where- fg1 == fg2 = exportSchnorrSig fg1 == exportSchnorrSig fg2--instance IsString SchnorrSig where- fromString = fromMaybe e . (importSchnorrSig <=< decodeHex) where- e = error "Could not decode Schnorr signature from hex string"--instance Hashable SchnorrSig where- i `hashWithSalt` s = i `hashWithSalt` exportSchnorrSig s--instance NFData XOnlyPubKey where- rnf (XOnlyPubKey p) = p `seq` ()--instance Show XOnlyPubKey where- showsPrec _ = shows . B16.encode . exportXOnlyPubKey--instance Read XOnlyPubKey where- readPrec = do- String str <- lexP- maybe pfail return $ importXOnlyPubKey =<< decodeHex str--instance Eq XOnlyPubKey where- fp1 == fp2 = getXOnlyPubKey fp1 == getXOnlyPubKey fp2--instance IsString XOnlyPubKey where- fromString = fromMaybe e . (importXOnlyPubKey <=< decodeHex) where- e = error "Could not decode public key from hex string"--instance Hashable XOnlyPubKey where- i `hashWithSalt` k = i `hashWithSalt` exportXOnlyPubKey k-#endif- -- | Import 32-byte 'ByteString' as 'Msg'. msg :: ByteString -> Maybe Msg msg bs@@ -500,65 +390,8 @@ pointers ps (PubKey fp : pubs') f = withForeignPtr fp $ \p -> pointers (p:ps) pubs' f -#ifdef RECOVERY--- | Parse a compact ECDSA signature (64 bytes + recovery id).-importCompactRecSig :: CompactRecSig -> Maybe RecSig-importCompactRecSig cr =- if getCompactRecSigV cr `notElem` [0,1,2,3]- then Nothing- else withContext $ \ctx -> alloca $ \pc -> do- let- c = CompactSig (getCompactRecSigR cr) (getCompactRecSigS cr)- recid = fromIntegral $ getCompactRecSigV cr- poke pc c- fg <- mallocForeignPtr- ret <- withForeignPtr fg $ \pg ->- ecdsaRecoverableSignatureParseCompact ctx pg pc recid- if isSuccess ret then return $ Just $ RecSig fg else return Nothing---- | Serialize an ECDSA signature in compact format (64 bytes + recovery id).-exportCompactRecSig :: RecSig -> CompactRecSig-exportCompactRecSig (RecSig fg) = withContext $ \ctx ->- withForeignPtr fg $ \pg -> alloca $ \pc -> alloca $ \pr -> do- ret <- ecdsaRecoverableSignatureSerializeCompact ctx pc pr pg- unless (isSuccess ret) $ error "Could not obtain compact signature"- CompactSig r s <- peek pc- v <- fromIntegral <$> peek pr- return $ CompactRecSig r s v---- | Convert a recoverable signature into a normal signature.-convertRecSig :: RecSig -> Sig-convertRecSig (RecSig frg) = withContext $ \ctx ->- withForeignPtr frg $ \prg -> do- fg <- mallocForeignPtr- ret <- withForeignPtr fg $ \pg ->- ecdsaRecoverableSignatureConvert ctx pg prg- unless (isSuccess ret) $- error "Could not convert a recoverable signature"- return $ Sig fg---- | Create a recoverable ECDSA signature.-signRecMsg :: SecKey -> Msg -> RecSig-signRecMsg (SecKey fk) (Msg fm) = withContext $ \ctx ->- withForeignPtr fk $ \k -> withForeignPtr fm $ \m -> do- fg <- mallocForeignPtr- ret <- withForeignPtr fg $ \g ->- ecdsaSignRecoverable ctx g m k nullPtr nullPtr- unless (isSuccess ret) $ error "could not sign message"- return $ RecSig fg---- | Recover an ECDSA public key from a signature.-recover :: RecSig -> Msg -> Maybe PubKey-recover (RecSig frg) (Msg fm) = withContext $ \ctx ->- withForeignPtr frg $ \prg -> withForeignPtr fm $ \pm -> do- fp <- mallocForeignPtr- ret <- withForeignPtr fp $ \pp -> ecdsaRecover ctx pp prg pm- if isSuccess ret then return $ Just $ PubKey fp else return Nothing-#endif--#ifdef NEGATE tweakNegate :: Tweak -> Maybe Tweak-tweakNegate (Tweak fk) = withContext $ \ctx -> do+tweakNegate (Tweak fk) = unsafePerformIO $ do fnew <- mallocForeignPtr peeked <- withForeignPtr fk peek ret <- withForeignPtr fnew $ \n -> do@@ -568,122 +401,7 @@ if isSuccess ret then Just (Tweak fnew) else Nothing-#endif -#ifdef ECDH--- | Compute Diffie-Hellman secret.-ecdh :: PubKey -> SecKey -> ByteString-ecdh (PubKey pk) (SecKey sk) = withContext $ \ctx ->- withForeignPtr pk $ \pkPtr -> withForeignPtr sk $ \skPtr ->- allocaBytes size $ \o -> do- ret <- ecEcdh ctx o pkPtr skPtr nullPtr nullPtr- unless (isSuccess ret) $ error "ecdh failed"- packByteString (o, size)- where- size :: Integral a => a- size = 32-#endif--#ifdef SCHNORR--- Get 64-byte x-only public key.-getXOnlyPubKey :: XOnlyPubKey -> ByteString-getXOnlyPubKey (XOnlyPubKey fp) =- fromShort $ getXOnlyPubKey64 $ unsafePerformIO $ withForeignPtr fp peek---- | Add tweak to public key. Tweak is multiplied first by G to obtain a point.-schnorrTweakAddPubKey :: XOnlyPubKey -> Tweak -> Maybe (XOnlyPubKey, CInt)-schnorrTweakAddPubKey (XOnlyPubKey fp) (Tweak ft) = withContext $ \ctx ->- withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> alloca $ \is_negated -> do- fp' <- mallocForeignPtr- ret <- withForeignPtr fp' $ \p' -> do- pub <- peek p- poke p' pub- schnorrPubKeyTweakAdd ctx p' is_negated t- peeked_is_negated <- peek is_negated- if isSuccess ret then return $ Just $ (XOnlyPubKey fp', peeked_is_negated) else return Nothing---- | Add tweak to secret key.-schnorrTweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey-schnorrTweakAddSecKey (SecKey fk) (Tweak ft) = withContext $ \ctx ->- withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do- fk' <- mallocForeignPtr- ret <- withForeignPtr fk' $ \k' -> do- key <- peek k- poke k' key- schnorrSecKeyTweakAdd ctx k' t- if isSuccess ret then return $ Just $ SecKey fk' else return Nothing--signMsgSchnorr :: SecKey -> Msg -> SchnorrSig-signMsgSchnorr (SecKey fk) (Msg fm) =- withContext $ \ctx ->- withForeignPtr fk $ \k ->- withForeignPtr fm $ \m -> do- fg <- mallocForeignPtr- ret <-- withForeignPtr fg $ \g ->- schnorrSign ctx g m k nullPtr nullPtr- unless (isSuccess ret) $ error "could not schnorr-sign message"- return $ SchnorrSig fg--exportSchnorrSig :: SchnorrSig -> ByteString-exportSchnorrSig (SchnorrSig fg) = withContext $ \ctx ->- withForeignPtr fg $ \g -> allocaBytes 64 $ \o -> do- ret <- signatureSerializeSchnorr ctx o g- unless (isSuccess ret) $ error "could not serialize schnorr signature"- packByteString (o, 64)--importXOnlyPubKey :: ByteString -> Maybe XOnlyPubKey-importXOnlyPubKey bs- | BS.length bs == 32 = withContext $ \ctx -> do- fp <- mallocForeignPtr- ret <- withForeignPtr fp $ \pfp -> useByteString bs $ \(inp, _) ->- schnorrXOnlyPubKeyParse ctx pfp inp- if isSuccess ret- then return $ Just $ XOnlyPubKey fp- else return Nothing- | otherwise = Nothing--importSchnorrSig :: ByteString -> Maybe SchnorrSig-importSchnorrSig bs- | BS.length bs == 64 = withContext $ \ctx -> do- fp <- mallocForeignPtr- ret <- withForeignPtr fp $ \pfp -> useByteString bs $ \(inp, _) ->- schnorrSignatureParse ctx pfp inp- if isSuccess ret- then return $ Just $ SchnorrSig fp- else return Nothing- | otherwise = Nothing--verifyMsgSchnorr :: XOnlyPubKey -> SchnorrSig -> Msg -> Bool-verifyMsgSchnorr (XOnlyPubKey fp) (SchnorrSig fg) (Msg fm) = withContext $ \ctx ->- withForeignPtr fp $ \p -> withForeignPtr fg $ \g ->- withForeignPtr fm $ \m -> isSuccess <$> schnorrSignatureVerify ctx g m p--exportXOnlyPubKey :: XOnlyPubKey -> ByteString-exportXOnlyPubKey (XOnlyPubKey pub) = withContext $ \ctx ->- withForeignPtr pub $ \p -> allocaBytes 32 $ \o -> do- ret <- schnorrPubKeySerialize ctx o p- unless (isSuccess ret) $ error "could not serialize x-only public key"- packByteString (o, 32)--deriveXOnlyPubKey :: SecKey -> XOnlyPubKey-deriveXOnlyPubKey (SecKey fk) = withContext $ \ctx -> withForeignPtr fk $ \k -> do- fp <- mallocForeignPtr- ret <- withForeignPtr fp $ \p -> schnorrXOnlyPubKeyCreate ctx p k- unless (isSuccess ret) $ error "could not derive x-only public key"- return $ XOnlyPubKey fp--testTweakXOnlyPubKey :: XOnlyPubKey -> CInt -> XOnlyPubKey -> Tweak -> Bool-testTweakXOnlyPubKey (XOnlyPubKey fp) is_negated (XOnlyPubKey internal) (Tweak ft) =- withContext $ \ctx ->- withForeignPtr fp $ \p ->- withForeignPtr internal $ \internalp ->- withForeignPtr ft $ \t -> do- ret <- xOnlyPubKeyTweakTest ctx p is_negated internalp t- return $ isSuccess ret--- End of Schnorr block-#endif- instance Arbitrary Msg where arbitrary = gen_msg where@@ -699,10 +417,3 @@ instance Arbitrary PubKey where arbitrary = derivePubKey <$> arbitrary--#ifdef SCHNORR-instance Arbitrary XOnlyPubKey where- arbitrary = do- key <- arbitrary- return $ deriveXOnlyPubKey key-#endif
src/Crypto/Secp256k1/Internal.hs view
@@ -23,8 +23,8 @@ import qualified Data.Serialize.Get as Get import qualified Data.Serialize.Put as Put import Data.Void (Void)-import Foreign (FunPtr, Ptr, Storable (..),- alloca, castPtr, copyArray)+import Foreign (FunPtr, Ptr, Storable (..), alloca,+ castPtr, copyArray) import Foreign.C (CInt (..), CSize (..), CString, CUChar, CUInt (..)) import GHC.Generics (Generic)@@ -52,16 +52,6 @@ newtype RecSig65 = RecSig65 { getRecSig65 :: ShortByteString } deriving (Read, Show, Eq, Ord, Generic, NFData) -#ifdef RECOVERY-data CompactRecSig =- CompactRecSig- { getCompactRecSigR :: !ShortByteString- , getCompactRecSigS :: !ShortByteString- , getCompactRecSigV :: !Word8- }- deriving (Show, Eq, Ord, Generic, NFData)-#endif- newtype Seed32 = Seed32 { getSeed32 :: ShortByteString } deriving (Read, Show, Eq, Ord, Generic, NFData) @@ -86,14 +76,6 @@ newtype Ret = Ret { getRet :: CInt } deriving (Read, Show, Eq, Ord, Generic, NFData) -#ifdef SCHNORR-newtype XOnlyPubKey64 = XOnlyPubKey64 { getXOnlyPubKey64 :: ShortByteString }- deriving (Read, Show, Eq, Ord, Generic, NFData)--newtype SchnorrSig64 = SchnorrSig64 { getSchnorrSig64 :: ShortByteString }- deriving (Read, Show, Eq, Ord, Generic, NFData)-#endif- verify :: CtxFlags verify = CtxFlags 0x0101 @@ -116,22 +98,6 @@ packByteString :: (Ptr CUChar, CSize) -> IO ByteString packByteString (b, l) = BS.packCStringLen (castPtr b, fromIntegral l) -#if SCHNORR-instance Storable XOnlyPubKey64 where- sizeOf _ = 64- alignment _ = 1- peek p = XOnlyPubKey64 . toShort <$> packByteString (castPtr p, 64)- poke p (XOnlyPubKey64 k) = useByteString (fromShort k) $- \(b, _) -> copyArray (castPtr p) b 64--instance Storable SchnorrSig64 where- sizeOf _ = 64- alignment _ = 1- peek p = SchnorrSig64 . toShort <$> packByteString (castPtr p, 64)- poke p (SchnorrSig64 k) = useByteString (fromShort k) $- \(b, _) -> copyArray (castPtr p) b 64-#endif- instance Storable PubKey64 where sizeOf _ = 64 alignment _ = 1@@ -173,47 +139,6 @@ Put.putShortByteString r Put.putShortByteString s -#ifdef RECOVERY-instance Storable RecSig65 where- sizeOf _ = 65- alignment _ = 1- peek p = RecSig65 . toShort <$> packByteString (castPtr p, 65)- poke p (RecSig65 k) = useByteString (fromShort k) $- \(b, _) -> copyArray (castPtr p) b 65--instance Storable CompactRecSig where- sizeOf _ = 65- alignment _ = 1- peek p = do- bs <- BS.packCStringLen (castPtr p, 65)- let (r, s) = BS.splitAt 32 $ BS.take 64 bs- v = BS.last bs- return CompactRecSig { getCompactRecSigR = toShort r- , getCompactRecSigS = toShort s- , getCompactRecSigV = v- }- poke p CompactRecSig{..} =- useByteString bs $ \(b, _) -> copyArray (castPtr p) b 65- where- bs = fromShort getCompactRecSigR `BS.append`- fromShort getCompactRecSigS `BS.snoc`- getCompactRecSigV--instance Serialize CompactRecSig where- get = do- r <- Get.getByteString 32- s <- Get.getByteString 32- v <- Get.getWord8- return CompactRecSig { getCompactRecSigR = toShort r- , getCompactRecSigS = toShort s- , getCompactRecSigV = v- }- put (CompactRecSig r s v) = do- Put.putShortByteString r- Put.putShortByteString s- Put.putWord8 v-#endif- instance Storable Msg32 where sizeOf _ = 32 alignment _ = 1@@ -410,14 +335,12 @@ -> Ptr Tweak32 -> IO Ret -#ifdef NEGATE foreign import ccall "secp256k1.h secp256k1_ec_privkey_negate" ecTweakNegate :: Ptr Ctx -> Ptr Tweak32 -> IO Ret-#endif foreign import ccall "secp256k1.h secp256k1_ec_pubkey_tweak_add"@@ -458,156 +381,3 @@ -> Ptr (Ptr PubKey64) -- ^ pointer to array of public keys -> CInt -- ^ number of public keys -> IO Ret--#ifdef RECOVERY-foreign import ccall- "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_parse_compact"- ecdsaRecoverableSignatureParseCompact- :: Ptr Ctx- -> Ptr RecSig65- -> Ptr CompactSig- -> CInt- -> IO Ret--foreign import ccall- "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_convert"- ecdsaRecoverableSignatureConvert- :: Ptr Ctx- -> Ptr Sig64- -> Ptr RecSig65- -> IO Ret--foreign import ccall- "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_serialize_compact"- ecdsaRecoverableSignatureSerializeCompact- :: Ptr Ctx- -> Ptr CompactSig- -> Ptr CInt- -> Ptr RecSig65- -> IO Ret--foreign import ccall- "secp256k1_recovery.h secp256k1_ecdsa_sign_recoverable"- ecdsaSignRecoverable- :: Ptr Ctx- -> Ptr RecSig65- -> Ptr Msg32- -> Ptr SecKey32- -> Ptr Void- -> Ptr a -- ^ nonce data- -> IO Ret--foreign import ccall- "secp256k1_recovery.h secp256k1_ecdsa_recover"- ecdsaRecover- :: Ptr Ctx- -> Ptr PubKey64- -> Ptr RecSig65- -> Ptr Msg32- -> IO Ret-#endif--#ifdef SCHNORR-foreign import ccall- "secp256k1.h secp256k1_xonly_pubkey_tweak_add"- schnorrPubKeyTweakAdd- :: Ptr Ctx- -> Ptr XOnlyPubKey64- -> Ptr CInt- -> Ptr Tweak32- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_xonly_seckey_tweak_add"- schnorrSecKeyTweakAdd- :: Ptr Ctx- -> Ptr SecKey32- -> Ptr Tweak32- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_schnorrsig_serialize"- signatureSerializeSchnorr- :: Ptr Ctx- -> Ptr CUChar -- ^ array for encoded signature, must be large enough- -> Ptr SchnorrSig64- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_schnorrsig_sign"- schnorrSign- :: Ptr Ctx- -> Ptr SchnorrSig64- -> Ptr Msg32- -> Ptr SecKey32- -- TODO- -- This is actually an "extended nonce function" in the C code. So this signature is broken,- -- but we pass a nullFunPtr (and this module is Internal), so it doesn't matter right now.- -> Ptr Void- -> Ptr a -- ^ nonce data- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_xonly_pubkey_tweak_test"- xOnlyPubKeyTweakTest- :: Ptr Ctx- -> Ptr XOnlyPubKey64 -- output_pubkey- -> CInt -- is_negated- -> Ptr XOnlyPubKey64 -- internal_pubkey- -> Ptr Tweak32- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_xonly_pubkey_serialize"- schnorrPubKeySerialize- :: Ptr Ctx- -> Ptr CUChar -- 32 bytes output buffer- -> Ptr XOnlyPubKey64- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_schnorrsig_verify"- schnorrSignatureVerify- :: Ptr Ctx- -> Ptr SchnorrSig64- -> Ptr Msg32- -> Ptr XOnlyPubKey64- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_schnorrsig_parse"- schnorrSignatureParse- :: Ptr Ctx- -> Ptr SchnorrSig64 -- out- -> Ptr CUChar -- in- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_xonly_pubkey_parse"- schnorrXOnlyPubKeyParse- :: Ptr Ctx- -> Ptr XOnlyPubKey64 -- out- -> Ptr CUChar -- in- -> IO Ret--foreign import ccall- "secp256k1.h secp256k1_xonly_pubkey_create"- schnorrXOnlyPubKeyCreate- :: Ptr Ctx- -> Ptr XOnlyPubKey64- -> Ptr SecKey32- -> IO Ret-#endif--#ifdef ECDH-foreign import ccall- "secp256k1_ecdh.h secp256k1_ecdh"- ecEcdh- :: Ptr Ctx- -> Ptr CUChar- -> Ptr PubKey64- -> Ptr SecKey32- -> Ptr a- -> Ptr b- -> IO Ret-#endif
test/Crypto/Secp256k1Spec.hs view
@@ -61,53 +61,7 @@ it "multiply public key" $ property tweakMulPubKeyTest it "combine public keys" $ property combinePubKeyTest it "can't combine 0 public keys" $ property combinePubKeyEmptyListTest-#ifdef RECOVERY- describe "recovery" $ do- it "recovers public keys" $- property recoverTest- it "recovers key from signed message" $- property signRecMsgTest- it "does not recover bad public keys" $- property badRecoverTest- it "detects bad recoverable signature" $- property badRecSignatureTest- it "serializes compact recoverable signature" $- property serializeCompactRecSigTest- it "shows and reads recoverable signature" $- property (showReadRecSig :: (SecKey, Msg) -> Bool)- it "reads recoverable signature from string" $ property $ isStringRecSig-#endif-#ifdef NEGATE- describe "negate" $ it "negates tweak" $ property negateTweakTest-#endif-#ifdef ECDH- describe "ecdh" $ do- it "computes dh secret" $ property computeDhSecret-#endif-#ifdef SCHNORR- describe "schnorr (bip-340)" $ do- it "validates test vector 0" $- property bip340Vector0- it "rejects test vector 5" $- property $- failingVectorToAssertion InvalidPubKey- (- hexToBytes "eefdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34",- hexToBytes "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",- hexToBytes "667c2f778e0616e611bd0c14b8a600c5884551701a949ef0ebfd72d452d64e844160bcfc3f466ecb8facd19ade57d8699d74e7207d78c6aedc3799b52a8e0598"- )- it "rejects test vector 6" $- property $- failingVectorToAssertion InvalidSig- (- hexToBytes "dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",- hexToBytes "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",- hexToBytes "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9935554d1aa5f0374e5cdaacb3925035c7c169b27c4426df0a6b19af3baeab138"- )- it "makes a valid taproot key spend signature" $- property taprootKeySpend-#endif hexToBytes :: String -> BS.ByteString hexToBytes = fst . B16.decode . B8.pack@@ -121,13 +75,6 @@ g = signMsg k m hex = B16.encode $ exportSig g -#ifdef RECOVERY-isStringRecSig :: (SecKey, Msg) -> Bool-isStringRecSig (k, m) = g == fromString (cs hex) where- g = signRecMsg k m- hex = B16.encode . S.encode $ exportCompactRecSig g-#endif- isStringMsg :: Msg -> Bool isStringMsg m = m == fromString (cs m') where m' = B16.encode $ getMsg m@@ -150,12 +97,6 @@ showReadSig (k, m) = showRead sig where sig = signMsg k m -#ifdef RECOVERY-showReadRecSig :: (SecKey, Msg) -> Bool-showReadRecSig (k, m) = showRead recSig where- recSig = signRecMsg k m-#endif- showRead :: (Show a, Read a, Eq a) => a -> Bool showRead x = read (show x) == x @@ -169,36 +110,10 @@ ys <- mapM (P.spawnP . signMsgTest) xs and <$> mapM P.get ys -#ifdef RECOVERY-signRecMsgTest :: (Msg, SecKey) -> Bool-signRecMsgTest (fm, fk) = verifySig fp fg fm where- fp = derivePubKey fk- fg = convertRecSig $ signRecMsg fk fm--recoverTest :: (Msg, SecKey) -> Bool-recoverTest (fm, fk) = recover fg fm == Just fp where- fp = derivePubKey fk- fg = signRecMsg fk fm--badRecoverTest :: (Msg, SecKey, Msg) -> Property-badRecoverTest (fm, fk, fm') =- fm' /= fm ==> fp' /= Nothing ==> fp' /= Just fp- where- fg = signRecMsg fk fm- fp = derivePubKey fk- fp' = recover fg fm'-#endif- badSignatureTest :: (Msg, SecKey, PubKey) -> Bool badSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where fg = signMsg fk fm -#ifdef RECOVERY-badRecSignatureTest :: (Msg, SecKey, PubKey) -> Bool-badRecSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where- fg = convertRecSig $ signRecMsg fk fm-#endif- normalizeSigTest :: (Msg, SecKey) -> Bool normalizeSigTest (fm, fk) = not norm && sig == fg where fg = signMsg fk fm@@ -226,16 +141,6 @@ where fg = signMsg fk fm -#ifdef RECOVERY-serializeCompactRecSigTest :: (Msg, SecKey) -> Bool-serializeCompactRecSigTest (fm, fk) =- case importCompactRecSig $ exportCompactRecSig fg of- Just fg' -> fg == fg'- Nothing -> False- where- fg = signRecMsg fk fm-#endif- serializeSecKeyTest :: SecKey -> Bool serializeSecKeyTest fk = case secKey $ getSecKey fk of@@ -316,7 +221,6 @@ expected = Nothing combined = combinePubKeys [] -#ifdef NEGATE negateTweakTest :: Assertion negateTweakTest = assertEqual "can recover secret key 1 after adding tweak 1" oneKey subtracted@@ -328,85 +232,3 @@ Just minusOneTwk = tweakNegate oneTwk Just twoKey = tweakAddSecKey oneKey oneTwk Just subtracted = tweakAddSecKey twoKey minusOneTwk-#endif--#ifdef ECDH-computeDhSecret :: Assertion-computeDhSecret =- assertEqual "ecdh computes known secret" expected computed- where- computed = do- pub <- importPubKey $ hexToBytes- "028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7"- sec <- secKey $ hexToBytes- "1212121212121212121212121212121212121212121212121212121212121212"- pure $ ecdh pub sec- expected = Just $ hexToBytes- "1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3"-#endif--#ifdef SCHNORR-data VectorError = InvalidPubKey | InvalidSig | InvalidMsg | InvalidSigFormat- deriving (Eq, Show)--failingVectorToAssertion :: VectorError -> (BS.ByteString, BS.ByteString, BS.ByteString) -> Assertion-failingVectorToAssertion expectedFailure (pubBytes, msgBytes, sigBytes) =- assertEqual ("expected error " <> show expectedFailure <> " occurs") (Left expectedFailure) computed- where- computed :: Either VectorError ()- computed =- let- pubM = importXOnlyPubKey pubBytes- sigM = importSchnorrSig sigBytes- msgM = msg $ msgBytes- in- case (pubM, sigM, msgM) of- (Nothing, _, _) -> Left InvalidPubKey- (_, Nothing, _) -> Left InvalidSigFormat- (_, _, Nothing) -> Left InvalidMsg- (Just pub, Just sig, Just msg) ->- if verifyMsgSchnorr pub sig msg- then Right ()- else Left InvalidSig--bip340Vector0 :: Assertion-bip340Vector0 =- passingVectorToAssertion 0- (- BS.replicate 31 0 <> B8.pack ['\x01']- , BS.replicate 32 0- , hexToBytes "db46b5cdc554edbd7765611b75d7bdbc639cf538fb6e9ef04a61884b765343aae148954f27eb69291a9045862f8ae4fa53436c117e9397c70275d5398066d44b"- )---- Integer is BIP-340 vector test number-passingVectorToAssertion :: Integer -> (BS.ByteString, BS.ByteString, BS.ByteString) -> Assertion-passingVectorToAssertion idx (secBytes, msgBytes, sigBytes) =- assertEqual ("BIP-340 test vector " <> show idx <> " signature matches") expectedSig computedSig- where- expectedSig :: Maybe SchnorrSig- expectedSig = importSchnorrSig $ sigBytes- computedSig :: Maybe SchnorrSig- computedSig = do- sec <- secKey secBytes- msg <- msg $ msgBytes- pure $ signMsgSchnorr sec msg---- This test is ported from the C code, it is called 'test_schnorrsig_taproot'.--- But this version was modified to use fixed keys and messages.-taprootKeySpend :: Assertion-taprootKeySpend =- assertEqual "derivation succeded and resulting signature is valid" (Just True) computed- where- computed = do- sec <- secKey $ BS.replicate 32 32- msgToSign <- msg $ BS.replicate 32 00-- let internalPub = deriveXOnlyPubKey sec- twea <- tweak $ exportXOnlyPubKey internalPub- (outputPub, isNegated) <- schnorrTweakAddPubKey internalPub twea-- -- This is a 'key spend' in Taproot terminology:- tweakSec <- schnorrTweakAddSecKey sec twea- let sig = signMsgSchnorr tweakSec msgToSign- pure $ verifyMsgSchnorr outputPub sig msgToSign-#endif