packages feed

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