newhope-0.1.0.0: test/Crypto/NewHope/Test/FIPS202.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE Trustworthy #-}
{-|
Module : Crypto.NewHope.Test.FIPS202
Description : Testing code for NewHope.FIPS202 (an implementation of Keccak/SHA-3)
Copyright : © Jeremy Bornstein 2019
License : Apache 2.0
Maintainer : jeremy@bornstein.org
Stability : experimental
Portability : portable
-}
module Crypto.NewHope.Test.FIPS202 where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.Map as Map
import qualified Data.Vector.Unboxed as VU
import Data.Word
import Test.Tasty
import Test.Tasty.HUnit
import Text.Trifecta hiding (count)
import ConfigFile
import Crypto.NewHope.FIPS202
import StringUtils
-- all the validation data for these tests
configFile :: IO Config
configFile = fromFile "FIPS202.cfg"
data StatePermuteValidationParams = StatePermuteValidationParams { keccakInput :: VU.Vector Word64
, keccakOutput :: VU.Vector Word64
} deriving Show
keccakFromConfig :: Assignments -> StatePermuteValidationParams
keccakFromConfig section = StatePermuteValidationParams { keccakInput = inputVector, keccakOutput = outputVector }
where
Text.Trifecta.Success input = parseString parseListWordIntegral mempty $ section Map.! "input"
inputVector = VU.fromList input
Text.Trifecta.Success output = parseString parseListWordIntegral mempty $ section Map.! "output"
outputVector = VU.fromList output
keccakPermuteCheck :: StatePermuteValidationParams -> TestTree
keccakPermuteCheck params = testCase "keccakf1600_permute"
$ assertEqual "it is" validatedResult calculatedResult
where
validatedResult = keccakOutput params
calculatedResult = keccakF1600StatePermute $ keccakInput params
data Shake128AbsorbValidationParams = Shake128AbsorbValidationParams { absorbSeed :: BS.ByteString
, absorbState :: [Word64]
} deriving Show
absorbStateVector :: Shake128AbsorbValidationParams -> VU.Vector Word64
absorbStateVector params = VU.fromList $ absorbState params
absorbFromConfig :: Assignments -> Shake128AbsorbValidationParams
absorbFromConfig section = Shake128AbsorbValidationParams { absorbSeed = seed, absorbState = state }
where
seed = BSC.pack $ section Map.! "seed"
Text.Trifecta.Success state = parseString parseListWordIntegral mempty $ section Map.! "state"
shake128AbsorbCheck :: Shake128AbsorbValidationParams -> String -> TestTree
shake128AbsorbCheck params variant = testCase ("shake128Absorb (" ++ variant ++ ")")
$ assertEqual "it is" validatedResult calculatedResult
where
validatedResult = absorbStateVector params
calculatedResult = shake128Absorb $ absorbSeed params
data Shake128SqueezeValidationParams = Shake128SqueezeValidationParams { squeezeSeed :: BS.ByteString
, squeezeOutput :: BS.ByteString
, squeezeState :: VU.Vector Word64
} deriving Show
squeezeFromConfig :: Assignments -> Shake128SqueezeValidationParams
squeezeFromConfig section = Shake128SqueezeValidationParams { squeezeSeed = seedVector, squeezeOutput = outputVector, squeezeState = stateVector }
where
seedVector = BS.pack $ toIntegralList $ section Map.! "seed"
Text.Trifecta.Success output = parseString parseListWordIntegral mempty $ section Map.! "output"
outputVector = BS.pack output
Text.Trifecta.Success state = parseString parseListWordIntegral mempty $ section Map.! "state"
stateVector = VU.fromList state
shake128SqueezeBlocksCheck :: Shake128SqueezeValidationParams -> Int -> String -> TestTree
shake128SqueezeBlocksCheck params count variant = testCase ("shake128SqueezeBlocks (" ++ variant ++ ")")
$ assertEqual "it is" (validatedOutput, validatedOutputState) (output, outputState)
where
validatedOutput = squeezeOutput params
validatedOutputState = squeezeState params
startingState = shake128Absorb $ squeezeSeed params
(output, outputState) = shake128SqueezeBlocks startingState count
data Shake256ValidationParams = Shake256ValidationParams { shake256Input :: BS.ByteString
, shake256Output :: BS.ByteString
} deriving Show
shake256FromConfig :: Assignments -> Shake256ValidationParams
shake256FromConfig section = Shake256ValidationParams { shake256Input = inputVector, shake256Output = outputVector }
where
inputVector = BS.pack $ toIntegralList $ section Map.! "seed"
Text.Trifecta.Success output = parseString parseListWordIntegral mempty $ section Map.! "output"
outputVector = BS.pack output
shake256Check :: Shake256ValidationParams -> String -> TestTree
shake256Check params variant = testCase ("shake256 (" ++ variant ++ ")")
$ assertEqual "it is" validatedResult calculatedResult
where
validatedResult = shake256Output params
inputVector = shake256Input params
calculatedResult = shake256 inputVector $ BS.length validatedResult
data KeccakF1600VectorsValidationParams = KeccakF1600VectorsValidationParams { keccakVectorsInput :: BS.ByteString
, keccakVectorsRate :: Int
, keccakVectorsOutput :: Map.Map Int (VU.Vector Word64)
, keccakVectorsUnifiedOutput :: VU.Vector Word64
} deriving Show
keccakVectorsFromConfig :: Assignments -> KeccakF1600VectorsValidationParams
keccakVectorsFromConfig section = KeccakF1600VectorsValidationParams { keccakVectorsInput = inputBS
, keccakVectorsRate = rate
, keccakVectorsOutput = outputMap
, keccakVectorsUnifiedOutput = unifiedVector
}
where
rate = read $ section Map.! "r"
inputBS = hexStringToByteString $ section Map.! "m"
Text.Trifecta.Success unified = parseString parseListWordIntegral mempty $ section Map.! "unified"
unifiedVector = VU.fromList unified
Text.Trifecta.Success output = parseString parseListIntegralMap mempty $ section Map.! "vectors"
outputMap = Map.fromList $ fmap go output
where
go (key, value) = (key, VU.fromList value)
keccakVectorsCheck :: KeccakF1600VectorsValidationParams -> TestTree
keccakVectorsCheck params = testCase "keccakf1600 vectors check"
$ assertEqual "it is" validatedResult calculatedResult
where
validatedResult = keccakVectorsOutput params
rate = keccakVectorsRate params
input = keccakVectorsInput params
calculatedResult = inputLoadVectors rate input
tests :: IO TestTree
tests = do
config <- configFile
let
statePermuteParams :: StatePermuteValidationParams
statePermuteParams = keccakFromConfig $ sectionNamed config "keccakf1600_statepermute"
absorbParams :: Shake128AbsorbValidationParams
absorbParams = absorbFromConfig $ sectionNamed config "shake128_absorb"
absorbParams2 :: Shake128AbsorbValidationParams
absorbParams2 = absorbFromConfig $ sectionNamed config "shake128_absorb_2"
squeezeParams :: Shake128SqueezeValidationParams
squeezeParams = squeezeFromConfig $ sectionNamed config "shake128_squeezeblocks"
squeezeParams2 :: Shake128SqueezeValidationParams
squeezeParams2 = squeezeFromConfig $ sectionNamed config "shake128_squeezeblocks_2"
shake256Params :: Shake256ValidationParams
shake256Params = shake256FromConfig $ sectionNamed config "shake256"
shake256Params2 :: Shake256ValidationParams
shake256Params2 = shake256FromConfig $ sectionNamed config "shake256_2"
keccakVectorsParams :: KeccakF1600VectorsValidationParams
keccakVectorsParams = keccakVectorsFromConfig $ sectionNamed config "keccak_absorb.loop"
return $ testGroup "FIPS202 Tests" [ keccakPermuteCheck statePermuteParams
, keccakVectorsCheck keccakVectorsParams
, shake128AbsorbCheck absorbParams "#1"
, shake128AbsorbCheck absorbParams2 "#2"
, shake128SqueezeBlocksCheck squeezeParams 1 "#1"
, shake128SqueezeBlocksCheck squeezeParams2 2 "#2"
, shake256Check shake256Params "#1"
, shake256Check shake256Params2 "#2"
]