diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,14 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## 0.2.0
+### Added
+- Support for ECDH APIs.
+- Support for Schnorr APIs.
+
+### Removed
+- Enabling key recovery APIs need a flag.
+
 ## 0.1.8
 ### Added
 - Add missing `NFData` instances for some types.
diff --git a/secp256k1-haskell.cabal b/secp256k1-haskell.cabal
--- a/secp256k1-haskell.cabal
+++ b/secp256k1-haskell.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 89af02aeb2d034b30650caa8ac3da870deac12d246a8fa513ab1fdc3033461f5
+-- hash: b923e5c7c968e7e6740b791bf84e395fcf448a13a190d70959b199e94e9843c7
 
 name:           secp256k1-haskell
-version:        0.1.8
+version:        0.2.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
@@ -32,6 +32,16 @@
   manual: True
   default: False
 
+flag recovery
+  description: Enable signature key recovery APIs
+  manual: True
+  default: False
+
+flag schnorr
+  description: Enable BIP-340 (Schnorr) APIs
+  manual: True
+  default: False
+
 library
   exposed-modules:
       Crypto.Secp256k1
@@ -54,6 +64,10 @@
     , string-conversions
   if flag(ecdh)
     cpp-options: -DECDH
+  if flag(schnorr)
+    cpp-options: -DSCHNORR
+  if flag(recovery)
+    cpp-options: -DRECOVERY
   default-language: Haskell2010
 
 test-suite spec
diff --git a/src/Crypto/Secp256k1.hs b/src/Crypto/Secp256k1.hs
--- a/src/Crypto/Secp256k1.hs
+++ b/src/Crypto/Secp256k1.hs
@@ -3,8 +3,8 @@
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-|
 Module      : Crypto.Secp256k1
-License     : MIT
-Maintainer  : Jean-Pierre Rupp <root@haskoin.com>
+License     : UNLICENSE
+Maintainer  : Jean-Pierre Rupp <jprupp@protonmail.ch>
 Stability   : experimental
 Portability : POSIX
 
@@ -39,7 +39,8 @@
     , CompactSig(..)
     , exportCompactSig
     , importCompactSig
-    -- * Recoverable
+#ifdef RECOVERY
+    -- ** Recovery
     , RecSig
     , CompactRecSig(..)
     , importCompactRecSig
@@ -47,6 +48,7 @@
     , convertRecSig
     , signRecMsg
     , recover
+#endif
 
     -- * Addition & Multiplication
     , Tweak
@@ -57,11 +59,27 @@
     , tweakAddPubKey
     , tweakMulPubKey
     , combinePubKeys
+    , tweakNegate
 
 #ifdef ECDH
     -- * Diffie Hellman
     , ecdh
 #endif
+
+#ifdef SCHNORR
+    , XOnlyPubKey
+    , SchnorrSig
+    , signMsgSchnorr
+    , exportSchnorrSig
+    , importSchnorrSig
+    , exportXOnlyPubKey
+    , importXOnlyPubKey
+    , verifyMsgSchnorr
+    , deriveXOnlyPubKey
+    , schnorrTweakAddPubKey
+    , schnorrTweakAddSecKey
+    , testTweakXOnlyPubKey
+#endif
     ) where
 
 import           Control.Monad             (replicateM, unless, (<=<))
@@ -78,6 +96,7 @@
                                             allocaBytes, mallocForeignPtr,
                                             nullFunPtr, nullPtr, peek, poke,
                                             pokeArray, withForeignPtr)
+import           Foreign.C                 (CInt)
 import           System.IO.Unsafe          (unsafePerformIO)
 import           Test.QuickCheck           (Arbitrary (..),
                                             arbitraryBoundedRandom, suchThat)
@@ -93,6 +112,10 @@
 newtype SecKey = SecKey (ForeignPtr SecKey32)
 newtype Tweak = Tweak (ForeignPtr Tweak32)
 newtype RecSig = RecSig (ForeignPtr RecSig65)
+#ifdef SCHNORR
+newtype XOnlyPubKey = XOnlyPubKey (ForeignPtr XOnlyPubKey64)
+newtype SchnorrSig = SchnorrSig (ForeignPtr SchnorrSig64)
+#endif
 
 instance NFData PubKey where
     rnf (PubKey p) = p `seq` ()
@@ -161,6 +184,7 @@
 instance Show Sig where
     showsPrec _ = shows . B16.encode . exportSig
 
+#ifdef RECOVERY
 recSigFromString :: String -> Maybe RecSig
 recSigFromString str = do
     bs <- decodeHex str
@@ -182,6 +206,7 @@
 
 instance Show RecSig where
     showsPrec _ = shows . B16.encode . encode . exportCompactRecSig
+#endif
 
 instance Read SecKey where
     readPrec = parens $ do
@@ -222,8 +247,10 @@
 instance Eq Sig where
     fg1 == fg2 = exportCompactSig fg1 == exportCompactSig fg2
 
+#ifdef RECOVERY
 instance Eq RecSig where
     fg1 == fg2 = exportCompactRecSig fg1 == exportCompactRecSig fg2
+#endif
 
 instance Eq SecKey where
     fk1 == fk2 = getSecKey fk1 == getSecKey fk2
@@ -231,6 +258,50 @@
 instance Eq Tweak where
     ft1 == ft2 = getTweak ft1 == getTweak ft2
 
+#ifdef SCHNORR
+instance NFData SchnorrSig where
+    rnf (SchnorrSig p) = p `seq` ()
+
+instance Show SchnorrSig where
+    showsPrec _ = shows . B16.encode . exportSchnorrSig
+
+instance Read SchnorrSig where
+    readPrec = parens $ do
+        String str <- lexP
+        maybe pfail return $ importSchnorrSig =<< decodeHex str
+
+instance Eq SchnorrSig where
+    fg1 == fg2 = exportSchnorrSig fg1 == exportSchnorrSig fg2
+
+instance IsString SchnorrSig where
+    fromString = fromMaybe e . (importSchnorrSig <=< decodeHex) where
+        e = error "Could not decode Schnorr signature from hex string"
+
+instance Hashable SchnorrSig where
+    i `hashWithSalt` s = i `hashWithSalt` exportSchnorrSig s
+
+instance NFData XOnlyPubKey where
+    rnf (XOnlyPubKey p) = p `seq` ()
+
+instance Show XOnlyPubKey where
+    showsPrec _ = shows . B16.encode . exportXOnlyPubKey
+
+instance Read XOnlyPubKey where
+    readPrec = do
+        String str <- lexP
+        maybe pfail return $ importXOnlyPubKey =<< decodeHex str
+
+instance Eq XOnlyPubKey where
+    fp1 == fp2 = getXOnlyPubKey fp1 == getXOnlyPubKey fp2
+
+instance IsString XOnlyPubKey where
+    fromString = fromMaybe e . (importXOnlyPubKey <=< decodeHex) where
+        e = error "Could not decode public key from hex string"
+
+instance Hashable XOnlyPubKey where
+    i `hashWithSalt` k = i `hashWithSalt` exportXOnlyPubKey k
+#endif
+
 -- | Import 32-byte 'ByteString' as 'Msg'.
 msg :: ByteString -> Maybe Msg
 msg bs
@@ -365,6 +436,7 @@
     unless (isSuccess ret) $ error "could not compute public key"
     return $ PubKey fp
 
+
 -- | Add tweak to secret key.
 tweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey
 tweakAddSecKey (SecKey fk) (Tweak ft) = withContext $ \ctx ->
