btcec/schnorr/musig2: add sig verify+sign test vectors

This commit is contained in:
Olaoluwa Osuntokun 2022-10-20 16:35:05 -07:00
parent cc12483f0a
commit ca28a98425
No known key found for this signature in database
GPG Key ID: 3BBD59E99B280306
3 changed files with 452 additions and 0 deletions

View File

@ -0,0 +1,183 @@
{
"sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
"pubkeys": [
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA661",
"020000000000000000000000000000000000000000000000000000000000000007"
],
"secnonces": [
"508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7",
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
],
"pnonces": [
"0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046",
"0237C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0387BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"020000000000000000000000000000000000000000000000000000000000000009"
],
"aggnonces": [
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"048465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61020000000000000000000000000000000000000000000000000000000000000009",
"028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD6102FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"
],
"msgs": [
"F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
"",
"2626262626262626262626262626262626262626262626262626262626262626262626262626"
],
"valid_test_cases": [
{
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"aggnonce_index": 0,
"msg_index": 0,
"signer_index": 0,
"expected": "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB"
},
{
"key_indices": [1, 0, 2],
"nonce_indices": [1, 0, 2],
"aggnonce_index": 0,
"msg_index": 0,
"signer_index": 1,
"expected": "9FF2F7AAA856150CC8819254218D3ADEEB0535269051897724F9DB3789513A52"
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"aggnonce_index": 0,
"msg_index": 0,
"signer_index": 2,
"expected": "FA23C359F6FAC4E7796BB93BC9F0532A95468C539BA20FF86D7C76ED92227900"
},
{
"key_indices": [0, 1],
"nonce_indices": [0, 3],
"aggnonce_index": 1,
"msg_index": 0,
"signer_index": 0,
"expected": "AE386064B26105404798F75DE2EB9AF5EDA5387B064B83D049CB7C5E08879531",
"comment": "Both halves of aggregate nonce correspond to point at infinity"
}
],
"sign_error_test_cases": [
{
"key_indices": [1, 0, 3],
"aggnonce_index": 0,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "invalid_contribution",
"signer": 2,
"contrib": "pubkey"
},
"comment": "Signer 2 provided an invalid public key"
},
{
"key_indices": [1, 2, 0],
"aggnonce_index": 2,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "invalid_contribution",
"signer": null,
"contrib": "aggnonce"
},
"comment": "Aggregate nonce is invalid due wrong tag, 0x04, in the first half"
},
{
"key_indices": [1, 2, 0],
"aggnonce_index": 3,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "invalid_contribution",
"signer": null,
"contrib": "aggnonce"
},
"comment": "Aggregate nonce is invalid because the second half does not correspond to an X coordinate"
},
{
"key_indices": [1, 2, 0],
"aggnonce_index": 4,
"msg_index": 0,
"secnonce_index": 0,
"error": {
"type": "invalid_contribution",
"signer": null,
"contrib": "aggnonce"
},
"comment": "Aggregate nonce is invalid because second half exceeds field size"
},
{
"key_indices": [0, 1, 2],
"aggnonce_index": 0,
"msg_index": 0,
"signer_index": 0,
"secnonce_index": 1,
"error": {
"type": "value",
"message": "first secnonce value is out of range."
},
"comment": "Secnonce is invalid which may indicate nonce reuse"
}
],
"verify_fail_test_cases": [
{
"sig": "97AC833ADCB1AFA42EBF9E0725616F3C9A0D5B614F6FE283CEAAA37A8FFAF406",
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"msg_index": 0,
"signer_index": 0,
"comment": "Wrong signature (which is equal to the negation of valid signature)"
},
{
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"msg_index": 0,
"signer_index": 1,
"comment": "Wrong signer"
},
{
"sig": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
"key_indices": [0, 1, 2],
"nonce_indices": [0, 1, 2],
"msg_index": 0,
"signer_index": 0,
"comment": "Signature exceeds group size"
}
],
"verify_error_test_cases": [
{
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
"key_indices": [0, 1, 2],
"nonce_indices": [4, 1, 2],
"msg_index": 0,
"signer_index": 0,
"error": {
"type": "invalid_contribution",
"signer": 0,
"contrib": "pubnonce"
},
"comment": "Invalid pubnonce"
},
{
"sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B",
"key_indices": [3, 1, 2],
"nonce_indices": [0, 1, 2],
"msg_index": 0,
"signer_index": 0,
"error": {
"type": "invalid_contribution",
"signer": 0,
"contrib": "pubkey"
},
"comment": "Invalid pubkey"
}
]
}

