module Crypto.Cipher.Tests.KATs where import Data.ByteString (ByteString) import Test.Framework (Test, testGroup, TestName) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit ((@?=)) import Crypto.Cipher.Types import Data.Maybe -- | ECB KAT data KAT_ECB = KAT_ECB { ecbKey :: ByteString -- ^ Key , ecbPlaintext :: ByteString -- ^ Plaintext , ecbCiphertext :: ByteString -- ^ Ciphertext } deriving (Show,Eq) -- | CBC KAT data KAT_CBC = KAT_CBC { cbcKey :: ByteString -- ^ Key , cbcIV :: ByteString -- ^ IV , cbcPlaintext :: ByteString -- ^ Plaintext , cbcCiphertext :: ByteString -- ^ Ciphertext } deriving (Show,Eq) -- | CTR KAT data KAT_CTR = KAT_CTR { ctrKey :: ByteString -- ^ Key , ctrIV :: ByteString -- ^ IV (usually represented as a 128 bits integer) , ctrPlaintext :: ByteString -- ^ Plaintext , ctrCiphertext :: ByteString -- ^ Ciphertext } deriving (Show,Eq) -- | XTS KAT data KAT_XTS = KAT_XTS { xtsKey1 :: ByteString -- ^ 1st XTS key , xtsKey2 :: ByteString -- ^ 2nd XTS key , xtsIV :: ByteString -- ^ XTS IV , xtsPlaintext :: ByteString -- ^ plaintext , xtsCiphertext :: ByteString -- ^ Ciphertext } deriving (Show,Eq) -- | AEAD KAT data KAT_AEAD = KAT_AEAD { aeadMode :: AEADMode -- ^ AEAD mode to use , aeadKey :: ByteString -- ^ Key , aeadIV :: ByteString -- ^ IV for initialization , aeadHeader :: ByteString -- ^ Authentificated Header , aeadPlaintext :: ByteString -- ^ Plaintext , aeadCiphertext :: ByteString -- ^ Ciphertext , aeadTaglen :: Int -- ^ aead tag len , aeadTag :: AuthTag -- ^ expected tag } deriving (Show,Eq) -- | all the KATs. use defaultKATs to prevent compilation error -- from future expansion of this data structure data KATs = KATs { kat_ECB :: [KAT_ECB] , kat_CBC :: [KAT_CBC] , kat_CTR :: [KAT_CTR] , kat_XTS :: [KAT_XTS] , kat_AEAD :: [KAT_AEAD] } deriving (Show,Eq) -- | KAT for Stream cipher data KAT_Stream = KAT_Stream { streamKey :: ByteString , streamPlaintext :: ByteString , streamCiphertext :: ByteString } deriving (Show,Eq) -- | the empty KATs defaultKATs :: KATs defaultKATs = KATs { kat_ECB = [] , kat_CBC = [] , kat_CTR = [] , kat_XTS = [] , kat_AEAD = [] } -- | the empty KATs for stream defaultStreamKATs :: [KAT_Stream] defaultStreamKATs = [] -- | tests related to KATs testKATs :: BlockCipher cipher => KATs -> cipher -> Test testKATs kats cipher = testGroup "KAT" ( maybeGroup makeECBTest "ECB" (kat_ECB kats) ++ maybeGroup makeCBCTest "CBC" (kat_CBC kats) ++ maybeGroup makeCTRTest "CTR" (kat_CTR kats) ++ maybeGroup makeXTSTest "XTS" (kat_XTS kats) ++ maybeGroup makeAEADTest "AEAD" (kat_AEAD kats) ) where makeECBTest i d = [ testCase ("E" ++ i) (ecbEncrypt ctx (ecbPlaintext d) @?= ecbCiphertext d) , testCase ("D" ++ i) (ecbDecrypt ctx (ecbCiphertext d) @?= ecbPlaintext d) ] where ctx = cipherInit (cipherMakeKey cipher $ ecbKey d) makeCBCTest i d = [ testCase ("E" ++ i) (cbcEncrypt ctx iv (cbcPlaintext d) @?= cbcCiphertext d) , testCase ("D" ++ i) (cbcDecrypt ctx iv (cbcCiphertext d) @?= cbcPlaintext d) ] where ctx = cipherInit (cipherMakeKey cipher $ cbcKey d) iv = cipherMakeIV cipher $ cbcIV d makeCTRTest i d = [ testCase ("E" ++ i) (ctrCombine ctx iv (ctrPlaintext d) @?= ctrCiphertext d) , testCase ("D" ++ i) (ctrCombine ctx iv (ctrCiphertext d) @?= ctrPlaintext d) ] where ctx = cipherInit (cipherMakeKey cipher $ ctrKey d) iv = cipherMakeIV cipher $ ctrIV d makeXTSTest i d = [ testCase ("E" ++ i) (xtsEncrypt ctx iv 0 (xtsPlaintext d) @?= xtsCiphertext d) , testCase ("D" ++ i) (xtsDecrypt ctx iv 0 (xtsCiphertext d) @?= xtsPlaintext d) ] where ctx1 = cipherInit (cipherMakeKey cipher $ xtsKey1 d) ctx2 = cipherInit (cipherMakeKey cipher $ xtsKey2 d) ctx = (ctx1, ctx2) iv = cipherMakeIV cipher $ xtsIV d makeAEADTest i d = [ testCase ("AE" ++ i) (etag @?= aeadTag d) , testCase ("AD" ++ i) (dtag @?= aeadTag d) , testCase ("E" ++ i) (ebs @?= aeadCiphertext d) , testCase ("D" ++ i) (dbs @?= aeadPlaintext d) ] where ctx = cipherInit (cipherMakeKey cipher $ aeadKey d) (Just aead) = aeadInit (aeadMode d) ctx (aeadIV d) aeadHeaded = aeadAppendHeader aead (aeadHeader d) (ebs,aeadEFinal) = aeadEncrypt aeadHeaded (aeadPlaintext d) (dbs,aeadDFinal) = aeadDecrypt aeadHeaded (aeadCiphertext d) etag = aeadFinalize aeadEFinal (aeadTaglen d) dtag = aeadFinalize aeadDFinal (aeadTaglen d) cipherMakeIV :: BlockCipher cipher => cipher -> ByteString -> IV cipher cipherMakeIV _ bs = fromJust $ makeIV bs testStreamKATs :: StreamCipher cipher => [KAT_Stream] -> cipher -> Test testStreamKATs kats cipher = testGroup "KAT" $ maybeGroup makeStreamTest "Stream" kats where makeStreamTest i d = [ testCase ("E" ++ i) (fst (streamCombine ctx (streamPlaintext d)) @?= streamCiphertext d) , testCase ("D" ++ i) (fst (streamCombine ctx (streamCiphertext d)) @?= streamPlaintext d) ] where ctx = cipherInit (cipherMakeKey cipher $ streamKey d) cipherMakeKey :: Cipher cipher => cipher -> ByteString -> Key cipher cipherMakeKey c bs = case makeKey bs of Left e -> error ("invalid key " ++ show bs ++ " for " ++ show (cipherName c) ++ " " ++ show e) Right k -> k maybeGroup :: (String -> t -> [Test]) -> TestName -> [t] -> [Test] maybeGroup mkTest groupName l | null l = [] | otherwise = [testGroup groupName (concatMap (\(i, d) -> mkTest (show i) d) $ zip nbs l)] where nbs :: [Int] nbs = [0..]