tahoe-chk-0.1.0.2: test/Vectors.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Vectors where
import Control.Exception (IOException, try)
import Data.Aeson (
FromJSON (..),
ToJSON (..),
Value (..),
withText,
(.:),
)
import Data.Aeson.Types (
parseFail,
withObject,
)
import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as Base64
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Data.Yaml (ParseException, decodeEither')
import GHC.Generics (
Generic,
)
import Tahoe.CHK.Types (
Parameters (..),
)
newtype JSONByteString = JSONByteString B.ByteString deriving newtype (Ord, Eq)
instance Show JSONByteString where
show (JSONByteString bs) = T.unpack . T.decodeLatin1 . Base64.encode $ bs
instance FromJSON JSONByteString where
parseJSON =
withText
"base64 encoded bytestring"
( \t ->
case Base64.decode . T.encodeUtf8 $ t of
Left err -> parseFail $ "parsing base64-encoded byte string failed" <> show err
Right stuff -> pure $ JSONByteString stuff
)
instance ToJSON JSONByteString where
toJSON (JSONByteString bs) = String . T.decodeLatin1 . Base64.encode $ bs
data SSKFormat
= SDMF
{ sskPrivateKey :: T.Text
}
| MDMF
{ sskPrivateKey :: T.Text
}
deriving (Show, Ord, Eq)
data Format = CHK | SSK SSKFormat deriving (Show, Ord, Eq)
instance FromJSON Format where
parseJSON = withObject "format" $ \o -> do
kind <- o .: "kind"
case kind of
"chk" -> pure CHK
"ssk" -> SSK <$> o .: "params"
invalid -> parseFail $ "Unsupported format: " <> T.unpack invalid
instance FromJSON SSKFormat where
parseJSON = withObject "ssk-format" $ \o -> do
format <- o .: "format"
key <- o .: "key"
sskFormat <- case format of
"sdmf" -> pure SDMF
"mdmf" -> pure MDMF
invalid -> parseFail $ "Unsupported SSK format: " <> T.unpack invalid
pure $ sskFormat key
data Sample = Sample
{ sampleTemplate :: JSONByteString
, sampleLength :: Int
}
deriving (Show, Ord, Eq)
instance FromJSON Sample where
parseJSON = withObject "sample" $ \o ->
Sample <$> o .: "seed" <*> o .: "length"
data VectorSpec = VectorSpec
{ version :: T.Text
, vector :: [TestCase]
}
deriving (Generic, Show, Ord, Eq, FromJSON)
data TestCase = TestCase
{ convergence :: JSONByteString
, format :: Format
, sample :: Sample
, zfec :: Parameters
, expected :: T.Text
}
deriving (Generic, Show, Ord, Eq, FromJSON)
instance FromJSON Parameters where
parseJSON = withObject "parameters" $ \o ->
Parameters <$> o .: "segmentSize" <*> o .: "total" <*> pure 1 <*> o .: "required"
data LoadError = IOError IOException | ParseError ParseException deriving (Show)
-- | Load the test vectors from the yaml file.
loadTestVectorData :: IO (Either LoadError VectorSpec)
loadTestVectorData = go "test_vectors.yaml"
where
go :: String -> IO (Either LoadError VectorSpec)
go path = do
bs <- read' path
pure $
case bs of
Left le -> Left . IOError $ le
Right bs' -> parse bs'
read' :: String -> IO (Either IOError B.ByteString)
read' = try . B.readFile
parse :: B.ByteString -> Either LoadError VectorSpec
parse = either (Left . ParseError) pure . decodeEither'