View File

@ -346,6 +346,8 @@ func TestMuSig2TweakTestVectors(t *testing.T) {
copy(secNonce[:], mustParseHex(testCases.PrivNonce))
for _, testCase := range testCases.ValidCases {
testCase := testCase
testName := fmt.Sprintf("valid_%v",
strings.ToLower(testCase.Comment))
t.Run(testName, func(t *testing.T) {

View File

@ -0,0 +1,267 @@
// Copyright 2013-2022 The btcsuite developers
package musig2
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path"
"strings"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/stretchr/testify/require"
)
const (
signVerifyTestVectorFileName = "sign_verify_vectors.json"
)
type signVerifyValidCase struct {
Indices []int `json:"key_indices"`
NonceIndices []int `json:"nonce_indices"`
AggNonceIndex int `json:"aggnonce_index"`
MsgIndex int `json:"msg_index"`
SignerIndex int `json:"signer_index"`
Expected string `json:"expected"`
}
type signErrorCase struct {
Indices []int `json:"key_indices"`
AggNonceIndex int `json:"aggnonce_index"`
MsgIndex int `json:"msg_index"`
SecNonceIndex int `json:"secnonce_index"`
Comment string `json:"comment"`
}
type verifyFailCase struct {
Sig string `json:"sig"`
Indices []int `json:"key_indices"`
NonceIndices []int `json:"nonce_indices"`
MsgIndex int `json:"msg_index"`
SignerIndex int `json:"signer_index"`
Comment string `json:"comment"`
}
type verifyErrorCase struct {
Sig string `json:"sig"`
Indices []int `json:"key_indices"`
NonceIndices []int `json:"nonce_indices"`
MsgIndex int `json:"msg_index"`
SignerIndex int `json:"signer_index"`
Comment string `json:"comment"`
}
type signVerifyTestVectors struct {
PrivKey string `json:"sk"`
PubKeys []string `json:"pubkeys"`
PrivNonces []string `json:"secnonces"`
PubNonces []string `json:"pnonces"`
AggNonces []string `json:"aggnonces"`
Msgs []string `json:"msgs"`
ValidCases []signVerifyValidCase `json:"valid_test_cases"`
SignErrorCases []signErrorCase `json:"sign_error_test_cases"`
VerifyFailCases []verifyFailCase `json:"verify_fail_test_cases"`
VerifyErrorCases []verifyErrorCase `json:"verify_error_test_cases"`
}
// TestMusig2SignVerify tests that we pass the musig2 verification tests.
func TestMusig2SignVerify(t *testing.T) {
t.Parallel()
testVectorPath := path.Join(
testVectorBaseDir, signVerifyTestVectorFileName,
)
testVectorBytes, err := os.ReadFile(testVectorPath)
require.NoError(t, err)
var testCases signVerifyTestVectors
require.NoError(t, json.Unmarshal(testVectorBytes, &testCases))
privKey, _ := btcec.PrivKeyFromBytes(mustParseHex(testCases.PrivKey))
for i, testCase := range testCases.ValidCases {
testCase := testCase
testName := fmt.Sprintf("valid_case_%v", i)
t.Run(testName, func(t *testing.T) {
pubKeys, err := keysFromIndices(
t, testCase.Indices, testCases.PubKeys,
)
require.NoError(t, err)
pubNonces := pubNoncesFromIndices(
t, testCase.NonceIndices, testCases.PubNonces,
)
combinedNonce, err := AggregateNonces(pubNonces)
require.NoError(t, err)
var msg [32]byte
copy(msg[:], mustParseHex(testCases.Msgs[testCase.MsgIndex]))
var secNonce [SecNonceSize]byte
copy(secNonce[:], mustParseHex(testCases.PrivNonces[0]))
partialSig, err := Sign(
secNonce, privKey, combinedNonce, pubKeys,
msg,
)
var partialSigBytes [32]byte
partialSig.S.PutBytesUnchecked(partialSigBytes[:])
require.Equal(
t, hex.EncodeToString(partialSigBytes[:]),
hex.EncodeToString(mustParseHex(testCase.Expected)),
)
})
}
for _, testCase := range testCases.SignErrorCases {
testCase := testCase
testName := fmt.Sprintf("invalid_case_%v",
strings.ToLower(testCase.Comment))
t.Run(testName, func(t *testing.T) {
pubKeys, err := keysFromIndices(
t, testCase.Indices, testCases.PubKeys,
)
if err != nil {
require.ErrorIs(t, err, secp.ErrPubKeyNotOnCurve)
return
}
var aggNonce [PubNonceSize]byte
copy(
aggNonce[:],
mustParseHex(
testCases.AggNonces[testCase.AggNonceIndex],
),
)
var msg [32]byte
copy(msg[:], mustParseHex(testCases.Msgs[testCase.MsgIndex]))
var secNonce [SecNonceSize]byte
copy(
secNonce[:],
mustParseHex(
testCases.PrivNonces[testCase.SecNonceIndex],
),
)
_, err = Sign(
secNonce, privKey, aggNonce, pubKeys,
msg,
)
require.Error(t, err)
})
}
for _, testCase := range testCases.VerifyFailCases {
testCase := testCase
testName := fmt.Sprintf("verify_fail_%v",
strings.ToLower(testCase.Comment))
t.Run(testName, func(t *testing.T) {
pubKeys, err := keysFromIndices(
t, testCase.Indices, testCases.PubKeys,
)
require.NoError(t, err)
pubNonces := pubNoncesFromIndices(
t, testCase.NonceIndices, testCases.PubNonces,
)
combinedNonce, err := AggregateNonces(pubNonces)
require.NoError(t, err)
var msg [32]byte
copy(
msg[:],
mustParseHex(testCases.Msgs[testCase.MsgIndex]),
)
var secNonce [SecNonceSize]byte
copy(secNonce[:], mustParseHex(testCases.PrivNonces[0]))
signerNonce := secNonceToPubNonce(secNonce)
var partialSig PartialSignature
err = partialSig.Decode(
bytes.NewReader(mustParseHex(testCase.Sig)),
)
if err != nil && strings.Contains(testCase.Comment, "group size") {
require.ErrorIs(t, err, ErrPartialSigInvalid)
}
err = verifyPartialSig(
&partialSig, signerNonce, combinedNonce,
pubKeys, privKey.PubKey().SerializeCompressed(),
msg,
)
require.Error(t, err)
})
}
for _, testCase := range testCases.VerifyErrorCases {
testCase := testCase
testName := fmt.Sprintf("verify_error_%v",
strings.ToLower(testCase.Comment))
t.Run(testName, func(t *testing.T) {
switch testCase.Comment {
case "Invalid pubnonce":
pubNonces := pubNoncesFromIndices(
t, testCase.NonceIndices, testCases.PubNonces,
)
_, err := AggregateNonces(pubNonces)
require.ErrorIs(t, err, secp.ErrPubKeyNotOnCurve)
case "Invalid pubkey":
_, err := keysFromIndices(
t, testCase.Indices, testCases.PubKeys,
)
require.ErrorIs(t, err, secp.ErrPubKeyNotOnCurve)
default:
t.Fatalf("unhandled case: %v", testCase.Comment)
}
})
}
}