cwtch/model/profile.go

164 lines
5.0 KiB
Go
Raw Normal View History

2018-03-09 20:44:13 +00:00
package model
import (
"crypto/rand"
"crypto/rsa"
"encoding/json"
"git.mascherari.press/cwtch/protocol"
"github.com/golang/protobuf/proto"
"github.com/s-rah/go-ricochet/utils"
"golang.org/x/crypto/ed25519"
"io/ioutil"
)
2018-03-15 16:33:26 +00:00
// PublicProfile is a local copy of a CwtchIdentity
2018-03-09 20:44:13 +00:00
type PublicProfile struct {
Name string
Ed25519PublicKey ed25519.PublicKey
}
2018-03-15 16:33:26 +00:00
// Profile encapsulates all the attributes necessary to be a Cwtch Peer.
2018-03-09 20:44:13 +00:00
type Profile struct {
PublicProfile
Contacts map[string]PublicProfile
Ed25519PrivateKey ed25519.PrivateKey
OnionPrivateKey *rsa.PrivateKey
Groups map[string]*Group
}
2018-03-15 16:33:26 +00:00
// GenerateNewProfile creates a new profile, with new encryption and signing keys, and a profile name.
2018-03-09 20:44:13 +00:00
func GenerateNewProfile(name string) *Profile {
p := new(Profile)
p.Name = name
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
p.Ed25519PublicKey = pub
p.Ed25519PrivateKey = priv
p.OnionPrivateKey, _ = utils.GeneratePrivateKey()
p.Contacts = make(map[string]PublicProfile)
p.Groups = make(map[string]*Group)
return p
}
2018-03-15 16:33:26 +00:00
// GetCwtchIdentityPacket returns the wire message for conveying this profiles identity.
2018-03-09 20:44:13 +00:00
func (p *Profile) GetCwtchIdentityPacket() (message []byte) {
ci := &protocol.CwtchIdentity{
Name: p.Name,
Ed25519PublicKey: p.Ed25519PublicKey,
}
cpp := &protocol.CwtchPeerPacket{
CwtchIdentify: ci,
}
message, err := proto.Marshal(cpp)
utils.CheckError(err)
return
}
// GetCwtchIdentity returns the wire message for conveying this profiles identity.
func (p *Profile) GetCwtchIdentity() (message []byte) {
ci := &protocol.CwtchIdentity{
Name: p.Name,
Ed25519PublicKey: p.Ed25519PublicKey,
}
message, err := proto.Marshal(ci)
utils.CheckError(err)
return
}
// AddCwtchIdentity takes a wire message and if it is a CwtchIdentity message adds the identity as a contact
// otherwise returns an error
2018-03-12 18:43:51 +00:00
func (p *Profile) AddCwtchIdentity(onion string, ci *protocol.CwtchIdentity) {
2018-03-09 20:44:13 +00:00
p.AddContact(onion, PublicProfile{Name: ci.GetName(), Ed25519PublicKey: ci.GetEd25519PublicKey()})
}
// AddContact allows direct manipulation of cwtch contacts
func (p *Profile) AddContact(onion string, profile PublicProfile) {
p.Contacts[onion] = profile
}
// VerifyMessage confirms the authenticity of a message given an onion, message and signature.
func (p *Profile) VerifyMessage(onion string, message string, signature []byte) bool {
return ed25519.Verify(p.Contacts[onion].Ed25519PublicKey, []byte(message), signature)
}
2018-03-15 16:33:26 +00:00
// SignMessage takes a given message and returns an Ed21159 signature
2018-03-09 20:44:13 +00:00
func (p *Profile) SignMessage(message string) []byte {
sig := ed25519.Sign(p.Ed25519PrivateKey, []byte(message))
return sig
}
2018-03-15 16:33:26 +00:00
//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.
2018-03-09 20:44:13 +00:00
func (p *Profile) StartGroup(server string) (groupID string, invite []byte) {
group := NewGroup(server)
p.AddGroup(group)
groupID = group.GroupID
gci := &protocol.GroupChatInvite{
GroupName: groupID,
GroupSharedKey: group.GroupKey[:],
ServerHost: server,
}
cp := &protocol.CwtchPeerPacket{
GroupChatInvite: gci,
}
invite, err := proto.Marshal(cp)
2018-03-09 20:44:13 +00:00
utils.CheckError(err)
return
}
2018-03-15 16:33:26 +00:00
// ProcessInvite adds a new group invite to the profile.
2018-03-12 18:43:51 +00:00
func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite) {
2018-03-09 20:44:13 +00:00
group := new(Group)
group.GroupID = gci.GetGroupName()
copy(group.GroupKey[:], gci.GetGroupSharedKey()[:])
group.GroupServer = gci.GetServerHost()
p.AddGroup(group)
}
2018-03-15 16:33:26 +00:00
// AddGroup is a conveniance method for adding a group to a profle.
2018-03-09 20:44:13 +00:00
func (p *Profile) AddGroup(group *Group) {
p.Groups[group.GroupID] = group
}
2018-03-15 16:33:26 +00:00
// AttemptDecryption takes a ciphertext and signature and attempts to decrypt it under known groups.
func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) (success bool, groupID string, onion string, message string) {
2018-03-09 20:44:13 +00:00
for id, group := range p.Groups {
success, message := group.DecryptMessage(ciphertext)
if success {
for onion := range p.Contacts {
if p.VerifyMessage(onion, message+string(ciphertext), signature) {
return true, id, onion, message
}
}
return true, id, "not-verified", message
}
}
return false, "", "", ""
}
2018-03-15 16:33:26 +00:00
// EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and
// profile
2018-03-09 20:44:13 +00:00
func (p *Profile) EncryptMessageToGroup(message string, groupid string) (ciphertext []byte, signature []byte) {
group := p.Groups[groupid]
ciphertext = group.EncryptMessage(message)
signature = p.SignMessage(message + string(ciphertext))
return
}
2018-03-15 16:33:26 +00:00
// Save makes a opy of the profile in the given file
2018-03-09 20:44:13 +00:00
func (p *Profile) Save(profilefile string) error {
bytes, _ := json.Marshal(p)
return ioutil.WriteFile(profilefile, bytes, 0600)
}
2018-03-15 16:33:26 +00:00
// LoadProfile loads a saved profile from a file.
2018-03-09 20:44:13 +00:00
func LoadProfile(profilefile string) (*Profile, error) {
bytes, _ := ioutil.ReadFile(profilefile)
profile := new(Profile)
err := json.Unmarshal(bytes, &profile)
return profile, err
}