crypto-conduit (empty) → 0.1
raw patch · 6 files changed
+726/−0 lines, 6 filesdep +basedep +bytestringdep +cerealsetup-changed
Dependencies added: base, bytestring, cereal, conduit, crypto-api, crypto-conduit, cryptocipher, cryptohash, hspec, skein
Files
- LICENSE +30/−0
- README +0/−0
- Setup.hs +2/−0
- crypto-conduit.cabal +56/−0
- src/Crypto/Conduit.hs +419/−0
- tests/runtests.hs +219/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2012, Felipe Lessa++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Felipe Lessa nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README view
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ crypto-conduit.cabal view
@@ -0,0 +1,56 @@+Cabal-version: >= 1.8+Name: crypto-conduit+Version: 0.1+Synopsis: Conduit interface for cryptographic operations (from crypto-api).+Homepage: https://github.com/meteficha/crypto-conduit+License: BSD3+License-file: LICENSE+Author: Felipe Lessa <felipe.lessa@gmail.com>+Maintainer: Felipe Lessa <felipe.lessa@gmail.com>+Category: Cryptography+Build-type: Simple++Description:+ This package contains everything that you need to use a+ cryptographic package that supports the @crypto-api@ package+ using conduits from the @conduit@ package.++Extra-source-files:+ README+ tests/runtests.hs++Source-repository head+ Type: git+ Location: git://github.com/meteficha/crypto-conduit.git++Library+ Hs-Source-Dirs: src+ Exposed-modules:+ Crypto.Conduit+ Build-depends:+ base >= 3 && < 5,+ bytestring >= 0.9 && < 0.10,+ cereal >= 0.3 && < 0.4,+ crypto-api >= 0.8 && < 0.9,+ conduit >= 0.0 && < 0.1+ GHC-options: -Wall++Test-suite runtests+ Type: exitcode-stdio-1.0+ Build-depends:+ base >= 3 && < 5,+ bytestring >= 0.9 && < 0.10,+ cereal >= 0.3 && < 0.4,+ crypto-api >= 0.8 && < 0.9,+ conduit >= 0.0 && < 0.1,++ cryptocipher == 0.3.*,+ cryptohash == 0.7.*,+ skein == 0.1.*,+ hspec == 0.9.*,++ -- finally, our own package+ crypto-conduit+ GHC-options: -Wall+ Hs-source-dirs: tests+ Main-is: runtests.hs
+ src/Crypto/Conduit.hs view
@@ -0,0 +1,419 @@+{-# LANGUAGE BangPatterns #-}+-- | This module contains wrappers for cryptographic functions+-- using the @conduit@ package. Currently there is support for+-- hashes, HMACs and many modes of block ciphers (but not+-- everything @crypto-api@ supports has a counterpart here).+-- All functions on this package work in constant memory.+module Crypto.Conduit+ ( -- * Cryptographic hash functions+ sinkHash++ -- * Hash-based message authentication code (HMAC)+ , sinkHmac++ -- * Block ciphers+ -- ** Electronic codebook mode (ECB)+ , conduitEncryptEcb+ , conduitDecryptEcb+ -- ** Cipher-block chaining mode (CBC)+ , conduitEncryptCbc+ , conduitDecryptCbc+ -- ** Cipher feedback mode (CFB)+ , conduitEncryptCfb+ , conduitDecryptCfb+ -- ** Output feedback mode (OFB)+ , conduitEncryptOfb+ , conduitDecryptOfb+ -- ** Counter mode (CTR)+ , conduitEncryptCtr+ , conduitDecryptCtr+ , sourceCtr+ -- ** Cipher-block chaining message authentication code (CBC-MAC)+ , sinkCbcMac++ -- * Helpers+ , blocked+ , BlockMode(..)+ , Block(..)+ ) where++-- from base+import Control.Applicative ((<$>))+import Control.Arrow (first)+import Data.Bits (xor)++-- from bytestring+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L++-- from cereal+import qualified Data.Serialize as S++-- from crypto-api+import Crypto.Classes ((.::.))+import qualified Crypto.Classes as C+import qualified Crypto.HMAC as C+import qualified Crypto.Modes as C+--import qualified Crypto.Padding as C+--import qualified Crypto.Random as C+import qualified Crypto.Types as C++-- from conduit+import Data.Conduit+++-- | Helper to get our return type.+getType :: Monad m => sink input m output -> output+getType = undefined+++----------------------------------------------------------------------+++-- | A 'Sink' that hashes a stream of 'B.ByteString'@s@ and+-- creates a digest @d@.+sinkHash :: (Resource m, C.Hash ctx d) => Sink B.ByteString m d+sinkHash = blocked AnyMultiple blockSize =$ sink+ where+ sink = sinkState C.initialCtx+ push+ (const $ fail "sinkHash")++ push ctx (Full bs) =+ let !ctx' = C.updateCtx ctx bs+ in return (ctx', Processing)+ push ctx (LastOne bs) =+ let !ret = C.finalize ctx bs+ in return (error "sinkHash", Done Nothing ret)++ blockSize = (C.blockLength .::. getType sink) `div` 8+++----------------------------------------------------------------------+++-- | A 'Sink' that computes the HMAC of a stream of+-- 'B.ByteString'@s@ and creates a digest @d@.+sinkHmac :: (Resource m, C.Hash ctx d) => C.MacKey -> Sink B.ByteString m d+sinkHmac (C.MacKey key) = blocked AnyMultiple blockSize =$ sink+ where+ --------- Taken and modified from Crypto.HMAC:+ key' =+ case B.length key `compare` blockSize of+ GT -> B.append+ (S.encode $ C.hashFunc' d key)+ (B.replicate (blockSize - outputSize) 0x00)+ EQ -> key+ LT -> B.append key (B.replicate (blockSize - B.length key) 0x00)+ ko = B.map (`xor` 0x5c) key'+ ki = B.map (`xor` 0x36) key'+ ---------++ sink = sinkState (C.updateCtx C.initialCtx ki)+ push+ (const $ fail "sinkHmac")++ push ctx (Full bs) =+ let !ctx' = C.updateCtx ctx bs+ in return (ctx', Processing)+ push ctx (LastOne bs) =+ let !inner = C.finalize ctx bs `asTypeOf` d+ !outer = C.hash $ L.fromChunks [ko, S.encode inner]+ in return (error "sinkHmac", Done Nothing outer)++ d = getType sink+ blockSize = (C.blockLength .::. d) `div` 8+ outputSize = (C.outputLength .::. d) `div` 8+++----------------------------------------------------------------------+++-- | A 'Conduit' that encrypts a stream of 'B.ByteString'@s@+-- using ECB mode. Expects the input length to be a multiple of+-- the block size of the cipher and fails otherwise. (Note that+-- ECB has many undesirable cryptographic properties, please+-- avoid it if you don't know what you're doing.)+conduitEncryptEcb :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> Conduit B.ByteString m B.ByteString+conduitEncryptEcb k =+ blockCipherConduit k+ AnyMultiple+ ()+ (\_ input -> ((), C.encryptBlock k input))+ (\_ _ -> fail "conduitEncryptEcb: input has an incomplete final block.")+++-- | A 'Conduit' that decrypts a stream of 'B.ByteString'@s@+-- using ECB mode. Expects the input length to be a multiple of+-- the block size of the cipher and fails otherwise.+conduitDecryptEcb :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> Conduit B.ByteString m B.ByteString+conduitDecryptEcb k =+ blockCipherConduit k+ AnyMultiple+ ()+ (\_ input -> ((), C.decryptBlock k input))+ (\_ _ -> fail "conduitDecryptEcb: input has an incomplete final block.")+++----------------------------------------------------------------------+++-- | A 'Conduit' that encrypts a stream of 'B.ByteString'@s@+-- using CBC mode. Expects the input length to be a multiple of+-- the block size of the cipher and fails otherwise.+conduitEncryptCbc :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> Conduit B.ByteString m B.ByteString+conduitEncryptCbc k iv =+ blockCipherConduit k+ StrictBlockSize+ (S.encode iv)+ (\iv' input -> let output = C.encryptBlock k (iv' `zwp` input)+ in (output, output))+ (\_ _ -> fail "conduitEncryptCbc: input has an incomplete final block.")+++-- | A 'Conduit' that decrypts a stream of 'B.ByteString'@s@+-- using CBC mode. Expects the input length to be a multiple of+-- the block size of the cipher and fails otherwise.+conduitDecryptCbc :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> Conduit B.ByteString m B.ByteString+conduitDecryptCbc k iv =+ blockCipherConduit k+ StrictBlockSize+ (S.encode iv)+ (\iv' input -> let output = C.decryptBlock k input `zwp` iv'+ in (input, output))+ (\_ _ -> fail "conduitDecryptCbc: input has an incomplete final block.")+++----------------------------------------------------------------------+++-- | A 'Conduit' that encrypts a stream of 'B.ByteString'@s@+-- using CFB mode. Expects the input length to be a multiple of+-- the block size of the cipher and fails otherwise.+conduitEncryptCfb :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> Conduit B.ByteString m B.ByteString+conduitEncryptCfb k iv =+ blockCipherConduit k+ StrictBlockSize+ (S.encode iv)+ (\iv' input -> let output = C.encryptBlock k iv' `zwp` input+ in (output, output))+ (\_ _ -> fail "conduitEncryptCfb: input has an incomplete final block.")+++-- | A 'Conduit' that decrypts a stream of 'B.ByteString'@s@+-- using CFB mode. Expects the input length to be a multiple of+-- the block size of the cipher and fails otherwise.+conduitDecryptCfb :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> Conduit B.ByteString m B.ByteString+conduitDecryptCfb k iv =+ blockCipherConduit k+ StrictBlockSize+ (S.encode iv)+ (\iv' input -> let output = C.encryptBlock k iv' `zwp` input+ in (input, output))+ (\_ _ -> fail "conduitDecryptCfb: input has an incomplete final block.")+++----------------------------------------------------------------------+++-- | A 'Conduit' that encrypts a stream of 'B.ByteString'@s@+-- using OFB mode. Expects the input length to be a multiple of+-- the block size of the cipher and fails otherwise.+conduitEncryptOfb :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> Conduit B.ByteString m B.ByteString+conduitEncryptOfb k iv =+ blockCipherConduit k+ StrictBlockSize+ (S.encode iv)+ (\iv' input -> let inter = C.encryptBlock k iv'+ in (inter, inter `zwp` input))+ (\_ _ -> fail "conduitEncryptOfb: input has an incomplete final block.")+++-- | Synonym for 'conduitEncryptOfb', since for OFB mode both+-- encryption and decryption are the same.+conduitDecryptOfb :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> Conduit B.ByteString m B.ByteString+conduitDecryptOfb = conduitEncryptOfb+++----------------------------------------------------------------------+++-- | A 'Conduit' that encrypts a stream of 'B.ByteString'@s@+-- using CTR mode. The input may have any length, even+-- non-multiples of the block size.+conduitEncryptCtr :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> (C.IV k -> C.IV k) -- ^ Increment counter ('C.incIV' is recommended)+ -> Conduit B.ByteString m B.ByteString+conduitEncryptCtr k iv incIV =+ blockCipherConduit k+ StrictBlockSize+ iv+ (\iv' input -> let !iv'' = incIV iv'+ output = C.encryptBlock k (S.encode iv') `zwp` input+ in (iv'', output))+ (\iv' input -> let output = C.encryptBlock k (S.encode iv') `zwp` input+ in return output)+++-- | Synonym for 'conduitEncryptCtr', since for CTR mode both+-- encryption and decryption are the same.+conduitDecryptCtr :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> (C.IV k -> C.IV k) -- ^ Increment counter ('C.incIV' is recommended)+ -> Conduit B.ByteString m B.ByteString+conduitDecryptCtr = conduitEncryptCtr+++-- | An infinite stream of bytes generated by a block cipher on+-- CTR mode.+sourceCtr :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> C.IV k -- ^ Initialization vector.+ -> Source m B.ByteString+sourceCtr k iv = sourceState iv pull+ where+ pull iv' =+ let !iv'' = C.incIV iv'+ block = C.encryptBlock k $ S.encode iv'+ in return (iv'', Open block)+++----------------------------------------------------------------------+++-- | A 'Sink' that computes the CBC-MAC of a stream of+-- 'B.ByteString'@s@ and creates a digest @d@. Expects the input+-- length to be a multiple of the block size of the cipher and+-- fails otherwise. (Note that CBC-MAC is not secure for+-- variable-length messages.)+sinkCbcMac :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key.+ -> Sink B.ByteString m B.ByteString+sinkCbcMac k = blocked StrictBlockSize blockSize =$ sink+ where+ sink = sinkState (B.replicate blockSize 0) push close++ push iv (Full input) =+ let !iv' = C.encryptBlock k (iv `zwp` input)+ in return (iv', Processing)+ push iv (LastOne input)+ | B.null input = return (error "sinkCbcMac", Done Nothing iv)+ | otherwise = fail "sinkCbcMac: input has an incomplete final block."++ close _ = fail "sinkCbcMac"++ blockSize = (C.blockSize .::. k) `div` 8+++----------------------------------------------------------------------+++-- | A 'Conduit' that takes arbitrary 'B.ByteString'@s@ and+-- outputs 'Block'@s@. Each 'Full' block will have a length that+-- is multiple of the given block size (either exactly the block+-- size or a multiple of at least 1x the block size, depending on+-- the 'BlockMode'). All 'Block'@s@ beside the last one will be+-- 'Full'. The last block will always be 'LastOne' with less+-- bytes than the block size, possibly zero.+blocked :: Resource m =>+ BlockMode+ -> C.ByteLength -- ^ Block size+ -> Conduit B.ByteString m Block+blocked mode blockSize = conduitState B.empty push close+ where+ block = case mode of+ StrictBlockSize -> blockStrict []+ AnyMultiple -> blockAny+ where+ blockStrict acc bs+ | B.length bs < blockSize = (reverse acc, bs)+ | otherwise = blockStrict (Full this : acc) rest+ where (this, rest) = B.splitAt blockSize bs++ blockAny bs+ | n >= 1 = first ((:[]) . Full) $ B.splitAt (n * blockSize) bs+ | otherwise = ([], bs)+ where n = B.length bs `div` blockSize++ append bs1 bs2+ | B.null bs1 = bs2+ | otherwise = B.append bs1 bs2++ push acc = return . mk . block . append acc+ where+ mk (blks, rest) = (rest, Producing blks)++ close = return . (:[]) . LastOne+++-- | How 'Block's should be returned, either with strictly the+-- block size or with a multiple of at least 1x the block size.+data BlockMode = StrictBlockSize | AnyMultiple+ deriving (Eq, Ord, Show, Enum)+++-- | A block returned by 'blocked'.+data Block = Full B.ByteString | LastOne B.ByteString+ deriving (Eq, Ord, Show)+++-- | Constructs a 'Conduit' for a 'BlockCipher'.+blockCipherConduit :: (Resource m, C.BlockCipher k) =>+ k -- ^ Cipher key (not used, just for getting block size).+ -> BlockMode+ -> s -- ^ Initial state.+ -> (s -> B.ByteString -> (s, B.ByteString)) -- ^ Encrypt block.+ -> (s -> B.ByteString -> ResourceT m B.ByteString) -- ^ Final encryption.+ -> Conduit B.ByteString m B.ByteString+blockCipherConduit key mode initialState apply final = blocked mode blockSize =$= conduit+ where+ blockSize = (C.blockSize .::. key) `div` 8++ conduit = conduitState initialState push close++ push state (Full input) =+ let (!state', !output) = apply state input+ in return (state', Producing [output])+ push _ (LastOne input) | B.null input =+ return (error "blockCipherConduit", Finished Nothing [])+ push state (LastOne input) = mk <$> final state input+ where mk output = (error "blockCipherConduit", Finished Nothing [output])++ close _ = fail "blockCipherConduit"+++-- | zipWith xor + pack+--+-- As a result of rewrite rules, this should automatically be+-- optimized (at compile time) to use the bytestring libraries+-- 'zipWith'' function.+--+-- Taken from crypto-api.+zwp :: B.ByteString -> B.ByteString -> B.ByteString+zwp a = B.pack . B.zipWith xor a+{-# INLINEABLE zwp #-}
+ tests/runtests.hs view
@@ -0,0 +1,219 @@+{-# LANGUAGE Rank2Types #-}++-- from base+import Control.Applicative ((<$>))+import Control.Monad.ST (runST)+import Data.Word (Word8)++-- from bytestring+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L++-- from crypto-api+import Crypto.Classes ((.::.))+import qualified Crypto.Classes as C+import qualified Crypto.HMAC as C+import qualified Crypto.Modes as C+--import qualified Crypto.Padding as C+--import qualified Crypto.Random as C+import qualified Crypto.Types as C++-- from conduit+import Data.Conduit+import Data.Conduit.Binary (isolate)+import Data.Conduit.List (sourceList, consume)++-- from cryptocipher+import Crypto.Cipher.AES (AES128, AES192, AES256)++-- from cryptohash+import Crypto.Hash.MD2 (MD2)+import Crypto.Hash.MD4 (MD4)+import Crypto.Hash.MD5 (MD5)+import Crypto.Hash.RIPEMD160 (RIPEMD160)+import Crypto.Hash.SHA1 (SHA1)+import Crypto.Hash.SHA224 (SHA224)+import Crypto.Hash.SHA256 (SHA256)+import Crypto.Hash.SHA384 (SHA384)+import Crypto.Hash.SHA512 (SHA512)+import Crypto.Hash.Skein256 (Skein256)+import Crypto.Hash.Skein512 (Skein512)+import Crypto.Hash.Tiger (Tiger)++-- from skein+import qualified Crypto.Skein as Skein++-- from hspec+import Test.Hspec.Monadic+import Test.Hspec.QuickCheck+import Test.Hspec.HUnit ()++-- from this package+import Crypto.Conduit++++main :: IO ()+main = hspecX $ do+ describe "cryptohash's MD2" $ testHash (undefined :: MD2)+ describe "cryptohash's MD4" $ testHash (undefined :: MD4)+ describe "cryptohash's MD5" $ testHash (undefined :: MD5)+ describe "cryptohash's RIPEMD160" $ testHash (undefined :: RIPEMD160)+ describe "cryptohash's SHA1" $ testHash (undefined :: SHA1)+ describe "cryptohash's SHA224" $ testHash (undefined :: SHA224)+ describe "cryptohash's SHA256" $ testHash (undefined :: SHA256)+ describe "cryptohash's SHA384" $ testHash (undefined :: SHA384)+ describe "cryptohash's SHA512" $ testHash (undefined :: SHA512)+ describe "cryptohash's Skein256" $ testHash (undefined :: Skein256)+ describe "cryptohash's Skein512" $ testHash (undefined :: Skein512)+ describe "cryptohash's Tiger" $ testHash (undefined :: Tiger)+ describe "skein's Skein_512_512" $ testHash (undefined :: Skein.Skein_512_512)+ describe "skein's Skein_1024_1024" $ testHash (undefined :: Skein.Skein_1024_1024)+ describe "skein's Skein_256_256" $ testHash (undefined :: Skein.Skein_256_256)+ describe "skein's Skein_256_128" $ testHash (undefined :: Skein.Skein_256_128)+ describe "skein's Skein_256_160" $ testHash (undefined :: Skein.Skein_256_160)+ describe "skein's Skein_256_224" $ testHash (undefined :: Skein.Skein_256_224)+ describe "skein's Skein_512_128" $ testHash (undefined :: Skein.Skein_512_128)+ describe "skein's Skein_512_160" $ testHash (undefined :: Skein.Skein_512_160)+ describe "skein's Skein_512_224" $ testHash (undefined :: Skein.Skein_512_224)+ describe "skein's Skein_512_256" $ testHash (undefined :: Skein.Skein_512_256)+ describe "skein's Skein_512_384" $ testHash (undefined :: Skein.Skein_512_384)+ describe "skein's Skein_1024_384" $ testHash (undefined :: Skein.Skein_1024_384)+ describe "skein's Skein_1024_512" $ testHash (undefined :: Skein.Skein_1024_512)+ describe "cryptocipher's AES128" $ testBlockCipher (undefined :: AES128)+ describe "cryptocipher's AES192" $ testBlockCipher (undefined :: AES192)+ describe "cryptocipher's AES256" $ testBlockCipher (undefined :: AES256)+++----------------------------------------------------------------------+++testHash :: C.Hash ctx d => d -> Specs+testHash d = do+ prop "works with sinkHash" $+ \str -> prop_sinkHash d (L.pack str)+ prop "works with sinkHmac" $+ \key str -> prop_sinkHmac d (C.MacKey $ B.pack key) (L.pack str)+++prop_sinkHash :: C.Hash ctx d => d -> L.ByteString -> Bool+prop_sinkHash d input =+ let d1 = runPureResource $ sourceList (L.toChunks input) $$ sinkHash+ d2 = C.hashFunc d input+ in d1 == d2+++prop_sinkHmac :: C.Hash ctx d => d -> C.MacKey -> L.ByteString -> Bool+prop_sinkHmac d mackey input =+ let d1 = runPureResource $ sourceList (L.toChunks input) $$ sinkHmac mackey+ d2 = C.hmac mackey input `asTypeOf` d+ in d1 == d2+++----------------------------------------------------------------------+++testBlockCipher :: C.BlockCipher k => k -> Specs+testBlockCipher undefinedKey = do+ let Just k =+ let len = (C.keyLength .::. k) `div` 8+ in C.buildKey (B.replicate len 0xFF) `asTypeOf` Just undefinedKey+ blockSize = (C.blockSize .::. k) `div` 8++ prop "works with conduitEncryptEcb" $+ testBlockCipherConduit+ (Just blockSize)+ (conduitEncryptEcb k)+ (C.ecb k)+ prop "works with conduitDecryptEcb" $+ testBlockCipherConduit+ (Just blockSize)+ (conduitDecryptEcb k)+ (C.unEcb k)++ prop "works with conduitEncryptCbc" $+ testBlockCipherConduit+ (Just blockSize)+ (conduitEncryptCbc k C.zeroIV)+ (fst . C.cbc k C.zeroIV)+ prop "works with conduitDecryptCbc" $+ testBlockCipherConduit+ (Just blockSize)+ (conduitDecryptCbc k C.zeroIV)+ (fst . C.unCbc k C.zeroIV)++ prop "works with conduitEncryptCfb" $+ testBlockCipherConduit+ (Just blockSize)+ (conduitEncryptCfb k C.zeroIV)+ (fst . C.cfb k C.zeroIV)+ prop "works with conduitDecryptCfb" $+ testBlockCipherConduit+ (Just blockSize)+ (conduitDecryptCfb k C.zeroIV)+ (fst . C.unCfb k C.zeroIV)++ prop "works with conduitEncryptOfb" $+ testBlockCipherConduit+ (Just blockSize)+ (conduitEncryptOfb k C.zeroIV)+ (fst . C.ofb k C.zeroIV)+ prop "works with conduitDecryptOfb" $+ testBlockCipherConduit+ (Just blockSize)+ (conduitDecryptOfb k C.zeroIV)+ (fst . C.unOfb k C.zeroIV)++ prop "works with conduitEncryptCtr" $+ testBlockCipherConduit+ Nothing+ (conduitEncryptCtr k C.zeroIV C.incIV)+ (fst . C.ctr C.incIV k C.zeroIV)+ prop "works with conduitDecryptCtr" $+ testBlockCipherConduit+ Nothing+ (conduitDecryptCtr k C.zeroIV C.incIV)+ (fst . C.unCtr C.incIV k C.zeroIV)++ it "works with sourceCtr" $+ let len :: Num a => a+ len = 1024 * 1024 -- 1 MiB+ r1 = runPureResource $ sourceCtr k C.zeroIV $$ isolate len =$ consumeAsLazy+ r2 = fst $ C.ctr C.incIV k C.zeroIV (L.replicate len 0)+ in r1 == r2++ prop "works with sinkCbcMac" $+ \input -> let inputL = fixBlockedSize blockSize (L.pack input)+ r1 = runPureResource $ sourceList (L.toChunks inputL) $$ sinkCbcMac k+ r2 = B.concat $ L.toChunks $ C.cbcMac k inputL+ in r1 == r2+++testBlockCipherConduit ::+ Maybe C.ByteLength -- ^ Fix input length to be a multiple of the block size?+ -> (forall m. Resource m => Conduit B.ByteString m B.ByteString)+ -> (L.ByteString -> L.ByteString)+ -> [Word8]+ -> Bool+testBlockCipherConduit mblockSize conduit lazyfun input =+ let inputL = maybe id fixBlockedSize mblockSize (L.pack input)+ r1 = runPureResource $ sourceList (L.toChunks inputL) $$ conduit =$ consumeAsLazy+ r2 = lazyfun inputL+ in r1 == r2+++----------------------------------------------------------------------+++runPureResource :: (forall m. Resource m => ResourceT m a) -> a+runPureResource r = runST (runResourceT r)++consumeAsLazy :: Resource m => Sink B.ByteString m L.ByteString+consumeAsLazy = L.fromChunks <$> consume++fixBlockedSize :: C.ByteLength -> L.ByteString -> L.ByteString+fixBlockedSize blockSize lbs =+ let blockSize' = fromIntegral blockSize+ toFill = let leftovers = L.length lbs `mod` blockSize'+ in if leftovers == 0 then 0 else blockSize' - leftovers+ in L.append lbs $ L.replicate toFill 0xFF