diff --git a/aws-cloudfront-signed-cookies.cabal b/aws-cloudfront-signed-cookies.cabal
--- a/aws-cloudfront-signed-cookies.cabal
+++ b/aws-cloudfront-signed-cookies.cabal
@@ -1,17 +1,17 @@
-cabal-version: 2.2
+cabal-version: 3.0
 
 name: aws-cloudfront-signed-cookies
-version: 0.2.0.10
+version: 0.2.0.11
 
 synopsis: Generate signed cookies for AWS CloudFront
 category: Network, AWS, Cloud
 
 description: One way to [serve private content through AWS CloudFront](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html) is to use [signed cookies](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html). This package helps you generate signed cookies [using a custom IAM policy](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html) which may include a range of time for which the cookie is valid and an IP address restriction.
 
-homepage:    https://github.com/typeclasses/aws-cloudfront-signed-cookies
+homepage: https://github.com/typeclasses/aws-cloudfront-signed-cookies
 bug-reports: https://github.com/typeclasses/aws-cloudfront-signed-cookies/issues
 
-author:     Chris Martin
+author: Chris Martin
 maintainer: Chris Martin, Julie Moronuki
 
 copyright: 2021 Mission Valley Software LLC
@@ -20,132 +20,77 @@
 
 build-type: Simple
 
-tested-with:
-    GHC == 9.0.1
-  , GHC == 8.10.1
-  , GHC == 8.8.1
-  , GHC == 8.6.3
-  , GHC == 8.4.4
-  , GHC == 8.2.2
-
-extra-source-files:
-    readme.md
-  , changelog.md
+extra-source-files: readme.md
 
 source-repository head
-  type: git
-  location: https://github.com/typeclasses/aws-cloudfront-signed-cookies
+    type: git
+    location: https://github.com/typeclasses/aws-cloudfront-signed-cookies
 
+common base
+    default-language: Haskell2010
+    ghc-options: -Wall
+    default-extensions:
+        FlexibleContexts
+        LambdaCase
+        OverloadedStrings
+        RecordWildCards
+        ScopedTypeVariables
+        TypeApplications
+    build-depends:
+        base ^>= 4.14 || ^>= 4.15 || ^>= 4.16
+
 library
-  default-language: Haskell2010
-  hs-source-dirs: library
+    import: base
+    hs-source-dirs: library
 
-  exposed-modules:
-      Network.AWS.CloudFront.SignedCookies
-    , Network.AWS.CloudFront.SignedCookies.CLI
-    , Network.AWS.CloudFront.SignedCookies.CLI.Decode
-    , Network.AWS.CloudFront.SignedCookies.CLI.Sign
-    , Network.AWS.CloudFront.SignedCookies.Crypto
-    , Network.AWS.CloudFront.SignedCookies.Encoding
-    , Network.AWS.CloudFront.SignedCookies.Policy
-    , Network.AWS.CloudFront.SignedCookies.Types
+    exposed-modules:
+        Network.AWS.CloudFront.SignedCookies
+        Network.AWS.CloudFront.SignedCookies.CLI
+        Network.AWS.CloudFront.SignedCookies.CLI.Decode
+        Network.AWS.CloudFront.SignedCookies.CLI.Sign
+        Network.AWS.CloudFront.SignedCookies.Crypto
+        Network.AWS.CloudFront.SignedCookies.Encoding
+        Network.AWS.CloudFront.SignedCookies.Policy
+        Network.AWS.CloudFront.SignedCookies.Types
 
-  other-modules:
-      Network.AWS.CloudFront.SignedCookies.CLI.Internal
-    , Network.AWS.CloudFront.SignedCookies.Crypto.Internal
+    other-modules:
+        Network.AWS.CloudFront.SignedCookies.CLI.Internal
+        Network.AWS.CloudFront.SignedCookies.Crypto.Internal
 
