diff --git a/secp256k1.cabal b/secp256k1.cabal
--- a/secp256k1.cabal
+++ b/secp256k1.cabal
@@ -1,149 +1,167 @@
-name:                secp256k1
-version:             0.5.3
-synopsis:            Bindings for secp256k1 library from Bitcoin Core
-description:         Please see README.md
-homepage:            http://github.com/haskoin/secp256k1-haskell#readme
-license:             PublicDomain
-license-file:        UNLICENSE
-author:              Jean-Pierre Rupp
-maintainer:          xenog@protonmail.com
-copyright:           (c) 2017 Jean-Pierre Rupp
-category:            Crypto
-build-type:          Custom
+-- This file has been generated from package.yaml by hpack version 0.28.2.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 9fd6f5be2cfb4b06721e74cd378459ffdf2d51e1a03a62d8484d9d342e985261
+
+name:           secp256k1
+version:        1.0.0
+synopsis:       Bindings for secp256k1 library from Bitcoin Core
+description:    Sign and verify signatures using the very fast C secp256k1 library from Pieter Wuille. Has Haskell types and abstractions for keys and signatures.
+category:       Crypto
+homepage:       http://github.com/haskoin/secp256k1-haskell#readme
+bug-reports:    https://github.com/haskoin/secp256k1-haskell.git/issues
+author:         Jean-Pierre Rupp
+maintainer:     xenog@protonmail.com
+copyright:      (c) 2017 Jean-Pierre Rupp
+license:        PublicDomain
+license-file:   UNLICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
 extra-source-files:
-    secp256k1/README.md
+    README.md
     secp256k1/autogen.sh
+    secp256k1/build-aux/m4/ax_jni_include_dir.m4
+    secp256k1/build-aux/m4/ax_prog_cc_for_build.m4
+    secp256k1/build-aux/m4/bitcoin_secp.m4
     secp256k1/configure.ac
-    secp256k1/Makefile.am
+    secp256k1/contrib/lax_der_parsing.c
+    secp256k1/contrib/lax_der_parsing.h
+    secp256k1/contrib/lax_der_privatekey_parsing.c
+    secp256k1/contrib/lax_der_privatekey_parsing.h
     secp256k1/COPYING
-    secp256k1/src/num_impl.h
-    secp256k1/src/bench_verify.c
-    secp256k1/src/gen_context.c
-    secp256k1/src/bench_recover.c
+    secp256k1/include/secp256k1.h
+    secp256k1/include/secp256k1_ecdh.h
+    secp256k1/include/secp256k1_recovery.h
+    secp256k1/libsecp256k1.pc.in
+    secp256k1/Makefile.am
+    secp256k1/README.md
+    secp256k1/sage/group_prover.sage
+    secp256k1/sage/secp256k1.sage
+    secp256k1/sage/weierstrass_prover.sage
+    secp256k1/src/asm/field_10x26_arm.s
     secp256k1/src/basic-config.h
+    secp256k1/src/bench.h
     secp256k1/src/bench_ecdh.c
-    secp256k1/src/ecmult_const.h
-    secp256k1/src/scalar_low.h
-    secp256k1/src/tests_exhaustive.c
+    secp256k1/src/bench_internal.c
+    secp256k1/src/bench_recover.c
+    secp256k1/src/bench_schnorr_verify.c
+    secp256k1/src/bench_sign.c
+    secp256k1/src/bench_verify.c
+    secp256k1/src/ecdsa.h
     secp256k1/src/ecdsa_impl.h
-    secp256k1/src/ecmult_impl.h
+    secp256k1/src/eckey.h
+    secp256k1/src/eckey_impl.h
+    secp256k1/src/ecmult.h
+    secp256k1/src/ecmult_const.h
+    secp256k1/src/ecmult_const_impl.h
+    secp256k1/src/ecmult_gen.h
     secp256k1/src/ecmult_gen_impl.h
-    secp256k1/src/scalar_8x32.h
-    secp256k1/src/scalar_8x32_impl.h
-    secp256k1/src/group.h
-    secp256k1/src/testrand_impl.h
-    secp256k1/src/modules/ecdh/main_impl.h
-    secp256k1/src/modules/ecdh/tests_impl.h
-    secp256k1/src/modules/ecdh/Makefile.am.include
-    secp256k1/src/modules/recovery/main_impl.h
-    secp256k1/src/modules/recovery/tests_impl.h
-    secp256k1/src/modules/recovery/Makefile.am.include
-    secp256k1/src/ecdsa.h
-    secp256k1/src/testrand.h
-    secp256k1/src/bench_internal.c
+    secp256k1/src/ecmult_impl.h
+    secp256k1/src/field.h
+    secp256k1/src/field_10x26.h
     secp256k1/src/field_10x26_impl.h
-    secp256k1/src/eckey.h
     secp256k1/src/field_5x52.h
     secp256k1/src/field_5x52_asm_impl.h
-    secp256k1/src/asm/field_10x26_arm.s
-    secp256k1/src/field_10x26.h
-    secp256k1/src/util.h
-    secp256k1/src/num.h
-    secp256k1/src/num_gmp.h
-    secp256k1/src/scalar_4x64_impl.h
-    secp256k1/src/hash.h
-    secp256k1/src/scalar_4x64.h
-    secp256k1/src/hash_impl.h
-    secp256k1/src/tests.c
-    secp256k1/src/scalar_impl.h
-    secp256k1/src/bench_sign.c
-    secp256k1/src/field_5x52_int128_impl.h
     secp256k1/src/field_5x52_impl.h
-    secp256k1/src/field.h
-    secp256k1/src/secp256k1.c
+    secp256k1/src/field_5x52_int128_impl.h
+    secp256k1/src/field_impl.h
+    secp256k1/src/gen_context.c
+    secp256k1/src/group.h
     secp256k1/src/group_impl.h
-    secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
-    secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
+    secp256k1/src/hash.h
+    secp256k1/src/hash_impl.h
     secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
-    secp256k1/src/java/org/bitcoin/Secp256k1Context.java
+    secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
     secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java
-    secp256k1/src/java/org_bitcoin_Secp256k1Context.h
+    secp256k1/src/java/org/bitcoin/Secp256k1Context.java
     secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
+    secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
     secp256k1/src/java/org_bitcoin_Secp256k1Context.c
-    secp256k1/src/ecmult_gen.h
-    secp256k1/src/ecmult_const_impl.h
-    secp256k1/src/eckey_impl.h
-    secp256k1/src/field_impl.h
-    secp256k1/src/bench.h
-    secp256k1/src/scalar_low_impl.h
-    secp256k1/src/bench_schnorr_verify.c
-    secp256k1/src/scalar.h
+    secp256k1/src/java/org_bitcoin_Secp256k1Context.h
+    secp256k1/src/modules/ecdh/main_impl.h
+    secp256k1/src/modules/ecdh/Makefile.am.include
+    secp256k1/src/modules/ecdh/tests_impl.h
+    secp256k1/src/modules/recovery/main_impl.h
+    secp256k1/src/modules/recovery/Makefile.am.include
+    secp256k1/src/modules/recovery/tests_impl.h
+    secp256k1/src/num.h
+    secp256k1/src/num_gmp.h
     secp256k1/src/num_gmp_impl.h
