diff --git a/src/Tahoe/CHK/Cipher.hs b/src/Tahoe/CHK/Cipher.hs
index 380c130e26793e70c78cce37941bfadff5c056ab..43a45ef7b44bbf6b838ce3b03c99dc8cd249cf58 100644
--- a/src/Tahoe/CHK/Cipher.hs
+++ b/src/Tahoe/CHK/Cipher.hs
@@ -10,7 +10,7 @@ module Tahoe.CHK.Cipher (
 ) where
 
 import Control.DeepSeq (NFData)
-import Crypto.Cipher.Types (BlockCipher (..), Cipher (..))
+import Crypto.Cipher.Types (AEAD, BlockCipher (..), Cipher (..))
 import Data.ByteArray (ScrubbedBytes)
 import qualified Data.ByteArray as BA
 import Data.Coerce (coerce)
@@ -37,7 +37,10 @@ instance forall cipher. BlockCipher cipher => BlockCipher (Key cipher) where
     cfbDecrypt (Key _ cipher) iv = cfbDecrypt cipher (coerce iv)
     ctrCombine (Key _ cipher) iv = ctrCombine cipher (coerce iv)
 
-    aeadInit mode (Key _ cipher) iv = xxx
+    aeadInit mode (Key _ cipher) iv = wrap <$> aeadInit mode cipher iv
+      where
+        wrap = coerce @(AEAD cipher) @(AEAD (Key cipher))
+
 instance BA.ByteArrayAccess (Key cipher) where
     length (Key ba _) = BA.length ba
     withByteArray (Key ba _) = BA.withByteArray ba
diff --git a/src/Tahoe/CHK/Crypto.hs b/src/Tahoe/CHK/Crypto.hs
index b332c0a8feda32f12fe07b66bb4ce982a8d66476..e4c727bd3b51c645d2646fac42d091741340b6f7 100644
--- a/src/Tahoe/CHK/Crypto.hs
+++ b/src/Tahoe/CHK/Crypto.hs
@@ -32,7 +32,6 @@ import Crypto.Hash (
     hashDigestSize,
     hashlazy,
  )
-import Crypto.Types (ByteLength)
 import Data.ByteArray (convert)
 
 import Crypto.Cipher.AES (AES128)
@@ -151,6 +150,8 @@ convergenceEncryptionHashLazy secret params bytes =
 
     tag = BL.fromStrict . netstring $ convergenceEncryptionTag secret params
 
+type ByteLength = Int
+
 convergenceSecretLength :: ByteLength
 convergenceSecretLength = 16
 
diff --git a/src/Tahoe/CHK/Encrypt.hs b/src/Tahoe/CHK/Encrypt.hs
index 65a2f231e9d2cdca2921fbe016bece3b8a6a2aae..de9c796045a7782ee457497fd0cf275366ce6705 100644
--- a/src/Tahoe/CHK/Encrypt.hs
+++ b/src/Tahoe/CHK/Encrypt.hs
@@ -1,10 +1,13 @@
 -- | Support the encryption requirements of CHK.
-module Tahoe.CHK.Encrypt (encrypt, decrypt) where
+module Tahoe.CHK.Encrypt (encrypt, encryptLazy, decrypt, decryptLazy) where
 
 import Crypto.Cipher.Types (BlockCipher (ctrCombine), nullIV)
 import Data.ByteArray (ByteArray)
+import qualified Data.ByteString.Lazy as LBS
 