-  build-depends:
-      aeson
-           ^>= 1.4
-        || ^>= 1.5
-        || ^>= 2.0
-    , aeson-pretty
-           ^>= 0.8
-    , asn1-encoding
-           ^>= 0.9.5
-    , asn1-types
-           ^>= 0.3.2
-    , base
-           ^>= 4.10
-        || ^>= 4.11
-        || ^>= 4.12
-        || ^>= 4.13
-        || ^>= 4.14
-        || ^>= 4.15
-    , base64-bytestring
-           ^>= 1.0.0.2
-        || ^>= 1.1
-        || ^>= 1.2
-    , bytestring
-           ^>= 0.10.8.1
-        || ^>= 0.11
-    , cookie
-           ^>= 0.4.3
-    , cryptonite
-           ^>= 0.25
-        || ^>= 0.26
-        || ^>= 0.27
-        || ^>= 0.28
-        || ^>= 0.29
-    , lens
-           ^>= 4.15.2
-        || ^>= 4.16
-        || ^>= 4.17
-        || ^>= 4.18
-        || ^>= 4.19
-        || ^>= 5
-        || ^>= 5.1
-    , lens-aeson
-           ^>= 1.0.2
-        || ^>= 1.1
-    , optparse-applicative
-           ^>= 0.14.1.0
-        || ^>= 0.15
-        || ^>= 0.16
-    , pem
-           ^>= 0.2.3
-    , text
-           ^>= 1.2.3
-    , time
-           ^>= 1.8
-        || ^>= 1.9
-    , vector
-           ^>= 0.12.0.1
+    build-depends:
+        aeson ^>= 2.0.2
+      , aeson-pretty ^>= 0.8.9
+      , asn1-encoding ^>= 0.9.6
+      , asn1-types ^>= 0.3.4
+      , base64-bytestring ^>= 1.2.0.1
+      , bytestring ^>= 0.11.1
+      , cookie ^>= 0.4.5
+      , cryptonite ^>= 0.29 || ^>= 0.30
+      , lens ^>= 5.0.1 || ^>= 5.1
+      , lens-aeson ^>= 1.2
+      , optparse-applicative ^>= 0.16.1 || ^>= 0.17
+      , pem ^>= 0.2.4
+      , text ^>= 1.2.4.1
+      , time ^>= 1.9.3 || ^>= 1.10 || ^>= 1.11
+      , vector ^>= 0.12.3
 
 executable aws-cloudfront-signed-cookies
-  default-language: Haskell2010
-  hs-source-dirs: executables
-  main-is: aws-cloudfront-signed-cookies.hs
-
-  build-depends:
-      aws-cloudfront-signed-cookies
-    , base
-           ^>= 4.10
-        || ^>= 4.11
-        || ^>= 4.12
-        || ^>= 4.13
-        || ^>= 4.14
-        || ^>= 4.15
+    import: base
+    hs-source-dirs: executables
+    main-is: aws-cloudfront-signed-cookies.hs
+    build-depends: aws-cloudfront-signed-cookies
 
 test-suite hedgehog
