Wycheproof.hs (3116B)
1 {-# LANGUAGE BangPatterns #-} 2 {-# LANGUAGE OverloadedStrings #-} 3 {-# LANGUAGE RecordWildCards #-} 4 {-# LANGUAGE ViewPatterns #-} 5 6 module Wycheproof ( 7 Wycheproof(..) 8 , execute_group 9 ) where 10 11 import Control.Exception 12 import Crypto.Curve.Secp256k1 13 import qualified Crypto.Hash.SHA256 as SHA256 14 import Data.Aeson ((.:)) 15 import qualified Data.Aeson as A 16 import qualified Data.ByteString.Base16 as B16 17 import qualified Data.Text as T 18 import qualified Data.Text.Encoding as TE 19 import Test.Tasty (TestTree, testGroup) 20 import Test.Tasty.HUnit (assertBool, testCase) 21 22 execute_group :: Context -> EcdsaTestGroup -> IO TestTree 23 execute_group tex EcdsaTestGroup {..} = do 24 let raw = B16.decodeLenient (TE.encodeUtf8 pk_uncompressed) 25 pub <- parse_pub tex raw 26 let tests = fmap (execute tex pub) etg_tests 27 pure (testGroup msg tests) 28 where 29 msg = "wycheproof (" <> T.unpack etg_type <> ", " <> T.unpack etg_sha <> ")" 30 PublicKey {..} = etg_publicKey 31 32 execute :: Context -> Pub -> EcdsaVerifyTest -> TestTree 33 execute tex pub EcdsaVerifyTest {..} = testCase report $ do 34 let msg = SHA256.hash (B16.decodeLenient (TE.encodeUtf8 t_msg)) 35 sig = B16.decodeLenient (TE.encodeUtf8 t_sig) 36 syg <- try (parse_der tex sig) :: IO (Either Secp256k1Exception Sig) 37 case syg of 38 Left _ -> assertBool mempty (t_result == "invalid") 39 Right s -> do 40 ver <- verify_ecdsa tex pub msg s 41 if t_result == "invalid" 42 then assertBool mempty (not ver) 43 else assertBool mempty ver 44 where 45 report = "wycheproof " <> show t_tcId 46 47 data Wycheproof = Wycheproof { 48 wp_algorithm :: !T.Text 49 , wp_generatorVersion :: !T.Text 50 , wp_numberOfTests :: !Int 51 , wp_testGroups :: ![EcdsaTestGroup] 52 } deriving Show 53 54 instance A.FromJSON Wycheproof where 55 parseJSON = A.withObject "Wycheproof" $ \m -> Wycheproof 56 <$> m .: "algorithm" 57 <*> m .: "generatorVersion" 58 <*> m .: "numberOfTests" 59 <*> m .: "testGroups" 60 61 data EcdsaTestGroup = EcdsaTestGroup { 62 etg_type :: !T.Text 63 , etg_publicKey :: !PublicKey 64 , etg_sha :: !T.Text 65 , etg_tests :: ![EcdsaVerifyTest] 66 } deriving Show 67 68 instance A.FromJSON EcdsaTestGroup where 69 parseJSON = A.withObject "EcdsaTestGroup" $ \m -> EcdsaTestGroup 70 <$> m .: "type" 71 <*> m .: "publicKey" 72 <*> m .: "sha" 73 <*> m .: "tests" 74 75 data PublicKey = PublicKey { 76 pk_type :: !T.Text 77 , pk_curve :: !T.Text 78 , pk_keySize :: !Int 79 , pk_uncompressed :: !T.Text 80 } deriving Show 81 82 instance A.FromJSON PublicKey where 83 parseJSON = A.withObject "PublicKey" $ \m -> PublicKey 84 <$> m .: "type" 85 <*> m .: "curve" 86 <*> m .: "keySize" 87 <*> m .: "uncompressed" 88 89 data EcdsaVerifyTest = EcdsaVerifyTest { 90 t_tcId :: !Int 91 , t_comment :: !T.Text 92 , t_msg :: !T.Text 93 , t_sig :: !T.Text 94 , t_result :: !T.Text 95 } deriving Show 96 97 instance A.FromJSON EcdsaVerifyTest where 98 parseJSON = A.withObject "EcdsaVerifyTest" $ \m -> EcdsaVerifyTest 99 <$> m .: "tcId" 100 <*> m .: "comment" 101 <*> m .: "msg" 102 <*> m .: "sig" 103 <*> m .: "result" 104