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.

370 lines
11KB

  1. package model
  2. import (
  3. "crypto/rand"
  4. "cwtch.im/cwtch/protocol"
  5. "encoding/base32"
  6. "errors"
  7. "git.openprivacy.ca/openprivacy/libricochet-go/utils"
  8. "github.com/golang/protobuf/proto"
  9. "golang.org/x/crypto/ed25519"
  10. "io"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. // PublicProfile is a local copy of a CwtchIdentity
  16. type PublicProfile struct {
  17. Name string
  18. Ed25519PublicKey ed25519.PublicKey
  19. Trusted bool
  20. Blocked bool
  21. Onion string
  22. Attributes map[string]string
  23. lock sync.Mutex
  24. }
  25. // Profile encapsulates all the attributes necessary to be a Cwtch Peer.
  26. type Profile struct {
  27. PublicProfile
  28. Contacts map[string]*PublicProfile
  29. Ed25519PrivateKey ed25519.PrivateKey
  30. Groups map[string]*Group
  31. Custom map[string]string
  32. lock sync.Mutex
  33. }
  34. func (p *PublicProfile) init() {
  35. p.Attributes = make(map[string]string)
  36. }
  37. // SetAttribute allows applications to store arbitrary configuration info at the profile level.
  38. func (p *PublicProfile) SetAttribute(name string, value string) {
  39. p.lock.Lock()
  40. defer p.lock.Unlock()
  41. p.Attributes[name] = value
  42. }
  43. // GetAttribute returns the value of a value set with SetCustomAttribute. If no such value has been set exists is set to false.
  44. func (p *PublicProfile) GetAttribute(name string) (value string, exists bool) {
  45. p.lock.Lock()
  46. defer p.lock.Unlock()
  47. value, exists = p.Attributes[name]
  48. return
  49. }
  50. // GenerateNewProfile creates a new profile, with new encryption and signing keys, and a profile name.
  51. func GenerateNewProfile(name string) *Profile {
  52. p := new(Profile)
  53. p.Name = name
  54. pub, priv, _ := ed25519.GenerateKey(rand.Reader)
  55. p.Ed25519PublicKey = pub
  56. p.Ed25519PrivateKey = priv
  57. p.Onion = utils.GetTorV3Hostname(pub)
  58. p.Contacts = make(map[string]*PublicProfile)
  59. p.Contacts[p.Onion] = &p.PublicProfile
  60. p.Groups = make(map[string]*Group)
  61. p.Custom = make(map[string]string)
  62. return p
  63. }
  64. // GetCwtchIdentityPacket returns the wire message for conveying this profiles identity.
  65. func (p *Profile) GetCwtchIdentityPacket() (message []byte) {
  66. ci := &protocol.CwtchIdentity{
  67. Name: p.Name,
  68. Ed25519PublicKey: p.Ed25519PublicKey,
  69. }
  70. cpp := &protocol.CwtchPeerPacket{
  71. CwtchIdentify: ci,
  72. }
  73. message, err := proto.Marshal(cpp)
  74. utils.CheckError(err)
  75. return
  76. }
  77. // AddContact allows direct manipulation of cwtch contacts
  78. func (p *Profile) AddContact(onion string, profile *PublicProfile) {
  79. p.lock.Lock()
  80. profile.init()
  81. // TODO: More Robust V3 Onion Handling
  82. decodedPub, _ := base32.StdEncoding.DecodeString(strings.ToUpper(onion[:56]))
  83. profile.Ed25519PublicKey = ed25519.PublicKey(decodedPub[:32])
  84. p.Contacts[onion] = profile
  85. p.lock.Unlock()
  86. }
  87. // RejectInvite rejects and removes a group invite
  88. func (p *Profile) RejectInvite(groupID string) {
  89. p.lock.Lock()
  90. delete(p.Groups, groupID)
  91. p.lock.Unlock()
  92. }
  93. // AcceptInvite accepts a group invite
  94. func (p *Profile) AcceptInvite(groupID string) (err error) {
  95. p.lock.Lock()
  96. defer p.lock.Unlock()
  97. group, ok := p.Groups[groupID]
  98. if ok {
  99. group.Accepted = true
  100. } else {
  101. err = errors.New("group does not exist")
  102. }
  103. return
  104. }
  105. // GetGroups returns an unordered list of group IDs associated with this profile.
  106. func (p *Profile) GetGroups() []string {
  107. p.lock.Lock()
  108. defer p.lock.Unlock()
  109. var keys []string
  110. for onion := range p.Groups {
  111. keys = append(keys, onion)
  112. }
  113. return keys
  114. }
  115. // SetCustomAttribute allows applications to store arbitrary configuration info at the profile level.
  116. func (p *Profile) SetCustomAttribute(name string, value string) {
  117. p.lock.Lock()
  118. defer p.lock.Unlock()
  119. p.Custom[name] = value
  120. }
  121. // GetCustomAttribute returns the value of a value set with SetCustomAttribute. If no such value has been set exists is set to false.
  122. func (p *Profile) GetCustomAttribute(name string) (value string, exists bool) {
  123. p.lock.Lock()
  124. defer p.lock.Unlock()
  125. value, exists = p.Custom[name]
  126. return
  127. }
  128. // GetContacts returns an unordered list of contact onions associated with this profile.
  129. func (p *Profile) GetContacts() []string {
  130. p.lock.Lock()
  131. defer p.lock.Unlock()
  132. var keys []string
  133. for onion := range p.Contacts {
  134. if onion != p.Onion {
  135. keys = append(keys, onion)
  136. }
  137. }
  138. return keys
  139. }
  140. // BlockPeer blocks a contact
  141. func (p *Profile) BlockPeer(onion string) (err error) {
  142. p.lock.Lock()
  143. defer p.lock.Unlock()
  144. contact, ok := p.Contacts[onion]
  145. if ok {
  146. contact.Blocked = true
  147. } else {
  148. err = errors.New("peer does not exist")
  149. }
  150. return
  151. }
  152. // TrustPeer sets a contact to trusted
  153. func (p *Profile) TrustPeer(onion string) (err error) {
  154. p.lock.Lock()
  155. defer p.lock.Unlock()
  156. contact, ok := p.Contacts[onion]
  157. if ok {
  158. contact.Trusted = true
  159. } else {
  160. err = errors.New("peer does not exist")
  161. }
  162. return
  163. }
  164. // IsBlocked returns true if the contact has been blocked, false otherwise
  165. func (p *Profile) IsBlocked(onion string) bool {
  166. contact, ok := p.GetContact(onion)
  167. if ok {
  168. return contact.Blocked
  169. }
  170. return false
  171. }
  172. // GetContact returns a contact if the profile has it
  173. func (p *Profile) GetContact(onion string) (*PublicProfile, bool) {
  174. p.lock.Lock()
  175. defer p.lock.Unlock()
  176. contact, ok := p.Contacts[onion]
  177. return contact, ok
  178. }
  179. // VerifyGroupMessage confirms the authenticity of a message given an onion, message and signature.
  180. func (p *Profile) VerifyGroupMessage(onion string, groupID string, message string, timestamp int32, ciphertext []byte, signature []byte) bool {
  181. group := p.GetGroupByGroupID(groupID)
  182. if group == nil {
  183. return false
  184. }
  185. if onion == p.Onion {
  186. m := groupID + group.GroupServer + string(ciphertext)
  187. return ed25519.Verify(p.Ed25519PublicKey, []byte(m), signature)
  188. }
  189. m := groupID + group.GroupServer + string(ciphertext)
  190. decodedPub, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
  191. if err == nil {
  192. return ed25519.Verify(decodedPub[:32], []byte(m), signature)
  193. }
  194. return false
  195. }
  196. // SignMessage takes a given message and returns an Ed21159 signature
  197. func (p *Profile) SignMessage(message string) []byte {
  198. sig := ed25519.Sign(p.Ed25519PrivateKey, []byte(message))
  199. return sig
  200. }
  201. // StartGroup when given a server, creates a new Group under this profile and returns the group id an a precomputed
  202. // invite which can be sent on the wire.
  203. func (p *Profile) StartGroup(server string) (groupID string, invite []byte, err error) {
  204. return p.StartGroupWithMessage(server, []byte{})
  205. }
  206. // StartGroupWithMessage when given a server, and an initial message creates a new Group under this profile and returns the group id an a precomputed
  207. // invite which can be sent on the wire.
  208. func (p *Profile) StartGroupWithMessage(server string, initialMessage []byte) (groupID string, invite []byte, err error) {
  209. group, err := NewGroup(server)
  210. if err != nil {
  211. return "", nil, err
  212. }
  213. groupID = group.GroupID
  214. signedGroupID := p.SignMessage(groupID + server)
  215. group.SignGroup(signedGroupID)
  216. invite, err = group.Invite(initialMessage)
  217. p.lock.Lock()
  218. defer p.lock.Unlock()
  219. p.Groups[group.GroupID] = group
  220. return
  221. }
  222. // GetGroupByGroupID a pointer to a Group by the group Id, returns nil if no group found.
  223. func (p *Profile) GetGroupByGroupID(groupID string) (g *Group) {
  224. p.lock.Lock()
  225. defer p.lock.Unlock()
  226. g = p.Groups[groupID]
  227. return
  228. }
  229. // ProcessInvite adds a new group invite to the profile.
  230. func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname string) {
  231. group := new(Group)
  232. group.GroupID = gci.GetGroupName()
  233. group.SignedGroupID = gci.GetSignedGroupId()
  234. copy(group.GroupKey[:], gci.GetGroupSharedKey()[:])
  235. group.GroupServer = gci.GetServerHost()
  236. group.InitialMessage = gci.GetInitialMessage()[:]
  237. group.Accepted = false
  238. group.Owner = peerHostname
  239. p.AddGroup(group)
  240. }
  241. // AddGroup is a convenience method for adding a group to a profile.
  242. func (p *Profile) AddGroup(group *Group) {
  243. existingGroup, exists := p.Groups[group.GroupID]
  244. if !exists {
  245. // TODO More robust error handling (confirm this onion checksum is correct)
  246. decodedPub, _ := base32.StdEncoding.DecodeString(strings.ToUpper(group.Owner[:56]))
  247. valid := ed25519.Verify(ed25519.PublicKey(decodedPub[:32]), []byte(group.GroupID+group.GroupServer), group.SignedGroupID)
  248. if valid {
  249. p.lock.Lock()
  250. defer p.lock.Unlock()
  251. p.Groups[group.GroupID] = group
  252. }
  253. } else if exists && existingGroup.Owner == group.Owner {
  254. p.lock.Lock()
  255. defer p.lock.Unlock()
  256. p.Groups[group.GroupID] = group
  257. }
  258. // If we are sent an invite or group update by someone who is not an owner
  259. // then we reject the group.
  260. }
  261. // AttemptDecryption takes a ciphertext and signature and attempts to decrypt it under known groups.
  262. func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) (bool, *Message) {
  263. for _, group := range p.Groups {
  264. success, dgm := group.DecryptMessage(ciphertext)
  265. if success {
  266. // Assert that we know the owner of the group
  267. owner, ok := p.Contacts[group.Owner]
  268. if ok {
  269. valid := ed25519.Verify(owner.Ed25519PublicKey, []byte(group.GroupID+group.GroupServer), dgm.SignedGroupId)
  270. // If we can decrypt the message, but the group id is wrong that means that
  271. // this message is from someone who was not invited to the group.
  272. // As such this group has been compromised, probably by one of the other members.
  273. // We set the flag to be handled by the UX and reject the message.
  274. if !valid {
  275. group.Compromised()
  276. return false, nil
  277. }
  278. }
  279. verified := p.VerifyGroupMessage(dgm.GetOnion(), group.GroupID, dgm.GetText(), dgm.GetTimestamp(), ciphertext, signature)
  280. // So we have a message that has a valid group key, but the signature can't be verified.
  281. // 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)
  282. // 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.
  283. if !verified {
  284. group.Compromised()
  285. return false, nil
  286. }
  287. return true, group.AddMessage(dgm, signature)
  288. }
  289. }
  290. return false, nil
  291. }
  292. func getRandomness(arr *[]byte) {
  293. if _, err := io.ReadFull(rand.Reader, (*arr)[:]); err != nil {
  294. utils.CheckError(err)
  295. }
  296. }
  297. // EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and
  298. // profile
  299. func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte, []byte, error) {
  300. group := p.GetGroupByGroupID(groupID)
  301. if group != nil {
  302. timestamp := time.Now().Unix()
  303. var prevSig []byte
  304. if len(group.Timeline.Messages) > 0 {
  305. prevSig = group.Timeline.Messages[len(group.Timeline.Messages)-1].Signature
  306. } else {
  307. prevSig = group.SignedGroupID
  308. }
  309. lenPadding := 1024 - len(message)
  310. padding := make([]byte, lenPadding)
  311. getRandomness(&padding)
  312. dm := &protocol.DecryptedGroupMessage{
  313. Onion: proto.String(p.Onion),
  314. Text: proto.String(message),
  315. SignedGroupId: group.SignedGroupID[:],
  316. Timestamp: proto.Int32(int32(timestamp)),
  317. PreviousMessageSig: prevSig,
  318. Padding: padding[:],
  319. }
  320. ciphertext, err := group.EncryptMessage(dm)
  321. if err != nil {
  322. return nil, nil, err
  323. }
  324. signature := p.SignMessage(groupID + group.GroupServer + string(ciphertext))
  325. return ciphertext, signature, nil
  326. }
  327. return nil, nil, errors.New("group does not exist")
  328. }