newhope-0.1.0.0: auxiliary/KAT.hs
{-# LANGUAGE Safe #-}
{-|
Module : KAT
Description : Known Answer Tests
Copyright : © Jeremy Bornstein 2019
License : Apache 2.0
Maintainer : jeremy@bornstein.org
Stability : experimental
Portability : portable
KAT stands for Known Answer Test(s). The validity of an
implementation may be judged in part by its ability to produce the
correct output.
This module generates test vectors (according to the NIST PQC spec)
which should be identical (in filenames and contents) to the ones
generated by the original NewHope reference code.
The automated tests in this codebase contain tests which take those
reference implementation vectors as input to verify that we generate
the same data with the below code.
-}
module KAT where
import qualified Data.ByteString as BS
import Data.ByteString.Builder
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy.Char8 as BSLC
import Data.Char
import Data.Semigroup ((<>))
import AuxUtil
import qualified Crypto.NewHope as NewHope
import qualified Crypto.NewHope.Internal.CCA_KEM as CCA_KEM
import qualified Crypto.NewHope.Internal.CPA_KEM as CPA_KEM
import qualified Crypto.NewHope.Internal.RNG as RNG
type Filename = String
type VectorGenerator = NewHope.N -> Int -> (Filename, Builder)
recordsToGenerate :: Int
recordsToGenerate = 100
-- | output ByteString as uppercase hex string representation
processBS :: BS.ByteString -> String
processBS bs = toUpper <$> (BSLC.unpack . toLazyByteString . byteStringHex) bs
-- | Note that the seeds used are the same for both algorithms and both variants thereof.
getSeeds :: [RNG.RandomSeed]
getSeeds = go initialCTX
where
initialSeed = RNG.makeRandomSeed $ BS.pack [0 .. 47]
initialCTX = RNG.randomBytesInit initialSeed Nothing 256
go ctx = newSeed : go nextCTX
where
(newSeed, nextCTX) = (RNG.makeRandomSeed seed', ctx')
where
(seed', ctx') = RNG.randomBytes ctx 48
ccaKemTestVectors :: VectorGenerator
ccaKemTestVectors n count = (filename, start <> contents)
where
filename = "PQCkemKAT_" ++ show (CCA_KEM.secretKeyBytes n) ++ ".rsp"
start = lazyByteString . BSLC.pack $ "# " ++ show (WrapN n) ++ "-CCAKEM\n\n"
contents = foldr1 mappend $ outputRecord <$> Prelude.zip [0 ..] (take count getSeeds)
outputRecord :: (Int, RNG.RandomSeed) -> Builder
outputRecord (i, seed) = header <> record
where
header = lazyByteString . BSLC.pack $ "count = " ++ show i ++ "\n"
record = testVectorsFromSeed seed
testVectorsFromSeed :: RNG.RandomSeed -> Builder
testVectorsFromSeed seed = seq check output'
where
ctx = RNG.randomBytesInit seed Nothing 256
(pk, sk, ctx') = CCA_KEM.keypair ctx n
(ct, ss, _) = CCA_KEM.encrypt ctx' pk
check = let (success, ss') = CCA_KEM.decrypt ct sk
ssOK = success && ss == ss'
in if not ssOK
then error "Shared secret could not be generated."
else ()
seedData = RNG.getRandomSeedData seed
output = (lazyByteString . BSLC.pack) <$> [ "seed = " ++ processBS seedData ++ "\n"
, "pk = " ++ processBS (CCA_KEM.getPKData pk) ++ "\n"
, "sk = " ++ processBS (CCA_KEM.getSKData sk) ++ "\n"
, "ct = " ++ processBS (CCA_KEM.getCTData ct) ++ "\n"
, "ss = " ++ processBS (CCA_KEM.getSSData ss) ++ "\n"
, "\n"
]
output' = foldr go (byteString $ BSC.pack "") output
where
go a as = a <> as
cpaKemTestVectors :: VectorGenerator
cpaKemTestVectors n count = (filename, start <> contents)
where
filename = "PQCkemKAT_" ++ show (CPA_KEM.secretKeyBytes n) ++ ".rsp"
start = lazyByteString . BSLC.pack $ "# " ++ show (WrapN n) ++ "-CPAKEM\n\n"
contents = foldr1 mappend $ outputRecord <$> Prelude.zip [0 ..] (take count getSeeds)
outputRecord :: (Int, RNG.RandomSeed) -> Builder
outputRecord (i, seed) = header <> record
where
header = lazyByteString . BSLC.pack $ "count = " ++ show i ++ "\n"
record = testVectorsFromSeed seed
testVectorsFromSeed :: RNG.RandomSeed -> Builder
testVectorsFromSeed seed = seq check output'
where
ctx = RNG.randomBytesInit seed Nothing 256
(pk, sk, ctx') = CPA_KEM.keypair ctx n
(ct, ss, _) = CPA_KEM.encrypt ctx' pk
check = let mss = CPA_KEM.decrypt ct sk
ssOK = ss == mss
in if not ssOK
then error "Shared secret could not be generated."
else ()
seedData = RNG.getRandomSeedData seed
output = (lazyByteString . BSLC.pack) <$> [ "seed = " ++ processBS seedData ++ "\n"
, "pk = " ++ processBS (CPA_KEM.getPKData pk) ++ "\n"
, "sk = " ++ processBS (CPA_KEM.getSKData sk) ++ "\n"
, "ct = " ++ processBS (CPA_KEM.getCTData ct) ++ "\n"
, "ss = " ++ processBS (CPA_KEM.getSSData ss) ++ "\n"
, "\n"
]
output' = foldr go (byteString $ BSC.pack "") output
where
go a as = a <> as