packages feed

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 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