Official cwtch.im peer and server implementations. https://cwtch.im
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
3.5KB

  1. package model
  2. import (
  3. "crypto/rand"
  4. "cwtch.im/cwtch/protocol"
  5. "errors"
  6. "fmt"
  7. "github.com/golang/protobuf/proto"
  8. "github.com/s-rah/go-ricochet/utils"
  9. "golang.org/x/crypto/nacl/secretbox"
  10. "io"
  11. "sync"
  12. "time"
  13. )
  14. //Group defines and encapsulates Cwtch's conception of group chat. Which are sessions
  15. // tied to a server under a given group key. Each group has a set of messages.
  16. type Group struct {
  17. GroupID string
  18. SignedGroupID []byte
  19. GroupKey [32]byte
  20. GroupServer string
  21. Timeline Timeline
  22. Accepted bool
  23. Owner string
  24. IsCompromised bool
  25. lock sync.Mutex
  26. }
  27. // NewGroup initializes a new group associated with a given CwtchServer
  28. func NewGroup(server string) *Group {
  29. group := new(Group)
  30. group.GroupServer = server
  31. var groupID [16]byte
  32. if _, err := io.ReadFull(rand.Reader, groupID[:]); err != nil {
  33. panic(err)
  34. }
  35. group.GroupID = fmt.Sprintf("%x", groupID)
  36. var groupKey [32]byte
  37. if _, err := io.ReadFull(rand.Reader, groupKey[:]); err != nil {
  38. panic(err)
  39. }
  40. copy(group.GroupKey[:], groupKey[:])
  41. group.Owner = "self"
  42. return group
  43. }
  44. // SignGroup adds a signature to the group.
  45. func (g *Group) SignGroup(signature []byte) {
  46. g.SignedGroupID = signature
  47. copy(g.Timeline.SignedGroupId[:], g.SignedGroupID)
  48. }
  49. // Compromised should be called if we detect a a groupkey leak.
  50. func (g *Group) Compromised() {
  51. g.IsCompromised = true
  52. }
  53. // Invite generates a invitation that can be sent to a cwtch peer
  54. func (g *Group) Invite() ([]byte, error) {
  55. if g.SignedGroupID == nil {
  56. return nil, errors.New("group isn't signed")
  57. }
  58. gci := &protocol.GroupChatInvite{
  59. GroupName: g.GroupID,
  60. GroupSharedKey: g.GroupKey[:],
  61. ServerHost: g.GroupServer,
  62. SignedGroupId: g.SignedGroupID[:],
  63. }
  64. cp := &protocol.CwtchPeerPacket{
  65. GroupChatInvite: gci,
  66. }
  67. invite, err := proto.Marshal(cp)
  68. return invite, err
  69. }
  70. // AddMessage takes a DecryptedGroupMessage and adds it to the Groups Timeline
  71. func (g *Group) AddMessage(message *protocol.DecryptedGroupMessage, verified bool) *Message {
  72. g.lock.Lock()
  73. timelineMessage := &Message{
  74. Message: message.GetText(),
  75. Timestamp: time.Unix(int64(message.GetTimestamp()), 0),
  76. Received: time.Now(),
  77. Signature: message.GetSignature(),
  78. Verified: verified,
  79. PeerID: message.GetOnion(),
  80. PreviousMessageSig: message.GetPreviousMessageSig(),
  81. }
  82. g.Timeline.Insert(timelineMessage)
  83. g.lock.Unlock()
  84. return timelineMessage
  85. }
  86. // GetTimeline provides a safe copy of the timeline-=
  87. func (g *Group) GetTimeline() (t []Message) {
  88. g.lock.Lock()
  89. t = g.Timeline.GetMessages()
  90. g.lock.Unlock()
  91. return
  92. }
  93. //EncryptMessage takes a message and encrypts the message under the group key.
  94. func (g *Group) EncryptMessage(message *protocol.DecryptedGroupMessage) []byte {
  95. var nonce [24]byte
  96. if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
  97. panic(err)
  98. }
  99. wire, err := proto.Marshal(message)
  100. utils.CheckError(err)
  101. encrypted := secretbox.Seal(nonce[:], []byte(wire), &nonce, &g.GroupKey)
  102. return encrypted
  103. }
  104. // DecryptMessage takes a ciphertext and returns true and the decrypted message if the
  105. // cipher text can be successfully decrypted,else false.
  106. func (g *Group) DecryptMessage(ciphertext []byte) (bool, *protocol.DecryptedGroupMessage) {
  107. var decryptNonce [24]byte
  108. copy(decryptNonce[:], ciphertext[:24])
  109. decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &g.GroupKey)
  110. if ok {
  111. dm := &protocol.DecryptedGroupMessage{}
  112. err := proto.Unmarshal(decrypted, dm)
  113. if err == nil {
  114. return true, dm
  115. }
  116. }
  117. return false, nil
  118. }