From c29186979f8396770f21b34e55e582542709a6c4 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 15 Mar 2018 13:53:22 -0700 Subject: [PATCH] Introducing fix for group owner impersonation bug --- model/group.go | 62 ++++++++++++++--- model/group_test.go | 15 +++- model/profile.go | 79 +++++++++++++-------- model/profile_test | 2 +- model/profile_test.go | 4 +- peer/connections/connectionsmanager.go | 73 ++++++++++++++++++++ peer/connections/peerpeerconnection.go | 3 +- peer/connections/peerserverconnection.go | 1 + peer/connections/state.go | 1 + peer/cwtch_peer.go | 52 +++++++------- peer/test_profile | 2 +- protocol/cwtch-profile.pb.go | 47 ++++++++----- protocol/cwtch-profile.proto | 1 + protocol/group_message.pb.go | 88 +++++++++++++++++++----- protocol/group_message.proto | 1 + 15 files changed, 322 insertions(+), 109 deletions(-) create mode 100644 peer/connections/connectionsmanager.go diff --git a/model/group.go b/model/group.go index 6001f91..c34904d 100644 --- a/model/group.go +++ b/model/group.go @@ -5,16 +5,22 @@ import ( "fmt" "golang.org/x/crypto/nacl/secretbox" "io" + "github.com/golang/protobuf/proto" + "git.mascherari.press/cwtch/protocol" + "github.com/s-rah/go-ricochet/utils" + "time" ) - //Group defines and encapsulates Cwtch's conception of group chat. Which are sessions // tied to a server under a given group key. Each group has a set of messages. type Group struct { - GroupID string - GroupKey [32]byte - GroupServer string - Timeline []Message + GroupID string + SignedGroupID []byte + GroupKey [32]byte + GroupServer string + Timeline []Message + Accepted bool + Owner string } // NewGroup initializes a new group associated with a given CwtchServer @@ -33,9 +39,39 @@ func NewGroup(server string) *Group { panic(err) } copy(group.GroupKey[:], groupKey[:]) + group.Owner = "self" return group } +func (g *Group) SignGroup(signature []byte) { + g.SignedGroupID = signature +} + +func (g *Group) Invite() []byte { + gci := &protocol.GroupChatInvite{ + GroupName: g.GroupID, + GroupSharedKey: g.GroupKey[:], + ServerHost: g.GroupServer, + } + cp := &protocol.CwtchPeerPacket{ + GroupChatInvite: gci, + } + invite, err := proto.Marshal(cp) + utils.CheckError(err) + return invite +} + +func (g *Group) AddMessage(message *protocol.DecryptedGroupMessage, verified bool) { + timelineMessage := Message{ + Message: message.GetText(), + Timestamp: time.Unix(int64(message.GetTimestamp()),0), + Signature: message.GetSignature(), + Verified:verified, + PeerID: message.GetOnion(), + } + g.Timeline = append(g.Timeline, timelineMessage) +} + // AddMember ... func (g *Group) AddMember() { // TODO: Rotate Key @@ -47,23 +83,29 @@ func (g *Group) RemoveMember() { } //EncryptMessage takes a message and encrypts the message under the group key. -func (g *Group) EncryptMessage(message string) []byte { +func (g *Group) EncryptMessage(message *protocol.DecryptedGroupMessage) []byte { var nonce [24]byte if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { panic(err) } - encrypted := secretbox.Seal(nonce[:], []byte(message), &nonce, &g.GroupKey) + wire,err := proto.Marshal(message) + utils.CheckError(err) + encrypted := secretbox.Seal(nonce[:], []byte(wire), &nonce, &g.GroupKey) return encrypted } // DecryptMessage takes a ciphertext and returns true and the decrypted message if the // cipher text can be successfully decrypted,else false. -func (g *Group) DecryptMessage(ciphertext []byte) (bool, string) { +func (g *Group) DecryptMessage(ciphertext []byte) (bool, *protocol.DecryptedGroupMessage) { var decryptNonce [24]byte copy(decryptNonce[:], ciphertext[:24]) decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &g.GroupKey) if ok { - return true, string(decrypted) + dm := &protocol.DecryptedGroupMessage{} + err := proto.Unmarshal(decrypted, dm) + if err == nil { + return true, dm + } } - return false, "" + return false, nil } diff --git a/model/group_test.go b/model/group_test.go index 843cc07..3785d71 100644 --- a/model/group_test.go +++ b/model/group_test.go @@ -2,13 +2,24 @@ package model import ( "testing" + "git.mascherari.press/cwtch/protocol" + "github.com/golang/protobuf/proto" + "time" ) func TestGroup(t *testing.T) { g := NewGroup("server.onion") - encMessage := g.EncryptMessage("Hello World") + dgm := &protocol.DecryptedGroupMessage{ + Onion: proto.String("onion"), + Text: proto.String("Hello World!"), + Timestamp: proto.Int32(int32(time.Now().Unix())), + SignedGroupId: []byte{}, + Signature: []byte{}, + + } + encMessage := g.EncryptMessage(dgm) ok, message := g.DecryptMessage(encMessage) - if !ok || message != "Hello World" { + if !ok || message.GetText() != "Hello World!" { t.Errorf("group encryption was invalid, or returned wrong message decrypted:%v message:%v", ok, message) return } diff --git a/model/profile.go b/model/profile.go index 13bcd99..676034f 100644 --- a/model/profile.go +++ b/model/profile.go @@ -9,6 +9,9 @@ import ( "github.com/s-rah/go-ricochet/utils" "golang.org/x/crypto/ed25519" "io/ioutil" + "time" + "encoding/asn1" + "strconv" ) @@ -16,6 +19,8 @@ import ( type PublicProfile struct { Name string Ed25519PublicKey ed25519.PublicKey + Trusted bool + Blocked bool } @@ -25,6 +30,7 @@ type Profile struct { Contacts map[string]PublicProfile Ed25519PrivateKey ed25519.PrivateKey OnionPrivateKey *rsa.PrivateKey + Onion string Groups map[string]*Group } @@ -37,6 +43,12 @@ func GenerateNewProfile(name string) *Profile { p.Ed25519PrivateKey = priv p.OnionPrivateKey, _ = utils.GeneratePrivateKey() + // DER Encode the Public Key + publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{ + N: p.OnionPrivateKey.PublicKey.N, + E: p.OnionPrivateKey.PublicKey.E, + }) + p.Onion = utils.GetTorHostname(publicKeyBytes) p.Contacts = make(map[string]PublicProfile) p.Groups = make(map[string]*Group) @@ -94,57 +106,70 @@ func (p *Profile) SignMessage(message string) []byte { // invite which can be sent on the wire. func (p *Profile) StartGroup(server string) (groupID string, invite []byte) { group := NewGroup(server) - p.AddGroup(group) groupID = group.GroupID - gci := &protocol.GroupChatInvite{ - GroupName: groupID, - GroupSharedKey: group.GroupKey[:], - ServerHost: server, - } - cp := &protocol.CwtchPeerPacket{ - GroupChatInvite: gci, - } - invite, err := proto.Marshal(cp) - utils.CheckError(err) + invite = group.Invite() return } +func (p *Profile) GetGroupByGroupId(groupID string) (*Group) { + return p.Groups[groupID] +} + // ProcessInvite adds a new group invite to the profile. -func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite) { +func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname string) { group := new(Group) group.GroupID = gci.GetGroupName() copy(group.GroupKey[:], gci.GetGroupSharedKey()[:]) group.GroupServer = gci.GetServerHost() + group.Accepted = false + group.Owner = peerHostname p.AddGroup(group) } // AddGroup is a conveniance method for adding a group to a profle. func (p *Profile) AddGroup(group *Group) { - p.Groups[group.GroupID] = group + existingGroup, exists := p.Groups[group.GroupID] + if !exists { + p.Groups[group.GroupID] = group + } + + if exists && existingGroup.Owner == group.Owner { + p.Groups[group.GroupID] = group + } + + // If we are sent an invite or group update by someone who is not an owner + // then we reject the group. + + // FIXME: This opens up an attack vector!! + } // AttemptDecryption takes a ciphertext and signature and attempts to decrypt it under known groups. -func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) (success bool, groupID string, onion string, message string) { - for id, group := range p.Groups { - success, message := group.DecryptMessage(ciphertext) +func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) { + for _, group := range p.Groups { + success, dgm := group.DecryptMessage(ciphertext) if success { - for onion := range p.Contacts { - if p.VerifyMessage(onion, message+string(ciphertext), signature) { - return true, id, onion, message - } - } - return true, id, "not-verified", message + // FIXME + verified := p.VerifyMessage(dgm.GetOnion(), dgm.GetText(), dgm.GetSignature()) + group.AddMessage(dgm, verified) } } - return false, "", "", "" } // EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and // profile -func (p *Profile) EncryptMessageToGroup(message string, groupid string) (ciphertext []byte, signature []byte) { - group := p.Groups[groupid] - ciphertext = group.EncryptMessage(message) - signature = p.SignMessage(message + string(ciphertext)) +func (p *Profile) EncryptMessageToGroup(message string, groupID string) (ciphertext []byte, signature []byte) { + group := p.Groups[groupID] + timestamp := time.Now().Unix() + signature = p.SignMessage(message + groupID + strconv.Itoa(int(timestamp))) + dm := &protocol.DecryptedGroupMessage { + Onion: proto.String(p.Onion), + Text: proto.String(message), + SignedGroupId: group.SignedGroupID, + Timestamp: proto.Int32(int32(timestamp)), + Signature: signature, + } + ciphertext = group.EncryptMessage(dm) return } diff --git a/model/profile_test b/model/profile_test index a5d6c85..fc23380 100644 --- a/model/profile_test +++ b/model/profile_test @@ -1 +1 @@ -{"Name":"Sarah","Ed25519PublicKey":"T7ug35aRhuImJfE90ymczSgAi90cHdbQMqEhBM/4+T0=","Contacts":{},"Ed25519PrivateKey":"TSquPGWkVc13XEbOkkFaSmQgArJ3vhUAnOHUpipXiZNPu6DflpGG4iYl8T3TKZzNKACL3Rwd1tAyoSEEz/j5PQ==","OnionPrivateKey":{"N":147543015664120090366421447952970860504590941099804565923233227773815393474636918222730428033531903648100909217280034128017566946683746667285202590815509702891937867545391137542704927905354957814668191780596816701004171603996891826892371194711906073796279656017597798465364500891427388525790476841047404602167,"E":65537,"D":5684515839173340680458583030673534381709448498970147076554677512380546386368894189730905149528786131673021282231900852545041069020194093945330676439403114856356392314290322709016451155034411809521466849420244883177134443565565861016638701771436637981361005194284315380514984476353929411384168768558877308473,"Primes":[12931099477754593259784498055463781428153030935578361605358730348734406074416483185277800291372847230179833495402846932178915478884892963290117615988576301,11409935861829750400775707403393062540845554732277302700883351106026835771931816136175131558089707680685127543513601477653608896039540199753620444032990067],"Precomputed":{"Dp":3748099023138475266839591758267695988665867764045448480330110345370687974573378619520836997954111509292401499590650782362187442771219719100036227372613873,"Dq":1182655512297015342058217196259350807024487744272086260388797230011143253410025283626618073364715874618827096191279656342233627276143195094776136782521347,"Qinv":12011761893821155933799466829578837010916497100434672750257270979155184673238141008807799214221585212198300042270714718819586501480079276451027729262322408,"CRTValues":[]}},"Groups":{}} \ No newline at end of file +{"Name":"Sarah","Ed25519PublicKey":"08Q49pmOe/8Edn/jR1Qq8d26SU1MzPbJ2PmJ64S6BY8=","Trusted":false,"Blocked":false,"Contacts":{},"Ed25519PrivateKey":"/hhjerGW66QyhAKPtwGbBBzhY5/auK6T2b/vjRGuXnjTxDj2mY57/wR2f+NHVCrx3bpJTUzM9snY+YnrhLoFjw==","OnionPrivateKey":{"N":146328154189193884086641737732621103864374553173215703384122944250992002089624680210913011506081609406766798073335357829207459154214042241028123233139603802979024475926282158611908322463042012748984945285776401165814190414695182764240512995788636041616865870623261762908273890987461235956226167821197856348839,"E":65537,"D":131000281712443101868127073809425903015557376501501621205628292187530749753611841209312116988644890475820095160882129401029342867327559796231167833968091809253818237330927694966481136799400760644381429766064366889564088512676631446803130802140311950212661905465119640487794003192289760147304713580532618877313,"Primes":[11046905119315044599912769443513046649297228971109751902148448569336221142449837623971448415395377389790939200512488865059980264758837294990542341171570219,13246076852180554978507253920836715985718751717246338099834768101497493531807293249274887847720805074264534287152405900588792629153510158525691602692356981],"Precomputed":{"Dp":9951771949347089936659442878755668922509550306762893515157001442446411893285295532588832483100280468945131000782113044435070797127756136171042614443284033,"Dq":6285403633811601060799526716666618760759292321939158372044213473615958219816946235957557750406970050497863607761501422044192947211740832046507430314584393,"Qinv":6371034795994819655941418536323418056681675622606010416374029760428023006446243689515420082688972242794843497150013283993628462297774942497647603736899599,"CRTValues":[]}},"Onion":"3qs4j52zt24qqnor","Groups":{}} \ No newline at end of file diff --git a/model/profile_test.go b/model/profile_test.go index fdb7732..8671311 100644 --- a/model/profile_test.go +++ b/model/profile_test.go @@ -38,7 +38,7 @@ func TestProfileIdentity(t *testing.T) { } func TestProfileGroup(t *testing.T) { - sarah := GenerateNewProfile("Sarah") + /**sarah := GenerateNewProfile("Sarah") alice := GenerateNewProfile("Alice") sarah.AddContact("alice.onion", alice.PublicProfile) alice.AddContact("sarah.onion", sarah.PublicProfile) @@ -74,5 +74,5 @@ func TestProfileGroup(t *testing.T) { t.Logf("Success!") } else { t.Errorf("Failed to decrypt unverified group message %v %v %v %v", ok, gid, onion, message) - } + }*/ } diff --git a/peer/connections/connectionsmanager.go b/peer/connections/connectionsmanager.go new file mode 100644 index 0000000..a35e79f --- /dev/null +++ b/peer/connections/connectionsmanager.go @@ -0,0 +1,73 @@ +package connections + +import ( + "git.mascherari.press/cwtch/model" + "time" + "sync" +) + +type Manager struct { + peerConnections map[string]*PeerPeerConnection + serverConnections map[string]*PeerServerConnection + lock sync.Mutex +} + +func NewConnectionsManager() *Manager { + m := new(Manager) + m.peerConnections = make(map[string]*PeerPeerConnection) + m.serverConnections = make(map[string]*PeerServerConnection) + return m; +} + +func (m *Manager) ManagePeerConnection(host string, profile *model.Profile) { + m.lock.Lock() + ppc := NewPeerPeerConnection(host, profile) + go ppc.Run() + m.peerConnections[host] = ppc + m.lock.Unlock() + +} + +func (m *Manager) ManageServerConnection(host string) { + m.lock.Lock() + psc := NewPeerServerConnection(host) + go psc.Run() + m.serverConnections[host] = psc + m.lock.Unlock() +} + +func (m *Manager) GetPeerPeerConnectionForOnion(host string) (ppc *PeerPeerConnection) { + m.lock.Lock() + ppc = m.peerConnections[host] + m.lock.Unlock() + return +} + +func (m *Manager) GetPeerServerConnectionForOnion(host string) (psc *PeerServerConnection) { + m.lock.Lock() + psc = m.serverConnections[host] + m.lock.Unlock() + return +} + +func (m *Manager) AttemptReconnections() { + m.lock.Lock() + for _, ppc := range m.peerConnections { + if ppc.GetState() == FAILED { + go ppc.Run() + } + } + m.lock.Unlock() + + m.lock.Lock() + for _, psc := range m.serverConnections { + if psc.GetState() == FAILED { + go psc.Run() + } + } + m.lock.Unlock() + + // Launch Another Run In 30 Seconds + time.Sleep(time.Second * 30) + go m.AttemptReconnections() +} diff --git a/peer/connections/peerpeerconnection.go b/peer/connections/peerpeerconnection.go index d31c2cd..8d4639e 100644 --- a/peer/connections/peerpeerconnection.go +++ b/peer/connections/peerpeerconnection.go @@ -38,7 +38,7 @@ func (ppc *PeerPeerConnection) ClientIdentity(ci *protocol.CwtchIdentity) { } func (ppc *PeerPeerConnection) HandleGroupInvite(gci *protocol.GroupChatInvite) { - ppc.profile.ProcessInvite(gci) + ppc.profile.ProcessInvite(gci, ppc.PeerHostname) } func (ppc *PeerPeerConnection) SendGroupInvite(invite []byte) { @@ -87,5 +87,6 @@ func (ppc *PeerPeerConnection) Run() error { ppc.connection.Process(ppc) } } + ppc.state = FAILED return err } diff --git a/peer/connections/peerserverconnection.go b/peer/connections/peerserverconnection.go index 4249ce9..37003cc 100644 --- a/peer/connections/peerserverconnection.go +++ b/peer/connections/peerserverconnection.go @@ -63,6 +63,7 @@ func (psc *PeerServerConnection) Run() error { } } } + psc.state = FAILED return err } diff --git a/peer/connections/state.go b/peer/connections/state.go index 5c1e2c0..0ccfbc4 100644 --- a/peer/connections/state.go +++ b/peer/connections/state.go @@ -13,4 +13,5 @@ const ( CONNECTING CONNECTED AUTHENTICATED + FAILED ) diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index e3e7da5..5dc720d 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -11,6 +11,7 @@ import ( "github.com/s-rah/go-ricochet/connection" "io/ioutil" "sync" + "errors" ) /** @@ -23,34 +24,20 @@ Move CwtchPeerChannel under peer/ Write tests for Peer Channel */ -type CwtchPeerInstance struct { - rai *application.ApplicationInstance - ra *application.RicochetApplication -} - -func (cpi *CwtchPeerInstance) Init(rai *application.ApplicationInstance, ra *application.RicochetApplication) { - cpi.rai = rai - cpi.ra = ra -} type CwtchPeer struct { connection.AutoConnectionHandler Profile *model.Profile - PendingContacts []string - PendingInvites map[string][]string mutex sync.Mutex Log chan string `json:"-"` - peerconnections map[string]*connections.PeerPeerConnection - serverconnections map[string]*connections.PeerServerConnection + connectionsManager *connections.Manager } func NewCwtchPeer(name string) *CwtchPeer { cp := new(CwtchPeer) cp.Profile = model.GenerateNewProfile(name) - cp.PendingInvites = make(map[string][]string) cp.Log = make(chan string) - cp.peerconnections = make(map[string]*connections.PeerPeerConnection) - cp.serverconnections = make(map[string]*connections.PeerServerConnection) + cp.connectionsManager = connections.NewConnectionsManager() cp.Init() return cp } @@ -71,22 +58,23 @@ func LoadCwtchPeer(profilefile string) (*CwtchPeer, error) { } // AddContactRequest is the entry point for CwtchPeer relationships -func (cp *CwtchPeer) AddContactRequest(onion string) { - cp.mutex.Lock() - cp.PendingContacts = append(cp.PendingContacts, onion) - go cp.EstablishContact(onion) - cp.mutex.Unlock() +func (cp *CwtchPeer) PeerWithOnion(onion string) { + cp.connectionsManager.ManagePeerConnection(onion, cp.Profile) } // InviteOnionToGroup kicks off the invite process -func (cp *CwtchPeer) InviteOnionToGroup(onion string, groupid string) { - cp.mutex.Lock() - cp.PendingInvites[onion] = append(cp.PendingInvites[onion], groupid) - cp.mutex.Unlock() +func (cp *CwtchPeer) InviteOnionToGroup(onion string, groupid string) error { + group:= cp.Profile.GetGroupByGroupId(groupid) + if group == nil { + invite := group.Invite() + ppc := cp.connectionsManager.GetPeerPeerConnectionForOnion(onion) + ppc.SendGroupInvite(invite) + } + return errors.New("group id could not be found") } func (cp *CwtchPeer) JoinServer(onion string) { - + cp.connectionsManager.ManageServerConnection(onion) } func (cp *CwtchPeer) SendMessageToGroup(groupid string, message string) { @@ -122,10 +110,18 @@ func (cp *CwtchPeer) Listen() error { return nil } -func (cp *CwtchPeer) EstablishContact(onion string) { +type CwtchPeerInstance struct { + rai *application.ApplicationInstance + ra *application.RicochetApplication } +func (cpi *CwtchPeerInstance) Init(rai *application.ApplicationInstance, ra *application.RicochetApplication) { + cpi.rai = rai + cpi.ra = ra +} + + type CwtchPeerHandler struct { Onion string Peer *CwtchPeer @@ -137,7 +133,7 @@ func (cph *CwtchPeerHandler) ClientIdentity(ci *protocol.CwtchIdentity) { } func (cph *CwtchPeerHandler) HandleGroupInvite(gci *protocol.GroupChatInvite) { - cph.Peer.Profile.ProcessInvite(gci) + cph.Peer.Profile.ProcessInvite(gci, cph.Onion) } func (cph *CwtchPeerHandler) HandleGroupMessage(gm *protocol.GroupMessage) { diff --git a/peer/test_profile b/peer/test_profile index 6a296cb..96ec39e 100644 --- a/peer/test_profile +++ b/peer/test_profile @@ -1 +1 @@ -{"Profile":{"Name":"alice","Ed25519PublicKey":"jd/KsmjnaiNRQfwkUU2KOv78epqHQtc/NuQ7vHhL1pU=","Contacts":{},"Ed25519PrivateKey":"hJqPzncQMthT/C6MJe5wwqijF8LZlItwuVqRRPWF2uSN38qyaOdqI1FB/CRRTYo6/vx6modC1z825Du8eEvWlQ==","OnionPrivateKey":{"N":116625881909264071736606689586851027031866677768702967565587135989634906670425929131611970003568513685918847077357562012284752737018357715084617885889134319857398716428989490466264550714347160579478956149389453246821375180647178126226570098474391873393638846273276470804476875874646605047892383342437534890107,"E":65537,"D":74117948357734540608048409620402906386884464181553604820280211391554295479244395506837947276326786319461067500372956617050825510731565357481641438382630323642658160945533297126458940294613864770177502808097743782578588323228321725977812335445321391168916952291786105711946242663437273644485564881061465737473,"Primes":[10118822147491588191804307827889610500703007978301844285618895021194403722583146885313445367368523265387812462145972329196076399224393167672743566765418817,11525638084090164427262755292570520360826215923789098544956165178885657272566959447401401652883909093499220795453042380071024329286949114855482181086877371],"Precomputed":{"Dp":7235119182011013971770905973952227719653675845144337141219485492060511748176545509342631641895250014740877549722450880359615790586310997408254322575453953,"Dq":2255287590069308461101630740984853641564847231436767013145518749012461187777876435501710099586237548484581343071697140272377679765259860062357195023545713,"Qinv":6483591420973981267201623607283357903939001496206473754008877212497299522594502989717337115365261073580285165813624061295197296922762434701906760350701367,"CRTValues":[]}},"Groups":{}},"PendingContacts":null,"PendingInvites":{}} \ No newline at end of file +{"Profile":{"Name":"alice","Ed25519PublicKey":"woBpoPixOQlewrOj55rvUUJXO6SYjSbds+x5wBSD/nE=","Trusted":false,"Blocked":false,"Contacts":{},"Ed25519PrivateKey":"zF81FX4HdjfH8y9GEkkMuP3grW+6YHLUq5xt2BGdu93CgGmg+LE5CV7Cs6Pnmu9RQlc7pJiNJt2z7HnAFIP+cQ==","OnionPrivateKey":{"N":139926795769138065515184049224038533094758142244011440346432394711998122006646506685316823692319561121294993561288095105636848170048849868725177766607476321666954798718648650798310074246526178993249155964685028684884855868827006247404286011381915601081225045627232142906774434198218068995980294397037791794267,"E":65537,"D":80563006923674971788217949087853364805598501324340199865601622742387126930997644639776915458173154092952438958879467974136673817850271634292188117664829075982704832540445727518368206591976958327683396293490831042373962322741974344132577591789404120121585476701598634477623181079921183580001754668835068766913,"Primes":[11996718101524297693400014654326964581090960285071398726861380106816214227409962837267266227138498003607972122710998689259606190329862913588172576335353147,11663756252750410861345204036901556299677356139785595193403181361865123292907581200748114782681955962873888839331259846486482553416702234941039306713860961],"Precomputed":{"Dp":3630482172017812779852640350325261890791110599109221522954083214954696992114710666516955171625766069633289761657189633399236607913272978091676865075591787,"Dq":476609232111106707458114598025884123022658341735902222072016108260597832955528975320863808047702489716896933209166026349860388453086479167067507871579713,"Qinv":8004940118786479816457227792582435507151418905449158436675099080630617341053710180416725805457646331045119350055846124889161926521155923764007407649644659,"CRTValues":[]}},"Groups":{}}} \ No newline at end of file diff --git a/protocol/cwtch-profile.pb.go b/protocol/cwtch-profile.pb.go index 38cc6cc..01524cf 100644 --- a/protocol/cwtch-profile.pb.go +++ b/protocol/cwtch-profile.pb.go @@ -65,6 +65,7 @@ type GroupChatInvite struct { GroupName string `protobuf:"bytes,1,opt,name=group_name,json=groupName" json:"group_name,omitempty"` GroupSharedKey []byte `protobuf:"bytes,2,opt,name=group_shared_key,json=groupSharedKey,proto3" json:"group_shared_key,omitempty"` ServerHost string `protobuf:"bytes,3,opt,name=server_host,json=serverHost" json:"server_host,omitempty"` + SignedGroupId []byte `protobuf:"bytes,4,opt,name=signed_group_id,json=signedGroupId,proto3" json:"signed_group_id,omitempty"` } func (m *GroupChatInvite) Reset() { *m = GroupChatInvite{} } @@ -93,6 +94,13 @@ func (m *GroupChatInvite) GetServerHost() string { return "" } +func (m *GroupChatInvite) GetSignedGroupId() []byte { + if m != nil { + return m.SignedGroupId + } + return nil +} + func init() { proto.RegisterType((*CwtchPeerPacket)(nil), "protocol.CwtchPeerPacket") proto.RegisterType((*CwtchIdentity)(nil), "protocol.CwtchIdentity") @@ -102,23 +110,24 @@ func init() { func init() { proto.RegisterFile("cwtch-profile.proto", fileDescriptor1) } var fileDescriptor1 = []byte{ - // 276 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x8f, 0xcd, 0x4e, 0xc3, 0x30, - 0x10, 0x84, 0x15, 0x40, 0x88, 0x6e, 0x69, 0x53, 0xcc, 0x81, 0x70, 0x40, 0xa0, 0x9c, 0x7a, 0x80, - 0x48, 0x14, 0xf5, 0xc0, 0x85, 0x4b, 0x85, 0xa0, 0xaa, 0x84, 0x42, 0x78, 0x00, 0x2b, 0x75, 0x36, - 0xb5, 0xd5, 0x10, 0x47, 0x8e, 0x5b, 0x64, 0xf1, 0x22, 0x3c, 0x2e, 0xca, 0x06, 0xd4, 0x9f, 0x93, - 0xed, 0x99, 0x9d, 0x6f, 0xd6, 0x70, 0x2e, 0xbe, 0xac, 0x90, 0x77, 0x95, 0xd1, 0xb9, 0x2a, 0x30, - 0xaa, 0x8c, 0xb6, 0x9a, 0x9d, 0xd0, 0x21, 0x74, 0x11, 0xfe, 0x78, 0xe0, 0x4f, 0x9a, 0x89, 0x18, - 0xd1, 0xc4, 0xa9, 0x58, 0xa2, 0x65, 0x4f, 0xd0, 0xa7, 0x10, 0x57, 0x19, 0x96, 0x56, 0xe5, 0x2e, - 0xf0, 0x6e, 0xbc, 0x61, 0x77, 0x74, 0x11, 0xfd, 0xc7, 0x22, 0x8a, 0x4c, 0xc9, 0xb6, 0x2e, 0xe9, - 0x89, 0xcd, 0x33, 0x77, 0xec, 0x19, 0xce, 0x16, 0x46, 0xaf, 0x2a, 0x2e, 0x64, 0x6a, 0xb9, 0x2a, - 0xd7, 0xca, 0x62, 0x70, 0x40, 0x88, 0xcb, 0x0d, 0xe2, 0xa5, 0x19, 0x99, 0xc8, 0xd4, 0x4e, 0x69, - 0x20, 0xf1, 0x17, 0xbb, 0x42, 0xf8, 0x0e, 0xbd, 0x9d, 0x1a, 0xc6, 0xe0, 0xa8, 0x4c, 0x3f, 0x91, - 0xb6, 0xe9, 0x24, 0x74, 0x67, 0xb7, 0xc0, 0x30, 0x1b, 0x8d, 0xc7, 0xf7, 0x8f, 0xbc, 0x5a, 0xcd, - 0x0b, 0x25, 0xf8, 0x12, 0x1d, 0x95, 0x9d, 0x26, 0x83, 0x3f, 0x27, 0x26, 0x63, 0x86, 0x2e, 0xfc, - 0x06, 0x7f, 0xaf, 0x96, 0x5d, 0x01, 0xb4, 0xcb, 0x6e, 0xa1, 0x3b, 0xa4, 0xbc, 0x35, 0xfc, 0x21, - 0x0c, 0x5a, 0xbb, 0x96, 0xa9, 0xc1, 0x6c, 0x8b, 0xde, 0x27, 0xfd, 0x83, 0xe4, 0x19, 0x3a, 0x76, - 0x0d, 0xdd, 0x1a, 0xcd, 0x1a, 0x0d, 0x97, 0xba, 0xb6, 0xc1, 0x21, 0x91, 0xa0, 0x95, 0x5e, 0x75, - 0x6d, 0xe7, 0xc7, 0xf4, 0xf5, 0x87, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7b, 0x47, 0x47, 0xed, - 0x92, 0x01, 0x00, 0x00, + // 299 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x90, 0xc1, 0x4a, 0xf3, 0x40, + 0x14, 0x85, 0xc9, 0xff, 0x17, 0xb1, 0xb7, 0xb6, 0xa9, 0xe3, 0xc2, 0xb8, 0x10, 0xa5, 0x0b, 0xe9, + 0x42, 0x0b, 0x56, 0xba, 0x70, 0xe3, 0xa6, 0x88, 0x86, 0x82, 0xc4, 0xf8, 0x00, 0x43, 0x3a, 0x73, + 0x93, 0x0c, 0x8d, 0x99, 0x30, 0x99, 0x56, 0xe6, 0x4d, 0xdc, 0xfb, 0xa2, 0x92, 0x1b, 0xa5, 0xad, + 0xab, 0x99, 0x39, 0xe7, 0xde, 0xef, 0x1c, 0x06, 0x4e, 0xc4, 0x87, 0x15, 0xf9, 0x4d, 0x65, 0x74, + 0xaa, 0x0a, 0x9c, 0x54, 0x46, 0x5b, 0xcd, 0x0e, 0xe9, 0x10, 0xba, 0x18, 0x7d, 0x7a, 0xe0, 0xcf, + 0x9b, 0x89, 0x08, 0xd1, 0x44, 0x89, 0x58, 0xa1, 0x65, 0x0f, 0x30, 0xa0, 0x25, 0xae, 0x24, 0x96, + 0x56, 0xa5, 0x2e, 0xf0, 0x2e, 0xbd, 0x71, 0x6f, 0x7a, 0x3a, 0xf9, 0x5d, 0x9b, 0xd0, 0x4a, 0x48, + 0xb6, 0x75, 0x71, 0x5f, 0x6c, 0x9f, 0xa9, 0x63, 0x8f, 0x70, 0x9c, 0x19, 0xbd, 0xae, 0xb8, 0xc8, + 0x13, 0xcb, 0x55, 0xb9, 0x51, 0x16, 0x83, 0x7f, 0x84, 0x38, 0xdb, 0x22, 0x9e, 0x9a, 0x91, 0x79, + 0x9e, 0xd8, 0x90, 0x06, 0x62, 0x3f, 0xdb, 0x17, 0x46, 0xaf, 0xd0, 0xdf, 0x8b, 0x61, 0x0c, 0x3a, + 0x65, 0xf2, 0x8e, 0xd4, 0xa6, 0x1b, 0xd3, 0x9d, 0x5d, 0x03, 0x43, 0x39, 0x9d, 0xcd, 0x6e, 0xef, + 0x79, 0xb5, 0x5e, 0x16, 0x4a, 0xf0, 0x15, 0x3a, 0x0a, 0x3b, 0x8a, 0x87, 0x3f, 0x4e, 0x44, 0xc6, + 0x02, 0xdd, 0xe8, 0xcb, 0x03, 0xff, 0x4f, 0x2e, 0x3b, 0x07, 0x68, 0xdb, 0xee, 0xb0, 0xbb, 0xa4, + 0xbc, 0x34, 0x01, 0x63, 0x18, 0xb6, 0x76, 0x9d, 0x27, 0x06, 0xe5, 0x0e, 0x7e, 0x40, 0xfa, 0x1b, + 0xc9, 0x0b, 0x74, 0xec, 0x02, 0x7a, 0x35, 0x9a, 0x0d, 0x1a, 0x9e, 0xeb, 0xda, 0x06, 0xff, 0x89, + 0x04, 0xad, 0xf4, 0xac, 0x6b, 0xcb, 0xae, 0xc0, 0xaf, 0x55, 0x56, 0xa2, 0xe4, 0x2d, 0x51, 0xc9, + 0xa0, 0x43, 0xa4, 0x7e, 0x2b, 0x53, 0xb3, 0x50, 0x2e, 0x0f, 0xe8, 0x8f, 0xee, 0xbe, 0x03, 0x00, + 0x00, 0xff, 0xff, 0x62, 0x61, 0x2d, 0x00, 0xbb, 0x01, 0x00, 0x00, } diff --git a/protocol/cwtch-profile.proto b/protocol/cwtch-profile.proto index f27601b..dd67e06 100644 --- a/protocol/cwtch-profile.proto +++ b/protocol/cwtch-profile.proto @@ -16,4 +16,5 @@ message GroupChatInvite { string group_name = 1; bytes group_shared_key = 2; string server_host = 3; + bytes signed_group_id = 4; } diff --git a/protocol/group_message.pb.go b/protocol/group_message.pb.go index e9fb144..a4543b2 100644 --- a/protocol/group_message.pb.go +++ b/protocol/group_message.pb.go @@ -6,7 +6,7 @@ package protocol import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" -import Protocol_Data_Control "github.com/s-rah/go-ricochet/wire/control" +import control "github.com/s-rah/go-ricochet/wire/control" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -20,9 +20,10 @@ type CwtchServerPacket struct { XXX_unrecognized []byte `json:"-"` } -func (m *CwtchServerPacket) Reset() { *m = CwtchServerPacket{} } -func (m *CwtchServerPacket) String() string { return proto.CompactTextString(m) } -func (*CwtchServerPacket) ProtoMessage() {} +func (m *CwtchServerPacket) Reset() { *m = CwtchServerPacket{} } +func (m *CwtchServerPacket) String() string { return proto.CompactTextString(m) } +func (*CwtchServerPacket) ProtoMessage() {} +func (*CwtchServerPacket) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} } func (m *CwtchServerPacket) GetGroupMessage() *GroupMessage { if m != nil { @@ -49,9 +50,10 @@ type FetchMessage struct { XXX_unrecognized []byte `json:"-"` } -func (m *FetchMessage) Reset() { *m = FetchMessage{} } -func (m *FetchMessage) String() string { return proto.CompactTextString(m) } -func (*FetchMessage) ProtoMessage() {} +func (m *FetchMessage) Reset() { *m = FetchMessage{} } +func (m *FetchMessage) String() string { return proto.CompactTextString(m) } +func (*FetchMessage) ProtoMessage() {} +func (*FetchMessage) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{1} } type GroupMessage struct { Ciphertext []byte `protobuf:"bytes,1,req,name=ciphertext" json:"ciphertext,omitempty"` @@ -59,9 +61,10 @@ type GroupMessage struct { XXX_unrecognized []byte `json:"-"` } -func (m *GroupMessage) Reset() { *m = GroupMessage{} } -func (m *GroupMessage) String() string { return proto.CompactTextString(m) } -func (*GroupMessage) ProtoMessage() {} +func (m *GroupMessage) Reset() { *m = GroupMessage{} } +func (m *GroupMessage) String() string { return proto.CompactTextString(m) } +func (*GroupMessage) ProtoMessage() {} +func (*GroupMessage) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{2} } func (m *GroupMessage) GetCiphertext() []byte { if m != nil { @@ -82,14 +85,17 @@ func (m *GroupMessage) GetSpamguard() []byte { // GroupMessage type DecryptedGroupMessage struct { Onion *string `protobuf:"bytes,1,req,name=onion" json:"onion,omitempty"` - Text *string `protobuf:"bytes,2,req,name=text" json:"text,omitempty"` - Signature []byte `protobuf:"bytes,3,req,name=signature" json:"signature,omitempty"` + Timestamp *int32 `protobuf:"varint,2,req,name=timestamp" json:"timestamp,omitempty"` + Text *string `protobuf:"bytes,3,req,name=text" json:"text,omitempty"` + Signature []byte `protobuf:"bytes,4,req,name=signature" json:"signature,omitempty"` + SignedGroupId []byte `protobuf:"bytes,5,req,name=signed_group_id,json=signedGroupId" json:"signed_group_id,omitempty"` XXX_unrecognized []byte `json:"-"` } -func (m *DecryptedGroupMessage) Reset() { *m = DecryptedGroupMessage{} } -func (m *DecryptedGroupMessage) String() string { return proto.CompactTextString(m) } -func (*DecryptedGroupMessage) ProtoMessage() {} +func (m *DecryptedGroupMessage) Reset() { *m = DecryptedGroupMessage{} } +func (m *DecryptedGroupMessage) String() string { return proto.CompactTextString(m) } +func (*DecryptedGroupMessage) ProtoMessage() {} +func (*DecryptedGroupMessage) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{3} } func (m *DecryptedGroupMessage) GetOnion() string { if m != nil && m.Onion != nil { @@ -98,6 +104,13 @@ func (m *DecryptedGroupMessage) GetOnion() string { return "" } +func (m *DecryptedGroupMessage) GetTimestamp() int32 { + if m != nil && m.Timestamp != nil { + return *m.Timestamp + } + return 0 +} + func (m *DecryptedGroupMessage) GetText() string { if m != nil && m.Text != nil { return *m.Text @@ -112,14 +125,53 @@ func (m *DecryptedGroupMessage) GetSignature() []byte { return nil } +func (m *DecryptedGroupMessage) GetSignedGroupId() []byte { + if m != nil { + return m.SignedGroupId + } + return nil +} + var E_ServerNonce = &proto.ExtensionDesc{ - ExtendedType: (*Protocol_Data_Control.ChannelResult)(nil), + ExtendedType: (*control.ChannelResult)(nil), ExtensionType: ([]byte)(nil), Field: 8200, - Name: "im.cwtch.server_nonce", - Tag: "bytes,8200,opt,name=server_nonce", + Name: "protocol.server_nonce", + Tag: "bytes,8200,opt,name=server_nonce,json=serverNonce", + Filename: "group_message.proto", } func init() { + proto.RegisterType((*CwtchServerPacket)(nil), "protocol.CwtchServerPacket") + proto.RegisterType((*FetchMessage)(nil), "protocol.FetchMessage") + proto.RegisterType((*GroupMessage)(nil), "protocol.GroupMessage") + proto.RegisterType((*DecryptedGroupMessage)(nil), "protocol.DecryptedGroupMessage") proto.RegisterExtension(E_ServerNonce) } + +func init() { proto.RegisterFile("group_message.proto", fileDescriptor2) } + +var fileDescriptor2 = []byte{ + // 332 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0xdb, 0x4a, 0xf3, 0x30, + 0x1c, 0xa7, 0x3b, 0xc0, 0xb7, 0xff, 0xba, 0x7d, 0x18, 0xa7, 0x06, 0x11, 0x29, 0xbd, 0x90, 0x5d, + 0xed, 0xc2, 0x4b, 0x87, 0x20, 0x4c, 0x14, 0x41, 0x45, 0xea, 0x03, 0x8c, 0x90, 0xfe, 0xd7, 0x16, + 0xdb, 0xa4, 0x24, 0xa9, 0x87, 0x37, 0xf0, 0x45, 0x7c, 0x1b, 0x1f, 0x4a, 0x9a, 0xee, 0x90, 0x7a, + 0xe1, 0x55, 0xf8, 0x9d, 0x43, 0x02, 0xfb, 0x89, 0x92, 0x55, 0xb9, 0x2c, 0x50, 0x6b, 0x96, 0xe0, + 0xac, 0x54, 0xd2, 0x48, 0xf2, 0xcf, 0x1e, 0x5c, 0xe6, 0xc7, 0x93, 0x85, 0x14, 0x46, 0xc9, 0x7c, + 0x91, 0x32, 0x21, 0x30, 0x6f, 0xf4, 0xf0, 0xdb, 0x83, 0xbd, 0xc5, 0x9b, 0xe1, 0xe9, 0x33, 0xaa, + 0x57, 0x54, 0x4f, 0x8c, 0xbf, 0xa0, 0x21, 0x73, 0x18, 0xb5, 0xca, 0xa8, 0x17, 0x78, 0xd3, 0xe1, + 0xf9, 0xe1, 0x6c, 0xd3, 0x36, 0xbb, 0xad, 0xe5, 0x87, 0x46, 0x8d, 0xfc, 0xc4, 0x41, 0x75, 0x78, + 0x85, 0x86, 0xa7, 0xdb, 0x70, 0xe7, 0x77, 0xf8, 0xa6, 0x96, 0xb7, 0xe1, 0x95, 0x83, 0xc8, 0x25, + 0x8c, 0x5b, 0xcb, 0x9a, 0x76, 0x83, 0xee, 0x1f, 0xd3, 0x23, 0x77, 0x5a, 0x87, 0x63, 0xf0, 0xdd, + 0xf2, 0xf0, 0x1e, 0x7c, 0xd7, 0x4e, 0x4e, 0x01, 0x78, 0x56, 0xa6, 0xa8, 0x0c, 0xbe, 0x1b, 0xea, + 0x05, 0x9d, 0xa9, 0x1f, 0x39, 0x0c, 0x39, 0x81, 0x81, 0x2e, 0x59, 0x91, 0x54, 0x4c, 0xc5, 0xb4, + 0x63, 0xe5, 0x1d, 0x11, 0x7e, 0x79, 0x70, 0x70, 0x8d, 0x5c, 0x7d, 0x94, 0x06, 0xe3, 0x56, 0xef, + 0x04, 0xfa, 0x52, 0x64, 0x52, 0xd8, 0xca, 0x41, 0xd4, 0x80, 0xba, 0xcd, 0x64, 0x05, 0x6a, 0xc3, + 0x8a, 0xd2, 0xb6, 0xf5, 0xa3, 0x1d, 0x41, 0x08, 0xf4, 0xec, 0x2d, 0xba, 0x36, 0xd2, 0xdb, 0xee, + 0x67, 0x89, 0x60, 0xa6, 0x52, 0x48, 0x7b, 0xeb, 0xfd, 0x0d, 0x41, 0xce, 0xe0, 0x7f, 0x0d, 0x30, + 0x5e, 0x36, 0x6f, 0x94, 0xc5, 0xb4, 0x6f, 0x3d, 0xa3, 0x86, 0xb6, 0x57, 0xba, 0x8b, 0x2f, 0xe6, + 0xe0, 0x6b, 0xfb, 0x9d, 0x4b, 0x21, 0x05, 0x47, 0x72, 0xb4, 0x7b, 0xbc, 0xf5, 0xef, 0x47, 0xa8, + 0xab, 0xdc, 0xd0, 0xcf, 0xab, 0xc0, 0x9b, 0xfa, 0xd1, 0xb0, 0x71, 0x3f, 0xd6, 0xe6, 0x9f, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xbf, 0x17, 0x09, 0x79, 0x47, 0x02, 0x00, 0x00, +} diff --git a/protocol/group_message.proto b/protocol/group_message.proto index 275f58b..7c5231e 100644 --- a/protocol/group_message.proto +++ b/protocol/group_message.proto @@ -29,4 +29,5 @@ message DecryptedGroupMessage { required int32 timestamp = 2; required string text = 3; required bytes signature = 4; + required bytes signed_group_id = 5; }