cwtch/model/profile.go

153 lines
4.2 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"
)
type PublicProfile struct {
Name string
Ed25519PublicKey ed25519.PublicKey
}
type Profile struct {
PublicProfile
Contacts map[string]PublicProfile
Ed25519PrivateKey ed25519.PrivateKey
OnionPrivateKey *rsa.PrivateKey
Groups map[string]*Group
}
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
}
// GetCwtchIdentity returns the wire message for conveying this profiles identity.
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)
}
func (p *Profile) SignMessage(message string) []byte {
sig := ed25519.Sign(p.Ed25519PrivateKey, []byte(message))
return sig
}
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
}
// ProcessInvite
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)
}
// AddGroup
func (p *Profile) AddGroup(group *Group) {
p.Groups[group.GroupID] = group
}
// EncryptMessageToGroup
func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) (success bool, groupId string, onion string, message string) {
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, "", "", ""
}
// EncryptMessageToGroup
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
}
func (p *Profile) Save(profilefile string) error {
bytes, _ := json.Marshal(p)
return ioutil.WriteFile(profilefile, bytes, 0600)
}
func LoadProfile(profilefile string) (*Profile, error) {
bytes, _ := ioutil.ReadFile(profilefile)
profile := new(Profile)
err := json.Unmarshal(bytes, &profile)
return profile, err
}