{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ViewPatterns #-} module BIP340 ( cases , execute ) where import Control.Applicative import Crypto.Curve.Secp256k1 import qualified Data.Attoparsec.ByteString.Char8 as AT import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as B16 import Test.Tasty import Test.Tasty.HUnit decodeLenient :: BS.ByteString -> BS.ByteString decodeLenient bs = case B16.decode bs of Nothing -> error "bang" Just b -> b data Case = Case { c_index :: !Int , c_sk :: !BS.ByteString , c_pk :: !BS.ByteString , c_aux :: !BS.ByteString , c_msg :: !BS.ByteString , c_sig :: !BS.ByteString , c_res :: !Bool , c_comment :: !BS.ByteString } deriving Show execute :: Context -> Case -> TestTree execute tex Case {..} = testCase ("bip0340 " <> show c_index) $ case parse_point (decodeLenient c_pk) of Nothing -> assertBool mempty (not c_res) Just pk -> do if c_sk == mempty then do -- no signature; test verification let ver = verify_schnorr c_msg pk c_sig ver' = verify_schnorr' tex c_msg pk c_sig if c_res then do assertBool mempty ver assertBool mempty ver' else do assertBool mempty (not ver) assertBool mempty (not ver') -- XX test pubkey derivation from sk else do -- signature present; test sig too let sk = unsafe_roll32 c_sk Just sig = sign_schnorr sk c_msg c_aux Just sig' = sign_schnorr' tex sk c_msg c_aux ver = verify_schnorr c_msg pk sig ver' = verify_schnorr' tex c_msg pk sig assertEqual mempty c_sig sig assertEqual mempty c_sig sig' if c_res then do assertBool mempty ver assertBool mempty ver' else do assertBool mempty (not ver) assertBool mempty (not ver') header :: AT.Parser () header = do _ <- AT.string "index,secret key,public key,aux_rand,message,signature,verification result,comment" AT.endOfLine test_case :: AT.Parser Case test_case = do c_index <- AT.decimal AT. "index" _ <- AT.char ',' c_sk <- fmap decodeLenient (AT.takeWhile (/= ',') AT. "sk") _ <- AT.char ',' c_pk <- AT.takeWhile1 (/= ',') AT. "pk" _ <- AT.char ',' c_aux <- fmap decodeLenient (AT.takeWhile (/= ',') AT. "aux") _ <- AT.char ',' c_msg <- fmap decodeLenient (AT.takeWhile (/= ',') AT. "msg") _ <- AT.char ',' c_sig <- fmap decodeLenient (AT.takeWhile1 (/= ',') AT. "sig") _ <- AT.char ',' c_res <- (AT.string "TRUE" *> pure True) <|> (AT.string "FALSE" *> pure False) AT. "res" _ <- AT.char ',' c_comment <- AT.takeWhile (/= '\n') AT. "comment" AT.endOfLine pure Case {..} cases :: AT.Parser [Case] cases = header *> AT.many1 test_case