-    secp256k1/src/ecmult.h
+    secp256k1/src/num_impl.h
+    secp256k1/src/scalar.h
+    secp256k1/src/scalar_4x64.h
+    secp256k1/src/scalar_4x64_impl.h
+    secp256k1/src/scalar_8x32.h
+    secp256k1/src/scalar_8x32_impl.h
+    secp256k1/src/scalar_impl.h
+    secp256k1/src/scalar_low.h
+    secp256k1/src/scalar_low_impl.h
+    secp256k1/src/secp256k1.c
+    secp256k1/src/testrand.h
+    secp256k1/src/testrand_impl.h
+    secp256k1/src/tests.c
+    secp256k1/src/tests_exhaustive.c
+    secp256k1/src/util.h
     secp256k1/TODO
-    secp256k1/include/secp256k1_recovery.h
-    secp256k1/include/secp256k1.h
-    secp256k1/include/secp256k1_ecdh.h
-    secp256k1/contrib/lax_der_parsing.c
-    secp256k1/contrib/lax_der_privatekey_parsing.c
-    secp256k1/contrib/lax_der_privatekey_parsing.h
-    secp256k1/contrib/lax_der_parsing.h
-    secp256k1/libsecp256k1.pc.in
-    secp256k1/sage/group_prover.sage
-    secp256k1/sage/weierstrass_prover.sage
-    secp256k1/sage/secp256k1.sage
-    secp256k1/build-aux/m4/ax_jni_include_dir.m4
-    secp256k1/build-aux/m4/ax_prog_cc_for_build.m4
-    secp256k1/build-aux/m4/bitcoin_secp.m4
-    README.md
-cabal-version:       >=1.10
 
-library
-    hs-source-dirs:      src
-    exposed-modules:     Crypto.Secp256k1
-                       , Crypto.Secp256k1.Internal
-    build-depends:       base >= 4.8 && < 5
-                       , base16-bytestring
-                       , bytestring
-                       , cereal
-                       , entropy
-                       , mtl
-                       , QuickCheck
-                       , string-conversions
-    default-language:    Haskell2010
-    ghc-options:         -Wall
-    cc-options:          -DHAVE_CONFIG_H
-    c-sources:           secp256k1/src/secp256k1.c, secp256k1/contrib/lax_der_parsing.c, secp256k1/contrib/lax_der_privatekey_parsing.c
-    include-dirs:        secp256k1, secp256k1/contrib, secp256k1/include
-
-test-suite secp256k1-test
-    type:                exitcode-stdio-1.0
-    hs-source-dirs:      test
-    main-is:             Spec.hs
-    other-modules:       Crypto.Secp256k1.Tests
-                       , Crypto.Secp256k1.Internal.Tests
-    build-depends:       base
-                       , base16-bytestring
-                       , bytestring
-                       , cereal
-                       , cryptohash
-                       , entropy
-                       , HUnit
-                       , mtl
-                       , QuickCheck
-                       , secp256k1
-                       , string-conversions
-                       , test-framework
-                       , test-framework-hunit
-                       , test-framework-quickcheck2
-    ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N
-    default-language:    Haskell2010
-
 source-repository head
-    type:     git
-    location: https://github.com/haskoin/secp256k1-haskell.git
+  type: git
+  location: https://github.com/haskoin/secp256k1-haskell.git
 
-custom-setup
-    setup-depends:       base >= 4.8 && < 5
-                       , Cabal >= 1.10
+library
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  cc-options: -DHAVE_CONFIG_H
+  include-dirs:
+      secp256k1
+      secp256k1/contrib
+      secp256k1/include
+  c-sources:
+      secp256k1/contrib/lax_der_parsing.c
+      secp256k1/contrib/lax_der_privatekey_parsing.c
+      secp256k1/src/secp256k1.c
+  build-depends:
+      HUnit
+    , QuickCheck
+    , base >=4.8 && <5
+    , base16-bytestring
+    , bytestring
+    , cereal
+    , cryptohash
+    , entropy
+    , hspec
+    , mtl
+    , string-conversions
+  exposed-modules:
+      Crypto.Secp256k1
+      Crypto.Secp256k1.Internal
+  other-modules:
+      Paths_secp256k1
+  default-language: Haskell2010
 
+test-suite spec
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Crypto.Secp256k1.InternalSpec
+      Crypto.Secp256k1Spec
+      Paths_secp256k1
+  hs-source-dirs:
+      test
+  build-depends:
+      HUnit
+    , QuickCheck
+    , base >=4.8 && <5
+    , base16-bytestring
+    , bytestring
+    , cereal
+    , cryptohash
+    , entropy
+    , hspec ==2.*
+    , mtl
+    , secp256k1
+    , string-conversions
+  default-language: Haskell2010
+  build-tool-depends: hspec-discover:hspec-discover == 2.*
diff --git a/src/Crypto/Secp256k1.hs b/src/Crypto/Secp256k1.hs
--- a/src/Crypto/Secp256k1.hs
+++ b/src/Crypto/Secp256k1.hs
@@ -88,7 +88,7 @@
 
 instance Read PubKey where
     readPrec = parens $ do
-        Ident "PubKey" <- lexP
+        Ident "fromString" <- lexP
         String str <- lexP
         maybe pfail return $ importPubKey =<< decodeHex str
 
@@ -98,11 +98,11 @@
 
 instance Show PubKey where
     showsPrec d k = showParen (d > 10) $
-        showString "PubKey " . shows (B16.encode $ exportPubKey True k)
+        showString "fromString " . shows (B16.encode $ exportPubKey True k)
 
 instance Read Msg where
     readPrec = parens $ do
-        Ident "Msg" <- lexP
+        Ident "fromString" <- lexP
         String str <- lexP
         maybe pfail return $ msg =<< decodeHex str
 
@@ -112,11 +112,11 @@
 
 instance Show Msg where
     showsPrec d m = showParen (d > 10) $
-        showString "Msg " . shows (B16.encode $ getMsg m)
+        showString "fromString " . shows (B16.encode $ getMsg m)
 
 instance Read Sig where
     readPrec = parens $ do
-        Ident "Sig" <- lexP
+        Ident "fromString" <- lexP
         String str <- lexP
         maybe pfail return $ importSig =<< decodeHex str
 
@@ -126,7 +126,7 @@
 
 instance Show Sig where
     showsPrec d s = showParen (d > 10) $
-        showString "Sig " . shows (B16.encode $ exportSig s)
+        showString "fromString " . shows (B16.encode $ exportSig s)
 
 recSigFromString :: String -> Maybe RecSig
 recSigFromString str = do
@@ -136,7 +136,7 @@
 
 instance Read RecSig where
     readPrec = parens $ do
-        Ident "RecSig" <- lexP
+        Ident "fromString" <- lexP
         String str <- lexP
         maybe pfail return $ recSigFromString str
 
@@ -147,11 +147,11 @@
 
 instance Show RecSig where
     showsPrec d s = showParen (d > 10) $
-        showString "RecSig " . shows (B16.encode . encode $ exportCompactRecSig s)
+        showString "fromString " . shows (B16.encode . encode $ exportCompactRecSig s)
 
 instance Read SecKey where
     readPrec = parens $ do
-        Ident "SecKey" <- lexP
+        Ident "fromString" <- lexP
         String str <- lexP
         maybe pfail return $ secKey =<< decodeHex str
 
@@ -161,11 +161,11 @@
 
 instance Show SecKey where
     showsPrec d k = showParen (d > 10) $
-        showString "SecKey " . shows (B16.encode $ getSecKey k)
+        showString "fromString " . shows (B16.encode $ getSecKey k)
 
 instance Read Tweak where
     readPrec = parens $ do
-        Ident "Tweak" <- lexP
+        Ident "fromString" <- lexP
         String str <- lexP
         maybe pfail return $ tweak =<< decodeHex str
 
@@ -175,7 +175,7 @@
 
 instance Show Tweak where
     showsPrec d k = showParen (d > 10) $
