mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 18:00:11 +01:00
btcec/schnorr/musig2: add sig verify+sign test vectors
This commit is contained in:
parent
cc12483f0a
commit
ca28a98425
183
btcec/schnorr/musig2/data/sign_verify_vectors.json
Normal file
183
btcec/schnorr/musig2/data/sign_verify_vectors.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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) {
|
||||
|
267
btcec/schnorr/musig2/sign_test.go
Normal file
267
btcec/schnorr/musig2/sign_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user