{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} module Z.Crypto.CipherSpec where import Control.Monad import Test.Hspec import Test.HUnit import Z.IO import qualified Z.IO.FileSystem as FS import Z.Data.ASCII import Z.Data.Vector.Hex import qualified Z.Data.Parser as P import qualified Z.Data.Vector as V import qualified Z.Data.Text as T import Z.Crypto.Cipher import Z.Crypto.Hash import Z.Crypto.SafeMem import Utils spec :: Spec spec = do describe "Crypto.Cipher.BlockCipher" $ do forM_ [ ("aes.vec", "AES-128", AES128) , ("aes.vec", "AES-192", AES192) , ("aes.vec", "AES-256", AES256) , ("aria.vec", "ARIA-128", ARIA128) , ("aria.vec", "ARIA-192", ARIA192) , ("aria.vec", "ARIA-256", ARIA256) , ("blowfish.vec", "Blowfish", Blowfish) , ("camellia.vec", "Camellia-128", Camellia128) , ("camellia.vec", "Camellia-192", Camellia192) , ("camellia.vec", "Camellia-256", Camellia256) , ("cascade.vec", "Cascade(Serpent,Twofish)", Cascade Serpent Twofish) , ("cascade.vec", "Cascade(Serpent,AES-256)", Cascade Serpent AES256) , ("cascade.vec", "Cascade(Serpent,CAST-128)", Cascade Serpent CAST128) , ("cast128.vec", "CAST-128", CAST128) , ("cast256.vec", "CAST-256", CAST256) , ("des.vec", "DES", DES) , ("idea.vec", "IDEA", IDEA) , ("kasumi.vec", "KASUMI", KASUMI) , ("lion.vec", "Lion(SHA-160,RC4,64)", Lion SHA160 RC4 64) , ("misty.vec", "MISTY1", MISTY1) , ("noekeon.vec", "Noekeon", Noekeon) , ("seed.vec", "SEED", SEED) , ("serpent.vec", "Serpent", Serpent) , ("shacal2.vec", "SHACAL2", SHACAL2) -- dont support iterations -- , ("sm4.vec", "SM4", SM4) -- dont support tweak -- , ("threefish.vec", "Threefish-512", Threefish512) , ("twofish.vec", "Twofish", Twofish) , ("xtea.vec", "XTEA", XTEA) ] $ \ (file, algoName, cipherType) -> it (T.unpack $ T.validate algoName) $ do tvMap <- parseBlockCipherTestVector =<< "./third_party/botan/src/tests/data/block/" `FS.join` file tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap forM_ tvs $ \ (key0, i, o) -> do key <- unsafeSecretFromBytes key0 c <- newBlockCipher cipherType setBlockCipherKey c key o' <- encryptBlocks c i (V.length i `quot` blockCipherSize c) o' @=? o i' <- decryptBlocks c o (V.length o `quot` blockCipherSize c) i' @=? i describe "Crypto.Cipher.CipherMode" $ do forM_ [ ("cbc.vec", "DES/CBC/NoPadding", CBC_NoPadding DES) , ("cbc.vec", "CAST-128/CBC/PKCS7", CBC_PKCS7 CAST128) , ("cbc.vec", "DES/CBC/PKCS7", CBC_PKCS7 DES) , ("cbc.vec", "TripleDES/CBC/NoPadding", CBC_NoPadding TripleDES) , ("cbc.vec", "TripleDES/CBC/PKCS7", CBC_PKCS7 TripleDES) , ("cbc.vec", "Blowfish/CBC/NoPadding", CBC_NoPadding Blowfish) , ("cbc.vec", "Noekeon/CBC/PKCS7", CBC_PKCS7 Noekeon) , ("cbc.vec", "DES/CBC/OneAndZeros", CBC_OneAndZeros DES) , ("cbc.vec", "DES/CBC/CTS", CBC_CTS DES) , ("cbc.vec", "AES-128/CBC/PKCS7", CBC_PKCS7 AES128) , ("cbc.vec", "AES-128/CBC/NoPadding", CBC_NoPadding AES128) , ("cbc.vec", "AES-192/CBC/NoPadding", CBC_NoPadding AES192) , ("cbc.vec", "AES-256/CBC/NoPadding", CBC_NoPadding AES256) , ("cbc.vec", "AES-128/CBC/CTS", CBC_CTS AES128) , ("cbc.vec", "ARIA-256/CBC/NoPadding", CBC_NoPadding ARIA256) , ("cfb.vec", "DES/CFB", CFB DES) , ("cfb.vec", "DES/CFB(32)", CFB' DES 32) , ("cfb.vec", "DES/CFB(16)", CFB' DES 16) , ("cfb.vec", "DES/CFB(8)", CFB' DES 8) , ("cfb.vec", "TripleDES/CFB", CFB TripleDES) , ("cfb.vec", "TripleDES/CFB(8)", CFB' TripleDES 8) , ("cfb.vec", "AES-128/CFB(8)", CFB' AES128 8) , ("cfb.vec", "AES-192/CFB(8)", CFB' AES192 8) , ("cfb.vec", "AES-256/CFB(8)", CFB' AES256 8) , ("cfb.vec", "AES-128/CFB", CFB AES128) , ("cfb.vec", "AES-192/CFB", CFB AES192) , ("cfb.vec", "AES-256/CFB", CFB AES256) , ("xts.vec", "AES-128/XTS", XTS AES128) , ("xts.vec", "AES-256/XTS", XTS AES256) , ("xts.vec", "Twofish/XTS", XTS Twofish) , ("xts.vec", "Serpent/XTS", XTS Serpent) , ("xts.vec", "TripleDES/XTS", XTS TripleDES) , ("xts.vec", "SHACAL2/XTS", XTS SHACAL2) , ("xts.vec", "Threefish-512/XTS", XTS Threefish512) ] $ \ (file, algoName, cipherType) -> it (T.unpack $ T.validate algoName) $ do tvMap <- parseCipherModeTestVector =<< "./third_party/botan/src/tests/data/modes/" `FS.join` file tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap forM_ tvs $ \ (key0, nonce, i, o) -> do key <- unsafeSecretFromBytes key0 c <- newCipher cipherType CipherEncrypt setCipherKey c key d <- newCipher cipherType CipherDecrypt setCipherKey d key o' <- runCipher c nonce i "" o' @=? o i' <- runCipher d nonce o "" i' @=? i describe "Crypto.Cipher.AEAD" $ do forM_ [ ("ccm.vec", "AES-128/CCM(8,2)", CCM' AES128 8 2) , ("chacha20poly1305.vec", "ChaCha20Poly1305", ChaCha20Poly1305) ] $ \ (file, algoName, cipherType) -> it (T.unpack $ T.validate algoName) $ do tvMap <- parseCipherAEADTestVector =<< "./third_party/botan/src/tests/data/aead/" `FS.join` file tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap forM_ tvs $ \ (key0, nonce, i, ad, o) -> do key <- unsafeSecretFromBytes key0 c <- newCipher cipherType CipherEncrypt setCipherKey c key d <- newCipher cipherType CipherDecrypt setCipherKey d key o' <- runCipher c nonce i ad o' @=? o i' <- runCipher d nonce o ad i' @=? i describe "Crypto.Cipher.StreamCipher(I)" $ do forM_ [ ("ctr.vec", "CTR-BE(DES)", CTR_BE DES) , ("ctr.vec", "CTR-BE(AES-128)", CTR_BE AES128) ] $ \ (file, algoName, cipherType) -> it (T.unpack $ T.validate algoName) $ do tvMap <- parseCipherModeTestVector =<< "./third_party/botan/src/tests/data/modes/" `FS.join` file tvs <- unwrap' "ENOTFOUND" "no algo founded" $ lookup algoName tvMap forM_ tvs $ \ (key0, nonce, i, o) -> do key <- unsafeSecretFromBytes key0 c <- newStreamCipher cipherType setStreamCipherKey c key setStreamCipherIV c nonce d <- newStreamCipher cipherType setStreamCipherKey d key setStreamCipherIV d nonce o' <- runStreamCipher c i o' @?= o i' <- runStreamCipher d o i' @?= i