packages feed

age-0.0.1.0: src/Crypto/Age/Buffered.hs

{-# LANGUAGE LambdaCase #-}

-- | Buffered encryption and decryption of age files.
module Crypto.Age.Buffered
  ( -- * Encryption
    encrypt
  , encryptLazy

    -- * Decryption
  , decrypt
  , decryptLazy
  ) where

import Control.Monad.Except ( ExceptT (..) )
import Crypto.Age.Conduit
  ( DecryptError
  , EncryptError
  , conduitDecryptEither
  , conduitEncrypt
  , sinkDecryptEither
  , sinkEncrypt
  )
import Crypto.Age.Identity ( Identity )
import Crypto.Age.Recipient ( Recipients )
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Data.Conduit ( ConduitT, await, runConduit, runConduitPure, yield, (.|) )
import qualified Data.Conduit.Combinators as C
import Data.List.NonEmpty ( NonEmpty )
import Data.Sequences ( LazySequence )
import Prelude

-------------------------------------------------------------------------------
-- Encryption
-------------------------------------------------------------------------------

-- | Encrypt a strict 'BS.ByteString'.
encrypt :: Recipients -> BS.ByteString -> ExceptT EncryptError IO BS.ByteString
encrypt recipients plaintext =
  runConduit $
    yield plaintext .| sinkEncrypt recipients

-- | Encrypt a lazy 'LBS.ByteString'.
encryptLazy :: Recipients -> LBS.ByteString -> ExceptT EncryptError IO LBS.ByteString
encryptLazy recipients plaintext =
  runConduit $
    C.sourceLazy plaintext .| conduitEncrypt recipients .| C.sinkLazy

-------------------------------------------------------------------------------
-- Decryption
-------------------------------------------------------------------------------

-- | Decrypt a strict 'BS.ByteString'.
decrypt :: NonEmpty Identity -> BS.ByteString -> Either DecryptError BS.ByteString
decrypt identities ciphertext = runConduitPure $ yield ciphertext .| sinkDecryptEither identities

-- | Decrypt a lazy 'LBS.ByteString'.
decryptLazy :: NonEmpty Identity -> LBS.ByteString -> Either DecryptError LBS.ByteString
decryptLazy identities ciphertext =
  runConduitPure $
    C.sourceLazy ciphertext .| conduitDecryptEither identities .| sinkLazyEither

-------------------------------------------------------------------------------
-- Helpers
-------------------------------------------------------------------------------

sinkLazyEither ::
  (Monad m, LazySequence lazy strict) =>
  ConduitT (Either err strict) o m (Either err lazy)
sinkLazyEither = go mempty
  where
    go acc = await >>= \case
      Nothing -> pure (Right acc)
      Just (Left err) -> pure (Left err)
      Just (Right bs) -> do
        lbs <- yield bs .| C.sinkLazy
        go (acc <> lbs)