nettle-0.4: src/Tests/Ciphers/TestModes.hs
module Ciphers.TestModes
( testModes
, testStream
) where
-- source: crypto-cipher-tests
import Test.Framework (Test, testGroup)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.QuickCheck
import Control.Monad (liftM)
import Crypto.Cipher.Types as CCT
import Crypto.Error
import qualified Data.ByteString as B
import Ciphers.Utils
import KAT.Utils
-- | a ECB unit test
data ECBUnit a = ECBUnit B.ByteString a B.ByteString
deriving (Eq)
instance Show (ECBUnit a) where
show (ECBUnit k _ b) = "ECB(key=" ++ show k ++ ",input=" ++ show b ++ ")"
-- | a CBC unit test
data CBCUnit a = CBCUnit B.ByteString a (IV a) B.ByteString
deriving (Eq)
instance (CCT.BlockCipher a) => Show (CBCUnit a) where
show (CBCUnit k _ iv b) = "CBC(key=" ++ show k ++ ",iv=" ++ show (convertToShowable iv) ++ ",input=" ++ show b ++ ")"
-- | a CFB unit test
data CFBUnit a = CFBUnit B.ByteString a (IV a) B.ByteString
deriving (Eq)
instance (CCT.BlockCipher a) => Show (CFBUnit a) where
show (CFBUnit k _ iv b) = "CFB(key=" ++ show k ++ ",iv=" ++ show (convertToShowable iv) ++ ",input=" ++ show b ++ ")"
-- | a CTR unit test
data CTRUnit a = CTRUnit B.ByteString a (IV a) B.ByteString
deriving (Eq)
instance (CCT.BlockCipher a) => Show (CTRUnit a) where
show (CTRUnit k _ iv b) = "CTR(key=" ++ show k ++ ",iv=" ++ show (convertToShowable iv) ++ ",input=" ++ show b ++ ")"
-- | a AEAD unit test
data AEADUnit a = AEADUnit B.ByteString a (IV a) B.ByteString B.ByteString
deriving (Eq)
instance (CCT.BlockCipher a) => Show (AEADUnit a) where
show (AEADUnit k _ iv aad b) = "AEAD(key=" ++ show k ++ ",iv=" ++ show (convertToShowable iv) ++ ",aad=" ++ show aad ++ ",input=" ++ show b ++ ")"
-- | a Stream unit test
data StreamUnit a = StreamUnit B.ByteString a B.ByteString
deriving (Eq)
instance Show (StreamUnit a) where
show (StreamUnit k _ b) = "Stream(key=" ++ show k ++ ",input=" ++ show b ++ ")"
getKey :: Key a b -> b
getKey (Key _ b) = b
genPlaintextBlocks :: CCT.BlockCipher cipher => Gen cipher -> Gen B.ByteString
genPlaintextBlocks c = do
c' <- c
blocks <- choose (1,128)
genBlockCipherInput c' blocks
genPlaintext :: CCT.Cipher cipher => Gen cipher -> Gen B.ByteString
genPlaintext c = do
c' <- c
blocks <- choose (1,324)
genUnalignedBlockCipherInput c' blocks
instance CCT.BlockCipher a => Arbitrary (ECBUnit a) where
arbitrary = let
k = genTypedKey undefined
c = liftM initCipher k
p = genPlaintextBlocks c
in ECBUnit <$> (liftM getKey k) <*> c <*> p
instance CCT.BlockCipher a => Arbitrary (CBCUnit a) where
arbitrary = let
k = genTypedKey undefined
c = liftM initCipher k
iv = c >>= genIV
p = genPlaintextBlocks c
in CBCUnit <$> (liftM getKey k) <*> c <*> iv <*> p
instance CCT.BlockCipher a => Arbitrary (CFBUnit a) where
arbitrary = let
k = genTypedKey undefined
c = liftM initCipher k
iv = c >>= genIV
p = genPlaintextBlocks c
in CFBUnit <$> (liftM getKey k) <*> c <*> iv <*> p
instance CCT.BlockCipher a => Arbitrary (CTRUnit a) where
arbitrary = let
k = genTypedKey undefined
c = liftM initCipher k
iv = c >>= genIV
p = genPlaintext c
in CTRUnit <$> (liftM getKey k) <*> c <*> iv <*> p
instance CCT.BlockCipher a => Arbitrary (AEADUnit a) where
arbitrary = let
k = genTypedKey undefined
c = liftM initCipher k
iv = c >>= genIV
aad = genPlaintext c
p = genPlaintext c
in AEADUnit <$> (liftM getKey k) <*> c <*> iv <*> aad <*> p
instance CCT.StreamCipher a => Arbitrary (StreamUnit a) where
arbitrary = let
k = genTypedKey undefined
c = liftM initCipher k
p = genPlaintext c
in StreamUnit <$> (liftM getKey k) <*> c <*> p
-- | Test a generic block cipher for properties
-- related to block cipher modes.
testModes :: CCT.BlockCipher a => a -> [Test]
testModes cipher =
[ testGroup "decrypt.encrypt==id"
(testBlockCipherBasic cipher ++ testBlockCipherModes cipher ++ testBlockCipherAEAD cipher)
]
testBlockCipherBasic :: CCT.BlockCipher a => a -> [Test]
testBlockCipherBasic cipher = [ testProperty "ECB" ecbProp ]
where ecbProp = toTests cipher
toTests :: CCT.BlockCipher a => a -> ECBUnit a -> Bool
toTests _ = testProperty_ECB
testProperty_ECB (ECBUnit _ ctx plaintext) =
plaintext `assertEq` CCT.ecbDecrypt ctx (CCT.ecbEncrypt ctx plaintext)
testBlockCipherModes :: CCT.BlockCipher a => a -> [Test]
testBlockCipherModes cipher =
[ testProperty "CBC" cbcProp
, testProperty "CFB" cfbProp
, testProperty "CTR" ctrProp
]
where (cbcProp,cfbProp,ctrProp) = toTests cipher
toTests :: CCT.BlockCipher a
=> a
-> ((CBCUnit a -> Bool), (CFBUnit a -> Bool), (CTRUnit a -> Bool))
toTests _ = (testProperty_CBC
,testProperty_CFB
,testProperty_CTR
)
testProperty_CBC (CBCUnit _ ctx testIV plaintext) =
plaintext `assertEq` CCT.cbcDecrypt ctx testIV (CCT.cbcEncrypt ctx testIV plaintext)
testProperty_CFB (CFBUnit _ ctx testIV plaintext) =
plaintext `assertEq` CCT.cfbDecrypt ctx testIV (CCT.cfbEncrypt ctx testIV plaintext)
testProperty_CTR (CTRUnit _ ctx testIV plaintext) =
plaintext `assertEq` CCT.ctrCombine ctx testIV (CCT.ctrCombine ctx testIV plaintext)
testBlockCipherAEAD :: CCT.BlockCipher a => a -> [Test]
testBlockCipherAEAD cipher =
[ testProperty "OCB" (aeadProp CCT.AEAD_OCB)
, testProperty "CCM" (aeadProp (CCT.AEAD_CCM 0 CCM_M16 CCM_L2))
, testProperty "EAX" (aeadProp CCT.AEAD_EAX)
, testProperty "CWC" (aeadProp CCT.AEAD_CWC)
, testProperty "GCM" (aeadProp CCT.AEAD_GCM)
]
where aeadProp = toTests cipher
toTests :: CCT.BlockCipher a => a -> (CCT.AEADMode -> AEADUnit a -> Bool)
toTests _ = testProperty_AEAD
testProperty_AEAD mode (AEADUnit _ ctx testIV aad plaintext) =
case aeadInit mode ctx testIV of
CryptoPassed iniAead ->
let aead = CCT.aeadAppendHeader iniAead aad
(eText, aeadE) = CCT.aeadEncrypt aead plaintext
(dText, aeadD) = CCT.aeadDecrypt aead eText
eTag = CCT.aeadFinalize aeadE (blockSize ctx)
dTag = CCT.aeadFinalize aeadD (blockSize ctx)
in (plaintext `assertEq` dText) && (eTag `assertEq` dTag)
CryptoFailed _ -> True
-- | Test stream mode
testStream :: CCT.StreamCipher a => a -> [Test]
testStream cipher = [testProperty "combine.combine==id" (testStreamUnit cipher)]
where testStreamUnit :: CCT.StreamCipher a => a -> (StreamUnit a -> Bool)
testStreamUnit _ (StreamUnit _ ctx plaintext) =
let cipherText = fst $ CCT.streamCombine ctx plaintext
in fst (CCT.streamCombine ctx cipherText) `assertEq` plaintext