-        showString "Tweak " . shows (B16.encode $ getTweak k)
+        showString "fromString " . shows (B16.encode $ getTweak k)
 
 instance Eq PubKey where
     fp1 == fp2 = getPubKey fp1 == getPubKey fp2
diff --git a/test/Crypto/Secp256k1/Internal/Tests.hs b/test/Crypto/Secp256k1/Internal/Tests.hs
deleted file mode 100644
--- a/test/Crypto/Secp256k1/Internal/Tests.hs
+++ /dev/null
@@ -1,437 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module Crypto.Secp256k1.Internal.Tests (tests) where
-
-import           Control.Monad
-import           Control.Monad.Trans
-import           Crypto.Secp256k1.Internal
-import           Data.ByteString                (ByteString, packCStringLen,
-                                                 useAsCStringLen)
-import qualified Data.ByteString.Base16         as B16
-import           Data.ByteString.Short          (toShort)
-import           Foreign
-import           System.Entropy
-import           Test.Framework                 (Test, testGroup)
-import           Test.Framework.Providers.HUnit (testCase)
-import           Test.HUnit                     (Assertion, assertBool,
-                                                 assertEqual)
-
-tests :: [Test]
-tests =
-    [ testGroup "Housekeeping"
-        [ testCase "Create context"           createContextTest
-        , testCase "Randomize context"        randomizeContextTest
-        , testCase "Clone context"            cloneContextTest
-        ]
-    , testGroup "Serialization"
-        [ testCase "Parse public key"         ecPubkeyParseTest
-        , testCase "Serialize public key"     ecPubKeySerializeTest
-        , testCase "Storable public key"      pubkeyStorableTest
-        , testCase "Storable signature"       signatureStorableTest
-        , testCase "Parse DER signature"      ecdsaSignatureParseDerTest
-        , testCase "Lax parse DER signature"  laxDerParseTest
-        , testCase "Serialize DER signature"  ecdsaSignatureSerializeDerTest
-        ]
-    , testGroup "Signatures"
-        [ testCase "ECDSA verify"             ecdsaVerifyTest
-        -- TODO:
-        -- , testCase "RFC6979 nonce function"   nonce_function_rfc6979_test
-        , testCase "ECDSA sign"               ecdsaSignTest
-        ]
-    , testGroup "Secret keys"
-        [ testCase "Verify secret key"        ecSecKeyVerifyTest
-        , testCase "Create public key"        ecPubkeyCreateTest
-        , testCase "Tweak add secret key"     ecSecKeyTweakAddTest
-        , testCase "Tweak mult. secret key"   ecSecKeyTweakMulTest
-        ]
-    , testGroup "Public keys"
-        [ testCase "Tweak add public key"     ecPubKeyTweakAddTest
-        , testCase "Tweak mult. public key"   ecPubKeyTweakMulTest
-        , testCase "Combine public keys"      ecPubKeyCombineTest
-        ]
-    ]
-
-withEntropy :: (Ptr Seed32 -> IO a) -> IO a
-withEntropy f =
-    getEntropy 32 >>= \e ->
-    alloca $ \s ->
-    poke s (Seed32 (toShort e)) >> 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
-        alloca $ \pubkey ->
-            ecPubKeyParse x pubkey (castPtr i) (fromIntegral il)
-    assertBool "parsed public key" (isSuccess ret)
-  where
-    der = fst $ B16.decode
-        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
-
-ecPubKeySerializeTest :: Assertion
-ecPubKeySerializeTest = do
-    (ret, dec) <- liftIO $ useAsCStringLen der $ \(i, il) ->
-        alloca $ \k -> alloca $ \ol -> allocaBytes 72 $ \o -> do
-        poke ol 72
-        x <- contextCreate verify
-        ret1 <-
-            ecPubKeyParse x k (castPtr i) (fromIntegral 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 = fst $ B16.decode
-        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
-
-pubkeyStorableTest :: Assertion
-pubkeyStorableTest = do
-    (pk1, pk2, dec) <- liftIO $ useAsCStringLen der $ \(i, il) -> do
-        x <- contextCreate verify
-        pk1 <- alloca $ \pk -> do
-            ret <-
-                ecPubKeyParse x pk (castPtr i) (fromIntegral il)
-            unless (isSuccess ret) $ error "failed to parse pubkey"
-            peek pk
-        (pk2, dec) <- alloca $ \pk -> alloca $ \ol -> allocaBytes 72 $ \o -> do
-            poke ol 72
-            poke pk pk1
-            ret <-
-                ecPubKeySerialize x o ol pk compressed
-            unless (isSuccess ret) $ error "failed to serialize pubkey"
-            len <- fromIntegral <$> peek ol
-            dec <- packCStringLen (castPtr o, len)
-            pk2 <- peek pk
-            return (pk2, dec)
-        return (pk1, pk2, dec)
-    assertEqual "poke/peek public key" pk1 pk2
-    assertEqual "public key matches" der dec
-  where
-    der = fst $ B16.decode
-        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
-
-signatureStorableTest :: Assertion
-signatureStorableTest = do
-    (sig, ret) <- liftIO $ do
-        x <- contextCreate verify
-        g <- alloca $ \pc -> alloca $ \pg -> do
-            poke pc cpt
-            ret <- ecdsaSignatureParseCompact x pg (castPtr pc)
-            unless (isSuccess ret) $ error "failed to parse signature"
-            peek pg
-        alloca $ \pc -> alloca $ \pg -> do
-            poke pg g
-            ret <- ecdsaSignatureSerializeCompact x pc pg
-            c <- peek pc
-            return (c, ret)
-    assertBool "successful serialization" (isSuccess ret)
-    assertEqual "signatures match" cpt sig
-  where
-    cpt = CompactSig
-        (toShort $ fst $ B16.decode "f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37fb22")
-        (toShort $ fst $ B16.decode "6f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e3045")
-
-ecdsaSignatureParseDerTest :: Assertion
-ecdsaSignatureParseDerTest = do
-    ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do
-        x <- contextCreate verify
-        ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
-    assertBool "parsed signature successfully" $ isSuccess ret
-  where
-    der = fst $ B16.decode
-        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
-        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
-        \45"
-
-laxDerParseTest :: Assertion
-laxDerParseTest = do
-    ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do
-        x <- contextCreate verify
-        laxDerParse x s (castPtr d) (fromIntegral dl)
-    assertBool "parsed signature successfully" $ isSuccess ret
-  where
-    der = fst $ B16.decode
-        "30450220f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37fb\
-        \2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e3045"
-
-parseDer :: Ptr Ctx -> ByteString -> IO Sig64
-parseDer x bs = useAsCStringLen bs $ \(d, dl) -> alloca $ \s -> do
-    ret <- ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
-    unless (isSuccess ret) $ error "could not parse DER"
-    peek s
-
-ecdsaSignatureSerializeDerTest :: Assertion
-ecdsaSignatureSerializeDerTest = do
-    (ret, enc) <- liftIO $ do
-        x <- contextCreate verify
-        sig <- parseDer x der
-        alloca $ \s -> alloca $ \ol -> allocaBytes 72 $ \o -> do
-            poke ol 72
-            poke s sig
-            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 = fst $ B16.decode
-        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
-        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
-        \45"
-
-ecdsaVerifyTest :: Assertion
-ecdsaVerifyTest = do
-    ret <- liftIO $ do
-        x <- contextCreate verify
-        sig <- parseDer x der
-        pk <- useAsCStringLen pub $ \(p, pl) -> alloca $ \k -> do
-            ret <-
-                ecPubKeyParse x k (castPtr p) (fromIntegral pl)
-            unless (isSuccess ret) $ error "could not parse public key"
-            peek k
-        alloca $ \m -> alloca $ \k -> alloca $ \s -> do
-            poke m msg
-            poke k pk
-            poke s sig
-            ecdsaVerify x s m k
-    assertBool "signature valid" $ isSuccess ret
-  where
-    der = fst $ B16.decode
-        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
-        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
-        \45"
-    pub = fst $ B16.decode
-        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
-        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-    msg = Msg32 $ toShort $ fst $ B16.decode
-        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-
-signCtx :: IO (Ptr Ctx)
-signCtx = contextCreate sign >>= \c ->
-    withEntropy (contextRandomize c) >>= \r ->
-        unless (isSuccess r) (error "failed to randomize context") >> return c
-
-ecdsaSignTest :: Assertion
-ecdsaSignTest = do
-    (ret, sig) <- liftIO $ do
-        x <- signCtx
-        alloca $ \s -> alloca $ \m -> alloca $ \k -> alloca $ \ol ->
-            allocaBytes 72 $ \o -> do
-                poke ol 72
-                poke m msg
-                poke k key
-                ret1 <-
-                    -- TODO:
-                    -- ecdsaSign x s m k nonce_function_default nullPtr
-                    ecdsaSign x s m k nullFunPtr nullPtr
-                ret2 <- ecdsaSignatureSerializeDer x o ol s
-                unless (isSuccess ret2) $ error "could not serialize signature"
-                len <- peek ol
-                sig <- packCStringLen (castPtr o, fromIntegral len)
-                return (ret1, sig)
-    assertBool "successful signing" $ isSuccess ret
-    assertEqual "signature matches" sig der
-  where
-    msg = Msg32 $ toShort $ fst $ B16.decode
-        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    key = SecKey32 $ toShort $ fst $ B16.decode
-        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-    der = fst $ B16.decode
-        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
-        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
-        \45"
-
-ecSecKeyVerifyTest :: Assertion
-ecSecKeyVerifyTest = do
-    ret <- liftIO $ alloca $ \k -> do
-        poke k key
-        x <- signCtx
-        ecSecKeyVerify x k
-    assertBool "valid secret key" $ isSuccess ret
-  where
-    key = SecKey32 $ toShort $ fst $ B16.decode
-        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-
-ecPubkeyCreateTest :: Assertion
-ecPubkeyCreateTest = do
-    (ret, pk) <- liftIO $ alloca $ \p -> alloca $ \k -> do
-        poke k key
-        x <- signCtx
-        ret <- ecPubKeyCreate x p k
-        allocaBytes 65 $ \o -> alloca $ \ol -> do
-            poke ol 65
-            rets <- ecPubKeySerialize
-                x o ol p uncompressed
-            unless (isSuccess rets) $ error "failed to serialive public key"
-            len <- fromIntegral <$> peek ol
-            pk <- packCStringLen (castPtr o, len)
-            return (ret, pk)
-    assertBool "successful pubkey creation" $ isSuccess ret
-    assertEqual "public key matches" pub pk
-  where
-    key = SecKey32 $ toShort $ fst $ B16.decode
-        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-    pub = fst $ B16.decode
-        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
-        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-
-ecSecKeyTweakAddTest :: Assertion
-ecSecKeyTweakAddTest = do
-    (ret, tweaked) <- liftIO $ do
-        x <- signCtx
-        alloca $ \w -> alloca $ \k -> do
-            poke w tweak
-            poke k key
-            ret <- ecSecKeyTweakAdd x k w
-            tweaked <- peek k
-            return (ret, tweaked)
-    assertBool "successful secret key tweak" $ isSuccess ret
-    assertEqual "tweaked keys match" expected tweaked
-  where
-    key = SecKey32 $ toShort $ fst $ B16.decode
-        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-    tweak = Tweak32 $ toShort $ fst $ B16.decode
-        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    expected = SecKey32 $ toShort $ fst $ B16.decode
-        "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
-
-ecSecKeyTweakMulTest :: Assertion
-ecSecKeyTweakMulTest = do
-    (ret, tweaked) <- liftIO $ do
-        x <- contextCreate sign
-        retr <- withEntropy $ contextRandomize x
-        unless (isSuccess retr) $ error "failed to randomize context"
-        alloca $ \w -> alloca $ \k -> do
-            poke w tweak
-            poke k key
-            ret <- ecSecKeyTweakMul x k w
-            tweaked <- peek k
-            return (ret, tweaked)
-    assertBool "successful secret key tweak" $ isSuccess ret
-    assertEqual "tweaked keys match" expected tweaked
-  where
-    key = SecKey32 $ toShort $ fst $ B16.decode
-        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-    tweak = Tweak32 $ toShort $ fst $ B16.decode
-        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    expected = SecKey32 $ toShort $ fst $ B16.decode
-        "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
-
-serializeKey :: Ptr 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 :: Ptr Ctx -> ByteString -> IO PubKey64
-parseKey x bs = alloca $ \p -> useAsCStringLen bs $ \(d, dl) -> do
-    ret <- ecPubKeyParse x p (castPtr d) (fromIntegral dl)
-    unless (isSuccess ret) $ error "could not parse public key"
-    peek p
-
-ecPubKeyTweakAddTest :: Assertion
-ecPubKeyTweakAddTest = do
-    (ret, tweaked) <- liftIO $ do
-        x <- contextCreate verify
-        pk <- parseKey x pub
-        alloca $ \w -> alloca $ \p -> do
-            poke w tweak
-            poke p pk
-            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 = fst $ B16.decode
-        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
-        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-    tweak = Tweak32 $ toShort $ fst $ B16.decode
-        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    expected = fst $ B16.decode
-        "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
-        \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
-
-ecPubKeyTweakMulTest :: Assertion
-ecPubKeyTweakMulTest = do
-    (ret, tweaked) <- liftIO $ do
-        x <- contextCreate verify
-        pk <- parseKey x pub
-        alloca $ \w -> alloca $ \p -> do
-            poke w tweak
-            poke p pk
-            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 = fst $ B16.decode
-        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
-        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-    tweak = Tweak32 $ toShort $ fst $ B16.decode
-        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    expected = fst $ B16.decode
-        "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\
-        \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
-
-ecPubKeyCombineTest :: Assertion
-ecPubKeyCombineTest = do
-    (ret, com) <- liftIO $ alloca $ \p1 -> alloca $ \p2 -> alloca $ \p3 ->
-        allocaArray 3 $ \a -> alloca $ \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 = useAsCStringLen pub $ \(d, dl) -> do
-        ret <- ecPubKeyParse x p (castPtr d) (fromIntegral dl)
-        unless (isSuccess ret) $ error "could not parse public key"
-    pub1 = fst $ B16.decode
-        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
-        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-    pub2 = fst $ B16.decode
-        "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\
-        \77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
-    pub3 = fst $ B16.decode
-        "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\
-        \d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
-    expected = fst $ B16.decode
-        "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
-        \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
diff --git a/test/Crypto/Secp256k1/InternalSpec.hs b/test/Crypto/Secp256k1/InternalSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Crypto/Secp256k1/InternalSpec.hs
@@ -0,0 +1,429 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Crypto.Secp256k1.InternalSpec (spec) where
+
+import           Control.Monad
+import           Control.Monad.Trans
+import           Crypto.Secp256k1.Internal
+import           Data.ByteString           (ByteString, packCStringLen,
+                                            useAsCStringLen)
+import qualified Data.ByteString.Base16    as B16
+import           Data.ByteString.Short     (toShort)
+import           Foreign
+import           System.Entropy
+import           Test.Hspec
+import           Test.HUnit                (Assertion, assertBool, assertEqual)
+
+spec :: Spec
+spec = do
+    describe "housekeeping" $ do
+        it "creates context" createContextTest
+        it "randomizez context" randomizeContextTest
+        it "clones context" cloneContextTest
+    describe "serialization" $ do
+        it "parses public key" ecPubkeyParseTest
+        it "serializes public key" ecPubKeySerializeTest
+        it "handles storable public key" pubkeyStorableTest
+        it "handles storable signature" signatureStorableTest
+        it "parses DER signature" ecdsaSignatureParseDerTest
+        it "parses lax DER signature" laxDerParseTest
+        it "serializes DER signature" ecdsaSignatureSerializeDerTest
+    describe "signatures" $ do
+        it "verifies signature" ecdsaVerifyTest
+        -- TODO:
+        -- , testCase "RFC6979 nonce function"   nonce_function_rfc6979_test
+        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 ->
+    alloca $ \s ->
+    poke s (Seed32 (toShort e)) >> 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
+        alloca $ \pubkey ->
+            ecPubKeyParse x pubkey (castPtr i) (fromIntegral il)
+    assertBool "parsed public key" (isSuccess ret)
+  where
+    der = fst $ B16.decode
+        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
+
+ecPubKeySerializeTest :: Assertion
+ecPubKeySerializeTest = do
+    (ret, dec) <- liftIO $ useAsCStringLen der $ \(i, il) ->
+        alloca $ \k -> alloca $ \ol -> allocaBytes 72 $ \o -> do
+        poke ol 72
+        x <- contextCreate verify
+        ret1 <-
+            ecPubKeyParse x k (castPtr i) (fromIntegral 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 = fst $ B16.decode
+        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
+
+pubkeyStorableTest :: Assertion
+pubkeyStorableTest = do
+    (pk1, pk2, dec) <- liftIO $ useAsCStringLen der $ \(i, il) -> do
+        x <- contextCreate verify
+        pk1 <- alloca $ \pk -> do
+            ret <-
+                ecPubKeyParse x pk (castPtr i) (fromIntegral il)
+            unless (isSuccess ret) $ error "failed to parse pubkey"
+            peek pk
+        (pk2, dec) <- alloca $ \pk -> alloca $ \ol -> allocaBytes 72 $ \o -> do
+            poke ol 72
+            poke pk pk1
+            ret <-
+                ecPubKeySerialize x o ol pk compressed
+            unless (isSuccess ret) $ error "failed to serialize pubkey"
+            len <- fromIntegral <$> peek ol
+            dec <- packCStringLen (castPtr o, len)
+            pk2 <- peek pk
+            return (pk2, dec)
+        return (pk1, pk2, dec)
+    assertEqual "poke/peek public key" pk1 pk2
+    assertEqual "public key matches" der dec
+  where
+    der = fst $ B16.decode
+        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
+
+signatureStorableTest :: Assertion
+signatureStorableTest = do
+    (sig, ret) <- liftIO $ do
+        x <- contextCreate verify
+        g <- alloca $ \pc -> alloca $ \pg -> do
+            poke pc cpt
+            ret <- ecdsaSignatureParseCompact x pg (castPtr pc)
+            unless (isSuccess ret) $ error "failed to parse signature"
+            peek pg
+        alloca $ \pc -> alloca $ \pg -> do
+            poke pg g
+            ret <- ecdsaSignatureSerializeCompact x pc pg
+            c <- peek pc
+            return (c, ret)
+    assertBool "successful serialization" (isSuccess ret)
+    assertEqual "signatures match" cpt sig
+  where
+    cpt = CompactSig
+        (toShort $ fst $ B16.decode "f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37fb22")
+        (toShort $ fst $ B16.decode "6f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e3045")
+
+ecdsaSignatureParseDerTest :: Assertion
+ecdsaSignatureParseDerTest = do
+    ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do
+        x <- contextCreate verify
+        ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
+    assertBool "parsed signature successfully" $ isSuccess ret
+  where
+    der = fst $ B16.decode
+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
+        \45"
+
+laxDerParseTest :: Assertion
+laxDerParseTest = do
+    ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do
+        x <- contextCreate verify
+        laxDerParse x s (castPtr d) (fromIntegral dl)
+    assertBool "parsed signature successfully" $ isSuccess ret
+  where
+    der = fst $ B16.decode
+        "30450220f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37fb\
+        \2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e3045"
+
+parseDer :: Ptr Ctx -> ByteString -> IO Sig64
+parseDer x bs = useAsCStringLen bs $ \(d, dl) -> alloca $ \s -> do
+    ret <- ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
+    unless (isSuccess ret) $ error "could not parse DER"
+    peek s
+
+ecdsaSignatureSerializeDerTest :: Assertion
+ecdsaSignatureSerializeDerTest = do
+    (ret, enc) <- liftIO $ do
+        x <- contextCreate verify
+        sig <- parseDer x der
+        alloca $ \s -> alloca $ \ol -> allocaBytes 72 $ \o -> do
+            poke ol 72
+            poke s sig
+            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 = fst $ B16.decode
+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
+        \45"
+
+ecdsaVerifyTest :: Assertion
+ecdsaVerifyTest = do
+    ret <- liftIO $ do
+        x <- contextCreate verify
+        sig <- parseDer x der
+        pk <- useAsCStringLen pub $ \(p, pl) -> alloca $ \k -> do
+            ret <-
+                ecPubKeyParse x k (castPtr p) (fromIntegral pl)
+            unless (isSuccess ret) $ error "could not parse public key"
+            peek k
+        alloca $ \m -> alloca $ \k -> alloca $ \s -> do
+            poke m msg
+            poke k pk
+            poke s sig
+            ecdsaVerify x s m k
+    assertBool "signature valid" $ isSuccess ret
+  where
+    der = fst $ B16.decode
+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
+        \45"
+    pub = fst $ B16.decode
+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+    msg = Msg32 $ toShort $ fst $ B16.decode
+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+
+signCtx :: IO (Ptr Ctx)
+signCtx = contextCreate sign >>= \c ->
+    withEntropy (contextRandomize c) >>= \r ->
+        unless (isSuccess r) (error "failed to randomize context") >> return c
+
+ecdsaSignTest :: Assertion
+ecdsaSignTest = do
+    (ret, sig) <- liftIO $ do
+        x <- signCtx
+        alloca $ \s -> alloca $ \m -> alloca $ \k -> alloca $ \ol ->
+            allocaBytes 72 $ \o -> do
+                poke ol 72
+                poke m msg
+                poke k key
+                ret1 <-
+                    -- TODO:
+                    -- ecdsaSign x s m k nonce_function_default nullPtr
+                    ecdsaSign x s m k nullFunPtr nullPtr
+                ret2 <- ecdsaSignatureSerializeDer x o ol s
+                unless (isSuccess ret2) $ error "could not serialize signature"
+                len <- peek ol
+                sig <- packCStringLen (castPtr o, fromIntegral len)
+                return (ret1, sig)
+    assertBool "successful signing" $ isSuccess ret
+    assertEqual "signature matches" sig der
+  where
+    msg = Msg32 $ toShort $ fst $ B16.decode
+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+    key = SecKey32 $ toShort $ fst $ B16.decode
+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+    der = fst $ B16.decode
+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
+        \45"
+
+ecSecKeyVerifyTest :: Assertion
+ecSecKeyVerifyTest = do
+    ret <- liftIO $ alloca $ \k -> do
+        poke k key
+        x <- signCtx
+        ecSecKeyVerify x k
+    assertBool "valid secret key" $ isSuccess ret
+  where
+    key = SecKey32 $ toShort $ fst $ B16.decode
+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+
+ecPubkeyCreateTest :: Assertion
+ecPubkeyCreateTest = do
+    (ret, pk) <- liftIO $ alloca $ \p -> alloca $ \k -> do
+        poke k key
+        x <- signCtx
+        ret <- ecPubKeyCreate x p k
+        allocaBytes 65 $ \o -> alloca $ \ol -> do
+            poke ol 65
+            rets <- ecPubKeySerialize
+                x o ol p uncompressed
+            unless (isSuccess rets) $ error "failed to serialive public key"
+            len <- fromIntegral <$> peek ol
+            pk <- packCStringLen (castPtr o, len)
+            return (ret, pk)
+    assertBool "successful pubkey creation" $ isSuccess ret
+    assertEqual "public key matches" pub pk
+  where
+    key = SecKey32 $ toShort $ fst $ B16.decode
+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+    pub = fst $ B16.decode
+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+
+ecSecKeyTweakAddTest :: Assertion
+ecSecKeyTweakAddTest = do
+    (ret, tweaked) <- liftIO $ do
+        x <- signCtx
+        alloca $ \w -> alloca $ \k -> do
+            poke w tweak
+            poke k key
+            ret <- ecSecKeyTweakAdd x k w
+            tweaked <- peek k
+            return (ret, tweaked)
+    assertBool "successful secret key tweak" $ isSuccess ret
+    assertEqual "tweaked keys match" expected tweaked
+  where
+    key = SecKey32 $ toShort $ fst $ B16.decode
+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+    tweak = Tweak32 $ toShort $ fst $ B16.decode
+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+    expected = SecKey32 $ toShort $ fst $ B16.decode
+        "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
+
+ecSecKeyTweakMulTest :: Assertion
+ecSecKeyTweakMulTest = do
+    (ret, tweaked) <- liftIO $ do
+        x <- contextCreate sign
+        retr <- withEntropy $ contextRandomize x
+        unless (isSuccess retr) $ error "failed to randomize context"
+        alloca $ \w -> alloca $ \k -> do
+            poke w tweak
+            poke k key
+            ret <- ecSecKeyTweakMul x k w
+            tweaked <- peek k
+            return (ret, tweaked)
+    assertBool "successful secret key tweak" $ isSuccess ret
+    assertEqual "tweaked keys match" expected tweaked
+  where
+    key = SecKey32 $ toShort $ fst $ B16.decode
+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+    tweak = Tweak32 $ toShort $ fst $ B16.decode
+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+    expected = SecKey32 $ toShort $ fst $ B16.decode
+        "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
+
+serializeKey :: Ptr 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 :: Ptr Ctx -> ByteString -> IO PubKey64
+parseKey x bs = alloca $ \p -> useAsCStringLen bs $ \(d, dl) -> do
+    ret <- ecPubKeyParse x p (castPtr d) (fromIntegral dl)
+    unless (isSuccess ret) $ error "could not parse public key"
+    peek p
+
+ecPubKeyTweakAddTest :: Assertion
+ecPubKeyTweakAddTest = do
+    (ret, tweaked) <- liftIO $ do
+        x <- contextCreate verify
+        pk <- parseKey x pub
+        alloca $ \w -> alloca $ \p -> do
+            poke w tweak
+            poke p pk
+            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 = fst $ B16.decode
+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+    tweak = Tweak32 $ toShort $ fst $ B16.decode
+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+    expected = fst $ B16.decode
+        "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
+        \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
+
+ecPubKeyTweakMulTest :: Assertion
+ecPubKeyTweakMulTest = do
+    (ret, tweaked) <- liftIO $ do
+        x <- contextCreate verify
+        pk <- parseKey x pub
+        alloca $ \w -> alloca $ \p -> do
+            poke w tweak
+            poke p pk
+            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 = fst $ B16.decode
+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+    tweak = Tweak32 $ toShort $ fst $ B16.decode
+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+    expected = fst $ B16.decode
+        "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\
+        \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
+
+ecPubKeyCombineTest :: Assertion
+ecPubKeyCombineTest = do
+    (ret, com) <- liftIO $ alloca $ \p1 -> alloca $ \p2 -> alloca $ \p3 ->
+        allocaArray 3 $ \a -> alloca $ \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 = useAsCStringLen pub $ \(d, dl) -> do
+        ret <- ecPubKeyParse x p (castPtr d) (fromIntegral dl)
+        unless (isSuccess ret) $ error "could not parse public key"
+    pub1 = fst $ B16.decode
+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+    pub2 = fst $ B16.decode
+        "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\
+        \77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
+    pub3 = fst $ B16.decode
+        "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\
+        \d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
+    expected = fst $ B16.decode
+        "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
+        \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
diff --git a/test/Crypto/Secp256k1/Tests.hs b/test/Crypto/Secp256k1/Tests.hs
deleted file mode 100644
--- a/test/Crypto/Secp256k1/Tests.hs
+++ /dev/null
@@ -1,252 +0,0 @@
-module Crypto.Secp256k1.Tests (tests) where
-
-import           Crypto.Secp256k1
-import           Data.Serialize
-import qualified Data.ByteString.Base16               as B16
-import qualified Data.ByteString.Char8                as B8
-import           Data.Maybe                           (fromMaybe)
-import           Data.String                          (fromString)
-import           Data.String.Conversions              (cs)
-import           Test.Framework                       (Test, testGroup)
-import           Test.Framework.Providers.HUnit       (testCase)
-import           Test.Framework.Providers.QuickCheck2 (testProperty)
-import           Test.HUnit                           (Assertion, assertEqual)
-import           Test.QuickCheck                      (Property, (==>))
-
-tests :: [Test]
-tests =
-    [ testGroup "Signing"
-        [ testProperty "Signing messages" signMsgTest
-        , testProperty "Recoverably signing messages" signRecMsgTest
-        , testProperty "Bad signatures" badSignatureTest
-        , testProperty "Bad recoverable signatures" badRecSignatureTest
-        , testProperty "Normalize signatures" normalizeSigTest
-        , testProperty "Recover public keys" recoverTest
-        , testProperty "Bad recover public keys" badRecoverTest
-        ]
-    , testGroup "Serialization"
-        [ testProperty "Serialize public key" serializePubKeyTest
-        , testProperty "Serialize DER signature" serializeSigTest
-        , testProperty "Serialize lax DER signature" serializeLaxSigTest
-        , testProperty "Serialize compact signature" serializeCompactSigTest
-        , testProperty "Serialize compact recoverable signature" serializeCompactRecSigTest
-        , testProperty "Serialize secret key" serializeSecKeyTest
-        , testProperty "Show/Read public key" (showRead :: PubKey -> Bool)
-        , testProperty "Show/Read secret key" (showRead :: SecKey -> Bool)
-        , testProperty "Show/Read tweak" (showReadTweak :: SecKey -> Bool)
-        , testProperty "Show/Read signature" (showReadSig :: (SecKey, Msg) -> Bool)
-        , testProperty "Show/Read recoverable signature" (showReadRecSig :: (SecKey, Msg) -> Bool)
-        , testProperty "Show/Read message" (showRead :: Msg -> Bool)
-        , testProperty "String public key" isStringPubKey
-        , testProperty "String secret key" isStringSecKey
-        , testProperty "String signature" isStringSig
-        , testProperty "String recoverable signature" isStringRecSig
-        , testProperty "String message" isStringMsg
-        , testProperty "String tweak" isStringTweak
-        ]
-    , testGroup "Tweaks"
-        [ testCase "Tweak add secret key" tweakAddSecKeyTest
-        , testCase "Tweak multiply secret key" tweakMulSecKeyTest
-        , testCase "Tweak add public key" tweakAddPubKeyTest
-        , testCase "Tweak multiply public key" tweakMulPubKeyTest
-        , testCase "Combine public keys" combinePubKeyTest
-        ]
-    ]
-
-isStringPubKey :: (PubKey, Bool) -> Bool
-isStringPubKey (k, c) = k == fromString (cs hex) where
-    hex = B16.encode $ exportPubKey c k
-
-isStringSig :: (SecKey, Msg) -> Bool
-isStringSig (k, m) = g == fromString (cs hex) where
-    g = signMsg k m
-    hex = B16.encode $ exportSig g
-
-isStringRecSig :: (SecKey, Msg) -> Bool
-isStringRecSig (k, m) = g == fromString (cs hex) where
-    g = signRecMsg k m
-    hex = B16.encode . encode $ exportCompactRecSig g
-
-isStringMsg :: Msg -> Bool
-isStringMsg m = m == fromString (cs m') where
-    m' = B16.encode $ getMsg m
-
-isStringSecKey :: SecKey -> Bool
-isStringSecKey k = k == fromString (cs hex) where
-    hex = B16.encode $ getSecKey k
-
-isStringTweak :: SecKey -> Bool
-isStringTweak k = t == fromString (cs hex) where
-    t = fromMaybe e . tweak $ getSecKey k
-    hex = B16.encode $ 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
-
-showReadRecSig :: (SecKey, Msg) -> Bool
-showReadRecSig (k, m) = showRead recSig where
-    recSig = signRecMsg 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
-
-signRecMsgTest :: (Msg, SecKey) -> Bool
-signRecMsgTest (fm, fk) = verifySig fp fg fm where
-    fp = derivePubKey fk
-    fg = convertRecSig $ signRecMsg fk fm
-
-recoverTest :: (Msg, SecKey) -> Bool
-recoverTest (fm, fk) = recover fg fm == Just fp where
-    fp = derivePubKey fk
-    fg = signRecMsg fk fm
-
-badRecoverTest :: (Msg, SecKey, Msg) -> Property
-badRecoverTest (fm, fk, fm') =
-  fm' /= fm ==> fp' /= Nothing ==> fp' /= Just fp
-  where
-    fg  = signRecMsg fk fm
-    fp  = derivePubKey fk
-    fp' = recover fg fm'
-
-badSignatureTest :: (Msg, SecKey, PubKey) -> Bool
-badSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where
-    fg = signMsg fk fm
-
-badRecSignatureTest :: (Msg, SecKey, PubKey) -> Bool
-badRecSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where
-    fg = convertRecSig $ signRecMsg fk fm
-
-normalizeSigTest :: (Msg, SecKey) -> Bool
-normalizeSigTest (fm, fk) = not norm && sig == fg where
-    fg = signMsg fk fm
-    (sig, norm) = normalizeSig fg
-
-serializePubKeyTest :: (PubKey, Bool) -> Bool
-serializePubKeyTest (fp, b) =
-    case importPubKey $ exportPubKey b fp of
-        Just fp' -> fp == fp'
-        Nothing -> False
-
-serializeSigTest :: (Msg, SecKey) -> Bool
-serializeSigTest (fm, fk) =
-    case importSig $ exportSig fg of
-        Just fg' -> fg == fg'
-        Nothing -> False
-  where
-    fg = signMsg fk fm
-
-serializeLaxSigTest :: (Msg, SecKey) -> Bool
-serializeLaxSigTest (fm, fk) =
-    case laxImportSig $ exportSig fg of
-        Just fg' -> fg == fg'
-        Nothing -> False
-  where
-    fg = signMsg fk fm
-
-serializeCompactSigTest :: (Msg, SecKey) -> Bool
-serializeCompactSigTest (fm, fk) =
-    case importCompactSig $ exportCompactSig fg of
-        Just fg' -> fg == fg'
-        Nothing -> False
-  where
-    fg = signMsg fk fm
-
-serializeCompactRecSigTest :: (Msg, SecKey) -> Bool
-serializeCompactRecSigTest (fm, fk) =
-    case importCompactRecSig $ exportCompactRecSig fg of
-        Just fg' -> fg == fg'
-        Nothing -> False
-  where
-    fg = signRecMsg 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 $ fst $ B16.decode $ B8.pack
-            "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-        twk <- tweak $ fst $ B16.decode $ B8.pack
-            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-        tweakAddSecKey key twk
-    expected = secKey $ fst $ B16.decode $ B8.pack
-        "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
-
-tweakMulSecKeyTest :: Assertion
-tweakMulSecKeyTest =
-    assertEqual "tweaked keys match" expected tweaked
-  where
-    tweaked = do
-        key <- secKey $ fst $ B16.decode $ B8.pack
-            "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-        twk <- tweak $ fst $ B16.decode $ B8.pack
-            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-        tweakMulSecKey key twk
-    expected = secKey $ fst $ B16.decode $ B8.pack
-        "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
-
-tweakAddPubKeyTest :: Assertion
-tweakAddPubKeyTest =
-    assertEqual "tweaked keys match" expected tweaked
-  where
-    tweaked = do
-        pub <- importPubKey $ fst $ B16.decode $ B8.pack
-            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\
-            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-        twk <- tweak $ fst $ B16.decode $ B8.pack
-            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-        tweakAddPubKey pub twk
-    expected = importPubKey $ fst $ B16.decode $ B8.pack
-        "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
-        \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
-
-tweakMulPubKeyTest :: Assertion
-tweakMulPubKeyTest =
-    assertEqual "tweaked keys match" expected tweaked
-  where
-    tweaked = do
-        pub <- importPubKey $ fst $ B16.decode $ B8.pack
-            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\
-            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-        twk <- tweak $ fst $ B16.decode $ B8.pack
-            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-        tweakMulPubKey pub twk
-    expected = importPubKey $ fst $ B16.decode $ B8.pack
-        "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\
-        \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
-
-combinePubKeyTest :: Assertion
-combinePubKeyTest =
-    assertEqual "combined keys match" expected combined
-  where
-    combined = do
-        pub1 <- importPubKey $ fst $ B16.decode $ B8.pack
-            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\
-            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-        pub2 <- importPubKey $ fst $ B16.decode $ B8.pack
-            "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d1\
-            \53f577dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
-        pub3 <- importPubKey $ fst $ B16.decode $ B8.pack
-            "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee\
-            \3febd908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
-        combinePubKeys [pub1, pub2, pub3]
-    expected = importPubKey $ fst $ B16.decode $ B8.pack
-        "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
-        \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
diff --git a/test/Crypto/Secp256k1Spec.hs b/test/Crypto/Secp256k1Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Crypto/Secp256k1Spec.hs
@@ -0,0 +1,252 @@
+module Crypto.Secp256k1Spec (spec) where
+
+import           Crypto.Secp256k1
+import qualified Data.ByteString.Base16  as B16
+import qualified Data.ByteString.Char8   as B8
+import           Data.Maybe              (fromMaybe)
+import           Data.Serialize
+import           Data.String             (fromString)
+import           Data.String.Conversions (cs)
+import           Test.Hspec
+import           Test.HUnit              (Assertion, assertEqual)
+import           Test.QuickCheck         (Property, property, (==>))
+
+spec :: Spec
+spec = do
+    describe "signatures" $ do
+        it "signs message" $ property $ signMsgTest
+        it "recovers key from signed message" $ property $ signRecMsgTest
+        it "detects bad signature" $ property $ badSignatureTest
+        it "detects bad recoverable signature" $ property $ badRecSignatureTest
+        it "normalizes signatures" $ property $ normalizeSigTest
+        it "recovers public keys" $ property $ recoverTest
+        it "Bad recover public keys" $ property $ badRecoverTest
+    describe "serialization" $ do
+        it "serializes public key" $ property $ serializePubKeyTest
+        it "serializes DER signature" $ property $ serializeSigTest
+        it "serializes lax DER signature" $ property $ serializeLaxSigTest
+        it "serializes compact signature" $ property $ serializeCompactSigTest
+        it "serializes compact recoverable signature" $
+            property $ serializeCompactRecSigTest
+        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 recoverable signature" $
+            property $ (showReadRecSig :: (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 recoverable signature from string" $ property $ isStringRecSig
+        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
+
+isStringPubKey :: (PubKey, Bool) -> Bool
+isStringPubKey (k, c) = k == fromString (cs hex) where
+    hex = B16.encode $ exportPubKey c k
+
+isStringSig :: (SecKey, Msg) -> Bool
+isStringSig (k, m) = g == fromString (cs hex) where
+    g = signMsg k m
+    hex = B16.encode $ exportSig g
+
+isStringRecSig :: (SecKey, Msg) -> Bool
+isStringRecSig (k, m) = g == fromString (cs hex) where
+    g = signRecMsg k m
+    hex = B16.encode . encode $ exportCompactRecSig g
+
+isStringMsg :: Msg -> Bool
+isStringMsg m = m == fromString (cs m') where
+    m' = B16.encode $ getMsg m
+
+isStringSecKey :: SecKey -> Bool
+isStringSecKey k = k == fromString (cs hex) where
+    hex = B16.encode $ getSecKey k
+
+isStringTweak :: SecKey -> Bool
+isStringTweak k = t == fromString (cs hex) where
+    t = fromMaybe e . tweak $ getSecKey k
+    hex = B16.encode $ 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
+
+showReadRecSig :: (SecKey, Msg) -> Bool
+showReadRecSig (k, m) = showRead recSig where
+    recSig = signRecMsg 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
+
+signRecMsgTest :: (Msg, SecKey) -> Bool
+signRecMsgTest (fm, fk) = verifySig fp fg fm where
+    fp = derivePubKey fk
+    fg = convertRecSig $ signRecMsg fk fm
+
+recoverTest :: (Msg, SecKey) -> Bool
+recoverTest (fm, fk) = recover fg fm == Just fp where
+    fp = derivePubKey fk
+    fg = signRecMsg fk fm
+
+badRecoverTest :: (Msg, SecKey, Msg) -> Property
+badRecoverTest (fm, fk, fm') =
+  fm' /= fm ==> fp' /= Nothing ==> fp' /= Just fp
+  where
+    fg  = signRecMsg fk fm
+    fp  = derivePubKey fk
+    fp' = recover fg fm'
+
+badSignatureTest :: (Msg, SecKey, PubKey) -> Bool
+badSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where
+    fg = signMsg fk fm
+
+badRecSignatureTest :: (Msg, SecKey, PubKey) -> Bool
+badRecSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where
+    fg = convertRecSig $ signRecMsg fk fm
+
+normalizeSigTest :: (Msg, SecKey) -> Bool
+normalizeSigTest (fm, fk) = not norm && sig == fg where
+    fg = signMsg fk fm
+    (sig, norm) = normalizeSig fg
+
+serializePubKeyTest :: (PubKey, Bool) -> Bool
+serializePubKeyTest (fp, b) =
+    case importPubKey $ exportPubKey b fp of
+        Just fp' -> fp == fp'
+        Nothing  -> False
+
+serializeSigTest :: (Msg, SecKey) -> Bool
+serializeSigTest (fm, fk) =
+    case importSig $ exportSig fg of
+        Just fg' -> fg == fg'
+        Nothing  -> False
+  where
+    fg = signMsg fk fm
+
+serializeLaxSigTest :: (Msg, SecKey) -> Bool
+serializeLaxSigTest (fm, fk) =
+    case laxImportSig $ exportSig fg of
+        Just fg' -> fg == fg'
+        Nothing  -> False
+  where
+    fg = signMsg fk fm
+
+serializeCompactSigTest :: (Msg, SecKey) -> Bool
+serializeCompactSigTest (fm, fk) =
+    case importCompactSig $ exportCompactSig fg of
+        Just fg' -> fg == fg'
+        Nothing  -> False
+  where
+    fg = signMsg fk fm
+
+serializeCompactRecSigTest :: (Msg, SecKey) -> Bool
+serializeCompactRecSigTest (fm, fk) =
+    case importCompactRecSig $ exportCompactRecSig fg of
+        Just fg' -> fg == fg'
+        Nothing  -> False
+  where
+    fg = signRecMsg 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 $ fst $ B16.decode $ B8.pack
+            "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+        twk <- tweak $ fst $ B16.decode $ B8.pack
+            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        tweakAddSecKey key twk
+    expected = secKey $ fst $ B16.decode $ B8.pack
+        "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
+
+tweakMulSecKeyTest :: Assertion
+tweakMulSecKeyTest =
+    assertEqual "tweaked keys match" expected tweaked
+  where
+    tweaked = do
+        key <- secKey $ fst $ B16.decode $ B8.pack
+            "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
+        twk <- tweak $ fst $ B16.decode $ B8.pack
+            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        tweakMulSecKey key twk
+    expected = secKey $ fst $ B16.decode $ B8.pack
+        "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
+
+tweakAddPubKeyTest :: Assertion
+tweakAddPubKeyTest =
+    assertEqual "tweaked keys match" expected tweaked
+  where
+    tweaked = do
+        pub <- importPubKey $ fst $ B16.decode $ B8.pack
+            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\
+            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+        twk <- tweak $ fst $ B16.decode $ B8.pack
+            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        tweakAddPubKey pub twk
+    expected = importPubKey $ fst $ B16.decode $ B8.pack
+        "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
+        \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
+
+tweakMulPubKeyTest :: Assertion
+tweakMulPubKeyTest =
+    assertEqual "tweaked keys match" expected tweaked
+  where
+    tweaked = do
+        pub <- importPubKey $ fst $ B16.decode $ B8.pack
+            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\
+            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+        twk <- tweak $ fst $ B16.decode $ B8.pack
+            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
+        tweakMulPubKey pub twk
+    expected = importPubKey $ fst $ B16.decode $ B8.pack
+        "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\
+        \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
+
+combinePubKeyTest :: Assertion
+combinePubKeyTest =
+    assertEqual "combined keys match" expected combined
+  where
+    combined = do
+        pub1 <- importPubKey $ fst $ B16.decode $ B8.pack
+            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\
+            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
+        pub2 <- importPubKey $ fst $ B16.decode $ B8.pack
+            "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d1\
+            \53f577dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
+        pub3 <- importPubKey $ fst $ B16.decode $ B8.pack
+            "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee\
+            \3febd908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
+        combinePubKeys [pub1, pub2, pub3]
+    expected = importPubKey $ fst $ B16.decode $ B8.pack
+        "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
+        \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,8 +1,1 @@
-import Test.Framework (defaultMain)
-
--- Some simple signing tests
-import qualified Crypto.Secp256k1.Tests as T
-import qualified Crypto.Secp256k1.Internal.Tests as I
-
-main :: IO ()
-main = defaultMain $ T.tests ++ I.tests
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
