node: calculate digests using Vaa type or using message prefix

This commit is contained in:
Conor Patrick 2023-02-23 02:31:15 +00:00
parent 64fb51d68d
commit d4e0445785
17 changed files with 227 additions and 57 deletions

View File

@ -320,7 +320,7 @@ func (s *nodePrivilegedService) InjectGovernanceVAA(ctx context.Context, req *no
} }
// Generate digest of the unsigned VAA. // Generate digest of the unsigned VAA.
digest := v.SigningMsg() digest := v.SigningDigest()
s.logger.Info("governance VAA constructed", s.logger.Info("governance VAA constructed",
zap.Any("vaa", v), zap.Any("vaa", v),

View File

@ -68,7 +68,7 @@ func runGovernanceVAAVerify(cmd *cobra.Command, args []string) {
log.Fatalf("invalid update: %v", err) log.Fatalf("invalid update: %v", err)
} }
digest := v.SigningMsg().Bytes() digest := v.SigningDigest().Bytes()
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -331,7 +331,10 @@ func SubmitObservationsToContract(
return nil, fmt.Errorf("acct: failed to marshal accountant observation request: %w", err) 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) sigBytes, err := ethCrypto.Sign(digest.Bytes(), gk)
if err != nil { if err != nil {

View File

@ -152,6 +152,6 @@ func (msg *MessagePublication) CreateVAA(gsIndex uint32) *vaa.VAA {
func (msg *MessagePublication) CreateDigest() string { 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. 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()) return hex.EncodeToString(db.Bytes())
} }

View File

@ -680,6 +680,6 @@ func (tk tokenKey) String() string {
func (gov *ChainGovernor) HashFromMsg(msg *common.MessagePublication) 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. 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()) return hex.EncodeToString(digest.Bytes())
} }

View File

