libsecp256k1 (empty) → 0.0.0
raw patch · 10 files changed
+2008/−0 lines, 10 filesdep +HUnitdep +QuickCheckdep +basesetup-changed
Dependencies added: HUnit, QuickCheck, base, base16, bytestring, cereal, deepseq, entropy, hashable, hspec, monad-par, mtl, secp256k1-haskell, string-conversions, unliftio-core
Files
- CHANGELOG.md +123/−0
- LICENSE +19/−0
- README.md +6/−0
- Setup.hs +2/−0
- libsecp256k1.cabal +79/−0
- src/Crypto/Secp256k1.hs +510/−0
- src/Crypto/Secp256k1/Prim.hs +554/−0
- test/Crypto/Secp256k1/PrimSpec.hs +466/−0
- test/Crypto/Secp256k1Spec.hs +248/−0
- test/Spec.hs +1/−0
+ CHANGELOG.md view
@@ -0,0 +1,123 @@+# Changelog+All notable changes to this project will be documented in this file.++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.6.1+### Changed+- Bump version and LTS Haskell.+- Update dependencies.++## 0.6.0+### Changed+- Remove all use of CPP.+- Depend on base16 instead of the old base16-bytestring package.+- Target ghc-9.0.1 compiler.++### Fixed+- Do not crash on bytestrings that are backed by null C pointers.++## 0.5.0+### Changed+- Include version boundaries and other changes submitted by Emily Pillmore.++## 0.4.0+### Changed+- Remove fragile ForeignPtr implementation in favor of just storing ByteStrings.+- Reuse memory instead of copying when possible.++## 0.3.1+### Fixed+- Use unsafe calls in FFI.++## 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+- Update Cabal and package version.++## 0.2.3+### Changed+- Return meaningful error upon encountering weird ret status from upstream code.++### Added+- Test parallel signature creation and verification.++## 0.2.2+### Removed+- Hide tweak negation behind a flag for compatibilidy with Debian 9.++### Fixed+- Correct code that was not compiling with some flags enabled.++## 0.2.1+### Changed+- Do not depend on hardcoded DER signatures in tests.++## 0.2.0+### Added+- Support for ECDH APIs.+- Support for Schnorr APIs.++### Removed+- Enabling key recovery APIs need a flag.++## 0.1.8+### Added+- Add missing `NFData` instances for some types.++## 0.1.7+### Added+- Add `NFData` instances for all types.++## 0.1.6+### Added+- Use `pkgconfig` for C library dependency.++## 0.1.5+### Added+- Flag for ECDH bindings.++## 0.1.4+### Changed+- Constrain imports to avoid clashes with a QuickCheck function.++## 0.1.3+### Added+- Hashable instances for various types.++## 0.1.2+### Changed+- Separate dependencies between library and tests.+- Remove `hspec` default to prevent problems with Nix.++### Removed+- Dependency to `cryptohash` not needed.++## 0.1.1+### Changed+- Update changelog to reflect name and version change.+- Update to LTS Haskell 12.9.++## 0.1.0+### Changed+- Name of package change from `secp256k1` to `secp256k1-haskell` to avoid Nix conflicts.
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright 2020 Haskoin Developers+Copyright 2022 Keagan McClelland++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of+the Software, and to permit persons to whom the Software is furnished to do so,+subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,6 @@+[](https://travis-ci.org/haskoin/secp256k1-haskell)++# Haskell bindings for secp256k1++This project contains Haskell bindings for the+[secp256k1](https://github.com/bitcoin-core/secp256k1) library.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ libsecp256k1.cabal view
@@ -0,0 +1,79 @@+cabal-version: 2.0++-- This file has been generated from package.yaml by hpack version 0.34.4.+--+-- see: https://github.com/sol/hpack++name: libsecp256k1+version: 0.0.0+synopsis: Bindings for secp256k1+description: Sign and verify signatures using the secp256k1 library.+category: Crypto+homepage: http://github.com/ProofOfKeags/secp256k1-haskell#readme+bug-reports: https://github.com/ProofOfKeags/libsecp256k1-haskell.git/issues+author: Keagan McClelland+maintainer: keagan.mcclelland@gmail.com+copyright: (c) 2017 Jean-Pierre Rupp; (c) 2020 Haskoin Developers; (c) 2022 Keagan McClelland+license: MIT+license-file: LICENSE+build-type: Simple+extra-source-files:+ CHANGELOG.md+ README.md++source-repository head+ type: git+ location: https://github.com/ProofOfKeags/libsecp256k1-haskell.git++library+ exposed-modules:+ Crypto.Secp256k1+ Crypto.Secp256k1.Prim+ Paths_libsecp256k1+ autogen-modules:+ Paths_libsecp256k1+ hs-source-dirs:+ src+ pkgconfig-depends:+ libsecp256k1+ build-depends:+ QuickCheck >=2.9.2 && <2.15+ , base >=4.9 && <5+ , base16 >=0.3.0.1+ , bytestring >=0.10.8 && <0.12+ , cereal >=0.5.4 && <0.6+ , deepseq >=1.4.2 && <1.5+ , entropy >=0.3.8 && <0.5+ , hashable >=1.2.6 && <1.5+ , string-conversions ==0.4.*+ , unliftio-core >=0.1.0 && <0.3+ default-language: Haskell2010++test-suite spec+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Crypto.Secp256k1.PrimSpec+ Crypto.Secp256k1Spec+ Paths_libsecp256k1+ hs-source-dirs:+ test+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ HUnit+ , QuickCheck >=2.9.2 && <2.15+ , base >=4.9 && <5+ , base16 >=0.3.0.1+ , bytestring >=0.10.8 && <0.12+ , cereal >=0.5.4 && <0.6+ , deepseq >=1.4.2 && <1.5+ , entropy >=0.3.8 && <0.5+ , hashable >=1.2.6 && <1.5+ , hspec+ , monad-par+ , mtl+ , secp256k1-haskell+ , string-conversions ==0.4.*+ , unliftio-core >=0.1.0 && <0.3+ default-language: Haskell2010+ build-tool-depends: hspec-discover:hspec-discover
+ src/Crypto/Secp256k1.hs view
@@ -0,0 +1,510 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}++-- |+-- Module : Crypto.Secp256k1+-- License : UNLICENSE+-- Maintainer : Keagan McClelland <keagan.mcclelland@gmail.com>+-- Stability : experimental+-- Portability : POSIX+--+-- Crytpographic functions from Bitcoin’s secp256k1 library.+module Crypto.Secp256k1 (+ -- * Messages+ Msg,+ msg,+ getMsg,++ -- * Secret Keys+ SecKey,+ secKey,+ getSecKey,+ derivePubKey,++ -- * Public Keys+ PubKey,+ importPubKey,+ exportPubKey,++ -- * Signatures+ Sig,+ signMsg,+ verifySig,+ normalizeSig,++ -- ** DER+ importSig,+ exportSig,++ -- ** Compact+ CompactSig,+ getCompactSig,+ compactSig,+ exportCompactSig,+ importCompactSig,++ -- * Addition & Multiplication+ Tweak,+ tweak,+ getTweak,+ tweakAddSecKey,+ tweakMulSecKey,+ tweakAddPubKey,+ tweakMulPubKey,+ combinePubKeys,+) where++import Control.DeepSeq (NFData)+import Control.Monad (replicateM, unless, (<=<))+import qualified Crypto.Secp256k1.Prim as Prim+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Base16 as B16+import Data.Hashable (Hashable (..))+import Data.Maybe (fromJust, fromMaybe, isJust)+import Data.Serialize (+ Serialize (..),+ getByteString,+ putByteString,+ )+import Data.String (IsString (..))+import Data.String.Conversions (ConvertibleStrings, cs)+import Foreign (+ alloca,+ allocaArray,+ allocaBytes,+ free,+ mallocBytes,+ nullFunPtr,+ nullPtr,+ peek,+ poke,+ pokeArray,+ )+import GHC.Generics (Generic)+import System.IO.Unsafe (unsafePerformIO)+import Test.QuickCheck (+ Arbitrary (..),+ arbitraryBoundedRandom,+ suchThat,+ )+import Text.Read (+ Lexeme (String),+ lexP,+ parens,+ pfail,+ readPrec,+ )+++newtype PubKey = PubKey {getPubKey :: ByteString}+ deriving (Eq, Generic, NFData)+newtype Msg = Msg {getMsg :: ByteString}+ deriving (Eq, Generic, NFData)+newtype Sig = Sig {getSig :: ByteString}+ deriving (Eq, Generic, NFData)+newtype SecKey = SecKey {getSecKey :: ByteString}+ deriving (Eq, Generic, NFData)+newtype Tweak = Tweak {getTweak :: ByteString}+ deriving (Eq, Generic, NFData)+newtype CompactSig = CompactSig {getCompactSig :: ByteString}+ deriving (Eq, Generic, NFData)+++instance Serialize PubKey where+ put (PubKey bs) = putByteString bs+ get = PubKey <$> getByteString 64+++instance Serialize Msg where+ put (Msg m) = putByteString m+ get = Msg <$> getByteString 32+++instance Serialize Sig where+ put (Sig bs) = putByteString bs+ get = Sig <$> getByteString 64+++instance Serialize SecKey where+ put (SecKey bs) = putByteString bs+ get = SecKey <$> getByteString 32+++instance Serialize Tweak where+ put (Tweak bs) = putByteString bs+ get = Tweak <$> getByteString 32+++instance Serialize CompactSig where+ put (CompactSig bs) = putByteString bs+ get = CompactSig <$> getByteString 64+++decodeHex :: ConvertibleStrings a ByteString => a -> Maybe ByteString+decodeHex str = case B16.decodeBase16 $ cs str of+ Right bs -> Just bs+ Left _ -> Nothing+++instance Read PubKey where+ readPrec = do+ String str <- lexP+ maybe pfail return $ importPubKey =<< decodeHex str+++instance Hashable PubKey where+ i `hashWithSalt` k = i `hashWithSalt` exportPubKey True k+++instance IsString PubKey where+ fromString = fromMaybe e . (importPubKey <=< decodeHex)+ where+ e = error "Could not decode public key from hex string"+++instance Show PubKey where+ showsPrec _ = shows . B16.encodeBase16 . exportPubKey True+++instance Read Msg where+ readPrec = parens $ do+ String str <- lexP+ maybe pfail return $ msg =<< decodeHex str+++instance Hashable Msg where+ i `hashWithSalt` m = i `hashWithSalt` getMsg m+++instance IsString Msg where+ fromString = fromMaybe e . (msg <=< decodeHex)+ where+ e = error "Could not decode message from hex string"+++instance Show Msg where+ showsPrec _ = shows . B16.encodeBase16 . getMsg+++instance Read Sig where+ readPrec = parens $ do+ String str <- lexP+ maybe pfail return $ importSig =<< decodeHex str+++instance IsString Sig where+ fromString = fromMaybe e . (importSig <=< decodeHex)+ where+ e = error "Could not decode signature from hex string"+++instance Hashable Sig where+ i `hashWithSalt` s = i `hashWithSalt` exportSig s+++instance Show Sig where+ showsPrec _ = shows . B16.encodeBase16 . exportSig+++instance Read SecKey where+ readPrec = parens $ do+ String str <- lexP+ maybe pfail return $ secKey =<< decodeHex str+++instance Hashable SecKey where+ i `hashWithSalt` k = i `hashWithSalt` getSecKey k+++instance IsString SecKey where+ fromString = fromMaybe e . (secKey <=< decodeHex)+ where+ e = error "Colud not decode secret key from hex string"+++instance Show SecKey where+ showsPrec _ = shows . B16.encodeBase16 . getSecKey+++instance Hashable Tweak where+ i `hashWithSalt` t = i `hashWithSalt` getTweak t+++instance Read Tweak where+ readPrec = parens $ do+ String str <- lexP+ maybe pfail return $ tweak =<< decodeHex str+++instance IsString Tweak where+ fromString = fromMaybe e . (tweak <=< decodeHex)+ where+ e = error "Could not decode tweak from hex string"+++instance Show Tweak where+ showsPrec _ = shows . B16.encodeBase16 . getTweak+++-- | Import 32-byte 'ByteString' as 'Msg'.+msg :: ByteString -> Maybe Msg+msg bs+ | BS.length bs == 32 = Just (Msg bs)+ | otherwise = Nothing+++-- | Import 32-byte 'ByteString' as 'SecKey'.+secKey :: ByteString -> Maybe SecKey+secKey bs+ | BS.length bs == 32 = Just (SecKey bs)+ | otherwise = Nothing+++compactSig :: ByteString -> Maybe CompactSig+compactSig bs+ | BS.length bs == 64 = Just (CompactSig bs)+ | otherwise = Nothing+++-- | Convert signature to a normalized lower-S form. 'Nothing' indicates that it+-- was already normal.+normalizeSig :: Sig -> Maybe Sig+normalizeSig (Sig sig) = unsafePerformIO $+ Prim.unsafeUseByteString sig $ \(sig_in, _) -> do+ sig_out <- mallocBytes 64+ ret <- Prim.ecdsaSignatureNormalize Prim.ctx sig_out sig_in+ if Prim.isSuccess ret+ then do+ bs <- Prim.unsafePackByteString (sig_out, 64)+ return (Just (Sig bs))+ else do+ free sig_out+ return Nothing+++-- | 32-Byte 'ByteString' as 'Tweak'.+tweak :: ByteString -> Maybe Tweak+tweak bs+ | BS.length bs == 32 = Just (Tweak bs)+ | otherwise = Nothing+++-- | Import DER-encoded public key.+importPubKey :: ByteString -> Maybe PubKey+importPubKey bs+ | BS.null bs = Nothing+ | otherwise = unsafePerformIO $+ Prim.unsafeUseByteString bs $ \(input, len) -> do+ pub_key <- mallocBytes 64+ ret <- Prim.ecPubkeyParse Prim.ctx pub_key input len+ if Prim.isSuccess ret+ then do+ out <- Prim.unsafePackByteString (pub_key, 64)+ return (Just (PubKey out))+ else do+ free pub_key+ return Nothing+++-- | Encode public key as DER. First argument 'True' for compressed output.+exportPubKey :: Bool -> PubKey -> ByteString+exportPubKey compress (PubKey in_bs) = unsafePerformIO $+ Prim.unsafeUseByteString in_bs $ \(in_ptr, _) ->+ alloca $ \len_ptr ->+ allocaBytes len $ \out_ptr -> do+ poke len_ptr $ fromIntegral len+ ret <- Prim.ecPubKeySerialize Prim.ctx out_ptr len_ptr in_ptr flags+ unless (Prim.isSuccess ret) $ error "could not serialize public key"+ final_len <- peek len_ptr+ Prim.packByteString (out_ptr, final_len)+ where+ len = if compress then 33 else 65+ flags = if compress then Prim.compressed else Prim.uncompressed+++exportCompactSig :: Sig -> CompactSig+exportCompactSig (Sig sig_bs) = unsafePerformIO $+ Prim.unsafeUseByteString sig_bs $ \(sig_ptr, _) -> do+ out_ptr <- mallocBytes 64+ ret <- Prim.ecdsaSignatureSerializeCompact Prim.ctx out_ptr sig_ptr+ unless (Prim.isSuccess ret) $ do+ free out_ptr+ error "Could not obtain compact signature"+ out_bs <- Prim.unsafePackByteString (out_ptr, 64)+ return $ CompactSig out_bs+++importCompactSig :: CompactSig -> Maybe Sig+importCompactSig (CompactSig compact_sig) = unsafePerformIO $+ Prim.unsafeUseByteString compact_sig $ \(compact_ptr, _) -> do+ out_sig <- mallocBytes 64+ ret <- Prim.ecdsaSignatureParseCompact Prim.ctx out_sig compact_ptr+ if Prim.isSuccess ret+ then do+ out_bs <- Prim.unsafePackByteString (out_sig, 64)+ return (Just (Sig out_bs))+ else do+ free out_sig+ return Nothing+++-- | Import DER-encoded signature.+importSig :: ByteString -> Maybe Sig+importSig bs+ | BS.null bs = Nothing+ | otherwise = unsafePerformIO $+ Prim.unsafeUseByteString bs $ \(in_ptr, in_len) -> do+ out_sig <- mallocBytes 64+ ret <- Prim.ecdsaSignatureParseDer Prim.ctx out_sig in_ptr in_len+ if Prim.isSuccess ret+ then do+ out_bs <- Prim.unsafePackByteString (out_sig, 64)+ return (Just (Sig out_bs))+ else do+ free out_sig+ return Nothing+++-- | Encode signature as strict DER.+exportSig :: Sig -> ByteString+exportSig (Sig in_sig) = unsafePerformIO $+ Prim.unsafeUseByteString in_sig $ \(in_ptr, _) ->+ alloca $ \out_len ->+ allocaBytes 72 $ \out_ptr -> do+ poke out_len 72+ ret <- Prim.ecdsaSignatureSerializeDer Prim.ctx out_ptr out_len in_ptr+ unless (Prim.isSuccess ret) $ error "could not serialize signature"+ final_len <- peek out_len+ Prim.packByteString (out_ptr, final_len)+++-- | Verify message signature. 'True' means that the signature is correct.+verifySig :: PubKey -> Sig -> Msg -> Bool+verifySig (PubKey pub_key) (Sig sig) (Msg m) = unsafePerformIO $+ Prim.unsafeUseByteString pub_key $ \(pub_key_ptr, _) ->+ Prim.unsafeUseByteString sig $ \(sig_ptr, _) ->+ Prim.unsafeUseByteString m $ \(msg_ptr, _) ->+ Prim.isSuccess <$> Prim.ecdsaVerify Prim.ctx sig_ptr msg_ptr pub_key_ptr+++signMsg :: SecKey -> Msg -> Sig+signMsg (SecKey sec_key) (Msg m) = unsafePerformIO $+ Prim.unsafeUseByteString sec_key $ \(sec_key_ptr, _) ->+ Prim.unsafeUseByteString m $ \(msg_ptr, _) -> do+ sig_ptr <- mallocBytes 64+ ret <- Prim.ecdsaSign Prim.ctx sig_ptr msg_ptr sec_key_ptr nullFunPtr nullPtr+ unless (Prim.isSuccess ret) $ do+ free sig_ptr+ error "could not sign message"+ Sig <$> Prim.unsafePackByteString (sig_ptr, 64)+++derivePubKey :: SecKey -> PubKey+derivePubKey (SecKey sec_key) = unsafePerformIO $+ Prim.unsafeUseByteString sec_key $ \(sec_key_ptr, _) -> do+ pub_key_ptr <- mallocBytes 64+ ret <- Prim.ecPubKeyCreate Prim.ctx pub_key_ptr sec_key_ptr+ unless (Prim.isSuccess ret) $ do+ free pub_key_ptr+ error "could not compute public key"+ PubKey <$> Prim.unsafePackByteString (pub_key_ptr, 64)+++-- | Add tweak to secret key.+tweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey+tweakAddSecKey (SecKey sec_key) (Tweak t) = unsafePerformIO $+ Prim.unsafeUseByteString new_bs $ \(sec_key_ptr, _) ->+ Prim.unsafeUseByteString t $ \(tweak_ptr, _) -> do+ ret <- Prim.ecSeckeyTweakAdd Prim.ctx sec_key_ptr tweak_ptr+ if Prim.isSuccess ret+ then return (Just (SecKey new_bs))+ else return Nothing+ where+ new_bs = BS.copy sec_key+++-- | Multiply secret key by tweak.+tweakMulSecKey :: SecKey -> Tweak -> Maybe SecKey+tweakMulSecKey (SecKey sec_key) (Tweak t) = unsafePerformIO $+ Prim.unsafeUseByteString new_bs $ \(sec_key_ptr, _) ->+ Prim.unsafeUseByteString t $ \(tweak_ptr, _) -> do+ ret <- Prim.ecSeckeyTweakMul Prim.ctx sec_key_ptr tweak_ptr+ if Prim.isSuccess ret+ then return (Just (SecKey new_bs))+ else return Nothing+ where+ new_bs = BS.copy sec_key+++-- | Add tweak to public key. Tweak is multiplied first by G to obtain a point.+tweakAddPubKey :: PubKey -> Tweak -> Maybe PubKey+tweakAddPubKey (PubKey pub_key) (Tweak t) = unsafePerformIO $+ Prim.unsafeUseByteString new_bs $ \(pub_key_ptr, _) ->+ Prim.unsafeUseByteString t $ \(tweak_ptr, _) -> do+ ret <- Prim.ecPubKeyTweakAdd Prim.ctx pub_key_ptr tweak_ptr+ if Prim.isSuccess ret+ then return (Just (PubKey new_bs))+ else return Nothing+ where+ new_bs = BS.copy pub_key+++-- | Multiply public key by tweak. Tweak is multiplied first by G to obtain a+-- point.+tweakMulPubKey :: PubKey -> Tweak -> Maybe PubKey+tweakMulPubKey (PubKey pub_key) (Tweak t) = unsafePerformIO $+ Prim.unsafeUseByteString new_bs $ \(pub_key_ptr, _) ->+ Prim.unsafeUseByteString t $ \(tweak_ptr, _) -> do+ ret <- Prim.ecPubKeyTweakMul Prim.ctx pub_key_ptr tweak_ptr+ if Prim.isSuccess ret+ then return (Just (PubKey new_bs))+ else return Nothing+ where+ new_bs = BS.copy pub_key+++-- | Add multiple public keys together.+combinePubKeys :: [PubKey] -> Maybe PubKey+combinePubKeys [] = Nothing+combinePubKeys pubs = unsafePerformIO $+ pointers [] pubs $ \ps ->+ allocaArray (length ps) $ \a -> do+ out <- mallocBytes 64+ pokeArray a ps+ ret <- Prim.ecPubKeyCombine Prim.ctx out a (fromIntegral $ length ps)+ if Prim.isSuccess ret+ then do+ bs <- Prim.unsafePackByteString (out, 64)+ return (Just (PubKey bs))+ else do+ free out+ return Nothing+ where+ pointers ps [] f = f ps+ pointers ps (PubKey pub_key : pub_keys) f =+ Prim.unsafeUseByteString pub_key $ \(p, _) ->+ pointers (p : ps) pub_keys f+++instance Arbitrary Msg where+ arbitrary = gen_msg+ where+ valid_bs = bs_gen `suchThat` isJust+ bs_gen = msg . BS.pack <$> replicateM 32 arbitraryBoundedRandom+ gen_msg = fromJust <$> valid_bs+++instance Arbitrary SecKey where+ arbitrary = gen_key+ where+ valid_bs = bs_gen `suchThat` isJust+ bs_gen = secKey . BS.pack <$> replicateM 32 arbitraryBoundedRandom+ gen_key = fromJust <$> valid_bs+++instance Arbitrary PubKey where+ arbitrary = derivePubKey <$> arbitrary
+ src/Crypto/Secp256k1/Prim.hs view
@@ -0,0 +1,554 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE RankNTypes #-}++-- |+-- Module : Crypto.Secp256k1.Prim+-- License : UNLICENSE+-- Maintainer : Keagan McClelland <keagan.mcclelland@gmail.com>+-- Stability : experimental+-- Portability : POSIX+--+-- The API for this module may change at any time. This is an internal module only+-- exposed for hacking and experimentation.+module Crypto.Secp256k1.Prim where++import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Unsafe as BU+import Foreign (FunPtr, Ptr, castPtr)+import Foreign.C (+ CInt (..),+ CSize (..),+ CString,+ CUChar,+ CUInt (..),+ )+import GHC.TypeNats (Nat)+import System.IO.Unsafe (unsafePerformIO)+++data LCtx+data Pubkey64+data XonlyPubkey64+data Keypair96+data Msg32+data RecSig65+data Sig64+data Compact64+data Seed32+data Seckey32+data Tweak32+data SchnorrExtra+data Scratch+data Bytes (n :: Nat)+++type CtxFlags = CUInt+type SerFlags = CUInt+type Ret = CInt+++type NonceFun a =+ Ptr CUChar ->+ Ptr CUChar ->+ Ptr CUChar ->+ Ptr CUChar ->+ Ptr a ->+ CInt ->+ IO CInt+++type NonceFunHardened a =+ Ptr CUChar ->+ Ptr CUChar ->+ CSize ->+ Ptr CUChar ->+ Ptr CUChar ->+ Ptr CUChar ->+ CSize ->+ Ptr a ->+ IO CInt+++type EcdhHashFun a =+ Ptr CUChar ->+ Ptr CUChar ->+ Ptr CUChar ->+ Ptr a ->+ IO CInt+++type Ctx = Ptr LCtx+++verify :: CtxFlags+verify = 0x0101+++sign :: CtxFlags+sign = 0x0201+++signVerify :: CtxFlags+signVerify = 0x0301+++compressed :: SerFlags+compressed = 0x0102+++uncompressed :: SerFlags+uncompressed = 0x0002+++isSuccess :: Ret -> Bool+isSuccess 0 = False+isSuccess 1 = True+isSuccess n = error $ "isSuccess expected 0 or 1 but got " ++ show n+++unsafeUseByteString :: ByteString -> ((Ptr a, CSize) -> IO b) -> IO b+unsafeUseByteString bs f =+ BU.unsafeUseAsCStringLen bs $ \(b, l) ->+ f (castPtr b, fromIntegral l)+++useByteString :: ByteString -> ((Ptr a, CSize) -> IO b) -> IO b+useByteString bs f =+ BS.useAsCStringLen bs $ \(b, l) ->+ f (castPtr b, fromIntegral l)+++unsafePackByteString :: (Ptr a, CSize) -> IO ByteString+unsafePackByteString (b, l) =+ BU.unsafePackMallocCStringLen (castPtr b, fromIntegral l)+++packByteString :: (Ptr a, CSize) -> IO ByteString+packByteString (b, l) =+ BS.packCStringLen (castPtr b, fromIntegral l)+++ctx :: Ctx+ctx = unsafePerformIO $ contextCreate signVerify+{-# NOINLINE ctx #-}+++-- secp256k1_context_clone+foreign import ccall safe "secp256k1.h secp256k1_context_clone"+ contextClone :: Ctx -> IO Ctx+++-- secp256k1_context_create+foreign import ccall safe "secp256k1.h secp256k1_context_create"+ contextCreate :: CtxFlags -> IO Ctx+++-- secp256k1_context_destroy+foreign import ccall safe "secp256k1.h &secp256k1_context_destroy"+ contextDestroy :: FunPtr (Ctx -> IO ())+++-- secp256k1_context_no_precomp+foreign import ccall safe "secp256k1.h secp256k1_context_no_precomp"+ contextNoPrecomp :: Ctx+++-- secp256k1_context_preallocated_clone+foreign import ccall safe "secp256k1.h secp256k1_context_preallocated_clone"+ contextPreallocatedClone :: Ctx -> Ptr (Bytes n) -> IO Ctx+++-- secp256k1_context_preallocated_clone_size+foreign import ccall safe "secp256k1.h secp256k1_context_preallocated_clone_size"+ contextPreallocatedCloneSize :: Ctx -> IO CSize+++-- secp256k1_context_preallocated_create+foreign import ccall safe "secp256k1.h secp256k1_context_preallocated_create"+ contextPreallocatedCreate :: Ptr (Bytes n) -> CUInt -> IO Ctx+++-- secp256k1_context_preallocated_destroy+foreign import ccall safe "secp256k1.h secp256k1_context_preallocated_destroy"+ contextPreallocatedDestroy :: Ctx -> IO ()+++-- secp256k1_context_preallocated_size+foreign import ccall safe "secp256k1.h secp256k1_context_preallocated_size"+ contextPreallocatedSize :: CUInt -> IO CSize+++-- secp256k1_context_randomize+foreign import ccall safe "secp256k1.h secp256k1_context_randomize"+ contextRandomize :: Ctx -> Ptr Seed32 -> IO Ret+++-- secp256k1_context_set_error_callback+foreign import ccall safe "secp256k1.h secp256k1_context_set_error_callback"+ setErrorCallback ::+ Ctx ->+ -- | message, data+ FunPtr (CString -> Ptr a -> IO ()) ->+ -- | data+ Ptr a ->+ IO ()+++-- secp256k1_context_set_illegal_callback+foreign import ccall safe "secp256k1.h secp256k1_context_set_illegal_callback"+ setIllegalCallback ::+ Ctx ->+ -- | message, data+ FunPtr (CString -> Ptr a -> IO ()) ->+ -- | data+ Ptr a ->+ IO ()+++-- secp256k1_ecdh+foreign import ccall safe "secp256k1.h secp256k1_ecdh"+ ecdh :: Ctx -> Ptr (Bytes n) -> Ptr Pubkey64 -> Ptr Seckey32 -> FunPtr (EcdhHashFun a) -> Ptr a -> IO Ret+++-- secp256k1_ecdh_hash_function_default+foreign import ccall safe "secp256k1.h &secp256k1_ecdh_hash_function_default"+ ecdhHashFunctionDefault :: FunPtr (EcdhHashFun a)+++-- secp256k1_ecdh_hash_function_sha256+foreign import ccall safe "secp256k1.h &secp256k1_ecdh_hash_sha256"+ ecdhHashSha256 :: FunPtr (EcdhHashFun a)+++-- secp256k1_ecdsa_recover+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_recover"+ ecdsaRecover :: Ctx -> Ptr Pubkey64 -> Ptr RecSig65 -> Ptr Msg32 -> IO Ret+++-- secp256k1_ecdsa_recoverable_signature_convert+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_recoverable_signature_convert"+ ecdsaRecoverableSignatureConvert :: Ctx -> Ptr Sig64 -> Ptr RecSig65 -> IO Ret+++-- secp256k1_ecdsa_recoverable_signature_parse_compact+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_recoverable_signature_parse_compact"+ ecdsaRecoverableSignatureParseCompact :: Ctx -> Ptr RecSig65 -> Ptr (Bytes 64) -> CInt -> IO Ret+++-- secp256k1_ecdsa_recoverable_signature_serialize_compact+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_recoverable_signature_serialize_compact"+ ecdsaRecoverableSignatureSerializeCompact :: Ctx -> Ptr (Bytes 64) -> Ptr CInt -> Ptr RecSig65 -> IO Ret+++-- secp256k1_ecdsa_sign+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_sign"+ ecdsaSign ::+ Ctx ->+ Ptr Sig64 ->+ Ptr Msg32 ->+ Ptr Seckey32 ->+ FunPtr (NonceFun a) ->+ -- | nonce data+ Ptr a ->+ IO Ret+++-- secp256k1_ecdsa_signature_normalize+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_normalize"+ ecdsaSignatureNormalize ::+ Ctx ->+ -- | output+ Ptr Sig64 ->+ -- | input+ Ptr Sig64 ->+ IO Ret+++-- secp256k1_ecdsa_signature_parse_compact+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_parse_compact"+ ecdsaSignatureParseCompact ::+ Ctx ->+ Ptr Sig64 ->+ Ptr Compact64 ->+ IO Ret+++-- secp256k1_ecdsa_signature_parse_der+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_parse_der"+ ecdsaSignatureParseDer ::+ Ctx ->+ Ptr Sig64 ->+ -- | encoded DER signature+ Ptr (Bytes n) ->+ -- | size of encoded signature+ CSize ->+ IO Ret+++-- secp256k1_ecdsa_signature_serialize_compact+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_serialize_compact"+ ecdsaSignatureSerializeCompact ::+ Ctx ->+ Ptr Compact64 ->+ Ptr Sig64 ->+ IO Ret+++-- secp256k1_ecdsa_signature_serialize_der+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_serialize_der"+ ecdsaSignatureSerializeDer ::+ Ctx ->+ -- | array for encoded signature, must be large enough+ Ptr (Bytes n) ->+ -- | size of encoded signature, will be updated+ Ptr CSize ->+ Ptr Sig64 ->+ IO Ret+++-- secp256k1_ecdsa_sign_recoverable+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_sign_recoverable"+ ecdsaSignRecoverable ::+ Ctx -> Ptr RecSig65 -> Ptr Msg32 -> Ptr Seckey32 -> FunPtr (NonceFun a) -> Ptr a -> IO Ret+++-- secp256k1_ecdsa_verify+foreign import ccall safe "secp256k1.h secp256k1_ecdsa_verify"+ ecdsaVerify ::+ Ctx ->+ Ptr Sig64 ->+ Ptr Msg32 ->+ Ptr Pubkey64 ->+ IO Ret+++-- secp256k1_ec_privkey_negate+{-# DEPRECATED ecPrivkeyNegate "use ecSeckeyNegate instead" #-}+foreign import ccall safe "secp256k1.h secp256k1_ec_privkey_negate"+ ecPrivkeyNegate ::+ Ctx ->+ Ptr Tweak32 ->+ IO Ret+++-- secp256k1_ec_privkey_tweak_add+{-# DEPRECATED ecPrivkeyTweakAdd "use ecSeckeyTweakAdd instead" #-}+foreign import ccall safe "secp256k1.h secp256k1_ec_privkey_tweak_add"+ ecPrivkeyTweakAdd ::+ Ctx ->+ Ptr Seckey32 ->+ Ptr Tweak32 ->+ IO Ret+++-- secp256k1_ec_privkey_tweak_mul+{-# DEPRECATED ecPrivkeyTweakMul "use ecSeckeyTweakMul instead" #-}+foreign import ccall safe "secp256k1.h secp256k1_ec_privkey_tweak_mul"+ ecPrivkeyTweakMul ::+ Ctx ->+ Ptr Seckey32 ->+ Ptr Tweak32 ->+ IO Ret+++-- secp256k1_ec_pubkey_cmp+foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_cmp"+ ecPubkeyCmp ::+ Ctx ->+ Ptr Pubkey64 ->+ Ptr Pubkey64+++-- secp256k1_ec_pubkey_combine+foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_combine"+ ecPubKeyCombine ::+ Ctx ->+ -- | pointer to public key storage+ Ptr Pubkey64 ->+ -- | pointer to array of public keys+ Ptr (Ptr Pubkey64) ->+ -- | number of public keys+ CInt ->+ IO Ret+++-- secp256k1_ec_pubkey_create+foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_create"+ ecPubKeyCreate ::+ Ctx ->+ Ptr Pubkey64 ->+ Ptr Seckey32 ->+ IO Ret+++-- secp256k1_ec_pubkey_negate+foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_negate"+ ecPubkeyNegate :: Ctx -> Ptr Pubkey64 -> IO Ret+++-- secp256k1_ec_pubkey_parse+foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_parse"+ ecPubkeyParse ::+ Ctx ->+ Ptr Pubkey64 ->+ -- | encoded public key array+ Ptr (Bytes n) ->+ -- | size of encoded public key array+ CSize ->+ IO Ret+++-- secp256k1_ec_pubkey_serialize+foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_serialize"+ ecPubKeySerialize ::+ Ctx ->+ -- | array for encoded public key, must be large enough+ Ptr (Bytes n) ->+ -- | size of encoded public key, will be updated+ Ptr CSize ->+ Ptr Pubkey64 ->+ SerFlags ->+ IO Ret+++-- secp256k1_ec_pubkey_tweak_add+foreign import ccall unsafe "secp256k1.h secp256k1_ec_pubkey_tweak_add"+ ecPubKeyTweakAdd ::+ Ctx ->+ Ptr Pubkey64 ->+ Ptr Tweak32 ->+ IO Ret+++-- secp256k1_ec_pubkey_tweak_mul+foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_tweak_mul"+ ecPubKeyTweakMul ::+ Ctx ->+ Ptr Pubkey64 ->+ Ptr Tweak32 ->+ IO Ret+++-- secp256k1_ec_seckey_negate+foreign import ccall safe "secp256k1.h secp256k1_ec_seckey_negate"+ ecSeckeyNegate :: Ctx -> Ptr Seckey32 -> IO Ret+++-- secp256k1_ec_seckey_tweak_add+foreign import ccall safe "secp256k1.h secp256k1_ec_seckey_tweak_add"+ ecSeckeyTweakAdd :: Ctx -> Ptr Seckey32 -> Ptr Tweak32 -> IO Ret+++-- secp256k1_ec_seckey_tweak_mul+foreign import ccall safe "secp256k1.h secp256k1_ec_seckey_tweak_mul"+ ecSeckeyTweakMul :: Ctx -> Ptr Seckey32 -> Ptr Tweak32 -> IO Ret+++-- secp256k1_ec_seckey_verify+foreign import ccall safe "secp256k1.h secp256k1_ec_seckey_verify"+ ecSecKeyVerify ::+ Ctx ->+ Ptr Seckey32 ->+ IO Ret+++-- secp256k1_keypair_create+foreign import ccall safe "secp256k1.h secp256k1_keypair_create"+ keypairCreate :: Ctx -> Ptr Keypair96 -> Ptr Seckey32 -> IO Ret+++-- secp256k1_keypair_pub+foreign import ccall safe "secp256k1.h secp256k1_keypair_pub"+ keypairPub :: Ctx -> Ptr Pubkey64 -> Ptr Keypair96 -> IO Ret+++-- secp256k1_keypair_sec+foreign import ccall safe "secp256k1.h secp256k1_keypair_sec"+ keypairSec :: Ctx -> Ptr Seckey32 -> Ptr Keypair96 -> IO Ret+++-- secp256k1_keypair_xonly_pub+foreign import ccall safe "secp256k1.h secp256k1_keypair_xonly_pub"+ keypairXonlyPub :: Ctx -> Ptr XonlyPubkey64 -> Ptr CInt -> Ptr Keypair96 -> IO Ret+++-- secp256k1_keypair_xonly_tweak_add+foreign import ccall safe "secp256k1.h secp256k1_keypair_xonly_tweak_add"+ keypairXonlyTweakAdd :: Ctx -> Ptr Keypair96 -> Ptr Tweak32 -> IO Ret+++-- secp256k1_nonce_function_bip340+foreign import ccall safe "secp256k1.h &secp256k1_nonce_function_bip340"+ nonceFunctionBip340 :: FunPtr (NonceFunHardened a)+++-- secp256k1_nonce_function_default+foreign import ccall safe "secp256k1.h &secp256k1_nonce_function_default"+ nonceFunctionDefault :: FunPtr (NonceFun a)+++-- secp256k1_nonce_function_rfc6979+foreign import ccall safe "secp256k1.h &secp256k1_nonce_function_rfc6979"+ nonceFunctionRfc6979 :: FunPtr (NonceFun a)+++-- secp256k1_schnorrsig_sign+foreign import ccall safe "secp256k1.h secp256k1_schnorrsig_sign"+ schnorrsigSign :: Ctx -> Ptr Sig64 -> Ptr Msg32 -> Ptr Keypair96 -> Ptr (Bytes 32) -> IO Ret+++-- secp256k1_schnorrsig_sign_custom+foreign import ccall safe "secp256k1.h secp256k1_schnorrsig_sign_custom"+ schnorrsigSignCustom :: Ctx -> Ptr Sig64 -> Ptr (Bytes n) -> CSize -> Ptr Keypair96 -> Ptr SchnorrExtra -> IO Ret+++-- secp256k1_schnorrsig_verify+foreign import ccall safe "secp256k1.h secp256k1_schnorrsig_verify"+ schnorrSigSignVerify :: Ctx -> Ptr Sig64 -> Ptr (Bytes n) -> CSize -> Ptr XonlyPubkey64 -> IO Ret+++-- secp256k1_scratch_space_create+foreign import ccall safe "secp256k1.h secp256k1_scratch_space_create"+ scratchSpaceCreate :: Ctx -> CSize -> IO (Ptr Scratch)+++-- secp256k1_scratch_space_destroy+foreign import ccall safe "secp256k1.h secp256k1_scratch_space_destroy"+ scratchSpaceDestroy :: Ctx -> Ptr Scratch -> IO ()+++-- secp256k1_tagged_sha256+foreign import ccall safe "secp256k1.h secp256k1_tagged_sha256"+ taggedSha256 :: Ctx -> Ptr (Bytes 32) -> Ptr (Bytes n) -> CSize -> Ptr (Bytes n) -> CSize -> IO Ret+++-- secp256k1_xonly_pubkey_cmp+foreign import ccall safe "secp256k1.h secp256k1_xonly_pubkey_cmp"+ xonlyPubkeyCmp :: Ctx -> Ptr XonlyPubkey64 -> Ptr XonlyPubkey64 -> IO Ret+++-- secp256k1_xonly_pubkey_from_pubkey+foreign import ccall safe "secp256k1.h secp256k1_xonly_pubkey_from_pubkey"+ xonlyPubkeyFromPubkey :: Ctx -> Ptr XonlyPubkey64 -> Ptr CInt -> Ptr Pubkey64 -> IO Ret+++-- secp256k1_xonly_pubkey_parse+foreign import ccall safe "secp256k1.h secp256k1_xonly_pubkey_parse"+ xonlyPubkeyParse :: Ctx -> Ptr XonlyPubkey64 -> Ptr (Bytes 32) -> IO Ret+++-- secp256k1_xonly_pubkey_serialize+foreign import ccall safe "secp256k1.h secp256k1_xonly_pubkey_serialize"+ xonlyPubkeySerialize :: Ctx -> Ptr (Bytes 32) -> Ptr XonlyPubkey64 -> IO Ret+++-- secp256k1_xonly_pubkey_tweak_add+foreign import ccall safe "secp256k1.h secp256k1_xonly_pubkey_tweak_add"+ xonlyPubkeyTweakAdd :: Ctx -> Ptr Pubkey64 -> Ptr XonlyPubkey64 -> Ptr Tweak32 -> IO Ret+++-- secp256k1_xonly_pubkey_tweak_add_check+foreign import ccall safe "secp256k1.h secp256k1_xonly_pubkey_tweak_add_check"+ xonlyPubkeyTweakAddCheck :: Ctx -> Ptr (Bytes 32) -> CInt -> Ptr XonlyPubkey64 -> Ptr Tweak32 -> IO Ret
+ test/Crypto/Secp256k1/PrimSpec.hs view
@@ -0,0 +1,466 @@+{-# LANGUAGE OverloadedStrings #-}++module Crypto.Secp256k1.PrimSpec (spec) where++import Control.Monad+import Control.Monad.Trans+import Crypto.Secp256k1.Internal+import Data.ByteString (+ ByteString,+ copy,+ packCStringLen,+ useAsCStringLen,+ )+import qualified Data.ByteString.Base16 as B16+import Data.Either (fromRight)+import Foreign+import System.Entropy+import Test.HUnit (Assertion, assertBool, assertEqual)+import Test.Hspec+++spec :: Spec+spec = do+ describe "housekeeping" $ do+ it "creates context" createContextTest+ it "randomizes context" randomizeContextTest+ it "clones context" cloneContextTest+ describe "serialization" $ do+ it "parses public key" ecPubkeyParseTest+ it "serializes public key" ecPubKeySerializeTest+ it "parses DER signature" ecdsaSignatureParseDerTest+ it "serializes DER signature" ecdsaSignatureSerializeDerTest+ describe "signatures" $ do+ it "verifies signature" ecdsaVerifyTest+ it "signs message" ecdsaSignTest+ describe "secret keys" $ do+ it "verifies secret key" ecSecKeyVerifyTest+ it "creates public key" ecPubkeyCreateTest+ it "adds secret key" ecSecKeyTweakAddTest+ it "multiplies secret key" ecSecKeyTweakMulTest+ describe "public keys" $ do+ it "adds public key" ecPubKeyTweakAddTest+ it "multiplies public key" ecPubKeyTweakMulTest+ it "combines public keys" ecPubKeyCombineTest+++withEntropy :: (Ptr Seed32 -> IO a) -> IO a+withEntropy f =+ getEntropy 32 >>= \e ->+ useByteString e $ \(s, _) -> f s+++createContextTest :: Assertion+createContextTest = do+ context_ptr <- liftIO $ contextCreate signVerify+ assertBool "context not null" $ context_ptr /= nullPtr+++randomizeContextTest :: Assertion+randomizeContextTest = do+ ret <-+ liftIO $+ contextCreate sign >>= \x ->+ withEntropy (contextRandomize x)+ assertBool "context randomized" $ isSuccess ret+++cloneContextTest :: Assertion+cloneContextTest = do+ (x1, x2) <- liftIO $ do+ x1 <- contextCreate signVerify+ ret <- withEntropy $ contextRandomize x1+ unless (isSuccess ret) $ error "failed to randomize context"+ x2 <- contextClone x1+ return (x1, x2)+ assertBool "original context not null" $ x1 /= nullPtr+ assertBool "cloned context not null" $ x2 /= nullPtr+ assertBool "context ptrs different" $ x1 /= x2+++ecPubkeyParseTest :: Assertion+ecPubkeyParseTest = do+ ret <- liftIO $+ useAsCStringLen der $ \(i, il) -> do+ x <- contextCreate verify+ allocaBytes 64 $ \pubkey ->+ ecPubKeyParse x pubkey (castPtr i) (fromIntegral il)+ assertBool "parsed public key" (isSuccess ret)+ where+ der =+ fromRight undefined $+ B16.decodeBase16+ "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"+++ecPubKeySerializeTest :: Assertion+ecPubKeySerializeTest = do+ (ret, dec) <- liftIO $+ useByteString der $ \(i, il) ->+ allocaBytes 64 $ \k ->+ alloca $ \ol ->+ allocaBytes 72 $ \o -> do+ poke ol 72+ x <- contextCreate verify+ ret1 <- ecPubKeyParse x k i il+ unless (isSuccess ret1) $ error "failed to parse pubkey"+ ret2 <- ecPubKeySerialize x o ol k compressed+ len <- fromIntegral <$> peek ol+ decoded <- packCStringLen (castPtr o, len)+ return (ret2, decoded)+ assertBool "serialized public key successfully" $ isSuccess ret+ assertEqual "public key matches" der dec+ where+ der =+ fromRight undefined $+ B16.decodeBase16+ "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"+++ecdsaSignatureParseDerTest :: Assertion+ecdsaSignatureParseDerTest = do+ ret <- liftIO $+ useAsCStringLen der $ \(d, dl) -> allocaBytes 64 $ \s -> do+ x <- contextCreate verify+ ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)+ assertBool "parsed signature successfully" $ isSuccess ret+ where+ der =+ fromRight undefined $+ B16.decodeBase16+ "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+ \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+ \45"+++parseDer :: Ctx -> ByteString -> IO ByteString+parseDer x bs =+ useAsCStringLen bs $ \(d, dl) ->+ allocaBytes 64 $ \s -> do+ ret <- ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)+ unless (isSuccess ret) $ error "could not parse DER"+ packByteString (s, 64)+++ecdsaSignatureSerializeDerTest :: Assertion+ecdsaSignatureSerializeDerTest = do+ (ret, enc) <- liftIO $ do+ x <- contextCreate verify+ sig <- parseDer x der+ alloca $ \ol ->+ allocaBytes 72 $ \o ->+ useByteString sig $ \(s, _) -> do+ poke ol 72+ ret <- ecdsaSignatureSerializeDer x o ol s+ len <- fromIntegral <$> peek ol+ enc <- packCStringLen (castPtr o, len)+ return (ret, enc)+ assertBool "serialization successful" $ isSuccess ret+ assertEqual "signatures match" der enc+ where+ der =+ fromRight undefined $+ B16.decodeBase16+ "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+ \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+ \45"+++ecdsaVerifyTest :: Assertion+ecdsaVerifyTest = do+ ret <- liftIO $ do+ x <- contextCreate verify+ sig <- parseDer x der+ pk <- useByteString pub $ \(p, pl) ->+ allocaBytes 64 $ \k -> do+ ret <- ecPubKeyParse x k p (fromIntegral pl)+ unless (isSuccess ret) $ error "could not parse public key"+ packByteString (k, 64)+ useByteString msg $ \(m, _) ->+ useByteString pk $ \(k, _) ->+ useByteString sig $ \(s, _) ->+ ecdsaVerify x s m k+ assertBool "signature valid" $ isSuccess ret+ where+ der =+ fromRight undefined $+ B16.decodeBase16+ "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+ \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+ \45"+ pub =+ fromRight undefined $+ B16.decodeBase16+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+ msg =+ fromRight undefined $+ B16.decodeBase16+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+++signCtx :: IO Ctx+signCtx =+ contextCreate sign >>= \c ->+ withEntropy (contextRandomize c) >>= \r ->+ unless (isSuccess r) (error "failed to randomize context") >> return c+++createPubKey :: Ctx -> Ptr SecKey32 -> Ptr PubKey64 -> IO ()+createPubKey x k p = do+ ret <- ecPubKeyCreate x p k+ unless (isSuccess ret) $ error "failed to create public key"+++ecdsaSignTest :: Assertion+ecdsaSignTest = do+ der <- liftIO $ do+ x <- signCtx+ allocaBytes 64 $ \s ->+ useByteString msg $ \(m, _) ->+ useByteString key $ \(k, _) ->+ alloca $ \ol ->+ allocaBytes 72 $ \o -> do+ poke ol 72+ ret1 <- ecdsaSign x s m k nullFunPtr nullPtr+ unless (isSuccess ret1) $ error "could not sign message"+ ret2 <- ecdsaSignatureSerializeDer x o ol s+ unless (isSuccess ret2) $ error "could not serialize signature"+ len <- peek ol+ packCStringLen (castPtr o, fromIntegral len)+ ret <- liftIO $ do+ pub <- allocaBytes 64 $ \p ->+ useByteString key $ \(s, _) -> do+ x <- signCtx+ createPubKey x s p+ packByteString (p, 64)+ useByteString msg $ \(m, _) ->+ useByteString pub $ \(p, _) -> do+ x <- contextCreate verify+ s' <- parseDer x der+ useByteString s' $ \(s, _) -> ecdsaVerify x s m p+ assertBool "signature matches" (isSuccess ret)+ where+ msg =+ fromRight undefined $+ B16.decodeBase16+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ key =+ fromRight undefined $+ B16.decodeBase16+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+++ecSecKeyVerifyTest :: Assertion+ecSecKeyVerifyTest = do+ ret <- liftIO $+ useByteString key $ \(k, _) -> do+ x <- signCtx+ ecSecKeyVerify x k+ assertBool "valid secret key" $ isSuccess ret+ where+ key =+ fromRight undefined $+ B16.decodeBase16+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+++ecPubkeyCreateTest :: Assertion+ecPubkeyCreateTest = do+ pk <- liftIO $+ useByteString key $ \(s, _) ->+ allocaBytes 64 $ \k -> do+ x <- signCtx+ createPubKey x s k+ allocaBytes 65 $ \o ->+ alloca $ \ol -> do+ poke ol 65+ rets <- ecPubKeySerialize x o ol k uncompressed+ unless (isSuccess rets) $ error "failed to serialize public key"+ len <- fromIntegral <$> peek ol+ packCStringLen (castPtr o, len)+ assertEqual "public key matches" pub pk+ where+ key =+ fromRight undefined $+ B16.decodeBase16+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+ pub =+ fromRight undefined $+ B16.decodeBase16+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+++ecSecKeyTweakAddTest :: Assertion+ecSecKeyTweakAddTest = do+ (ret, tweaked) <-+ liftIO $+ signCtx >>= \x ->+ useByteString tweak $ \(w, _) ->+ useByteString key $ \(k, _) -> do+ ret <- ecSecKeyTweakAdd x k w+ tweaked <- packByteString (k, 32)+ return (ret, tweaked)+ assertBool "successful secret key tweak" $ isSuccess ret+ assertEqual "tweaked keys match" expected tweaked+ where+ key =+ fromRight undefined $+ B16.decodeBase16+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+ tweak =+ fromRight undefined $+ B16.decodeBase16+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ expected =+ fromRight undefined $+ B16.decodeBase16+ "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"+++ecSecKeyTweakMulTest :: Assertion+ecSecKeyTweakMulTest = do+ (ret, tweaked) <- liftIO $ do+ x <- contextCreate sign+ retr <- withEntropy $ contextRandomize x+ unless (isSuccess retr) $ error "failed to randomize context"+ useByteString tweak $ \(w, _) -> useByteString key $ \(k, _) -> do+ ret <- ecSecKeyTweakMul x k w+ tweaked <- packByteString (k, 32)+ return (ret, tweaked)+ assertBool "successful secret key tweak" $ isSuccess ret+ assertEqual "tweaked keys match" expected tweaked+ where+ key =+ fromRight undefined $+ B16.decodeBase16+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+ tweak =+ fromRight undefined $+ B16.decodeBase16+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ expected =+ fromRight undefined $+ B16.decodeBase16+ "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"+++serializeKey :: Ctx -> Ptr PubKey64 -> IO ByteString+serializeKey x p = allocaBytes 72 $ \d -> alloca $ \dl -> do+ poke dl 72+ ret <- ecPubKeySerialize x d dl p uncompressed+ unless (isSuccess ret) $ error "could not serialize public key"+ len <- peek dl+ packCStringLen (castPtr d, fromIntegral len)+++parseKey :: Ctx -> ByteString -> IO ByteString+parseKey x bs =+ allocaBytes 64 $ \p ->+ useByteString bs $ \(d, dl) -> do+ ret <- ecPubKeyParse x p d dl+ unless (isSuccess ret) $ error "could not parse public key"+ packByteString (p, 64)+++ecPubKeyTweakAddTest :: Assertion+ecPubKeyTweakAddTest = do+ (ret, tweaked) <- liftIO $ do+ x <- contextCreate verify+ pk <- copy <$> parseKey x pub+ useByteString tweak $ \(w, _) ->+ useByteString pk $ \(p, _) -> do+ ret <- ecPubKeyTweakAdd x p w+ tweaked <- serializeKey x p+ return (ret, tweaked)+ assertBool "successful secret key tweak" $ isSuccess ret+ assertEqual "tweaked keys match" expected tweaked+ where+ pub =+ fromRight undefined $+ B16.decodeBase16+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+ tweak =+ fromRight undefined $+ B16.decodeBase16+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ expected =+ fromRight undefined $+ B16.decodeBase16+ "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\+ \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"+++ecPubKeyTweakMulTest :: Assertion+ecPubKeyTweakMulTest = do+ (ret, tweaked) <- liftIO $ do+ x <- contextCreate verify+ pk <- copy <$> parseKey x pub+ useByteString tweak $ \(w, _) ->+ useByteString pk $ \(p, _) -> do+ ret <- ecPubKeyTweakMul x p w+ tweaked <- serializeKey x p+ return (ret, tweaked)+ assertBool "successful secret key tweak" $ isSuccess ret+ assertEqual "tweaked keys match" expected tweaked+ where+ pub =+ fromRight undefined $+ B16.decodeBase16+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+ tweak =+ fromRight undefined $+ B16.decodeBase16+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ expected =+ fromRight undefined $+ B16.decodeBase16+ "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\+ \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"+++ecPubKeyCombineTest :: Assertion+ecPubKeyCombineTest = do+ (ret, com) <- liftIO $+ allocaBytes 64 $ \p1 ->+ allocaBytes 64 $ \p2 ->+ allocaBytes 64 $ \p3 ->+ allocaArray 3 $ \a ->+ allocaBytes 64 $ \p -> do+ x <- contextCreate verify+ parse x pub1 p1+ parse x pub2 p2+ parse x pub3 p3+ pokeArray a [p1, p2, p3]+ ret <- ecPubKeyCombine x p a 3+ com <- serializeKey x p+ return (ret, com)+ assertBool "successful key combination" $ isSuccess ret+ assertEqual "combined keys match" expected com+ where+ parse x pub p = useByteString pub $ \(d, dl) -> do+ ret <- ecPubKeyParse x p d dl+ unless (isSuccess ret) $ error "could not parse public key"+ pub1 =+ fromRight undefined $+ B16.decodeBase16+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+ pub2 =+ fromRight undefined $+ B16.decodeBase16+ "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\+ \77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"+ pub3 =+ fromRight undefined $+ B16.decodeBase16+ "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\+ \d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"+ expected =+ fromRight undefined $+ B16.decodeBase16+ "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\+ \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
+ test/Crypto/Secp256k1Spec.hs view
@@ -0,0 +1,248 @@+module Crypto.Secp256k1Spec (spec) where++import Control.Monad.Par+import qualified Control.Monad.Par as P+import Crypto.Secp256k1+import qualified Data.ByteString as BS+import qualified Data.ByteString.Base16 as B16+import qualified Data.ByteString.Char8 as B8+import Data.Either (fromRight)+import Data.Maybe (fromMaybe, isNothing)+import Data.String (fromString)+import Data.String.Conversions (cs)+import Test.HUnit (Assertion, assertEqual)+import Test.Hspec+import Test.QuickCheck++spec :: Spec+spec = do+ describe "signatures" $ do+ it "signs message" $+ property signMsgTest+ it "signs messages in parallel" $+ property signMsgParTest+ it "detects bad signature" $+ property badSignatureTest+ it "normalizes signatures" $+ property normalizeSigTest+ describe "serialization" $ do+ it "serializes public key" $+ property serializePubKeyTest+ it "serializes public keys in parallel" $+ property parSerializePubKeyTest+ it "serializes DER signature" $+ property serializeSigTest+ it "serializes DER signatures in parallel" $+ property parSerializeSigTest+ it "serializes compact signature" $+ property serializeCompactSigTest+ it "serialize secret key" $+ property serializeSecKeyTest+ it "shows and reads public key" $+ property (showRead :: PubKey -> Bool)+ it "shows and reads secret key" $+ property (showRead :: SecKey -> Bool)+ it "shows and reads tweak" $+ property (showReadTweak :: SecKey -> Bool)+ it "shows and reads signature" $+ property (showReadSig :: (SecKey, Msg) -> Bool)+ it "shows and reads message" $+ property (showRead :: Msg -> Bool)+ it "reads public key from string" $+ property isStringPubKey+ it "reads secret key from string" $+ property isStringSecKey+ it "reads signature from string" $+ property isStringSig+ it "reads message from string" $+ property isStringMsg+ it "reads tweak from string" $+ property isStringTweak+ describe "tweaks" $ do+ it "add secret key" $ property tweakAddSecKeyTest+ it "multiply secret key" $ property tweakMulSecKeyTest+ it "add public key" $ property tweakAddPubKeyTest+ it "multiply public key" $ property tweakMulPubKeyTest+ it "combine public keys" $ property combinePubKeyTest+ it "can't combine 0 public keys" $ property combinePubKeyEmptyListTest+ it "negates tweak" $ property negateTweakTest++hexToBytes :: String -> BS.ByteString+hexToBytes = fromRight undefined . B16.decodeBase16 . B8.pack++isStringPubKey :: (PubKey, Bool) -> Bool+isStringPubKey (k, c) = k == fromString (cs hex) where+ hex = B16.encodeBase16 $ exportPubKey c k++isStringSig :: (SecKey, Msg) -> Bool+isStringSig (k, m) = g == fromString (cs hex) where+ g = signMsg k m+ hex = B16.encodeBase16 $ exportSig g++isStringMsg :: Msg -> Bool+isStringMsg m = m == fromString (cs m') where+ m' = B16.encodeBase16 $ getMsg m++isStringSecKey :: SecKey -> Bool+isStringSecKey k = k == fromString (cs hex) where+ hex = B16.encodeBase16 $ getSecKey k++isStringTweak :: SecKey -> Bool+isStringTweak k = t == fromString (cs hex) where+ t = fromMaybe e . tweak $ getSecKey k+ hex = B16.encodeBase16 $ getTweak t+ e = error "Could not extract tweak from secret key"++showReadTweak :: SecKey -> Bool+showReadTweak k = showRead t where+ t = tweak $ getSecKey k++showReadSig :: (SecKey, Msg) -> Bool+showReadSig (k, m) = showRead sig where+ sig = signMsg k m++showRead :: (Show a, Read a, Eq a) => a -> Bool+showRead x = read (show x) == x++signMsgTest :: (Msg, SecKey) -> Bool+signMsgTest (fm, fk) = verifySig fp fg fm where+ fp = derivePubKey fk+ fg = signMsg fk fm++signMsgParTest :: [(Msg, SecKey)] -> Bool+signMsgParTest xs = P.runPar $ do+ ys <- mapM (P.spawnP . signMsgTest) xs+ and <$> mapM P.get ys++badSignatureTest :: (Msg, SecKey, PubKey) -> Bool+badSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where+ fg = signMsg fk fm++normalizeSigTest :: (Msg, SecKey) -> Bool+normalizeSigTest (fm, fk) = isNothing sig where+ fg = signMsg fk fm+ sig = normalizeSig fg++serializePubKeyTest :: (PubKey, Bool) -> Bool+serializePubKeyTest (fp, b) =+ case importPubKey $ exportPubKey b fp of+ Just fp' -> fp == fp'+ Nothing -> False++parSerializePubKeyTest :: [(PubKey, Bool)] -> Bool+parSerializePubKeyTest ps = runPar $ do+ as <- mapM (spawnP . serializePubKeyTest) ps+ and <$> mapM get as++serializeSigTest :: (Msg, SecKey) -> Bool+serializeSigTest (fm, fk) =+ case importSig $ exportSig fg of+ Just fg' -> fg == fg'+ Nothing -> False+ where+ fg = signMsg fk fm++parSerializeSigTest :: [(Msg, SecKey)] -> Bool+parSerializeSigTest ms = runPar $ do+ as <- mapM (spawnP . serializeSigTest) ms+ and <$> mapM get as++serializeCompactSigTest :: (Msg, SecKey) -> Bool+serializeCompactSigTest (fm, fk) =+ case importCompactSig $ exportCompactSig fg of+ Just fg' -> fg == fg'+ Nothing -> False+ where+ fg = signMsg fk fm++serializeSecKeyTest :: SecKey -> Bool+serializeSecKeyTest fk =+ case secKey $ getSecKey fk of+ Just fk' -> fk == fk'+ Nothing -> False++tweakAddSecKeyTest :: Assertion+tweakAddSecKeyTest =+ assertEqual "tweaked keys match" expected tweaked+ where+ tweaked = do+ key <- secKey $ hexToBytes+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+ twk <- tweak $ hexToBytes+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ tweakAddSecKey key twk+ expected = secKey $ hexToBytes+ "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"++tweakMulSecKeyTest :: Assertion+tweakMulSecKeyTest =+ assertEqual "tweaked keys match" expected tweaked+ where+ tweaked = do+ key <- secKey $ hexToBytes+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+ twk <- tweak $ hexToBytes+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ tweakMulSecKey key twk+ expected = secKey $ hexToBytes+ "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"++tweakAddPubKeyTest :: Assertion+tweakAddPubKeyTest =+ assertEqual "tweaked keys match" expected tweaked+ where+ tweaked = do+ pub <- importPubKey $ hexToBytes+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+ twk <- tweak $ hexToBytes+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ tweakAddPubKey pub twk+ expected = importPubKey $ hexToBytes+ "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fdc87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"++tweakMulPubKeyTest :: Assertion+tweakMulPubKeyTest =+ assertEqual "tweaked keys match" expected tweaked+ where+ tweaked = do+ pub <- importPubKey $ hexToBytes+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+ twk <- tweak $ hexToBytes+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+ tweakMulPubKey pub twk+ expected = importPubKey $ hexToBytes+ "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae49c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"++combinePubKeyTest :: Assertion+combinePubKeyTest =+ assertEqual "combined keys match" expected combined+ where+ combined = do+ pub1 <- importPubKey $ hexToBytes+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+ pub2 <- importPubKey $ hexToBytes+ "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f577dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"+ pub3 <- importPubKey $ hexToBytes+ "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3febd908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"+ combinePubKeys [pub1, pub2, pub3]+ expected = importPubKey $ hexToBytes+ "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"++combinePubKeyEmptyListTest :: Assertion+combinePubKeyEmptyListTest =+ assertEqual "empty pubkey list must return Nothing" expected combined+ where+ expected = Nothing+ combined = combinePubKeys []++negateTweakTest :: Assertion+negateTweakTest =+ assertEqual "can recover secret key 1 after adding tweak 1" oneKey subtracted+ where+ Just oneKey = secKey $ fromRight undefined $ B16.decodeBase16 $ B8.pack+ "0000000000000000000000000000000000000000000000000000000000000001"+ Just oneTwk = tweak $ fromRight undefined $ B16.decodeBase16 $ B8.pack+ "0000000000000000000000000000000000000000000000000000000000000001"+ Just minusOneTwk = tweakNegate oneTwk+ Just twoKey = tweakAddSecKey oneKey oneTwk+ Just subtracted = tweakAddSecKey twoKey minusOneTwk
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}