diff --git a/node/cmd/guardiand/adminserver.go b/node/cmd/guardiand/adminserver.go index 3a24df38c..82607854c 100644 --- a/node/cmd/guardiand/adminserver.go +++ b/node/cmd/guardiand/adminserver.go @@ -320,7 +320,7 @@ func (s *nodePrivilegedService) InjectGovernanceVAA(ctx context.Context, req *no } // Generate digest of the unsigned VAA. - digest := v.SigningMsg() + digest := v.SigningDigest() s.logger.Info("governance VAA constructed", zap.Any("vaa", v), diff --git a/node/cmd/guardiand/adminverify.go b/node/cmd/guardiand/adminverify.go index 542512b4d..2013c545d 100644 --- a/node/cmd/guardiand/adminverify.go +++ b/node/cmd/guardiand/adminverify.go @@ -68,7 +68,7 @@ func runGovernanceVAAVerify(cmd *cobra.Command, args []string) { log.Fatalf("invalid update: %v", err) } - digest := v.SigningMsg().Bytes() + digest := v.SigningDigest().Bytes() if err != nil { panic(err) } diff --git a/node/pkg/accountant/submit_obs.go b/node/pkg/accountant/submit_obs.go index 8229a70ef..d313a857f 100644 --- a/node/pkg/accountant/submit_obs.go +++ b/node/pkg/accountant/submit_obs.go @@ -331,7 +331,10 @@ func SubmitObservationsToContract( return nil, fmt.Errorf("acct: failed to marshal accountant observation request: %w", err) } - digest := vaa.SigningMsg(append(submitObservationPrefix, bytes...)) + digest, err := vaa.MessageSigningDigest(submitObservationPrefix, bytes) + if err != nil { + return nil, fmt.Errorf("acct: failed to sign accountant Observation request: %w", err) + } sigBytes, err := ethCrypto.Sign(digest.Bytes(), gk) if err != nil { diff --git a/node/pkg/common/chainlock.go b/node/pkg/common/chainlock.go index 6696bbd96..6c93388ae 100644 --- a/node/pkg/common/chainlock.go +++ b/node/pkg/common/chainlock.go @@ -152,6 +152,6 @@ func (msg *MessagePublication) CreateVAA(gsIndex uint32) *vaa.VAA { func (msg *MessagePublication) CreateDigest() string { v := msg.CreateVAA(0) // The guardian set index is not part of the digest, so we can pass in zero. - db := v.SigningMsg() + db := v.SigningDigest() return hex.EncodeToString(db.Bytes()) } diff --git a/node/pkg/governor/governor.go b/node/pkg/governor/governor.go index 74f303ca5..056bac013 100644 --- a/node/pkg/governor/governor.go +++ b/node/pkg/governor/governor.go @@ -680,6 +680,6 @@ func (tk tokenKey) String() string { func (gov *ChainGovernor) HashFromMsg(msg *common.MessagePublication) string { v := msg.CreateVAA(0) // We can pass zero in as the guardian set index because it is not part of the digest. - digest := v.SigningMsg() + digest := v.SigningDigest() return hex.EncodeToString(digest.Bytes()) } diff --git a/node/pkg/notify/discord/notify.go b/node/pkg/notify/discord/notify.go index 3a42f0acb..d37773e2a 100644 --- a/node/pkg/notify/discord/notify.go +++ b/node/pkg/notify/discord/notify.go @@ -104,7 +104,7 @@ func (d *DiscordNotifier) LookupGroupID(groupName string) (string, error) { type Observation interface { GetEmitterChain() vaa.ChainID MessageID() string - SigningMsg() common.Hash + SigningDigest() common.Hash } func (d *DiscordNotifier) MissingSignaturesOnObservation(o Observation, hasSigs, wantSigs int, quorum bool, missing []string) error { @@ -145,7 +145,7 @@ func (d *DiscordNotifier) MissingSignaturesOnObservation(o Observation, hasSigs, Title: "Message with missing signatures", Fields: []discord.EmbedField{ {Name: "Message ID", Value: wrapCode(o.MessageID()), Inline: true}, - {Name: "Digest", Value: wrapCode(hex.EncodeToString(o.SigningMsg().Bytes())), Inline: true}, + {Name: "Digest", Value: wrapCode(hex.EncodeToString(o.SigningDigest().Bytes())), Inline: true}, {Name: "Quorum", Value: quorumText, Inline: true}, {Name: "Source Chain", Value: caser.String(o.GetEmitterChain().String()), Inline: false}, {Name: "Missing Guardians", Value: missingText.String(), Inline: false}, diff --git a/node/pkg/processor/broadcast.go b/node/pkg/processor/broadcast.go index c71387c0e..ee93bed29 100644 --- a/node/pkg/processor/broadcast.go +++ b/node/pkg/processor/broadcast.go @@ -28,7 +28,7 @@ func (p *Processor) broadcastSignature( signature []byte, txhash []byte, ) { - digest := o.SigningMsg() + digest := o.SigningDigest() obsv := gossipv1.SignedObservation{ Addr: crypto.PubkeyToAddress(p.gk.PublicKey).Bytes(), Hash: digest.Bytes(), diff --git a/node/pkg/processor/injection.go b/node/pkg/processor/injection.go index ac1552596..f36c90ba2 100644 --- a/node/pkg/processor/injection.go +++ b/node/pkg/processor/injection.go @@ -25,7 +25,7 @@ var ( // handleInjection processes a pre-populated VAA injected locally. func (p *Processor) handleInjection(ctx context.Context, v *vaa.VAA) { // Generate digest of the unsigned VAA. - digest := v.SigningMsg() + digest := v.SigningDigest() // The internal originator is responsible for logging the full VAA, just log the digest here. supervisor.Logger(ctx).Info("signing injected VAA", diff --git a/node/pkg/processor/message.go b/node/pkg/processor/message.go index 8b7ba0c16..d698464a1 100644 --- a/node/pkg/processor/message.go +++ b/node/pkg/processor/message.go @@ -129,7 +129,7 @@ func (p *Processor) handleMessage(ctx context.Context, k *common.MessagePublicat } // Generate digest of the unsigned VAA. - digest := v.SigningMsg() + digest := v.SigningDigest() // Sign the digest using our node's guardian key. s, err := crypto.Sign(digest.Bytes(), p.gk) diff --git a/node/pkg/processor/observation.go b/node/pkg/processor/observation.go index 4d860fa40..d1f83ce1b 100644 --- a/node/pkg/processor/observation.go +++ b/node/pkg/processor/observation.go @@ -227,7 +227,7 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(ctx context.Context, m *gos } // Calculate digest for logging - digest := v.SigningMsg() + digest := v.SigningDigest() hash := hex.EncodeToString(digest.Bytes()) if p.gs == nil { diff --git a/node/pkg/processor/processor.go b/node/pkg/processor/processor.go index 710e781ce..3e86e8413 100644 --- a/node/pkg/processor/processor.go +++ b/node/pkg/processor/processor.go @@ -30,9 +30,9 @@ type ( GetEmitterChain() vaa.ChainID // MessageID returns a human-readable emitter_chain/emitter_address/sequence tuple. MessageID() string - // SigningMsg returns the hash of the signing body of the observation. This is used + // SigningDigest returns the hash of the hash signing body of the observation. This is used // for signature generation and verification. - SigningMsg() ethcommon.Hash + SigningDigest() ethcommon.Hash // IsReliable returns whether this message is considered reliable meaning it can be reobserved. IsReliable() bool // HandleQuorum finishes processing the observation once a quorum of signatures have diff --git a/sdk/vaa/structs.go b/sdk/vaa/structs.go index 76892ce1c..f7e5c805c 100644 --- a/sdk/vaa/structs.go +++ b/sdk/vaa/structs.go @@ -617,7 +617,7 @@ func UnmarshalBatch(data []byte) (*BatchVAA, error) { // check for malformed data - verify that the hash of the observation matches what was supplied // the guardian has no interest in or use for observations after the batch has been signed, but still check - obsHash := headless.SigningMsg() + obsHash := headless.SigningDigest() if obsHash != v.Hashes[obsvIndex] { return nil, fmt.Errorf( "BatchVAA Observation %v does not match supplied hash", obsvIndex) @@ -652,21 +652,42 @@ func (v *BatchVAA) signingBody() []byte { return buf.Bytes() } -// SigningMsg returns the hash of the signing body. -func SigningMsg(data []byte) common.Hash { +func doubleKeccak(bz []byte) common.Hash { // In order to save space in the solana signature verification instruction, we hash twice so we only need to pass in // the first hash (32 bytes) vs the full body data. - return crypto.Keccak256Hash(crypto.Keccak256Hash(data).Bytes()) + return crypto.Keccak256Hash(crypto.Keccak256Hash(bz).Bytes()) } -// SigningMsg returns the hash of the signing body. This is used for signature generation and verification -func (v *VAA) SigningMsg() common.Hash { - return SigningMsg(v.signingBody()) +// This is a temporary method to produce a vaa signing digest on raw bytes. +// It is error prone and we should use `v.SigningDigest()` instead. +// whenever possible. +// This will be removed in a subsequent release. +func DeprecatedSigningDigest(bz []byte) common.Hash { + return doubleKeccak(bz) } -// SigningMsg returns the hash of the signing body. This is used for signature generation and verification -func (v *BatchVAA) SigningMsg() common.Hash { - return SigningMsg(v.signingBody()) +// MessageSigningDigest returns the hash of the data prepended with it's signing prefix. +// This is intending to be used for signing messages of different types from VAA's. +// The message prefix help protect from message collisions. +func MessageSigningDigest(prefix []byte, data []byte) (common.Hash, error) { + if len(prefix) < 32 { + // Prefixes must be at least 32 bytes + // https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0009_guardian_key.md + return common.Hash([32]byte{}), errors.New("prefix must be atleast 32 bytes") + } + return crypto.Keccak256Hash(prefix[:], data), nil +} + +// SigningDigest returns the hash of the vaa hash to be signed directly. +// This is used for signature generation and verification +func (v *VAA) SigningDigest() common.Hash { + return doubleKeccak(v.signingBody()) +} + +// BatchSigningDigest returns the hash of the batch vaa hash to be signed directly. +// This is used for signature generation and verification +func (v *BatchVAA) SigningDigest() common.Hash { + return doubleKeccak(v.signingBody()) } // ObsvHashArray creates an array of hashes of Observation. @@ -675,7 +696,7 @@ func (v *BatchVAA) ObsvHashArray() []common.Hash { hashes := make([]common.Hash, len(v.Observations)) for _, msg := range v.Observations { obsIndex := msg.Index - hashes[obsIndex] = msg.Observation.SigningMsg() + hashes[obsIndex] = msg.Observation.SigningDigest() } return hashes @@ -683,9 +704,10 @@ func (v *BatchVAA) ObsvHashArray() []common.Hash { // Verify Signature checks that the provided address matches the address that created the signature for the provided digest // Digest should be the output of SigningMsg(data).Bytes() -func VerifySignature(digest []byte, signature *Signature, address common.Address) bool { +// Should not be public as other message types should be verified using a message prefix. +func verifySignature(vaa_digest []byte, signature *Signature, address common.Address) bool { // retrieve the address that signed the data - pubKey, err := crypto.Ecrecover(digest, signature.Signature[:]) + pubKey, err := crypto.Ecrecover(vaa_digest, signature.Signature[:]) if err != nil { return false } @@ -696,7 +718,8 @@ func VerifySignature(digest []byte, signature *Signature, address common.Address } // Digest should be the output of SigningMsg(data).Bytes() -func VerifySignatures(digest []byte, signatures []*Signature, addresses []common.Address) bool { +// Should not be public as other message types should be verified using a message prefix. +func verifySignatures(vaa_digest []byte, signatures []*Signature, addresses []common.Address) bool { if len(addresses) < len(signatures) { return false } @@ -717,7 +740,7 @@ func VerifySignatures(digest []byte, signatures []*Signature, addresses []common // verify this signature addr := addresses[sig.Index] - ok := VerifySignature(digest, sig, addr) + ok := verifySignature(vaa_digest, sig, addr) if !ok { return false } @@ -734,16 +757,34 @@ func VerifySignatures(digest []byte, signatures []*Signature, addresses []common return true } +// Operating on bytes directly is error prone. We should use `vaa.VerifyingSignatures()` whenever possible. +// This function will be removed in a subsequent release. +func DeprecatedVerifySignatures(vaaBody []byte, signatures []*Signature, addresses []common.Address) bool { + vaaDigest := doubleKeccak(vaaBody) + return verifySignatures(vaaDigest[:], signatures, addresses) +} + +func VerifyMessageSignature(prefix []byte, messageBody []byte, signatures *Signature, addresses common.Address) bool { + if len(prefix) < 32 { + return false + } + msgDigest, err := MessageSigningDigest(prefix, messageBody) + if err != nil { + return false + } + return verifySignature(msgDigest[:], signatures, addresses) +} + // VerifySignatures verifies the signature of the VAA given the signer addresses. // Returns true if the signatures were verified successfully. func (v *VAA) VerifySignatures(addresses []common.Address) bool { - return VerifySignatures(v.SigningMsg().Bytes(), v.Signatures, addresses) + return verifySignatures(v.SigningDigest().Bytes(), v.Signatures, addresses) } // VerifySignatures verifies the signature of the BatchVAA given the signer addresses. // Returns true if the signatures were verified successfully. func (v *BatchVAA) VerifySignatures(addresses []common.Address) bool { - return VerifySignatures(v.SigningMsg().Bytes(), v.Signatures, addresses) + return verifySignatures(v.SigningDigest().Bytes(), v.Signatures, addresses) } // Marshal returns the binary representation of the BatchVAA @@ -798,7 +839,7 @@ func (v *BatchVAA) serializeBody() []byte { // - The signatures in the VAA is verified against the guardian set keys. func (v *VAA) Verify(addresses []common.Address) error { if addresses == nil { - return errors.New("No addresses were provided") + return errors.New("no addresses were provided") } // Check if VAA doesn't have any signatures @@ -906,12 +947,12 @@ func (v *BatchVAA) GetTransactionID() common.Hash { // HexDigest returns the hex-encoded digest. func (v *VAA) HexDigest() string { - return hex.EncodeToString(v.SigningMsg().Bytes()) + return hex.EncodeToString(v.SigningDigest().Bytes()) } // HexDigest returns the hex-encoded digest. func (b *BatchVAA) HexDigest() string { - return hex.EncodeToString(b.SigningMsg().Bytes()) + return hex.EncodeToString(b.SigningDigest().Bytes()) } /* @@ -932,7 +973,7 @@ func (v *VAA) serializeBody() []byte { } func (v *VAA) AddSignature(key *ecdsa.PrivateKey, index uint8) { - sig, err := crypto.Sign(v.SigningMsg().Bytes(), key) + sig, err := crypto.Sign(v.SigningDigest().Bytes(), key) if err != nil { panic(err) } @@ -948,7 +989,7 @@ func (v *VAA) AddSignature(key *ecdsa.PrivateKey, index uint8) { // creates signature of BatchVAA.Hashes and adds it to BatchVAA.Signatures. func (v *BatchVAA) AddSignature(key *ecdsa.PrivateKey, index uint8) { - sig, err := crypto.Sign(v.SigningMsg().Bytes(), key) + sig, err := crypto.Sign(v.SigningDigest().Bytes(), key) if err != nil { panic(err) } diff --git a/sdk/vaa/structs_test.go b/sdk/vaa/structs_test.go index 620c4facf..b3c297486 100644 --- a/sdk/vaa/structs_test.go +++ b/sdk/vaa/structs_test.go @@ -328,7 +328,7 @@ func TestSigningBody(t *testing.T) { func TestSigningMsg(t *testing.T) { vaa := getVaa() expected := common.HexToHash("4fae136bb1fd782fe1b5180ba735cdc83bcece3f9b7fd0e5e35300a61c8acd8f") - assert.Equal(t, vaa.SigningMsg(), expected) + assert.Equal(t, vaa.SigningDigest(), expected) } func TestMessageID(t *testing.T) { diff --git a/sdk/vaa/types_test.go b/sdk/vaa/types_test.go index 677d769b3..b6d0a7569 100644 --- a/sdk/vaa/types_test.go +++ b/sdk/vaa/types_test.go @@ -65,7 +65,7 @@ func TestVerifySignature(t *testing.T) { Payload: []byte("abcd"), } - data := v.SigningMsg() + data := v.SigningDigest() key, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) require.NoError(t, err) diff --git a/wormchain/x/wormhole/keeper/query_plugins.go b/wormchain/x/wormhole/keeper/query_plugins.go index 9960ff2b6..3494d19ca 100644 --- a/wormchain/x/wormhole/keeper/query_plugins.go +++ b/wormchain/x/wormhole/keeper/query_plugins.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/json" + "fmt" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmvmtypes "github.com/CosmWasm/wasmvm/types" @@ -17,18 +18,35 @@ func NewCustomQueryHandler(keeper Keeper) *wasmkeeper.QueryPlugins { } type WormholeQuery struct { - VerifyQuorum *verifyQuorumParams `json:"verify_quorum,omitempty"` - VerifySignature *verifySignatureParams `json:"verify_signature,omitempty"` + // This is deprecated and will be removed in a subsequent release + // because it uses an error-prone verification interface. + VerifyQuorum *verifyQuorumParams `json:"verify_quorum,omitempty"` + + // Verify the signatures on a VAA. + // Successor to `VerifyQuorum` as the verification uses a safe interface. + VerifyVaa *verifyVaaParams `json:"verify_vaa,omitempty"` + + // Verify the signatures on a message with a given message prefix. + // The caller should take care not to allow outside sources to choose the prefix. + VerifyMessageSignature *verifyMessageSignatureParams `json:"verify_message_signature,omitempty"` + + // Calculate the minimum number of participants required in quorum for the latest guardian set. CalculateQuorum *calculateQuorumParams `json:"calculate_quorum,omitempty"` } +// deprecated type verifyQuorumParams struct { Data []byte `json:"data"` GuardianSetIndex uint32 `json:"guardian_set_index"` Signatures []*vaa.Signature `json:"signatures"` } -type verifySignatureParams struct { +type verifyVaaParams struct { + Vaa []byte +} + +type verifyMessageSignatureParams struct { + Prefix []byte `json:"prefix"` Data []byte `json:"data"` GuardianSetIndex uint32 `json:"guardian_set_index"` Signature *vaa.Signature `json:"signature"` @@ -45,20 +63,37 @@ func WormholeQuerier(keeper Keeper) func(ctx sdk.Context, data json.RawMessage) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } + fmt.Println("-- WE GOT QUERIED") if wormholeQuery.VerifyQuorum != nil { - // handle the verify quorum query - digest := vaa.SigningMsg(wormholeQuery.VerifyQuorum.Data).Bytes() - err := keeper.VerifyQuorum(ctx, digest, wormholeQuery.VerifyQuorum.GuardianSetIndex, wormholeQuery.VerifyQuorum.Signatures) + // verify vaa using deprecated method + err := keeper.DeprecatedVerifyVaa(ctx, wormholeQuery.VerifyQuorum.Data, wormholeQuery.VerifyQuorum.GuardianSetIndex, wormholeQuery.VerifyQuorum.Signatures) if err != nil { return nil, err } return []byte("{}"), nil } - if wormholeQuery.VerifySignature != nil { - // handle the verify signature query - digest := vaa.SigningMsg(wormholeQuery.VerifySignature.Data).Bytes() - err := keeper.VerifySignature(ctx, digest, wormholeQuery.VerifySignature.GuardianSetIndex, wormholeQuery.VerifySignature.Signature) + if wormholeQuery.VerifyVaa != nil { + // verify vaa using recommended method + v, err := vaa.Unmarshal(wormholeQuery.VerifyVaa.Vaa) + if err != nil { + return nil, err + } + err = keeper.VerifyVAA(ctx, v) + if err != nil { + return nil, err + } + return []byte("{}"), nil + } + if wormholeQuery.VerifyMessageSignature != nil { + // handle the verify message signature query + err := keeper.VerifyMessageSignature( + ctx, + wormholeQuery.VerifyMessageSignature.Prefix, + wormholeQuery.VerifyMessageSignature.Data, + wormholeQuery.VerifyMessageSignature.GuardianSetIndex, + wormholeQuery.VerifyMessageSignature.Signature, + ) if err != nil { return nil, err } diff --git a/wormchain/x/wormhole/keeper/vaa.go b/wormchain/x/wormhole/keeper/vaa.go index fe74f3cdf..3da46e574 100644 --- a/wormchain/x/wormhole/keeper/vaa.go +++ b/wormchain/x/wormhole/keeper/vaa.go @@ -40,7 +40,7 @@ func (k Keeper) CalculateQuorum(ctx sdk.Context, guardianSetIndex uint32) (int, return CalculateQuorum(len(guardianSet.Keys)), &guardianSet, nil } -func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex uint32, signature *vaa.Signature) error { +func (k Keeper) VerifyMessageSignature(ctx sdk.Context, prefix []byte, data []byte, guardianSetIndex uint32, signature *vaa.Signature) error { // Calculate quorum and retrieve guardian set _, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex) if err != nil { @@ -53,7 +53,7 @@ func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex u return types.ErrGuardianIndexOutOfBounds } - ok := vaa.VerifySignature(data, signature, addresses[signature.Index]) + ok := vaa.VerifyMessageSignature(prefix, data, signature, addresses[signature.Index]) if !ok { return types.ErrSignaturesInvalid } @@ -61,7 +61,7 @@ func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex u return nil } -func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint32, signatures []*vaa.Signature) error { +func (k Keeper) DeprecatedVerifyVaa(ctx sdk.Context, vaaBody []byte, guardianSetIndex uint32, signatures []*vaa.Signature) error { // Calculate quorum and retrieve guardian set quorum, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex) if err != nil { @@ -72,7 +72,7 @@ func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint } // Verify signatures - ok := vaa.VerifySignatures(data, signatures, guardianSet.KeysAsAddresses()) + ok := vaa.DeprecatedVerifySignatures(vaaBody, signatures, guardianSet.KeysAsAddresses()) if !ok { return types.ErrSignaturesInvalid } @@ -80,8 +80,23 @@ func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint return nil } -func (k Keeper) VerifyVAA(ctx sdk.Context, vaa *vaa.VAA) error { - return k.VerifyQuorum(ctx, vaa.SigningMsg().Bytes(), vaa.GuardianSetIndex, vaa.Signatures) +func (k Keeper) VerifyVAA(ctx sdk.Context, v *vaa.VAA) error { + // Calculate quorum and retrieve guardian set + quorum, guardianSet, err := k.CalculateQuorum(ctx, v.GuardianSetIndex) + if err != nil { + return err + } + if len(v.Signatures) < quorum { + return types.ErrNoQuorum + } + + // Verify signatures + ok := v.VerifySignatures(guardianSet.KeysAsAddresses()) + if !ok { + return types.ErrSignaturesInvalid + } + + return nil } // Verify a governance VAA: diff --git a/wormchain/x/wormhole/keeper/vaa_test.go b/wormchain/x/wormhole/keeper/vaa_test.go index 2231cf38b..cad33154c 100644 --- a/wormchain/x/wormhole/keeper/vaa_test.go +++ b/wormchain/x/wormhole/keeper/vaa_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" keepertest "github.com/wormhole-foundation/wormchain/testutil/keeper" "github.com/wormhole-foundation/wormchain/x/wormhole/keeper" "github.com/wormhole-foundation/wormchain/x/wormhole/types" @@ -110,8 +111,11 @@ func sign(data common.Hash, key *ecdsa.PrivateKey, index uint8) *vaa.Signature { } } -func TestVerifySignature(t *testing.T) { - payload := vaa.SigningMsg([]byte{97, 97, 97, 97, 97, 97}) +func TestVerifyMessageSignature(t *testing.T) { + prefix := [32]byte{} + payload := []byte{97, 97, 97, 97, 97, 97} + digest, err := vaa.MessageSigningDigest(prefix[:], payload) + require.NoError(t, err) privKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader) privKey2, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader) @@ -154,13 +158,85 @@ func TestVerifySignature(t *testing.T) { keeper.AppendGuardianSet(ctx, tc.guardianSet) // build the signature - signature := sign(payload, tc.signer, 0) + signature := sign(digest, tc.signer, 0) if tc.setSigIndex { signature.Index = tc.sigIndex } // verify the signature - err := keeper.VerifySignature(ctx, payload.Bytes(), tc.guardianSet.Index, signature) + err := keeper.VerifyMessageSignature(ctx, prefix[:], payload, tc.guardianSet.Index, signature) + + if tc.willError == true { + assert.NotNil(t, err) + assert.Equal(t, err, tc.err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestVerifyVaaSignature(t *testing.T) { + v := &vaa.VAA{ + Version: 1, + GuardianSetIndex: 0, + Timestamp: time.Now(), + Nonce: 1, + Sequence: 1, + Payload: []byte{97, 97, 97, 97, 97, 97}, + } + digest := v.SigningDigest() + privKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader) + privKey2, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader) + + addr1 := crypto.PubkeyToAddress(privKey1.PublicKey) + + addrsBytes := [][]byte{} + addrsBytes = append(addrsBytes, addr1.Bytes()) + + tests := []struct { + label string + guardianSet types.GuardianSet + signer *ecdsa.PrivateKey + setSigIndex bool + sigIndex uint8 + willError bool + err error + }{ + + {label: "ValidSigner", + guardianSet: types.GuardianSet{Index: 0, Keys: addrsBytes, ExpirationTime: 0}, + signer: privKey1, + willError: false}, + {label: "IndexOutOfBounds", + guardianSet: types.GuardianSet{Index: 0, Keys: addrsBytes, ExpirationTime: 0}, + signer: privKey1, + setSigIndex: true, + sigIndex: 1, + willError: true, + // this out of bounds issue will trigger invalid signature from sdk. + err: types.ErrSignaturesInvalid}, + {label: "InvalidSigner", + guardianSet: types.GuardianSet{Index: 0, Keys: addrsBytes, ExpirationTime: 0}, + signer: privKey2, + willError: true, + err: types.ErrSignaturesInvalid}, + } + + for _, tc := range tests { + t.Run(tc.label, func(t *testing.T) { + keeper, ctx := keepertest.WormholeKeeper(t) + keeper.AppendGuardianSet(ctx, tc.guardianSet) + + // build the signature + signature := sign(digest, tc.signer, 0) + if tc.setSigIndex { + signature.Index = tc.sigIndex + } + v.Signatures = append(v.Signatures, signature) + + // verify the signature + err := keeper.VerifyVAA(ctx, v) if tc.willError == true { assert.NotNil(t, err)