diff --git a/channels/channelMessages.pb.go b/channels/channelMessages.pb.go index f9fe30584be5787e7b73924071839cb87ef233a9..911c35da9cfb33b1e319f64233c174f81f1c25d6 100644 --- a/channels/channelMessages.pb.go +++ b/channels/channelMessages.pb.go @@ -47,6 +47,10 @@ type ChannelMessage struct { // nickname is the name which the user is using for this message // it will not be longer than 24 characters Nickname string `protobuf:"bytes,5,opt,name=Nickname,proto3" json:"Nickname,omitempty"` + // Nonce is 32 bits of randomness to ensure that two messages in the same + // round with that have the same nickname, payload, and lease will not have + // the same message ID. + Nonce []byte `protobuf:"bytes,6,opt,name=Nonce,proto3" json:"Nonce,omitempty"` } func (x *ChannelMessage) Reset() { @@ -116,6 +120,13 @@ func (x *ChannelMessage) GetNickname() string { return "" } +func (x *ChannelMessage) GetNonce() []byte { + if x != nil { + return x.Nonce + } + return nil +} + // UserMessage is a message sent by a user who is a member within the channel. type UserMessage struct { state protoimpl.MessageState @@ -194,7 +205,7 @@ var File_channelMessages_proto protoreflect.FileDescriptor var file_channelMessages_proto_rawDesc = []byte{ 0x0a, 0x15, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x73, 0x22, 0x98, 0x01, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, + 0x73, 0x22, 0xae, 0x01, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x52, 0x6f, 0x75, @@ -203,17 +214,18 @@ var file_channelMessages_proto_rawDesc = []byte{ 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x69, 0x0a, 0x0b, - 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x45, 0x43, 0x43, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x45, 0x43, 0x43, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x6c, 0x61, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6c, 0x69, 0x78, 0x78, 0x69, 0x72, 0x2f, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x09, 0x52, 0x08, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x4e, 0x6f, 0x6e, + 0x63, 0x65, 0x22, 0x69, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x45, 0x43, 0x43, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0c, 0x45, 0x43, 0x43, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x24, 0x5a, + 0x22, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6c, 0x69, 0x78, + 0x78, 0x69, 0x72, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/channels/channelMessages.proto b/channels/channelMessages.proto index b0ceb6fd8c156c4c24f80743eb8e1b54973cf084..548cfc67c9e8f02a750f12fff45bb27b8b613b4b 100644 --- a/channels/channelMessages.proto +++ b/channels/channelMessages.proto @@ -32,10 +32,10 @@ message ChannelMessage{ // it will not be longer than 24 characters string Nickname = 5; - // Nonce is 32 bits of randomness to ensure that in the event that in the - // same round two messages are send with the same nickname, payload, - // and lease, they will not have the same message ID. - bytes Nonce = 6; + // Nonce is 32 bits of randomness to ensure that two messages in the same + // round with that have the same nickname, payload, and lease will not have + // the same message ID. + bytes Nonce = 6; } // UserMessage is a message sent by a user who is a member within the channel. @@ -54,4 +54,4 @@ message UserMessage { // ECCPublicKey is the user's EC Public key. This is provided by the // network. bytes ECCPublicKey = 5; -} +} \ No newline at end of file diff --git a/channels/send.go b/channels/send.go index af1c33e09a926a0cbb4c3dff2fff8c353919574a..9b5b4fdb5fed58078168286e553ad7dc5dc78137 100644 --- a/channels/send.go +++ b/channels/send.go @@ -9,6 +9,7 @@ package channels import ( "crypto/ed25519" + "github.com/pkg/errors" "gitlab.com/elixxir/client/cmix" "gitlab.com/elixxir/client/cmix/rounds" cryptoChannel "gitlab.com/elixxir/crypto/channel" @@ -24,6 +25,9 @@ const ( cmixChannelReactionVersion = 0 ) +// The size of the nonce used in the message ID. +const messageNonceSize = 4 + // SendGeneric is used to send a raw message over a channel. In general, it // should be wrapped in a function which defines the wire protocol // If the final message, before being sent over the wire, is too long, this will @@ -49,6 +53,21 @@ func (m *manager) SendGeneric(channelID *id.ID, messageType MessageType, PayloadType: uint32(messageType), Payload: msg, Nickname: nickname, + Nonce: make([]byte, messageNonceSize), + } + + // Generate random nonce to be used for message ID generation. This makes it + // so two identical messages sent on the same round have different message IDs + rng := m.rng.GetStream() + n, err := rng.Read(chMsg.Nonce) + rng.Close() + if err != nil { + return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, + errors.Errorf("Failed to generate nonce: %+v", err) + } else if n != messageNonceSize { + return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, + errors.Errorf( + "Generated %d bytes for %-byte nonce", n, messageNonceSize) } usrMsg := &UserMessage{ @@ -124,9 +143,25 @@ func (m *manager) SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID, PayloadType: uint32(messageType), Payload: msg, Nickname: AdminUsername, + Nonce: make([]byte, messageNonceSize), } - //Note: we are not checking check if message is too long before trying to - //find a round + + // Generate random nonce to be used for message ID generation. This makes it + // so two identical messages sent on the same round have different message IDs + rng := m.rng.GetStream() + n, err := rng.Read(chMsg.Nonce) + rng.Close() + if err != nil { + return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, + errors.Errorf("Failed to generate nonce: %+v", err) + } else if n != messageNonceSize { + return cryptoChannel.MessageID{}, rounds.Round{}, ephemeral.Id{}, + errors.Errorf( + "Generated %d bytes for %-byte nonce", n, messageNonceSize) + } + + // Note: we are not checking if message is too long before trying to + // find a round //Build the function pointer that will build the message assemble := func(rid id.Round) ([]byte, error) { diff --git a/channels/send_test.go b/channels/send_test.go index d8c96c028fa249a74de615e749c60767697b8b1e..5da3010922520fe229f918784d72ad32146e59f0 100644 --- a/channels/send_test.go +++ b/channels/send_test.go @@ -15,11 +15,13 @@ import ( "gitlab.com/elixxir/client/cmix/rounds" "gitlab.com/elixxir/client/storage/versioned" cryptoChannel "gitlab.com/elixxir/crypto/channel" + "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/elixxir/crypto/rsa" "gitlab.com/elixxir/ekv" "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/primitives/netTime" "math/rand" + "sync" "testing" "time" @@ -153,6 +155,8 @@ func TestSendGeneric(t *testing.T) { m := &manager{ me: pi, channels: make(map[id.ID]*joinedChannel), + mux: sync.RWMutex{}, + rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), nicknameManager: &nicknameManager{ byChannel: make(map[id.ID]string), kv: nil, @@ -196,9 +200,9 @@ func TestSendGeneric(t *testing.T) { } t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) - //verify the message was handled correctly + // verify the message was handled correctly - //decode the user message + // decode the user message umi, err := unmarshalUserMessageInternal(mbc.payload, channelID) if err != nil { t.Fatalf("Failed to decode the user message: %s", err) @@ -242,7 +246,8 @@ func TestAdminGeneric(t *testing.T) { byChannel: make(map[id.ID]string), kv: nil, }, - me: pi, + me: pi, + rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), st: loadSendTracker(&mockBroadcastClient{}, versioned.NewKV(ekv.MakeMemstore()), func(chID *id.ID, umi *userMessageInternal, ts time.Time, @@ -282,7 +287,7 @@ func TestAdminGeneric(t *testing.T) { } t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) - //verify the message was handled correctly + // verify the message was handled correctly msgID := cryptoChannel.MakeMessageID(mbc.payload, ch.ReceptionID) @@ -291,7 +296,7 @@ func TestAdminGeneric(t *testing.T) { msgID, messageId) } - //decode the channel message + // decode the channel message chMgs := &ChannelMessage{} err = proto.Unmarshal(mbc.payload, chMgs) if err != nil { @@ -332,6 +337,7 @@ func TestSendMessage(t *testing.T) { byChannel: make(map[id.ID]string), kv: nil, }, + rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), st: loadSendTracker(&mockBroadcastClient{}, versioned.NewKV(ekv.MakeMemstore()), func(chID *id.ID, umi *userMessageInternal, ts time.Time, @@ -370,9 +376,9 @@ func TestSendMessage(t *testing.T) { } t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) - //verify the message was handled correctly + // verify the message was handled correctly - //decode the user message + // decode the user message umi, err := unmarshalUserMessageInternal(mbc.payload, channelID) if err != nil { t.Fatalf("Failed to decode the user message: %s", err) @@ -394,7 +400,7 @@ func TestSendMessage(t *testing.T) { umi.GetChannelMessage().RoundID, returnedRound) } - //decode the text message + // decode the text message txt := &CMIXChannelText{} err = proto.Unmarshal(umi.GetChannelMessage().Payload, txt) if err != nil { @@ -426,6 +432,7 @@ func TestSendReply(t *testing.T) { byChannel: make(map[id.ID]string), kv: nil, }, + rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), st: loadSendTracker(&mockBroadcastClient{}, versioned.NewKV(ekv.MakeMemstore()), func(chID *id.ID, umi *userMessageInternal, ts time.Time, @@ -464,9 +471,9 @@ func TestSendReply(t *testing.T) { } t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) - //verify the message was handled correctly + // verify the message was handled correctly - //decode the user message + // decode the user message umi, err := unmarshalUserMessageInternal(mbc.payload, channelID) if err != nil { t.Fatalf("Failed to decode the user message: %s", err) @@ -488,7 +495,7 @@ func TestSendReply(t *testing.T) { umi.GetChannelMessage().RoundID, returnedRound) } - //decode the text message + // decode the text message txt := &CMIXChannelText{} err = proto.Unmarshal(umi.GetChannelMessage().Payload, txt) if err != nil { @@ -519,6 +526,7 @@ func TestSendReaction(t *testing.T) { byChannel: make(map[id.ID]string), kv: nil, }, + rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), channels: make(map[id.ID]*joinedChannel), st: loadSendTracker(&mockBroadcastClient{}, versioned.NewKV(ekv.MakeMemstore()), func(chID *id.ID, @@ -557,9 +565,9 @@ func TestSendReaction(t *testing.T) { } t.Logf("messageId %v, roundId %v, ephemeralId %v", messageId, roundId, ephemeralId) - //verify the message was handled correctly + // verify the message was handled correctly - //decode the user message + // decode the user message umi, err := unmarshalUserMessageInternal(mbc.payload, channelID) if err != nil { t.Fatalf("Failed to decode the user message: %s", err) @@ -581,7 +589,7 @@ func TestSendReaction(t *testing.T) { umi.GetChannelMessage().RoundID, returnedRound) } - //decode the text message + // decode the text message txt := &CMIXChannelReaction{} err = proto.Unmarshal(umi.GetChannelMessage().Payload, txt) if err != nil {