chore: add unit test for signed validator and --protected-topic-flag

This commit is contained in:
Richard Ramos 2023-05-02 11:10:45 -04:00 committed by RichΛrd
parent f2d2e8127a
commit 837a0f2708
7 changed files with 129 additions and 5 deletions

View File

@ -206,6 +206,14 @@ var (
Destination: &options.Relay.Topics,
EnvVars: []string{"WAKUNODE2_TOPICS"},
})
ProtectedTopics = cliutils.NewGenericFlagMultiValue(&cli.GenericFlag{
Name: "protected-topic",
Usage: "Topics and its public key to be used for message validation, topic:pubkey. Argument may be repeated.",
EnvVars: []string{"WAKUNODE2_PROTECTED_TOPIC"},
Value: &cliutils.ProtectedTopicSlice{
Values: &options.Relay.ProtectedTopics,
},
})
RelayPeerExchange = altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "relay-peer-exchange",
Value: false,

View File

@ -45,6 +45,7 @@ func main() {
AgentString,
Relay,
Topics,
ProtectedTopics,
RelayPeerExchange,
MinRelayPeersToPublish,
StoreNodeFlag,

View File

@ -0,0 +1,55 @@
package cliutils
import (
"crypto/ecdsa"
"encoding/hex"
"errors"
"fmt"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
type ProtectedTopic struct {
Topic string
PublicKey *ecdsa.PublicKey
}
func (p ProtectedTopic) String() string {
pubKBytes := crypto.FromECDSAPub(p.PublicKey)
return fmt.Sprintf("%s:%s", p.Topic, hex.EncodeToString(pubKBytes))
}
type ProtectedTopicSlice struct {
Values *[]ProtectedTopic
}
func (k *ProtectedTopicSlice) Set(value string) error {
protectedTopicParts := strings.Split(value, ":")
if len(protectedTopicParts) != 2 {
return errors.New("expected topic_name:hex_encoded_public_key")
}
pubk, err := crypto.UnmarshalPubkey(common.FromHex(protectedTopicParts[1]))
if err != nil {
return err
}
*k.Values = append(*k.Values, ProtectedTopic{
Topic: protectedTopicParts[0],
PublicKey: pubk,
})
return nil
}
func (v *ProtectedTopicSlice) String() string {
if v.Values == nil {
return ""
}
var output []string
for _, v := range *v.Values {
output = append(output, v.String())
}
return strings.Join(output, ", ")
}

View File

@ -300,6 +300,11 @@ func Execute(options Options) {
failOnErr(err, "Error subscring to topic")
wakuNode.Broadcaster().Unregister(&nodeTopic, sub.C)
}
for _, protectedTopic := range options.Relay.ProtectedTopics {
err := wakuNode.Relay().AddSignedTopicValidator(protectedTopic.Topic, protectedTopic.PublicKey)
failOnErr(err, "Error adding signed topic validator")
}
}
for _, n := range options.StaticNodes {

View File

@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/multiformats/go-multiaddr"
"github.com/urfave/cli/v2"
"github.com/waku-org/go-waku/waku/cliutils"
)
// DiscV5Options are settings to enable a modified version of Ethereums Node
@ -24,6 +25,7 @@ type DiscV5Options struct {
type RelayOptions struct {
Enable bool
Topics cli.StringSlice
ProtectedTopics []cliutils.ProtectedTopic
PeerExchange bool
MinRelayPeersToPublish int
}

View File

@ -21,9 +21,10 @@ func MsgHash(pubSubTopic string, msg *pb.WakuMessage) []byte {
return hash.SHA256([]byte(pubSubTopic), msg.Payload, []byte(msg.ContentTopic))
}
func (w *WakuRelay) AddSignedTopicValidator(topic string, publicKey *ecdsa.PublicKey) error {
w.log.Info("adding validator to signed topic", zap.String("topic", topic), zap.String("publicKey", hex.EncodeToString(elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y))))
err := w.pubsub.RegisterTopicValidator(topic, func(ctx context.Context, peerID peer.ID, message *pubsub.Message) bool {
type validatorFn = func(ctx context.Context, peerID peer.ID, message *pubsub.Message) bool
func validatorFnBuilder(topic string, publicKey *ecdsa.PublicKey) validatorFn {
return func(ctx context.Context, peerID peer.ID, message *pubsub.Message) bool {
msg := new(pb.WakuMessage)
err := proto.Unmarshal(message.Data, msg)
if err != nil {
@ -34,11 +35,16 @@ func (w *WakuRelay) AddSignedTopicValidator(topic string, publicKey *ecdsa.Publi
signature := msg.Meta
return ecdsa.VerifyASN1(publicKey, msgHash, signature)
})
}
}
func (w *WakuRelay) AddSignedTopicValidator(topic string, publicKey *ecdsa.PublicKey) error {
w.log.Info("adding validator to signed topic", zap.String("topic", topic), zap.String("publicKey", hex.EncodeToString(elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y))))
err := w.pubsub.RegisterTopicValidator(topic, validatorFnBuilder(topic, publicKey))
return err
}
func (w *WakuRelay) SignMessage(privKey *ecdsa.PrivateKey, topic string, msg *pb.WakuMessage) error {
func SignMessage(privKey *ecdsa.PrivateKey, topic string, msg *pb.WakuMessage) error {
msgHash := MsgHash(topic, msg)
sign, err := ecdsa.SignASN1(rand.Reader, privKey, msgHash)
if err != nil {

View File

@ -0,0 +1,47 @@
package relay
import (
"bytes"
"context"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
pubsub "github.com/libp2p/go-libp2p-pubsub"
pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb"
"github.com/stretchr/testify/require"
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
"google.golang.org/protobuf/proto"
)
func TestMsgHash(t *testing.T) {
privKeyB, _ := hexutil.Decode("0x5526a8990317c9b7b58d07843d270f9cd1d9aaee129294c1c478abf7261dd9e6")
prvKey, _ := crypto.ToECDSA(privKeyB)
payload, _ := hexutil.Decode("0x3af5c7a8d71498e82e1991089d8429448f3b78277fac141af9052e77fc003dfb")
contentTopic := "my-content-topic"
pubsubTopic := "some-spam-protected-topic"
msg := &pb.WakuMessage{
Payload: payload,
ContentTopic: contentTopic,
}
err := SignMessage(prvKey, pubsubTopic, msg)
require.NoError(t, err)
msgData, _ := proto.Marshal(msg)
expectedMessageHash, _ := hexutil.Decode("0xd0e3231ec48f9c0cf9306b7100c30b4e85c78854b67b41e4ee388fb4610f543d")
messageHash := MsgHash(pubsubTopic, msg)
require.True(t, bytes.Equal(expectedMessageHash, messageHash))
myValidator := validatorFnBuilder(pubsubTopic, &prvKey.PublicKey)
result := myValidator(context.Background(), "", &pubsub.Message{
Message: &pubsub_pb.Message{
Data: msgData,
},
})
require.True(t, result)
}