diff --git a/libsecp256k1.cabal b/libsecp256k1.cabal
--- a/libsecp256k1.cabal
+++ b/libsecp256k1.cabal
@@ -1,11 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.34.4.
+-- This file has been generated from package.yaml by hpack version 0.36.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:           libsecp256k1
-version:        0.1.0
+version:        0.1.1
 synopsis:       Bindings for secp256k1
 description:    Sign and verify signatures using the secp256k1 library.
 category:       Crypto
@@ -35,13 +35,15 @@
       Paths_libsecp256k1
   hs-source-dirs:
       src
+  default-extensions:
+      ImportQualifiedPost
   pkgconfig-depends:
       libsecp256k1
   build-depends:
       base >=4.9 && <5
     , bytestring >=0.10.8 && <0.12
     , entropy >=0.3.8 && <0.5
-    , hedgehog
+    , hedgehog ==1.2.*
     , memory >=0.14.15 && <1.0
     , transformers >=0.4.0.0 && <1.0
   default-language: Haskell2010
@@ -58,15 +60,19 @@
       Paths_libsecp256k1
   hs-source-dirs:
       test
+  default-extensions:
+      ImportQualifiedPost
   ghc-options: -threaded -rtsopts -with-rtsopts=-N
   build-depends:
       HUnit
     , base >=4.9 && <5
     , bytestring >=0.10.8 && <0.12
+    , either
     , entropy >=0.3.8 && <0.5
-    , hedgehog
+    , hedgehog ==1.2.*
     , hspec
     , libsecp256k1
     , memory >=0.14.15 && <1.0
+    , monad-par
     , transformers >=0.4.0.0 && <1.0
   default-language: Haskell2010
diff --git a/src/Crypto/Secp256k1.hs b/src/Crypto/Secp256k1.hs
--- a/src/Crypto/Secp256k1.hs
+++ b/src/Crypto/Secp256k1.hs
@@ -47,6 +47,7 @@
     ecdsaSign,
     ecdsaSignRecoverable,
     ecdsaRecover,
+    ecdsaNormalizeSignature,
 
     -- * Conversions
     recSigToSig,
@@ -64,6 +65,7 @@
     pubKeyCombine,
     pubKeyNegate,
     secKeyNegate,
+    tweakNegate,
     pubKeyTweakAdd,
     pubKeyTweakMul,
     pubKeyXOTweakAdd,
@@ -83,25 +85,18 @@
 import Control.Monad.Trans.Cont (ContT (..), evalContT)
 import Crypto.Secp256k1.Internal
 import Crypto.Secp256k1.Prim (flagsEcUncompressed)
-import qualified Crypto.Secp256k1.Prim as Prim
-import qualified Data.ByteArray.Encoding as BA
+import Crypto.Secp256k1.Prim qualified as Prim
+import Data.ByteArray.Encoding qualified as BA
 import Data.ByteArray.Sized
 import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-
--- import qualified Data.ByteString.Base16 as B16
+import Data.ByteString qualified as BS
+import Data.ByteString.Char8 qualified as B8
 import Data.ByteString.Unsafe (unsafePackCStringLen, unsafePackMallocCStringLen)
+import Data.Foldable (for_)
 import Data.Functor (($>))
-
--- import Data.Hashable (Hashable (..))
 import Data.Maybe (fromJust, fromMaybe, isJust)
-import Data.String (IsString (..))
-
--- import Data.String.Conversions (ConvertibleStrings, cs)
-
-import qualified Data.ByteString.Char8 as B8
-import Data.Foldable (for_)
 import Data.Memory.PtrMethods (memCompare)
