diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,15 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## 0.6.0
+### Changed
+- Remove all use of CPP.
+- Depend on base16 instead of the old base16-bytestring package.
+- Target ghc-9.0.1 compiler.
+
+### Fixed
+- Do not crash on bytestrings that are backed by null C pointers.
+
 ## 0.5.0
 ### Changed
 - Include version boundaries and other changes submitted by Emily Pillmore.
diff --git a/secp256k1-haskell.cabal b/secp256k1-haskell.cabal
--- a/secp256k1-haskell.cabal
+++ b/secp256k1-haskell.cabal
@@ -1,11 +1,11 @@
 cabal-version: 1.24
 
--- This file has been generated from package.yaml by hpack version 0.34.2.
+-- This file has been generated from package.yaml by hpack version 0.34.4.
 --
 -- see: https://github.com/sol/hpack
 
 name:           secp256k1-haskell
-version:        0.5.0
+version:        0.6.0
 synopsis:       Bindings for secp256k1
 description:    Sign and verify signatures using the secp256k1 library.
 category:       Crypto
@@ -39,13 +39,13 @@
   build-depends:
       QuickCheck >=2.9.2 && <2.15
     , base >=4.9 && <5
-    , base16-bytestring >=0.1.1.6 && <1.1
+    , base16 >=0.3.0.1
     , bytestring >=0.10.8 && <0.11
     , cereal >=0.5.4 && <0.6
     , deepseq >=1.4.2 && <1.5
     , entropy >=0.3.8 && <0.5
     , hashable >=1.2.6 && <1.4
-    , string-conversions >=0.4 && <0.5
+    , string-conversions ==0.4.*
     , unliftio-core >=0.1.0 && <0.3
   default-language: Haskell2010
 
@@ -63,7 +63,7 @@
       HUnit
     , QuickCheck >=2.9.2 && <2.15
     , base >=4.9 && <5
-    , base16-bytestring >=0.1.1.6 && <1.1
+    , base16 >=0.3.0.1
     , bytestring >=0.10.8 && <0.11
     , cereal >=0.5.4 && <0.6
     , deepseq >=1.4.2 && <1.5
@@ -73,7 +73,7 @@
     , monad-par
     , mtl
     , secp256k1-haskell
-    , string-conversions >=0.4 && <0.5
+    , string-conversions ==0.4.*
     , unliftio-core >=0.1.0 && <0.3
   default-language: Haskell2010
   build-tool-depends: hspec-discover:hspec-discover
