From d37447f320449087d679bdec1366cf72ebb9bc05 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Fri, 21 Sep 2018 11:02:46 -0700 Subject: [PATCH] Fixing #118 - Adding an initial message to group setup --- model/group.go | 31 +++++++++++++------- model/profile.go | 9 +++++- model/profile_test.go | 8 +++++- peer/cwtch_peer.go | 9 ++++-- protocol/cwtch-profile.pb.go | 56 +++++++++++++++++++----------------- protocol/cwtch-profile.proto | 1 + 6 files changed, 73 insertions(+), 41 deletions(-) diff --git a/model/group.go b/model/group.go index 6907c6b..4939a93 100644 --- a/model/group.go +++ b/model/group.go @@ -16,15 +16,16 @@ import ( //Group defines and encapsulates Cwtch's conception of group chat. Which are sessions // tied to a server under a given group key. Each group has a set of messages. type Group struct { - GroupID string - SignedGroupID []byte - GroupKey [32]byte - GroupServer string - Timeline Timeline - Accepted bool - Owner string - IsCompromised bool - lock sync.Mutex + GroupID string + SignedGroupID []byte + GroupKey [32]byte + GroupServer string + Timeline Timeline + Accepted bool + Owner string + IsCompromised bool + InitialMessage []byte + lock sync.Mutex } // NewGroup initializes a new group associated with a given CwtchServer @@ -58,18 +59,28 @@ func (g *Group) Compromised() { g.IsCompromised = true } +// GetInitialMessage returns the first message of the group, if one was sent with the invite. +func (g *Group) GetInitialMessage() []byte { + g.lock.Lock() + defer g.lock.Unlock() + return g.InitialMessage +} + // Invite generates a invitation that can be sent to a cwtch peer -func (g *Group) Invite() ([]byte, error) { +func (g *Group) Invite(initialMessage []byte) ([]byte, error) { if g.SignedGroupID == nil { return nil, errors.New("group isn't signed") } + g.InitialMessage = initialMessage[:] + gci := &protocol.GroupChatInvite{ GroupName: g.GroupID, GroupSharedKey: g.GroupKey[:], ServerHost: g.GroupServer, SignedGroupId: g.SignedGroupID[:], + InitialMessage: initialMessage[:], } cp := &protocol.CwtchPeerPacket{ diff --git a/model/profile.go b/model/profile.go index 1456159..0532e6c 100644 --- a/model/profile.go +++ b/model/profile.go @@ -199,11 +199,17 @@ func (p *Profile) SignMessage(message string) []byte { //StartGroup when given a server, creates a new Group under this profile and returns the group id an a precomputed // invite which can be sent on the wire. func (p *Profile) StartGroup(server string) (groupID string, invite []byte, err error) { + return p.StartGroupWithMessage(server, []byte{}) +} + +//StartGroupWithMessage when given a server, and an initial message creates a new Group under this profile and returns the group id an a precomputed +// invite which can be sent on the wire. +func (p *Profile) StartGroupWithMessage(server string, initialMessage []byte) (groupID string, invite []byte, err error) { group := NewGroup(server) groupID = group.GroupID signedGroupID := p.SignMessage(groupID + server) group.SignGroup(signedGroupID) - invite, err = group.Invite() + invite, err = group.Invite(initialMessage) p.lock.Lock() defer p.lock.Unlock() p.Groups[group.GroupID] = group @@ -225,6 +231,7 @@ func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname stri group.SignedGroupID = gci.GetSignedGroupId() copy(group.GroupKey[:], gci.GetGroupSharedKey()[:]) group.GroupServer = gci.GetServerHost() + group.InitialMessage = gci.GetInitialMessage()[:] group.Accepted = false group.Owner = peerHostname p.AddGroup(group) diff --git a/model/profile_test.go b/model/profile_test.go index 62432aa..4c3c35a 100644 --- a/model/profile_test.go +++ b/model/profile_test.go @@ -94,7 +94,7 @@ func TestProfileGroup(t *testing.T) { sarah.AddContact(alice.Onion, &alice.PublicProfile) alice.AddContact(sarah.Onion, &sarah.PublicProfile) - gid, invite, _ := alice.StartGroup("aaa.onion") + gid, invite, _ := alice.StartGroupWithMessage("aaa.onion", []byte("Hello World")) gci := &protocol.CwtchPeerPacket{} proto.Unmarshal(invite, gci) sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion) @@ -115,6 +115,12 @@ func TestProfileGroup(t *testing.T) { c2, s2, _ := sarah.EncryptMessageToGroup("Hello World", group2.GroupID) alice.AttemptDecryption(c2, s2) + sarahGroup := sarah.GetGroupByGroupID(group.GroupID) + im := sarahGroup.GetInitialMessage() + if string(im) != "Hello World" { + t.Errorf("Initial Message was not stored properly: %v", im) + } + bob := GenerateNewProfile("bob") bob.AddContact(alice.Onion, &alice.PublicProfile) bob.ProcessInvite(gci2.GetGroupChatInvite(), alice.Onion) diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index 5aaab02..3c8f8e8 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -224,7 +224,7 @@ func (cp *cwtchPeer) ImportGroup(exportedInvite string) (groupID string, err err func (cp *cwtchPeer) ExportGroup(groupID string) (string, error) { group := cp.Profile.GetGroupByGroupID(groupID) if group != nil { - invite, err := group.Invite() + invite, err := group.Invite([]byte{}) if err == nil { exportedInvite := "torv2" + cp.Profile.Onion + base64.StdEncoding.EncodeToString(cp.Profile.Ed25519PublicKey) + base64.StdEncoding.EncodeToString(invite) return exportedInvite, err @@ -238,6 +238,11 @@ func (cp *cwtchPeer) StartGroup(server string) (string, []byte, error) { return cp.Profile.StartGroup(server) } +// StartGroupWithMessage create a new group linked to the given server and returns the group ID, an invite or an error. +func (cp *cwtchPeer) StartGroupWithMessage(server string, initialMessage []byte) (string, []byte, error) { + return cp.Profile.StartGroupWithMessage(server, initialMessage) +} + // GetGroups returns an unordered list of all group IDs. func (cp *cwtchPeer) GetGroups() []string { return cp.Profile.GetGroups() @@ -276,7 +281,7 @@ func (cp *cwtchPeer) InviteOnionToGroup(onion string, groupid string) error { group := cp.Profile.GetGroupByGroupID(groupid) if group != nil { log.Printf("Constructing invite for group: %v\n", group) - invite, err := group.Invite() + invite, err := group.Invite(group.GetInitialMessage()) if err != nil { return err } diff --git a/protocol/cwtch-profile.pb.go b/protocol/cwtch-profile.pb.go index 01524cf..b78b7b2 100644 --- a/protocol/cwtch-profile.pb.go +++ b/protocol/cwtch-profile.pb.go @@ -1,6 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: cwtch-profile.proto +/* +Package protocol is a generated protocol buffer package. + +It is generated from these files: + cwtch-profile.proto + +It has these top-level messages: + CwtchPeerPacket + CwtchIdentity + GroupChatInvite +*/ package protocol import proto "github.com/golang/protobuf/proto" @@ -12,6 +23,12 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + type CwtchPeerPacket struct { CwtchIdentify *CwtchIdentity `protobuf:"bytes,1,opt,name=cwtch_identify,json=cwtchIdentify" json:"cwtch_identify,omitempty"` GroupChatInvite *GroupChatInvite `protobuf:"bytes,2,opt,name=group_chat_invite,json=groupChatInvite" json:"group_chat_invite,omitempty"` @@ -20,7 +37,7 @@ type CwtchPeerPacket struct { func (m *CwtchPeerPacket) Reset() { *m = CwtchPeerPacket{} } func (m *CwtchPeerPacket) String() string { return proto.CompactTextString(m) } func (*CwtchPeerPacket) ProtoMessage() {} -func (*CwtchPeerPacket) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} } +func (*CwtchPeerPacket) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (m *CwtchPeerPacket) GetCwtchIdentify() *CwtchIdentity { if m != nil { @@ -44,7 +61,7 @@ type CwtchIdentity struct { func (m *CwtchIdentity) Reset() { *m = CwtchIdentity{} } func (m *CwtchIdentity) String() string { return proto.CompactTextString(m) } func (*CwtchIdentity) ProtoMessage() {} -func (*CwtchIdentity) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} } +func (*CwtchIdentity) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } func (m *CwtchIdentity) GetName() string { if m != nil { @@ -66,12 +83,13 @@ type GroupChatInvite struct { GroupSharedKey []byte `protobuf:"bytes,2,opt,name=group_shared_key,json=groupSharedKey,proto3" json:"group_shared_key,omitempty"` ServerHost string `protobuf:"bytes,3,opt,name=server_host,json=serverHost" json:"server_host,omitempty"` SignedGroupId []byte `protobuf:"bytes,4,opt,name=signed_group_id,json=signedGroupId,proto3" json:"signed_group_id,omitempty"` + InitialMessage []byte `protobuf:"bytes,5,opt,name=initial_message,json=initialMessage,proto3" json:"initial_message,omitempty"` } func (m *GroupChatInvite) Reset() { *m = GroupChatInvite{} } func (m *GroupChatInvite) String() string { return proto.CompactTextString(m) } func (*GroupChatInvite) ProtoMessage() {} -func (*GroupChatInvite) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} } +func (*GroupChatInvite) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } func (m *GroupChatInvite) GetGroupName() string { if m != nil { @@ -101,33 +119,17 @@ func (m *GroupChatInvite) GetSignedGroupId() []byte { return nil } +func (m *GroupChatInvite) GetInitialMessage() []byte { + if m != nil { + return m.InitialMessage + } + return nil +} + func init() { proto.RegisterType((*CwtchPeerPacket)(nil), "protocol.CwtchPeerPacket") proto.RegisterType((*CwtchIdentity)(nil), "protocol.CwtchIdentity") proto.RegisterType((*GroupChatInvite)(nil), "protocol.GroupChatInvite") } -func init() { proto.RegisterFile("cwtch-profile.proto", fileDescriptor1) } - -var fileDescriptor1 = []byte{ - // 299 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x90, 0xc1, 0x4a, 0xf3, 0x40, - 0x14, 0x85, 0xc9, 0xff, 0x17, 0xb1, 0xb7, 0xb6, 0xa9, 0xe3, 0xc2, 0xb8, 0x10, 0xa5, 0x0b, 0xe9, - 0x42, 0x0b, 0x56, 0xba, 0x70, 0xe3, 0xa6, 0x88, 0x86, 0x82, 0xc4, 0xf8, 0x00, 0x43, 0x3a, 0x73, - 0x93, 0x0c, 0x8d, 0x99, 0x30, 0x99, 0x56, 0xe6, 0x4d, 0xdc, 0xfb, 0xa2, 0x92, 0x1b, 0xa5, 0xad, - 0xab, 0x99, 0x39, 0xe7, 0xde, 0xef, 0x1c, 0x06, 0x4e, 0xc4, 0x87, 0x15, 0xf9, 0x4d, 0x65, 0x74, - 0xaa, 0x0a, 0x9c, 0x54, 0x46, 0x5b, 0xcd, 0x0e, 0xe9, 0x10, 0xba, 0x18, 0x7d, 0x7a, 0xe0, 0xcf, - 0x9b, 0x89, 0x08, 0xd1, 0x44, 0x89, 0x58, 0xa1, 0x65, 0x0f, 0x30, 0xa0, 0x25, 0xae, 0x24, 0x96, - 0x56, 0xa5, 0x2e, 0xf0, 0x2e, 0xbd, 0x71, 0x6f, 0x7a, 0x3a, 0xf9, 0x5d, 0x9b, 0xd0, 0x4a, 0x48, - 0xb6, 0x75, 0x71, 0x5f, 0x6c, 0x9f, 0xa9, 0x63, 0x8f, 0x70, 0x9c, 0x19, 0xbd, 0xae, 0xb8, 0xc8, - 0x13, 0xcb, 0x55, 0xb9, 0x51, 0x16, 0x83, 0x7f, 0x84, 0x38, 0xdb, 0x22, 0x9e, 0x9a, 0x91, 0x79, - 0x9e, 0xd8, 0x90, 0x06, 0x62, 0x3f, 0xdb, 0x17, 0x46, 0xaf, 0xd0, 0xdf, 0x8b, 0x61, 0x0c, 0x3a, - 0x65, 0xf2, 0x8e, 0xd4, 0xa6, 0x1b, 0xd3, 0x9d, 0x5d, 0x03, 0x43, 0x39, 0x9d, 0xcd, 0x6e, 0xef, - 0x79, 0xb5, 0x5e, 0x16, 0x4a, 0xf0, 0x15, 0x3a, 0x0a, 0x3b, 0x8a, 0x87, 0x3f, 0x4e, 0x44, 0xc6, - 0x02, 0xdd, 0xe8, 0xcb, 0x03, 0xff, 0x4f, 0x2e, 0x3b, 0x07, 0x68, 0xdb, 0xee, 0xb0, 0xbb, 0xa4, - 0xbc, 0x34, 0x01, 0x63, 0x18, 0xb6, 0x76, 0x9d, 0x27, 0x06, 0xe5, 0x0e, 0x7e, 0x40, 0xfa, 0x1b, - 0xc9, 0x0b, 0x74, 0xec, 0x02, 0x7a, 0x35, 0x9a, 0x0d, 0x1a, 0x9e, 0xeb, 0xda, 0x06, 0xff, 0x89, - 0x04, 0xad, 0xf4, 0xac, 0x6b, 0xcb, 0xae, 0xc0, 0xaf, 0x55, 0x56, 0xa2, 0xe4, 0x2d, 0x51, 0xc9, - 0xa0, 0x43, 0xa4, 0x7e, 0x2b, 0x53, 0xb3, 0x50, 0x2e, 0x0f, 0xe8, 0x8f, 0xee, 0xbe, 0x03, 0x00, - 0x00, 0xff, 0xff, 0x62, 0x61, 0x2d, 0x00, 0xbb, 0x01, 0x00, 0x00, -} +func init() { proto.RegisterFile("cwtch-profile.proto", fileDescriptor0) } diff --git a/protocol/cwtch-profile.proto b/protocol/cwtch-profile.proto index dd67e06..3de2ef5 100644 --- a/protocol/cwtch-profile.proto +++ b/protocol/cwtch-profile.proto @@ -17,4 +17,5 @@ message GroupChatInvite { bytes group_shared_key = 2; string server_host = 3; bytes signed_group_id = 4; + bytes initial_message = 5; }