cwtch/model/group.go

112 lines
2.8 KiB
Go

package model
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/nacl/secretbox"
"io"
"github.com/golang/protobuf/proto"
"git.mascherari.press/cwtch/protocol"
"github.com/s-rah/go-ricochet/utils"
"time"
)
//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.
type Group struct {
GroupID string
SignedGroupID []byte
GroupKey [32]byte
GroupServer string
Timeline []Message
Accepted bool
Owner string
}
// NewGroup initializes a new group associated with a given CwtchServer
func NewGroup(server string) *Group {
group := new(Group)
group.GroupServer = server
var groupID [16]byte
if _, err := io.ReadFull(rand.Reader, groupID[:]); err != nil {
panic(err)
}
group.GroupID = fmt.Sprintf("%x", groupID)
var groupKey [32]byte
if _, err := io.ReadFull(rand.Reader, groupKey[:]); err != nil {
panic(err)
}
copy(group.GroupKey[:], groupKey[:])
group.Owner = "self"
return group
}
func (g *Group) SignGroup(signature []byte) {
g.SignedGroupID = signature
}
func (g *Group) Invite() []byte {
gci := &protocol.GroupChatInvite{
GroupName: g.GroupID,
GroupSharedKey: g.GroupKey[:],
ServerHost: g.GroupServer,
}
cp := &protocol.CwtchPeerPacket{
GroupChatInvite: gci,
}
invite, err := proto.Marshal(cp)
utils.CheckError(err)
return invite
}
func (g *Group) AddMessage(message *protocol.DecryptedGroupMessage, verified bool) {
timelineMessage := Message{
Message: message.GetText(),
Timestamp: time.Unix(int64(message.GetTimestamp()),0),
Signature: message.GetSignature(),
Verified:verified,
PeerID: message.GetOnion(),
}
g.Timeline = append(g.Timeline, timelineMessage)
}
// AddMember ...
func (g *Group) AddMember() {
// TODO: Rotate Key
}
// RemoveMember ...
func (g *Group) RemoveMember() {
// TODO: Rotate Key
}
//EncryptMessage takes a message and encrypts the message under the group key.
func (g *Group) EncryptMessage(message *protocol.DecryptedGroupMessage) []byte {
var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
panic(err)
}
wire,err := proto.Marshal(message)
utils.CheckError(err)
encrypted := secretbox.Seal(nonce[:], []byte(wire), &nonce, &g.GroupKey)
return encrypted
}
// DecryptMessage takes a ciphertext and returns true and the decrypted message if the
// cipher text can be successfully decrypted,else false.
func (g *Group) DecryptMessage(ciphertext []byte) (bool, *protocol.DecryptedGroupMessage) {
var decryptNonce [24]byte
copy(decryptNonce[:], ciphertext[:24])
decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &g.GroupKey)
if ok {
dm := &protocol.DecryptedGroupMessage{}
err := proto.Unmarshal(decrypted, dm)
if err == nil {
return true, dm
}
}
return false, nil
}