Fixing #118 - Adding an initial message to group setup

This commit is contained in:
Sarah Jamie Lewis 2018-09-21 11:02:46 -07:00
parent 291f717e7e
commit d37447f320
6 changed files with 73 additions and 41 deletions

View File

@ -16,15 +16,16 @@ import (
//Group defines and encapsulates Cwtch's conception of group chat. Which are sessions //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. // tied to a server under a given group key. Each group has a set of messages.
type Group struct { type Group struct {
GroupID string GroupID string
SignedGroupID []byte SignedGroupID []byte
GroupKey [32]byte GroupKey [32]byte
GroupServer string GroupServer string
Timeline Timeline Timeline Timeline
Accepted bool Accepted bool
Owner string Owner string
IsCompromised bool IsCompromised bool
lock sync.Mutex InitialMessage []byte
lock sync.Mutex
} }
// NewGroup initializes a new group associated with a given CwtchServer // NewGroup initializes a new group associated with a given CwtchServer
@ -58,18 +59,28 @@ func (g *Group) Compromised() {
g.IsCompromised = true 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 // 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 { if g.SignedGroupID == nil {
return nil, errors.New("group isn't signed") return nil, errors.New("group isn't signed")
} }
g.InitialMessage = initialMessage[:]
gci := &protocol.GroupChatInvite{ gci := &protocol.GroupChatInvite{
GroupName: g.GroupID, GroupName: g.GroupID,
GroupSharedKey: g.GroupKey[:], GroupSharedKey: g.GroupKey[:],
ServerHost: g.GroupServer, ServerHost: g.GroupServer,
SignedGroupId: g.SignedGroupID[:], SignedGroupId: g.SignedGroupID[:],
InitialMessage: initialMessage[:],
} }
cp := &protocol.CwtchPeerPacket{ cp := &protocol.CwtchPeerPacket{

View File

@ -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 //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. // invite which can be sent on the wire.
func (p *Profile) StartGroup(server string) (groupID string, invite []byte, err error) { 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) group := NewGroup(server)
groupID = group.GroupID groupID = group.GroupID
signedGroupID := p.SignMessage(groupID + server) signedGroupID := p.SignMessage(groupID + server)
group.SignGroup(signedGroupID) group.SignGroup(signedGroupID)
invite, err = group.Invite() invite, err = group.Invite(initialMessage)
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
p.Groups[group.GroupID] = group p.Groups[group.GroupID] = group
@ -225,6 +231,7 @@ func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname stri
group.SignedGroupID = gci.GetSignedGroupId() group.SignedGroupID = gci.GetSignedGroupId()
copy(group.GroupKey[:], gci.GetGroupSharedKey()[:]) copy(group.GroupKey[:], gci.GetGroupSharedKey()[:])
group.GroupServer = gci.GetServerHost() group.GroupServer = gci.GetServerHost()
group.InitialMessage = gci.GetInitialMessage()[:]
group.Accepted = false group.Accepted = false
group.Owner = peerHostname group.Owner = peerHostname
p.AddGroup(group) p.AddGroup(group)

View File

@ -94,7 +94,7 @@ func TestProfileGroup(t *testing.T) {
sarah.AddContact(alice.Onion, &alice.PublicProfile) sarah.AddContact(alice.Onion, &alice.PublicProfile)
alice.AddContact(sarah.Onion, &sarah.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{} gci := &protocol.CwtchPeerPacket{}
proto.Unmarshal(invite, gci) proto.Unmarshal(invite, gci)
sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion) sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion)
@ -115,6 +115,12 @@ func TestProfileGroup(t *testing.T) {
c2, s2, _ := sarah.EncryptMessageToGroup("Hello World", group2.GroupID) c2, s2, _ := sarah.EncryptMessageToGroup("Hello World", group2.GroupID)
alice.AttemptDecryption(c2, s2) 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 := GenerateNewProfile("bob")
bob.AddContact(alice.Onion, &alice.PublicProfile) bob.AddContact(alice.Onion, &alice.PublicProfile)
bob.ProcessInvite(gci2.GetGroupChatInvite(), alice.Onion) bob.ProcessInvite(gci2.GetGroupChatInvite(), alice.Onion)

View File

@ -224,7 +224,7 @@ func (cp *cwtchPeer) ImportGroup(exportedInvite string) (groupID string, err err
func (cp *cwtchPeer) ExportGroup(groupID string) (string, error) { func (cp *cwtchPeer) ExportGroup(groupID string) (string, error) {
group := cp.Profile.GetGroupByGroupID(groupID) group := cp.Profile.GetGroupByGroupID(groupID)
if group != nil { if group != nil {
invite, err := group.Invite() invite, err := group.Invite([]byte{})
if err == nil { if err == nil {
exportedInvite := "torv2" + cp.Profile.Onion + base64.StdEncoding.EncodeToString(cp.Profile.Ed25519PublicKey) + base64.StdEncoding.EncodeToString(invite) exportedInvite := "torv2" + cp.Profile.Onion + base64.StdEncoding.EncodeToString(cp.Profile.Ed25519PublicKey) + base64.StdEncoding.EncodeToString(invite)
return exportedInvite, err return exportedInvite, err
@ -238,6 +238,11 @@ func (cp *cwtchPeer) StartGroup(server string) (string, []byte, error) {
return cp.Profile.StartGroup(server) 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. // GetGroups returns an unordered list of all group IDs.
func (cp *cwtchPeer) GetGroups() []string { func (cp *cwtchPeer) GetGroups() []string {
return cp.Profile.GetGroups() return cp.Profile.GetGroups()
@ -276,7 +281,7 @@ func (cp *cwtchPeer) InviteOnionToGroup(onion string, groupid string) error {
group := cp.Profile.GetGroupByGroupID(groupid) group := cp.Profile.GetGroupByGroupID(groupid)
if group != nil { if group != nil {
log.Printf("Constructing invite for group: %v\n", group) log.Printf("Constructing invite for group: %v\n", group)
invite, err := group.Invite() invite, err := group.Invite(group.GetInitialMessage())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,6 +1,17 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: cwtch-profile.proto // 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 package protocol
import proto "github.com/golang/protobuf/proto" import proto "github.com/golang/protobuf/proto"
@ -12,6 +23,12 @@ var _ = proto.Marshal
var _ = fmt.Errorf var _ = fmt.Errorf
var _ = math.Inf 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 { type CwtchPeerPacket struct {
CwtchIdentify *CwtchIdentity `protobuf:"bytes,1,opt,name=cwtch_identify,json=cwtchIdentify" json:"cwtch_identify,omitempty"` 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"` 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) Reset() { *m = CwtchPeerPacket{} }
func (m *CwtchPeerPacket) String() string { return proto.CompactTextString(m) } func (m *CwtchPeerPacket) String() string { return proto.CompactTextString(m) }
func (*CwtchPeerPacket) ProtoMessage() {} 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 { func (m *CwtchPeerPacket) GetCwtchIdentify() *CwtchIdentity {
if m != nil { if m != nil {
@ -44,7 +61,7 @@ type CwtchIdentity struct {
func (m *CwtchIdentity) Reset() { *m = CwtchIdentity{} } func (m *CwtchIdentity) Reset() { *m = CwtchIdentity{} }
func (m *CwtchIdentity) String() string { return proto.CompactTextString(m) } func (m *CwtchIdentity) String() string { return proto.CompactTextString(m) }
func (*CwtchIdentity) ProtoMessage() {} 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 { func (m *CwtchIdentity) GetName() string {
if m != nil { 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"` 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"` 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"` 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) Reset() { *m = GroupChatInvite{} }
func (m *GroupChatInvite) String() string { return proto.CompactTextString(m) } func (m *GroupChatInvite) String() string { return proto.CompactTextString(m) }
func (*GroupChatInvite) ProtoMessage() {} 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 { func (m *GroupChatInvite) GetGroupName() string {
if m != nil { if m != nil {
@ -101,33 +119,17 @@ func (m *GroupChatInvite) GetSignedGroupId() []byte {
return nil return nil
} }
func (m *GroupChatInvite) GetInitialMessage() []byte {
if m != nil {
return m.InitialMessage
}
return nil
}
func init() { func init() {
proto.RegisterType((*CwtchPeerPacket)(nil), "protocol.CwtchPeerPacket") proto.RegisterType((*CwtchPeerPacket)(nil), "protocol.CwtchPeerPacket")
proto.RegisterType((*CwtchIdentity)(nil), "protocol.CwtchIdentity") proto.RegisterType((*CwtchIdentity)(nil), "protocol.CwtchIdentity")
proto.RegisterType((*GroupChatInvite)(nil), "protocol.GroupChatInvite") proto.RegisterType((*GroupChatInvite)(nil), "protocol.GroupChatInvite")
} }
func init() { proto.RegisterFile("cwtch-profile.proto", fileDescriptor1) } func init() { proto.RegisterFile("cwtch-profile.proto", fileDescriptor0) }
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,
}

View File

@ -17,4 +17,5 @@ message GroupChatInvite {
bytes group_shared_key = 2; bytes group_shared_key = 2;
string server_host = 3; string server_host = 3;
bytes signed_group_id = 4; bytes signed_group_id = 4;
bytes initial_message = 5;
} }