Skip to content

Commit

Permalink
btcec/schnorr/musig2: add key tweak sign test vectors
Browse files Browse the repository at this point in the history
  • Loading branch information
Roasbeef committed Oct 21, 2022
1 parent 4e55273 commit cc12483
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 3 deletions.
84 changes: 84 additions & 0 deletions btcec/schnorr/musig2/data/tweak_vectors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671",
"pubkeys": [
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9",
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
],
"secnonce": "508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7",
"pnonces": [
"0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480",
"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046"
],
"aggnonce": "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9",
"tweaks": [
"E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB",
"AE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455",
"F52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0",
"1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
],
"msg": "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",
"valid_test_cases": [
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0],
"is_xonly": [true],
"signer_index": 2,
"expected": "E28A5C66E61E178C2BA19DB77B6CF9F7E2F0F56C17918CD13135E60CC848FE91",
"comment": "A single x-only tweak"
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0],
"is_xonly": [false],
"signer_index": 2,
"expected": "38B0767798252F21BF5702C48028B095428320F73A4B14DB1E25DE58543D2D2D",
"comment": "A single plain tweak"
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0, 1],
"is_xonly": [false, true],
"signer_index": 2,
"expected": "408A0A21C4A0F5DACAF9646AD6EB6FECD7F7A11F03ED1F48DFFF2185BC2C2408",
"comment": "A plain tweak followed by an x-only tweak"
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0, 1, 2, 3],
"is_xonly": [false, false, true, true],
"signer_index": 2,
"expected": "45ABD206E61E3DF2EC9E264A6FEC8292141A633C28586388235541F9ADE75435",
"comment": "Four tweaks: plain, plain, x-only, x-only."
},
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [0, 1, 2, 3],
"is_xonly": [true, false, true, false],
"signer_index": 2,
"expected": "B255FDCAC27B40C7CE7848E2D3B7BF5EA0ED756DA81565AC804CCCA3E1D5D239",
"comment": "Four tweaks: x-only, plain, x-only, plain. If an implementation prohibits applying plain tweaks after x-only tweaks, it can skip this test vector or return an error."
}
],
"error_test_cases": [
{
"key_indices": [1, 2, 0],
"nonce_indices": [1, 2, 0],
"tweak_indices": [4],
"is_xonly": [false],
"signer_index": 2,
"error": {
"type": "value",
"message": "The tweak must be less than n."
},
"comment": "Tweak is invalid because it exceeds group size"
}
]
}
2 changes: 1 addition & 1 deletion btcec/schnorr/musig2/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (

// ErrTweakedKeyOverflows is returned if a tweaking key is larger than
// 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141.
ErrTweakedKeyOverflows = fmt.Errorf("tweaked key is to large")
ErrTweakedKeyOverflows = fmt.Errorf("tweaked key is too large")
)

// sortableKeys defines a type of slice of public keys that implements the sort
Expand Down
139 changes: 137 additions & 2 deletions btcec/schnorr/musig2/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package musig2

import (
"encoding/hex"
"encoding/json"
"fmt"
"os"
Expand All @@ -20,6 +21,8 @@ const (
keySortTestVectorFileName = "key_sort_vectors.json"

keyAggTestVectorFileName = "key_agg_vectors.json"

keyTweakTestVectorFileName = "tweak_vectors.json"
)

type keySortTestVector struct {
Expand Down Expand Up @@ -114,7 +117,7 @@ func keysFromIndices(t *testing.T, indices []int,
}

func tweaksFromIndices(t *testing.T, indices []int,
tweaks []string, isXonly bool) []KeyTweakDesc {
tweaks []string, isXonly []bool) []KeyTweakDesc {

t.Helper()

Expand All @@ -125,7 +128,7 @@ func tweaksFromIndices(t *testing.T, indices []int,

testTweaks[i] = KeyTweakDesc{
Tweak: rawTweak,
IsXOnly: isXonly,
IsXOnly: isXonly[i],
}
}

Expand Down Expand Up @@ -255,3 +258,135 @@ func TestMuSig2KeyAggTestVectors(t *testing.T) {
})
}
}