diff --git a/src/Crypto/Secp256k1.hs b/src/Crypto/Secp256k1.hs
--- a/src/Crypto/Secp256k1.hs
+++ b/src/Crypto/Secp256k1.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP                        #-}
 {-# LANGUAGE DeriveGeneric              #-}
 {-# LANGUAGE FlexibleContexts           #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
@@ -116,14 +115,9 @@
     get = CompactSig <$> getByteString 64
 
 decodeHex :: ConvertibleStrings a ByteString => a -> Maybe ByteString
-#if MIN_VERSION_base16_bytestring(1,0,0)
-decodeHex str = case B16.decode $ cs str of
+decodeHex str = case B16.decodeBase16 $ cs str of
   Right bs -> Just bs
   Left _ -> Nothing
-#else
-decodeHex str = if BS.null r then Just bs else Nothing where
-    (bs, r) = B16.decode $ cs str
-#endif
 
 instance Read PubKey where
     readPrec = do
@@ -138,7 +132,7 @@
         e = error "Could not decode public key from hex string"
 
 instance Show PubKey where
-    showsPrec _ = shows . B16.encode . exportPubKey True
+    showsPrec _ = shows . B16.encodeBase16 . exportPubKey True
 
 instance Read Msg where
     readPrec = parens $ do
@@ -153,7 +147,7 @@
         e = error "Could not decode message from hex string"
 
 instance Show Msg where
-    showsPrec _ = shows . B16.encode . getMsg
+    showsPrec _ = shows . B16.encodeBase16 . getMsg
 
 instance Read Sig where
     readPrec = parens $ do
@@ -168,7 +162,7 @@
     i `hashWithSalt` s = i `hashWithSalt` exportSig s
 
 instance Show Sig where
-    showsPrec _ = shows . B16.encode . exportSig
+    showsPrec _ = shows . B16.encodeBase16 . exportSig
 
 instance Read SecKey where
     readPrec = parens $ do
@@ -183,7 +177,7 @@
         e = error "Colud not decode secret key from hex string"
 
 instance Show SecKey where
-    showsPrec _ = shows . B16.encode . getSecKey
+    showsPrec _ = shows . B16.encodeBase16 . getSecKey
 
 instance Hashable Tweak where
     i `hashWithSalt` t = i `hashWithSalt` getTweak t
@@ -198,7 +192,7 @@
         e = error "Could not decode tweak from hex string"
 
 instance Show Tweak where
-    showsPrec _ = shows . B16.encode . getTweak
+    showsPrec _ = shows . B16.encodeBase16 . getTweak
 
 -- | Import 32-byte 'ByteString' as 'Msg'.
 msg :: ByteString -> Maybe Msg
@@ -240,7 +234,9 @@
 
 -- | Import DER-encoded public key.
 importPubKey :: ByteString -> Maybe PubKey
-importPubKey bs =  unsafePerformIO $
+importPubKey bs
+  | BS.null bs = Nothing
+  | otherwise = unsafePerformIO $
     unsafeUseByteString bs $ \(input, len) -> do
     pub_key <- mallocBytes 64
     ret <- ecPubKeyParse ctx pub_key input len
@@ -293,7 +289,9 @@
 
 -- | Import DER-encoded signature.
 importSig :: ByteString -> Maybe Sig
-importSig bs = unsafePerformIO $
+importSig bs
+  | BS.null bs = Nothing
+  | otherwise = unsafePerformIO $
     unsafeUseByteString bs $ \(in_ptr, in_len) -> do
     out_sig <- mallocBytes 64
     ret <- ecdsaSignatureParseDer ctx out_sig in_ptr in_len
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
@@ -4,13 +4,14 @@
 import           Control.Monad
 import           Control.Monad.Trans
 import           Crypto.Secp256k1.Internal
-import           Data.ByteString           (ByteString, packCStringLen,
-                                            useAsCStringLen, copy)
+import           Data.ByteString           (ByteString, copy, packCStringLen,
+                                            useAsCStringLen)
 import qualified Data.ByteString.Base16    as B16
+import           Data.Either               (fromRight)
 import           Foreign
 import           System.Entropy
-import           Test.Hspec
 import           Test.HUnit                (Assertion, assertBool, assertEqual)
+import           Test.Hspec
 
 spec :: Spec
 spec = do
@@ -74,7 +75,7 @@
             ecPubKeyParse x pubkey (castPtr i) (fromIntegral il)
     assertBool "parsed public key" (isSuccess ret)
   where
-    der = fst $ B16.decode
+    der = fromRight undefined $ B16.decodeBase16
         "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
 
 ecPubKeySerializeTest :: Assertion
@@ -95,7 +96,7 @@
     assertBool  "serialized public key successfully" $ isSuccess ret
     assertEqual "public key matches" der dec
   where
-    der = fst $ B16.decode
+    der = fromRight undefined $ B16.decodeBase16
         "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
 
 ecdsaSignatureParseDerTest :: Assertion
@@ -105,7 +106,7 @@
         ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
     assertBool "parsed signature successfully" $ isSuccess ret
   where
-    der = fst $ B16.decode
+    der = fromRight undefined $ B16.decodeBase16
         "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
         \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
         \45"
@@ -134,7 +135,7 @@
     assertBool  "serialization successful" $ isSuccess ret
     assertEqual "signatures match" der enc
   where
-    der = fst $ B16.decode
+    der = fromRight undefined $ B16.decodeBase16
         "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
         \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
         \45"
@@ -155,14 +156,14 @@
             ecdsaVerify x s m k
     assertBool "signature valid" $ isSuccess ret
   where
-    der = fst $ B16.decode
+    der = fromRight undefined $ B16.decodeBase16
         "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
         \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
         \45"
-    pub = fst $ B16.decode
+    pub = fromRight undefined $ B16.decodeBase16
         "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
         \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-    msg = fst $ B16.decode
+    msg = fromRight undefined $ B16.decodeBase16
         "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
 
 signCtx :: IO Ctx
@@ -204,9 +205,9 @@
             useByteString s' $ \(s, _) -> ecdsaVerify x s m p
     assertBool "signature matches" (isSuccess ret)
   where
-    msg = fst $ B16.decode
+    msg = fromRight undefined $ B16.decodeBase16
         "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    key = fst $ B16.decode
+    key = fromRight undefined $ B16.decodeBase16
         "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
 
 
@@ -217,7 +218,7 @@
         ecSecKeyVerify x k
     assertBool "valid secret key" $ isSuccess ret
   where
-    key = fst $ B16.decode
+    key = fromRight undefined $ B16.decodeBase16
         "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
 
 ecPubkeyCreateTest :: Assertion
@@ -236,9 +237,9 @@
             packCStringLen (castPtr o, len)
     assertEqual "public key matches" pub pk
   where
-    key = fst $ B16.decode
+    key = fromRight undefined $ B16.decodeBase16
         "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-    pub = fst $ B16.decode
+    pub = fromRight undefined $ B16.decodeBase16
         "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
         \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
 
@@ -254,11 +255,11 @@
     assertBool "successful secret key tweak" $ isSuccess ret
     assertEqual "tweaked keys match" expected tweaked
   where
-    key = fst $ B16.decode
+    key = fromRight undefined $ B16.decodeBase16
         "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-    tweak = fst $ B16.decode
+    tweak = fromRight undefined $ B16.decodeBase16
         "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    expected = fst $ B16.decode
+    expected = fromRight undefined $ B16.decodeBase16
         "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
 
 ecSecKeyTweakMulTest :: Assertion
@@ -274,11 +275,11 @@
     assertBool "successful secret key tweak" $ isSuccess ret
     assertEqual "tweaked keys match" expected tweaked
   where
-    key = fst $ B16.decode
+    key = fromRight undefined $ B16.decodeBase16
         "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
-    tweak = fst $ B16.decode
+    tweak = fromRight undefined $ B16.decodeBase16
         "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    expected = fst $ B16.decode
+    expected = fromRight undefined $ B16.decodeBase16
         "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
 
 serializeKey :: Ctx -> Ptr PubKey64 -> IO ByteString
@@ -310,12 +311,12 @@
     assertBool "successful secret key tweak" $ isSuccess ret
     assertEqual "tweaked keys match" expected tweaked
   where
-    pub = fst $ B16.decode
+    pub = fromRight undefined $ B16.decodeBase16
         "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
         \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-    tweak = fst $ B16.decode
+    tweak = fromRight undefined $ B16.decodeBase16
         "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    expected = fst $ B16.decode
+    expected = fromRight undefined $ B16.decodeBase16
         "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
         \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
 
@@ -332,12 +333,12 @@
     assertBool "successful secret key tweak" $ isSuccess ret
     assertEqual "tweaked keys match" expected tweaked
   where
-    pub = fst $ B16.decode
+    pub = fromRight undefined $ B16.decodeBase16
         "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
         \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-    tweak = fst $ B16.decode
+    tweak = fromRight undefined $ B16.decodeBase16
         "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
-    expected = fst $ B16.decode
+    expected = fromRight undefined $ B16.decodeBase16
         "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\
         \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
 
@@ -363,15 +364,15 @@
     parse x pub p = useByteString pub $ \(d, dl) -> do
         ret <- ecPubKeyParse x p d dl
         unless (isSuccess ret) $ error "could not parse public key"
-    pub1 = fst $ B16.decode
+    pub1 = fromRight undefined $ B16.decodeBase16
         "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
         \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
-    pub2 = fst $ B16.decode
+    pub2 = fromRight undefined $ B16.decodeBase16
         "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\
         \77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
-    pub3 = fst $ B16.decode
+    pub3 = fromRight undefined $ B16.decodeBase16
         "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\
         \d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
-    expected = fst $ B16.decode
+    expected = fromRight undefined $ B16.decodeBase16
         "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
         \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
diff --git a/test/Crypto/Secp256k1Spec.hs b/test/Crypto/Secp256k1Spec.hs
--- a/test/Crypto/Secp256k1Spec.hs
+++ b/test/Crypto/Secp256k1Spec.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 module Crypto.Secp256k1Spec (spec) where
 
 import           Control.Monad.Par
@@ -7,11 +6,12 @@
 import qualified Data.ByteString         as BS
 import qualified Data.ByteString.Base16  as B16
 import qualified Data.ByteString.Char8   as B8
+import           Data.Either             (fromRight)
 import           Data.Maybe              (fromMaybe, isNothing)
 import           Data.String             (fromString)
 import           Data.String.Conversions (cs)
-import           Test.Hspec
 import           Test.HUnit              (Assertion, assertEqual)
+import           Test.Hspec
 import           Test.QuickCheck
 
 spec :: Spec
@@ -68,29 +68,29 @@
         it "negates tweak" $ property negateTweakTest
 
 hexToBytes :: String -> BS.ByteString
-hexToBytes = fst . B16.decode . B8.pack
+hexToBytes = fromRight undefined . B16.decodeBase16 . B8.pack
 
 isStringPubKey :: (PubKey, Bool) -> Bool
 isStringPubKey (k, c) = k == fromString (cs hex) where
-    hex = B16.encode $ exportPubKey c k
+    hex = B16.encodeBase16 $ exportPubKey c k
 
 isStringSig :: (SecKey, Msg) -> Bool
 isStringSig (k, m) = g == fromString (cs hex) where
     g = signMsg k m
-    hex = B16.encode $ exportSig g
+    hex = B16.encodeBase16 $ exportSig g
 
 isStringMsg :: Msg -> Bool
 isStringMsg m = m == fromString (cs m') where
-    m' = B16.encode $ getMsg m
+    m' = B16.encodeBase16 $ getMsg m
 
 isStringSecKey :: SecKey -> Bool
 isStringSecKey k = k == fromString (cs hex) where
-    hex = B16.encode $ getSecKey k
+    hex = B16.encodeBase16 $ getSecKey k
 
 isStringTweak :: SecKey -> Bool
 isStringTweak k = t == fromString (cs hex) where
     t = fromMaybe e . tweak $ getSecKey k
-    hex = B16.encode $ getTweak t
+    hex = B16.encodeBase16 $ getTweak t
     e = error "Could not extract tweak from secret key"
 
 showReadTweak :: SecKey -> Bool
@@ -239,9 +239,9 @@
 negateTweakTest =
     assertEqual "can recover secret key 1 after adding tweak 1" oneKey subtracted
   where
-    Just oneKey = secKey $ fst $ B16.decode $ B8.pack
+    Just oneKey = secKey $ fromRight undefined $ B16.decodeBase16 $ B8.pack
         "0000000000000000000000000000000000000000000000000000000000000001"
-    Just oneTwk = tweak $ fst $ B16.decode $ B8.pack
+    Just oneTwk = tweak $ fromRight undefined $ B16.decodeBase16 $ B8.pack
         "0000000000000000000000000000000000000000000000000000000000000001"
     Just minusOneTwk = tweakNegate oneTwk
     Just twoKey = tweakAddSecKey oneKey oneTwk