@ -104,7 +104,7 @@ func (d *DiscordNotifier) LookupGroupID(groupName string) (string, error) {
type Observation interface { type Observation interface {
GetEmitterChain() vaa.ChainID GetEmitterChain() vaa.ChainID
MessageID() string MessageID() string
SigningMsg() common.Hash SigningDigest() common.Hash
} }
func (d *DiscordNotifier) MissingSignaturesOnObservation(o Observation, hasSigs, wantSigs int, quorum bool, missing []string) error { 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", Title: "Message with missing signatures",
Fields: []discord.EmbedField{ Fields: []discord.EmbedField{
{Name: "Message ID", Value: wrapCode(o.MessageID()), Inline: true}, {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: "Quorum", Value: quorumText, Inline: true},
{Name: "Source Chain", Value: caser.String(o.GetEmitterChain().String()), Inline: false}, {Name: "Source Chain", Value: caser.String(o.GetEmitterChain().String()), Inline: false},
{Name: "Missing Guardians", Value: missingText.String(), Inline: false}, {Name: "Missing Guardians", Value: missingText.String(), Inline: false},

View File

@ -28,7 +28,7 @@ func (p *Processor) broadcastSignature(
signature []byte, signature []byte,
txhash []byte, txhash []byte,
) { ) {
digest := o.SigningMsg() digest := o.SigningDigest()
obsv := gossipv1.SignedObservation{ obsv := gossipv1.SignedObservation{
Addr: crypto.PubkeyToAddress(p.gk.PublicKey).Bytes(), Addr: crypto.PubkeyToAddress(p.gk.PublicKey).Bytes(),
Hash: digest.Bytes(), Hash: digest.Bytes(),

View File

@ -25,7 +25,7 @@ var (
// handleInjection processes a pre-populated VAA injected locally. // handleInjection processes a pre-populated VAA injected locally.
func (p *Processor) handleInjection(ctx context.Context, v *vaa.VAA) { func (p *Processor) handleInjection(ctx context.Context, v *vaa.VAA) {
// Generate digest of the unsigned 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. // The internal originator is responsible for logging the full VAA, just log the digest here.
supervisor.Logger(ctx).Info("signing injected VAA", supervisor.Logger(ctx).Info("signing injected VAA",

View File

@ -129,7 +129,7 @@ func (p *Processor) handleMessage(ctx context.Context, k *common.MessagePublicat
} }
// Generate digest of the unsigned VAA. // Generate digest of the unsigned VAA.
digest := v.SigningMsg() digest := v.SigningDigest()
// Sign the digest using our node's guardian key. // Sign the digest using our node's guardian key.
s, err := crypto.Sign(digest.Bytes(), p.gk) s, err := crypto.Sign(digest.Bytes(), p.gk)

View File

@ -227,7 +227,7 @@ func (p *Processor) handleInboundSignedVAAWithQuorum(ctx context.Context, m *gos
} }
// Calculate digest for logging // Calculate digest for logging
digest := v.SigningMsg() digest := v.SigningDigest()
hash := hex.EncodeToString(digest.Bytes()) hash := hex.EncodeToString(digest.Bytes())
if p.gs == nil { if p.gs == nil {

View File

@ -30,9 +30,9 @@ type (
GetEmitterChain() vaa.ChainID GetEmitterChain() vaa.ChainID
// MessageID returns a human-readable emitter_chain/emitter_address/sequence tuple. // MessageID returns a human-readable emitter_chain/emitter_address/sequence tuple.
MessageID() string 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. // 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 returns whether this message is considered reliable meaning it can be reobserved.
IsReliable() bool IsReliable() bool
// HandleQuorum finishes processing the observation once a quorum of signatures have // HandleQuorum finishes processing the observation once a quorum of signatures have

View File

@ -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 // 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 // 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] { if obsHash != v.Hashes[obsvIndex] {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"BatchVAA Observation %v does not match supplied hash", obsvIndex) "BatchVAA Observation %v does not match supplied hash", obsvIndex)
@ -652,21 +652,42 @@ func (v *BatchVAA) signingBody() []byte {
return buf.Bytes() return buf.Bytes()
} }
// SigningMsg returns the hash of the signing body. func doubleKeccak(bz []byte) common.Hash {
func SigningMsg(data []byte) common.Hash {
// In order to save space in the solana signature verification instruction, we hash twice so we only need to pass in // 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. // 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 // This is a temporary method to produce a vaa signing digest on raw bytes.
func (v *VAA) SigningMsg() common.Hash { // It is error prone and we should use `v.SigningDigest()` instead.
return SigningMsg(v.signingBody()) // 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 // MessageSigningDigest returns the hash of the data prepended with it's signing prefix.
func (v *BatchVAA) SigningMsg() common.Hash { // This is intending to be used for signing messages of different types from VAA's.
return SigningMsg(v.signingBody()) // 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. // 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)) hashes := make([]common.Hash, len(v.Observations))
for _, msg := range v.Observations { for _, msg := range v.Observations {
obsIndex := msg.Index obsIndex := msg.Index
hashes[obsIndex] = msg.Observation.SigningMsg() hashes[obsIndex] = msg.Observation.SigningDigest()
} }
return hashes 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 // 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() // 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 // 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 { if err != nil {
return false return false
} }
@ -696,7 +718,8 @@ func VerifySignature(digest []byte, signature *Signature, address common.Address
} }
// Digest should be the output of SigningMsg(data).Bytes() // 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) { if len(addresses) < len(signatures) {
return false return false
} }
@ -717,7 +740,7 @@ func VerifySignatures(digest []byte, signatures []*Signature, addresses []common
// verify this signature // verify this signature
addr := addresses[sig.Index] addr := addresses[sig.Index]
ok := VerifySignature(digest, sig, addr) ok := verifySignature(vaa_digest, sig, addr)
if !ok { if !ok {
return false return false
} }
@ -734,16 +757,34 @@ func VerifySignatures(digest []byte, signatures []*Signature, addresses []common
return true 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. // VerifySignatures verifies the signature of the VAA given the signer addresses.
// Returns true if the signatures were verified successfully. // Returns true if the signatures were verified successfully.
func (v *VAA) VerifySignatures(addresses []common.Address) bool { 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. // VerifySignatures verifies the signature of the BatchVAA given the signer addresses.
// Returns true if the signatures were verified successfully. // Returns true if the signatures were verified successfully.
func (v *BatchVAA) VerifySignatures(addresses []common.Address) bool { 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 // 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. // - The signatures in the VAA is verified against the guardian set keys.
func (v *VAA) Verify(addresses []common.Address) error { func (v *VAA) Verify(addresses []common.Address) error {
if addresses == nil { 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 // Check if VAA doesn't have any signatures
@ -906,12 +947,12 @@ func (v *BatchVAA) GetTransactionID() common.Hash {
// HexDigest returns the hex-encoded digest. // HexDigest returns the hex-encoded digest.
func (v *VAA) HexDigest() string { func (v *VAA) HexDigest() string {
return hex.EncodeToString(v.SigningMsg().Bytes()) return hex.EncodeToString(v.SigningDigest().Bytes())
} }
// HexDigest returns the hex-encoded digest. // HexDigest returns the hex-encoded digest.
func (b *BatchVAA) HexDigest() string { 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) { 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 { if err != nil {
panic(err) 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. // creates signature of BatchVAA.Hashes and adds it to BatchVAA.Signatures.
func (v *BatchVAA) AddSignature(key *ecdsa.PrivateKey, index uint8) { 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 { if err != nil {
panic(err) panic(err)
} }

View File

@ -328,7 +328,7 @@ func TestSigningBody(t *testing.T) {
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.SigningDigest(), expected)
} }
func TestMessageID(t *testing.T) { func TestMessageID(t *testing.T) {

View File

@ -65,7 +65,7 @@ func TestVerifySignature(t *testing.T) {
Payload: []byte("abcd"), Payload: []byte("abcd"),
} }
data := v.SigningMsg() data := v.SigningDigest()
key, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) key, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
require.NoError(t, err) require.NoError(t, err)

View File

@ -2,6 +2,7 @@ package keeper
import ( import (
"encoding/json" "encoding/json"
"fmt"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmvmtypes "github.com/CosmWasm/wasmvm/types" wasmvmtypes "github.com/CosmWasm/wasmvm/types"
@ -17,18 +18,35 @@ func NewCustomQueryHandler(keeper Keeper) *wasmkeeper.QueryPlugins {
} }
type WormholeQuery struct { type WormholeQuery struct {
// 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"` VerifyQuorum *verifyQuorumParams `json:"verify_quorum,omitempty"`
VerifySignature *verifySignatureParams `json:"verify_signature,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"` CalculateQuorum *calculateQuorumParams `json:"calculate_quorum,omitempty"`
} }
// deprecated
type verifyQuorumParams struct { type verifyQuorumParams struct {
Data []byte `json:"data"` Data []byte `json:"data"`
GuardianSetIndex uint32 `json:"guardian_set_index"` GuardianSetIndex uint32 `json:"guardian_set_index"`
Signatures []*vaa.Signature `json:"signatures"` Signatures []*vaa.Signature `json:"signatures"`
} }
type verifySignatureParams struct { type verifyVaaParams struct {
Vaa []byte
}
type verifyMessageSignatureParams struct {
Prefix []byte `json:"prefix"`
Data []byte `json:"data"` Data []byte `json:"data"`
GuardianSetIndex uint32 `json:"guardian_set_index"` GuardianSetIndex uint32 `json:"guardian_set_index"`
Signature *vaa.Signature `json:"signature"` Signature *vaa.Signature `json:"signature"`
@ -45,20 +63,37 @@ func WormholeQuerier(keeper Keeper) func(ctx sdk.Context, data json.RawMessage)
if err != nil { if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
} }
fmt.Println("-- WE GOT QUERIED")
if wormholeQuery.VerifyQuorum != nil { if wormholeQuery.VerifyQuorum != nil {
// handle the verify quorum query // verify vaa using deprecated method
digest := vaa.SigningMsg(wormholeQuery.VerifyQuorum.Data).Bytes() err := keeper.DeprecatedVerifyVaa(ctx, wormholeQuery.VerifyQuorum.Data, wormholeQuery.VerifyQuorum.GuardianSetIndex, wormholeQuery.VerifyQuorum.Signatures)
err := keeper.VerifyQuorum(ctx, digest, wormholeQuery.VerifyQuorum.GuardianSetIndex, wormholeQuery.VerifyQuorum.Signatures)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return []byte("{}"), nil return []byte("{}"), nil
} }
if wormholeQuery.VerifySignature != nil { if wormholeQuery.VerifyVaa != nil {
// handle the verify signature query // verify vaa using recommended method
digest := vaa.SigningMsg(wormholeQuery.VerifySignature.Data).Bytes() v, err := vaa.Unmarshal(wormholeQuery.VerifyVaa.Vaa)
err := keeper.VerifySignature(ctx, digest, wormholeQuery.VerifySignature.GuardianSetIndex, wormholeQuery.VerifySignature.Signature) 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -40,7 +40,7 @@ func (k Keeper) CalculateQuorum(ctx sdk.Context, guardianSetIndex uint32) (int,
return CalculateQuorum(len(guardianSet.Keys)), &guardianSet, nil 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 // Calculate quorum and retrieve guardian set
_, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex) _, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex)
if err != nil { if err != nil {
@ -53,7 +53,7 @@ func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex u
return types.ErrGuardianIndexOutOfBounds return types.ErrGuardianIndexOutOfBounds
} }
ok := vaa.VerifySignature(data, signature, addresses[signature.Index]) ok := vaa.VerifyMessageSignature(prefix, data, signature, addresses[signature.Index])
if !ok { if !ok {
return types.ErrSignaturesInvalid return types.ErrSignaturesInvalid
} }
@ -61,7 +61,7 @@ func (k Keeper) VerifySignature(ctx sdk.Context, data []byte, guardianSetIndex u
return nil 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 // Calculate quorum and retrieve guardian set
quorum, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex) quorum, guardianSet, err := k.CalculateQuorum(ctx, guardianSetIndex)
if err != nil { if err != nil {
@ -72,7 +72,7 @@ func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint
} }
// Verify signatures // Verify signatures
ok := vaa.VerifySignatures(data, signatures, guardianSet.KeysAsAddresses()) ok := vaa.DeprecatedVerifySignatures(vaaBody, signatures, guardianSet.KeysAsAddresses())
if !ok { if !ok {
return types.ErrSignaturesInvalid return types.ErrSignaturesInvalid
} }
@ -80,8 +80,23 @@ func (k Keeper) VerifyQuorum(ctx sdk.Context, data []byte, guardianSetIndex uint
return nil return nil
} }
func (k Keeper) VerifyVAA(ctx sdk.Context, vaa *vaa.VAA) error { func (k Keeper) VerifyVAA(ctx sdk.Context, v *vaa.VAA) error {
return k.VerifyQuorum(ctx, vaa.SigningMsg().Bytes(), vaa.GuardianSetIndex, vaa.Signatures) // 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: // Verify a governance VAA:

View File

@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"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"
keepertest "github.com/wormhole-foundation/wormchain/testutil/keeper" keepertest "github.com/wormhole-foundation/wormchain/testutil/keeper"
"github.com/wormhole-foundation/wormchain/x/wormhole/keeper" "github.com/wormhole-foundation/wormchain/x/wormhole/keeper"
"github.com/wormhole-foundation/wormchain/x/wormhole/types" "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) { func TestVerifyMessageSignature(t *testing.T) {
payload := vaa.SigningMsg([]byte{97, 97, 97, 97, 97, 97}) 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) privKey1, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
privKey2, _ := 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) keeper.AppendGuardianSet(ctx, tc.guardianSet)
// build the signature // build the signature
signature := sign(payload, tc.signer, 0) signature := sign(digest, tc.signer, 0)
if tc.setSigIndex { if tc.setSigIndex {
signature.Index = tc.sigIndex signature.Index = tc.sigIndex
} }
// verify the signature // 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 { if tc.willError == true {
assert.NotNil(t, err) assert.NotNil(t, err)