cwtch/model/group.go

115 lines
2.9 KiB
Go
Raw Normal View History

2018-03-09 20:44:13 +00:00
package model
import (
"crypto/rand"
"fmt"
"git.mascherari.press/cwtch/protocol"
2018-03-30 21:16:51 +00:00
"github.com/golang/protobuf/proto"
"github.com/s-rah/go-ricochet/utils"
2018-03-30 21:16:51 +00:00
"golang.org/x/crypto/nacl/secretbox"
"io"
"log"
"time"
2018-03-09 20:44:13 +00:00
)
2018-03-15 16:33:26 +00:00
//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.
2018-03-09 20:44:13 +00:00
type Group struct {
GroupID string
SignedGroupID []byte
GroupKey [32]byte
GroupServer string
Timeline []Message
Accepted bool
Owner string
2018-03-09 20:44:13 +00:00
}
2018-03-15 16:33:26 +00:00
// NewGroup initializes a new group associated with a given CwtchServer
2018-03-09 20:44:13 +00:00
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"
2018-03-09 20:44:13 +00:00
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,
2018-03-30 21:16:51 +00:00
SignedGroupId: g.SignedGroupID[:],
}
2018-03-30 21:16:51 +00:00
log.Printf("INVITEBEFORE %v", gci)
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{
2018-03-30 21:16:51 +00:00
Message: message.GetText(),
Timestamp: time.Unix(int64(message.GetTimestamp()), 0),
Signature: message.GetSignature(),
2018-03-30 21:16:51 +00:00
Verified: verified,
PeerID: message.GetOnion(),
}
g.Timeline = append(g.Timeline, timelineMessage)
}
2018-03-15 16:33:26 +00:00
// AddMember ...
2018-03-09 20:44:13 +00:00
func (g *Group) AddMember() {
// TODO: Rotate Key
}
2018-03-15 16:33:26 +00:00
// RemoveMember ...
2018-03-09 20:44:13 +00:00
func (g *Group) RemoveMember() {
// TODO: Rotate Key
}
2018-03-15 16:33:26 +00:00
//EncryptMessage takes a message and encrypts the message under the group key.
func (g *Group) EncryptMessage(message *protocol.DecryptedGroupMessage) []byte {
2018-03-09 20:44:13 +00:00
var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
panic(err)
}
2018-03-30 21:16:51 +00:00
wire, err := proto.Marshal(message)
utils.CheckError(err)
encrypted := secretbox.Seal(nonce[:], []byte(wire), &nonce, &g.GroupKey)
2018-03-09 20:44:13 +00:00
return encrypted
}
2018-03-15 16:33:26 +00:00
// 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) {
2018-03-09 20:44:13 +00:00
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
}
2018-03-09 20:44:13 +00:00
}
return false, nil
2018-03-09 20:44:13 +00:00
}