Adds unit-tests and functional improvements to guardian signature verification (#1068)
* Add more comprehensive structs unit-tests * Make VerifySignatures fail on duplicate addresses * Adjust duplicate detection to guard on signatures instead of addresses * Add monotonic check in VerifySignatures * Move logic into VerifySignatures and add more test cases * Add a paranoid check for duplicate signers * Make VerifySignatures unit-tests less contrived * Add more verify signature test cases * Refactor VerifySignatures tests * Add VerifySignature fuzz tests * Add tc.result checking instead of hardcoded true * Change comparison so it throws debug on failure for fuzz tests * Add unit-tests for observation signature logic * Fix comment typos * Refactor observation tests * Add missing test case * Fix VAAInvalidSignatures test case label * Clean up unit-tests for observation and structs * Change errorString convention in test * Format Signature Verification Test Cases * Remove unnecessary casting * Add multi-signer same key cases * Fix err usage in test cases * Remove duplicate getVAA
This commit is contained in:
parent
5a316207dd
commit
bad4f7061b
|
@ -270,6 +270,25 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(ctx context.Context, m *gos
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if guardianSet doesn't have any keys
|
||||||
|
if len(p.gs.Keys) == 0 {
|
||||||
|
p.logger.Warn("dropping SignedVAAWithQuorum message since we have a guardian set without keys",
|
||||||
|
zap.String("digest", hash),
|
||||||
|
zap.Any("message", m),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if VAA doesn't have any signatures
|
||||||
|
if len(v.Signatures) == 0 {
|
||||||
|
p.logger.Warn("received SignedVAAWithQuorum message with no VAA signatures",
|
||||||
|
zap.String("digest", hash),
|
||||||
|
zap.Any("message", m),
|
||||||
|
zap.Any("vaa", v),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Verify VAA signature to prevent a DoS attack on our local store.
|
// Verify VAA signature to prevent a DoS attack on our local store.
|
||||||
if !v.VerifySignatures(p.gs.Keys) {
|
if !v.VerifySignatures(p.gs.Keys) {
|
||||||
p.logger.Warn("received SignedVAAWithQuorum message with invalid VAA signatures",
|
p.logger.Warn("received SignedVAAWithQuorum message with invalid VAA signatures",
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"github.com/certusone/wormhole/node/pkg/common"
|
||||||
|
gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
|
||||||
|
"github.com/certusone/wormhole/node/pkg/vaa"
|
||||||
|
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zaptest/observer"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getVAA() vaa.VAA {
|
||||||
|
var payload = []byte{97, 97, 97, 97, 97, 97}
|
||||||
|
var governanceEmitter = vaa.Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}
|
||||||
|
|
||||||
|
vaa := vaa.VAA{
|
||||||
|
Version: uint8(1),
|
||||||
|
GuardianSetIndex: uint32(1),
|
||||||
|
Signatures: nil,
|
||||||
|
Timestamp: time.Unix(0, 0),
|
||||||
|
Nonce: uint32(1),
|
||||||
|
Sequence: uint64(1),
|
||||||
|
ConsistencyLevel: uint8(32),
|
||||||
|
EmitterChain: vaa.ChainIDSolana,
|
||||||
|
EmitterAddress: governanceEmitter,
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
|
||||||
|
return vaa
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleInboundSignedVAAWithQuorum_NilGuardianSet(t *testing.T) {
|
||||||
|
vaa := getVAA()
|
||||||
|
marshalVAA, _ := vaa.Marshal()
|
||||||
|
|
||||||
|
// Stub out the minimum to get processor to dance
|
||||||
|
observedZapCore, observedLogs := observer.New(zap.InfoLevel)
|
||||||
|
observedLogger := zap.New(observedZapCore)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
signedVAAWithQuorum := &gossipv1.SignedVAAWithQuorum{Vaa: marshalVAA}
|
||||||
|
processor := Processor{}
|
||||||
|
processor.logger = observedLogger
|
||||||
|
|
||||||
|
processor.handleInboundSignedVAAWithQuorum(ctx, signedVAAWithQuorum)
|
||||||
|
|
||||||
|
// Check to see if we got an error, which we should have,
|
||||||
|
// because a `gs` is not defined on processor
|
||||||
|
assert.Equal(t, 1, observedLogs.Len())
|
||||||
|
firstLog := observedLogs.All()[0]
|
||||||
|
errorString := "dropping SignedVAAWithQuorum message since we haven't initialized our guardian set yet"
|
||||||
|
assert.Equal(t, errorString, firstLog.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleInboundSignedVAAWithQuorum(t *testing.T) {
|
||||||
|
goodPrivateKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
goodAddr1 := crypto.PubkeyToAddress(goodPrivateKey1.PublicKey)
|
||||||
|
badPrivateKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
label string
|
||||||
|
keyOrder []*ecdsa.PrivateKey
|
||||||
|
indexOrder []uint8
|
||||||
|
addrs []ethcommon.Address
|
||||||
|
errString string
|
||||||
|
}{
|
||||||
|
{label: "GuardianSetNoKeys", keyOrder: []*ecdsa.PrivateKey{}, indexOrder: []uint8{}, addrs: []ethcommon.Address{},
|
||||||
|
errString: "dropping SignedVAAWithQuorum message since we have a guardian set without keys"},
|
||||||
|
{label: "VAANoSignatures", keyOrder: []*ecdsa.PrivateKey{}, indexOrder: []uint8{0}, addrs: []ethcommon.Address{goodAddr1},
|
||||||
|
errString: "received SignedVAAWithQuorum message with no VAA signatures"},
|
||||||
|
{label: "VAAInvalidSignatures", keyOrder: []*ecdsa.PrivateKey{badPrivateKey1}, indexOrder: []uint8{0}, addrs: []ethcommon.Address{goodAddr1},
|
||||||
|
errString: "received SignedVAAWithQuorum message with invalid VAA signatures"},
|
||||||
|
{label: "DuplicateGoodSignaturesNonMonotonic", keyOrder: []*ecdsa.PrivateKey{goodPrivateKey1, goodPrivateKey1, goodPrivateKey1, goodPrivateKey1}, indexOrder: []uint8{0, 0, 0, 0}, addrs: []ethcommon.Address{goodAddr1},
|
||||||
|
errString: "received SignedVAAWithQuorum message with invalid VAA signatures"},
|
||||||
|
{label: "DuplicateGoodSignaturesMonotonic", keyOrder: []*ecdsa.PrivateKey{goodPrivateKey1, goodPrivateKey1, goodPrivateKey1, goodPrivateKey1}, indexOrder: []uint8{0, 1, 2, 3}, addrs: []ethcommon.Address{goodAddr1},
|
||||||
|
errString: "received SignedVAAWithQuorum message with invalid VAA signatures"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.label, func(t *testing.T) {
|
||||||
|
vaa := getVAA()
|
||||||
|
|
||||||
|
// Define a GuardianSet from test addrs
|
||||||
|
guardianSet := common.GuardianSet{
|
||||||
|
Keys: tc.addrs,
|
||||||
|
Index: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign with the keys at the proper index
|
||||||
|
for i, key := range tc.keyOrder {
|
||||||
|
vaa.AddSignature(key, tc.indexOrder[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
marshalVAA, err := vaa.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stub out the minimum to get processor to dance
|
||||||
|
observedZapCore, observedLogs := observer.New(zap.InfoLevel)
|
||||||
|
observedLogger := zap.New(observedZapCore)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
signedVAAWithQuorum := &gossipv1.SignedVAAWithQuorum{Vaa: marshalVAA}
|
||||||
|
processor := Processor{}
|
||||||
|
processor.gs = &guardianSet
|
||||||
|
processor.logger = observedLogger
|
||||||
|
|
||||||
|
processor.handleInboundSignedVAAWithQuorum(ctx, signedVAAWithQuorum)
|
||||||
|
|
||||||
|
// Check to see if we got an error, which we should have
|
||||||
|
assert.Equal(t, 1, observedLogs.Len())
|
||||||
|
firstLog := observedLogs.All()[0]
|
||||||
|
assert.Equal(t, tc.errString, firstLog.Message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -291,20 +291,39 @@ func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
|
|
||||||
h := v.SigningMsg()
|
h := v.SigningMsg()
|
||||||
|
|
||||||
|
last_index := -1
|
||||||
|
signing_addresses := []common.Address{}
|
||||||
|
|
||||||
for _, sig := range v.Signatures {
|
for _, sig := range v.Signatures {
|
||||||
if int(sig.Index) >= len(addresses) {
|
if int(sig.Index) >= len(addresses) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure increasing indexes
|
||||||
|
if int(sig.Index) <= last_index {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
last_index = int(sig.Index)
|
||||||
|
|
||||||
|
// Get pubKey to determine who signers address
|
||||||
pubKey, err := crypto.Ecrecover(h.Bytes(), sig.Signature[:])
|
pubKey, err := crypto.Ecrecover(h.Bytes(), sig.Signature[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
addr := common.BytesToAddress(crypto.Keccak256(pubKey[1:])[12:])
|
addr := common.BytesToAddress(crypto.Keccak256(pubKey[1:])[12:])
|
||||||
|
|
||||||
|
// Ensure this signer is at the correct positional index
|
||||||
if addr != addresses[sig.Index] {
|
if addr != addresses[sig.Index] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure we never see the same signer twice
|
||||||
|
for _, signing_address := range signing_addresses {
|
||||||
|
if signing_address == addr {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signing_addresses = append(signing_addresses, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -9,30 +9,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getVAA() *VAA {
|
|
||||||
var payload = []byte{97, 97, 97, 97, 97, 97}
|
|
||||||
var governanceEmitter = Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}
|
|
||||||
|
|
||||||
vaa := &VAA{
|
|
||||||
Version: uint8(1),
|
|
||||||
GuardianSetIndex: uint32(1),
|
|
||||||
Signatures: nil,
|
|
||||||
Timestamp: time.Unix(0, 0),
|
|
||||||
Nonce: uint32(1),
|
|
||||||
Sequence: uint64(1),
|
|
||||||
ConsistencyLevel: uint8(32),
|
|
||||||
EmitterChain: ChainIDSolana,
|
|
||||||
EmitterAddress: governanceEmitter,
|
|
||||||
Payload: payload,
|
|
||||||
}
|
|
||||||
|
|
||||||
return vaa
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainIDFromString(t *testing.T) {
|
func TestChainIDFromString(t *testing.T) {
|
||||||
type test struct {
|
type test struct {
|
||||||
input string
|
input string
|
||||||
|
@ -160,8 +141,26 @@ func TestChainId_String(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getVaa() VAA {
|
||||||
|
var payload = []byte{97, 97, 97, 97, 97, 97}
|
||||||
|
var governanceEmitter = Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}
|
||||||
|
|
||||||
|
return VAA{
|
||||||
|
Version: uint8(1),
|
||||||
|
GuardianSetIndex: uint32(1),
|
||||||
|
Signatures: nil,
|
||||||
|
Timestamp: time.Unix(0, 0),
|
||||||
|
Nonce: uint32(1),
|
||||||
|
Sequence: uint64(1),
|
||||||
|
ConsistencyLevel: uint8(32),
|
||||||
|
EmitterChain: ChainIDSolana,
|
||||||
|
EmitterAddress: governanceEmitter,
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAddSignature(t *testing.T) {
|
func TestAddSignature(t *testing.T) {
|
||||||
vaa := getVAA()
|
vaa := getVaa()
|
||||||
|
|
||||||
// Generate a random private key to sign with
|
// Generate a random private key to sign with
|
||||||
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
@ -173,57 +172,309 @@ func TestAddSignature(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSerializeBody(t *testing.T) {
|
func TestSerializeBody(t *testing.T) {
|
||||||
vaa := getVAA()
|
vaa := getVaa()
|
||||||
expected := []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x20, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61}
|
expected := []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x20, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61}
|
||||||
assert.Equal(t, vaa.serializeBody(), expected)
|
assert.Equal(t, vaa.serializeBody(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSigningBody(t *testing.T) {
|
func TestSigningBody(t *testing.T) {
|
||||||
vaa := getVAA()
|
vaa := getVaa()
|
||||||
expected := []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x20, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61}
|
expected := []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x20, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61}
|
||||||
assert.Equal(t, vaa.signingBody(), expected)
|
assert.Equal(t, vaa.signingBody(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSigningMsg(t *testing.T) {
|
func TestSigningMsg(t *testing.T) {
|
||||||
vaa := getVAA()
|
vaa := getVaa()
|
||||||
expected := common.HexToHash("4fae136bb1fd782fe1b5180ba735cdc83bcece3f9b7fd0e5e35300a61c8acd8f")
|
expected := common.HexToHash("4fae136bb1fd782fe1b5180ba735cdc83bcece3f9b7fd0e5e35300a61c8acd8f")
|
||||||
assert.Equal(t, vaa.SigningMsg(), expected)
|
assert.Equal(t, vaa.SigningMsg(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMessageID(t *testing.T) {
|
func TestMessageID(t *testing.T) {
|
||||||
vaa := getVAA()
|
vaa := getVaa()
|
||||||
expected := "1/0000000000000000000000000000000000000000000000000000000000000004/1"
|
expected := "1/0000000000000000000000000000000000000000000000000000000000000004/1"
|
||||||
assert.Equal(t, vaa.MessageID(), expected)
|
assert.Equal(t, vaa.MessageID(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHexDigest(t *testing.T) {
|
func TestHexDigest(t *testing.T) {
|
||||||
vaa := getVAA()
|
vaa := getVaa()
|
||||||
expected := "4fae136bb1fd782fe1b5180ba735cdc83bcece3f9b7fd0e5e35300a61c8acd8f"
|
expected := "4fae136bb1fd782fe1b5180ba735cdc83bcece3f9b7fd0e5e35300a61c8acd8f"
|
||||||
assert.Equal(t, vaa.HexDigest(), expected)
|
assert.Equal(t, vaa.HexDigest(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifySignatures(t *testing.T) {
|
func TestVerifySignatures(t *testing.T) {
|
||||||
vaa := getVAA()
|
// Generate some random private keys to sign with
|
||||||
|
privKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
privKey2, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
privKey3, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
privKey4, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
|
||||||
// Generate a random private key to sign with
|
// Give a fixed order of trusted addresses
|
||||||
privKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
addrs := []common.Address{}
|
||||||
assert.Nil(t, vaa.Signatures)
|
addrs = append(addrs, crypto.PubkeyToAddress(privKey1.PublicKey))
|
||||||
|
addrs = append(addrs, crypto.PubkeyToAddress(privKey2.PublicKey))
|
||||||
|
addrs = append(addrs, crypto.PubkeyToAddress(privKey3.PublicKey))
|
||||||
|
|
||||||
// Add a signature and make sure it's added
|
type test struct {
|
||||||
vaa.AddSignature(privKey, 0)
|
label string
|
||||||
assert.Equal(t, len(vaa.Signatures), 1)
|
keyOrder []*ecdsa.PrivateKey
|
||||||
|
addrs []common.Address
|
||||||
|
indexOrder []uint8
|
||||||
|
result bool
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a public key to compare to from our private key
|
tests := []test{
|
||||||
h := vaa.SigningMsg()
|
{label: "NoSignerZero",
|
||||||
pubKey, err := crypto.Ecrecover(h.Bytes(), vaa.Signatures[0].Signature[:])
|
keyOrder: []*ecdsa.PrivateKey{},
|
||||||
assert.Nil(t, err)
|
addrs: addrs,
|
||||||
assert.NotNil(t, pubKey)
|
indexOrder: []uint8{0},
|
||||||
|
result: true},
|
||||||
|
{label: "NoSignerOne",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{1},
|
||||||
|
result: true},
|
||||||
|
{label: "SingleZero",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0},
|
||||||
|
result: true},
|
||||||
|
{label: "RogueSingleOne",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey4},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0},
|
||||||
|
result: false},
|
||||||
|
{label: "RogueSingleZero",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey4},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0},
|
||||||
|
result: false},
|
||||||
|
{label: "SingleOne",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0},
|
||||||
|
result: true},
|
||||||
|
{label: "MultiUniqSignerMonotonicIndex",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey2, privKey3},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 1, 2},
|
||||||
|
result: true},
|
||||||
|
{label: "MultiMisOrderedSignerMonotonicIndex",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey3, privKey2, privKey1},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 1, 2}, result: false},
|
||||||
|
{label: "MultiUniqSignerNonMonotonic",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey2, privKey3},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 2, 1},
|
||||||
|
result: false},
|
||||||
|
{label: "MultiUniqSignerFullSameIndex0",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey2, privKey3},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 0, 0},
|
||||||
|
result: false},
|
||||||
|
{label: "MultiUniqSignerFullSameIndex1",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey2, privKey3},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 0, 0},
|
||||||
|
result: false},
|
||||||
|
{label: "MultiUniqSignerPartialSameIndex",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey2, privKey3},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 1, 1},
|
||||||
|
result: false},
|
||||||
|
{label: "MultiSameSignerPartialSameIndex",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey2, privKey2},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 1, 1},
|
||||||
|
result: false},
|
||||||
|
{label: "MultiSameSignerNonMonotonic",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey2, privKey2},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 2, 1},
|
||||||
|
result: false},
|
||||||
|
{label: "MultiSameSignerFullSameIndex",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey1, privKey1},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 0, 0},
|
||||||
|
result: false},
|
||||||
|
{label: "MultiSameSignerMonotonic",
|
||||||
|
keyOrder: []*ecdsa.PrivateKey{privKey1, privKey1, privKey1},
|
||||||
|
addrs: addrs,
|
||||||
|
indexOrder: []uint8{0, 1, 2},
|
||||||
|
result: false},
|
||||||
|
}
|
||||||
|
|
||||||
// Translate that public key back to an address
|
for _, tc := range tests {
|
||||||
addr := common.BytesToAddress(crypto.Keccak256(pubKey[1:])[12:])
|
t.Run(tc.label, func(t *testing.T) {
|
||||||
|
vaa := getVaa()
|
||||||
|
|
||||||
// Make sure that it verifies
|
for i, key := range tc.keyOrder {
|
||||||
assert.True(t, vaa.VerifySignatures([]common.Address{addr}))
|
vaa.AddSignature(key, tc.indexOrder[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, tc.result, vaa.VerifySignatures(tc.addrs))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifySignaturesFuzz(t *testing.T) {
|
||||||
|
// Generate some random trusted private keys to sign with
|
||||||
|
privKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
privKey2, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
privKey3, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
|
||||||
|
// Generate some random untrusted private keys to sign with
|
||||||
|
privKey4, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
privKey5, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
privKey6, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
|
||||||
|
// Give a fixed order of trusted addresses (we intentionally omit privKey4, privKey5, privKey6)
|
||||||
|
addrs := []common.Address{}
|
||||||
|
addrs = append(addrs, crypto.PubkeyToAddress(privKey1.PublicKey))
|
||||||
|
addrs = append(addrs, crypto.PubkeyToAddress(privKey2.PublicKey))
|
||||||
|
addrs = append(addrs, crypto.PubkeyToAddress(privKey3.PublicKey))
|
||||||
|
|
||||||
|
// key space for fuzz tests
|
||||||
|
keys := []*ecdsa.PrivateKey{}
|
||||||
|
keys = append(keys, privKey1)
|
||||||
|
keys = append(keys, privKey2)
|
||||||
|
keys = append(keys, privKey3)
|
||||||
|
keys = append(keys, privKey4)
|
||||||
|
keys = append(keys, privKey5)
|
||||||
|
keys = append(keys, privKey6)
|
||||||
|
|
||||||
|
// index space for fuzz tests
|
||||||
|
indexes := []uint8{0, 1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
label string
|
||||||
|
keyOrder []*ecdsa.PrivateKey
|
||||||
|
addrs []common.Address
|
||||||
|
indexOrder []uint8
|
||||||
|
result bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type allow struct {
|
||||||
|
keyPair []*ecdsa.PrivateKey
|
||||||
|
indexPair []uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Known good cases where we should have a verified result for
|
||||||
|
allows := []allow{
|
||||||
|
{keyPair: []*ecdsa.PrivateKey{}, indexPair: []uint8{}},
|
||||||
|
{keyPair: []*ecdsa.PrivateKey{privKey1}, indexPair: []uint8{0}},
|
||||||
|
{keyPair: []*ecdsa.PrivateKey{privKey2}, indexPair: []uint8{1}},
|
||||||
|
{keyPair: []*ecdsa.PrivateKey{privKey3}, indexPair: []uint8{2}},
|
||||||
|
{keyPair: []*ecdsa.PrivateKey{privKey1, privKey2}, indexPair: []uint8{0, 1}},
|
||||||
|
{keyPair: []*ecdsa.PrivateKey{privKey1, privKey3}, indexPair: []uint8{0, 2}},
|
||||||
|
{keyPair: []*ecdsa.PrivateKey{privKey2, privKey3}, indexPair: []uint8{1, 2}},
|
||||||
|
{keyPair: []*ecdsa.PrivateKey{privKey1, privKey2, privKey3}, indexPair: []uint8{0, 1, 2}},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []test{}
|
||||||
|
keyPairs := [][]*ecdsa.PrivateKey{}
|
||||||
|
indexPairs := [][]uint8{}
|
||||||
|
|
||||||
|
// Build empty keyPair
|
||||||
|
keyPairs = append(keyPairs, []*ecdsa.PrivateKey{})
|
||||||
|
|
||||||
|
// Build single keyPairs
|
||||||
|
for _, key := range keys {
|
||||||
|
keyPairs = append(keyPairs, []*ecdsa.PrivateKey{key})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build double keyPairs
|
||||||
|
for _, key_i := range keys {
|
||||||
|
for _, key_j := range keys {
|
||||||
|
keyPairs = append(keyPairs, []*ecdsa.PrivateKey{key_i, key_j})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build triple keyPairs
|
||||||
|
for _, key_i := range keys {
|
||||||
|
for _, key_j := range keys {
|
||||||
|
for _, key_k := range keys {
|
||||||
|
keyPairs = append(keyPairs, []*ecdsa.PrivateKey{key_i, key_j, key_k})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build empty indexPairs
|
||||||
|
indexPairs = append(indexPairs, []uint8{})
|
||||||
|
|
||||||
|
// Build single indexPairs
|
||||||
|
for _, ind := range indexes {
|
||||||
|
indexPairs = append(indexPairs, []uint8{ind})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build double indexPairs
|
||||||
|
for _, ind_i := range indexes {
|
||||||
|
for _, ind_j := range indexes {
|
||||||
|
indexPairs = append(indexPairs, []uint8{ind_i, ind_j})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build triple keyPairs
|
||||||
|
for _, ind_i := range indexes {
|
||||||
|
for _, ind_j := range indexes {
|
||||||
|
for _, ind_k := range indexes {
|
||||||
|
indexPairs = append(indexPairs, []uint8{ind_i, ind_j, ind_k})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build out the fuzzTest cases
|
||||||
|
for _, keyPair := range keyPairs {
|
||||||
|
for _, indexPair := range indexPairs {
|
||||||
|
if len(keyPair) == len(indexPair) {
|
||||||
|
result := false
|
||||||
|
|
||||||
|
for _, allow := range allows {
|
||||||
|
if reflect.DeepEqual(allow.indexPair, indexPair) && reflect.DeepEqual(allow.keyPair, keyPair) {
|
||||||
|
result = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test := test{label: "A", keyOrder: keyPair, addrs: addrs, indexOrder: indexPair, result: result}
|
||||||
|
tests = append(tests, test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the fuzzTest cases
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.label, func(t *testing.T) {
|
||||||
|
vaa := getVaa()
|
||||||
|
|
||||||
|
for i, key := range tc.keyOrder {
|
||||||
|
vaa.AddSignature(key, tc.indexOrder[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fuzz Debugging
|
||||||
|
* Tell us what keys and indexes were used (for debug when/if we have a failure case)
|
||||||
|
*/
|
||||||
|
if vaa.VerifySignatures(tc.addrs) != tc.result {
|
||||||
|
if len(tc.keyOrder) == 0 {
|
||||||
|
t.Logf("Key Order %v\n", tc.keyOrder)
|
||||||
|
} else {
|
||||||
|
keyIndex := []uint8{}
|
||||||
|
for i, key_i := range keys {
|
||||||
|
for _, key_k := range tc.keyOrder {
|
||||||
|
if key_i == key_k {
|
||||||
|
keyIndex = append(keyIndex, uint8(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("Key Order %v\n", keyIndex)
|
||||||
|
}
|
||||||
|
t.Logf("Index Order %v\n", tc.indexOrder)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, tc.result, vaa.VerifySignatures(tc.addrs))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringToAddress(t *testing.T) {
|
func TestStringToAddress(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue