2023-04-05 21:03:29 +00:00
package relay
import (
2023-05-09 14:48:55 +00:00
"bytes"
2023-04-05 21:03:29 +00:00
"context"
"crypto/ecdsa"
2023-05-22 21:03:40 +00:00
"crypto/elliptic"
2023-05-04 15:56:56 +00:00
"encoding/binary"
2023-05-22 21:03:40 +00:00
"encoding/hex"
2023-05-04 15:56:56 +00:00
"time"
2023-04-05 21:03:29 +00:00
2023-05-03 15:59:47 +00:00
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
2023-04-05 21:03:29 +00:00
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/waku-org/go-waku/waku/v2/hash"
2023-05-09 14:48:55 +00:00
"github.com/waku-org/go-waku/waku/v2/protocol"
2023-04-05 21:03:29 +00:00
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
2023-05-04 15:56:56 +00:00
"github.com/waku-org/go-waku/waku/v2/timesource"
2023-04-05 21:03:29 +00:00
"go.uber.org/zap"
proto "google.golang.org/protobuf/proto"
)
// Application level message hash
func MsgHash ( pubSubTopic string , msg * pb . WakuMessage ) [ ] byte {
2023-05-04 15:56:56 +00:00
timestampBytes := make ( [ ] byte , 8 )
binary . LittleEndian . PutUint64 ( timestampBytes , uint64 ( msg . Timestamp ) )
var ephemeralByte byte
if msg . Ephemeral {
ephemeralByte = 1
}
return hash . SHA256 (
[ ] byte ( pubSubTopic ) ,
msg . Payload ,
[ ] byte ( msg . ContentTopic ) ,
timestampBytes ,
[ ] byte { ephemeralByte } ,
)
}
const MessageWindowDuration = time . Minute * 5
func withinTimeWindow ( t timesource . Timesource , msg * pb . WakuMessage ) bool {
if msg . Timestamp == 0 {
return false
}
now := t . Now ( )
msgTime := time . Unix ( 0 , msg . Timestamp )
return now . Sub ( msgTime ) . Abs ( ) <= MessageWindowDuration
2023-04-05 21:03:29 +00:00
}
2023-05-02 15:10:45 +00:00
type validatorFn = func ( ctx context . Context , peerID peer . ID , message * pubsub . Message ) bool
2023-05-22 21:03:40 +00:00
func validatorFnBuilder ( t timesource . Timesource , publicKey * ecdsa . PublicKey ) ( validatorFn , error ) {
address := crypto . PubkeyToAddress ( * publicKey )
2023-05-09 14:48:55 +00:00
topic := protocol . NewNamedShardingPubsubTopic ( address . String ( ) + "/proto" ) . String ( )
2023-05-02 15:10:45 +00:00
return func ( ctx context . Context , peerID peer . ID , message * pubsub . Message ) bool {
2023-04-05 21:03:29 +00:00
msg := new ( pb . WakuMessage )
err := proto . Unmarshal ( message . Data , msg )
if err != nil {
return false
}
2023-05-04 15:56:56 +00:00
if ! withinTimeWindow ( t , msg ) {
return false
}
2023-04-05 21:03:29 +00:00
msgHash := MsgHash ( topic , msg )
signature := msg . Meta
2023-05-09 14:48:55 +00:00
pubKey , err := crypto . SigToPub ( msgHash , signature )
if err != nil {
return false
}
msgAddress := crypto . PubkeyToAddress ( * pubKey )
return bytes . Equal ( msgAddress . Bytes ( ) , address . Bytes ( ) )
} , nil
2023-05-02 15:10:45 +00:00
}
2023-05-22 21:03:40 +00:00
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 ) ) ) )
2023-05-09 14:48:55 +00:00
2023-05-22 21:03:40 +00:00
fn , err := validatorFnBuilder ( w . timesource , publicKey )
2023-05-09 14:48:55 +00:00
if err != nil {
return err
}
err = w . pubsub . RegisterTopicValidator ( topic , fn )
2023-05-04 14:04:54 +00:00
if err != nil {
return err
}
if ! w . IsSubscribed ( topic ) {
w . log . Warn ( "relay is not subscribed to signed topic" , zap . String ( "topic" , topic ) )
}
return nil
2023-04-05 21:03:29 +00:00
}
2023-05-09 14:48:55 +00:00
func SignMessage ( privKey * ecdsa . PrivateKey , msg * pb . WakuMessage ) error {
topic := PrivKeyToTopic ( privKey )
2023-04-05 21:03:29 +00:00
msgHash := MsgHash ( topic , msg )
2023-05-03 15:59:47 +00:00
sign , err := secp256k1 . Sign ( msgHash , crypto . FromECDSA ( privKey ) )
2023-04-05 21:03:29 +00:00
if err != nil {
return err
}
2023-05-09 14:48:55 +00:00
msg . Meta = sign
2023-04-05 21:03:29 +00:00
return nil
}
2023-05-09 14:48:55 +00:00
func PrivKeyToTopic ( privKey * ecdsa . PrivateKey ) string {
address := crypto . PubkeyToAddress ( privKey . PublicKey )
return protocol . NewNamedShardingPubsubTopic ( address . String ( ) + "/proto" ) . String ( )
}