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.
 
 
 

229 lines
6.8 KiB

  1. package model
  2. import (
  3. "crypto/rand"
  4. "cwtch.im/cwtch/protocol/groups"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "git.openprivacy.ca/openprivacy/connectivity/tor"
  9. "git.openprivacy.ca/openprivacy/log"
  10. "golang.org/x/crypto/nacl/secretbox"
  11. "io"
  12. "sync"
  13. "time"
  14. )
  15. // CurrentGroupVersion is used to set the version of newly created groups and make sure group structs stored are correct and up to date
  16. const CurrentGroupVersion = 2
  17. // Group defines and encapsulates Cwtch's conception of group chat. Which are sessions
  18. // tied to a server under a given group key. Each group has a set of Messages.
  19. type Group struct {
  20. GroupID string
  21. SignedGroupID []byte
  22. GroupKey [32]byte
  23. GroupServer string
  24. Timeline Timeline `json:"-"`
  25. Accepted bool
  26. Owner string
  27. IsCompromised bool
  28. InitialMessage []byte
  29. Attributes map[string]string
  30. lock sync.Mutex
  31. LocalID string
  32. State string `json:"-"`
  33. unacknowledgedMessages []Message
  34. Version int
  35. }
  36. // NewGroup initializes a new group associated with a given CwtchServer
  37. func NewGroup(server string) (*Group, error) {
  38. group := new(Group)
  39. group.Version = CurrentGroupVersion
  40. group.LocalID = GenerateRandomID()
  41. if tor.IsValidHostname(server) == false {
  42. return nil, errors.New("Server is not a valid v3 onion")
  43. }
  44. group.GroupServer = server
  45. var groupID [16]byte
  46. if _, err := io.ReadFull(rand.Reader, groupID[:]); err != nil {
  47. log.Errorf("Cannot read from random: %v\n", err)
  48. return nil, err
  49. }
  50. group.GroupID = fmt.Sprintf("%x", groupID)
  51. var groupKey [32]byte
  52. if _, err := io.ReadFull(rand.Reader, groupKey[:]); err != nil {
  53. log.Errorf("Error: Cannot read from random: %v\n", err)
  54. return nil, err
  55. }
  56. copy(group.GroupKey[:], groupKey[:])
  57. group.Owner = "self"
  58. group.Attributes = make(map[string]string)
  59. return group, nil
  60. }
  61. // SignGroup adds a signature to the group.
  62. func (g *Group) SignGroup(signature []byte) {
  63. g.SignedGroupID = signature
  64. copy(g.Timeline.SignedGroupID[:], g.SignedGroupID)
  65. }
  66. // Compromised should be called if we detect a a groupkey leak.
  67. func (g *Group) Compromised() {
  68. g.IsCompromised = true
  69. }
  70. // GetInitialMessage returns the first message of the group, if one was sent with the invite.
  71. func (g *Group) GetInitialMessage() []byte {
  72. g.lock.Lock()
  73. defer g.lock.Unlock()
  74. return g.InitialMessage
  75. }
  76. // Invite generates a invitation that can be sent to a cwtch peer
  77. func (g *Group) Invite(initialMessage []byte) ([]byte, error) {
  78. if g.SignedGroupID == nil {
  79. return nil, errors.New("group isn't signed")
  80. }
  81. g.InitialMessage = initialMessage[:]
  82. gci := &groups.GroupInvite{
  83. GroupName: g.GroupID,
  84. SharedKey: g.GroupKey[:],
  85. ServerHost: g.GroupServer,
  86. SignedGroupID: g.SignedGroupID[:],
  87. InitialMessage: initialMessage[:],
  88. }
  89. invite, err := json.Marshal(gci)
  90. return invite, err
  91. }
  92. // AddSentMessage takes a DecryptedGroupMessage and adds it to the Groups Timeline
  93. func (g *Group) AddSentMessage(message *groups.DecryptedGroupMessage, sig []byte) Message {
  94. g.lock.Lock()
  95. defer g.lock.Unlock()
  96. timelineMessage := Message{
  97. Message: message.Text,
  98. Timestamp: time.Unix(int64(message.Timestamp), 0),
  99. Received: time.Unix(0, 0),
  100. Signature: sig,
  101. PeerID: message.Onion,
  102. PreviousMessageSig: message.PreviousMessageSig,
  103. ReceivedByServer: false,
  104. }
  105. g.unacknowledgedMessages = append(g.unacknowledgedMessages, timelineMessage)
  106. return timelineMessage
  107. }
  108. // ErrorSentMessage removes a sent message from the unacknowledged list and sets its error flag if found, otherwise returns false
  109. func (g *Group) ErrorSentMessage(sig []byte, error string) bool {
  110. g.lock.Lock()
  111. defer g.lock.Unlock()
  112. var message *Message
  113. // Delete the message from the unack'd buffer if it exists
  114. for i, unAckedMessage := range g.unacknowledgedMessages {
  115. if compareSignatures(unAckedMessage.Signature, sig) {
  116. message = &unAckedMessage
  117. g.unacknowledgedMessages = append(g.unacknowledgedMessages[:i], g.unacknowledgedMessages[i+1:]...)
  118. message.Error = error
  119. g.Timeline.Insert(message)
  120. return true
  121. }
  122. }
  123. return false
  124. }
  125. // AddMessage takes a DecryptedGroupMessage and adds it to the Groups Timeline
  126. func (g *Group) AddMessage(message *groups.DecryptedGroupMessage, sig []byte) (*Message, bool) {
  127. g.lock.Lock()
  128. defer g.lock.Unlock()
  129. // Delete the message from the unack'd buffer if it exists
  130. for i, unAckedMessage := range g.unacknowledgedMessages {
  131. if compareSignatures(unAckedMessage.Signature, sig) {
  132. g.unacknowledgedMessages = append(g.unacknowledgedMessages[:i], g.unacknowledgedMessages[i+1:]...)
  133. break
  134. }
  135. }
  136. timelineMessage := &Message{
  137. Message: message.Text,
  138. Timestamp: time.Unix(int64(message.Timestamp), 0),
  139. Received: time.Now(),
  140. Signature: sig,
  141. PeerID: message.Onion,
  142. PreviousMessageSig: message.PreviousMessageSig,
  143. ReceivedByServer: true,
  144. Error: "",
  145. }
  146. seen := g.Timeline.Insert(timelineMessage)
  147. return timelineMessage, seen
  148. }
  149. // GetTimeline provides a safe copy of the timeline
  150. func (g *Group) GetTimeline() (timeline []Message) {
  151. g.lock.Lock()
  152. defer g.lock.Unlock()
  153. return append(g.Timeline.GetMessages(), g.unacknowledgedMessages...)
  154. }
  155. //EncryptMessage takes a message and encrypts the message under the group key.
  156. func (g *Group) EncryptMessage(message *groups.DecryptedGroupMessage) ([]byte, error) {
  157. var nonce [24]byte
  158. if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
  159. log.Errorf("Cannot read from random: %v\n", err)
  160. return nil, err
  161. }
  162. wire, err := json.Marshal(message)
  163. if err != nil {
  164. return nil, err
  165. }
  166. encrypted := secretbox.Seal(nonce[:], []byte(wire), &nonce, &g.GroupKey)
  167. return encrypted, nil
  168. }
  169. // DecryptMessage takes a ciphertext and returns true and the decrypted message if the
  170. // cipher text can be successfully decrypted,else false.
  171. func (g *Group) DecryptMessage(ciphertext []byte) (bool, *groups.DecryptedGroupMessage) {
  172. if len(ciphertext) > 24 {
  173. var decryptNonce [24]byte
  174. copy(decryptNonce[:], ciphertext[:24])
  175. decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &g.GroupKey)
  176. if ok {
  177. dm := &groups.DecryptedGroupMessage{}
  178. err := json.Unmarshal(decrypted, dm)
  179. if err == nil {
  180. return true, dm
  181. }
  182. }
  183. }
  184. return false, nil
  185. }
  186. // SetAttribute allows applications to store arbitrary configuration info at the group level.
  187. func (g *Group) SetAttribute(name string, value string) {
  188. g.lock.Lock()
  189. defer g.lock.Unlock()
  190. g.Attributes[name] = value
  191. }
  192. // GetAttribute returns the value of a value set with SetAttribute. If no such value has been set exists is set to false.
  193. func (g *Group) GetAttribute(name string) (value string, exists bool) {
  194. g.lock.Lock()
  195. defer g.lock.Unlock()
  196. value, exists = g.Attributes[name]
  197. return
  198. }