Z-Botan-0.4.0.0: test/Utils.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Utils where
import Control.Applicative
import Control.Monad
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 Z.Data.Parser.Numeric (decLoopIntegerFast)
import Z.Data.CBytes (CBytes)
import qualified Z.Data.CBytes as CB
import qualified Z.Data.Vector as V
import qualified Z.Data.Text as T
import Prelude hiding (lines, mod)
-- import Data.IORef
-- | Parse test data vector files.
-- See `./third_party/botan/src/tests/data/`.
parseNamedTestVector :: HasCallStack => P.Parser x -> CBytes -> IO [(V.Bytes, [x])]
parseNamedTestVector p path = do
withResource (FS.initFile path FS.O_RDONLY FS.DEFAULT_FILE_MODE) $ \ f -> do
bi <- newBufferedInput f
let loopRead = do
name <- readParser algoName bi
(lines, eof) <- readParser (go []) bi
if eof
then return [(name, lines)]
else do
rest <- loopRead
return ((name, lines):rest)
loopRead
where
algoName = do
P.skipWhile (/= BRACKET_LEFT)
P.skipWord8
n <- P.takeWhile1 (/= BRACKET_RIGHT)
P.skipWord8
return n
go acc = do
P.skipWhile (== NEWLINE)
w <- P.peekMaybe
case w of
Nothing -> return (acc, True)
Just HASH -> skipComment >> go acc
Just BRACKET_LEFT -> return (acc, False)
_ -> do
x <- p
go (x:acc)
-- | Parse test data vector files.
-- See `./third_party/botan/src/tests/data/`.
parseTestVector :: HasCallStack => P.Parser x -> CBytes -> IO [x]
parseTestVector p path = do
withResource (FS.initFile path FS.O_RDONLY FS.DEFAULT_FILE_MODE) $ \f -> do
bi <- newBufferedInput f
readParser (h []) bi
where
h acc = do
P.skipWhile (== NEWLINE)
c <- P.peekMaybe
case c of
Nothing -> pure acc
Just HASH -> skipComment >> h acc
_ -> do
x <- p
h (x:acc)
-- | Parse @key = value@ or @key = value@ or @key =@ line
parseKeyValueLines :: P.Parser [(V.Bytes, V.Bytes)]
parseKeyValueLines = go []
where
go acc = do
mw <- P.peekMaybe
case mw of
Just w ->
if w == NEWLINE
then return acc
else do
k <- P.takeWhile (/= SPACE)
P.skipWhile (== SPACE)
P.word8 EQUAL
P.skipWhile (== SPACE)
-- some lines contains trailing spaces, e.g., tls_prf.vec
v <- V.dropWhileR isSpace <$> P.takeWhile (/= NEWLINE)
P.skipWord8 <|> return ()
go ((k, v):acc)
_ -> return acc
skipComment :: P.Parser ()
{-# INLINE skipComment #-}
skipComment = do
P.skipWhile (/= NEWLINE)
P.skipWhile (== NEWLINE)
lookupOrEmpty :: [(V.Bytes, V.Bytes)] -> V.Bytes -> V.Bytes
lookupOrEmpty m k = maybe V.empty id (lookup k m)
parseHashTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes)])]
parseHashTestVector = parseNamedTestVector $ do
m <- parseKeyValueLines
let i = lookupOrEmpty m "In"
let o = lookupOrEmpty m "Out"
return (hexDecode' i, hexDecode' o)
parseBlockCipherTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes, V.Bytes)])]
parseBlockCipherTestVector = parseNamedTestVector $ do
m <- parseKeyValueLines
let key = lookupOrEmpty m "Key"
let i = lookupOrEmpty m "In"
let o = lookupOrEmpty m "Out"
return (hexDecode' key, hexDecode' i, hexDecode' o)
parseCipherModeTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes, V.Bytes, V.Bytes)])]
parseCipherModeTestVector = parseNamedTestVector $ do
m <- parseKeyValueLines
let key = lookupOrEmpty m "Key"
let nonce = lookupOrEmpty m "Nonce"
let i = lookupOrEmpty m "In"
let o = lookupOrEmpty m "Out"
return (hexDecode' key, hexDecode' nonce, hexDecode' i, hexDecode' o)
parseCipherAEADTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes, V.Bytes, V.Bytes, V.Bytes)])]
parseCipherAEADTestVector = parseNamedTestVector $ do
m <- parseKeyValueLines
let key = lookupOrEmpty m "Key"
let nonce = lookupOrEmpty m "Nonce"
let i = lookupOrEmpty m "In"
let o = lookupOrEmpty m "Out"
let ad = lookupOrEmpty m "AD"
return (hexDecode' key, hexDecode' nonce, hexDecode' i, hexDecode' ad, hexDecode' o)
parsePasswdHashTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, V.Bytes)]
parsePasswdHashTestVector = parseTestVector $ do
m <- parseKeyValueLines
let passwd = lookupOrEmpty m "Password"
let passhash = lookupOrEmpty m "Passhash"
return (hexDecode' passwd, passhash)
parseKDFTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, V.Bytes, V.Bytes, V.Bytes)])]
parseKDFTestVector = parseNamedTestVector $ do
m <- parseKeyValueLines
let ikm = lookupOrEmpty m "IKM"
let xts = lookupOrEmpty m "XTS"
let salt = lookupOrEmpty m "Salt"
let label = lookupOrEmpty m "Label"
let secret = lookupOrEmpty m "Secret"
let o = lookupOrEmpty m "Output"
return (hexDecode' salt, hexDecode' label, hexDecode' secret, hexDecode' o)
parsePBKDFTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(V.Bytes, Int, T.Text, V.Bytes)])]
parsePBKDFTestVector = parseNamedTestVector $ do
m <- parseKeyValueLines
let salt = lookupOrEmpty m "Salt"
let iter = lookupOrEmpty m "Iterations"
let passphrase = lookupOrEmpty m "Passphrase"
let o = lookupOrEmpty m "Output"
return (hexDecode' salt , (fromIntegral . decLoopIntegerFast) iter , T.validate passphrase , hexDecode' o)
parseMACTestVector :: HasCallStack => CBytes -> IO [(V.Bytes, [(Maybe V.Bytes, V.Bytes, V.Bytes, V.Bytes)])]
parseMACTestVector = parseNamedTestVector $ do
m <- parseKeyValueLines
let iv = lookup "IV" m
let key = lookupOrEmpty m "Key"
let in_ = lookupOrEmpty m "In"
let out = lookupOrEmpty m "Out"
return (fmap hexDecode' iv, hexDecode' key, hexDecode' in_, hexDecode' out)
parseKeyWrapVec :: HasCallStack => CBytes -> IO [(V.Bytes, V.Bytes, V.Bytes)]
parseKeyWrapVec = parseTestVector $ do
m <- parseKeyValueLines
let key = lookupOrEmpty m "Key"
let kek = lookupOrEmpty m "KEK"
let o = lookupOrEmpty m "Output"
return (hexDecode' key, hexDecode' kek, hexDecode' o)
parseFPEVec :: HasCallStack => CBytes -> IO [(V.Bytes, V.Bytes, V.Bytes, V.Bytes, V.Bytes)]
parseFPEVec = parseTestVector $ do
m <- parseKeyValueLines
let mod = lookupOrEmpty m "Mod"
let i = lookupOrEmpty m "In"
let o = lookupOrEmpty m "Out"
let key = lookupOrEmpty m "Key"
let tweak = lookupOrEmpty m "Tweak"
return (mod, i, o, hexDecode' key, hexDecode' tweak)