123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- {- |
- Functions for encrypting and decrypting long data.
- Given a stream cypher and a chunk size,the functions on
- this module encrypt long data in a way that requires
- constant memory and permits random access to the chunks.
- The encryption and decryption operations return lazy data
- and lists of encryption errors. Those lists should not
- be consumed before the data, as that would cause the
- evaluation of all data, and its consequent store on the
- memory.
- The presence of any error on the error list
- invalidates the list equivalent chunk and every subsequent
- one. The errors are returned in the form of an either-like
- monad, what permits validation of the operation with a
- "sequence" call. (Just consume the data first!)
- -}
- module Crypto.Chunked (
- ChunkedCrypto(..),
- encrypt,
- decrypt
- ) where
- import Data.ByteString (ByteString)
- import qualified Data.ByteString as BS
- import qualified Data.ByteString.Lazy as LBS
- import Data.Bits
- import Data.Int
- import Data.Word8
- import Crypto.Error
- import Debug.Trace
- type Nonce = ByteString
- -- | A set of encryption programs
- data ChunkedCrypto = ChunkedCrypto {
- encryptGen :: Nonce -> ByteString -> CryptoFailable ByteString,
- decryptGen :: Nonce -> ByteString -> CryptoFailable ByteString,
- plainSize :: Int64,
- encryptedSize :: Int64
- }
- encrypt ::
- -- | The algorithm used
- ChunkedCrypto ->
- -- | The nonce for this operation
- Nonce ->
- -- | The chunk index (starting at 0) of the start of the data.
- -- If reading from the begining of the data, use 0.
- Int64 ->
- -- | Data to be encrypted.
- LBS.ByteString ->
- (LBS.ByteString, [CryptoFailable ()])
- encrypt _ _ _ t | LBS.null t = (LBS.empty, [])
- encrypt algo nonce firstChunk plainText = let
- (d, dd) = LBS.splitAt (fromIntegral . plainSize $ algo) plainText
- cyp = encryptGen algo $ mixNonce firstChunk nonce
- (ct, st) = case cyp . LBS.toStrict $ d of
- CryptoPassed txt -> (txt, CryptoPassed ())
- CryptoFailed e -> (BS.empty, CryptoFailed e)
- (cct, sst) = encrypt algo nonce (firstChunk+1) dd
- in trace (show . LBS.length $ d) (LBS.append (LBS.fromStrict ct) cct, st:sst)
- decrypt ::
- -- | The algorithm used
- ChunkedCrypto ->
- -- | The nonce for this operation
- Nonce ->
- -- | The chunk index (starting at 0) of the start of the data.
- -- If reading from the begining of the data, use 0.
- Int64 ->
- -- | Encrypted data to be decrypted
- LBS.ByteString ->
- (LBS.ByteString, [CryptoFailable ()])
- decrypt _ _ _ t | LBS.null t = (LBS.empty, [])
- decrypt algo nonce firstChunk plainText = let
- (d, dd) = LBS.splitAt (fromIntegral . encryptedSize $ algo) plainText
- dec = decryptGen algo $ mixNonce firstChunk nonce
- (pt, st) = case dec . LBS.toStrict $ d of
- CryptoPassed txt -> (txt, CryptoPassed ())
- CryptoFailed e -> (BS.empty, CryptoFailed e)
- (ppt, sst) = decrypt algo nonce (firstChunk+1) dd
- in trace (show . LBS.length $ d) (LBS.append (LBS.fromStrict pt) ppt, st:sst)
- toBytes :: Integral a => a -> [Word8]
- toBytes x
- | x <= 0 = []
- | otherwise = fromIntegral (mod x 256) : toBytes (div x 256)
- xorlist :: [Word8] -> [Word8] -> [Word8]
- xorlist [] n = n
- xorlist _ [] = [] -- Must preserve nonce length
- xorlist (c:cc) (n:nn) = xor c n : xorlist cc nn
- mixNonce c n = BS.pack $ xorlist (toBytes c) (BS.unpack n)
|