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