Making profile thread safe, making blocking do something

This commit is contained in:
Sarah Jamie Lewis 2018-05-30 10:41:02 -07:00
parent becfe7c928
commit ded21271f6
6 changed files with 130 additions and 33 deletions

View File

@ -13,6 +13,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"strconv" "strconv"
"sync"
"time" "time"
) )
@ -32,6 +33,7 @@ type Profile struct {
Ed25519PrivateKey ed25519.PrivateKey Ed25519PrivateKey ed25519.PrivateKey
OnionPrivateKey *rsa.PrivateKey OnionPrivateKey *rsa.PrivateKey
Groups map[string]*Group Groups map[string]*Group
lock sync.Mutex
} }
// GenerateNewProfile creates a new profile, with new encryption and signing keys, and a profile name. // GenerateNewProfile creates a new profile, with new encryption and signing keys, and a profile name.
@ -78,7 +80,74 @@ func (p *Profile) AddCwtchIdentity(onion string, ci *protocol.CwtchIdentity) {
// AddContact allows direct manipulation of cwtch contacts // AddContact allows direct manipulation of cwtch contacts
func (p *Profile) AddContact(onion string, profile *PublicProfile) { func (p *Profile) AddContact(onion string, profile *PublicProfile) {
p.lock.Lock()
p.Contacts[onion] = profile p.Contacts[onion] = profile
p.lock.Unlock()
}
// RejectInvite rejects and removes a group invite
func (p *Profile) RejectInvite(groupID string) {
p.lock.Lock()
delete(p.Groups, groupID)
p.lock.Unlock()
}
// AcceptInvite accepts a group invite
func (p *Profile) AcceptInvite(groupID string) error {
p.lock.Lock()
group, ok := p.Groups[groupID]
if ok {
group.Accepted = true
}
p.lock.Unlock()
if !ok {
return errors.New("group does not exist")
}
return nil
}
// BlockPeer blocks a contact
func (p *Profile) BlockPeer(onion string) error {
p.lock.Lock()
contact, ok := p.Contacts[onion]
if ok {
contact.Blocked = true
}
p.lock.Unlock()
if !ok {
return errors.New("peer does not exist")
}
return nil
}
// TrustPeer sets a contact to trusted
func (p *Profile) TrustPeer(onion string) error {
p.lock.Lock()
contact, ok := p.Contacts[onion]
if ok {
contact.Trusted = true
}
p.lock.Unlock()
if !ok {
return errors.New("peer does not exist")
}
return nil
}
// IsBlocked returns true if the contact has been blocked, false otherwise
func (p *Profile) IsBlocked(onion string) bool {
contact, ok := p.GetContact(onion)
if ok {
return contact.Blocked
}
return false
}
func (p *Profile) GetContact(onion string) (*PublicProfile, bool) {
p.lock.Lock()
contact, ok := p.Contacts[onion]
p.lock.Unlock()
return contact, ok
} }
// VerifyGroupMessage confirms the authenticity of a message given an onion, message and signature. // VerifyGroupMessage confirms the authenticity of a message given an onion, message and signature.
@ -89,7 +158,7 @@ func (p *Profile) VerifyGroupMessage(onion string, groupID string, message strin
return ed25519.Verify(p.Ed25519PublicKey, []byte(m), signature) return ed25519.Verify(p.Ed25519PublicKey, []byte(m), signature)
} }
contact, found := p.Contacts[onion] contact, found := p.GetContact(onion)
if found { if found {
m := message + groupID + strconv.Itoa(int(timestamp)) m := message + groupID + strconv.Itoa(int(timestamp))
return ed25519.Verify(contact.Ed25519PublicKey, []byte(m), signature) return ed25519.Verify(contact.Ed25519PublicKey, []byte(m), signature)
@ -111,13 +180,18 @@ func (p *Profile) StartGroup(server string) (groupID string, invite []byte, err
signedGroupID := p.SignMessage(groupID + server) signedGroupID := p.SignMessage(groupID + server)
group.SignGroup(signedGroupID) group.SignGroup(signedGroupID)
invite, err = group.Invite() invite, err = group.Invite()
p.lock.Lock()
p.Groups[group.GroupID] = group p.Groups[group.GroupID] = group
p.lock.Unlock()
return return
} }
// GetGroupByGroupID a pointer to a Group by the group Id, returns nil if no group found. // GetGroupByGroupID a pointer to a Group by the group Id, returns nil if no group found.
func (p *Profile) GetGroupByGroupID(groupID string) *Group { func (p *Profile) GetGroupByGroupID(groupID string) (g *Group) {
return p.Groups[groupID] p.lock.Lock()
g = p.Groups[groupID]
p.lock.Unlock()
return g
} }
// ProcessInvite adds a new group invite to the profile. // ProcessInvite adds a new group invite to the profile.
@ -134,17 +208,23 @@ func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname stri
// 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()
existingGroup, exists := p.Groups[group.GroupID] existingGroup, exists := p.Groups[group.GroupID]
p.lock.Unlock()
if !exists { if !exists {
owner, ok := p.Contacts[group.Owner] owner, ok := p.GetContact(group.Owner)
if ok { if ok {
valid := ed25519.Verify(owner.Ed25519PublicKey, []byte(group.GroupID+group.GroupServer), group.SignedGroupID) valid := ed25519.Verify(owner.Ed25519PublicKey, []byte(group.GroupID+group.GroupServer), group.SignedGroupID)
if valid { if valid {
p.lock.Lock()
p.Groups[group.GroupID] = group p.Groups[group.GroupID] = group
p.lock.Unlock()
} }
} }
} else if exists && existingGroup.Owner == group.Owner { } else if exists && existingGroup.Owner == group.Owner {
p.lock.Lock()
p.Groups[group.GroupID] = group p.Groups[group.GroupID] = group
p.lock.Unlock()
} }
// If we are sent an invite or group update by someone who is not an owner // If we are sent an invite or group update by someone who is not an owner
@ -187,7 +267,7 @@ func getRandomness(arr *[]byte) {
// EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and // EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and
// profile // profile
func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte, error) { func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte, error) {
group := p.Groups[groupID] group := p.GetGroupByGroupID(groupID)
if group != nil { if group != nil {
timestamp := time.Now().Unix() timestamp := time.Now().Unix()
signature := p.SignMessage(message + groupID + strconv.Itoa(int(timestamp))) signature := p.SignMessage(message + groupID + strconv.Itoa(int(timestamp)))
@ -219,7 +299,9 @@ func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte,
// Save makes a opy of the profile in the given file // Save makes a opy of the profile in the given file
func (p *Profile) Save(profilefile string) error { func (p *Profile) Save(profilefile string) error {
p.lock.Lock()
bytes, _ := json.Marshal(p) bytes, _ := json.Marshal(p)
p.lock.Unlock()
return ioutil.WriteFile(profilefile, bytes, 0600) return ioutil.WriteFile(profilefile, bytes, 0600)
} }

View File

@ -50,6 +50,17 @@ func (m *Manager) ManageServerConnection(host string, handler func(string, *prot
m.lock.Unlock() m.lock.Unlock()
} }
// TearDownPeerConnection closes an existing peer connection
func (m *Manager) TearDownPeerConnection(onion string) {
m.lock.Lock()
pc, ok := m.peerConnections[onion]
if ok {
pc.Kill()
delete(m.peerConnections, onion)
}
m.lock.Unlock()
}
// GetPeers returns a map of all peer connections with their state // GetPeers returns a map of all peer connections with their state
func (m *Manager) GetPeers() map[string]ConnectionState { func (m *Manager) GetPeers() map[string]ConnectionState {
rm := make(map[string]ConnectionState) rm := make(map[string]ConnectionState)

View File

@ -103,3 +103,10 @@ func (ppc *PeerPeerConnection) Run() error {
ppc.state = FAILED ppc.state = FAILED
return err return err
} }
// Kill closes the connection
func (ppc *PeerPeerConnection) Kill() {
ppc.state = KILLED
ppc.connection.Break()
// TODO We should kill the connection outright, but we need to add that to libricochet-go
}

View File

@ -14,4 +14,5 @@ const (
CONNECTED CONNECTED
AUTHENTICATED AUTHENTICATED
FAILED FAILED
KILLED
) )

View File

@ -1,6 +1,7 @@
package peer package peer
import ( import (
"crypto/rsa"
"cwtch.im/cwtch/model" "cwtch.im/cwtch/model"
"cwtch.im/cwtch/peer/connections" "cwtch.im/cwtch/peer/connections"
"cwtch.im/cwtch/peer/peer" "cwtch.im/cwtch/peer/peer"
@ -147,44 +148,39 @@ func (cp *CwtchPeer) GetServers() map[string]connections.ConnectionState {
// TrustPeer sets an existing peer relationship to trusted // TrustPeer sets an existing peer relationship to trusted
func (cp *CwtchPeer) TrustPeer(peer string) error { func (cp *CwtchPeer) TrustPeer(peer string) error {
_, ok := cp.Profile.Contacts[peer] err := cp.Profile.TrustPeer(peer)
if !ok { if err == nil {
return errors.New("peer does not exist") cp.PeerWithOnion(peer)
} }
cp.Profile.Contacts[peer].Trusted = true return err
cp.PeerWithOnion(peer)
return nil
} }
// BlockPeer blocks an existing peer relationship. // BlockPeer blocks an existing peer relationship.
func (cp *CwtchPeer) BlockPeer(peer string) error { func (cp *CwtchPeer) BlockPeer(peer string) error {
_, ok := cp.Profile.Contacts[peer] err := cp.Profile.BlockPeer(peer)
if !ok { cp.connectionsManager.TearDownConnection(peer)
return errors.New("peer does not exist") return err
}
cp.Profile.Contacts[peer].Blocked = true
return nil
} }
// 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 {
g := cp.Profile.GetGroupByGroupID(groupID) return cp.Profile.AcceptInvite(groupID)
if g == nil {
return errors.New("group invite does not exit")
}
g.Accepted = true
return nil
} }
// RejectInvite rejects a given group invite. // RejectInvite rejects a given group invite.
func (cp *CwtchPeer) RejectInvite(groupID string) error { func (cp *CwtchPeer) RejectInvite(groupID string) {
g := cp.Profile.GetGroupByGroupID(groupID) cp.Profile.RejectInvite(groupID)
if g == nil { }
return errors.New("group invite does not exit")
} // LookupContact returns that a contact is known and allowed to communicate for all cases.
g.Accepted = false func (cp *CwtchPeer) LookupContact(hostname string, publicKey rsa.PublicKey) (allowed, known bool) {
// TODO delete group from Profile blocked := cp.Profile.IsBlocked(hostname)
return nil return !blocked, true
}
// ContactRequest needed to implement ContactRequestHandler Interface
func (cp *CwtchPeer) ContactRequest(name string, message string) string {
return "Accepted"
} }
// Listen sets up an onion listener to process incoming cwtch messages // Listen sets up an onion listener to process incoming cwtch messages
@ -208,7 +204,7 @@ func (cp *CwtchPeer) Listen() error {
} }
}) })
cwtchpeer.Init(cp.Profile.Name, cp.Profile.OnionPrivateKey, af, new(application.AcceptAllContactManager)) cwtchpeer.Init(cp.Profile.Name, cp.Profile.OnionPrivateKey, af, cp)
log.Printf("Running cwtch peer on %v", l.Addr().String()) log.Printf("Running cwtch peer on %v", l.Addr().String())
cwtchpeer.Run(l) cwtchpeer.Run(l)
return nil return nil

View File

@ -66,7 +66,7 @@ func (s *Server) Run(privateKeyFile string) {
} }
}) })
cwtchserver.Init("cwtch server for " + l.Addr().String()[0:16], pk, af, new(application.AcceptAllContactManager)) cwtchserver.Init("cwtch server for "+l.Addr().String()[0:16], pk, af, new(application.AcceptAllContactManager))
log.Printf("cwtch server running on cwtch:%s", l.Addr().String()[0:16]) log.Printf("cwtch server running on cwtch:%s", l.Addr().String()[0:16])
cwtchserver.Run(l) cwtchserver.Run(l)
} }