-  default-language: Haskell2010
-  hs-source-dirs: test
-  type: exitcode-stdio-1.0
-  main-is: Main.hs
-  ghc-options: -threaded
-  build-depends:
-      aws-cloudfront-signed-cookies
-    , base
-           ^>= 4.10
-        || ^>= 4.11
-        || ^>= 4.12
-        || ^>= 4.13
-        || ^>= 4.14
-        || ^>= 4.15
-    , hedgehog
-           ^>= 0.6
-        || ^>= 1.0
-    , neat-interpolation
-           ^>= 0.3.2
-        || ^>= 0.4
-        || ^>= 0.5
+    import: base
+    hs-source-dirs: test
+    type: exitcode-stdio-1.0
+    main-is: Main.hs
+    ghc-options: -threaded
+    default-extensions:
+        QuasiQuotes
+        TemplateHaskell
+
+    build-depends:
+        aws-cloudfront-signed-cookies
+      , hedgehog ^>= 1.0.5 || ^>= 1.1
+      , neat-interpolation ^>= 0.5.1.2
diff --git a/changelog.md b/changelog.md
deleted file mode 100644
--- a/changelog.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Changelog
-
-## 0.2.0.0 - 2018-04-13
-
-- Start of changelog
-
-## 0.2.0.1 - 2019-06-17
-
-- Relaxed a test case to accommodate a change
-  in the error message output from Aeson.
-
-## 0.2.0.2 - 2020-03-15
-
-- Add a lower bound on the time library because
-  we require at least version 1.8.
-
-## 0.2.0.4 - 2020-03-15
-
-- Add a lower bound on base 4.9
-
-## 0.2.0.6 - 2020-05-18
-
-- Support GHC 8.10
-
-## 0.2.0.8 - 2021-06-10
-
-- Add proper version bounds for all dependencies
-- Support GHC 9.0
-
-## 0.2.0.9 - 2021-11-15
-
-- Support Aeson 2.0
-- Remove unordered-containers dependency
-- [Fix test suite on macos](https://github.com/typeclasses/aws-cloudfront-signed-cookies/issues/2)
-
-## 0.2.0.10 - 2021-11-17
-
-- Support lens 5.1
diff --git a/library/Network/AWS/CloudFront/SignedCookies.hs b/library/Network/AWS/CloudFront/SignedCookies.hs
--- a/library/Network/AWS/CloudFront/SignedCookies.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, TypeApplications #-}
-
 -- |
 -- Example usage:
 --
@@ -95,7 +93,6 @@
 -- base
 import Control.Monad ((>=>))
 import Data.Coerce (coerce)
-import Data.Semigroup ((<>))
 
 -- bytestring
 import qualified Data.ByteString.Lazy as LBS
@@ -104,8 +101,8 @@
 import qualified Data.Text as Text
 
 -- time
-import Data.Time.Clock.POSIX (getPOSIXTime)
 import Data.Time.Clock (nominalDay)
+import Data.Time.Clock.POSIX (getPOSIXTime)
 
 createSignedCookies
   :: KeyPairId
diff --git a/library/Network/AWS/CloudFront/SignedCookies/CLI.hs b/library/Network/AWS/CloudFront/SignedCookies/CLI.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/CLI.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/CLI.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE FlexibleContexts, OverloadedStrings, RecordWildCards #-}
-
 -- | Command-line interface for generating AWS CloudFront signed cookies.
 --
 -- This program is the aggregation of these subcommands:
@@ -11,14 +9,11 @@
   ( main
   ) where
 
-import Network.AWS.CloudFront.SignedCookies.CLI.Internal
-
 import qualified Network.AWS.CloudFront.SignedCookies.CLI.Decode as Decode
-import qualified Network.AWS.CloudFront.SignedCookies.CLI.Sign   as Sign
+import qualified Network.AWS.CloudFront.SignedCookies.CLI.Sign as Sign
 
 -- base
 import Control.Monad (join)
-import Data.Semigroup ((<>))
 
 -- optparse-applicative
 import qualified Options.Applicative as Opt
diff --git a/library/Network/AWS/CloudFront/SignedCookies/CLI/Decode.hs b/library/Network/AWS/CloudFront/SignedCookies/CLI/Decode.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/CLI/Decode.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/CLI/Decode.hs
@@ -1,14 +1,12 @@
-{-# LANGUAGE OverloadedStrings, RecordWildCards, ScopedTypeVariables, TypeApplications #-}
-
 -- | Command-line interface for decoding AWS CloudFront policy cookies.
 
 module Network.AWS.CloudFront.SignedCookies.CLI.Decode
   ( main, mainOpts, Opts (..), optsParser, mainParserInfo
   ) where
 
-import Network.AWS.CloudFront.SignedCookies
-import Network.AWS.CloudFront.SignedCookies.CLI.Internal
-import Network.AWS.CloudFront.SignedCookies.Encoding
+import Network.AWS.CloudFront.SignedCookies.CLI.Internal (text)
+import Network.AWS.CloudFront.SignedCookies.Encoding (base64Decode)
+import Network.AWS.CloudFront.SignedCookies.Types (PolicyCookie (..), Text)
 
 -- aeson
 import qualified Data.Aeson as A
@@ -17,9 +15,7 @@
 import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder)
 
 -- base
-import Data.Coerce (Coercible, coerce)
-import Data.Foldable (for_)
-import Data.Semigroup ((<>))
+import Data.Coerce (coerce)
 
 -- bytestring
 import qualified Data.ByteString as BS
@@ -29,14 +25,9 @@
 import qualified Options.Applicative as Opt
 
 -- text
-import qualified Data.Text as Text
-import qualified Data.Text.Encoding as Text
 import qualified Data.Text.Lazy as LText
+import Data.Text.Lazy.Builder as Text (toLazyText)
 import qualified Data.Text.Lazy.IO as LText
-import Data.Text.Lazy.Builder as Text
-
--- time
-import Data.Time.Clock (nominalDay)
 
 -- | Entry point for the AWS CloudFront cookie decoding command-line interface.
 
diff --git a/library/Network/AWS/CloudFront/SignedCookies/CLI/Internal.hs b/library/Network/AWS/CloudFront/SignedCookies/CLI/Internal.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/CLI/Internal.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/CLI/Internal.hs
@@ -1,15 +1,11 @@
-{-# LANGUAGE FlexibleContexts #-}
-
 module Network.AWS.CloudFront.SignedCookies.CLI.Internal
   ( text, days
   ) where
 
-import Network.AWS.CloudFront.SignedCookies
-import Network.AWS.CloudFront.SignedCookies.Types
+import Network.AWS.CloudFront.SignedCookies (NominalDiffTime, Text, nominalDay)
 
 -- base
 import Data.Coerce (Coercible, coerce)
-import Data.Semigroup ((<>))
 import Prelude hiding (mod)
 
 -- optparse-applicative
@@ -17,7 +13,6 @@
 
 -- text
 import qualified Data.Text as Text
-
 
 text :: Coercible Text a => String -> String -> Opt.Parser a
 text long help = fmap (coerce . Text.pack) $ Opt.strOption $ mod long help
diff --git a/library/Network/AWS/CloudFront/SignedCookies/CLI/Sign.hs b/library/Network/AWS/CloudFront/SignedCookies/CLI/Sign.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/CLI/Sign.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/CLI/Sign.hs
@@ -1,28 +1,17 @@
-{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
-
 -- | Command-line interface for generating AWS CloudFront signed cookies.
 
 module Network.AWS.CloudFront.SignedCookies.CLI.Sign
   ( main, mainOpts, Opts (..), optsParser, mainParserInfo
   ) where
 
-import Network.AWS.CloudFront.SignedCookies
-import Network.AWS.CloudFront.SignedCookies.CLI.Internal
-
--- base
-import Data.Coerce (Coercible, coerce)
-import Data.Foldable (for_)
-import Data.Semigroup ((<>))
+import Network.AWS.CloudFront.SignedCookies (KeyPairId (..), Lifespan (..), PemFilePath (..), Resource (..), createSignedCookies, readPrivateKeyPemFile, renderCookiesText, simplePolicy)
+import Network.AWS.CloudFront.SignedCookies.CLI.Internal (days, text)
 
 -- optparse-applicative
 import qualified Options.Applicative as Opt
 
 -- text
-import qualified Data.Text as Text
 import qualified Data.Text.IO as Text
-
--- time
-import Data.Time.Clock (nominalDay)
 
 -- | Entry point for the AWS CloudFront cookie-signing command-line interface.
 
diff --git a/library/Network/AWS/CloudFront/SignedCookies/Crypto.hs b/library/Network/AWS/CloudFront/SignedCookies/Crypto.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/Crypto.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/Crypto.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}
-
 module Network.AWS.CloudFront.SignedCookies.Crypto
   (
   -- * Reading the private key
@@ -14,25 +12,22 @@
 
   ) where
 
-import Network.AWS.CloudFront.SignedCookies.Crypto.Internal
-import Network.AWS.CloudFront.SignedCookies.Types
+import Network.AWS.CloudFront.SignedCookies.Crypto.Internal (rsaPrivateKeyFromASN1)
+import Network.AWS.CloudFront.SignedCookies.Types (ByteString, PemFilePath (..), PrivateKey)
 
 -- asn1-encoding
 import Data.ASN1.BinaryEncoding (DER (DER))
 import Data.ASN1.Encoding (decodeASN1')
-import Data.ASN1.Error (ASN1Error)
 
 -- asn1-types
 import Data.ASN1.Types (ASN1)
 
 -- bytestring
 import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as LBS
 
 -- cryptonite
-import Crypto.PubKey.RSA (PrivateKey)
-import qualified Crypto.PubKey.RSA.PKCS15 as RSA
 import Crypto.Hash.Algorithms (SHA1 (SHA1))
+import qualified Crypto.PubKey.RSA.PKCS15 as RSA
 
 -- pem
 import qualified Data.PEM as PEM
diff --git a/library/Network/AWS/CloudFront/SignedCookies/Crypto/Internal.hs b/library/Network/AWS/CloudFront/SignedCookies/Crypto/Internal.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/Crypto/Internal.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/Crypto/Internal.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE LambdaCase, RecordWildCards, TypeApplications #-}
-
 {-
 
 This module is based on a part of snaplet-saml. If my hs-certificate PR gets merged, then we probably ought to remove this and use hs-certificate instead.
@@ -14,13 +12,7 @@
   ( rsaPrivateKeyFromASN1
   ) where
 
-import Data.Bifunctor (first)
-import Data.Semigroup ((<>))
-
-import Data.ASN1.Types
-import Data.ASN1.Encoding
-import Data.ASN1.BinaryEncoding
-import Data.ASN1.BitArray
+import Data.ASN1.Types (ASN1 (End, IntVal, Start), ASN1ConstructionType (Sequence))
 
 import qualified Crypto.PubKey.RSA as RSA
 
@@ -43,7 +35,7 @@
 
       case version of
         0 -> Right (buildKey Params{..})
-        v -> Left $ "rsaPrivateKeyFromASN1: unexpected version " <>
+        _ -> Left $ "rsaPrivateKeyFromASN1: unexpected version " <>
                     show @Integer version
 
     _ -> Left "rsaPrivateKeyFromASN1: unexpected format"
diff --git a/library/Network/AWS/CloudFront/SignedCookies/Encoding.hs b/library/Network/AWS/CloudFront/SignedCookies/Encoding.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/Encoding.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/Encoding.hs
@@ -1,11 +1,9 @@
-{-# LANGUAGE LambdaCase #-}
-
 module Network.AWS.CloudFront.SignedCookies.Encoding
   ( base64Encode
   , base64Decode
   ) where
 
-import Network.AWS.CloudFront.SignedCookies.Types
+import Network.AWS.CloudFront.SignedCookies.Types (ByteString, Text)
 
 -- base64-bytestring
 import qualified Data.ByteString.Base64 as Base64
diff --git a/library/Network/AWS/CloudFront/SignedCookies/Policy.hs b/library/Network/AWS/CloudFront/SignedCookies/Policy.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/Policy.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/Policy.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, TypeApplications #-}
-
 module Network.AWS.CloudFront.SignedCookies.Policy
   (
   -- * Defining a policy
@@ -20,25 +18,24 @@
 
   ) where
 
-import Network.AWS.CloudFront.SignedCookies.Types
+import Network.AWS.CloudFront.SignedCookies.Types (ByteString, EndTime (..), IpAddress (..), Lifespan (..), POSIXTime, Policy (..), Resource (..), StartTime (..), Text)
 
 -- aeson
+import Data.Aeson (toJSON, (.=))
 import qualified Data.Aeson as A (Value, eitherDecode', encode, object)
 import qualified Data.Aeson.Types as A (Pair)
-import Data.Aeson ((.=), toJSON)
 
 -- base
 import Control.Monad ((>=>))
-import Data.Semigroup ((<>))
 
 -- bytestring
 import qualified Data.ByteString.Lazy as LBS
 
 -- lens
-import Control.Lens ((&), (^.), (^?))
+import Control.Lens ((^?))
 
 -- lens-aeson
-import Data.Aeson.Lens (AsNumber (..), AsPrimitive (..), key, nth, _Array, _Object)
+import Data.Aeson.Lens (AsNumber (..), AsValue (..), key, nth)
 
 -- text
 import qualified Data.Text.Encoding as Text
@@ -47,7 +44,6 @@
 import Data.Time.Clock.POSIX (getPOSIXTime)
 
 -- vector
-import qualified Data.Vector as Vec
 
 {- |
 
diff --git a/library/Network/AWS/CloudFront/SignedCookies/Types.hs b/library/Network/AWS/CloudFront/SignedCookies/Types.hs
--- a/library/Network/AWS/CloudFront/SignedCookies/Types.hs
+++ b/library/Network/AWS/CloudFront/SignedCookies/Types.hs
@@ -32,7 +32,7 @@
 import Web.Cookie (CookiesText, SetCookie)
 
 -- cryptonite
-import Crypto.PubKey.RSA (PrivateKey (..), PublicKey (..))
+import Crypto.PubKey.RSA (PrivateKey (..))
 
 -- text
 import Data.Text (Text)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,23 +1,19 @@
-{-# LANGUAGE OverloadedStrings, QuasiQuotes, TemplateHaskell #-}
-
 module Main (main) where
 
-import Network.AWS.CloudFront.SignedCookies
+import Network.AWS.CloudFront.SignedCookies (EndTime (EndTime), IpAddress (IpAddress), Policy (Policy), Resource (Resource), StartTime (StartImmediately, StartTime), jsonTextPolicy)
 
 -- base
 import Control.Monad (unless)
 import Data.Either (isLeft)
 import Data.Foldable (for_)
 import qualified System.Exit as Exit
-import qualified System.IO   as IO
+import qualified System.IO as IO
 
 -- hedgehog
-import           Hedgehog
-import qualified Hedgehog.Gen as Gen
-import qualified Hedgehog.Range as Range
+import Hedgehog (Property, assert, checkParallel, discover, property, withTests, (===))
 
 -- neat-interpolation
-import NeatInterpolation
+import NeatInterpolation (text)
 
 main :: IO ()
 main = do
