packages feed

crypton-1.1.5: tests/KAT_AES.hs

{-# LANGUAGE OverloadedStrings #-}

module KAT_AES (tests) where

import BlockCipher
import qualified Crypto.Cipher.AES as AES
import Crypto.Cipher.Types
import Crypto.Error
import qualified Data.ByteArray as BA
import qualified Data.ByteString as B
import Data.Maybe
import Imports

import qualified KAT_AES.KATCBC as KATCBC
import qualified KAT_AES.KATCCM as KATCCM
import qualified KAT_AES.KATECB as KATECB
import qualified KAT_AES.KATGCM as KATGCM
import qualified KAT_AES.KATOCB3 as KATOCB3
import qualified KAT_AES.KATXTS as KATXTS

{-
instance Show AES.AES where
    show _ = "AES"
instance Arbitrary AES.AESIV where
    arbitrary = AES.aesIV_ . B.pack <$> replicateM 16 arbitrary
instance Arbitrary AES.AES where
    arbitrary = AES.initAES . B.pack <$> replicateM 16 arbitrary
-}

toKatECB (k, p, c) = KAT_ECB{ecbKey = k, ecbPlaintext = p, ecbCiphertext = c}
toKatCBC (k, iv, p, c) = KAT_CBC{cbcKey = k, cbcIV = iv, cbcPlaintext = p, cbcCiphertext = c}
toKatXTS (k1, k2, iv, p, _, c) =
    KAT_XTS
        { xtsKey1 = k1
        , xtsKey2 = k2
        , xtsIV = iv
        , xtsPlaintext = p
        , xtsCiphertext = c
        }
toKatAEAD mode (k, iv, h, p, c, taglen, tag) =
    KAT_AEAD
        { aeadMode = mode
        , aeadKey = k
        , aeadIV = iv
        , aeadHeader = h
        , aeadPlaintext = p
        , aeadCiphertext = c
        , aeadTaglen = taglen
        , aeadTag = tag
        }
toKatGCM = toKatAEAD AEAD_GCM
toKatOCB = toKatAEAD AEAD_OCB

toKatCCM (k, iv, h, i, o, m) =
    KAT_AEAD
        { aeadMode = AEAD_CCM (B.length i) (ccmMVal m) CCM_L2
        , aeadKey = k
        , aeadIV = iv
        , aeadHeader = h
        , aeadPlaintext = i
        , aeadCiphertext = ct
        , aeadTaglen = m
        , aeadTag = at
        }
  where
    ccmMVal x =
        fromMaybe (error $ "unsupported CCM tag length: " ++ show x) $
            lookup
                x
                [ (4, CCM_M4)
                , (6, CCM_M6)
                , (8, CCM_M8)
                , (10, CCM_M10)
                , (12, CCM_M12)
                , (14, CCM_M14)
                , (16, CCM_M16)
                ]
    ctWithTag = B.drop (B.length h) o
    (ct, at) = B.splitAt (B.length ctWithTag - m) ctWithTag

kats128 =
    defaultKATs
        { kat_ECB = map toKatECB KATECB.vectors_aes128_enc
        , kat_CBC = map toKatCBC KATCBC.vectors_aes128_enc
        , kat_CFB =
            [ KAT_CFB
                { cfbKey =
                    "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c"
                , cfbIV =
                    "\xC8\xA6\x45\x37\xA0\xB3\xA9\x3F\xCD\xE3\xCD\xAD\x9F\x1C\xE5\x8B"
                , cfbPlaintext =
                    "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
                , cfbCiphertext =
                    "\x26\x75\x1f\x67\xa3\xcb\xb1\x40\xb1\x80\x8c\xf1\x87\xa4\xf4\xdf"
                }
            ]
        , kat_XTS = map toKatXTS KATXTS.vectors_aes128_enc
        , kat_AEAD =
            map toKatGCM KATGCM.vectors_aes128_enc
                ++ map toKatOCB KATOCB3.vectors_aes128_enc
                ++ map toKatCCM KATCCM.vectors_aes128_enc
        }

kats192 =
    defaultKATs
        { kat_ECB = map toKatECB KATECB.vectors_aes192_enc
        , kat_CBC = map toKatCBC KATCBC.vectors_aes192_enc
        }

kats256 =
    defaultKATs
        { kat_ECB = map toKatECB KATECB.vectors_aes256_enc
        , kat_CBC = map toKatCBC KATCBC.vectors_aes256_enc
        , kat_XTS = map toKatXTS KATXTS.vectors_aes256_enc
        , kat_AEAD = map toKatGCM KATGCM.vectors_aes256_enc
        }

-- SP 800-38D 5.2.1.1: 1 <= len(IV) <= 2^64 - 1.  A zero-length IV makes
-- J0 the GHASH of the empty string, which leaks the authentication key.
aeadIVLengthTests :: TestTree
aeadIVLengthTests =
    testGroup
        "AEAD IV length"
        [ testCase "96-bit IV accepted" $
            True @=? isRight (initWith (B.replicate 12 0))
        , testCase "8-bit IV accepted" $
            True @=? isRight (initWith (B.replicate 1 0))
        , testCase "empty IV rejected" $
            Left CryptoError_IvSizeInvalid @=? initWith B.empty
        ]
  where
    ctx = throwCryptoError (cipherInit (B.replicate 16 0)) :: AES.AES128
    initWith iv =
        eitherCryptoError (() <$ aeadInit AEAD_GCM ctx (iv :: ByteString))
    isRight = either (const False) (const True)

aeadTagLengthTests :: TestTree
aeadTagLengthTests =
    testGroup
        "AEAD tag length"
        [ testCase "full tag verifies" $ Just message @=? openWith fullTag
        , testCase "empty tag rejected" $ Nothing @=? openWith B.empty
        , testCase "1-byte tag rejected" $ Nothing @=? openWith (B.take 1 fullTag)
        , testCase "3-byte tag rejected" $ Nothing @=? openWith (B.take 3 fullTag)
        , testCase "wrong tag rejected" $
            Nothing @=? openWith (B.map (+ 1) fullTag)
        ]
  where
    key = B.replicate 16 0
    iv = B.replicate 12 0
    aad = "additional data" :: ByteString
    message = "authenticated message" :: ByteString
    ctx = throwCryptoError (cipherInit key) :: AES.AES128
    aead = throwCryptoError (aeadInit AEAD_GCM ctx iv)
    (AuthTag tag, ciphertext) = aeadSimpleEncrypt aead aad message 16
    fullTag = BA.convert tag :: ByteString
    openWith t = aeadSimpleDecrypt aead aad ciphertext (AuthTag (BA.convert t))

tests =
    testGroup
        "AES"
        [ testBlockCipher kats128 (undefined :: AES.AES128)
        , testBlockCipher kats192 (undefined :: AES.AES192)
        , testBlockCipher kats256 (undefined :: AES.AES256)
        , aeadIVLengthTests
        , aeadTagLengthTests
        {-
            , testProperty "genCtr" $ \(key, iv1) ->
                let (bs1, iv2)    = AES.genCounter key iv1 32
                    (bs2, iv3)    = AES.genCounter key iv2 32
                    (bsAll, iv3') = AES.genCounter key iv1 64
                 in (B.concat [bs1,bs2] == bsAll && iv3 == iv3')
        -}
        ]