+import Data.String (IsString (..))
 import Foreign (
     Bits (..),
     ForeignPtr,
@@ -362,12 +357,12 @@
         outBuf <- mallocBytes 64
         ret <-
             if
-                    -- compact
-                    | len == 64 -> Prim.ecdsaSignatureParseCompact ctx outBuf inBuf
-                    -- der
-                    | len >= 69 && len <= 73 -> Prim.ecdsaSignatureParseDer ctx outBuf inBuf len
-                    -- invalid
-                    | otherwise -> pure 0
+                -- compact
+                | len == 64 -> Prim.ecdsaSignatureParseCompact ctx outBuf inBuf
+                -- der
+                | len >= 69 && len <= 73 -> Prim.ecdsaSignatureParseDer ctx outBuf inBuf len
+                -- invalid
+                | otherwise -> pure 0
         if isSuccess ret
             then Just . Signature <$> newForeignPtr finalizerFree outBuf
             else free outBuf $> Nothing
@@ -485,6 +480,17 @@
                 else free pubKeyBuf $> Nothing
 
 
+-- | Convert a 'Signature' to a normalized lower-S form. If the 'Signature' was already in its lower-S form it will
+-- be equal to the input.
+ecdsaNormalizeSignature :: Signature -> Signature
+ecdsaNormalizeSignature Signature{..} = unsafePerformIO . evalContT $ do
+    sigPtr <- ContT (withForeignPtr signatureFPtr)
+    lift $ do
+        outBuf <- mallocBytes 64
+        _ret <- Prim.ecdsaSignatureNormalize ctx outBuf sigPtr
+        Signature <$> newForeignPtr finalizerFree outBuf
+
+
 -- | Forgets the recovery id of a signature
 recSigToSig :: RecoverableSignature -> Signature
 recSigToSig RecoverableSignature{..} = unsafePerformIO . evalContT $ do
@@ -722,6 +728,16 @@
     withForeignPtr secKeyFPtr $ flip (memcpy outBuf) 32
     _ret <- Prim.ecSeckeyNegate ctx outBuf
     SecKey <$> newForeignPtr finalizerFree outBuf
+
+
+-- | Negate a 'Tweak'
+tweakNegate :: Tweak -> Tweak
+tweakNegate Tweak{..} = unsafePerformIO $ do
+    outBuf <- mallocBytes 32
+    let asKey = castForeignPtr tweakFPtr
+    withForeignPtr asKey $ flip (memcpy outBuf) 32
+    _ret <- Prim.ecSeckeyNegate ctx outBuf
+    Tweak <$> newForeignPtr finalizerFree (castPtr outBuf)
 
 
 -- | Convert 'PubKeyXY' to 'PubKeyXO'. See 'keyPairPubKeyXO' for more information on how to interpret the parity bit.
diff --git a/src/Crypto/Secp256k1/Gen.hs b/src/Crypto/Secp256k1/Gen.hs
--- a/src/Crypto/Secp256k1/Gen.hs
+++ b/src/Crypto/Secp256k1/Gen.hs
@@ -1,30 +1,30 @@
 module Crypto.Secp256k1.Gen where
 
-import Crypto.Secp256k1 (KeyPair, PubKeyXO, PubKeyXY, SecKey, Tweak, derivePubKey, importSecKey, importTweak, keyPairCreate, xyToXO)
+import Crypto.Secp256k1
 import Hedgehog (MonadGen)
 import Hedgehog.Gen (bytes, discard, prune)
 import Hedgehog.Range (singleton)
 
 
-secKeyGen :: MonadGen m => m SecKey
+secKeyGen :: (MonadGen m) => m SecKey
 secKeyGen = do
-    bs <- prune $ bytes (singleton 32)
-    maybe discard pure (importSecKey bs)
+    bs <- prune . bytes $ singleton 32
+    maybe discard pure $ importSecKey bs
 
 
-pubKeyXYGen :: MonadGen m => m PubKeyXY
+pubKeyXYGen :: (MonadGen m) => m PubKeyXY
 pubKeyXYGen = derivePubKey <$> secKeyGen
 
 
-pubKeyXOGen :: MonadGen m => m PubKeyXO
+pubKeyXOGen :: (MonadGen m) => m PubKeyXO
 pubKeyXOGen = fst . xyToXO <$> pubKeyXYGen
 
 
-keyPairGen :: MonadGen m => m KeyPair
+keyPairGen :: (MonadGen m) => m KeyPair
 keyPairGen = keyPairCreate <$> secKeyGen
 
 
-tweakGen :: MonadGen m => m Tweak
+tweakGen :: (MonadGen m) => m Tweak
 tweakGen = do
     bs <- prune $ bytes (singleton 32)
     maybe discard pure (importTweak bs)
diff --git a/src/Crypto/Secp256k1/Internal.hs b/src/Crypto/Secp256k1/Internal.hs
--- a/src/Crypto/Secp256k1/Internal.hs
+++ b/src/Crypto/Secp256k1/Internal.hs
@@ -1,10 +1,10 @@
 module Crypto.Secp256k1.Internal where
 
 import Crypto.Secp256k1.Prim
-import qualified Data.ByteArray.Encoding as BA
+import Data.ByteArray.Encoding qualified as BA
 import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Unsafe as BU
+import Data.ByteString qualified as BS
+import Data.ByteString.Unsafe qualified as BU
 import Foreign (Ptr, castPtr)
 import Foreign.C (CSize)
 
diff --git a/src/Crypto/Secp256k1/Prim.hs b/src/Crypto/Secp256k1/Prim.hs
--- a/src/Crypto/Secp256k1/Prim.hs
+++ b/src/Crypto/Secp256k1/Prim.hs
@@ -17,8 +17,8 @@
 module Crypto.Secp256k1.Prim where
 
 import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Unsafe as BU
+import Data.ByteString qualified as BS
+import Data.ByteString.Unsafe qualified as BU
 import Foreign (Bits (..), FunPtr, Ptr, castPtr)
 import Foreign.C (
     CInt (..),
diff --git a/test/Crypto/Secp256k1/PrimSpec.hs b/test/Crypto/Secp256k1/PrimSpec.hs
--- a/test/Crypto/Secp256k1/PrimSpec.hs
+++ b/test/Crypto/Secp256k1/PrimSpec.hs
@@ -4,12 +4,16 @@
 
 import Control.Monad
 
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Cont
 import Crypto.Secp256k1.Internal
-import qualified Data.ByteArray.Encoding as BA
+import Crypto.Secp256k1.Prim
+import Data.ByteArray.Encoding qualified as BA
 import Data.ByteString (
     ByteString,
     copy,
     packCStringLen,
+    useAsCString,
     useAsCStringLen,
  )
 import Data.Either (fromRight)
@@ -21,426 +25,461 @@
 
 spec :: Spec
 spec = do
-    pure ()
+    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
 
---     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
+flagsSignVerify :: ContextFlags
+flagsSignVerify = flagsContextSign .|. flagsContextVerify
 
--- 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
+withEntropy :: (Ptr Seed32 -> IO a) -> IO a
+withEntropy f =
+    getEntropy 32 >>= \e ->
+        useByteString e $ \(s, _) -> f s
 
--- 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 $
---                 decodeBase16
---                     "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
+createContextTest :: Assertion
+createContextTest = do
+    context_ptr <- liftIO $ contextCreate flagsSignVerify
+    assertBool "context not null" $ context_ptr /= nullPtr
 
--- 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 $
---                 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 $
---                 decodeBase16
---                     "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
---                     \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
---                     \45"
+randomizeContextTest :: Assertion
+randomizeContextTest = do
+    ret <- liftIO $ contextCreate flagsContextSign >>= withEntropy . contextRandomize
+    assertBool "context randomized" $ isSuccess ret
 
--- 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 $
---                 decodeBase16
---                     "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
---                     \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
---                     \45"
+cloneContextTest :: Assertion
+cloneContextTest = do
+    (x1, x2) <- liftIO $ do
+        x1 <- contextCreate flagsSignVerify
+        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
 
--- 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 $
---                 decodeBase16
---                     "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
---                     \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
---                     \45"
---         pub =
---             fromRight undefined $
---                 decodeBase16
---                     "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
---                     \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
---         msg =
---             fromRight undefined $
---                 decodeBase16
---                     "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
 
--- signCtx :: IO Ctx
--- signCtx =
---     contextCreate sign >>= \c ->
---         withEntropy (contextRandomize c) >>= \r ->
---             unless (isSuccess r) (error "failed to randomize context") >> return c
+ecPubkeyParseTest :: Assertion
+ecPubkeyParseTest = evalContT $ do
+    (i, il) <- ContT (useAsCStringLen der)
+    pubkey <- ContT (allocaBytes 64)
+    liftIO $ do
+        x <- contextCreate flagsContextVerify
+        ret <- ecPubkeyParse x pubkey (castPtr i) (fromIntegral il)
+        assertBool "parsed public key" (isSuccess ret)
+    where
+        der =
+            fromRight undefined $
+                decodeBase16
+                    "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
 
--- 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 $
---                 decodeBase16
---                     "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
---         key =
---             fromRight undefined $
---                 decodeBase16
---                     "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+ecPubKeySerializeTest :: Assertion
+ecPubKeySerializeTest = evalContT $ do
+    (i, il) <- ContT (useByteString der)
+    k <- ContT (allocaBytes 64)
+    ol <- ContT alloca
+    o <- ContT (allocaBytes 72)
+    liftIO $ do
+        poke ol 72
+        x <- contextCreate flagsContextVerify
+        ret1 <- ecPubkeyParse x k i il
+        unless (isSuccess ret1) $ error "failed to parse pubkey"
+        ret2 <- ecPubkeySerialize x o ol k flagsEcCompressed
+        len <- fromIntegral <$> peek ol
+        decoded <- packCStringLen (castPtr o, len)
 
--- 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 $
---                 decodeBase16
---                     "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+        assertBool "serialized public key successfully" $ isSuccess ret2
+        assertEqual "public key matches" der decoded
+    where
+        der =
+            fromRight undefined $
+                decodeBase16
+                    "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
 
--- 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 $
---                 decodeBase16
---                     "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
---         pub =
---             fromRight undefined $
---                 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 $
---                 decodeBase16
---                     "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
---         tweak =
---             fromRight undefined $
---                 decodeBase16
---                     "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
---         expected =
---             fromRight undefined $
---                 decodeBase16
---                     "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
+ecdsaSignatureParseDerTest :: Assertion
+ecdsaSignatureParseDerTest = evalContT $ do
+    (d, dl) <- ContT (useAsCStringLen der)
+    s <- ContT (allocaBytes 64)
+    liftIO $ do
+        x <- contextCreate flagsContextVerify
+        ret <- ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
+        assertBool "parsed signature successfully" $ isSuccess ret
+    where
+        der =
+            fromRight undefined $
+                decodeBase16
+                    "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
+                    \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
+                    \45"
 
--- 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 $
---                 decodeBase16
---                     "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
---         tweak =
---             fromRight undefined $
---                 decodeBase16
---                     "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
---         expected =
---             fromRight undefined $
---                 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)
+parseDer :: Ctx -> ByteString -> IO ByteString
+parseDer x bs = evalContT $ do
+    (d, dl) <- ContT (useAsCStringLen bs)
+    s <- ContT (allocaBytes 64)
+    liftIO $ do
+        ret <- ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
+        unless (isSuccess ret) $ error "could not parse DER"
+        packByteString (s, 64)
 
--- 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 $
---                 decodeBase16
---                     "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
---                     \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
---         tweak =
---             fromRight undefined $
---                 decodeBase16
---                     "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
---         expected =
---             fromRight undefined $
---                 decodeBase16
---                     "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
---                     \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
+ecdsaSignatureSerializeDerTest :: Assertion
+ecdsaSignatureSerializeDerTest = evalContT $ do
+    ol <- ContT alloca
+    o <- ContT (allocaBytes 72)
+    x <- liftIO $ contextCreate flagsContextVerify
+    sig <- liftIO $ parseDer x der
+    (s, _) <- ContT (useByteString sig)
+    liftIO $ do
+        poke ol 72
+        ret <- ecdsaSignatureSerializeDer x o ol s
+        len <- fromIntegral <$> peek ol
+        enc <- packCStringLen (castPtr o, len)
+        assertBool "serialization successful" $ isSuccess ret
+        assertEqual "signatures match" der enc
+    where
+        der =
+            fromRight undefined $
+                decodeBase16
+                    "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
+                    \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
+                    \45"
 
--- 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 $
---                 decodeBase16
---                     "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
---                     \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
---         tweak =
---             fromRight undefined $
---                 decodeBase16
---                     "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
---         expected =
---             fromRight undefined $
---                 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 $
---                 decodeBase16
---                     "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
---                     \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
---         pub2 =
---             fromRight undefined $
---                 decodeBase16
---                     "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\
---                     \77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
---         pub3 =
---             fromRight undefined $
---                 decodeBase16
---                     "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\
---                     \d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
---         expected =
---             fromRight undefined $
---                 decodeBase16
---                     "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
---                     \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
+ecdsaVerifyTest :: Assertion
+ecdsaVerifyTest = evalContT $ do
+    (p, pl) <- ContT (useByteString pub)
+    (m, _) <- ContT (useByteString msg)
+    k <- ContT (allocaBytes 64)
+    (x, pk, sig) <- liftIO $ do
+        x <- contextCreate flagsContextVerify
+        ret <- ecPubkeyParse x k p (fromIntegral pl)
+        sig <- parseDer x der
+        unless (isSuccess ret) $ error "could not parse public key"
+        pk <- packByteString (k, 64)
+        pure (x, pk, sig)
+
+    (k, _) <- ContT (useByteString pk)
+    (s, _) <- ContT (useByteString sig)
+    ret <- liftIO $ ecdsaVerify x s m k
+    liftIO $ assertBool "signature valid" $ isSuccess ret
+    where
+        der =
+            fromRight undefined $
+                decodeBase16
+                    "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
+                    \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
+                    \45"
+        pub =
+            fromRight undefined $
+                decodeBase16
+                    "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+                    \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+        msg =
+            fromRight undefined $
+                decodeBase16
+                    "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+
+
+signCtx :: IO Ctx
+signCtx = do
+    c <- contextCreate flagsContextSign
+    r <- withEntropy (contextRandomize c)
+    unless (isSuccess r) (error "failed to randomize context")
+    pure 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
+    x <- signCtx
+    der <- evalContT $ do
+        s <- ContT (allocaBytes 64)
+        (m, _) <- ContT (useByteString msg)
+        (k, _) <- ContT (useByteString key)
+        ol <- ContT alloca
+        o <- ContT (allocaBytes 72)
+        liftIO $ 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 <- evalContT $ do
+        p <- ContT (allocaBytes 64)
+        (s, _) <- ContT (useByteString key)
+        (m, _) <- ContT (useByteString msg)
+        pub <- liftIO $ do
+            x <- signCtx
+            createPubKey x s p
+            packByteString (p, 64)
+        (p, _) <- ContT (useByteString pub)
+        x <- liftIO $ contextCreate flagsContextVerify
+        s' <- liftIO $ parseDer x der
+        (s, _) <- ContT (useByteString s')
+        liftIO $ ecdsaVerify x s m p
+    assertBool "signature matches" (isSuccess ret)
+    where
+        msg =
+            fromRight undefined $
+                decodeBase16
+                    "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        key =
+            fromRight undefined $
+                decodeBase16
+                    "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+
+
+ecSecKeyVerifyTest :: Assertion
+ecSecKeyVerifyTest = evalContT $ do
+    (k, _) <- ContT (useByteString key)
+    liftIO $ do
+        x <- signCtx
+        ret <- ecSecKeyVerify x k
+        assertBool "valid secret key" $ isSuccess ret
+    where
+        key =
+            fromRight undefined $
+                decodeBase16
+                    "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+
+
+ecPubkeyCreateTest :: Assertion
+ecPubkeyCreateTest = evalContT $ do
+    (s, _) <- ContT (useByteString key)
+    k <- ContT (allocaBytes 64)
+    o <- ContT (allocaBytes 65)
+    ol <- ContT alloca
+    liftIO $ do
+        x <- signCtx
+        createPubKey x s k
+        poke ol 65
+        rets <- ecPubkeySerialize x o ol k flagsEcUncompressed
+        unless (isSuccess rets) $ error "failed to serialize public key"
+        len <- fromIntegral <$> peek ol
+        pk <- packCStringLen (castPtr o, len)
+        assertEqual "public key matches" pub pk
+    where
+        key =
+            fromRight undefined $
+                decodeBase16
+                    "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+        pub =
+            fromRight undefined $
+                decodeBase16
+                    "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+                    \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+
+
+ecSecKeyTweakAddTest :: Assertion
+ecSecKeyTweakAddTest = evalContT $ do
+    (w, _) <- ContT (useByteString tweak)
+    (k, _) <- ContT (useByteString key)
+    liftIO $ do
+        x <- signCtx
+        ret <- ecSeckeyTweakAdd x k w
+        tweaked <- packByteString (k, 32)
+
+        assertBool "successful secret key tweak" $ isSuccess ret
+        assertEqual "tweaked keys match" expected tweaked
+    where
+        key =
+            fromRight undefined $
+                decodeBase16
+                    "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+        tweak =
+            fromRight undefined $
+                decodeBase16
+                    "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        expected =
+            fromRight undefined $
+                decodeBase16
+                    "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
+
+
+ecSecKeyTweakMulTest :: Assertion
+ecSecKeyTweakMulTest = evalContT $ do
+    (w, _) <- ContT (useByteString tweak)
+    (k, _) <- ContT (useByteString key)
+    liftIO $ do
+        x <- contextCreate flagsContextSign
+        retr <- withEntropy $ contextRandomize x
+        unless (isSuccess retr) $ error "failed to randomize context"
+        ret <- ecSeckeyTweakMul x k w
+        tweaked <- packByteString (k, 32)
+
+        assertBool "successful secret key tweak" $ isSuccess ret
+        assertEqual "tweaked keys match" expected tweaked
+    where
+        key =
+            fromRight undefined $
+                decodeBase16
+                    "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+        tweak =
+            fromRight undefined $
+                decodeBase16
+                    "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        expected =
+            fromRight undefined $
+                decodeBase16
+                    "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
+
+
+serializeKey :: Ctx -> Ptr Pubkey64 -> IO ByteString
+serializeKey x p = evalContT $ do
+    d <- ContT (allocaBytes 72)
+    dl <- ContT alloca
+    liftIO $ do
+        poke dl 72
+        ret <- ecPubkeySerialize x d dl p flagsEcUncompressed
+        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 = evalContT $ do
+    p <- ContT (allocaBytes 64)
+    (d, dl) <- ContT (useByteString bs)
+    liftIO $ do
+        ret <- ecPubkeyParse x p d dl
+        unless (isSuccess ret) $ error "could not parse public key"
+        packByteString (p, 64)
+
+
+ecPubKeyTweakAddTest :: Assertion
+ecPubKeyTweakAddTest = do
+    x <- contextCreate flagsContextVerify
+    pk <- copy <$> parseKey x pub
+    (w, p) <- evalContT $ do
+        (w, _) <- ContT (useByteString tweak)
+        (p, _) <- ContT (useByteString pk)
+        pure (w, p)
+
+    ret <- ecPubkeyTweakAdd x p w
+    tweaked <- serializeKey x p
+
+    assertBool "successful secret key tweak" $ isSuccess ret
+    assertEqual "tweaked keys match" expected tweaked
+    where
+        pub =
+            fromRight undefined $
+                decodeBase16
+                    "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+                    \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+        tweak =
+            fromRight undefined $
+                decodeBase16
+                    "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        expected =
+            fromRight undefined $
+                decodeBase16
+                    "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
+                    \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
+
+
+ecPubKeyTweakMulTest :: Assertion
+ecPubKeyTweakMulTest = do
+    x <- contextCreate flagsContextVerify
+    pk <- copy <$> parseKey x pub
+    (w, p) <- evalContT $ do
+        (w, _) <- ContT (useByteString tweak)
+        (p, _) <- ContT (useByteString pk)
+        pure (w, p)
+    ret <- ecPubkeyTweakMul x p w
+    tweaked <- serializeKey x p
+    assertBool "successful secret key tweak" $ isSuccess ret
+    assertEqual "tweaked keys match" expected tweaked
+    where
+        pub =
+            fromRight undefined $
+                decodeBase16
+                    "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+                    \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+        tweak =
+            fromRight undefined $
+                decodeBase16
+                    "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        expected =
+            fromRight undefined $
+                decodeBase16
+                    "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\
+                    \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
+
+
+ecPubKeyCombineTest :: Assertion
+ecPubKeyCombineTest = evalContT $ do
+    p1 <- ContT (allocaBytes 64)
+    p2 <- ContT (allocaBytes 64)
+    p3 <- ContT (allocaBytes 64)
+    a <- ContT (allocaArray 3)
+    p <- ContT (allocaBytes 64)
+
+    liftIO $ do
+        x <- contextCreate flagsContextVerify
+        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
+
+        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 $
+                decodeBase16
+                    "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+                    \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+        pub2 =
+            fromRight undefined $
+                decodeBase16
+                    "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\
+                    \77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
+        pub3 =
+            fromRight undefined $
+                decodeBase16
+                    "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\
+                    \d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
+        expected =
+            fromRight undefined $
+                decodeBase16
+                    "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
+                    \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
diff --git a/test/Crypto/Secp256k1Prop.hs b/test/Crypto/Secp256k1Prop.hs
--- a/test/Crypto/Secp256k1Prop.hs
+++ b/test/Crypto/Secp256k1Prop.hs
@@ -7,7 +7,7 @@
 import Crypto.Secp256k1
 import Crypto.Secp256k1.Gen
 import Data.ByteArray.Sized (sizedByteArray)
-import qualified Data.ByteString as BS
+import Data.ByteString qualified as BS
 import Data.Maybe (fromJust, isJust)
 import Data.Void
 import Hedgehog
@@ -64,7 +64,7 @@
 
 
 prop_pubKeyXOSerializeInvertsParse :: Property
-prop_pubKeyXOSerializeInvertsParse = property $ do
+prop_pubKeyXOSerializeInvertsParse = withDiscards 200 . property $ do
     bs <- forAll (bytes $ singleton 32)
     case importPubKeyXO bs of
         Nothing -> discard
@@ -75,12 +75,12 @@
 prop_signatureParseInvertsSerialize = property $ do
     sk <- forAll secKeyGen
     bs <- forAll $ bytes (singleton 32)
-    sig <- case ecdsaSign sk bs of
-        Nothing -> failure
-        Just x -> pure x
+
+    sig <- maybe failure pure (ecdsaSign sk bs)
+
     exportDer <- forAll $ element [False, True]
     let export = if exportDer then exportSignatureDer else exportSignatureCompact
-    let serialized = (export sig)
+    let serialized = export sig
     annotateShow serialized
     annotateShow (BS.length serialized)
     case importSignature serialized of
@@ -92,10 +92,10 @@
 prop_recoverableSignatureParseInvertsSerialize = property $ do
     sk <- forAll secKeyGen
     bs <- forAll $ bytes (singleton 32)
-    sig <- case ecdsaSignRecoverable sk bs of
-        Nothing -> failure
-        Just x -> pure x
-    let serialized = (exportRecoverableSignature sig)
+
+    sig <- maybe failure pure (ecdsaSignRecoverable sk bs)
+
+    let serialized = exportRecoverableSignature sig
     annotateShow serialized
     annotateShow (BS.length serialized)
     case importRecoverableSignature serialized of
@@ -249,4 +249,4 @@
 
 
 tests :: Group
-tests = $$(discover)
+tests = $$discover
diff --git a/test/Crypto/Secp256k1Spec.hs b/test/Crypto/Secp256k1Spec.hs
--- a/test/Crypto/Secp256k1Spec.hs
+++ b/test/Crypto/Secp256k1Spec.hs
@@ -1,12 +1,12 @@
 module Crypto.Secp256k1Spec (spec) where
 
--- import Control.Monad.Par
--- import qualified Control.Monad.Par as P
+import Control.Monad.Par qualified as P
 import Crypto.Secp256k1
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Char8 as B8
+import Crypto.Secp256k1.Internal
+import Data.ByteString qualified as BS
+import Data.ByteString.Char8 qualified as B8
 import Data.Either (fromRight)
-import Data.Maybe (fromMaybe, isNothing)
+import Data.Maybe (fromMaybe, isJust, isNothing)
 import Data.String (fromString)
 import Test.HUnit (Assertion, assertEqual)
 import Test.Hspec
@@ -14,215 +14,214 @@
 
 spec :: Spec
 spec = do
-    pure ()
+    describe "tweaks" $ do
+        it "add secret key" tweakAddSecKeyTest
+        it "multiply secret key" tweakMulSecKeyTest
+        it "add public key" tweakAddPubKeyTest
+        it "multiply public key" tweakMulPubKeyTest
+        it "combine public keys" combinePubKeyTest
+        it "can't combine 0 public keys" combinePubKeyEmptyListTest
+        it "negates tweak" negateTweakTest
 
---     describe "tweaks" $ do
---         it "add secret key" tweakAddSecKeyTest
---         it "multiply secret key" tweakMulSecKeyTest
---         it "add public key" tweakAddPubKeyTest
---         it "multiply public key" tweakMulPubKeyTest
---         it "combine public keys" combinePubKeyTest
---         it "can't combine 0 public keys" combinePubKeyEmptyListTest
---         it "negates tweak" negateTweakTest
 
--- hexToBytes :: String -> BS.ByteString
--- hexToBytes = fromRight undefined . decodeBase16 . B8.pack
+hexToBytes :: String -> BS.ByteString
+hexToBytes = fromRight undefined . decodeBase16 . B8.pack
 
--- 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
+signMsgTest :: (BS.ByteString, SecKey) -> Bool
+signMsgTest (fm, fk) = (ecdsaVerify fm fp <$> fg) == Just True
+    where
+        fp = derivePubKey fk
+        fg = ecdsaSign fk fm
 
--- 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 :: [(BS.ByteString, SecKey)] -> Bool
+signMsgParTest xs = P.runPar $ do
+    ys <- mapM (P.spawnP . signMsgTest) xs
+    and <$> mapM P.get ys
 
--- 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
+badSignatureTest :: (BS.ByteString, SecKey, PubKeyXY) -> Bool
+badSignatureTest (fm, fk, fp) = (ecdsaVerify fm fp <$> fg) == Just False
+    where
+        fg = ecdsaSign 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
+normalizeSigTest :: (BS.ByteString, SecKey) -> Bool
+normalizeSigTest (fm, fk) = sig == fg
+    where
+        fg = ecdsaSign fk fm
+        sig = ecdsaNormalizeSignature <$> fg
 
--- 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
+serializePubKeyTest :: (PubKeyXY, Bool) -> Bool
+serializePubKeyTest (fp, b) =
+    case importPubKeyXY $ exportPubKeyXY b fp of
+        Just fp' -> fp == fp'
+        Nothing -> False
 
--- 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
+parSerializePubKeyTest :: [(PubKeyXY, Bool)] -> Bool
+parSerializePubKeyTest ps = P.runPar $ do
+    as <- mapM (P.spawnP . serializePubKeyTest) ps
+    and <$> mapM P.get as
 
--- 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"
+serializeSigTest :: (BS.ByteString, SecKey) -> Bool
+serializeSigTest (fm, fk) =
+    (fg >>= importSignature . exportSignatureDer) == fg && isJust fg
+    where
+        fg = ecdsaSign fk fm
 
--- 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"
+parSerializeSigTest :: [(BS.ByteString, SecKey)] -> Bool
+parSerializeSigTest ms = P.runPar $ do
+    as <- mapM (P.spawnP . serializeSigTest) ms
+    and <$> mapM P.get as
 
--- 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"
+serializeCompactSigTest :: (BS.ByteString, SecKey) -> Bool
+serializeCompactSigTest (fm, fk) =
+    (fg >>= importSignature . exportSignatureCompact) == fg && isJust fg
+    where
+        fg = ecdsaSign fk fm
 
--- 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 $
---                     decodeBase16 $
---                         B8.pack
---                             "0000000000000000000000000000000000000000000000000000000000000001"
---         Just oneTwk =
---             tweak $
---                 fromRight undefined $
---                     decodeBase16 $
---                         B8.pack
---                             "0000000000000000000000000000000000000000000000000000000000000001"
---         Just minusOneTwk = tweakNegate oneTwk
---         Just twoKey = tweakAddSecKey oneKey oneTwk
---         Just subtracted = tweakAddSecKey twoKey minusOneTwk
+serializeSecKeyTest :: SecKey -> Bool
+serializeSecKeyTest fk =
+    case importSecKey $ exportSecKey fk of
+        Just fk' -> fk == fk'
+        Nothing -> False
+
+
+tweakAddSecKeyTest :: Assertion
+tweakAddSecKeyTest =
+    assertEqual "tweaked keys match" expected tweaked
+    where
+        tweaked = do
+            key <-
+                importSecKey $
+                    hexToBytes
+                        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+            twk <-
+                importTweak $
+                    hexToBytes
+                        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+            secKeyTweakAdd key twk
+        expected =
+            importSecKey $
+                hexToBytes
+                    "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
+
+
+tweakMulSecKeyTest :: Assertion
+tweakMulSecKeyTest =
+    assertEqual "tweaked keys match" expected tweaked
+    where
+        tweaked = do
+            key <-
+                importSecKey $
+                    hexToBytes
+                        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+            twk <-
+                importTweak $
+                    hexToBytes
+                        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+            secKeyTweakMul key twk
+        expected =
+            importSecKey $
+                hexToBytes
+                    "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
+
+
+tweakAddPubKeyTest :: Assertion
+tweakAddPubKeyTest =
+    assertEqual "tweaked keys match" expected tweaked
+    where
+        tweaked = do
+            pub <-
+                importPubKeyXY $
+                    hexToBytes
+                        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+            twk <-
+                importTweak $
+                    hexToBytes
+                        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+            pubKeyTweakAdd pub twk
+        expected =
+            importPubKeyXY $
+                hexToBytes
+                    "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fdc87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
+
+
+tweakMulPubKeyTest :: Assertion
+tweakMulPubKeyTest =
+    assertEqual "tweaked keys match" expected tweaked
+    where
+        tweaked = do
+            pub <-
+                importPubKeyXY $
+                    hexToBytes
+                        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+            twk <-
+                importTweak $
+                    hexToBytes
+                        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+            pubKeyTweakMul pub twk
+        expected =
+            importPubKeyXY $
+                hexToBytes
+                    "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae49c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
+
+
+combinePubKeyTest :: Assertion
+combinePubKeyTest =
+    assertEqual "combined keys match" expected combined
+    where
+        combined = do
+            pub1 <-
+                importPubKeyXY $
+                    hexToBytes
+                        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+            pub2 <-
+                importPubKeyXY $
+                    hexToBytes
+                        "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f577dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
+            pub3 <-
+                importPubKeyXY $
+                    hexToBytes
+                        "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3febd908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
+            pubKeyCombine [pub1, pub2, pub3]
+        expected =
+            importPubKeyXY $
+                hexToBytes
+                    "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
+
+
+combinePubKeyEmptyListTest :: Assertion
+combinePubKeyEmptyListTest =
+    assertEqual "empty pubkey list must return Nothing" expected combined
+    where
+        expected = Nothing
+        combined = pubKeyCombine []
+
+
+negateTweakTest :: Assertion
+negateTweakTest =
+    assertEqual "can recover secret key 1 after adding tweak 1" oneKey subtracted
+    where
+        Just oneKey =
+            importSecKey $
+                fromRight undefined $
+                    decodeBase16 $
+                        B8.pack
+                            "0000000000000000000000000000000000000000000000000000000000000001"
+        Just oneTwk =
+            importTweak $
+                fromRight undefined $
+                    decodeBase16 $
+                        B8.pack
+                            "0000000000000000000000000000000000000000000000000000000000000001"
+        minusOneTwk = tweakNegate oneTwk
+        Just twoKey = secKeyTweakAdd oneKey oneTwk
+        Just subtracted = secKeyTweakAdd twoKey minusOneTwk
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,10 +1,10 @@
 module Main where
 
-import qualified Crypto.Secp256k1Prop as Secp256k1Prop
+import Crypto.Secp256k1Prop qualified as Secp256k1Prop
 import GHC.IO.Encoding (setLocaleEncoding, utf8)
 import Hedgehog (checkSequential)
 import Hedgehog.Main
-import qualified Spec
+import Spec qualified
 import Test.Hspec.Formatters
 import Test.Hspec.Runner (Config (..), defaultConfig, hspecWith)
 
@@ -13,6 +13,6 @@
 main = do
     setLocaleEncoding utf8
     hspecWith defaultConfig{configFormatter = Just progress} Spec.spec
-    defaultMain $
+    defaultMain
         [ checkSequential Secp256k1Prop.tests
         ]
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,9 +1,11 @@
 module Spec where
 
-import qualified Crypto.Secp256k1Spec as Secp256k1
+import Crypto.Secp256k1.PrimSpec qualified as Prim
+import Crypto.Secp256k1Spec qualified as Secp256k1
 import Test.Hspec
 
 
 spec :: Spec
 spec = do
+    Prim.spec
     Secp256k1.spec
