packages feed

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 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 @@+[![Build Status](https://travis-ci.org/haskoin/secp256k1-haskell.svg?branch=master)](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 #-}