type keyTweakInvalidTest struct {
Indices []int `json:"key_indices"`

NonceIndices []int `json:"nonce_indices"`

TweakIndices []int `json:"tweak_indices"`

IsXOnly []bool `json:"is_only"`

SignerIndex int `json:"signer_index"`

Comment string `json:"comment"`
}

type keyTweakValidTest struct {
Indices []int `json:"key_indices"`

NonceIndices []int `json:"nonce_indices"`

TweakIndices []int `json:"tweak_indices"`

IsXOnly []bool `json:"is_xonly"`

SignerIndex int `json:"signer_index"`

Expected string `json:"expected"`

Comment string `json:"comment"`
}

type keyTweakVector struct {
PrivKey string `json:"sk"`

PubKeys []string `json:"pubkeys"`

PrivNonce string `json:"secnonce"`

PubNonces []string `json:"pnonces"`

AggNnoce string `json:"aggnonce"`

Tweaks []string `json:"tweaks"`

Msg string `json:"msg"`

ValidCases []keyTweakValidTest `json:"valid_test_cases"`

InvalidCases []keyTweakInvalidTest `json:"error_test_cases"`
}

func pubNoncesFromIndices(t *testing.T, nonceIndices []int, pubNonces []string) [][PubNonceSize]byte {

nonces := make([][PubNonceSize]byte, len(nonceIndices))

for i, idx := range nonceIndices {
var pubNonce [PubNonceSize]byte
copy(pubNonce[:], mustParseHex(pubNonces[idx]))

nonces[i] = pubNonce
}

return nonces
}

// TestMuSig2TweakTestVectors tests that we properly handle the various edge
// cases related to tweaking public keys.
func TestMuSig2TweakTestVectors(t *testing.T) {
t.Parallel()

testVectorPath := path.Join(
testVectorBaseDir, keyTweakTestVectorFileName,
)
testVectorBytes, err := os.ReadFile(testVectorPath)
require.NoError(t, err)

var testCases keyTweakVector
require.NoError(t, json.Unmarshal(testVectorBytes, &testCases))

privKey, _ := btcec.PrivKeyFromBytes(mustParseHex(testCases.PrivKey))

var msg [32]byte
copy(msg[:], mustParseHex(testCases.Msg))

var secNonce [SecNonceSize]byte
copy(secNonce[:], mustParseHex(testCases.PrivNonce))

for _, testCase := range testCases.ValidCases {
testName := fmt.Sprintf("valid_%v",
strings.ToLower(testCase.Comment))
t.Run(testName, func(t *testing.T) {
pubKeys, err := keysFromIndices(
t, testCase.Indices, testCases.PubKeys,
)
require.NoError(t, err)

var tweaks []KeyTweakDesc
if len(testCase.TweakIndices) != 0 {
tweaks = tweaksFromIndices(
t, testCase.TweakIndices,
testCases.Tweaks, testCase.IsXOnly,
)
}

pubNonces := pubNoncesFromIndices(
t, testCase.NonceIndices, testCases.PubNonces,
)

combinedNonce, err := AggregateNonces(pubNonces)
require.NoError(t, err)

var opts []SignOption
if len(tweaks) != 0 {
opts = append(opts, WithTweaks(tweaks...))
}

partialSig, err := Sign(
secNonce, privKey, combinedNonce, pubKeys,
msg, opts...,
)

var partialSigBytes [32]byte
partialSig.S.PutBytesUnchecked(partialSigBytes[:])

require.Equal(
t, hex.EncodeToString(partialSigBytes[:]),
hex.EncodeToString(mustParseHex(testCase.Expected)),
)

})
}
}

0 comments on commit cc12483

Please sign in to comment.