musig2-0.2.0: test/SignVerify.hs
{-# LANGUAGE OverloadedStrings #-}
module SignVerify (testSignVerify) where
import Crypto.Curve.Secp256k1 (Pub, derive_pub, _CURVE_ZERO)
import Crypto.Curve.Secp256k1.MuSig2 (MuSig2Error (..), PubNonce, SecKey (..), aggNonces, mkSecNonce, mkSessionContext, partialSigVerify, publicNonce, sign)
import Crypto.Curve.Secp256k1.MuSig2.Internal (bytesToInteger)
import Data.ByteString (ByteString)
import Data.Maybe (fromMaybe)
import Test.Tasty
import Test.Tasty.HUnit
import Util (decodeHex, parsePoint, parsePubNonce, unsafeMkSecNonce, unsafeRight)
-- | Input public keys from BIP-0327 test vectors.
inputPubkeys :: [Pub]
inputPubkeys =
map
parsePoint
[ "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
, "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"
, "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA661"
, "020000000000000000000000000000000000000000000000000000000000000007"
]
-- | Public nonces from BIP-0327 test vectors.
inputPubNonces :: [PubNonce]
inputPubNonces =
map
parsePubNonce
[ "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480"
, "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
, "032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046"
, "0237C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0387BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480"
, "0200000000000000000000000000000000000000000000000000000000000000090287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480"
]
-- | Messages from BIP-0327 test vectors.
testMessages :: [ByteString]
testMessages =
[ decodeHex "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF"
, ""
, decodeHex "2626262626262626262626262626262626262626262626262626262626262626262626262626"
]
validTestVectors :: [([Int], [Int], Int, Int, ByteString)]
validTestVectors =
[ ([0, 1, 2], [0, 1, 2], 0, 0, decodeHex "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB")
, ([1, 0, 2], [1, 0, 2], 0, 1, decodeHex "9FF2F7AAA856150CC8819254218D3ADEEB0535269051897724F9DB3789513A52")
, ([1, 2, 0], [1, 2, 0], 0, 2, decodeHex "FA23C359F6FAC4E7796BB93BC9F0532A95468C539BA20FF86D7C76ED92227900")
, ([0, 1, 2], [0, 1, 2], 1, 0, decodeHex "D7D63FFD644CCDA4E62BC2BC0B1D02DD32A1DC3030E155195810231D1037D82D")
, ([0, 1, 2], [0, 1, 2], 2, 0, decodeHex "E184351828DA5094A97C79CABDAAA0BFB87608C32E8829A4DF5340A6F243B78C")
]
invalidTestVectors :: [(ByteString, [Int], [Int], Int, Int, String)]
invalidTestVectors =
[ (decodeHex "FED54434AD4CFE953FC527DC6A5E5BE8F6234907B7C187559557CE87A0541C46", [0, 1, 2], [0, 1, 2], 0, 0, "Wrong signature (negation of valid signature)")
, (decodeHex "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB", [0, 1, 2], [0, 1, 2], 0, 1, "Wrong signer")
]
errorTestVectors :: [(ByteString, [Int], [Int], Int, Int, MuSig2Error)]
errorTestVectors =
[ (decodeHex "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", [0, 1, 2], [0, 1, 2], 0, 0, PartialSignatureOutOfRange)
]
invalidSecNoncePub :: Pub
invalidSecNoncePub = fromMaybe (error "Failed to derive pubkey") $ derive_pub 1
makeValidTestCase :: Int -> ([Int], [Int], Int, Int, ByteString) -> TestTree
makeValidTestCase i (keyIndices, nonceIndices, msgIndex, signerIndex, expectedSig) =
testCase ("BIP-0327 valid test vector " <> show (i + 1)) $ do
let selectedKeys = map (inputPubkeys !!) keyIndices
selectedNonces = map (inputPubNonces !!) nonceIndices
msg = testMessages !! msgIndex
signature = bytesToInteger expectedSig
partialSigVerify signature selectedNonces selectedKeys [] msg signerIndex @?= Right True
makeInvalidTestCase :: Int -> (ByteString, [Int], [Int], Int, Int, String) -> TestTree
makeInvalidTestCase i (sigBytes, keyIndices, nonceIndices, msgIndex, signerIndex, comment) =
testCase ("BIP-0327 invalid test vector " <> show (i + 1) <> ": " <> comment) $ do
let selectedKeys = map (inputPubkeys !!) keyIndices
selectedNonces = map (inputPubNonces !!) nonceIndices
msg = testMessages !! msgIndex
signature = bytesToInteger sigBytes
partialSigVerify signature selectedNonces selectedKeys [] msg signerIndex @?= Right False
makeErrorTestCase :: Int -> (ByteString, [Int], [Int], Int, Int, MuSig2Error) -> TestTree
makeErrorTestCase i (sigBytes, keyIndices, nonceIndices, msgIndex, signerIndex, expectedError) =
testCase ("BIP-0327 error test vector " <> show (i + 1)) $ do
let selectedKeys = map (inputPubkeys !!) keyIndices
selectedNonces = map (inputPubNonces !!) nonceIndices
msg = testMessages !! msgIndex
signature = bytesToInteger sigBytes
partialSigVerify signature selectedNonces selectedKeys [] msg signerIndex @?= Left expectedError
testInvalidSecNonce :: TestTree
testInvalidSecNonce =
testCase "mkSecNonce rejects zero scalars" $
case mkSecNonce invalidSecNoncePub 1 0 of
Left (SecretScalarZero "k2") -> pure ()
Left err -> assertFailure $ "Expected SecretScalarZero \"k2\", got: Left " ++ show err
Right _ -> assertFailure "Expected Left, got Right"
testContextRejectsInfinityPubkey :: TestTree
testContextRejectsInfinityPubkey =
testCase "mkSessionContext rejects infinity public keys" $ do
let aggNonce = unsafeRight $ aggNonces [firstPubNonce]
case mkSessionContext aggNonce [_CURVE_ZERO] [] "msg" of
Left PublicKeyAtInfinity -> pure ()
_ -> assertFailure "Expected Left PublicKeyAtInfinity"
where
firstPubNonce = case inputPubNonces of
nonce : _ -> nonce
[] -> error "Expected at least one input public nonce"
testSignerKeyMismatch :: TestTree
testSignerKeyMismatch =
testCase "sign rejects nonce/key mismatches" $ do
let signerPub = fromMaybe (error "Failed to derive signer pubkey") $ derive_pub 2
secNonce = unsafeMkSecNonce signerPub 5 7
aggNonce = unsafeRight $ aggNonces [unsafeRight $ publicNonce secNonce]
ctx = unsafeRight $ mkSessionContext aggNonce [signerPub] [] "msg"
sign secNonce (SecKey 3) ctx @?= Left SecretKeyPublicKeyMismatch
testSignVerify :: TestTree
testSignVerify =
testGroup "sign and verify" $
zipWith makeValidTestCase [0 ..] validTestVectors
<> zipWith makeInvalidTestCase [0 ..] invalidTestVectors
<> zipWith makeErrorTestCase [0 ..] errorTestVectors
<> [testInvalidSecNonce, testContextRejectsInfinityPubkey, testSignerKeyMismatch]