-{- | AES128-CTR encrypt a byte string in the manner used by CHK.
+{- | CTR-mode encrypt a byte string using some block cipher.
+
+ When used for CHKv1 or CHKv2 the block cipher should be AES128.
 
  This replaces allmydata.immutable.upload.EncryptAnUploadable
 
@@ -13,6 +16,18 @@ import Data.ByteArray (ByteArray)
 encrypt :: (BlockCipher cipher, ByteArray ba) => cipher -> ba -> ba
 encrypt key = ctrCombine key nullIV
 
+{- | Like encrypt but operate on lazy bytestrings.  TODO: Make this more
+ efficient than converting to/from strict ByteString!
+-}
+encryptLazy :: BlockCipher cipher => cipher -> LBS.ByteString -> LBS.ByteString
+encryptLazy cipher lbs = LBS.fromStrict (encrypt cipher (LBS.toStrict lbs))
+
 -- | AES128-CTR decrypt a byte string in the manner used by CHK.
 decrypt :: (BlockCipher cipher, ByteArray ba) => cipher -> ba -> ba
 decrypt = encrypt
+
+{- | Like decrypt but operate on lazy bytestrings.  TODO: Make this more
+ efficient than converting to/from strict ByteString!
+-}
+decryptLazy :: BlockCipher cipher => cipher -> LBS.ByteString -> LBS.ByteString
+decryptLazy = encryptLazy
diff --git a/src/Tahoe/CHK/Upload.hs b/src/Tahoe/CHK/Upload.hs
index 53290f60c4b658145d97e1a7ed4906adaee22498..0db1afc00b2e09c155f4e35b53aa5dda8c7bc199 100644
--- a/src/Tahoe/CHK/Upload.hs
+++ b/src/Tahoe/CHK/Upload.hs
@@ -89,7 +89,7 @@ import Data.Tuple.Extra (thd3)
 import Tahoe.CHK (
     encode,
  )
-import Tahoe.CHK.Encrypt (encrypt)
+import Tahoe.CHK.Encrypt (encryptLazy)
 
 -- Some data that can be uploaded.
 data Uploadable = Uploadable
@@ -189,7 +189,7 @@ encryptAndEncode ::
     IO ([BL.ByteString], Cap.Reader)
 encryptAndEncode (Uploadable readKey _ params read') = do
     plaintext <- readAll read'
-    let ciphertext = encrypt readKey plaintext
+    let ciphertext = encryptLazy readKey plaintext
     (shares, cap) <- encode readKey params ciphertext
     pure (map Binary.encode shares, cap)
   where
diff --git a/tahoe-chk.cabal b/tahoe-chk.cabal
index 05d2890838d06bbcb0bba1d259815fba90b3e27c..383af07df5aece4189e7c5c03ac3a1578681aab9 100644
--- a/tahoe-chk.cabal
+++ b/tahoe-chk.cabal
@@ -59,10 +59,8 @@ library
     , base64-bytestring   >=1.0.0.3  && <1.3
     , binary              >=0.8.6    && <0.9
     , bytestring          >=0.10.8.2 && <0.11
-    , cereal              >=0.5.8.1  && <0.6
     , concurrency         >=1.11     && <2
     , containers          >=0.6.0.1  && <0.7
-    , crypto-api          >=0.13.3   && <0.14
     , cryptonite          >=0.27     && <0.30
     , deepseq
     , directory           >=1.3.3    && <1.4
@@ -72,11 +70,9 @@ library
     , lens                >=5.0      && <5.3
     , megaparsec          >=8.0      && <9.3
     , memory              >=0.15     && <0.17
-    , monad-loops         >=0.4.3    && <0.5
     , network-byte-order  >=0.1.5    && <0.2
     , parser-combinators  >=1.2.1    && <1.4
     , primitive           >=0.7.0.1  && <0.8
-    , tagged              >=0.8.6    && <0.9
     , text                >=1.2.3.1  && <1.3
     , tree-diff           >=0.1      && <0.3
     , utility-ht          >=0.0.15   && <0.1
@@ -128,9 +124,7 @@ test-suite tahoe-chk-tests
     , base64-bytestring  >=1.0.0.3  && <1.3
     , binary             >=0.8.6    && <0.9
     , bytestring         >=0.10.8.2 && <0.11
-    , cereal             >=0.5.8.1  && <0.6
     , containers         >=0.6.0.1  && <0.7
-    , crypto-api         >=0.13.3   && <0.14
     , cryptonite         >=0.27     && <0.30
     , directory          >=1.3.3    && <1.4
     , fec                >=0.1.1    && <0.2
@@ -140,7 +134,6 @@ test-suite tahoe-chk-tests
     , megaparsec         >=8.0      && <9.3
     , memory             >=0.15     && <0.17
     , scientific         >=0.3.6.2  && <0.4
-    , tagged             >=0.8.6    && <0.9
     , tahoe-chk
     , tasty              >=1.2.3    && <1.5
     , tasty-hedgehog     >=1.0.0.2  && <1.2
diff --git a/test/SpecCHK.hs b/test/SpecCHK.hs
index 157e32c158f3cb613184e8e01a0ba6d2e60a8549..066ca15534a27f44e5fadbd5d1cbabca004361e8 100644
--- a/test/SpecCHK.hs
+++ b/test/SpecCHK.hs
@@ -11,13 +11,9 @@ import Control.Arrow (
  )
 import Control.Lens (view)
 import Control.Monad.IO.Class (MonadIO (liftIO))
-import Crypto.Cipher.AES128 (
-    AESKey128,
- )
-import Crypto.Classes (
-    encode,
- )
+import Crypto.Cipher.AES (AES128)
 import qualified Data.Binary as Binary
+import Data.ByteArray (convert)
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Base64 as Base64
 import qualified Data.ByteString.Lazy as BL
@@ -54,8 +50,9 @@ import qualified Hedgehog.Range as Range
 import Tahoe.CHK (padCiphertext)
 import qualified Tahoe.CHK (decode, encode, segmentCiphertext)
 import Tahoe.CHK.Capability (Reader, dangerRealShow, pCapability, pReader, verifier)
+import Tahoe.CHK.Cipher (Key)
 import Tahoe.CHK.Crypto (ciphertextSegmentHash', convergenceSecretLength)
-import Tahoe.CHK.Encrypt (encrypt)
+import Tahoe.CHK.Encrypt (encryptLazy)
 import Tahoe.CHK.Share (
     Share (
         _blockSize
@@ -112,7 +109,7 @@ import Vectors (
    A hard-coded convergence secret is used for simplicity and reproducibility.
 -}
 makeValidShares :: Parameters -> BL.ByteString -> IO ([Share], Reader)
-makeValidShares params plaintext = Tahoe.CHK.encode key params (encrypt key plaintext)
+makeValidShares params plaintext = Tahoe.CHK.encode key params (encryptLazy key plaintext)
   where
     key = getConvergentKey "secret" params plaintext
 
@@ -245,7 +242,7 @@ wellKnownCase :: WellKnown -> Assertion
 wellKnownCase WellKnown{..} =
     do
         uploadable <- memoryUploadableWithConvergence wellKnownConvergenceSecret (fromIntegral $ BL.length wellKnownPlaintext) wellKnownPlaintext wellKnownParameters
-        let ciphertext = encrypt (uploadableKey uploadable) wellKnownPlaintext
+        let ciphertext = encryptLazy (uploadableKey uploadable) wellKnownPlaintext
         (shares', cap) <- Tahoe.CHK.encode (uploadableKey uploadable) wellKnownParameters ciphertext
 
         let allValid = replicate (fromIntegral $ paramTotalShares wellKnownParameters) True
@@ -331,7 +328,7 @@ testEncrypt =
             assertEqual
                 "expected convergence key"
                 "oBcuR/wKdCgCV2GKKXqiNg=="
-                (Base64.encode $ encode convergenceKey)
+                (Base64.encode $ convert convergenceKey)
             let b64ciphertext = Base64.encode (BL.toStrict ciphertext)
             assertEqual "known result" knownCorrect b64ciphertext
         ]
@@ -345,9 +342,9 @@ testEncrypt =
     plaintext = "hello world"
 
     ciphertext :: BL.ByteString
-    ciphertext = encrypt convergenceKey plaintext
+    ciphertext = encryptLazy convergenceKey plaintext
 
-    convergenceKey :: AESKey128
+    convergenceKey :: Key AES128
     convergenceKey = getConvergentKey convergenceSecret params plaintext
 
     convergenceSecret = B.replicate convergenceSecretLength 0x42
diff --git a/test/SpecCrypto.hs b/test/SpecCrypto.hs
index 0b07b6b0ef5d5ff5d5882f84f28fd9647a8aa57d..09f9366b61eabec69480945e23ae23e712fe0122 100644
--- a/test/SpecCrypto.hs
+++ b/test/SpecCrypto.hs
@@ -5,19 +5,14 @@ module SpecCrypto (
     tests,
 ) where
 
-import Crypto.Cipher.AES128 (
-    AESKey128,
- )
-import Crypto.Classes (
-    buildKey,
-    keyLength,
- )
-import Crypto.Types (ByteLength)
+import Crypto.Cipher.AES (AES128)
+import Crypto.Cipher.Types (Cipher (..), KeySizeSpecifier (..))
+import Crypto.Error (CryptoFailable (CryptoPassed))
 import qualified Data.ByteString as B
 import Data.Char (
     ord,
  )
-import Data.Tagged (Tagged, untag)
+import Tahoe.CHK.Cipher (Key)
 import Tahoe.CHK.Crypto (
     blockHash',
     ciphertextSegmentHash',
@@ -64,7 +59,7 @@ tests =
                 -- Adapted from allmydata.test.test_hashutil.HashUtilTests.test_known_answers
                 assertEqual
                     "known value"
-                    (Just "\xb5\x4c\x60\xc5\xb1\x26\x46\xf0\x77\x0\xc4\x4c\x8b\x75\xb9\x48")
+                    (CryptoPassed "\xb5\x4c\x60\xc5\xb1\x26\x46\xf0\x77\x0\xc4\x4c\x8b\x75\xb9\x48")
                     (storageIndexHash <$> xKey)
             , testCase "tagged hash length" $ do
                 -- The length of the result equals the given size.
@@ -129,4 +124,10 @@ tests =
             ]
         ]
   where
-    xKey = buildKey (B.replicate (untag (keyLength :: Tagged AESKey128 ByteLength)) . fromIntegral . ord $ 'x') :: Maybe AESKey128
+    xKey = cipherInit keyBytes :: CryptoFailable (Key AES128)
+    keyBytes = B.replicate keySize (fromIntegral $ ord 'x')
+    keySize = case cipherKeySize @(Key AES128) undefined of
+        KeySizeRange _ high -> high
+        KeySizeEnum [] -> error "no key sizes!"
+        KeySizeEnum (s : _) -> s
+        KeySizeFixed s -> s
diff --git a/test/SpecUpload.hs b/test/SpecUpload.hs
index 056ddfac229a7b9d8b414ce7f266f105556292d5..74606a751e36f28a17b7fcedd6114dda94b15614 100644
--- a/test/SpecUpload.hs
+++ b/test/SpecUpload.hs
@@ -2,13 +2,11 @@ module SpecUpload (
     tests,
 ) where
 
+import Data.ByteArray (convert)
 import Data.ByteString.Base32 (
     encodeBase32Unpadded,
  )
 import Data.Maybe (mapMaybe)
-import Data.Serialize (
-    encode,
- )
 
 import Test.Tasty (
     TestTree,
@@ -153,7 +151,7 @@ testConvergence =
         assertEqual
             "The key matches the known correct result"
             expectedKeyBytes
-            (encodeBase32Unpadded . encode $ key)
+            (encodeBase32Unpadded . convert $ key)
       where
         key = getConvergentKey secret params (BL.fromStrict dataContent)