Official cwtch.im peer and server implementations.
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 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. package model
  2. import (
  3. "crypto/rand"
  4. "crypto/rsa"
  5. "encoding/asn1"
  6. "encoding/json"
  7. "git.mascherari.press/cwtch/protocol"
  8. "github.com/golang/protobuf/proto"
  9. "github.com/s-rah/go-ricochet/utils"
  10. "golang.org/x/crypto/ed25519"
  11. "io/ioutil"
  12. "log"
  13. "strconv"
  14. "time"
  15. )
  16. // PublicProfile is a local copy of a CwtchIdentity
  17. type PublicProfile struct {
  18. Name string
  19. Ed25519PublicKey ed25519.PublicKey
  20. Trusted bool
  21. Blocked bool
  22. }
  23. // Profile encapsulates all the attributes necessary to be a Cwtch Peer.
  24. type Profile struct {
  25. PublicProfile
  26. Contacts map[string]PublicProfile
  27. Ed25519PrivateKey ed25519.PrivateKey
  28. OnionPrivateKey *rsa.PrivateKey
  29. Onion string
  30. Groups map[string]*Group
  31. }
  32. // GenerateNewProfile creates a new profile, with new encryption and signing keys, and a profile name.
  33. func GenerateNewProfile(name string) *Profile {
  34. p := new(Profile)
  35. p.Name = name
  36. pub, priv, _ := ed25519.GenerateKey(rand.Reader)
  37. p.Ed25519PublicKey = pub
  38. p.Ed25519PrivateKey = priv
  39. p.OnionPrivateKey, _ = utils.GeneratePrivateKey()
  40. // DER Encode the Public Key
  41. publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{
  42. N: p.OnionPrivateKey.PublicKey.N,
  43. E: p.OnionPrivateKey.PublicKey.E,
  44. })
  45. p.Onion = utils.GetTorHostname(publicKeyBytes)
  46. p.Contacts = make(map[string]PublicProfile)
  47. p.Groups = make(map[string]*Group)
  48. return p
  49. }
  50. // GetCwtchIdentityPacket returns the wire message for conveying this profiles identity.
  51. func (p *Profile) GetCwtchIdentityPacket() (message []byte) {
  52. ci := &protocol.CwtchIdentity{
  53. Name: p.Name,
  54. Ed25519PublicKey: p.Ed25519PublicKey,
  55. }
  56. cpp := &protocol.CwtchPeerPacket{
  57. CwtchIdentify: ci,
  58. }
  59. message, err := proto.Marshal(cpp)
  60. utils.CheckError(err)
  61. return
  62. }
  63. // GetCwtchIdentity returns the wire message for conveying this profiles identity.
  64. func (p *Profile) GetCwtchIdentity() (message []byte) {
  65. ci := &protocol.CwtchIdentity{
  66. Name: p.Name,
  67. Ed25519PublicKey: p.Ed25519PublicKey,
  68. }
  69. message, err := proto.Marshal(ci)
  70. utils.CheckError(err)
  71. return
  72. }
  73. // AddCwtchIdentity takes a wire message and if it is a CwtchIdentity message adds the identity as a contact
  74. // otherwise returns an error
  75. func (p *Profile) AddCwtchIdentity(onion string, ci *protocol.CwtchIdentity) {
  76. p.AddContact(onion, PublicProfile{Name: ci.GetName(), Ed25519PublicKey: ci.GetEd25519PublicKey()})
  77. }
  78. // AddContact allows direct manipulation of cwtch contacts
  79. func (p *Profile) AddContact(onion string, profile PublicProfile) {
  80. p.Contacts[onion] = profile
  81. }
  82. // VerifyMessage confirms the authenticity of a message given an onion, message and signature.
  83. func (p *Profile) VerifyMessage(onion string, message string, signature []byte) bool {
  84. return ed25519.Verify(p.Contacts[onion].Ed25519PublicKey, []byte(message), signature)
  85. }
  86. // VerifyMessage confirms the authenticity of a message given an onion, message and signature.
  87. func (p *Profile) VerifyGroupMessage(onion string, groupID string, message string, timestamp int32, signature []byte) bool {
  88. m := message + groupID + strconv.Itoa(int(timestamp))
  89. return ed25519.Verify(p.Contacts[onion].Ed25519PublicKey, []byte(m), signature)
  90. }
  91. // SignMessage takes a given message and returns an Ed21159 signature
  92. func (p *Profile) SignMessage(message string) []byte {
  93. sig := ed25519.Sign(p.Ed25519PrivateKey, []byte(message))
  94. return sig
  95. }
  96. //StartGroup when given a server, creates a new Group under this profile and returns the group id an a precomputed
  97. // invite which can be sent on the wire.
  98. func (p *Profile) StartGroup(server string) (groupID string, invite []byte) {
  99. group := NewGroup(server)
  100. groupID = group.GroupID
  101. signedGroupId := p.SignMessage(groupID)
  102. group.SignGroup(signedGroupId)
  103. invite = group.Invite()
  104. p.AddGroup(group)
  105. return
  106. }
  107. func (p *Profile) GetGroupByGroupId(groupID string) *Group {
  108. return p.Groups[groupID]
  109. }
  110. // ProcessInvite adds a new group invite to the profile.
  111. func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname string) {
  112. group := new(Group)
  113. group.GroupID = gci.GetGroupName()
  114. group.SignedGroupID = gci.GetSignedGroupId()
  115. copy(group.GroupKey[:], gci.GetGroupSharedKey()[:])
  116. group.GroupServer = gci.GetServerHost()
  117. group.Accepted = false
  118. group.Owner = peerHostname
  119. p.AddGroup(group)
  120. }
  121. // AddGroup is a conveniance method for adding a group to a profile.
  122. func (p *Profile) AddGroup(group *Group) {
  123. existingGroup, exists := p.Groups[group.GroupID]
  124. if !exists {
  125. p.Groups[group.GroupID] = group
  126. }
  127. if exists && existingGroup.Owner == group.Owner {
  128. p.Groups[group.GroupID] = group
  129. }
  130. // If we are sent an invite or group update by someone who is not an owner
  131. // then we reject the group.
  132. // FIXME: This opens up an attack vector!!
  133. }
  134. // AttemptDecryption takes a ciphertext and signature and attempts to decrypt it under known groups.
  135. func (p *Profile) AttemptDecryption(ciphertext []byte) {
  136. for _, group := range p.Groups {
  137. success, dgm := group.DecryptMessage(ciphertext)
  138. log.Printf("Decrypt Attempt %v %v", success, dgm)
  139. if success {
  140. // FIXME
  141. verified := p.VerifyGroupMessage(dgm.GetOnion(), group.GroupID, dgm.GetText(), dgm.GetTimestamp(), dgm.GetSignature())
  142. group.AddMessage(dgm, verified)
  143. }
  144. }
  145. }
  146. // EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and
  147. // profile
  148. func (p *Profile) EncryptMessageToGroup(message string, groupID string) (ciphertext []byte, signature []byte) {
  149. group := p.Groups[groupID]
  150. timestamp := time.Now().Unix()
  151. signature = p.SignMessage(message + groupID + strconv.Itoa(int(timestamp)))
  152. dm := &protocol.DecryptedGroupMessage{
  153. Onion: proto.String(p.Onion),
  154. Text: proto.String(message),
  155. SignedGroupId: group.SignedGroupID[:],
  156. Timestamp: proto.Int32(int32(timestamp)),
  157. Signature: signature,
  158. }
  159. ciphertext = group.EncryptMessage(dm)
  160. return
  161. }
  162. // Save makes a opy of the profile in the given file
  163. func (p *Profile) Save(profilefile string) error {
  164. bytes, _ := json.Marshal(p)
  165. return ioutil.WriteFile(profilefile, bytes, 0600)
  166. }
  167. // LoadProfile loads a saved profile from a file.
  168. func LoadProfile(profilefile string) (*Profile, error) {
  169. bytes, _ := ioutil.ReadFile(profilefile)
  170. profile := new(Profile)
  171. err := json.Unmarshal(bytes, &profile)
  172. return profile, err
  173. }