@@ -426,6 +498,7 @@
     pointers ps (PubKey fp : pubs') f =
         withForeignPtr fp $ \p -> pointers (p:ps) pubs' f
 
+#ifdef RECOVERY
 -- | Parse a compact ECDSA signature (64 bytes + recovery id).
 importCompactRecSig :: CompactRecSig -> Maybe RecSig
 importCompactRecSig cr =
@@ -479,7 +552,20 @@
         fp <- mallocForeignPtr
         ret <- withForeignPtr fp $ \pp -> ecdsaRecover ctx pp prg pm
         if isSuccess ret then return $ Just $ PubKey fp else return Nothing
+#endif
 
+tweakNegate :: Tweak -> Maybe Tweak
+tweakNegate (Tweak fk) = withContext $ \ctx -> do
+    fnew <- mallocForeignPtr
+    peeked <- withForeignPtr fk peek
+    ret <- withForeignPtr fnew $ \n -> do
+        poke n peeked
+        ecTweakNegate ctx n
+    return $
+        if isSuccess ret
+            then Just (Tweak fnew)
+            else Nothing
+
 #ifdef ECDH
 -- | Compute Diffie-Hellman secret.
 ecdh :: PubKey -> SecKey -> ByteString
@@ -494,6 +580,102 @@
     size = 32
 #endif
 
+#ifdef SCHNORR
+-- Get 64-byte x-only public key.
+getXOnlyPubKey :: XOnlyPubKey -> ByteString
+getXOnlyPubKey (XOnlyPubKey fp) =
+    fromShort $ getXOnlyPubKey64 $ unsafePerformIO $ withForeignPtr fp peek
+
+-- | Add tweak to public key. Tweak is multiplied first by G to obtain a point.
+schnorrTweakAddPubKey :: XOnlyPubKey -> Tweak -> Maybe (XOnlyPubKey, CInt)
+schnorrTweakAddPubKey (XOnlyPubKey fp) (Tweak ft) = withContext $ \ctx ->
+    withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> alloca $ \is_negated -> do
+        fp' <- mallocForeignPtr
+        ret <- withForeignPtr fp' $ \p' -> do
+            pub <- peek p
+            poke p' pub
+            schnorrPubKeyTweakAdd ctx p' is_negated t
+        peeked_is_negated <- peek is_negated
+        if isSuccess ret then return $ Just $ (XOnlyPubKey fp', peeked_is_negated) else return Nothing
+
+-- | Add tweak to secret key.
+schnorrTweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey
+schnorrTweakAddSecKey (SecKey fk) (Tweak ft) = withContext $ \ctx ->
+    withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do
+        fk' <- mallocForeignPtr
+        ret <- withForeignPtr fk' $ \k' ->  do
+            key <- peek k
+            poke k' key
+            schnorrSecKeyTweakAdd ctx k' t
+        if isSuccess ret then return $ Just $ SecKey fk' else return Nothing
+
+signMsgSchnorr :: SecKey -> Msg -> SchnorrSig
+signMsgSchnorr (SecKey fk) (Msg fm) = withContext $ \ctx ->
+    withForeignPtr fk $ \k -> withForeignPtr fm $ \m -> do
+        fg <- mallocForeignPtr
+        ret <- withForeignPtr fg $ \g -> schnorrSign ctx g m k nullFunPtr nullPtr
+        unless (isSuccess ret) $ error "could not schnorr-sign message"
+        return $ SchnorrSig fg
+
+exportSchnorrSig :: SchnorrSig -> ByteString
+exportSchnorrSig (SchnorrSig fg) = withContext $ \ctx ->
+    withForeignPtr fg $ \g -> allocaBytes 64 $ \o -> do
+        ret <- signatureSerializeSchnorr ctx o g
+        unless (isSuccess ret) $ error "could not serialize schnorr signature"
+        packByteString (o, 64)
+
+importXOnlyPubKey :: ByteString -> Maybe XOnlyPubKey
+importXOnlyPubKey bs
+    | BS.length bs == 32 = withContext $ \ctx -> do
+        fp <- mallocForeignPtr
+        ret <- withForeignPtr fp $ \pfp -> useByteString bs $ \(inp, _) ->
+            schnorrXOnlyPubKeyParse ctx pfp inp
+        if isSuccess ret
+            then return $ Just $ XOnlyPubKey fp
+            else return Nothing
+    | otherwise = Nothing
+
+importSchnorrSig :: ByteString -> Maybe SchnorrSig
+importSchnorrSig bs
+    | BS.length bs == 64 = withContext $ \ctx -> do
+        fp <- mallocForeignPtr
+        ret <- withForeignPtr fp $ \pfp -> useByteString bs $ \(inp, _) ->
+            schnorrSignatureParse ctx pfp inp
+        if isSuccess ret
+            then return $ Just $ SchnorrSig fp
+            else return Nothing
+    | otherwise = Nothing
+
+verifyMsgSchnorr :: XOnlyPubKey -> SchnorrSig -> Msg -> Bool
+verifyMsgSchnorr (XOnlyPubKey fp) (SchnorrSig fg) (Msg fm) = withContext $ \ctx ->
+    withForeignPtr fp $ \p -> withForeignPtr fg $ \g ->
+        withForeignPtr fm $ \m -> isSuccess <$> schnorrSignatureVerify ctx g m p
+
+exportXOnlyPubKey :: XOnlyPubKey -> ByteString
+exportXOnlyPubKey (XOnlyPubKey pub) = withContext $ \ctx ->
+    withForeignPtr pub $ \p -> allocaBytes 32 $ \o -> do
+        ret <- schnorrPubKeySerialize ctx o p
+        unless (isSuccess ret) $ error "could not serialize x-only public key"
+        packByteString (o, 32)
+
+deriveXOnlyPubKey :: SecKey -> XOnlyPubKey
+deriveXOnlyPubKey (SecKey fk) = withContext $ \ctx -> withForeignPtr fk $ \k -> do
+    fp <- mallocForeignPtr
+    ret <- withForeignPtr fp $ \p -> schnorrXOnlyPubKeyCreate ctx p k
+    unless (isSuccess ret) $ error "could not derive x-only public key"
+    return $ XOnlyPubKey fp
+
+testTweakXOnlyPubKey :: XOnlyPubKey -> CInt -> XOnlyPubKey -> Tweak -> Bool
+testTweakXOnlyPubKey (XOnlyPubKey fp) is_negated (XOnlyPubKey internal) (Tweak ft) =
+    withContext $ \ctx ->
+    withForeignPtr fp $ \p ->
+    withForeignPtr internal $ \internalp ->
+    withForeignPtr ft $ \t -> do
+        ret <- xOnlyPubKeyTweakTest ctx p is_negated internalp t
+        return $ isSuccess ret
+-- End of Schnorr block
+#endif
+
 instance Arbitrary Msg where
     arbitrary = gen_msg
       where
@@ -511,3 +693,10 @@
     arbitrary = do
         key <- arbitrary
         return $ derivePubKey key
+
+#ifdef SCHNORR
+instance Arbitrary XOnlyPubKey where
+    arbitrary = do
+        key <- arbitrary
+        return $ deriveXOnlyPubKey key
+#endif
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
@@ -4,8 +4,8 @@
 {-# LANGUAGE RecordWildCards #-}
 {-|
 Module      : Crypto.Secp256k1.Internal
-License     : MIT
-Maintainer  : Jean-Pierre Rupp <root@haskoin.com>
+License     : UNLICENSE
+Maintainer  : Jean-Pierre Rupp <jprupp@protonmail.ch>
 Stability   : experimental
 Portability : POSIX
 
@@ -49,6 +49,7 @@
 newtype RecSig65 = RecSig65 { getRecSig65 :: ShortByteString }
     deriving (Read, Show, Eq, Ord, Generic, NFData)
 
+#ifdef RECOVERY
 data CompactRecSig =
     CompactRecSig
         { getCompactRecSigR :: !ShortByteString
@@ -56,6 +57,7 @@
         , getCompactRecSigV :: !Word8
         }
     deriving (Show, Eq, Ord, Generic, NFData)
+#endif
 
 newtype Seed32 = Seed32 { getSeed32 :: ShortByteString }
     deriving (Read, Show, Eq, Ord, Generic, NFData)
@@ -81,6 +83,14 @@
 newtype Ret = Ret { getRet :: CInt }
     deriving (Read, Show, Eq, Ord, Generic, NFData)
 
+#ifdef SCHNORR
+newtype XOnlyPubKey64 = XOnlyPubKey64 { getXOnlyPubKey64 :: ShortByteString }
+    deriving (Read, Show, Eq, Ord, Generic, NFData)
+
+newtype SchnorrSig64 = SchnorrSig64 { getSchnorrSig64 :: ShortByteString }
+    deriving (Read, Show, Eq, Ord, Generic, NFData)
+#endif
+
 -- | Nonce32-generating function
 type NonceFunction a
     =  Ptr Nonce32
@@ -113,6 +123,22 @@
 packByteString :: (Ptr CUChar, CSize) -> IO ByteString
 packByteString (b, l) = BS.packCStringLen (castPtr b, fromIntegral l)
 
+#if SCHNORR
+instance Storable XOnlyPubKey64 where
+    sizeOf _ = 64
+    alignment _ = 1
+    peek p = XOnlyPubKey64 . toShort <$> packByteString (castPtr p, 64)
+    poke p (XOnlyPubKey64 k) = useByteString (fromShort k) $
+        \(b, _) -> copyArray (castPtr p) b 64
+
+instance Storable SchnorrSig64 where
+    sizeOf _ = 64
+    alignment _ = 1
+    peek p = SchnorrSig64 . toShort <$> packByteString (castPtr p, 64)
+    poke p (SchnorrSig64 k) = useByteString (fromShort k) $
+        \(b, _) -> copyArray (castPtr p) b 64
+#endif
+
 instance Storable PubKey64 where
     sizeOf _ = 64
     alignment _ = 1
@@ -154,6 +180,7 @@
         Put.putShortByteString s
         Put.putShortByteString r
 
+#ifdef RECOVERY
 instance Storable RecSig65 where
     sizeOf _ = 65
     alignment _ = 1
@@ -192,6 +219,7 @@
         Put.putShortByteString s
         Put.putShortByteString r
         Put.putWord8 v
+#endif
 
 instance Storable Msg32 where
     sizeOf _ = 32
@@ -392,6 +420,13 @@
     -> IO Ret
 
 foreign import ccall
+    "secp256k1.h secp256k1_ec_privkey_negate"
+    ecTweakNegate
+    :: Ptr Ctx
+    -> Ptr Tweak32
+    -> IO Ret
+
+foreign import ccall
     "secp256k1.h secp256k1_ec_pubkey_tweak_add"
     ecPubKeyTweakAdd
     :: Ptr Ctx
@@ -431,6 +466,7 @@
     -> CInt -- ^ number of public keys
     -> IO Ret
 
+#ifdef RECOVERY
 foreign import ccall
     "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_parse_compact"
     ecdsaRecoverableSignatureParseCompact
@@ -476,6 +512,99 @@
     -> Ptr RecSig65
     -> Ptr Msg32
     -> IO Ret
+#endif
+
+#ifdef SCHNORR
+foreign import ccall
+    "secp256k1.h secp256k1_xonly_pubkey_tweak_add"
+    schnorrPubKeyTweakAdd
+    :: Ptr Ctx
+    -> Ptr XOnlyPubKey64
+    -> Ptr CInt
+    -> Ptr Tweak32
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_xonly_seckey_tweak_add"
+    schnorrSecKeyTweakAdd
+    :: Ptr Ctx
+    -> Ptr SecKey32
+    -> Ptr Tweak32
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_schnorrsig_serialize"
+    signatureSerializeSchnorr
+    :: Ptr Ctx
+    -> Ptr CUChar -- ^ array for encoded signature, must be large enough
+    -> Ptr SchnorrSig64
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_schnorrsig_sign"
+    schnorrSign
+    :: Ptr Ctx
+    -> Ptr SchnorrSig64
+    -> Ptr Msg32
+    -> Ptr SecKey32
+    -- TODO
+    -- This is actually an "extended nonce function" in the C code. So this signature is broken,
+    -- but we pass a nullFunPtr (and this module is Internal), so it doesn't matter right now.
+    -> FunPtr (NonceFunction a)
+    -> Ptr a -- ^ nonce data
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_xonly_pubkey_tweak_test"
+    xOnlyPubKeyTweakTest
+    :: Ptr Ctx
+    -> Ptr XOnlyPubKey64 -- output_pubkey
+    -> CInt -- is_negated
+    -> Ptr XOnlyPubKey64 -- internal_pubkey
+    -> Ptr Tweak32
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_xonly_pubkey_serialize"
+    schnorrPubKeySerialize
+    :: Ptr Ctx
+    -> Ptr CUChar -- 32 bytes output buffer
+    -> Ptr XOnlyPubKey64
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_schnorrsig_verify"
+    schnorrSignatureVerify
+    :: Ptr Ctx
+    -> Ptr SchnorrSig64
+    -> Ptr Msg32
+    -> Ptr XOnlyPubKey64
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_schnorrsig_parse"
+    schnorrSignatureParse
+    :: Ptr Ctx
+    -> Ptr SchnorrSig64 -- out
+    -> Ptr CUChar -- in
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_xonly_pubkey_parse"
+    schnorrXOnlyPubKeyParse
+    :: Ptr Ctx
+    -> Ptr XOnlyPubKey64 -- out
+    -> Ptr CUChar -- in
+    -> IO Ret
+
+foreign import ccall
+    "secp256k1.h secp256k1_xonly_pubkey_create"
+    schnorrXOnlyPubKeyCreate
+    :: Ptr Ctx
+    -> Ptr XOnlyPubKey64
+    -> Ptr SecKey32
+    -> IO Ret
+#endif
 
 #ifdef ECDH
 foreign import ccall
diff --git a/test/Crypto/Secp256k1/InternalSpec.hs b/test/Crypto/Secp256k1/InternalSpec.hs
--- a/test/Crypto/Secp256k1/InternalSpec.hs
+++ b/test/Crypto/Secp256k1/InternalSpec.hs
@@ -221,7 +221,7 @@
 
 ecdsaSignTest :: Assertion
 ecdsaSignTest = do
-    (ret, sig) <- liftIO $ do
+    der <- liftIO $ do
         x <- signCtx
         alloca $ \s -> alloca $ \m -> alloca $ \k -> alloca $ \ol ->
             allocaBytes 72 $ \o -> do
@@ -232,22 +232,31 @@
                     -- TODO:
                     -- ecdsaSign x s m k nonce_function_default nullPtr
                     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
-                sig <- packCStringLen (castPtr o, fromIntegral len)
-                return (ret1, sig)
-    assertBool "successful signing" $ isSuccess ret
-    assertEqual "signature matches" sig der
+                der <- packCStringLen (castPtr o, fromIntegral len)
+                return der
+    ret <- liftIO $ do
+        p <- alloca $ \p -> alloca $ \k -> do
+            poke k key
+            x <- signCtx
+            ret <- ecPubKeyCreate x p k
+            unless (isSuccess ret) $ error "failed to create public key"
+            return p
+        alloca $ \m -> alloca $ \s -> do
+            x <- contextCreate verify
+            sig <- parseDer x der
+            poke m msg
+            poke s sig
+            ecdsaVerify x s m p
+    assertBool "signature matches" (isSuccess ret)
   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
@@ -266,11 +275,12 @@
         poke k key
         x <- signCtx
         ret <- ecPubKeyCreate x p k
+        unless (isSuccess ret) $ error "failed to create public key"
         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"
+            unless (isSuccess rets) $ error "failed to serialize public key"
             len <- fromIntegral <$> peek ol
             pk <- packCStringLen (castPtr o, len)
             return (ret, pk)
diff --git a/test/Crypto/Secp256k1Spec.hs b/test/Crypto/Secp256k1Spec.hs
--- a/test/Crypto/Secp256k1Spec.hs
+++ b/test/Crypto/Secp256k1Spec.hs
@@ -2,6 +2,7 @@
 module Crypto.Secp256k1Spec (spec) where
 
 import           Crypto.Secp256k1
+import qualified Data.ByteString         as BS
 import qualified Data.ByteString.Base16  as B16
 import qualified Data.ByteString.Char8   as B8
 import           Data.Maybe              (fromMaybe)
@@ -16,18 +17,18 @@
 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
+#ifdef RECOVERY
         it "recovers public keys" $ property $ recoverTest
-        it "Bad recover public keys" $ property $ badRecoverTest
+        it "recovers key from signed message" $ property $ signRecMsgTest
+        it "does not recover bad public keys" $ property $ badRecoverTest
+        it "detects bad recoverable signature" $ property $ badRecSignatureTest
+#endif
     describe "serialization" $ do
         it "serializes public key" $ property $ serializePubKeyTest
         it "serializes DER signature" $ property $ serializeSigTest
         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)
@@ -37,26 +38,53 @@
             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
+#ifdef RECOVERY
+        it "serializes compact recoverable signature" $
+            property $ serializeCompactRecSigTest
+        it "shows and reads recoverable signature" $
+            property $ (showReadRecSig :: (SecKey, Msg) -> Bool)
+        it "reads recoverable signature from string" $ property $ isStringRecSig
+#endif
     describe "tweaks" $ do
         it "add secret key" $ property $ tweakAddSecKeyTest
         it "multiply secret key" $ property $ tweakMulSecKeyTest
         it "add public key" $ property $ tweakAddPubKeyTest
         it "multiply public key" $ property $ tweakMulPubKeyTest
         it "combine public keys" $ property $ combinePubKeyTest
+        it "negates tweak" $ property $ negateTweakTest
 #ifdef ECDH
     describe "ecdh" $ do
         it "computes dh secret" $ property $ computeDhSecret
 #endif
+#ifdef SCHNORR
+    describe "schnorr (bip-340)" $ do
+        it "validates test vector 0" $ property bip340Vector0
+        it "rejects test vector 5" $ property $
+            failingVectorToAssertion InvalidPubKey
+              (
+                hexToBytes "eefdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34",
+                hexToBytes "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",
+                hexToBytes "667c2f778e0616e611bd0c14b8a600c5884551701a949ef0ebfd72d452d64e844160bcfc3f466ecb8facd19ade57d8699d74e7207d78c6aedc3799b52a8e0598"
+              )
+        it "rejects test vector 6" $ property $
+            failingVectorToAssertion InvalidSig
+              (
+                hexToBytes "dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",
+                hexToBytes "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",
+                hexToBytes "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9935554d1aa5f0374e5cdaacb3925035c7c169b27c4426df0a6b19af3baeab138"
+              )
+        it "makes a valid taproot key spend signature" $ property taprootKeySpend
+#endif
 
+hexToBytes :: String -> BS.ByteString
+hexToBytes = fst . B16.decode . B8.pack
+
 isStringPubKey :: (PubKey, Bool) -> Bool
 isStringPubKey (k, c) = k == fromString (cs hex) where
     hex = B16.encode $ exportPubKey c k
@@ -66,10 +94,12 @@
     g = signMsg k m
     hex = B16.encode $ exportSig g
 
+#ifdef RECOVERY
 isStringRecSig :: (SecKey, Msg) -> Bool
 isStringRecSig (k, m) = g == fromString (cs hex) where
     g = signRecMsg k m
     hex = B16.encode . encode $ exportCompactRecSig g
+#endif
 
 isStringMsg :: Msg -> Bool
 isStringMsg m = m == fromString (cs m') where
@@ -93,9 +123,11 @@
 showReadSig (k, m) = showRead sig where
     sig = signMsg k m
 
+#ifdef RECOVERY
 showReadRecSig :: (SecKey, Msg) -> Bool
 showReadRecSig (k, m) = showRead recSig where
     recSig = signRecMsg k m
+#endif
 
 showRead :: (Show a, Read a, Eq a) => a -> Bool
 showRead x = read (show x) == x
@@ -105,6 +137,7 @@
     fp = derivePubKey fk
     fg = signMsg fk fm
 
+#ifdef RECOVERY
 signRecMsgTest :: (Msg, SecKey) -> Bool
 signRecMsgTest (fm, fk) = verifySig fp fg fm where
     fp = derivePubKey fk
@@ -122,14 +155,17 @@
     fg  = signRecMsg fk fm
     fp  = derivePubKey fk
     fp' = recover fg fm'
+#endif
 
 badSignatureTest :: (Msg, SecKey, PubKey) -> Bool
 badSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where
     fg = signMsg fk fm
 
+#ifdef RECOVERY
 badRecSignatureTest :: (Msg, SecKey, PubKey) -> Bool
 badRecSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where
     fg = convertRecSig $ signRecMsg fk fm
+#endif
 
 normalizeSigTest :: (Msg, SecKey) -> Bool
 normalizeSigTest (fm, fk) = not norm && sig == fg where
@@ -158,6 +194,7 @@
   where
     fg = signMsg fk fm
 
+#ifdef RECOVERY
 serializeCompactRecSigTest :: (Msg, SecKey) -> Bool
 serializeCompactRecSigTest (fm, fk) =
     case importCompactRecSig $ exportCompactRecSig fg of
@@ -165,6 +202,7 @@
         Nothing  -> False
   where
     fg = signRecMsg fk fm
+#endif
 
 serializeSecKeyTest :: SecKey -> Bool
 serializeSecKeyTest fk =
@@ -177,12 +215,12 @@
     assertEqual "tweaked keys match" expected tweaked
   where
     tweaked = do
-        key <- secKey $ fst $ B16.decode $ B8.pack
+        key <- secKey $ hexToBytes
             "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-        twk <- tweak $ fst $ B16.decode $ B8.pack
+        twk <- tweak $ hexToBytes
             "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
         tweakAddSecKey key twk
-    expected = secKey $ fst $ B16.decode $ B8.pack
+    expected = secKey $ hexToBytes
         "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
 
 tweakMulSecKeyTest :: Assertion
@@ -190,12 +228,12 @@
     assertEqual "tweaked keys match" expected tweaked
   where
     tweaked = do
-        key <- secKey $ fst $ B16.decode $ B8.pack
+        key <- secKey $ hexToBytes
             "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-        twk <- tweak $ fst $ B16.decode $ B8.pack
+        twk <- tweak $ hexToBytes
             "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
         tweakMulSecKey key twk
-    expected = secKey $ fst $ B16.decode $ B8.pack
+    expected = secKey $ hexToBytes
         "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
 
 tweakAddPubKeyTest :: Assertion
@@ -203,12 +241,12 @@
     assertEqual "tweaked keys match" expected tweaked
   where
     tweaked = do
-        pub <- importPubKey $ fst $ B16.decode $ B8.pack
+        pub <- importPubKey $ hexToBytes
             "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-        twk <- tweak $ fst $ B16.decode $ B8.pack
+        twk <- tweak $ hexToBytes
             "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
         tweakAddPubKey pub twk
-    expected = importPubKey $ fst $ B16.decode $ B8.pack
+    expected = importPubKey $ hexToBytes
         "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fdc87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
 
 tweakMulPubKeyTest :: Assertion
@@ -216,12 +254,12 @@
     assertEqual "tweaked keys match" expected tweaked
   where
     tweaked = do
-        pub <- importPubKey $ fst $ B16.decode $ B8.pack
+        pub <- importPubKey $ hexToBytes
             "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-        twk <- tweak $ fst $ B16.decode $ B8.pack
+        twk <- tweak $ hexToBytes
             "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
         tweakMulPubKey pub twk
-    expected = importPubKey $ fst $ B16.decode $ B8.pack
+    expected = importPubKey $ hexToBytes
         "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae49c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
 
 combinePubKeyTest :: Assertion
@@ -229,27 +267,105 @@
     assertEqual "combined keys match" expected combined
   where
     combined = do
-        pub1 <- importPubKey $ fst $ B16.decode $ B8.pack
+        pub1 <- importPubKey $ hexToBytes
             "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-        pub2 <- importPubKey $ fst $ B16.decode $ B8.pack
+        pub2 <- importPubKey $ hexToBytes
             "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f577dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
-        pub3 <- importPubKey $ fst $ B16.decode $ B8.pack
+        pub3 <- importPubKey $ hexToBytes
             "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3febd908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
         combinePubKeys [pub1, pub2, pub3]
-    expected = importPubKey $ fst $ B16.decode $ B8.pack
+    expected = importPubKey $ hexToBytes
         "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
 
+negateTweakTest :: Assertion
+negateTweakTest =
+    assertEqual "can recover secret key 1 after adding tweak 1" oneKey subtracted
+  where
+    Just oneKey = secKey $ fst $ B16.decode $ B8.pack
+        "0000000000000000000000000000000000000000000000000000000000000001"
+    Just oneTwk = tweak $ fst $ B16.decode $ B8.pack
+        "0000000000000000000000000000000000000000000000000000000000000001"
+    Just minusOneTwk = tweakNegate oneTwk
+    Just twoKey = tweakAddSecKey oneKey oneTwk
+    Just subtracted = tweakAddSecKey twoKey minusOneTwk
+
 #ifdef ECDH
 computeDhSecret :: Assertion
 computeDhSecret =
     assertEqual "ecdh computes known secret" expected computed
   where
     computed = do
-        pub <- importPubKey $ fst $ B16.decode $ B8.pack
+        pub <- importPubKey $ hexToBytes
             "028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7"
-        sec <- secKey $ fst $ B16.decode $ B8.pack
+        sec <- secKey $ hexToBytes
             "1212121212121212121212121212121212121212121212121212121212121212"
         pure $ ecdh pub sec
-    expected = Just $ fst $ B16.decode $ B8.pack
+    expected = Just $ hexToBytes
         "1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3"
+#endif
+
+#ifdef SCHNORR
+data VectorError = InvalidPubKey | InvalidSig | InvalidMsg | InvalidSigFormat
+  deriving (Eq, Show)
+
+failingVectorToAssertion :: VectorError -> (BS.ByteString, BS.ByteString, BS.ByteString) -> Assertion
+failingVectorToAssertion expectedFailure (pubBytes, msgBytes, sigBytes) =
+    assertEqual ("expected error " <> show expectedFailure <> " occurs") (Left expectedFailure) computed
+  where
+    computed :: Either VectorError ()
+    computed =
+        let
+            pubM = importXOnlyPubKey pubBytes
+            sigM = importSchnorrSig sigBytes
+            msgM = msg $ msgBytes
+        in
+            case (pubM, sigM, msgM) of
+                (Nothing, _, _) -> Left InvalidPubKey
+                (_, Nothing, _) -> Left InvalidSigFormat
+                (_, _, Nothing) -> Left InvalidMsg
+                (Just pub, Just sig, Just msg) ->
+                    if verifyMsgSchnorr pub sig msg
+                        then Right ()
+                        else Left InvalidSig
+
+bip340Vector0 :: Assertion
+bip340Vector0 =
+  passingVectorToAssertion 0
+    (
+      BS.replicate 31 0 <> B8.pack ['\x01']
+      , BS.replicate 32 0
+      , hexToBytes "db46b5cdc554edbd7765611b75d7bdbc639cf538fb6e9ef04a61884b765343aae148954f27eb69291a9045862f8ae4fa53436c117e9397c70275d5398066d44b"
+    )
+
+-- Integer is BIP-340 vector test number
+passingVectorToAssertion :: Integer -> (BS.ByteString, BS.ByteString, BS.ByteString) -> Assertion
+passingVectorToAssertion idx (secBytes, msgBytes, sigBytes) =
+    assertEqual ("BIP-340 test vector " <> show idx <> " signature matches") expectedSig computedSig
+  where
+    expectedSig :: Maybe SchnorrSig
+    expectedSig = importSchnorrSig $ sigBytes
+    computedSig :: Maybe SchnorrSig
+    computedSig = do
+        sec <- secKey secBytes
+        msg <- msg $ msgBytes
+        pure $ signMsgSchnorr sec msg
+
+-- This test is ported from the C code, it is called 'test_schnorrsig_taproot'.
+-- But this version was modified to use fixed keys and messages.
+taprootKeySpend :: Assertion
+taprootKeySpend =
+    assertEqual "derivation succeded and resulting signature is valid" (Just True) computed
+  where
+    computed = do
+        sec <- secKey $ BS.replicate 32 32
+        msgToSign <- msg $ BS.replicate 32 00
+
+        let internalPub = deriveXOnlyPubKey sec
+        twea <- tweak $ exportXOnlyPubKey internalPub
+        (outputPub, isNegated) <- schnorrTweakAddPubKey internalPub twea
+
+        -- This is a 'key spend' in Taproot terminology:
+        tweakSec <- schnorrTweakAddSecKey sec twea
+        let sig = signMsgSchnorr tweakSec msgToSign
+        pure $ verifyMsgSchnorr outputPub sig msgToSign
 #endif
