Chunked.hs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. {- |
  2. Functions for encrypting and decrypting long data.
  3. Given a stream cypher and a chunk size,the functions on
  4. this module encrypt long data in a way that requires
  5. constant memory and permits random access to the chunks.
  6. The encryption and decryption operations return lazy data
  7. and lists of encryption errors. Those lists should not
  8. be consumed before the data, as that would cause the
  9. evaluation of all data, and its consequent store on the
  10. memory.
  11. The presence of any error on the error list
  12. invalidates the list equivalent chunk and every subsequent
  13. one. The errors are returned in the form of an either-like
  14. monad, what permits validation of the operation with a
  15. "sequence" call. (Just consume the data first!)
  16. -}
  17. module Crypto.Chunked (
  18. ChunkedCrypto(..),
  19. encrypt,
  20. decrypt
  21. ) where
  22. import Data.ByteString (ByteString)
  23. import qualified Data.ByteString as BS
  24. import qualified Data.ByteString.Lazy as LBS
  25. import Data.Bits
  26. import Data.Int
  27. import Data.Word8
  28. import Crypto.Error
  29. import Debug.Trace
  30. type Nonce = ByteString
  31. -- | A set of encryption programs
  32. data ChunkedCrypto = ChunkedCrypto {
  33. encryptGen :: Nonce -> ByteString -> CryptoFailable ByteString,
  34. decryptGen :: Nonce -> ByteString -> CryptoFailable ByteString,
  35. plainSize :: Int64,
  36. encryptedSize :: Int64
  37. }
  38. encrypt ::
  39. -- | The algorithm used
  40. ChunkedCrypto ->
  41. -- | The nonce for this operation
  42. Nonce ->
  43. -- | The chunk index (starting at 0) of the start of the data.
  44. -- If reading from the begining of the data, use 0.
  45. Int64 ->
  46. -- | Data to be encrypted.
  47. LBS.ByteString ->
  48. (LBS.ByteString, [CryptoFailable ()])
  49. encrypt _ _ _ t | LBS.null t = (LBS.empty, [])
  50. encrypt algo nonce firstChunk plainText = let
  51. (d, dd) = LBS.splitAt (fromIntegral . plainSize $ algo) plainText
  52. cyp = encryptGen algo $ mixNonce firstChunk nonce
  53. (ct, st) = case cyp . LBS.toStrict $ d of
  54. CryptoPassed txt -> (txt, CryptoPassed ())
  55. CryptoFailed e -> (BS.empty, CryptoFailed e)
  56. (cct, sst) = encrypt algo nonce (firstChunk+1) dd
  57. in trace (show . LBS.length $ d) (LBS.append (LBS.fromStrict ct) cct, st:sst)
  58. decrypt ::
  59. -- | The algorithm used
  60. ChunkedCrypto ->
  61. -- | The nonce for this operation
  62. Nonce ->
  63. -- | The chunk index (starting at 0) of the start of the data.
  64. -- If reading from the begining of the data, use 0.
  65. Int64 ->
  66. -- | Encrypted data to be decrypted
  67. LBS.ByteString ->
  68. (LBS.ByteString, [CryptoFailable ()])
  69. decrypt _ _ _ t | LBS.null t = (LBS.empty, [])
  70. decrypt algo nonce firstChunk plainText = let
  71. (d, dd) = LBS.splitAt (fromIntegral . encryptedSize $ algo) plainText
  72. dec = decryptGen algo $ mixNonce firstChunk nonce
  73. (pt, st) = case dec . LBS.toStrict $ d of
  74. CryptoPassed txt -> (txt, CryptoPassed ())
  75. CryptoFailed e -> (BS.empty, CryptoFailed e)
  76. (ppt, sst) = decrypt algo nonce (firstChunk+1) dd
  77. in trace (show . LBS.length $ d) (LBS.append (LBS.fromStrict pt) ppt, st:sst)
  78. toBytes :: Integral a => a -> [Word8]
  79. toBytes x
  80. | x <= 0 = []
  81. | otherwise = fromIntegral (mod x 256) : toBytes (div x 256)
  82. xorlist :: [Word8] -> [Word8] -> [Word8]
  83. xorlist [] n = n
  84. xorlist _ [] = [] -- Must preserve nonce length
  85. xorlist (c:cc) (n:nn) = xor c n : xorlist cc nn
  86. mixNonce c n = BS.pack $ xorlist (toBytes c) (BS.unpack n)