qnap-decrypt-0.3.0: src/Data/Conduit/Crypto.hs
-- | This module provides a function to decrypt a padded and encrypted
-- stream of 'ByteString's.
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Conduit.Crypto
( -- * Decrypt
decryptPaddedStream
-- * Re-Export
, defaultChunkSize
) where
import Control.Exception (Exception, throw)
import Crypto.Classes (BlockCipher, ByteLength, IV, blockSizeBytes)
import Crypto.Padding (unpadPKCS5safe)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.ByteString.Builder.Extra (defaultChunkSize)
import qualified Data.ByteString.Lazy as BL
import Data.Conduit (ConduitT, yield)
import qualified Data.Conduit.Binary as CB
import Data.Tagged (Tagged, untag)
-- | Decrypt a PKCS padded and encrypted stream of 'ByteString's
--
-- @
-- runConduitRes $
-- sourceFile inName .|
-- decryptPaddedStream (error "Padding Error") defaultChunkSize (unCbc key) iv .|
-- sinkFile outName
-- @
--
-- /Since 0.3.0/
decryptPaddedStream
:: forall e cipher m.
Exception e
=> BlockCipher cipher
=> Monad m
=> e -- ^ The exception to throw in case of a padding error
-> Int -- ^ The chunk size (in Bytes, you may use 'defaultChunkSize', will be modified to be
-- a guaranteed multiple (at least 1) of the 'blockSizeBytes')
-> (IV cipher -> ByteString -> (ByteString, IV cipher))
-- ^ The decrypt function
-> IV cipher -- ^ The IV (see 'BlockCipher')
-> ConduitT ByteString ByteString m ()
decryptPaddedStream paddingError chunkSize decrypt = go BS.empty
where
go :: ByteString -> IV cipher -> ConduitT ByteString ByteString m ()
go lastBlock currentIv = do
(currentBlock, nextIv) <- decrypt currentIv . BL.toStrict <$> CB.take actualBlockSize
if BS.length currentBlock < actualBlockSize
then
case unpadPKCS5safe (lastBlock `BS.append` currentBlock) of
Just unpadded ->
yield unpadded
Nothing ->
throw paddingError
else do
yield lastBlock
go currentBlock nextIv
-- the actual block size is probably chunkSize but guaranteed to be a multiple of the cipher block size (at least one)
actualBlockSize :: Int
actualBlockSize = ((chunkSize `div` cipherBlockSize) * cipherBlockSize) `max` cipherBlockSize
-- block size of the cipher (16 in case of AES)
cipherBlockSize :: Int
cipherBlockSize = untag (blockSizeBytes :: Tagged cipher ByteLength)