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.

profile.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. package model
  2. import (
  3. "crypto/rand"
  4. "cwtch.im/cwtch/protocol"
  5. "encoding/base32"
  6. "encoding/hex"
  7. "encoding/json"
  8. "errors"
  9. "git.openprivacy.ca/openprivacy/libricochet-go/utils"
  10. "github.com/golang/protobuf/proto"
  11. "golang.org/x/crypto/ed25519"
  12. "io"
  13. "path/filepath"
  14. "strings"
  15. "sync"
  16. "time"
  17. )
  18. // PublicProfile is a local copy of a CwtchIdentity
  19. type PublicProfile struct {
  20. Name string
  21. Ed25519PublicKey ed25519.PublicKey
  22. Trusted bool
  23. Blocked bool
  24. Onion string
  25. Attributes map[string]string
  26. Timeline Timeline `json:"-"`
  27. LocalID string // used by storage engine
  28. State string `json:"-"`
  29. lock sync.Mutex
  30. unacknowledgedMessages map[string]Message
  31. }
  32. // Profile encapsulates all the attributes necessary to be a Cwtch Peer.
  33. type Profile struct {
  34. PublicProfile
  35. Contacts map[string]*PublicProfile
  36. Ed25519PrivateKey ed25519.PrivateKey
  37. Groups map[string]*Group
  38. }
  39. // MaxGroupMessageLength is the maximum length of a message posted to a server group.
  40. // TODO: Should this be per server?
  41. const MaxGroupMessageLength = 1800
  42. // GenerateRandomID generates a random 16 byte hex id code
  43. func GenerateRandomID() string {
  44. randBytes := make([]byte, 16)
  45. rand.Read(randBytes)
  46. return filepath.Join(hex.EncodeToString(randBytes))
  47. }
  48. func (p *PublicProfile) init() {
  49. if p.Attributes == nil {
  50. p.Attributes = make(map[string]string)
  51. }
  52. p.unacknowledgedMessages = make(map[string]Message)
  53. p.LocalID = GenerateRandomID()
  54. }
  55. // SetAttribute allows applications to store arbitrary configuration info at the profile level.
  56. func (p *PublicProfile) SetAttribute(name string, value string) {
  57. p.lock.Lock()
  58. defer p.lock.Unlock()
  59. p.Attributes[name] = value
  60. }
  61. // GetAttribute returns the value of a value set with SetCustomAttribute. If no such value has been set exists is set to false.
  62. func (p *PublicProfile) GetAttribute(name string) (value string, exists bool) {
  63. p.lock.Lock()
  64. defer p.lock.Unlock()
  65. value, exists = p.Attributes[name]
  66. return
  67. }
  68. // GenerateNewProfile creates a new profile, with new encryption and signing keys, and a profile name.
  69. func GenerateNewProfile(name string) *Profile {
  70. p := new(Profile)
  71. p.init()
  72. p.Name = name
  73. pub, priv, _ := ed25519.GenerateKey(rand.Reader)
  74. p.Ed25519PublicKey = pub
  75. p.Ed25519PrivateKey = priv
  76. p.Onion = utils.GetTorV3Hostname(pub)
  77. p.Contacts = make(map[string]*PublicProfile)
  78. p.Contacts[p.Onion] = &p.PublicProfile
  79. p.Groups = make(map[string]*Group)
  80. return p
  81. }
  82. // AddContact allows direct manipulation of cwtch contacts
  83. func (p *Profile) AddContact(onion string, profile *PublicProfile) {
  84. p.lock.Lock()
  85. profile.init()
  86. // TODO: More Robust V3 Onion Handling
  87. decodedPub, _ := base32.StdEncoding.DecodeString(strings.ToUpper(onion[:56]))
  88. profile.Ed25519PublicKey = ed25519.PublicKey(decodedPub[:32])
  89. p.Contacts[onion] = profile
  90. p.lock.Unlock()
  91. }
  92. // DeleteContact deletes a peer contact
  93. func (p *Profile) DeleteContact(onion string) {
  94. p.lock.Lock()
  95. defer p.lock.Unlock()
  96. delete(p.Contacts, onion)
  97. }
  98. // DeleteGroup deletes a group
  99. func (p *Profile) DeleteGroup(groupID string) {
  100. p.lock.Lock()
  101. defer p.lock.Unlock()
  102. delete(p.Groups, groupID)
  103. }
  104. // RejectInvite rejects and removes a group invite
  105. func (p *Profile) RejectInvite(groupID string) {
  106. p.lock.Lock()
  107. delete(p.Groups, groupID)
  108. p.lock.Unlock()
  109. }
  110. // AddSentMessageToContactTimeline allows the saving of a message sent via a direct connection chat to the profile.
  111. func (p *Profile) AddSentMessageToContactTimeline(onion string, messageTxt string, sent time.Time, eventID string) *Message {
  112. p.lock.Lock()
  113. defer p.lock.Unlock()
  114. contact, ok := p.Contacts[onion]
  115. if ok {
  116. now := time.Now()
  117. sig := p.SignMessage(onion + messageTxt + sent.String() + now.String())
  118. message := &Message{PeerID: p.Onion, Message: messageTxt, Timestamp: sent, Received: now, Signature: sig, Acknowledged: false}
  119. if contact.unacknowledgedMessages == nil {
  120. contact.unacknowledgedMessages = make(map[string]Message)
  121. }
  122. contact.unacknowledgedMessages[eventID] = *message
  123. return message
  124. }
  125. return nil
  126. }
  127. // AddMessageToContactTimeline allows the saving of a message sent via a direct connection chat to the profile.
  128. func (p *Profile) AddMessageToContactTimeline(onion string, messageTxt string, sent time.Time) (message *Message) {
  129. p.lock.Lock()
  130. defer p.lock.Unlock()
  131. contact, ok := p.Contacts[onion]
  132. // We don't really need a Signature here, but we use it to maintain order
  133. now := time.Now()
  134. sig := p.SignMessage(onion + messageTxt + sent.String() + now.String())
  135. if ok {
  136. message = &Message{PeerID: onion, Message: messageTxt, Timestamp: sent, Received: now, Signature: sig, Acknowledged: true}
  137. contact.Timeline.Insert(message)
  138. }
  139. return
  140. }
  141. // ErrorSentMessageToPeer sets a sent message's error message and removes it from the unacknowledged list
  142. func (p *Profile) ErrorSentMessageToPeer(onion string, eventID string, error string) {
  143. p.lock.Lock()
  144. defer p.lock.Unlock()
  145. contact, ok := p.Contacts[onion]
  146. if ok {
  147. message, ok := contact.unacknowledgedMessages[eventID]
  148. if ok {
  149. message.Error = error
  150. contact.Timeline.Insert(&message) // TODO: do we want a non timeline.Insert way to handle errors
  151. delete(contact.unacknowledgedMessages, eventID)
  152. }
  153. }
  154. }
  155. // AckSentMessageToPeer sets mesage to a peer as acknowledged
  156. func (p *Profile) AckSentMessageToPeer(onion string, eventID string) {
  157. p.lock.Lock()
  158. defer p.lock.Unlock()
  159. contact, ok := p.Contacts[onion]
  160. if ok {
  161. message, ok := contact.unacknowledgedMessages[eventID]
  162. if ok {
  163. message.Acknowledged = true
  164. contact.Timeline.Insert(&message)
  165. delete(contact.unacknowledgedMessages, eventID)
  166. }
  167. }
  168. }
  169. // AddGroupSentMessageError searches matching groups for the message by sig and marks it as an error
  170. func (p *Profile) AddGroupSentMessageError(groupServer string, signature string, error string) {
  171. for _, group := range p.Groups {
  172. if group.GroupServer == groupServer {
  173. if group.ErrorSentMessage([]byte(signature), error) {
  174. break
  175. }
  176. }
  177. }
  178. }
  179. // AcceptInvite accepts a group invite
  180. func (p *Profile) AcceptInvite(groupID string) (err error) {
  181. p.lock.Lock()
  182. defer p.lock.Unlock()
  183. group, ok := p.Groups[groupID]
  184. if ok {
  185. group.Accepted = true
  186. } else {
  187. err = errors.New("group does not exist")
  188. }
  189. return
  190. }
  191. // GetGroups returns an unordered list of group IDs associated with this profile.
  192. func (p *Profile) GetGroups() []string {
  193. p.lock.Lock()
  194. defer p.lock.Unlock()
  195. var keys []string
  196. for onion := range p.Groups {
  197. keys = append(keys, onion)
  198. }
  199. return keys
  200. }
  201. // GetContacts returns an unordered list of contact onions associated with this profile.
  202. func (p *Profile) GetContacts() []string {
  203. p.lock.Lock()
  204. defer p.lock.Unlock()
  205. var keys []string
  206. for onion := range p.Contacts {
  207. if onion != p.Onion {
  208. keys = append(keys, onion)
  209. }
  210. }
  211. return keys
  212. }
  213. // BlockPeer blocks a contact
  214. func (p *Profile) BlockPeer(onion string) (err error) {
  215. p.lock.Lock()
  216. defer p.lock.Unlock()
  217. contact, ok := p.Contacts[onion]
  218. if ok {
  219. contact.Blocked = true
  220. } else {
  221. err = errors.New("peer does not exist")
  222. }
  223. return
  224. }
  225. // UnblockPeer unblocks a contact
  226. func (p *Profile) UnblockPeer(onion string) (err error) {
  227. p.lock.Lock()
  228. defer p.lock.Unlock()
  229. contact, ok := p.Contacts[onion]
  230. if ok {
  231. contact.Blocked = false
  232. } else {
  233. err = errors.New("peer does not exist")
  234. }
  235. return
  236. }
  237. // BlockedPeers calculates a list of Peers who have been Blocked.
  238. func (p *Profile) BlockedPeers() []string {
  239. blockedPeers := []string{}
  240. for _, contact := range p.GetContacts() {
  241. c, _ := p.GetContact(contact)
  242. if c.Blocked {
  243. blockedPeers = append(blockedPeers, c.Onion)
  244. }
  245. }
  246. return blockedPeers
  247. }
  248. // TrustPeer sets a contact to trusted
  249. func (p *Profile) TrustPeer(onion string) (err error) {
  250. p.lock.Lock()
  251. defer p.lock.Unlock()
  252. contact, ok := p.Contacts[onion]
  253. if ok {
  254. contact.Trusted = true
  255. } else {
  256. err = errors.New("peer does not exist")
  257. }
  258. return
  259. }
  260. // IsBlocked returns true if the contact has been blocked, false otherwise
  261. func (p *Profile) IsBlocked(onion string) bool {
  262. contact, ok := p.GetContact(onion)
  263. if ok {
  264. return contact.Blocked
  265. }
  266. return false
  267. }
  268. // GetContact returns a contact if the profile has it
  269. func (p *Profile) GetContact(onion string) (*PublicProfile, bool) {
  270. p.lock.Lock()
  271. defer p.lock.Unlock()
  272. contact, ok := p.Contacts[onion]
  273. return contact, ok
  274. }
  275. // VerifyGroupMessage confirms the authenticity of a message given an onion, message and signature.
  276. func (p *Profile) VerifyGroupMessage(onion string, groupID string, message string, timestamp int32, ciphertext []byte, signature []byte) bool {
  277. group := p.GetGroup(groupID)
  278. if group == nil {
  279. return false
  280. }
  281. if onion == p.Onion {
  282. m := groupID + group.GroupServer + string(ciphertext)
  283. return ed25519.Verify(p.Ed25519PublicKey, []byte(m), signature)
  284. }
  285. m := groupID + group.GroupServer + string(ciphertext)
  286. decodedPub, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
  287. if err == nil && len(decodedPub) >= 32 {
  288. return ed25519.Verify(decodedPub[:32], []byte(m), signature)
  289. }
  290. return false
  291. }
  292. // SignMessage takes a given message and returns an Ed21159 signature
  293. func (p *Profile) SignMessage(message string) []byte {
  294. sig := ed25519.Sign(p.Ed25519PrivateKey, []byte(message))
  295. return sig
  296. }
  297. // StartGroup when given a server, creates a new Group under this profile and returns the group id an a precomputed
  298. // invite which can be sent on the wire.
  299. func (p *Profile) StartGroup(server string) (groupID string, invite []byte, err error) {
  300. return p.StartGroupWithMessage(server, []byte{})
  301. }
  302. // StartGroupWithMessage when given a server, and an initial message creates a new Group under this profile and returns the group id an a precomputed
  303. // invite which can be sent on the wire.
  304. func (p *Profile) StartGroupWithMessage(server string, initialMessage []byte) (groupID string, invite []byte, err error) {
  305. group, err := NewGroup(server)
  306. if err != nil {
  307. return "", nil, err
  308. }
  309. groupID = group.GroupID
  310. group.Owner = p.Onion
  311. signedGroupID := p.SignMessage(groupID + server)
  312. group.SignGroup(signedGroupID)
  313. invite, err = group.Invite(initialMessage)
  314. p.lock.Lock()
  315. defer p.lock.Unlock()
  316. p.Groups[group.GroupID] = group
  317. return
  318. }
  319. // GetGroup a pointer to a Group by the group Id, returns nil if no group found.
  320. func (p *Profile) GetGroup(groupID string) (g *Group) {
  321. p.lock.Lock()
  322. defer p.lock.Unlock()
  323. g = p.Groups[groupID]
  324. return
  325. }
  326. // ProcessInvite adds a new group invite to the profile. returns the new group ID
  327. func (p *Profile) ProcessInvite(invite string, peerHostname string) (string, error) {
  328. var gci protocol.GroupChatInvite
  329. err := proto.Unmarshal([]byte(invite), &gci)
  330. if err == nil {
  331. group := new(Group)
  332. group.GroupID = gci.GetGroupName()
  333. group.LocalID = GenerateRandomID()
  334. group.SignedGroupID = gci.GetSignedGroupId()
  335. copy(group.GroupKey[:], gci.GetGroupSharedKey()[:])
  336. group.GroupServer = gci.GetServerHost()
  337. group.InitialMessage = gci.GetInitialMessage()[:]
  338. group.Accepted = false
  339. group.Owner = peerHostname
  340. group.Attributes = make(map[string]string)
  341. p.AddGroup(group)
  342. return group.GroupID, nil
  343. }
  344. return "", err
  345. }
  346. // AddGroup is a convenience method for adding a group to a profile.
  347. func (p *Profile) AddGroup(group *Group) {
  348. _, exists := p.Groups[group.GroupID]
  349. if !exists {
  350. p.lock.Lock()
  351. defer p.lock.Unlock()
  352. p.Groups[group.GroupID] = group
  353. }
  354. }
  355. // AttemptDecryption takes a ciphertext and signature and attempts to decrypt it under known groups.
  356. // If successful, adds the message to the group's timeline
  357. func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) (bool, string, *Message, bool) {
  358. for _, group := range p.Groups {
  359. success, dgm := group.DecryptMessage(ciphertext)
  360. if success {
  361. verified := p.VerifyGroupMessage(dgm.GetOnion(), group.GroupID, dgm.GetText(), dgm.GetTimestamp(), ciphertext, signature)
  362. // So we have a message that has a valid group key, but the signature can't be verified.
  363. // The most obvious explanation for this is that the group key has been compromised (or we are in an open group and the server is being malicious)
  364. // Either way, someone who has the private key is being detectably bad so we are just going to throw this message away and mark the group as Compromised.
  365. if !verified {
  366. group.Compromised()
  367. return false, group.GroupID, nil, false
  368. }
  369. message, seen := group.AddMessage(dgm, signature)
  370. return true, group.GroupID, message, seen
  371. }
  372. }
  373. // If we couldn't find a group to decrypt the message with we just return false. This is an expected case
  374. return false, "", nil, false
  375. }
  376. func getRandomness(arr *[]byte) {
  377. if _, err := io.ReadFull(rand.Reader, (*arr)[:]); err != nil {
  378. utils.CheckError(err)
  379. }
  380. }
  381. // EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and
  382. // profile
  383. func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte, []byte, error) {
  384. if len(message) > MaxGroupMessageLength {
  385. return nil, nil, errors.New("group message is too long")
  386. }
  387. group := p.GetGroup(groupID)
  388. if group != nil {
  389. timestamp := time.Now().Unix()
  390. var prevSig []byte
  391. if len(group.Timeline.Messages) > 0 {
  392. prevSig = group.Timeline.Messages[len(group.Timeline.Messages)-1].Signature
  393. } else {
  394. prevSig = group.SignedGroupID
  395. }
  396. lenPadding := MaxGroupMessageLength - len(message)
  397. padding := make([]byte, lenPadding)
  398. getRandomness(&padding)
  399. dm := &protocol.DecryptedGroupMessage{
  400. Onion: proto.String(p.Onion),
  401. Text: proto.String(message),
  402. SignedGroupId: group.SignedGroupID[:],
  403. Timestamp: proto.Int32(int32(timestamp)),
  404. PreviousMessageSig: prevSig,
  405. Padding: padding[:],
  406. }
  407. ciphertext, err := group.EncryptMessage(dm)
  408. if err != nil {
  409. return nil, nil, err
  410. }
  411. signature := p.SignMessage(groupID + group.GroupServer + string(ciphertext))
  412. group.AddSentMessage(dm, signature)
  413. return ciphertext, signature, nil
  414. }
  415. return nil, nil, errors.New("group does not exist")
  416. }
  417. // GetCopy returns a full deep copy of the Profile struct and its members (timeline inclusion control by arg)
  418. func (p *Profile) GetCopy(timeline bool) *Profile {
  419. p.lock.Lock()
  420. defer p.lock.Unlock()
  421. newp := new(Profile)
  422. bytes, _ := json.Marshal(p)
  423. json.Unmarshal(bytes, &newp)
  424. if timeline {
  425. for groupID := range newp.Groups {
  426. newp.Groups[groupID].Timeline = *p.Groups[groupID].Timeline.GetCopy()
  427. }
  428. }
  429. return newp
  430. }