diff --git a/src/Tahoe/CHK/Encrypt.hs b/src/Tahoe/CHK/Encrypt.hs
index de9c796045a7782ee457497fd0cf275366ce6705..ce76a7db0a45269d4d132e691bb335b4e8e48c16 100644
--- a/src/Tahoe/CHK/Encrypt.hs
+++ b/src/Tahoe/CHK/Encrypt.hs
@@ -1,9 +1,13 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+
 -- | Support the encryption requirements of CHK.
 module Tahoe.CHK.Encrypt (encrypt, encryptLazy, decrypt, decryptLazy) where
 
-import Crypto.Cipher.Types (BlockCipher (ctrCombine), nullIV)
+import Crypto.Cipher.Types (BlockCipher (blockSize, ctrCombine), ivAdd, nullIV)
 import Data.ByteArray (ByteArray)
 import qualified Data.ByteString.Lazy as LBS
+import Data.List (unfoldr)
 
 {- | CTR-mode encrypt a byte string using some block cipher.
 
@@ -16,18 +20,29 @@ import qualified Data.ByteString.Lazy as LBS
 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))
+-- | Like encrypt but operate on lazy bytestrings.
+encryptLazy :: forall cipher. BlockCipher cipher => cipher -> LBS.ByteString -> LBS.ByteString
+encryptLazy cipher lbs = LBS.concat . (LBS.fromStrict <$>) $ zipWith (ctrCombine cipher) ivs blocks
+  where
+    -- The underlying encryption function works on strict bytes.  Here's the
+    -- number of *blocks* to feed to it (that is, to make strict) at a time.
+    -- This value here is a magic number that is meant to represent a good
+    -- compromise between performance and number of bytes forced at one time.
+    workingBlocks = 1024 * 16
+
+    -- The size of a block is determined by the cipher.
+    workingBytes = workingBlocks * blockSize @cipher undefined
+
+    ivs = iterate (`ivAdd` workingBlocks) nullIV
+    blocks = LBS.toStrict <$> unfoldr takeChunk lbs
+
+    takeChunk "" = Nothing
+    takeChunk xs = Just . LBS.splitAt (fromIntegral workingBytes) $ xs
 
 -- | 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!
--}
+-- | Like decrypt but operate on lazy bytestrings.
 decryptLazy :: BlockCipher cipher => cipher -> LBS.ByteString -> LBS.ByteString
 decryptLazy = encryptLazy