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.
 
 
 

420 lines
12 KiB

  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:"-"` // TODO: cache recent messages for client
  27. LocalID string // used by storage engine
  28. State string `json:"-"`
  29. lock sync.Mutex
  30. }
  31. // Profile encapsulates all the attributes necessary to be a Cwtch Peer.
  32. type Profile struct {
  33. PublicProfile
  34. Contacts map[string]*PublicProfile
  35. Ed25519PrivateKey ed25519.PrivateKey
  36. Groups map[string]*Group
  37. }
  38. // MaxGroupMessageLength is the maximum length of a message posted to a server group.
  39. // TODO: Should this be per server?
  40. const MaxGroupMessageLength = 1800
  41. func generateRandomID() string {
  42. randBytes := make([]byte, 16)
  43. rand.Read(randBytes)
  44. return filepath.Join(hex.EncodeToString(randBytes))
  45. }
  46. func (p *PublicProfile) init() {
  47. if p.Attributes == nil {
  48. p.Attributes = make(map[string]string)
  49. }
  50. p.LocalID = generateRandomID()
  51. }
  52. // SetAttribute allows applications to store arbitrary configuration info at the profile level.
  53. func (p *PublicProfile) SetAttribute(name string, value string) {
  54. p.lock.Lock()
  55. defer p.lock.Unlock()
  56. p.Attributes[name] = value
  57. }
  58. // GetAttribute returns the value of a value set with SetCustomAttribute. If no such value has been set exists is set to false.
  59. func (p *PublicProfile) GetAttribute(name string) (value string, exists bool) {
  60. p.lock.Lock()
  61. defer p.lock.Unlock()
  62. value, exists = p.Attributes[name]
  63. return
  64. }
  65. // GenerateNewProfile creates a new profile, with new encryption and signing keys, and a profile name.
  66. func GenerateNewProfile(name string) *Profile {
  67. p := new(Profile)
  68. p.init()
  69. p.Name = name
  70. pub, priv, _ := ed25519.GenerateKey(rand.Reader)
  71. p.Ed25519PublicKey = pub
  72. p.Ed25519PrivateKey = priv
  73. p.Onion = utils.GetTorV3Hostname(pub)
  74. p.Contacts = make(map[string]*PublicProfile)
  75. p.Contacts[p.Onion] = &p.PublicProfile
  76. p.Groups = make(map[string]*Group)
  77. return p
  78. }
  79. // GetCwtchIdentityPacket returns the wire message for conveying this profiles identity.
  80. func (p *Profile) GetCwtchIdentityPacket() (message []byte) {
  81. ci := &protocol.CwtchIdentity{
  82. Name: p.Name,
  83. Ed25519PublicKey: p.Ed25519PublicKey,
  84. }
  85. cpp := &protocol.CwtchPeerPacket{
  86. CwtchIdentify: ci,
  87. }
  88. message, err := proto.Marshal(cpp)
  89. utils.CheckError(err)
  90. return
  91. }
  92. // AddContact allows direct manipulation of cwtch contacts
  93. func (p *Profile) AddContact(onion string, profile *PublicProfile) {
  94. p.lock.Lock()
  95. profile.init()
  96. // TODO: More Robust V3 Onion Handling
  97. decodedPub, _ := base32.StdEncoding.DecodeString(strings.ToUpper(onion[:56]))
  98. profile.Ed25519PublicKey = ed25519.PublicKey(decodedPub[:32])
  99. p.Contacts[onion] = profile
  100. p.lock.Unlock()
  101. }
  102. // DeleteContact deletes a peer contact
  103. func (p *Profile) DeleteContact(onion string) {
  104. p.lock.Lock()
  105. defer p.lock.Unlock()
  106. delete(p.Contacts, onion)
  107. }
  108. // DeleteGroup deletes a group
  109. func (p *Profile) DeleteGroup(groupID string) {
  110. p.lock.Lock()
  111. defer p.lock.Unlock()
  112. delete(p.Groups, groupID)
  113. }
  114. // RejectInvite rejects and removes a group invite
  115. func (p *Profile) RejectInvite(groupID string) {
  116. p.lock.Lock()
  117. delete(p.Groups, groupID)
  118. p.lock.Unlock()
  119. }
  120. /*
  121. // AddMessageToContactTimeline allows the saving of a message sent via a direct connection chat to the profile.
  122. func (p *Profile) AddMessageToContactTimeline(onion string, fromMe bool, message string, sent time.Time) {
  123. p.lock.Lock()
  124. defer p.lock.Unlock()
  125. contact, ok := p.Contacts[onion]
  126. // We don't really need a Signature here, but we use it to maintain order
  127. now := time.Now()
  128. sig := p.SignMessage(onion + message + sent.String() + now.String())
  129. if ok {
  130. if fromMe {
  131. contact.Timeline.Insert(&Message{PeerID: p.Onion, Message: message, Timestamp: sent, Received: now, Signature: sig})
  132. } else {
  133. contact.Timeline.Insert(&Message{PeerID: onion, Message: message, Timestamp: sent, Received: now, Signature: sig})
  134. }
  135. }
  136. }
  137. */
  138. // AcceptInvite accepts a group invite
  139. func (p *Profile) AcceptInvite(groupID string) (err error) {
  140. p.lock.Lock()
  141. defer p.lock.Unlock()
  142. group, ok := p.Groups[groupID]
  143. if ok {
  144. group.Accepted = true
  145. } else {
  146. err = errors.New("group does not exist")
  147. }
  148. return
  149. }
  150. // GetGroups returns an unordered list of group IDs associated with this profile.
  151. func (p *Profile) GetGroups() []string {
  152. p.lock.Lock()
  153. defer p.lock.Unlock()
  154. var keys []string
  155. for onion := range p.Groups {
  156. keys = append(keys, onion)
  157. }
  158. return keys
  159. }
  160. // GetContacts returns an unordered list of contact onions associated with this profile.
  161. func (p *Profile) GetContacts() []string {
  162. p.lock.Lock()
  163. defer p.lock.Unlock()
  164. var keys []string
  165. for onion := range p.Contacts {
  166. if onion != p.Onion {
  167. keys = append(keys, onion)
  168. }
  169. }
  170. return keys
  171. }
  172. // BlockPeer blocks a contact
  173. func (p *Profile) BlockPeer(onion string) (err error) {
  174. p.lock.Lock()
  175. defer p.lock.Unlock()
  176. contact, ok := p.Contacts[onion]
  177. if ok {
  178. contact.Blocked = true
  179. } else {
  180. err = errors.New("peer does not exist")
  181. }
  182. return
  183. }
  184. // BlockedPeers calculates a list of Peers who have been Blocked.
  185. func (p *Profile) BlockedPeers() []string {
  186. blockedPeers := []string{}
  187. for _, contact := range p.GetContacts() {
  188. c, _ := p.GetContact(contact)
  189. if c.Blocked {
  190. blockedPeers = append(blockedPeers, c.Onion)
  191. }
  192. }
  193. return blockedPeers
  194. }
  195. // TrustPeer sets a contact to trusted
  196. func (p *Profile) TrustPeer(onion string) (err error) {
  197. p.lock.Lock()
  198. defer p.lock.Unlock()
  199. contact, ok := p.Contacts[onion]
  200. if ok {
  201. contact.Trusted = true
  202. } else {
  203. err = errors.New("peer does not exist")
  204. }
  205. return
  206. }
  207. // IsBlocked returns true if the contact has been blocked, false otherwise
  208. func (p *Profile) IsBlocked(onion string) bool {
  209. contact, ok := p.GetContact(onion)
  210. if ok {
  211. return contact.Blocked
  212. }
  213. return false
  214. }
  215. // GetContact returns a contact if the profile has it
  216. func (p *Profile) GetContact(onion string) (*PublicProfile, bool) {
  217. p.lock.Lock()
  218. defer p.lock.Unlock()
  219. contact, ok := p.Contacts[onion]
  220. return contact, ok
  221. }
  222. // VerifyGroupMessage confirms the authenticity of a message given an onion, message and signature.
  223. func (p *Profile) VerifyGroupMessage(onion string, groupID string, message string, timestamp int32, ciphertext []byte, signature []byte) bool {
  224. group := p.GetGroupByGroupID(groupID)
  225. if group == nil {
  226. return false
  227. }
  228. if onion == p.Onion {
  229. m := groupID + group.GroupServer + string(ciphertext)
  230. return ed25519.Verify(p.Ed25519PublicKey, []byte(m), signature)
  231. }
  232. m := groupID + group.GroupServer + string(ciphertext)
  233. decodedPub, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
  234. if err == nil {
  235. return ed25519.Verify(decodedPub[:32], []byte(m), signature)
  236. }
  237. return false
  238. }
  239. // SignMessage takes a given message and returns an Ed21159 signature
  240. func (p *Profile) SignMessage(message string) []byte {
  241. sig := ed25519.Sign(p.Ed25519PrivateKey, []byte(message))
  242. return sig
  243. }
  244. // StartGroup when given a server, creates a new Group under this profile and returns the group id an a precomputed
  245. // invite which can be sent on the wire.
  246. func (p *Profile) StartGroup(server string) (groupID string, invite []byte, err error) {
  247. return p.StartGroupWithMessage(server, []byte{})
  248. }
  249. // StartGroupWithMessage when given a server, and an initial message creates a new Group under this profile and returns the group id an a precomputed
  250. // invite which can be sent on the wire.
  251. func (p *Profile) StartGroupWithMessage(server string, initialMessage []byte) (groupID string, invite []byte, err error) {
  252. group, err := NewGroup(server)
  253. if err != nil {
  254. return "", nil, err
  255. }
  256. groupID = group.GroupID
  257. group.Owner = p.Onion
  258. signedGroupID := p.SignMessage(groupID + server)
  259. group.SignGroup(signedGroupID)
  260. invite, err = group.Invite(initialMessage)
  261. p.lock.Lock()
  262. defer p.lock.Unlock()
  263. p.Groups[group.GroupID] = group
  264. return
  265. }
  266. // GetGroupByGroupID a pointer to a Group by the group Id, returns nil if no group found.
  267. func (p *Profile) GetGroupByGroupID(groupID string) (g *Group) {
  268. p.lock.Lock()
  269. defer p.lock.Unlock()
  270. g = p.Groups[groupID]
  271. return
  272. }
  273. // ProcessInvite adds a new group invite to the profile.
  274. func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname string) {
  275. group := new(Group)
  276. group.GroupID = gci.GetGroupName()
  277. group.LocalID = generateRandomID()
  278. group.SignedGroupID = gci.GetSignedGroupId()
  279. copy(group.GroupKey[:], gci.GetGroupSharedKey()[:])
  280. group.GroupServer = gci.GetServerHost()
  281. group.InitialMessage = gci.GetInitialMessage()[:]
  282. group.Accepted = false
  283. group.Owner = peerHostname
  284. group.Attributes = make(map[string]string)
  285. p.AddGroup(group)
  286. }
  287. // AddGroup is a convenience method for adding a group to a profile.
  288. func (p *Profile) AddGroup(group *Group) {
  289. _, exists := p.Groups[group.GroupID]
  290. if !exists {
  291. p.lock.Lock()
  292. defer p.lock.Unlock()
  293. p.Groups[group.GroupID] = group
  294. }
  295. }
  296. // AttemptDecryption takes a ciphertext and signature and attempts to decrypt it under known groups.
  297. func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) (bool, string, *Message, bool) {
  298. for _, group := range p.Groups {
  299. success, dgm := group.DecryptMessage(ciphertext)
  300. if success {
  301. verified := p.VerifyGroupMessage(dgm.GetOnion(), group.GroupID, dgm.GetText(), dgm.GetTimestamp(), ciphertext, signature)
  302. // So we have a message that has a valid group key, but the signature can't be verified.
  303. // 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)
  304. // 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.
  305. if !verified {
  306. group.Compromised()
  307. return false, group.GroupID, nil, false
  308. }
  309. message, seen := group.AddMessage(dgm, signature)
  310. return true, group.GroupID, message, seen
  311. }
  312. }
  313. // If we couldn't find a group to decrypt the message with we just return false. This is an expected case
  314. return false, "", nil, false
  315. }
  316. func getRandomness(arr *[]byte) {
  317. if _, err := io.ReadFull(rand.Reader, (*arr)[:]); err != nil {
  318. utils.CheckError(err)
  319. }
  320. }
  321. // EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and
  322. // profile
  323. func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte, []byte, error) {
  324. if len(message) > MaxGroupMessageLength {
  325. return nil, nil, errors.New("group message is too long")
  326. }
  327. group := p.GetGroupByGroupID(groupID)
  328. if group != nil {
  329. timestamp := time.Now().Unix()
  330. var prevSig []byte
  331. if len(group.Timeline.Messages) > 0 {
  332. prevSig = group.Timeline.Messages[len(group.Timeline.Messages)-1].Signature
  333. } else {
  334. prevSig = group.SignedGroupID
  335. }
  336. lenPadding := MaxGroupMessageLength - len(message)
  337. padding := make([]byte, lenPadding)
  338. getRandomness(&padding)
  339. dm := &protocol.DecryptedGroupMessage{
  340. Onion: proto.String(p.Onion),
  341. Text: proto.String(message),
  342. SignedGroupId: group.SignedGroupID[:],
  343. Timestamp: proto.Int32(int32(timestamp)),
  344. PreviousMessageSig: prevSig,
  345. Padding: padding[:],
  346. }
  347. ciphertext, err := group.EncryptMessage(dm)
  348. if err != nil {
  349. return nil, nil, err
  350. }
  351. signature := p.SignMessage(groupID + group.GroupServer + string(ciphertext))
  352. group.AddSentMessage(dm, signature)
  353. return ciphertext, signature, nil
  354. }
  355. return nil, nil, errors.New("group does not exist")
  356. }
  357. // GetCopy returns a full deep copy of the Profile struct and its members (timeline inclusion control by arg)
  358. func (p *Profile) GetCopy(timeline bool) *Profile {
  359. p.lock.Lock()
  360. defer p.lock.Unlock()
  361. newp := new(Profile)
  362. bytes, _ := json.Marshal(p)
  363. json.Unmarshal(bytes, &newp)
  364. if timeline {
  365. for groupID := range newp.Groups {
  366. newp.Groups[groupID].Timeline = *p.Groups[groupID].Timeline.GetCopy()
  367. }
  368. }
  369. return newp
  370. }