Groups Cleanup

This commit is contained in:
Sarah Jamie Lewis 2021-05-03 16:32:48 -07:00
parent 361d7befd1
commit bea58b5ba4
20 changed files with 128 additions and 244 deletions

View File

@ -69,7 +69,7 @@ func (ap *appletPeers) ListPeers() map[string]string {
ap.peerLock.Lock() ap.peerLock.Lock()
defer ap.peerLock.Unlock() defer ap.peerLock.Unlock()
for k, p := range ap.peers { for k, p := range ap.peers {
keys[k] = p.GetName() keys[k] = p.GetOnion()
} }
return keys return keys
} }

View File

@ -2,6 +2,7 @@ package utils
import ( import (
app2 "cwtch.im/cwtch/app" app2 "cwtch.im/cwtch/app"
"cwtch.im/cwtch/model/attr"
"cwtch.im/cwtch/peer" "cwtch.im/cwtch/peer"
"time" "time"
) )
@ -12,9 +13,11 @@ import (
// may fill that usecase better // may fill that usecase better
func WaitGetPeer(app app2.Application, name string) peer.CwtchPeer { func WaitGetPeer(app app2.Application, name string) peer.CwtchPeer {
for true { for true {
for id, n := range app.ListPeers() { for id := range app.ListPeers() {
if n == name { peer := app.GetPeer(id)
return app.GetPeer(id) localName, _ := peer.GetAttribute(attr.GetLocalScope("name"))
if localName == name {
return peer
} }
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)

View File

@ -13,8 +13,6 @@ import (
"sync" "sync"
) )
// Event is the core struct type passed around between various subsystems. Events consist of a type which can be // Event is the core struct type passed around between various subsystems. Events consist of a type which can be
// filtered on, an event ID for tracing and a map of Fields to string values. // filtered on, an event ID for tracing and a map of Fields to string values.
type Event struct { type Event struct {
@ -60,7 +58,7 @@ type manager struct {
mapMutex sync.Mutex mapMutex sync.Mutex
internal chan bool internal chan bool
closed bool closed bool
trace bool trace bool
} }
// Manager is an interface for an event bus // Manager is an interface for an event bus
@ -86,7 +84,6 @@ func (em *manager) initialize() {
em.internal = make(chan bool) em.internal = make(chan bool)
em.closed = false em.closed = false
_, em.trace = os.LookupEnv("CWTCH_EVENT_SOURCE") _, em.trace = os.LookupEnv("CWTCH_EVENT_SOURCE")
go em.eventBus() go em.eventBus()

View File

@ -4,6 +4,7 @@ import (
"crypto/rand" "crypto/rand"
"cwtch.im/cwtch/model/attr" "cwtch.im/cwtch/model/attr"
"cwtch.im/cwtch/protocol/groups" "cwtch.im/cwtch/protocol/groups"
"encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -27,9 +28,7 @@ type Group struct {
GroupServer string GroupServer string
Timeline Timeline `json:"-"` Timeline Timeline `json:"-"`
Accepted bool Accepted bool
Owner string
IsCompromised bool IsCompromised bool
InitialMessage []byte
Attributes map[string]string Attributes map[string]string
lock sync.Mutex lock sync.Mutex
LocalID string LocalID string
@ -63,7 +62,6 @@ func NewGroup(server string) (*Group, error) {
return nil, err return nil, err
} }
copy(group.GroupKey[:], groupKey[:]) copy(group.GroupKey[:], groupKey[:])
group.Owner = "self"
group.Attributes = make(map[string]string) group.Attributes = make(map[string]string)
// By default we set the "name" of the group to a random string, we can override this later, but to simplify the // By default we set the "name" of the group to a random string, we can override this later, but to simplify the
// codes around invite, we assume that this is always set. // codes around invite, we assume that this is always set.
@ -82,33 +80,24 @@ 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(initialMessage []byte) ([]byte, error) { func (g *Group) Invite() (string, error) {
if g.SignedGroupID == nil { if g.SignedGroupID == nil {
return nil, errors.New("group isn't signed") return "", errors.New("group isn't signed")
} }
g.InitialMessage = initialMessage[:]
gci := &groups.GroupInvite{ gci := &groups.GroupInvite{
GroupID: g.GroupID, GroupID: g.GroupID,
GroupName: g.Attributes[attr.GetLocalScope("name")], GroupName: g.Attributes[attr.GetLocalScope("name")],
SharedKey: g.GroupKey[:], SharedKey: g.GroupKey[:],
ServerHost: g.GroupServer, ServerHost: g.GroupServer,
SignedGroupID: g.SignedGroupID[:], SignedGroupID: g.SignedGroupID[:],
InitialMessage: initialMessage[:],
} }
invite, err := json.Marshal(gci) invite, err := json.Marshal(gci)
return invite, err serializedInvite := fmt.Sprintf("%v\n", "torv3"+base64.StdEncoding.EncodeToString(invite))
return serializedInvite, err
} }
// AddSentMessage takes a DecryptedGroupMessage and adds it to the Groups Timeline // AddSentMessage takes a DecryptedGroupMessage and adds it to the Groups Timeline

View File

@ -16,7 +16,7 @@ func TestMessagePadding(t *testing.T) {
gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd") gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
sarah.ProcessInvite(string(invite), alice.Onion) sarah.ProcessInvite(invite)
group := alice.GetGroup(gid) group := alice.GetGroup(gid)
@ -49,7 +49,7 @@ func TestTranscriptConsistency(t *testing.T) {
gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd") gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
sarah.ProcessInvite(string(invite), alice.Onion) sarah.ProcessInvite(invite)
group := alice.GetGroup(gid) group := alice.GetGroup(gid)

View File

@ -4,6 +4,7 @@ import (
"crypto/rand" "crypto/rand"
"cwtch.im/cwtch/protocol/groups" "cwtch.im/cwtch/protocol/groups"
"encoding/base32" "encoding/base32"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
@ -333,22 +334,15 @@ 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 string, 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, err := NewGroup(server) group, err := NewGroup(server)
if err != nil { if err != nil {
return "", nil, err return "", "", err
} }
groupID = group.GroupID groupID = group.GroupID
group.Owner = p.Onion
signedGroupID := p.SignMessage(groupID + server) signedGroupID := p.SignMessage(groupID + server)
group.SignGroup(signedGroupID) group.SignGroup(signedGroupID)
invite, err = group.Invite(initialMessage) invite, err = group.Invite()
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
p.Groups[group.GroupID] = group p.Groups[group.GroupID] = group
@ -364,33 +358,36 @@ func (p *Profile) GetGroup(groupID string) (g *Group) {
} }
// ProcessInvite adds a new group invite to the profile. returns the new group ID // ProcessInvite adds a new group invite to the profile. returns the new group ID
func (p *Profile) ProcessInvite(invite string, peerHostname string) (string, string, error) { func (p *Profile) ProcessInvite(invite string) (string, error) {
var gci groups.GroupInvite if strings.HasPrefix(invite, "torv3") {
err := json.Unmarshal([]byte(invite), &gci) data, err := base64.StdEncoding.DecodeString(invite[5:])
if err == nil { if err == nil {
group := new(Group) var gci groups.GroupInvite
group.Version = CurrentGroupVersion err := json.Unmarshal(data, &gci)
group.GroupID = gci.GroupID if err == nil {
group.LocalID = GenerateRandomID() group := new(Group)
group.SignedGroupID = gci.SignedGroupID group.Version = CurrentGroupVersion
copy(group.GroupKey[:], gci.SharedKey[:]) group.GroupID = gci.GroupID
group.GroupServer = gci.ServerHost group.LocalID = GenerateRandomID()
group.InitialMessage = []byte(gci.InitialMessage) group.SignedGroupID = gci.SignedGroupID
group.Accepted = false copy(group.GroupKey[:], gci.SharedKey[:])
group.Owner = peerHostname group.GroupServer = gci.ServerHost
group.Attributes = make(map[string]string) group.Accepted = false
p.AddGroup(group) group.Attributes = make(map[string]string)
return group.GroupID, gci.GroupName, nil p.AddGroup(group)
return group.GroupID, nil
}
}
} }
return "", "", err return "", errors.New("unsupported exported group type")
} }
// AddGroup is a convenience method for adding a group to a profile. // AddGroup is a convenience method for adding a group to a profile.
func (p *Profile) AddGroup(group *Group) { func (p *Profile) AddGroup(group *Group) {
p.lock.Lock()
defer p.lock.Unlock()
_, exists := p.Groups[group.GroupID] _, exists := p.Groups[group.GroupID]
if !exists { if !exists {
p.lock.Lock()
defer p.lock.Unlock()
p.Groups[group.GroupID] = group p.Groups[group.GroupID] = group
} }
} }

View File

@ -64,7 +64,7 @@ func TestRejectGroupInvite(t *testing.T) {
alice.AddContact(sarah.Onion, &sarah.PublicProfile) alice.AddContact(sarah.Onion, &sarah.PublicProfile)
gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd") gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
sarah.ProcessInvite(string(invite), alice.Onion) sarah.ProcessInvite(invite)
group := alice.GetGroup(gid) group := alice.GetGroup(gid)
if len(sarah.Groups) == 1 { if len(sarah.Groups) == 1 {
if sarah.GetGroup(group.GroupID).Accepted { if sarah.GetGroup(group.GroupID).Accepted {
@ -85,8 +85,8 @@ 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.StartGroupWithMessage("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd", []byte("Hello World")) gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
sarah.ProcessInvite(string(invite), alice.Onion) sarah.ProcessInvite(invite)
if len(sarah.GetGroups()) != 1 { if len(sarah.GetGroups()) != 1 {
t.Errorf("sarah should only be in 1 group instead: %v", sarah.GetGroups()) t.Errorf("sarah should only be in 1 group instead: %v", sarah.GetGroups())
} }
@ -97,17 +97,11 @@ func TestProfileGroup(t *testing.T) {
alice.AttemptDecryption(c, s1) alice.AttemptDecryption(c, s1)
gid2, invite2, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd") gid2, invite2, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
sarah.ProcessInvite(string(invite2), alice.Onion) sarah.ProcessInvite(invite2)
group2 := alice.GetGroup(gid2) group2 := alice.GetGroup(gid2)
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.GetGroup(group.GroupID)
im := sarahGroup.GetInitialMessage()
if string(im) != "Hello World" {
t.Errorf("Initial Message was not stored properly: %v", im)
}
_, _, err := sarah.EncryptMessageToGroup(string(make([]byte, MaxGroupMessageLength*2)), group2.GroupID) _, _, err := sarah.EncryptMessageToGroup(string(make([]byte, MaxGroupMessageLength*2)), group2.GroupID)
if err == nil { if err == nil {
t.Errorf("Overly long message should have returned an error") t.Errorf("Overly long message should have returned an error")
@ -115,7 +109,7 @@ func TestProfileGroup(t *testing.T) {
bob := GenerateNewProfile("bob") bob := GenerateNewProfile("bob")
bob.AddContact(alice.Onion, &alice.PublicProfile) bob.AddContact(alice.Onion, &alice.PublicProfile)
bob.ProcessInvite(string(invite2), alice.Onion) bob.ProcessInvite(invite2)
c3, s3, err := bob.EncryptMessageToGroup("Bobs Message", group2.GroupID) c3, s3, err := bob.EncryptMessageToGroup("Bobs Message", group2.GroupID)
if err == nil { if err == nil {
ok, _, message, _ := alice.AttemptDecryption(c3, s3) ok, _, message, _ := alice.AttemptDecryption(c3, s3)

View File

@ -109,8 +109,8 @@ type ReadGroups interface {
// ModifyGroups provides write-only access add/edit/remove new groups // ModifyGroups provides write-only access add/edit/remove new groups
type ModifyGroups interface { type ModifyGroups interface {
ImportGroup(string) error ImportGroup(string) (string, error)
StartGroup(string) (string, []byte, error) StartGroup(string) (string, string, error)
AcceptInvite(string) error AcceptInvite(string) error
RejectInvite(string) RejectInvite(string)
DeleteGroup(string) DeleteGroup(string)
@ -172,14 +172,6 @@ type CwtchPeer interface {
SendMessages SendMessages
SendMessagesToGroup SendMessagesToGroup
// Deprecated
// TODO Should be removed
GetName() string
SetName(string)
// TODO Should not be exposed...
ProcessInvite(string, string) (string, string, error)
} }
// NewCwtchPeer creates and returns a new cwtchPeer with the given name. // NewCwtchPeer creates and returns a new cwtchPeer with the given name.
@ -223,20 +215,16 @@ func (cp *cwtchPeer) AutoHandleEvents(events []event.Type) {
} }
// ImportGroup initializes a group from an imported source rather than a peer invite // ImportGroup initializes a group from an imported source rather than a peer invite
func (cp *cwtchPeer) ImportGroup(exportedInvite string) (err error) { func (cp *cwtchPeer) ImportGroup(exportedInvite string) (string, error) {
if strings.HasPrefix(exportedInvite, "torv3") { cp.mutex.Lock()
data, err := base64.StdEncoding.DecodeString(exportedInvite[5:]) defer cp.mutex.Unlock()
if err == nil { gid, err := cp.Profile.ProcessInvite(exportedInvite)
cp.eventBus.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{
event.GroupInvite: string(data), if err == nil {
event.Imported: "true", cp.eventBus.Publish(event.NewEvent(event.NewGroup, map[event.Field]string{event.GroupID: gid, event.GroupInvite: exportedInvite}))
}))
} else {
log.Errorf("error decoding group invite: %v", err)
}
return nil
} }
return errors.New("unsupported exported group type")
return gid, err
} }
// ExportGroup serializes a group invite so it can be given offline // ExportGroup serializes a group invite so it can be given offline
@ -245,24 +233,15 @@ func (cp *cwtchPeer) ExportGroup(groupID string) (string, error) {
defer cp.mutex.Unlock() defer cp.mutex.Unlock()
group := cp.Profile.GetGroup(groupID) group := cp.Profile.GetGroup(groupID)
if group != nil { if group != nil {
invite, err := group.Invite(group.GetInitialMessage()) return group.Invite()
if err == nil {
exportedInvite := "torv3" + base64.StdEncoding.EncodeToString(invite)
return exportedInvite, err
}
} }
return "", errors.New("group id could not be found") return "", errors.New("group id could not be found")
} }
// StartGroup create a new group linked to the given server and returns the group ID, an invite or an error. // StartGroup create a new group linked to the given server and returns the group ID, an invite or an error.
func (cp *cwtchPeer) StartGroup(server string) (string, []byte, error) { func (cp *cwtchPeer) StartGroup(server string) (string, string, error) {
return cp.StartGroupWithMessage(server, []byte{})
}
// 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) (groupID string, invite []byte, err error) {
cp.mutex.Lock() cp.mutex.Lock()
groupID, invite, err = cp.Profile.StartGroupWithMessage(server, initialMessage) groupID, invite, err := cp.Profile.StartGroup(server)
cp.mutex.Unlock() cp.mutex.Unlock()
if err == nil { if err == nil {
group := cp.GetGroup(groupID) group := cp.GetGroup(groupID)
@ -275,7 +254,7 @@ func (cp *cwtchPeer) StartGroupWithMessage(server string, initialMessage []byte)
} else { } else {
log.Errorf("error creating group: %v", err) log.Errorf("error creating group: %v", err)
} }
return return groupID, invite, err
} }
// GetGroups returns an unordered list of all group IDs. // GetGroups returns an unordered list of all group IDs.
@ -400,18 +379,6 @@ func (cp *cwtchPeer) GetContact(onion string) *model.PublicProfile {
return contact return contact
} }
func (cp *cwtchPeer) GetName() string {
cp.mutex.Lock()
defer cp.mutex.Unlock()
return cp.Profile.Name
}
func (cp *cwtchPeer) SetName(newName string) {
cp.mutex.Lock()
defer cp.mutex.Unlock()
cp.Profile.Name = newName
}
func (cp *cwtchPeer) GetOnion() string { func (cp *cwtchPeer) GetOnion() string {
cp.mutex.Lock() cp.mutex.Lock()
defer cp.mutex.Unlock() defer cp.mutex.Unlock()
@ -465,14 +432,13 @@ func (cp *cwtchPeer) DeleteGroup(groupID string) {
func (cp *cwtchPeer) InviteOnionToGroup(onion string, groupid string) error { func (cp *cwtchPeer) InviteOnionToGroup(onion string, groupid string) error {
cp.mutex.Lock() cp.mutex.Lock()
group := cp.Profile.GetGroup(groupid) group := cp.Profile.GetGroup(groupid)
defer cp.mutex.Unlock()
if group == nil { if group == nil {
return errors.New("invalid group id") return errors.New("invalid group id")
} }
invite, err := group.Invite()
invite, err := group.Invite(group.InitialMessage) cp.mutex.Unlock()
if err == nil { if err == nil {
cp.eventBus.Publish(event.NewEvent(event.InvitePeerToGroup, map[event.Field]string{event.RemotePeer: onion, event.GroupInvite: string(invite)})) cp.SendMessageToPeer(onion, invite)
} }
return err return err
} }
@ -537,13 +503,6 @@ func (cp *cwtchPeer) SetContactAuthorization(peer string, authorization model.Au
return err return err
} }
// ProcessInvite adds a new group invite to the profile. returns the new group ID
func (cp *cwtchPeer) ProcessInvite(invite string, remotePeer string) (string, string, error) {
cp.mutex.Lock()
defer cp.mutex.Unlock()
return cp.Profile.ProcessInvite(invite, remotePeer)
}
// AcceptInvite accepts a given existing group invite // AcceptInvite accepts a given existing group invite
func (cp *cwtchPeer) AcceptInvite(groupID string) error { func (cp *cwtchPeer) AcceptInvite(groupID string) error {
cp.mutex.Lock() cp.mutex.Lock()
@ -615,6 +574,11 @@ func (cp *cwtchPeer) GetAttribute(key string) (string, bool) {
if val, exists := cp.Profile.GetAttribute(key); exists { if val, exists := cp.Profile.GetAttribute(key); exists {
return val, true return val, true
} }
if key == attr.GetLocalScope("name") {
return cp.Profile.Name, true
}
return "", false return "", false
} }
@ -772,23 +736,6 @@ func (cp *cwtchPeer) eventHandler() {
cp.SetContactAttribute(onion, attr.GetPeerScope(path), val) cp.SetContactAttribute(onion, attr.GetPeerScope(path), val)
} }
} }
case event.NewGroupInvite:
cp.mutex.Lock()
group, groupName, err := cp.Profile.ProcessInvite(ev.Data[event.GroupInvite], ev.Data[event.RemotePeer])
if err == nil {
if ev.Data[event.Imported] == "true" {
cp.Profile.GetGroup(group).Accepted = true
cp.mutex.Unlock() // TODO...seriously need a better way of handling these cases
cp.SetGroupAttribute(group, attr.GetLocalScope("name"), groupName)
err = cp.JoinServer(cp.Profile.GetGroup(group).GroupServer)
cp.mutex.Lock()
if err != nil {
log.Errorf("Joining Server should have worked %v", err)
}
}
cp.eventBus.Publish(event.NewEvent(event.NewGroup, map[event.Field]string{event.GroupID: group}))
}
cp.mutex.Unlock()
case event.PeerStateChange: case event.PeerStateChange:
cp.mutex.Lock() cp.mutex.Lock()
if _, exists := cp.Profile.Contacts[ev.Data[event.RemotePeer]]; exists { if _, exists := cp.Profile.Contacts[ev.Data[event.RemotePeer]]; exists {

View File

@ -468,10 +468,7 @@ func (e *engine) sendMessageToGroup(server string, ct []byte, sig []byte) error
func (e *engine) handlePeerMessage(hostname string, eventID string, context string, message []byte) { func (e *engine) handlePeerMessage(hostname string, eventID string, context string, message []byte) {
log.Debugf("New message from peer: %v %v", hostname, context) log.Debugf("New message from peer: %v %v", hostname, context)
if context == event.ContextInvite { if context == event.ContextGetVal {
// TODO: Convert this to an inline peer message...
// e.eventManager.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: hostname, event.GroupInvite: string(message)}))
} else if context == event.ContextGetVal {
var getVal peerGetVal var getVal peerGetVal
err := json.Unmarshal(message, &getVal) err := json.Unmarshal(message, &getVal)
if err == nil { if err == nil {

View File

@ -1,36 +0,0 @@
package groups
import (
"crypto/rand"
"cwtch.im/cwtch/model"
"golang.org/x/crypto/nacl/secretbox"
"io"
)
// Fuzz various group related functions
func Fuzz(data []byte) int {
profile := model.GenerateNewProfile("fuzz")
inviteid, _, err := profile.ProcessInvite(string(data), profile.Onion)
if err != nil {
if inviteid != "" {
panic("should not have added a group on err")
}
return 1
}
id, _, _ := profile.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
var nonce [24]byte
io.ReadFull(rand.Reader, nonce[:])
encrypted := secretbox.Seal(nonce[:], data, &nonce, &profile.GetGroup(id).GroupKey)
ok, _, _, _ := profile.AttemptDecryption(encrypted, data)
if ok {
panic("this probably shouldn't happen")
}
ok = profile.VerifyGroupMessage(string(data), string(data), string(data), 0, encrypted, data)
if ok {
panic("this probably shouldn't happen")
}
return 0
}

View File

@ -1,20 +0,0 @@
package invites
import (
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/peer"
)
// Fuzz import group function
func Fuzz(data []byte) int {
peer := peer.NewCwtchPeer("fuzz")
peer.Init(event.NewEventManager())
err := peer.ImportGroup(string(data))
if err != nil {
if len(peer.GetGroups()) > 0 {
panic("group added despite error")
}
return 0
}
return 1
}

View File

@ -11,13 +11,12 @@ const CwtchServerSyncedCapability = tapir.Capability("CwtchServerSyncedCapabilit
// GroupInvite provides a structured type for communicating group information to peers // GroupInvite provides a structured type for communicating group information to peers
type GroupInvite struct { type GroupInvite struct {
GroupID string GroupID string
GroupName string GroupName string
SignedGroupID []byte SignedGroupID []byte
Timestamp uint64 Timestamp uint64
SharedKey []byte SharedKey []byte
ServerHost string ServerHost string
InitialMessage []byte
} }
// DecryptedGroupMessage is the main encapsulation of group message data // DecryptedGroupMessage is the main encapsulation of group message data

View File

@ -21,6 +21,7 @@ const (
func main() { func main() {
log.AddEverythingFromPattern("server/app/main") log.AddEverythingFromPattern("server/app/main")
log.AddEverythingFromPattern("server/server") log.AddEverythingFromPattern("server/server")
log.SetLevel(log.LevelDebug)
configDir := os.Getenv("CWTCH_CONFIG_DIR") configDir := os.Getenv("CWTCH_CONFIG_DIR")
if len(os.Args) == 2 && os.Args[1] == "gen1" { if len(os.Args) == 2 && os.Args[1] == "gen1" {
@ -73,14 +74,17 @@ func main() {
// TODO create a random group for testing // TODO create a random group for testing
group, _ := model.NewGroup(tor.GetTorV3Hostname(serverConfig.PublicKey)) group, _ := model.NewGroup(tor.GetTorV3Hostname(serverConfig.PublicKey))
group.SignGroup([]byte{}) group.SignGroup([]byte{})
invite, err := group.Invite([]byte{}) invite, err := group.Invite()
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Printf("%v\n", "torv3"+base64.StdEncoding.EncodeToString(invite)) fmt.Printf("Invite: %v", invite)
bundle := server.KeyBundle().Serialize() bundle := server.KeyBundle().Serialize()
log.Infof("Server Config: server:%s", base64.StdEncoding.EncodeToString(bundle)) log.Infof("Server Config: server:%s", base64.StdEncoding.EncodeToString(bundle))
log.Infof("Server Tofu Bundle: tofubundle:server:%s||%s", base64.StdEncoding.EncodeToString(bundle), invite)
server.Run(acn) server.Run(acn)
for { for {
time.Sleep(time.Second) time.Sleep(time.Second)

View File

@ -39,7 +39,7 @@ type Server struct {
func (s *Server) Setup(serverConfig Config) { func (s *Server) Setup(serverConfig Config) {
s.config = serverConfig s.config = serverConfig
bs := new(persistence.BoltPersistence) bs := new(persistence.BoltPersistence)
bs.Open(path.Join(serverConfig.ConfigDir, "tokens.db")) bs.Open(path.Join(serverConfig.ConfigDir, "tokens1.db"))
s.tokenServer = privacypass.NewTokenServerFromStore(bs) s.tokenServer = privacypass.NewTokenServerFromStore(bs)
s.tokenService = s.config.TokenServiceIdentity() s.tokenService = s.config.TokenServiceIdentity()
s.tokenServicePrivKey = s.config.TokenServerPrivateKey s.tokenServicePrivKey = s.config.TokenServerPrivateKey

View File

@ -42,7 +42,7 @@ func TestProfileStoreUpgradeV0toV1(t *testing.T) {
t.Errorf("Creating group invite: %v\n", err) t.Errorf("Creating group invite: %v\n", err)
} }
ps1.AddGroup(invite, profile.Onion) ps1.AddGroup(invite)
fmt.Println("Sending 200 messages...") fmt.Println("Sending 200 messages...")

View File

@ -53,8 +53,8 @@ func ReadProfile(directory, password string) (*model.Profile, error) {
/********************************************************************************************/ /********************************************************************************************/
// AddGroup For testing, adds a group to the profile (and starts a stream store) // AddGroup For testing, adds a group to the profile (and starts a stream store)
func (ps *ProfileStoreV0) AddGroup(invite []byte, peer string) { func (ps *ProfileStoreV0) AddGroup(invite string) {
gid, _, err := ps.profile.ProcessInvite(string(invite), peer) gid, err := ps.profile.ProcessInvite(invite)
if err == nil { if err == nil {
ps.save() ps.save()
group := ps.profile.Groups[gid] group := ps.profile.Groups[gid]

View File

@ -40,7 +40,7 @@ func TestProfileStoreWriteRead(t *testing.T) {
t.Errorf("Creating group invite: %v\n", err) t.Errorf("Creating group invite: %v\n", err)
} }
ps1.AddGroup(invite, profile.Onion) ps1.AddGroup(invite)
ps1.AddGroupMessage(groupid, time.Now().Format(time.RFC3339Nano), time.Now().Format(time.RFC3339Nano), ps1.getProfileCopy(true).Onion, testMessage) ps1.AddGroupMessage(groupid, time.Now().Format(time.RFC3339Nano), time.Now().Format(time.RFC3339Nano), ps1.getProfileCopy(true).Onion, testMessage)

View File

@ -76,7 +76,7 @@ func (ps *ProfileStoreV1) initProfileWriterStore() {
ps.eventManager.Subscribe(event.SetGroupAttribute, ps.queue) ps.eventManager.Subscribe(event.SetGroupAttribute, ps.queue)
ps.eventManager.Subscribe(event.AcceptGroupInvite, ps.queue) ps.eventManager.Subscribe(event.AcceptGroupInvite, ps.queue)
ps.eventManager.Subscribe(event.RejectGroupInvite, ps.queue) ps.eventManager.Subscribe(event.RejectGroupInvite, ps.queue)
ps.eventManager.Subscribe(event.NewGroupInvite, ps.queue) ps.eventManager.Subscribe(event.NewGroup, ps.queue)
ps.eventManager.Subscribe(event.NewMessageFromGroup, ps.queue) ps.eventManager.Subscribe(event.NewMessageFromGroup, ps.queue)
ps.eventManager.Subscribe(event.SendMessageToPeer, ps.queue) ps.eventManager.Subscribe(event.SendMessageToPeer, ps.queue)
ps.eventManager.Subscribe(event.PeerAcknowledgement, ps.queue) ps.eventManager.Subscribe(event.PeerAcknowledgement, ps.queue)
@ -377,8 +377,8 @@ func (ps *ProfileStoreV1) eventHandler() {
case event.RejectGroupInvite: case event.RejectGroupInvite:
ps.profile.RejectInvite(ev.Data[event.GroupID]) ps.profile.RejectInvite(ev.Data[event.GroupID])
ps.save() ps.save()
case event.NewGroupInvite: case event.NewGroup:
gid, _, err := ps.profile.ProcessInvite(ev.Data[event.GroupInvite], ev.Data[event.RemotePeer]) gid, err := ps.profile.ProcessInvite(ev.Data[event.GroupInvite])
if err == nil { if err == nil {
ps.save() ps.save()
group := ps.profile.Groups[gid] group := ps.profile.Groups[gid]

View File

@ -35,7 +35,7 @@ func TestProfileStoreWriteRead(t *testing.T) {
t.Errorf("Creating group invite: %v\n", err) t.Errorf("Creating group invite: %v\n", err)
} }
eventBus.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: ps1.GetProfileCopy(true).Onion, event.GroupInvite: string(invite)})) eventBus.Publish(event.NewEvent(event.NewGroup, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: ps1.GetProfileCopy(true).Onion, event.GroupInvite: string(invite)}))
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
eventBus.Publish(event.NewEvent(event.NewMessageFromGroup, map[event.Field]string{ eventBus.Publish(event.NewEvent(event.NewMessageFromGroup, map[event.Field]string{
@ -89,7 +89,7 @@ func TestProfileStoreChangePassword(t *testing.T) {
t.Errorf("Creating group invite: %v\n", err) t.Errorf("Creating group invite: %v\n", err)
} }
eventBus.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: ps1.GetProfileCopy(true).Onion, event.GroupInvite: string(invite)})) eventBus.Publish(event.NewEvent(event.NewGroup, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: ps1.GetProfileCopy(true).Onion, event.GroupInvite: string(invite)}))
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
fmt.Println("Sending 200 messages...") fmt.Println("Sending 200 messages...")

View File

@ -23,6 +23,7 @@ import (
"path" "path"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"strings"
"testing" "testing"
"time" "time"
) )
@ -66,23 +67,25 @@ func serverCheck(t *testing.T, serverAddr string) bool {
} }
func waitForPeerGroupConnection(t *testing.T, peer peer.CwtchPeer, groupID string) { func waitForPeerGroupConnection(t *testing.T, peer peer.CwtchPeer, groupID string) {
peerName, _ := peer.GetAttribute(attr.GetLocalScope("name"))
for { for {
fmt.Printf("%v checking group connection...\n", peer.GetName()) fmt.Printf("%v checking group connection...\n", peerName)
state, ok := peer.GetGroupState(groupID) state, ok := peer.GetGroupState(groupID)
if ok { if ok {
fmt.Printf("Waiting for Peer %v to join group %v - state: %v\n", peer.GetName(), groupID, state) fmt.Printf("Waiting for Peer %v to join group %v - state: %v\n", peerName, groupID, state)
if state == connections.FAILED { if state == connections.FAILED {
t.Fatalf("%v could not connect to %v", peer.GetOnion(), groupID) t.Fatalf("%v could not connect to %v", peer.GetOnion(), groupID)
} }
if state != connections.SYNCED { if state != connections.SYNCED {
fmt.Printf("peer %v %v waiting connect to group %v, currently: %v\n", peer.GetName(), peer.GetOnion(), groupID, connections.ConnectionStateName[state]) fmt.Printf("peer %v %v waiting connect to group %v, currently: %v\n", peerName, peer.GetOnion(), groupID, connections.ConnectionStateName[state])
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
continue continue
} else { } else {
fmt.Printf("peer %v %v CONNECTED to group %v\n", peer.GetName(), peer.GetOnion(), groupID) fmt.Printf("peer %v %v CONNECTED to group %v\n", peerName, peer.GetOnion(), groupID)
break break
} }
} }
time.Sleep(time.Second * 2)
} }
return return
} }
@ -100,7 +103,9 @@ func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.Cw
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
continue continue
} else { } else {
fmt.Printf("%v CONNECTED and AUTHED to %v\n", peera.GetName(), peerb.GetName()) peerAName, _ := peera.GetAttribute(attr.GetLocalScope("name"))
peerBName, _ := peerb.GetAttribute(attr.GetLocalScope("name"))
fmt.Printf("%v CONNECTED and AUTHED to %v\n", peerAName, peerBName)
break break
} }
} }
@ -300,12 +305,16 @@ func TestCwtchPeerIntegration(t *testing.T) {
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
fmt.Println("Bob examining groups and accepting invites...") fmt.Println("Bob examining groups and accepting invites...")
for _, groupID := range bob.GetGroups() { for _, message := range bob.GetContact(alice.GetOnion()).Timeline.GetMessages() {
group := bob.GetGroup(groupID) fmt.Printf("Found message from Alice: %v", message.Message)
fmt.Printf("Bob group: %v (Accepted: %v)\n", group.GroupID, group.Accepted) if strings.HasPrefix(message.Message, "torv3") {
if group.Accepted == false { gid, err := bob.ImportGroup(message.Message)
fmt.Printf("Bob received and accepting group invite: %v\n", group.GroupID) if err == nil {
bob.AcceptInvite(group.GroupID) fmt.Printf("Bob found invite...now accepting %v...", gid)
bob.AcceptInvite(gid)
} else {
t.Fatalf("Bob could not accept invite...%v", gid)
}
} }
} }
@ -347,12 +356,16 @@ func TestCwtchPeerIntegration(t *testing.T) {
} }
time.Sleep(time.Second * 60) // Account for some token acquisition in Alice and Bob flows. time.Sleep(time.Second * 60) // Account for some token acquisition in Alice and Bob flows.
fmt.Println("Carol examining groups and accepting invites...") fmt.Println("Carol examining groups and accepting invites...")
for _, groupID := range carol.GetGroups() { for _, message := range carol.GetContact(alice.GetOnion()).Timeline.GetMessages() {
group := carol.GetGroup(groupID) fmt.Printf("Found message from Alice: %v", message.Message)
fmt.Printf("Carol group: %v (Accepted: %v)\n", group.GroupID, group.Accepted) if strings.HasPrefix(message.Message, "torv3") {
if group.Accepted == false { gid, err := carol.ImportGroup(message.Message)
fmt.Printf("Carol received and accepting group invite: %v\n", group.GroupID) if err == nil {
carol.AcceptInvite(group.GroupID) fmt.Printf("Carol found invite...now accepting %v...", gid)
carol.AcceptInvite(gid)
} else {
t.Fatalf("Carol could not accept invite...%v", gid)
}
} }
} }