feat: js-noise / go-noise interop

This commit is contained in:
Richard Ramos 2022-12-21 14:47:43 -04:00 committed by RichΛrd
parent 77ace0d772
commit 627fbbab6e
32 changed files with 3130 additions and 1527 deletions

View File

@ -125,7 +125,6 @@ require (
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 // indirect
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg // indirect
github.com/waku-org/noise v1.0.2 // indirect
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.10.0 // indirect

View File

@ -1435,7 +1435,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -1481,8 +1481,6 @@ github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 h1:xwY0kW5XZF
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw=
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg h1:2vVIBCtBih2w1K9ll8YnToTDZvbxcgbsClsPlJS/kkg=
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg/go.mod h1:GlyaVeEWNEBxVJrWC6jFTvb4LNb9d9qnjdS6EiWVUvk=
github.com/waku-org/noise v1.0.2 h1:7WmlhpJ0eliBzwzKz6SoTqQznaEU2IuebHF3oCekqqs=
github.com/waku-org/noise v1.0.2/go.mod h1:emThr8WZLeAtKqFW+/nXfHn9VucuXTh8aHap03UXP84=
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow=
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=

View File

@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
logging "github.com/ipfs/go-log/v2"
"github.com/waku-org/go-waku/waku/v2/node"
"github.com/waku-org/go-waku/waku/v2/payload"
"github.com/waku-org/go-waku/waku/v2/protocol"
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
"github.com/waku-org/go-waku/waku/v2/utils"
@ -87,9 +88,9 @@ func write(ctx context.Context, wakuNode *node.WakuNode, msgContent string) {
var version uint32 = 0
var timestamp int64 = utils.GetUnixEpoch(wakuNode.Timesource())
p := new(node.Payload)
p := new(payload.Payload)
p.Data = []byte(wakuNode.ID() + ": " + msgContent)
p.Key = &node.KeyInfo{Kind: node.None}
p.Key = &payload.KeyInfo{Kind: payload.None}
payload, err := p.Encode(version)
if err != nil {
@ -125,7 +126,7 @@ func readLoop(ctx context.Context, wakuNode *node.WakuNode) {
}
for value := range sub.C {
payload, err := node.DecodePayload(value.Message(), &node.KeyInfo{Kind: node.None})
payload, err := payload.DecodePayload(value.Message(), &payload.KeyInfo{Kind: payload.None})
if err != nil {
fmt.Println(err)
return

View File

@ -15,6 +15,7 @@ import (
"github.com/multiformats/go-multiaddr"
"github.com/waku-org/go-waku/waku/v2/dnsdisc"
"github.com/waku-org/go-waku/waku/v2/node"
"github.com/waku-org/go-waku/waku/v2/payload"
"github.com/waku-org/go-waku/waku/v2/protocol"
"github.com/waku-org/go-waku/waku/v2/protocol/filter"
"github.com/waku-org/go-waku/waku/v2/protocol/lightpush"
@ -261,18 +262,18 @@ func (c *Chat) publish(ctx context.Context, message string) error {
var version uint32
var timestamp int64 = utils.GetUnixEpochFrom(c.node.Timesource().Now())
var keyInfo *node.KeyInfo = &node.KeyInfo{}
var keyInfo *payload.KeyInfo = &payload.KeyInfo{}
if c.options.UsePayloadV1 { // Use WakuV1 encryption
keyInfo.Kind = node.Symmetric
keyInfo.Kind = payload.Symmetric
keyInfo.SymKey = generateSymKey(c.options.ContentTopic)
version = 1
} else {
keyInfo.Kind = node.None
keyInfo.Kind = payload.None
version = 0
}
p := new(node.Payload)
p := new(payload.Payload)
p.Data = msgBytes
p.Key = keyInfo
@ -318,15 +319,15 @@ func (c *Chat) publish(ctx context.Context, message string) error {
}
func decodeMessage(useV1Payload bool, contentTopic string, wakumsg *wpb.WakuMessage) (*pb.Chat2Message, error) {
var keyInfo *node.KeyInfo = &node.KeyInfo{}
var keyInfo *payload.KeyInfo = &payload.KeyInfo{}
if useV1Payload { // Use WakuV1 encryption
keyInfo.Kind = node.Symmetric
keyInfo.Kind = payload.Symmetric
keyInfo.SymKey = generateSymKey(contentTopic)
} else {
keyInfo.Kind = node.None
keyInfo.Kind = payload.None
}
payload, err := node.DecodePayload(wakumsg, keyInfo)
payload, err := payload.DecodePayload(wakumsg, keyInfo)
if err != nil {
return nil, err
}

View File

@ -142,7 +142,6 @@ require (
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 // indirect
github.com/waku-org/noise v1.0.2 // indirect
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.opencensus.io v0.23.0 // indirect

View File

@ -1470,7 +1470,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -1518,8 +1518,6 @@ github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 h1:xwY0kW5XZF
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw=
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg h1:2vVIBCtBih2w1K9ll8YnToTDZvbxcgbsClsPlJS/kkg=
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg/go.mod h1:GlyaVeEWNEBxVJrWC6jFTvb4LNb9d9qnjdS6EiWVUvk=
github.com/waku-org/noise v1.0.2 h1:7WmlhpJ0eliBzwzKz6SoTqQznaEU2IuebHF3oCekqqs=
github.com/waku-org/noise v1.0.2/go.mod h1:emThr8WZLeAtKqFW+/nXfHn9VucuXTh8aHap03UXP84=
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow=
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=

View File

@ -125,7 +125,6 @@ require (
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 // indirect
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg // indirect
github.com/waku-org/noise v1.0.2 // indirect
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.10.0 // indirect

View File

@ -1435,7 +1435,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -1481,8 +1481,6 @@ github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 h1:xwY0kW5XZF
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw=
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg h1:2vVIBCtBih2w1K9ll8YnToTDZvbxcgbsClsPlJS/kkg=
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg/go.mod h1:GlyaVeEWNEBxVJrWC6jFTvb4LNb9d9qnjdS6EiWVUvk=
github.com/waku-org/noise v1.0.2 h1:7WmlhpJ0eliBzwzKz6SoTqQznaEU2IuebHF3oCekqqs=
github.com/waku-org/noise v1.0.2/go.mod h1:emThr8WZLeAtKqFW+/nXfHn9VucuXTh8aHap03UXP84=
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow=
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=

View File

@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
logging "github.com/ipfs/go-log/v2"
"github.com/waku-org/go-waku/waku/v2/node"
"github.com/waku-org/go-waku/waku/v2/payload"
"github.com/waku-org/go-waku/waku/v2/protocol"
"github.com/waku-org/go-waku/waku/v2/protocol/filter"
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
@ -145,9 +146,9 @@ func write(ctx context.Context, wakuNode *node.WakuNode, msgContent string) {
var version uint32 = 0
var timestamp int64 = utils.GetUnixEpoch(wakuNode.Timesource())
p := new(node.Payload)
p := new(payload.Payload)
p.Data = []byte(wakuNode.ID() + ": " + msgContent)
p.Key = &node.KeyInfo{Kind: node.None}
p.Key = &payload.KeyInfo{Kind: payload.None}
payload, _ := p.Encode(version)
@ -180,7 +181,7 @@ func readLoop(ctx context.Context, wakuNode *node.WakuNode) {
}
for value := range sub.C {
payload, err := node.DecodePayload(value.Message(), &node.KeyInfo{Kind: node.None})
payload, err := payload.DecodePayload(value.Message(), &payload.KeyInfo{Kind: payload.None})
if err != nil {
fmt.Println(err)
return

6
examples/noise/Makefile Normal file
View File

@ -0,0 +1,6 @@
.PHONY: all build
build:
go build -o build/noise .
all: build

30
examples/noise/README.md Normal file
View File

@ -0,0 +1,30 @@
# Using the `noise` application
## Background
The `noise` application is an example that shows how to do pairing between js-waku and go-waku
## Preparation
```
make
```
Also clone https://github.com/waku-org/js-noise and execute the following commands:
```
git clone https://github.com/waku-org/js-noise
cd js-noise
npm install
npm run build
cd example
npm install
npm start
```
## Basic application usage
To start the `noise` application run the following from the project directory
```
./build/noise
```
The app will display a QR and a link that will open the js-noise example in the browser. The handshake process will start afterwards

165
examples/noise/chat2.pb.go Normal file
View File

@ -0,0 +1,165 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: chat2.proto
package main
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Chat2Message struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Timestamp uint64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
Nick string `protobuf:"bytes,2,opt,name=nick,proto3" json:"nick,omitempty"`
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
}
func (x *Chat2Message) Reset() {
*x = Chat2Message{}
if protoimpl.UnsafeEnabled {
mi := &file_chat2_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Chat2Message) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Chat2Message) ProtoMessage() {}
func (x *Chat2Message) ProtoReflect() protoreflect.Message {
mi := &file_chat2_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Chat2Message.ProtoReflect.Descriptor instead.
func (*Chat2Message) Descriptor() ([]byte, []int) {
return file_chat2_proto_rawDescGZIP(), []int{0}
}
func (x *Chat2Message) GetTimestamp() uint64 {
if x != nil {
return x.Timestamp
}
return 0
}
func (x *Chat2Message) GetNick() string {
if x != nil {
return x.Nick
}
return ""
}
func (x *Chat2Message) GetPayload() []byte {
if x != nil {
return x.Payload
}
return nil
}
var File_chat2_proto protoreflect.FileDescriptor
var file_chat2_proto_rawDesc = []byte{
0x0a, 0x0b, 0x63, 0x68, 0x61, 0x74, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70,
0x62, 0x22, 0x5a, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x74, 0x32, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01,
0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x69, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x69, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_chat2_proto_rawDescOnce sync.Once
file_chat2_proto_rawDescData = file_chat2_proto_rawDesc
)
func file_chat2_proto_rawDescGZIP() []byte {
file_chat2_proto_rawDescOnce.Do(func() {
file_chat2_proto_rawDescData = protoimpl.X.CompressGZIP(file_chat2_proto_rawDescData)
})
return file_chat2_proto_rawDescData
}
var file_chat2_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_chat2_proto_goTypes = []interface{}{
(*Chat2Message)(nil), // 0: pb.Chat2Message
}
var file_chat2_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_chat2_proto_init() }
func file_chat2_proto_init() {
if File_chat2_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_chat2_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Chat2Message); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_chat2_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_chat2_proto_goTypes,
DependencyIndexes: file_chat2_proto_depIdxs,
MessageInfos: file_chat2_proto_msgTypes,
}.Build()
File_chat2_proto = out.File
file_chat2_proto_rawDesc = nil
file_chat2_proto_goTypes = nil
file_chat2_proto_depIdxs = nil
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
package main;
message Chat2Message {
uint64 timestamp = 1;
string nick = 2;
bytes payload = 3;
}

147
examples/noise/go.mod Normal file
View File

@ -0,0 +1,147 @@
module noise
go 1.18
replace github.com/waku-org/go-waku => ../..
replace github.com/ethereum/go-ethereum v1.10.25 => github.com/status-im/go-ethereum v1.10.4-status.2
require (
github.com/ipfs/go-log/v2 v2.5.1
github.com/multiformats/go-multiaddr v0.7.0
github.com/waku-org/go-noise v0.0.4
github.com/waku-org/go-waku v0.2.3-0.20221109195301-b2a5a68d28ba
go.uber.org/zap v1.23.0
)
require (
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
github.com/beevik/ntp v0.3.0 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.20.1-beta // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.1 // indirect
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/cgroups v1.0.4 // indirect
github.com/coreos/go-systemd/v22 v22.4.0 // indirect
github.com/cruxic/go-hmac-drbg v0.0.0-20170206035330-84c46983886d // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/edsrzf/mmap-go v1.0.0 // indirect
github.com/elastic/gosigar v0.14.2 // indirect
github.com/ethereum/go-ethereum v1.10.25 // indirect
github.com/flynn/noise v1.0.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-migrate/migrate/v4 v4.15.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/huin/goupnp v1.0.3 // indirect
github.com/ipfs/go-cid v0.3.2 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/karalabe/usb v0.0.2 // indirect
github.com/klauspost/compress v1.15.10 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
github.com/koron/go-ssdp v0.0.3 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/libp2p/go-cidranger v1.1.0 // indirect
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
github.com/libp2p/go-libp2p v0.23.2 // indirect
github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect
github.com/libp2p/go-libp2p-pubsub v0.8.1 // indirect
github.com/libp2p/go-mplex v0.7.0 // indirect
github.com/libp2p/go-msgio v0.2.0 // indirect
github.com/libp2p/go-nat v0.1.0 // indirect
github.com/libp2p/go-netroute v0.2.0 // indirect
github.com/libp2p/go-openssl v0.1.0 // indirect
github.com/libp2p/go-reuseport v0.2.0 // indirect
github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
github.com/lucas-clemente/quic-go v0.29.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-pointer v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mattn/go-sqlite3 v1.14.15 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
github.com/multiformats/go-multibase v0.1.1 // indirect
github.com/multiformats/go-multicodec v0.6.0 // indirect
github.com/multiformats/go-multihash v0.2.1 // indirect
github.com/multiformats/go-multistream v0.3.3 // indirect
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.13.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/prometheus/tsdb v0.7.1 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/rjeczalik/notify v0.9.1 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 // indirect
github.com/status-im/status-go/extkeys v1.1.2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 // indirect
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg // indirect
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
golang.org/x/tools v0.1.12 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
)

2369
examples/noise/go.sum Normal file

File diff suppressed because it is too large Load Diff

227
examples/noise/main.go Normal file
View File

@ -0,0 +1,227 @@
package main
import (
"context"
"encoding/hex"
"fmt"
"net"
"net/url"
"os"
"os/signal"
"sync"
"syscall"
"time"
logging "github.com/ipfs/go-log/v2"
"github.com/multiformats/go-multiaddr"
n "github.com/waku-org/go-noise"
"github.com/waku-org/go-waku/waku/v2/dnsdisc"
"github.com/waku-org/go-waku/waku/v2/node"
"github.com/waku-org/go-waku/waku/v2/protocol/noise"
"github.com/waku-org/go-waku/waku/v2/timesource"
"github.com/waku-org/go-waku/waku/v2/utils"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
)
var log = logging.Logger("noise")
func main() {
// Removing noisy logs
lvl, err := logging.LevelFromString("error")
if err != nil {
panic(err)
}
logging.SetAllLoggers(lvl)
hostAddr, _ := net.ResolveTCPAddr("tcp", fmt.Sprint("0.0.0.0:0"))
wakuNode, err := node.New(context.Background(),
node.WithHostAddress(hostAddr),
node.WithNTP(),
node.WithWakuRelay(),
)
if err != nil {
log.Error(err)
return
}
if err := wakuNode.Start(); err != nil {
log.Error(err)
return
}
discoverFleetNodes(wakuNode)
myStaticKey, _ := n.DH25519.GenerateKeypair()
myEphemeralKey, _ := n.DH25519.GenerateKeypair()
relayMessenger, err := noise.NewWakuRelayMessenger(context.Background(), wakuNode.Relay(), nil, timesource.NewDefaultClock())
if err != nil {
log.Error(err)
return
}
pairingObj, err := noise.NewPairing(myStaticKey, myEphemeralKey, noise.WithDefaultResponderParameters(), relayMessenger, utils.Logger())
if err != nil {
log.Error(err)
return
}
qrString, qrMessageNameTag := pairingObj.PairingInfo()
qrURL := url.QueryEscape(hex.EncodeToString(qrMessageNameTag[:]) + ":" + qrString)
wg := sync.WaitGroup{}
// Execute in separate go routine
wg.Add(1)
go func() {
defer wg.Done()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
err := pairingObj.Execute(ctx)
if err != nil {
log.Error(err)
return
}
}()
// Confirmation is done by manually
wg.Add(1)
go func() {
defer wg.Done()
authCode := <-pairingObj.AuthCode()
fmt.Println("=============================================")
fmt.Println("TODO: ask to confirm pairing. Automaticaly confirming authcode", authCode)
fmt.Println("=============================================")
err := pairingObj.ConfirmAuthCode(true)
if err != nil {
log.Error(err)
return
}
}()
fmt.Println("=============================================================================")
fmt.Printf("Browse http://localhost:8080/?%s\n", qrURL)
fmt.Println("=============================================================================")
wg.Wait()
// Securely transferring messages
if pairingObj.HandshakeComplete() {
go writeLoop(context.Background(), wakuNode, pairingObj)
go readLoop(context.Background(), wakuNode, pairingObj)
}
// Wait for a SIGINT or SIGTERM signal
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
<-ch
fmt.Println("\n\n\nReceived signal, shutting down...")
// shut the node down
wakuNode.Stop()
}
func discoverFleetNodes(wakuNode *node.WakuNode) error {
log.Info("Connecting to test fleet...")
dnsDiscoveryUrl := "enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@test.waku.nodes.status.im"
nodes, err := dnsdisc.RetrieveNodes(context.Background(), dnsDiscoveryUrl)
if err != nil {
return err
}
var nodeList []multiaddr.Multiaddr
for _, n := range nodes {
nodeList = append(nodeList, n.Addresses...)
}
log.Info(fmt.Sprintf("Discovered and connecting to %v ", nodeList))
wg := sync.WaitGroup{}
wg.Add(len(nodeList))
for _, n := range nodeList {
go func(addr multiaddr.Multiaddr) {
defer wg.Done()
peerID, err := addr.ValueForProtocol(multiaddr.P_P2P)
if err != nil {
log.Error("error obtaining peerID", zap.Error(err))
return
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
defer cancel()
err = wakuNode.DialPeerWithMultiAddress(ctx, addr)
if err != nil {
log.Error("could not connect", zap.String("peerID", peerID), zap.Error(err))
} else {
log.Info("Connected", zap.String("peerID", peerID))
}
}(n)
}
wg.Wait()
return nil
}
func writeLoop(ctx context.Context, wakuNode *node.WakuNode, pairingObj *noise.Pairing) {
for {
time.Sleep(4 * time.Second)
chatMessage := &Chat2Message{
Timestamp: uint64(wakuNode.Timesource().Now().Unix()),
Nick: "go-waku",
Payload: []byte("Hello World!"),
}
chatMessageBytes, err := proto.Marshal(chatMessage)
if err != nil {
log.Error("Error encoding message: ", zap.Error(err))
continue
}
msg, err := pairingObj.Encrypt(chatMessageBytes)
if err != nil {
log.Error("error encrypting message", zap.Error(err))
continue
}
msg.Timestamp = wakuNode.Timesource().Now().UnixNano()
_, err = wakuNode.Relay().Publish(ctx, msg)
if err != nil {
log.Error("Error sending a message", zap.Error(err))
}
}
}
func readLoop(ctx context.Context, wakuNode *node.WakuNode, pairingObj *noise.Pairing) {
sub, err := wakuNode.Relay().Subscribe(ctx)
if err != nil {
log.Error("Could not subscribe: ", err)
return
}
for value := range sub.C {
if value.Message().ContentTopic != pairingObj.ContentTopic {
continue
}
msgBytes, err := pairingObj.Decrypt(value.Message())
if err != nil {
log.Debug("Error decoding a message", zap.Error(err))
continue
}
msg := &Chat2Message{}
if err := proto.Unmarshal(msgBytes, msg); err != nil {
log.Error("Error decoding a message", zap.Error(err))
continue
}
fmt.Println("Received msg from ", msg.Nick, " - "+string(msg.Payload))
}
}

7
go.mod
View File

@ -21,7 +21,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.15
github.com/multiformats/go-multiaddr v0.7.0
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/stretchr/testify v1.8.0
github.com/stretchr/testify v1.8.1
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/urfave/cli/v2 v2.20.2
go.opencensus.io v0.23.0
@ -33,10 +33,11 @@ require (
github.com/gorilla/mux v1.8.0
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg
github.com/waku-org/noise v1.0.3
golang.org/x/text v0.4.0
)
require github.com/waku-org/go-noise v0.0.4
require (
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
@ -149,7 +150,7 @@ require (
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 // indirect

8
go.sum
View File

@ -1462,6 +1462,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -1472,8 +1473,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -1520,10 +1522,10 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 h1:xwY0kW5XZFimdqfZb9cZwT1S3VJP9j3AE6bdNd9boXM=
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw=
github.com/waku-org/go-noise v0.0.4 h1:ZfQDcCw8pazm89EBl5SXY7GGAnzDQb9AHFXlw3Ktbvk=
github.com/waku-org/go-noise v0.0.4/go.mod h1:+PWRfs2eSOVwKrPcQlfhwDngSh3faL/1QoxvoqggEKc=
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg h1:2vVIBCtBih2w1K9ll8YnToTDZvbxcgbsClsPlJS/kkg=
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg/go.mod h1:GlyaVeEWNEBxVJrWC6jFTvb4LNb9d9qnjdS6EiWVUvk=
github.com/waku-org/noise v1.0.3 h1:BIecnRG0J0JlZmqcZTHphQ8yUeqqwkIaUAVk2JNK9VQ=
github.com/waku-org/noise v1.0.3/go.mod h1:emThr8WZLeAtKqFW+/nXfHn9VucuXTh8aHap03UXP84=
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow=
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=

View File

@ -1,15 +0,0 @@
package noise
import (
"crypto/ed25519"
"crypto/sha256"
)
// CommitPublicKey commits a public key pk for randomness r as H(pk || s)
func CommitPublicKey(publicKey ed25519.PublicKey, r []byte) []byte {
input := []byte{}
input = append(input, []byte(publicKey)...)
input = append(input, r...)
res := sha256.Sum256(input)
return res[:]
}

View File

@ -1,408 +0,0 @@
package noise
import (
"bytes"
"errors"
"fmt"
"hash"
"io"
"math/big"
n "github.com/waku-org/noise"
"golang.org/x/crypto/hkdf"
)
// WakuNoiseProtocolID indicates the protocol ID defined according to https://rfc.vac.dev/spec/35/#specification
type WakuNoiseProtocolID = byte
var (
None = WakuNoiseProtocolID(0)
Noise_K1K1_25519_ChaChaPoly_SHA256 = WakuNoiseProtocolID(10)
Noise_XK1_25519_ChaChaPoly_SHA256 = WakuNoiseProtocolID(11)
Noise_XX_25519_ChaChaPoly_SHA256 = WakuNoiseProtocolID(12)
Noise_XXpsk0_25519_ChaChaPoly_SHA256 = WakuNoiseProtocolID(13)
Noise_WakuPairing_25519_ChaChaPoly_SHA256 = WakuNoiseProtocolID(14)
ChaChaPoly = WakuNoiseProtocolID(30)
)
const NoisePaddingBlockSize = 248
var ErrorHandshakeComplete = errors.New("handshake complete")
// All protocols share same cipher suite
var cipherSuite = n.NewCipherSuite(n.DH25519, n.CipherChaChaPoly, n.HashSHA256)
func newHandshakeState(pattern n.HandshakePattern, initiator bool, staticKeypair n.DHKey, ephemeralKeyPair n.DHKey, prologue []byte, presharedKey []byte, peerStatic []byte, peerEphemeral []byte) (hs *n.HandshakeState, err error) {
defer func() {
if rerr := recover(); rerr != nil {
err = fmt.Errorf("panic in Noise handshake: %s", rerr)
}
}()
cfg := n.Config{
CipherSuite: cipherSuite,
Pattern: pattern,
Initiator: initiator,
StaticKeypair: staticKeypair,
EphemeralKeypair: ephemeralKeyPair,
Prologue: prologue,
PresharedKey: presharedKey,
PeerStatic: peerStatic,
PeerEphemeral: peerEphemeral,
}
return n.NewHandshakeState(cfg)
}
type Handshake struct {
protocolID WakuNoiseProtocolID
pattern n.HandshakePattern
state *n.HandshakeState
hsBuff []byte
enc *n.CipherState
dec *n.CipherState
nametagsInbound *MessageNametagBuffer
nametagsOutbound *MessageNametagBuffer
initiator bool
shouldWrite bool
}
// HandshakeStepResult stores the intermediate result of processing messages patterns
type HandshakeStepResult struct {
Payload2 PayloadV2
TransportMessage []byte
}
func getHandshakePattern(protocol WakuNoiseProtocolID) (n.HandshakePattern, error) {
switch protocol {
case Noise_K1K1_25519_ChaChaPoly_SHA256:
return HandshakeK1K1, nil
case Noise_XK1_25519_ChaChaPoly_SHA256:
return HandshakeXK1, nil
case Noise_XX_25519_ChaChaPoly_SHA256:
return HandshakeXX, nil
case Noise_XXpsk0_25519_ChaChaPoly_SHA256:
return HandshakeXXpsk0, nil
case Noise_WakuPairing_25519_ChaChaPoly_SHA256:
return HandshakeWakuPairing, nil
default:
return n.HandshakePattern{}, errors.New("unsupported handshake pattern")
}
}
// NewHandshake creates a new handshake using aa WakuNoiseProtocolID that is maped to a handshake pattern.
func NewHandshake(protocolID WakuNoiseProtocolID, initiator bool, staticKeypair n.DHKey, ephemeralKeyPair n.DHKey, prologue []byte, presharedKey []byte, peerStatic []byte, peerEphemeral []byte) (*Handshake, error) {
hsPattern, err := getHandshakePattern(protocolID)
if err != nil {
return nil, err
}
hsState, err := newHandshakeState(hsPattern, initiator, staticKeypair, ephemeralKeyPair, prologue, presharedKey, peerStatic, peerEphemeral)
if err != nil {
return nil, err
}
return &Handshake{
protocolID: protocolID,
pattern: hsPattern,
initiator: initiator,
shouldWrite: initiator,
state: hsState,
}, nil
}
func (hs *Handshake) Hash() hash.Hash {
return hs.state.Hash()
}
func (hs *Handshake) H() []byte {
return hs.state.H()
}
func (hs *Handshake) RS() []byte {
return hs.state.RS()
}
// Step advances a step in the handshake. Each user in a handshake alternates writing and reading of handshake messages.
// If the user is writing the handshake message, the transport message (if not empty) has to be passed to transportMessage and readPayloadV2 can be left to its default value
// It the user is reading the handshake message, the read payload v2 has to be passed to readPayloadV2 and the transportMessage can be left to its default values.
// TODO: this might be refactored into a separate `sendHandshakeMessage` and `receiveHandshakeMessage`.
// The messageNameTag is an optional value and can be used to identify missing messages
func (hs *Handshake) Step(readPayloadV2 *PayloadV2, transportMessage []byte, messageNametag *MessageNametag) (*HandshakeStepResult, error) {
if hs.enc != nil || hs.dec != nil {
return nil, ErrorHandshakeComplete
}
var cs1 *n.CipherState
var cs2 *n.CipherState
var err error
var msg []byte
var noisePubKeys [][]byte
result := HandshakeStepResult{}
if hs.shouldWrite {
// We initialize a payload v2 and we set proper protocol ID (if supported)
result.Payload2.ProtocolId = hs.protocolID
payload, err := PKCS7_Pad(transportMessage, NoisePaddingBlockSize)
if err != nil {
return nil, err
}
var mtag MessageNametag
if messageNametag != nil {
mtag = *messageNametag
}
msg, noisePubKeys, cs1, cs2, err = hs.state.WriteMessageAndGetPK(hs.hsBuff, [][]byte{}, payload, mtag[:])
if err != nil {
return nil, err
}
hs.shouldWrite = false
if messageNametag != nil {
result.Payload2.MessageNametag = *messageNametag
}
result.Payload2.TransportMessage = msg
for _, npk := range noisePubKeys {
result.Payload2.HandshakeMessage = append(result.Payload2.HandshakeMessage, byteToNoisePublicKey(npk))
}
} else {
if readPayloadV2 == nil {
return nil, errors.New("readPayloadV2 is required")
}
var mtag MessageNametag
if messageNametag != nil {
mtag = *messageNametag
if !bytes.Equal(readPayloadV2.MessageNametag[:], mtag[:]) {
return nil, ErrNametagNotExpected
}
}
readTMessage := readPayloadV2.TransportMessage
// We retrieve and store the (decrypted) received transport message by passing the messageNametag as extra additional data
// Since we only read, nothing meanigful (i.e. public keys) is returned. (hsBuffer is not affected)
msg, cs1, cs2, err = hs.state.ReadMessage(nil, readTMessage, mtag[:]...)
if err != nil {
return nil, err
}
hs.shouldWrite = true
// We retrieve, and store the (unpadded decrypted) received transport message
payload, err := PKCS7_Unpad(msg, NoisePaddingBlockSize)
if err != nil {
return nil, err
}
result.TransportMessage = payload
}
if cs1 != nil && cs2 != nil {
err = hs.setCipherStates(cs1, cs2)
if err != nil {
return nil, err
}
}
return &result, nil
}
// HandshakeComplete indicates whether the handshake process is complete or not
func (hs *Handshake) HandshakeComplete() bool {
return hs.enc != nil && hs.dec != nil
}
// This is called when the final handshake message is processed
func (hs *Handshake) setCipherStates(cs1, cs2 *n.CipherState) error {
// Optional: We derive a secret for the nametag derivation
nms1, nms2, err := hs.messageNametagSecrets()
if err != nil {
return err
}
if hs.initiator {
hs.enc = cs1
hs.dec = cs2
// and nametags secrets
hs.nametagsInbound = NewMessageNametagBuffer(nms1)
hs.nametagsOutbound = NewMessageNametagBuffer(nms2)
} else {
hs.enc = cs2
hs.dec = cs1
// and nametags secrets
hs.nametagsInbound = NewMessageNametagBuffer(nms2)
hs.nametagsOutbound = NewMessageNametagBuffer(nms1)
}
// We initialize the message nametags inbound/outbound buffers
hs.nametagsInbound.Init()
hs.nametagsOutbound.Init()
return nil
}
// Encrypt calls the cipher's encryption. It encrypts the provided plaintext and returns a PayloadV2
func (hs *Handshake) Encrypt(plaintext []byte, outboundMessageNametagBuffer ...*MessageNametagBuffer) (*PayloadV2, error) {
if hs.enc == nil {
return nil, errors.New("cannot encrypt, handshake incomplete")
}
if len(plaintext) == 0 {
return nil, errors.New("tried to encrypt empty plaintext")
}
paddedTransportMessage, err := PKCS7_Pad(plaintext, NoisePaddingBlockSize)
if err != nil {
return nil, err
}
// We set the message nametag using the input buffer
var messageNametag MessageNametag
if len(outboundMessageNametagBuffer) != 0 {
messageNametag = outboundMessageNametagBuffer[0].Pop()
} else {
messageNametag = hs.nametagsOutbound.Pop()
}
cyphertext, err := hs.enc.Encrypt(nil, messageNametag[:], paddedTransportMessage)
if err != nil {
return nil, err
}
// According to 35/WAKU2-NOISE RFC, no Handshake protocol information is sent when exchanging messages
// This correspond to setting protocol-id to 0 (None)
return &PayloadV2{
ProtocolId: None,
TransportMessage: cyphertext,
MessageNametag: messageNametag,
}, nil
}
// Decrypt calls the cipher's decryption. It decrypts the provided payload and returns the message in plaintext
func (hs *Handshake) Decrypt(payload *PayloadV2, inboundMessageNametagBuffer ...*MessageNametagBuffer) ([]byte, error) {
if hs.dec == nil {
return nil, errors.New("cannot decrypt, handshake incomplete")
}
if payload == nil {
return nil, errors.New("no payload to decrypt")
}
// If the message nametag does not correspond to the nametag expected in the inbound message nametag buffer
// an error is raised (to be handled externally, i.e. re-request lost messages, discard, etc.)
if len(inboundMessageNametagBuffer) != 0 {
err := inboundMessageNametagBuffer[0].CheckNametag(payload.MessageNametag)
if err != nil {
return nil, err
}
} else {
err := hs.nametagsInbound.CheckNametag(payload.MessageNametag)
if err != nil {
return nil, err
}
}
if len(payload.TransportMessage) == 0 {
return nil, errors.New("tried to decrypt empty ciphertext")
}
// Decryption is done with messageNametag as associated data
paddedMessage, err := hs.dec.Decrypt(nil, payload.MessageNametag[:], payload.TransportMessage)
if err != nil {
return nil, err
}
// The message successfully decrypted, we can delete the first element of the inbound Message Nametag Buffer
hs.nametagsInbound.Delete(1)
return PKCS7_Unpad(paddedMessage, NoisePaddingBlockSize)
}
func getHKDF(h func() hash.Hash, ck []byte, ikm []byte, numBytes int) ([]byte, error) {
hkdf := hkdf.New(h, ikm, ck, nil)
result := make([]byte, numBytes)
if _, err := io.ReadFull(hkdf, result); err != nil {
return nil, err
}
return result, nil
}
// Generates an 8 decimal digits authorization code using HKDF and the handshake state
func (hs *Handshake) Authcode() (string, error) {
output0, err := getHKDF(hs.Hash, hs.H(), nil, 8)
if err != nil {
return "", err
}
bn := new(big.Int)
bn.SetBytes(output0)
code := new(big.Int)
code.Mod(bn, big.NewInt(100_000_000))
return fmt.Sprintf("'%08s'", code.String()), nil
}
func (hs *Handshake) messageNametagSecrets() (nms1 []byte, nms2 []byte, err error) {
output, err := getHKDF(hs.Hash, hs.H(), nil, 64)
if err != nil {
return nil, nil, err
}
nms1 = output[0:32]
nms2 = output[32:]
return
}
// Uses the cryptographic information stored in the input handshake state to generate a random message nametag
// In current implementation the messageNametag = HKDF(handshake hash value), but other derivation mechanisms can be implemented
func (hs *Handshake) ToMessageNametag() (MessageNametag, error) {
output, err := getHKDF(hs.Hash, hs.H(), nil, 32)
if err != nil {
return [16]byte{}, err
}
return BytesToMessageNametag(output), nil
}
// NewHandshake_XX_25519_ChaChaPoly_SHA256 creates a handshake where the initiator and receiver are not aware of each other static keys
func NewHandshake_XX_25519_ChaChaPoly_SHA256(staticKeypair n.DHKey, initiator bool, prologue []byte) (*Handshake, error) {
return NewHandshake(Noise_XX_25519_ChaChaPoly_SHA256, initiator, staticKeypair, n.DHKey{}, prologue, nil, nil, nil)
}
// NewHandshake_XXpsk0_25519_ChaChaPoly_SHA256 creates a handshake where the initiator and receiver are not aware of each other static keys
// and use a preshared secret to strengthen their mutual authentication
func NewHandshake_XXpsk0_25519_ChaChaPoly_SHA256(staticKeypair n.DHKey, initiator bool, presharedKey []byte, prologue []byte) (*Handshake, error) {
return NewHandshake(Noise_XXpsk0_25519_ChaChaPoly_SHA256, initiator, staticKeypair, n.DHKey{}, prologue, presharedKey, nil, nil)
}
// NewHandshake_K1K1_25519_ChaChaPoly_SHA256 creates a handshake where both initiator and recever know each other handshake. Only ephemeral keys
// are exchanged. This handshake is useful in case the initiator needs to instantiate a new separate encrypted communication
// channel with the receiver
func NewHandshake_K1K1_25519_ChaChaPoly_SHA256(staticKeypair n.DHKey, initiator bool, peerStaticKey []byte, prologue []byte) (*Handshake, error) {
return NewHandshake(Noise_K1K1_25519_ChaChaPoly_SHA256, initiator, staticKeypair, n.DHKey{}, prologue, nil, peerStaticKey, nil)
}
// NewHandshake_XK1_25519_ChaChaPoly_SHA256 creates a handshake where the initiator knows the receiver public static key. Within this handshake,
// the initiator and receiver reciprocally authenticate their static keys using ephemeral keys. We note that while the receiver's
// static key is assumed to be known to Alice (and hence is not transmitted), The initiator static key is sent to the
// receiver encrypted with a key derived from both parties ephemeral keys and the receiver's static key.
func NewHandshake_XK1_25519_ChaChaPoly_SHA256(staticKeypair n.DHKey, initiator bool, peerStaticKey []byte, prologue []byte) (*Handshake, error) {
if !initiator && len(peerStaticKey) != 0 {
return nil, errors.New("recipient shouldnt know initiator key")
}
return NewHandshake(Noise_XK1_25519_ChaChaPoly_SHA256, initiator, staticKeypair, n.DHKey{}, prologue, nil, peerStaticKey, nil)
}
// NewHandshake_WakuPairing_25519_ChaChaPoly_SHA256
func NewHandshake_WakuPairing_25519_ChaChaPoly_SHA256(staticKeypair n.DHKey, ephemeralKeyPair n.DHKey, initiator bool, prologue []byte, presharedKey []byte) (*Handshake, error) {
peerEphemeral := presharedKey[0:32]
return NewHandshake(Noise_WakuPairing_25519_ChaChaPoly_SHA256, initiator, staticKeypair, ephemeralKeyPair, prologue, presharedKey, nil, peerEphemeral)
}

View File

@ -1,137 +0,0 @@
package noise
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"errors"
)
type MessageNametag [MessageNametagLength]byte
const MessageNametagLength = 16
const MessageNametagBufferSize = 50
var (
ErrNametagNotFound = errors.New("message nametag not found in buffer")
ErrNametagNotExpected = errors.New("message nametag is present in buffer but is not the next expected nametag. One or more messages were probably lost")
)
// Converts a sequence or array (arbitrary size) to a MessageNametag
func BytesToMessageNametag(input []byte) MessageNametag {
var result MessageNametag
copy(result[:], input)
return result
}
func (t MessageNametag) String() string {
return hex.EncodeToString(t[:])
}
type MessageNametagBuffer struct {
buffer []MessageNametag
counter uint64
secret []byte
}
func NewMessageNametagBuffer(secret []byte) *MessageNametagBuffer {
return &MessageNametagBuffer{
secret: secret,
}
}
// Initializes the empty Message nametag buffer. The n-th nametag is equal to HKDF( secret || n )
func (m *MessageNametagBuffer) Init() {
// We default the counter and buffer fields
m.counter = 0
m.buffer = make([]MessageNametag, MessageNametagBufferSize)
if len(m.secret) != 0 {
for i := range m.buffer {
counterBytesLE := make([]byte, 8)
binary.LittleEndian.PutUint64(counterBytesLE, m.counter)
toHash := []byte{}
toHash = append(toHash, m.secret...)
toHash = append(toHash, counterBytesLE...)
d := sha256.Sum256(toHash)
m.buffer[i] = BytesToMessageNametag(d[:])
m.counter++
}
}
}
func (m *MessageNametagBuffer) Pop() MessageNametag {
// Note that if the input MessageNametagBuffer is set to default, an all 0 messageNametag is returned
if len(m.buffer) == 0 {
var m MessageNametag
return m
} else {
messageNametag := m.buffer[0]
m.Delete(1)
return messageNametag
}
}
// Checks if the input messageNametag is contained in the input MessageNametagBuffer
func (m *MessageNametagBuffer) CheckNametag(messageNametag MessageNametag) error {
if len(m.buffer) != MessageNametagBufferSize {
return nil
}
index := -1
for i, x := range m.buffer {
if bytes.Equal(x[:], messageNametag[:]) {
index = i
break
}
}
if index == -1 {
return ErrNametagNotFound
} else if index > 0 {
return ErrNametagNotExpected
}
// index is 0, hence the read message tag is the next expected one
return nil
}
func rotateLeft(elems []MessageNametag, k int) []MessageNametag {
if k < 0 || len(elems) == 0 {
return elems
}
r := len(elems) - k%len(elems)
result := elems[r:]
result = append(result, elems[:r]...)
return result
}
// Deletes the first n elements in buffer and appends n new ones
func (m *MessageNametagBuffer) Delete(n int) {
if n <= 0 {
return
}
// We ensure n is at most MessageNametagBufferSize (the buffer will be fully replaced)
if n > MessageNametagBufferSize {
n = MessageNametagBufferSize
}
// We update the last n values in the array if a secret is set
// Note that if the input MessageNametagBuffer is set to default, nothing is done here
if len(m.secret) != 0 {
m.buffer = rotateLeft(m.buffer, n)
for i := 0; i < n; i++ {
counterBytesLE := make([]byte, 8)
binary.LittleEndian.PutUint64(counterBytesLE, m.counter)
toHash := []byte{}
toHash = append(toHash, m.secret...)
toHash = append(toHash, counterBytesLE...)
d := sha256.Sum256(toHash)
m.buffer[len(m.buffer)-n+i] = BytesToMessageNametag(d[:])
m.counter++
}
}
}

View File

@ -1,205 +0,0 @@
package noise
import (
"crypto/ed25519"
"crypto/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/waku-org/noise"
)
func generateRandomBytes(t *testing.T, n int) []byte {
b := make([]byte, n)
_, err := rand.Read(b)
require.NoError(t, err)
return b
}
func TestSerialization(t *testing.T) {
handshakeMessages := make([]*NoisePublicKey, 2)
pk1, _, _ := ed25519.GenerateKey(rand.Reader)
pk2, _, _ := ed25519.GenerateKey(rand.Reader)
handshakeMessages[0] = Ed25519PubKeyToNoisePublicKey(pk1)
handshakeMessages[1] = Ed25519PubKeyToNoisePublicKey(pk2)
p1 := &PayloadV2{
ProtocolId: Noise_K1K1_25519_ChaChaPoly_SHA256,
HandshakeMessage: handshakeMessages,
TransportMessage: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
}
serializedPayload, err := p1.Serialize()
require.NoError(t, err)
deserializedPayload, err := DeserializePayloadV2(serializedPayload)
require.NoError(t, err)
require.Equal(t, p1, deserializedPayload)
}
func handshakeTest(t *testing.T, hsAlice *Handshake, hsBob *Handshake) {
// ###############
// # 1st step
// ###############
// By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
// and the (encrypted) transport message
sentTransportMessage := generateRandomBytes(t, 32)
aliceStep, err := hsAlice.Step(nil, sentTransportMessage, nil)
require.NoError(t, err)
// Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep, err := hsBob.Step(&aliceStep.Payload2, nil, nil)
require.NoError(t, err)
// check:
require.Equal(t, sentTransportMessage, bobStep.TransportMessage)
// ###############
// # 2nd step
// ###############
// At this step, Bob writes and returns a payload
sentTransportMessage = generateRandomBytes(t, 32)
bobStep, err = hsBob.Step(nil, sentTransportMessage, nil)
require.NoError(t, err)
// While Alice reads and returns the (decrypted) transport message
aliceStep, err = hsAlice.Step(&bobStep.Payload2, nil, nil)
require.NoError(t, err)
// check:
require.Equal(t, sentTransportMessage, aliceStep.TransportMessage)
// ###############
// # 3rd step
// ###############
// Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
sentTransportMessage = generateRandomBytes(t, 32)
aliceStep, err = hsAlice.Step(nil, sentTransportMessage, nil)
require.NoError(t, err)
// Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep, err = hsBob.Step(&aliceStep.Payload2, nil, nil)
require.NoError(t, err)
// check:
require.Equal(t, sentTransportMessage, bobStep.TransportMessage)
// Note that for this handshake pattern, no more message patterns are left for processing
// We test that extra calls to stepHandshake do not affect parties' handshake states
require.True(t, hsAlice.HandshakeComplete())
require.True(t, hsBob.HandshakeComplete())
_, err = hsAlice.Step(nil, generateRandomBytes(t, 32), nil)
require.ErrorIs(t, err, ErrorHandshakeComplete)
_, err = hsBob.Step(nil, generateRandomBytes(t, 32), nil)
require.ErrorIs(t, err, ErrorHandshakeComplete)
// #########################
// After Handshake
// #########################
// We test read/write of random messages exchanged between Alice and Bob
defaultMessageNametagBuffer := NewMessageNametagBuffer(nil)
for i := 0; i < 10; i++ {
// Alice writes to Bob
message := generateRandomBytes(t, 32)
encryptedPayload, err := hsAlice.Encrypt(message, defaultMessageNametagBuffer)
require.NoError(t, err)
plaintext, err := hsBob.Decrypt(encryptedPayload, defaultMessageNametagBuffer)
require.NoError(t, err)
require.Equal(t, message, plaintext)
// Bob writes to Alice
message = generateRandomBytes(t, 32)
encryptedPayload, err = hsBob.Encrypt(message, defaultMessageNametagBuffer)
require.NoError(t, err)
plaintext, err = hsAlice.Decrypt(encryptedPayload, defaultMessageNametagBuffer)
require.NoError(t, err)
require.Equal(t, message, plaintext)
}
}
func TestNoiseXXHandshakeRoundtrip(t *testing.T) {
aliceKP, _ := noise.DH25519.GenerateKeypair(rand.Reader)
bobKP, _ := noise.DH25519.GenerateKeypair(rand.Reader)
hsAlice, err := NewHandshake_XX_25519_ChaChaPoly_SHA256(aliceKP, true, nil)
require.NoError(t, err)
hsBob, err := NewHandshake_XX_25519_ChaChaPoly_SHA256(bobKP, false, nil)
require.NoError(t, err)
handshakeTest(t, hsAlice, hsBob)
}
func TestNoiseXXpsk0HandshakeRoundtrip(t *testing.T) {
aliceKP, _ := noise.DH25519.GenerateKeypair(rand.Reader)
bobKP, _ := noise.DH25519.GenerateKeypair(rand.Reader)
// We generate a random psk
psk := generateRandomBytes(t, 32)
hsAlice, err := NewHandshake_XXpsk0_25519_ChaChaPoly_SHA256(aliceKP, true, psk, nil)
require.NoError(t, err)
hsBob, err := NewHandshake_XXpsk0_25519_ChaChaPoly_SHA256(bobKP, false, psk, nil)
require.NoError(t, err)
handshakeTest(t, hsAlice, hsBob)
}
func TestNoiseK1K1HandshakeRoundtrip(t *testing.T) {
aliceKP, _ := noise.DH25519.GenerateKeypair(rand.Reader)
bobKP, _ := noise.DH25519.GenerateKeypair(rand.Reader)
hsAlice, err := NewHandshake_K1K1_25519_ChaChaPoly_SHA256(aliceKP, true, bobKP.Public, nil)
require.NoError(t, err)
hsBob, err := NewHandshake_K1K1_25519_ChaChaPoly_SHA256(bobKP, false, aliceKP.Public, nil)
require.NoError(t, err)
handshakeTest(t, hsAlice, hsBob)
}
func TestNoiseXK1HandshakeRoundtrip(t *testing.T) {
aliceKP, _ := noise.DH25519.GenerateKeypair(rand.Reader)
bobKP, _ := noise.DH25519.GenerateKeypair(rand.Reader)
hsAlice, err := NewHandshake_XK1_25519_ChaChaPoly_SHA256(aliceKP, true, bobKP.Public, nil)
require.NoError(t, err)
hsBob, err := NewHandshake_XK1_25519_ChaChaPoly_SHA256(bobKP, false, nil, nil)
require.NoError(t, err)
handshakeTest(t, hsAlice, hsBob)
}
func TestPKCSPaddingUnpadding(t *testing.T) {
maxMessageLength := 3 * NoisePaddingBlockSize
for messageLen := 0; messageLen <= maxMessageLength; messageLen++ {
message := generateRandomBytes(t, messageLen)
padded, err := PKCS7_Pad(message, NoisePaddingBlockSize)
require.NoError(t, err)
unpadded, err := PKCS7_Unpad(padded, NoisePaddingBlockSize)
require.NoError(t, err)
require.Greater(t, len(padded), 0)
require.Equal(t, len(padded)%NoisePaddingBlockSize, 0)
require.Equal(t, message, unpadded)
}
}

View File

@ -1,48 +0,0 @@
package noise
import (
"errors"
)
// PKCS7_Pad pads a payload according to PKCS#7 as per
// RFC 5652 https://datatracker.ietf.org/doc/html/rfc5652#section-6.3
func PKCS7_Pad(payload []byte, paddingSize int) ([]byte, error) {
if paddingSize >= 256 {
return nil, errors.New("invalid padding size")
}
k := paddingSize - (len(payload) % paddingSize)
var padVal int
if k != 0 {
padVal = k
} else {
padVal = paddingSize
}
padding := make([]byte, padVal)
for i := range padding {
padding[i] = byte(padVal)
}
return append(payload, padding...), nil
}
// PKCS7_Unpad unpads a payload according to PKCS#7 as per
// RFC 5652 https://datatracker.ietf.org/doc/html/rfc5652#section-6.3
func PKCS7_Unpad(payload []byte, paddingSize int) ([]byte, error) {
if paddingSize >= 256 {
return nil, errors.New("invalid padding size")
}
if len(payload) == 0 {
return nil, nil // empty payload
}
high := len(payload) - 1
k := payload[high]
unpadded := payload[0:(high + 1 - int(k))]
return unpadded, nil
}

View File

@ -1,87 +0,0 @@
package noise
import (
n "github.com/waku-org/noise"
)
/*
K1K1:
-> s
<- s
...
-> e
<- e, ee, es
-> se
*/
var HandshakeK1K1 = n.HandshakePattern{
Name: "K1K1",
InitiatorPreMessages: []n.MessagePattern{n.MessagePatternS},
ResponderPreMessages: []n.MessagePattern{n.MessagePatternS},
Messages: [][]n.MessagePattern{
{n.MessagePatternE},
{n.MessagePatternE, n.MessagePatternDHEE, n.MessagePatternDHES},
{n.MessagePatternDHSE},
},
}
/*
XK1:
<- s
...
-> e
<- e, ee, es
-> s, se
*/
var HandshakeXK1 = n.HandshakePattern{
Name: "XK1",
ResponderPreMessages: []n.MessagePattern{n.MessagePatternS},
Messages: [][]n.MessagePattern{
{n.MessagePatternE},
{n.MessagePatternE, n.MessagePatternDHEE, n.MessagePatternDHES},
{n.MessagePatternS, n.MessagePatternDHSE},
},
}
/*
XX:
-> e
<- e, ee, s, es
-> s, se
*/
var HandshakeXX = n.HandshakePattern{
Name: "XX",
Messages: [][]n.MessagePattern{
{n.MessagePatternE},
{n.MessagePatternE, n.MessagePatternDHEE, n.MessagePatternS, n.MessagePatternDHES},
{n.MessagePatternS, n.MessagePatternDHSE},
},
}
/*
XXpsk0:
-> psk, e
<- e, ee, s, es
-> s, se
*/
var HandshakeXXpsk0 = n.HandshakePattern{
Name: "XXpsk0",
Messages: [][]n.MessagePattern{
{n.MessagePatternPSK, n.MessagePatternE},
{n.MessagePatternE, n.MessagePatternDHEE, n.MessagePatternS, n.MessagePatternDHES},
{n.MessagePatternS, n.MessagePatternDHSE},
},
}
var HandshakeWakuPairing = n.HandshakePattern{
Name: "WakuPairing",
ResponderPreMessages: []n.MessagePattern{n.MessagePatternE},
Messages: [][]n.MessagePattern{
{n.MessagePatternE, n.MessagePatternDHEE},
{n.MessagePatternS, n.MessagePatternDHES},
{n.MessagePatternS, n.MessagePatternDHSE, n.MessagePatternDHSS},
},
}

View File

@ -1,343 +0,0 @@
package noise
import (
"bytes"
"crypto/ed25519"
"encoding/binary"
"errors"
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
n "github.com/waku-org/noise"
)
const MaxUint8 = 1<<8 - 1
// This follows https://rfc.vac.dev/spec/35/#public-keys-serialization
// pk contains the X coordinate of the public key, if unencrypted (this implies flag = 0)
// or the encryption of the X coordinate concatenated with the authorization tag, if encrypted (this implies flag = 1)
// Note: besides encryption, flag can be used to distinguish among multiple supported Elliptic Curves
type NoisePublicKey struct {
Flag byte
PubKey []byte
}
func byteToNoisePublicKey(input []byte) *NoisePublicKey {
flag := byte(0)
if len(input) > n.DH25519.DHLen() {
flag = 1
}
return &NoisePublicKey{
Flag: flag,
PubKey: input,
}
}
// EcdsaPubKeyToNoisePublicKey converts a Elliptic Curve public key
// to an unencrypted Noise public key
func Ed25519PubKeyToNoisePublicKey(pk ed25519.PublicKey) *NoisePublicKey {
return &NoisePublicKey{
Flag: 0,
PubKey: pk,
}
}
// Equals checks equality between two Noise public keys
func (pk *NoisePublicKey) Equals(pk2 *NoisePublicKey) bool {
return pk.Flag == pk2.Flag && bytes.Equal(pk.PubKey, pk2.PubKey)
}
type SerializedNoisePublicKey []byte
// Serialize converts a Noise public key to a stream of bytes as in
// https://rfc.vac.dev/spec/35/#public-keys-serialization
func (pk *NoisePublicKey) Serialize() SerializedNoisePublicKey {
// Public key is serialized as (flag || pk)
// Note that pk contains the X coordinate of the public key if unencrypted
// or the encryption concatenated with the authorization tag if encrypted
serializedPK := make([]byte, len(pk.PubKey)+1)
serializedPK[0] = pk.Flag
copy(serializedPK[1:], pk.PubKey)
return serializedPK
}
// Unserialize converts a serialized Noise public key to a NoisePublicKey object as in
// https://rfc.vac.dev/spec/35/#public-keys-serialization
func (s SerializedNoisePublicKey) Unserialize() (*NoisePublicKey, error) {
if len(s) <= 1 {
return nil, errors.New("invalid serialized public key length")
}
pubk := &NoisePublicKey{}
pubk.Flag = s[0]
if !(pubk.Flag == 0 || pubk.Flag == 1) {
return nil, errors.New("invalid flag in serialized public key")
}
pubk.PubKey = s[1:]
return pubk, nil
}
// Encrypt encrypts a Noise public key using a Cipher State
func (pk *NoisePublicKey) Encrypt(state *n.CipherState) error {
if pk.Flag == 0 {
// Authorization tag is appended to output
encPk, err := state.Encrypt(nil, nil, pk.PubKey)
if err != nil {
return err
}
pk.Flag = 1
pk.PubKey = encPk
}
return nil
}
// Decrypts decrypts a Noise public key using a Cipher State
func (pk *NoisePublicKey) Decrypt(state *n.CipherState) error {
if pk.Flag == 1 {
decPk, err := state.Decrypt(nil, nil, pk.PubKey) // encrypted pk should contain the auth tag
if err != nil {
return err
}
pk.Flag = 0
pk.PubKey = decPk
}
return nil
}
// PayloadV2 defines an object for Waku payloads with version 2 as in
// https://rfc.vac.dev/spec/35/#public-keys-serialization
// It contains a protocol ID field, the handshake message (for Noise handshakes) and
// a transport message (for Noise handshakes and ChaChaPoly encryptions)
type PayloadV2 struct {
ProtocolId byte
HandshakeMessage []*NoisePublicKey
TransportMessage []byte
MessageNametag MessageNametag
}
// Checks equality between two PayloadsV2 objects
func (p *PayloadV2) Equals(p2 *PayloadV2) bool {
if p.ProtocolId != p2.ProtocolId || !bytes.Equal(p.TransportMessage, p2.TransportMessage) {
return false
}
for _, p1 := range p.HandshakeMessage {
for _, p2 := range p2.HandshakeMessage {
if !p1.Equals(p2) {
return false
}
}
}
return true
}
// Serializes a PayloadV2 object to a byte sequences according to https://rfc.vac.dev/spec/35/
// The output serialized payload concatenates the input PayloadV2 object fields as
// payload = ( protocolId || serializedHandshakeMessageLen || serializedHandshakeMessage || transportMessageLen || transportMessage)
// The output can be then passed to the payload field of a WakuMessage https://rfc.vac.dev/spec/14/
func (p *PayloadV2) Serialize() ([]byte, error) {
// We collect public keys contained in the handshake message
// According to https://rfc.vac.dev/spec/35/, the maximum size for the handshake message is 256 bytes, that is
// the handshake message length can be represented with 1 byte only. (its length can be stored in 1 byte)
// However, to ease public keys length addition operation, we declare it as int and later cast to uit8
serializedHandshakeMessageLen := 0
// This variables will store the concatenation of the serializations of all public keys in the handshake message
serializedHandshakeMessage := make([]byte, 0, 256)
serializedHandshakeMessageBuffer := bytes.NewBuffer(serializedHandshakeMessage)
for _, pk := range p.HandshakeMessage {
serializedPK := pk.Serialize()
serializedHandshakeMessageLen += len(serializedPK)
if _, err := serializedHandshakeMessageBuffer.Write(serializedPK); err != nil {
return nil, err
}
if serializedHandshakeMessageLen > MaxUint8 {
return nil, errors.New("too many public keys in handshake message")
}
}
// The output payload as in https://rfc.vac.dev/spec/35/. We concatenate all the PayloadV2 fields as
// payload = ( protocolId || serializedHandshakeMessageLen || serializedHandshakeMessage || transportMessageLen || transportMessage)
// We declare it as a byte sequence of length accordingly to the PayloadV2 information read
payload := make([]byte, 0, MessageNametagLength+
1+ // 1 byte for protocol ID
1+ // 1 byte for length of serializedHandshakeMessage field
serializedHandshakeMessageLen+ // serializedHandshakeMessageLen bytes for serializedHandshakeMessage
8+ // 8 bytes for transportMessageLen
len(p.TransportMessage), // transportMessageLen bytes for transportMessage
)
payloadBuf := bytes.NewBuffer(payload)
if _, err := payloadBuf.Write(p.MessageNametag[:]); err != nil {
return nil, err
}
// The protocol ID (1 byte) and handshake message length (1 byte) can be directly casted to byte to allow direct copy to the payload byte sequence
if err := payloadBuf.WriteByte(p.ProtocolId); err != nil {
return nil, err
}
if err := payloadBuf.WriteByte(byte(serializedHandshakeMessageLen)); err != nil {
return nil, err
}
if _, err := payloadBuf.Write(serializedHandshakeMessageBuffer.Bytes()); err != nil {
return nil, err
}
TransportMessageLen := uint64(len(p.TransportMessage))
if err := binary.Write(payloadBuf, binary.LittleEndian, TransportMessageLen); err != nil {
return nil, err
}
if _, err := payloadBuf.Write(p.TransportMessage); err != nil {
return nil, err
}
return payloadBuf.Bytes(), nil
}
func isProtocolIDSupported(protocolID WakuNoiseProtocolID) bool {
return protocolID == Noise_K1K1_25519_ChaChaPoly_SHA256 ||
protocolID == Noise_XK1_25519_ChaChaPoly_SHA256 ||
protocolID == Noise_XX_25519_ChaChaPoly_SHA256 ||
protocolID == Noise_XXpsk0_25519_ChaChaPoly_SHA256 ||
protocolID == ChaChaPoly ||
protocolID == Noise_WakuPairing_25519_ChaChaPoly_SHA256 ||
protocolID == None
}
const ChaChaPolyTagSize = byte(16)
// Deserializes a byte sequence to a PayloadV2 object according to https://rfc.vac.dev/spec/35/.
// The input serialized payload concatenates the output PayloadV2 object fields as
// payload = ( protocolId || serializedHandshakeMessageLen || serializedHandshakeMessage || transportMessageLen || transportMessage)
func DeserializePayloadV2(payload []byte) (*PayloadV2, error) {
payloadBuf := bytes.NewBuffer(payload)
result := &PayloadV2{}
// We start by reading the messageNametag
if err := binary.Read(payloadBuf, binary.BigEndian, &result.MessageNametag); err != nil {
return nil, err
}
// We read the Protocol ID
if err := binary.Read(payloadBuf, binary.BigEndian, &result.ProtocolId); err != nil {
return nil, err
}
if !isProtocolIDSupported(result.ProtocolId) {
return nil, errors.New("unsupported protocol")
}
// We read the Handshake Message length (1 byte)
var handshakeMessageLen byte
if err := binary.Read(payloadBuf, binary.BigEndian, &handshakeMessageLen); err != nil {
return nil, err
}
if handshakeMessageLen > MaxUint8 {
return nil, errors.New("too many public keys in handshake message")
}
written := byte(0)
var handshakeMessages []*NoisePublicKey
for written < handshakeMessageLen {
// We obtain the current Noise Public key encryption flag
flag, err := payloadBuf.ReadByte()
if err != nil {
return nil, err
}
if flag == 0 {
// If the key is unencrypted, we only read the X coordinate of the EC public key and we deserialize into a Noise Public Key
pkLen := ed25519.PublicKeySize
var pkBytes SerializedNoisePublicKey = make([]byte, pkLen)
if err := binary.Read(payloadBuf, binary.BigEndian, &pkBytes); err != nil {
return nil, err
}
serializedPK := SerializedNoisePublicKey(make([]byte, ed25519.PublicKeySize+1))
serializedPK[0] = flag
copy(serializedPK[1:], pkBytes)
pk, err := serializedPK.Unserialize()
if err != nil {
return nil, err
}
handshakeMessages = append(handshakeMessages, pk)
written += uint8(1 + pkLen)
} else if flag == 1 {
// If the key is encrypted, we only read the encrypted X coordinate and the authorization tag, and we deserialize into a Noise Public Key
pkLen := ed25519.PublicKeySize + ChaChaPolyTagSize
// TODO: duplicated code: ==============
var pkBytes SerializedNoisePublicKey = make([]byte, pkLen)
if err := binary.Read(payloadBuf, binary.BigEndian, &pkBytes); err != nil {
return nil, err
}
serializedPK := SerializedNoisePublicKey(make([]byte, ed25519.PublicKeySize+1))
serializedPK[0] = flag
copy(serializedPK[1:], pkBytes)
pk, err := serializedPK.Unserialize()
if err != nil {
return nil, err
}
handshakeMessages = append(handshakeMessages, pk)
written += uint8(1 + pkLen)
// TODO: duplicated
} else {
return nil, errors.New("invalid flag for Noise public key")
}
}
result.HandshakeMessage = handshakeMessages
var TransportMessageLen uint64
if err := binary.Read(payloadBuf, binary.LittleEndian, &TransportMessageLen); err != nil {
return nil, err
}
result.TransportMessage = make([]byte, TransportMessageLen)
if err := binary.Read(payloadBuf, binary.BigEndian, &result.TransportMessage); err != nil {
return nil, err
}
return result, nil
}
// Decodes a WakuMessage to a PayloadV2
// Currently, this is just a wrapper over deserializePayloadV2 and encryption/decryption is done on top (no KeyInfo)
func DecodePayloadV2(message *pb.WakuMessage) (*PayloadV2, error) {
if message.Version != 2 {
return nil, errors.New("wrong message version while decoding payload")
}
return DeserializePayloadV2(message.Payload)
}
// Encodes a PayloadV2 to a WakuMessage
// Currently, this is just a wrapper over serializePayloadV2 and encryption/decryption is done on top (no KeyInfo)
func EncodePayloadV2(payload2 *PayloadV2) (*pb.WakuMessage, error) {
serializedPayload2, err := payload2.Serialize()
if err != nil {
return nil, err
}
return &pb.WakuMessage{
Payload: serializedPayload2,
Version: 2,
}, nil
}

View File

@ -1,209 +0,0 @@
package noise
import (
"bytes"
"crypto/rand"
"testing"
"github.com/stretchr/testify/require"
n "github.com/waku-org/noise"
)
func TestWakuPairing(t *testing.T) {
// Pairing Phase
// ==========
// Alice static/ephemeral key initialization and commitment
aliceStaticKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
aliceEphemeralKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
s := generateRandomBytes(t, 32)
aliceCommittedStaticKey := CommitPublicKey(aliceStaticKey.Public, s)
// Bob static/ephemeral key initialization and commitment
bobStaticKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
bobEphemeralKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
r := generateRandomBytes(t, 32)
bobCommittedStaticKey := CommitPublicKey(bobStaticKey.Public, r)
// Content topic information
applicationName := "waku-noise-sessions"
applicationVersion := "0.1"
shardId := "10"
qrMessageNameTag := BytesToMessageNametag(generateRandomBytes(t, MessageNametagLength))
// Out-of-band Communication
// Bob prepares the QR and sends it out-of-band to Alice
qr := NewQR(applicationName, applicationVersion, shardId, bobEphemeralKey.Public, bobCommittedStaticKey)
// Alice deserializes the QR code
readQR, err := StringToQR(qr.String())
require.NoError(t, err)
// We check if QR serialization/deserialization works
require.Equal(t, applicationName, readQR.applicationName)
require.Equal(t, applicationVersion, readQR.applicationVersion)
require.Equal(t, shardId, readQR.shardId)
require.True(t, bytes.Equal(bobEphemeralKey.Public, readQR.ephemeralPublicKey))
require.True(t, bytes.Equal(bobCommittedStaticKey[:], readQR.committedStaticKey[:]))
// Pre-handshake message
// <- eB {H(sB||r), contentTopicParams, messageNametag}
preMessagePKs := bobEphemeralKey.Public
// We initialize the Handshake states.
// Note that we pass the whole qr serialization as prologue information
aliceHS, err := NewHandshake_WakuPairing_25519_ChaChaPoly_SHA256(aliceStaticKey, aliceEphemeralKey, true, qr.Bytes(), preMessagePKs)
require.NoError(t, err)
bobHS, err := NewHandshake_WakuPairing_25519_ChaChaPoly_SHA256(bobStaticKey, bobEphemeralKey, false, qr.Bytes(), preMessagePKs)
require.NoError(t, err)
// Pairing Handshake
// ==========
// Write and read calls alternate between Alice and Bob: the handhshake progresses by alternatively calling stepHandshake for each user
// 1st step
// -> eA, eAeB {H(sA||s)} [authcode]
// The messageNametag for the first handshake message is randomly generated and exchanged out-of-band
// and corresponds to qrMessageNametag
// We set the transport message to be H(sA||s)
sentTransportMessage := aliceCommittedStaticKey
// By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
// and the (encrypted) transport message
// The message is sent with a messageNametag equal to the one received through the QR code
aliceStep, err := aliceHS.Step(nil, sentTransportMessage, &qrMessageNameTag)
require.NoError(t, err)
// Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
// Note that Bob verifies if the received payloadv2 has the expected messageNametag set
bobStep, err := bobHS.Step(&aliceStep.Payload2, nil, &qrMessageNameTag)
require.NoError(t, err)
require.True(t, bytes.Equal(bobStep.TransportMessage, sentTransportMessage))
// We generate an authorization code using the handshake state
aliceAuthcode, err := aliceHS.Authcode()
require.NoError(t, err)
bobAuthcode, err := bobHS.Authcode()
require.NoError(t, err)
// We check that they are equal. Note that this check has to be confirmed with a user interaction.
require.Equal(t, aliceAuthcode, bobAuthcode)
// 2nd step
// <- sB, eAsB {r}
// Alice and Bob update their local next messageNametag using the available handshake information
// During the handshake, messageNametag = HKDF(h), where h is the handshake hash value at the end of the last processed message
aliceMessageNametag, err := aliceHS.ToMessageNametag()
require.NoError(t, err)
bobMessageNametag, err := bobHS.ToMessageNametag()
require.NoError(t, err)
// We set as a transport message the commitment randomness r
sentTransportMessage = r
// At this step, Bob writes and returns a payload
bobStep, err = bobHS.Step(nil, sentTransportMessage, &bobMessageNametag)
require.NoError(t, err)
// While Alice reads and returns the (decrypted) transport message
aliceStep, err = aliceHS.Step(&bobStep.Payload2, nil, &aliceMessageNametag)
require.NoError(t, err)
require.Equal(t, aliceStep.TransportMessage, sentTransportMessage)
// Alice further checks if Bob's commitment opens to Bob's static key she just received
expectedBobCommittedStaticKey := CommitPublicKey(aliceHS.RS(), aliceStep.TransportMessage)
require.True(t, bytes.Equal(expectedBobCommittedStaticKey, bobCommittedStaticKey))
// 3rd step
// -> sA, sAeB, sAsB {s}
// Alice and Bob update their local next messageNametag using the available handshake information
aliceMessageNametag, err = aliceHS.ToMessageNametag()
require.NoError(t, err)
bobMessageNametag, err = bobHS.ToMessageNametag()
require.NoError(t, err)
// We set as a transport message the commitment randomness s
sentTransportMessage = s
// Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
aliceStep, err = aliceHS.Step(nil, sentTransportMessage, &aliceMessageNametag)
require.NoError(t, err)
// Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep, err = bobHS.Step(&aliceStep.Payload2, nil, &bobMessageNametag)
require.NoError(t, err)
require.True(t, bytes.Equal(bobStep.TransportMessage, sentTransportMessage))
// Bob further checks if Alice's commitment opens to Alice's static key he just received
expectedAliceCommittedStaticKey := CommitPublicKey(bobHS.RS(), bobStep.TransportMessage)
require.True(t, bytes.Equal(expectedAliceCommittedStaticKey, aliceCommittedStaticKey))
// Secure Transfer Phase
// ==========
// We test read/write of random messages exchanged between Alice and Bob
// Note that we exchange more than the number of messages contained in the nametag buffer to test if they are filled correctly as the communication proceeds
for i := 0; i < 10*MessageNametagBufferSize; i++ {
// Alice writes to Bob
message := generateRandomBytes(t, 32)
payload, err := aliceHS.Encrypt(message)
require.NoError(t, err)
readMessage, err := bobHS.Decrypt(payload)
require.NoError(t, err)
require.True(t, bytes.Equal(message, readMessage))
// Bob writes to Alice
message = generateRandomBytes(t, 32)
payload, err = bobHS.Encrypt(message)
require.NoError(t, err)
readMessage, err = aliceHS.Decrypt(payload)
require.NoError(t, err)
require.True(t, bytes.Equal(message, readMessage))
}
// We test how nametag buffers help in detecting lost messages
// Alice writes two messages to Bob, but only the second is received
message := generateRandomBytes(t, 32)
_, err = aliceHS.Encrypt(message)
require.NoError(t, err)
message = generateRandomBytes(t, 32)
payload2, err := aliceHS.Encrypt(message)
require.NoError(t, err)
_, err = bobHS.Decrypt(payload2)
require.Error(t, err)
require.ErrorIs(t, err, ErrNametagNotExpected)
// We adjust bob nametag buffer for next test (i.e. the missed message is correctly recovered)
bobHS.nametagsInbound.Delete(2)
message = generateRandomBytes(t, 32)
payload2, err = bobHS.Encrypt(message)
require.NoError(t, err)
readMessage, err := aliceHS.Decrypt(payload2)
require.NoError(t, err)
require.True(t, bytes.Equal(message, readMessage))
// We test if a missing nametag is correctly detected
message = generateRandomBytes(t, 32)
payload2, err = aliceHS.Encrypt(message)
require.NoError(t, err)
bobHS.nametagsInbound.Delete(1)
_, err = bobHS.Decrypt(payload2)
require.ErrorIs(t, err, ErrNametagNotFound)
}

View File

@ -5,18 +5,19 @@ import (
"context"
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
"errors"
"sync"
n "github.com/waku-org/go-noise"
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
n "github.com/waku-org/noise"
"go.uber.org/zap"
)
var ErrPairingTimeout = errors.New("pairing has timed out")
type Sender interface {
Publish(ctx context.Context, contentTopic string, payload PayloadV2) error
Publish(ctx context.Context, contentTopic string, payload *n.PayloadV2) error
}
type Receiver interface {
@ -31,7 +32,7 @@ type Pairing struct {
randomFixLenVal []byte
myCommittedStaticKey []byte
params PairingParameters
handshake *Handshake
handshake *n.Handshake
authCode string
authCodeEmitted chan string
authCodeConfirmed chan bool
@ -40,11 +41,12 @@ type Pairing struct {
started bool
completed bool
hsResult *n.HandshakeResult
}
type PairingParameterOption func(*PairingParameters) error
func WithInitiatorParameters(qrString string, qrMessageNametag MessageNametag) PairingParameterOption {
func WithInitiatorParameters(qrString string, qrMessageNametag n.MessageNametag) PairingParameterOption {
return func(params *PairingParameters) error {
params.initiator = true
qr, err := StringToQR(qrString)
@ -57,16 +59,16 @@ func WithInitiatorParameters(qrString string, qrMessageNametag MessageNametag) P
}
}
func WithResponderParameters(applicationName, applicationVersion, shardId string, qrMessageNameTag *MessageNametag) PairingParameterOption {
func WithResponderParameters(applicationName, applicationVersion, shardId string, qrMessageNameTag *n.MessageNametag) PairingParameterOption {
return func(params *PairingParameters) error {
params.initiator = false
if qrMessageNameTag == nil {
b := make([]byte, MessageNametagLength)
b := make([]byte, n.MessageNametagLength)
_, err := rand.Read(b)
if err != nil {
return err
}
params.qrMessageNametag = BytesToMessageNametag(b)
params.qrMessageNametag = n.BytesToMessageNametag(b)
} else {
params.qrMessageNametag = *qrMessageNameTag
}
@ -88,17 +90,17 @@ type PairingParameters struct {
ephemeralPublicKey ed25519.PublicKey
initiator bool
qr QR
qrMessageNametag MessageNametag
qrMessageNametag n.MessageNametag
}
func NewPairing(myStaticKey n.DHKey, myEphemeralKey n.DHKey, opts PairingParameterOption, messenger NoiseMessenger, logger *zap.Logger) (*Pairing, error) {
func NewPairing(myStaticKey n.Keypair, myEphemeralKey n.Keypair, opts PairingParameterOption, messenger NoiseMessenger, logger *zap.Logger) (*Pairing, error) {
b := make([]byte, 32)
_, err := rand.Read(b)
if err != nil {
return nil, err
}
myCommitedStaticKey := CommitPublicKey(myStaticKey.Public, b)
myCommitedStaticKey := n.CommitPublicKey(sha256.New, myStaticKey.Public, b)
var params PairingParameters
params.myCommitedStaticKey = myCommitedStaticKey
@ -108,8 +110,7 @@ func NewPairing(myStaticKey n.DHKey, myEphemeralKey n.DHKey, opts PairingParamet
return nil, err
}
preMessagePKs := params.qr.ephemeralPublicKey
hs, err := NewHandshake_WakuPairing_25519_ChaChaPoly_SHA256(myStaticKey, myEphemeralKey, params.initiator, params.qr.Bytes(), preMessagePKs)
hs, err := n.NewHandshake_WakuPairing_25519_ChaChaPoly_SHA256(myStaticKey, myEphemeralKey, params.initiator, params.qr.Bytes(), params.qr.ephemeralPublicKey)
if err != nil {
return nil, err
}
@ -133,7 +134,7 @@ func NewPairing(myStaticKey n.DHKey, myEphemeralKey n.DHKey, opts PairingParamet
}, nil
}
func (p *Pairing) PairingInfo() (qrString string, qrMessageNametag MessageNametag) {
func (p *Pairing) PairingInfo() (qrString string, qrMessageNametag n.MessageNametag) {
p.RLock()
defer p.RUnlock()
return p.params.qr.String(), p.params.qrMessageNametag
@ -180,7 +181,7 @@ func (p *Pairing) isAuthCodeConfirmed(ctx context.Context) (bool, error) {
}
}
func (p *Pairing) executeReadStepWithNextMessage(ctx context.Context, nextMsgChan <-chan *pb.WakuMessage, messageNametag MessageNametag) (*HandshakeStepResult, error) {
func (p *Pairing) executeReadStepWithNextMessage(ctx context.Context, nextMsgChan <-chan *pb.WakuMessage, messageNametag n.MessageNametag) (*n.HandshakeStepResult, error) {
for {
select {
case <-ctx.Done():
@ -191,9 +192,9 @@ func (p *Pairing) executeReadStepWithNextMessage(ctx context.Context, nextMsgCha
return nil, err
}
step, err := p.handshake.Step(payload, nil, &messageNametag)
step, err := p.handshake.Step(payload, nil, messageNametag)
if err != nil {
if errors.Is(err, ErrNametagNotExpected) {
if errors.Is(err, n.ErrNametagNotExpected) || errors.Is(err, n.ErrUnexpectedMessageNametag) {
p.logger.Debug(err.Error())
continue
} else {
@ -210,11 +211,10 @@ func (p *Pairing) initiatorHandshake(ctx context.Context, msgCh <-chan *pb.WakuM
go func() {
defer close(doneCh)
// The handshake initiator writes a Waku2 payload v2 containing the handshake message
// and the (encrypted) transport message
// The message is sent with a messageNametag equal to the one received through the QR code
hsStep, err := p.handshake.Step(nil, p.myCommittedStaticKey, &p.params.qrMessageNametag)
hsStep, err := p.handshake.Step(nil, p.myCommittedStaticKey, p.params.qrMessageNametag)
if err != nil {
doneCh <- err
return
@ -222,12 +222,11 @@ func (p *Pairing) initiatorHandshake(ctx context.Context, msgCh <-chan *pb.WakuM
// We prepare a message from initiator's payload2
// At this point wakuMsg is sent over the Waku network to receiver content topic
err = p.messenger.Publish(ctx, p.ContentTopic, hsStep.Payload2)
err = p.messenger.Publish(ctx, p.ContentTopic, hsStep.PayloadV2)
if err != nil {
doneCh <- err
return
}
// We generate an authorization code using the handshake state
// this check has to be confirmed with a user interaction, comparing auth codes in both ends
authCode, err := p.handshake.Authcode()
@ -270,7 +269,7 @@ func (p *Pairing) initiatorHandshake(ctx context.Context, msgCh <-chan *pb.WakuM
}
// Initiator further checks if receiver's commitment opens to receiver's static key received
expectedReceiverCommittedStaticKey := CommitPublicKey(p.handshake.RS(), hsStep.TransportMessage)
expectedReceiverCommittedStaticKey := n.CommitPublicKey(sha256.New, p.handshake.RemoteStaticPublicKey(), hsStep.TransportMessage)
if !bytes.Equal(expectedReceiverCommittedStaticKey, p.params.qr.committedStaticKey) {
doneCh <- errors.New("expected committed static key does not match the receiver actual committed static key")
return
@ -285,25 +284,32 @@ func (p *Pairing) initiatorHandshake(ctx context.Context, msgCh <-chan *pb.WakuM
return
}
hsStep, err = p.handshake.Step(nil, p.randomFixLenVal, &hsMessageNametag)
hsStep, err = p.handshake.Step(nil, p.randomFixLenVal, hsMessageNametag)
if err != nil {
doneCh <- err
return
}
err = p.messenger.Publish(ctx, p.ContentTopic, hsStep.Payload2)
err = p.messenger.Publish(ctx, p.ContentTopic, hsStep.PayloadV2)
if err != nil {
doneCh <- err
return
}
hsResult, err := p.handshake.FinalizeHandshake()
if err != nil {
doneCh <- err
return
}
// Secure Transfer Phase
if !p.handshake.HandshakeComplete() {
if !p.handshake.IsComplete() {
doneCh <- errors.New("handshake is in undefined state")
return
}
p.Lock()
p.hsResult = hsResult
p.completed = true
p.Unlock()
}()
@ -363,14 +369,14 @@ func (p *Pairing) responderHandshake(ctx context.Context, msgCh <-chan *pb.WakuM
return
}
hsStep, err = p.handshake.Step(nil, p.randomFixLenVal, &hsMessageNametag)
hsStep, err = p.handshake.Step(nil, p.randomFixLenVal, hsMessageNametag)
if err != nil {
doneCh <- err
return
}
// We prepare a Waku message from receiver's payload2
err = p.messenger.Publish(ctx, p.ContentTopic, hsStep.Payload2)
err = p.messenger.Publish(ctx, p.ContentTopic, hsStep.PayloadV2)
if err != nil {
doneCh <- err
return
@ -392,20 +398,27 @@ func (p *Pairing) responderHandshake(ctx context.Context, msgCh <-chan *pb.WakuM
}
// The receiver further checks if the initiator's commitment opens to the initiator's static key received
expectedInitiatorCommittedStaticKey := CommitPublicKey(p.handshake.RS(), hsStep.TransportMessage)
expectedInitiatorCommittedStaticKey := n.CommitPublicKey(sha256.New, p.handshake.RemoteStaticPublicKey(), hsStep.TransportMessage)
if !bytes.Equal(expectedInitiatorCommittedStaticKey, initiatorCommittedStaticKey) {
doneCh <- errors.New("expected committed static key does not match the initiator actual committed static key")
return
}
hsResult, err := p.handshake.FinalizeHandshake()
if err != nil {
doneCh <- err
return
}
// Secure Transfer Phase
if !p.handshake.HandshakeComplete() {
if !p.handshake.IsComplete() {
doneCh <- errors.New("handshake is in undefined state")
return
}
p.Lock()
p.completed = true
p.hsResult = hsResult
p.Unlock()
}()
return doneCh
@ -419,20 +432,39 @@ func (p *Pairing) HandshakeComplete() bool {
// Returns a WakuMessage with version 2 and encrypted payload
func (p *Pairing) Encrypt(plaintext []byte) (*pb.WakuMessage, error) {
payload, err := p.handshake.Encrypt(plaintext)
p.RLock()
defer p.RUnlock()
if !p.completed {
return nil, errors.New("pairing is not complete")
}
payload, err := p.hsResult.WriteMessage(plaintext, nil)
if err != nil {
return nil, err
}
return EncodePayloadV2(payload)
encodedPayload, err := EncodePayloadV2(payload)
if err != nil {
return nil, err
}
encodedPayload.ContentTopic = p.ContentTopic
return encodedPayload, nil
}
func (p *Pairing) Decrypt(msg *pb.WakuMessage) ([]byte, error) {
p.RLock()
defer p.RUnlock()
if !p.completed {
return nil, errors.New("pairing is not complete")
}
payload, err := DecodePayloadV2(msg)
if err != nil {
return nil, err
}
return p.handshake.Decrypt(payload)
return p.hsResult.ReadMessage(payload, nil)
}
func (p *Pairing) ConfirmAuthCode(confirmed bool) error {

View File

@ -3,6 +3,7 @@ package noise
import (
"context"
n "github.com/waku-org/go-noise"
v2 "github.com/waku-org/go-waku/waku/v2"
"github.com/waku-org/go-waku/waku/v2/protocol"
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
@ -110,9 +111,9 @@ func (r *NoiseWakuRelay) Subscribe(ctx context.Context, contentTopic string) <-c
return sub.msgChan
}
func (r *NoiseWakuRelay) Publish(ctx context.Context, contentTopic string, payload PayloadV2) error {
func (r *NoiseWakuRelay) Publish(ctx context.Context, contentTopic string, payload *n.PayloadV2) error {
message, err := EncodePayloadV2(&payload)
message, err := EncodePayloadV2(payload)
if err != nil {
return err
}

View File

@ -11,12 +11,12 @@ import (
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peerstore"
"github.com/stretchr/testify/require"
n "github.com/waku-org/go-noise"
"github.com/waku-org/go-waku/tests"
v2 "github.com/waku-org/go-waku/waku/v2"
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
"github.com/waku-org/go-waku/waku/v2/timesource"
"github.com/waku-org/go-waku/waku/v2/utils"
n "github.com/waku-org/noise"
)
func createRelayNode(t *testing.T) (host.Host, *relay.WakuRelay) {
@ -49,8 +49,8 @@ func TestPairingObj1Success(t *testing.T) {
time.Sleep(2 * time.Second) // Wait for relay to form mesh
bobStaticKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
bobEphemeralKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
bobStaticKey, _ := n.DH25519.GenerateKeypair()
bobEphemeralKey, _ := n.DH25519.GenerateKeypair()
bobMessenger, err := NewWakuRelayMessenger(context.Background(), relay1, nil, timesource.NewDefaultClock())
require.NoError(t, err)
@ -91,8 +91,8 @@ func TestPairingObj1Success(t *testing.T) {
require.NoError(t, err)
}()
aliceStaticKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
aliceEphemeralKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
aliceStaticKey, _ := n.DH25519.GenerateKeypair()
aliceEphemeralKey, _ := n.DH25519.GenerateKeypair()
aliceMessenger, err := NewWakuRelayMessenger(context.Background(), relay2, nil, timesource.NewDefaultClock())
require.NoError(t, err)
@ -126,7 +126,7 @@ func TestPairingObj1Success(t *testing.T) {
// We test read/write of random messages exchanged between Alice and Bob
// Note that we exchange more than the number of messages contained in the nametag buffer to test if they are filled correctly as the communication proceeds
// We assume messages are sent via one of waku protocols
for i := 0; i < 10*MessageNametagBufferSize; i++ {
for i := 0; i < 10*n.MessageNametagBufferSize; i++ {
// Alice writes to Bob
message := generateRandomBytes(t, 32)
msg, err := alicePairingObj.Encrypt(message)
@ -165,8 +165,8 @@ func TestPairingObj1ShouldTimeout(t *testing.T) {
time.Sleep(2 * time.Second) // Wait for relay to form mesh
bobStaticKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
bobEphemeralKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
bobStaticKey, _ := n.DH25519.GenerateKeypair()
bobEphemeralKey, _ := n.DH25519.GenerateKeypair()
bobMessenger, err := NewWakuRelayMessenger(context.Background(), relay1, nil, timesource.NewDefaultClock())
require.NoError(t, err)
@ -174,8 +174,8 @@ func TestPairingObj1ShouldTimeout(t *testing.T) {
bobPairingObj, err := NewPairing(bobStaticKey, bobEphemeralKey, WithDefaultResponderParameters(), bobMessenger, utils.Logger())
require.NoError(t, err)
aliceStaticKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
aliceEphemeralKey, _ := n.DH25519.GenerateKeypair(rand.Reader)
aliceStaticKey, _ := n.DH25519.GenerateKeypair()
aliceEphemeralKey, _ := n.DH25519.GenerateKeypair()
aliceMessenger, err := NewWakuRelayMessenger(context.Background(), relay2, nil, timesource.NewDefaultClock())
require.NoError(t, err)

View File

@ -0,0 +1,31 @@
package noise
import (
"errors"
n "github.com/waku-org/go-noise"
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
)
// Decodes a WakuMessage to a PayloadV2
// Currently, this is just a wrapper over deserializePayloadV2 and encryption/decryption is done on top (no KeyInfo)
func DecodePayloadV2(message *pb.WakuMessage) (*n.PayloadV2, error) {
if message.Version != 2 {
return nil, errors.New("wrong message version while decoding payload")
}
return n.DeserializePayloadV2(message.Payload)
}
// Encodes a PayloadV2 to a WakuMessage
// Currently, this is just a wrapper over serializePayloadV2 and encryption/decryption is done on top (no KeyInfo)
func EncodePayloadV2(payload2 *n.PayloadV2) (*pb.WakuMessage, error) {
serializedPayload2, err := payload2.Serialize()
if err != nil {
return nil, err
}
return &pb.WakuMessage{
Payload: serializedPayload2,
Version: 2,
}, nil
}

View File

@ -0,0 +1,41 @@
package noise
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"testing"
"github.com/stretchr/testify/require"
n "github.com/waku-org/go-noise"
)
func generateRandomBytes(t *testing.T, n int) []byte {
b := make([]byte, n)
_, err := rand.Read(b)
require.NoError(t, err)
return b
}
func TestQR(t *testing.T) {
staticKey, _ := n.DH25519.GenerateKeypair()
ephemeralKey, _ := n.DH25519.GenerateKeypair()
r := generateRandomBytes(t, 32)
committedStaticKey := n.CommitPublicKey(sha256.New, staticKey.Public, r)
// Content topic information
applicationName := "waku-noise-sessions"
applicationVersion := "0.1"
shardId := "10"
qr := NewQR(applicationName, applicationVersion, shardId, ephemeralKey.Public, committedStaticKey)
readQR, err := StringToQR(qr.String())
require.NoError(t, err)
// We check if QR serialization/deserialization works
require.Equal(t, applicationName, readQR.applicationName)
require.Equal(t, applicationVersion, readQR.applicationVersion)
require.Equal(t, shardId, readQR.shardId)
require.True(t, bytes.Equal(ephemeralKey.Public, readQR.ephemeralPublicKey))
require.True(t, bytes.Equal(committedStaticKey[:], readQR.committedStaticKey[:]))
}