diff --git a/model/group.go b/model/group.go index c34904d..07e1ac2 100644 --- a/model/group.go +++ b/model/group.go @@ -3,11 +3,12 @@ package model import ( "crypto/rand" "fmt" + "git.mascherari.press/cwtch/protocol" + "github.com/golang/protobuf/proto" + "github.com/s-rah/go-ricochet/utils" "golang.org/x/crypto/nacl/secretbox" "io" - "github.com/golang/protobuf/proto" - "git.mascherari.press/cwtch/protocol" - "github.com/s-rah/go-ricochet/utils" + "log" "time" ) @@ -52,7 +53,9 @@ func (g *Group) Invite() []byte { GroupName: g.GroupID, GroupSharedKey: g.GroupKey[:], ServerHost: g.GroupServer, + SignedGroupId: g.SignedGroupID[:], } + log.Printf("INVITEBEFORE %v", gci) cp := &protocol.CwtchPeerPacket{ GroupChatInvite: gci, } @@ -63,11 +66,11 @@ func (g *Group) Invite() []byte { func (g *Group) AddMessage(message *protocol.DecryptedGroupMessage, verified bool) { timelineMessage := Message{ - Message: message.GetText(), - Timestamp: time.Unix(int64(message.GetTimestamp()),0), + Message: message.GetText(), + Timestamp: time.Unix(int64(message.GetTimestamp()), 0), Signature: message.GetSignature(), - Verified:verified, - PeerID: message.GetOnion(), + Verified: verified, + PeerID: message.GetOnion(), } g.Timeline = append(g.Timeline, timelineMessage) } @@ -88,7 +91,7 @@ func (g *Group) EncryptMessage(message *protocol.DecryptedGroupMessage) []byte { if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { panic(err) } - wire,err := proto.Marshal(message) + wire, err := proto.Marshal(message) utils.CheckError(err) encrypted := secretbox.Seal(nonce[:], []byte(wire), &nonce, &g.GroupKey) return encrypted diff --git a/model/group_test.go b/model/group_test.go index 3785d71..b73e9c9 100644 --- a/model/group_test.go +++ b/model/group_test.go @@ -1,21 +1,20 @@ package model import ( - "testing" "git.mascherari.press/cwtch/protocol" "github.com/golang/protobuf/proto" + "testing" "time" ) func TestGroup(t *testing.T) { g := NewGroup("server.onion") dgm := &protocol.DecryptedGroupMessage{ - Onion: proto.String("onion"), - Text: proto.String("Hello World!"), - Timestamp: proto.Int32(int32(time.Now().Unix())), + Onion: proto.String("onion"), + Text: proto.String("Hello World!"), + Timestamp: proto.Int32(int32(time.Now().Unix())), SignedGroupId: []byte{}, - Signature: []byte{}, - + Signature: []byte{}, } encMessage := g.EncryptMessage(dgm) ok, message := g.DecryptMessage(encMessage) diff --git a/model/message.go b/model/message.go index d045ae2..ad3d902 100644 --- a/model/message.go +++ b/model/message.go @@ -4,7 +4,6 @@ import ( "time" ) - // Message is a local representation of a given message sent over a group chat channel. type Message struct { Timestamp time.Time diff --git a/model/profile.go b/model/profile.go index 676034f..2fa0e40 100644 --- a/model/profile.go +++ b/model/profile.go @@ -3,34 +3,33 @@ package model import ( "crypto/rand" "crypto/rsa" + "encoding/asn1" "encoding/json" "git.mascherari.press/cwtch/protocol" "github.com/golang/protobuf/proto" "github.com/s-rah/go-ricochet/utils" "golang.org/x/crypto/ed25519" "io/ioutil" - "time" - "encoding/asn1" + "log" "strconv" + "time" ) - // PublicProfile is a local copy of a CwtchIdentity type PublicProfile struct { Name string Ed25519PublicKey ed25519.PublicKey - Trusted bool - Blocked bool + Trusted bool + Blocked bool } - // Profile encapsulates all the attributes necessary to be a Cwtch Peer. type Profile struct { PublicProfile Contacts map[string]PublicProfile Ed25519PrivateKey ed25519.PrivateKey OnionPrivateKey *rsa.PrivateKey - Onion string + Onion string Groups map[string]*Group } @@ -96,6 +95,12 @@ func (p *Profile) VerifyMessage(onion string, message string, signature []byte) return ed25519.Verify(p.Contacts[onion].Ed25519PublicKey, []byte(message), signature) } +// VerifyMessage confirms the authenticity of a message given an onion, message and signature. +func (p *Profile) VerifyGroupMessage(onion string, groupID string, message string, timestamp int32, signature []byte) bool { + m := message + groupID + strconv.Itoa(int(timestamp)) + return ed25519.Verify(p.Contacts[onion].Ed25519PublicKey, []byte(m), signature) +} + // SignMessage takes a given message and returns an Ed21159 signature func (p *Profile) SignMessage(message string) []byte { sig := ed25519.Sign(p.Ed25519PrivateKey, []byte(message)) @@ -107,11 +112,14 @@ func (p *Profile) SignMessage(message string) []byte { func (p *Profile) StartGroup(server string) (groupID string, invite []byte) { group := NewGroup(server) groupID = group.GroupID + signedGroupId := p.SignMessage(groupID) + group.SignGroup(signedGroupId) invite = group.Invite() + p.AddGroup(group) return } -func (p *Profile) GetGroupByGroupId(groupID string) (*Group) { +func (p *Profile) GetGroupByGroupId(groupID string) *Group { return p.Groups[groupID] } @@ -119,6 +127,7 @@ func (p *Profile) GetGroupByGroupId(groupID string) (*Group) { func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname string) { group := new(Group) group.GroupID = gci.GetGroupName() + group.SignedGroupID = gci.GetSignedGroupId() copy(group.GroupKey[:], gci.GetGroupSharedKey()[:]) group.GroupServer = gci.GetServerHost() group.Accepted = false @@ -126,7 +135,7 @@ func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname stri p.AddGroup(group) } -// AddGroup is a conveniance method for adding a group to a profle. +// AddGroup is a conveniance method for adding a group to a profile. func (p *Profile) AddGroup(group *Group) { existingGroup, exists := p.Groups[group.GroupID] if !exists { @@ -145,12 +154,13 @@ func (p *Profile) AddGroup(group *Group) { } // AttemptDecryption takes a ciphertext and signature and attempts to decrypt it under known groups. -func (p *Profile) AttemptDecryption(ciphertext []byte, signature []byte) { +func (p *Profile) AttemptDecryption(ciphertext []byte) { for _, group := range p.Groups { success, dgm := group.DecryptMessage(ciphertext) + log.Printf("Decrypt Attempt %v %v", success, dgm) if success { // FIXME - verified := p.VerifyMessage(dgm.GetOnion(), dgm.GetText(), dgm.GetSignature()) + verified := p.VerifyGroupMessage(dgm.GetOnion(), group.GroupID, dgm.GetText(), dgm.GetTimestamp(), dgm.GetSignature()) group.AddMessage(dgm, verified) } } @@ -162,12 +172,12 @@ func (p *Profile) EncryptMessageToGroup(message string, groupID string) (ciphert 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, + 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 fc23380..38c43bd 100644 --- a/model/profile_test +++ b/model/profile_test @@ -1 +1 @@ -{"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 +{"Name":"Sarah","Ed25519PublicKey":"57Vq0cuSR354/iWji5HYo1wRLejxtUHt3oG7Su/apTs=","Trusted":false,"Blocked":false,"Contacts":{},"Ed25519PrivateKey":"J8ccHg0nDRTt2hUcWFgcja7zp3q+stire73hfHQDlGDntWrRy5JHfnj+JaOLkdijXBEt6PG1Qe3egbtK79qlOw==","OnionPrivateKey":{"N":106324796443231372795329965451792784374908475257731042677987977989945962496419819543299936408371285042498791020833780363910141715665590232533252599354509160780458961503863367842703504977430864504358617442294691508603803037645994752383061089701523494391193738887906210739432038880652477581768063943643214298049,"E":65537,"D":52882540623824249319263554234503221073355763301661673056925036705376788585582196893867658378736750343245656531655368795367730890395281737333932003731626103306294071690361237344309695692052986927494790956908776032726936870574424804180862906949851814015510618834696423905205181829311680766288721499332239092113,"Primes":[10562771257917076828400600569279483678021278216385409474099349073602122852652613259317666843104807170827922546052248527778489213047126675685744594423055011,10065994410656021893110220970838146088057898596082565348257922502730196228417367015775815130802667391440031211811041050535960601302390904626913561708368459],"Precomputed":{"Dp":14666710170902757089650955213153379231578136284710503412469914181268492295823547104657028590300707272919739257072411249032493376066779490782348262698903,"Dq":8351748051846008307669886865591879879827216596130210009260002655117828861809706712999156561217721929245207091771627754763620453429647494239789002112611857,"Qinv":5157808396374745421942830050538059633959311365944586097995200192447276074688521466971512517666145546534306106545362826255395595072097617674779280863162908,"CRTValues":[]}},"Onion":"cf3nb4kb4ekfep7t","Groups":{}} \ No newline at end of file diff --git a/model/profile_test.go b/model/profile_test.go index 8671311..e748865 100644 --- a/model/profile_test.go +++ b/model/profile_test.go @@ -38,41 +38,26 @@ 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) + sarah.AddContact(alice.Onion, alice.PublicProfile) + alice.AddContact(sarah.Onion, sarah.PublicProfile) - group := NewGroup("server.onion") - alice.AddGroup(group) - sarah.AddGroup(group) + gid, invite := alice.StartGroup("aaa.onion") + gci := &protocol.CwtchPeerPacket{} + proto.Unmarshal(invite, gci) + sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion) + group := alice.GetGroupByGroupId(gid) c, s := sarah.EncryptMessageToGroup("Hello World", group.GroupID) - ok, gid, onion, message := alice.AttemptDecryption(c, s) + alice.AttemptDecryption(c, s) - if ok && gid == group.GroupID && onion == "sarah.onion" && message == "Hello World" { - t.Logf("Success!") - } else { - t.Errorf("Failed to decrypt group message %v %v %v %v", ok, gid, onion, message) - } - - group2 := NewGroup("server2.onion") - sarah.AddGroup(group2) - alice.AddGroup(group2) + gid2, invite2 := alice.StartGroup("bbb.onion") + gci2 := &protocol.CwtchPeerPacket{} + proto.Unmarshal(invite2, gci2) + sarah.ProcessInvite(gci2.GetGroupChatInvite(), alice.Onion) + group2 := alice.GetGroupByGroupId(gid2) c2, _ := sarah.EncryptMessageToGroup("Hello World", group2.GroupID) - ok, gid, onion, message = alice.AttemptDecryption(c2, s) - if onion != "not-verified" { - t.Errorf("verification should have failed %v %v %v %v", ok, gid, onion, message) - } + alice.AttemptDecryption(c2, s) - bob := GenerateNewProfile("Bob") - bob.AddGroup(group) - c, s = bob.EncryptMessageToGroup("Hello", group.GroupID) - ok, gid, onion, message = alice.AttemptDecryption(c, s) - - if ok && gid == group.GroupID && onion == "not-verified" && message == "Hello" { - 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 index a35e79f..0b0a4ce 100644 --- a/peer/connections/connectionsmanager.go +++ b/peer/connections/connectionsmanager.go @@ -2,21 +2,22 @@ package connections import ( "git.mascherari.press/cwtch/model" - "time" + "git.mascherari.press/cwtch/protocol" "sync" + "time" ) type Manager struct { peerConnections map[string]*PeerPeerConnection serverConnections map[string]*PeerServerConnection - lock sync.Mutex + lock sync.Mutex } func NewConnectionsManager() *Manager { m := new(Manager) m.peerConnections = make(map[string]*PeerPeerConnection) m.serverConnections = make(map[string]*PeerServerConnection) - return m; + return m } func (m *Manager) ManagePeerConnection(host string, profile *model.Profile) { @@ -28,17 +29,18 @@ func (m *Manager) ManagePeerConnection(host string, profile *model.Profile) { } -func (m *Manager) ManageServerConnection(host string) { +func (m *Manager) ManageServerConnection(host string, handler func(string, *protocol.GroupMessage)) { m.lock.Lock() psc := NewPeerServerConnection(host) go psc.Run() + psc.GroupMessageHandler = handler m.serverConnections[host] = psc m.lock.Unlock() } func (m *Manager) GetPeerPeerConnectionForOnion(host string) (ppc *PeerPeerConnection) { m.lock.Lock() - ppc = m.peerConnections[host] + ppc = m.peerConnections[host] m.lock.Unlock() return } diff --git a/peer/connections/peerserverconnection.go b/peer/connections/peerserverconnection.go index 37003cc..5e7949f 100644 --- a/peer/connections/peerserverconnection.go +++ b/peer/connections/peerserverconnection.go @@ -1,6 +1,7 @@ package connections import ( + "errors" "git.mascherari.press/cwtch/peer/fetch" "git.mascherari.press/cwtch/peer/listen" "git.mascherari.press/cwtch/peer/send" @@ -10,6 +11,7 @@ import ( "github.com/s-rah/go-ricochet/connection" "github.com/s-rah/go-ricochet/identity" "github.com/s-rah/go-ricochet/utils" + "log" "time" ) @@ -36,6 +38,7 @@ func (psc *PeerServerConnection) GetState() ConnectionState { // Run manages the setup and teardown of a peer server connection func (psc *PeerServerConnection) Run() error { + log.Printf("Connecting to %v", psc.Server) rc, err := goricochet.Open(psc.Server) if err == nil { rc.TraceLog(true) @@ -58,7 +61,6 @@ func (psc *PeerServerConnection) Run() error { return nil }) }() - psc.connection.Process(psc) } } @@ -73,25 +75,42 @@ func (psc *PeerServerConnection) Break() error { } func (psc *PeerServerConnection) SendGroupMessage(gm *protocol.GroupMessage) { + for psc.state != AUTHENTICATED { + time.Sleep(time.Second * 2) + } + log.Printf("Opening a Channel to Send") psc.connection.Do(func() error { psc.connection.RequestOpenChannel("im.cwtch.server.send", &send.CwtchPeerSendChannel{}) return nil }) + log.Printf("Waiting...") // TODO We have to wait to receive the channel result before we can continue // We should have a better mechanism for this kindof interaction - time.Sleep(time.Second * 1) - psc.connection.Do(func() error { + log.Printf("CWTCH PEER Sending...") +send: + time.Sleep(time.Second * 2) + err := psc.connection.Do(func() error { channel := psc.connection.Channel("im.cwtch.server.send", channels.Outbound) - if channel != nil { - sendchannel, ok := channel.Handler.(*send.CwtchPeerSendChannel) - if ok { - sendchannel.SendGroupMessage(gm) - } + if channel == nil { + return errors.New("No Channel") + } + sendchannel, ok := channel.Handler.(*send.CwtchPeerSendChannel) + if ok { + sendchannel.SendGroupMessage(gm) + } else { + return errors.New("Failed") } return nil }) + for err != nil { + log.Printf("CHANNEL ERROR %v", err) + goto send + } + + log.Printf("Done") } func (psc *PeerServerConnection) HandleGroupMessage(gm *protocol.GroupMessage) { + log.Printf("Received Group Message: %v", gm) psc.GroupMessageHandler(psc.Server, gm) } diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index 5dc720d..91fe8d5 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -2,6 +2,7 @@ package peer import ( "encoding/json" + "errors" "git.mascherari.press/cwtch/model" "git.mascherari.press/cwtch/peer/connections" "git.mascherari.press/cwtch/peer/peer" @@ -11,7 +12,6 @@ import ( "github.com/s-rah/go-ricochet/connection" "io/ioutil" "sync" - "errors" ) /** @@ -24,12 +24,11 @@ Move CwtchPeerChannel under peer/ Write tests for Peer Channel */ - type CwtchPeer struct { connection.AutoConnectionHandler - Profile *model.Profile - mutex sync.Mutex - Log chan string `json:"-"` + Profile *model.Profile + mutex sync.Mutex + Log chan string `json:"-"` connectionsManager *connections.Manager } @@ -64,8 +63,8 @@ func (cp *CwtchPeer) PeerWithOnion(onion string) { // InviteOnionToGroup kicks off the invite process func (cp *CwtchPeer) InviteOnionToGroup(onion string, groupid string) error { - group:= cp.Profile.GetGroupByGroupId(groupid) - if group == nil { + group := cp.Profile.GetGroupByGroupId(groupid) + if group == nil { invite := group.Invite() ppc := cp.connectionsManager.GetPeerPeerConnectionForOnion(onion) ppc.SendGroupInvite(invite) @@ -73,15 +72,22 @@ func (cp *CwtchPeer) InviteOnionToGroup(onion string, groupid string) error { return errors.New("group id could not be found") } +func (cp *CwtchPeer) ReceiveGroupMessage(server string, gm *protocol.GroupMessage) { + cp.Profile.AttemptDecryption(gm.Ciphertext) +} + func (cp *CwtchPeer) JoinServer(onion string) { - cp.connectionsManager.ManageServerConnection(onion) + cp.connectionsManager.ManageServerConnection(onion, cp.ReceiveGroupMessage) } func (cp *CwtchPeer) SendMessageToGroup(groupid string, message string) { - // Lookup Group - // Lookup Sever Connection - // If no server connection, spin off server connection - // Else group.EncryptMessage(message) and send result to server + group := cp.Profile.GetGroupByGroupId(groupid) + psc := cp.connectionsManager.GetPeerServerConnectionForOnion(group.GroupServer) + ct, _ := cp.Profile.EncryptMessageToGroup(message, groupid) + gm := &protocol.GroupMessage{ + Ciphertext: ct, + } + psc.SendGroupMessage(gm) } func (cp *CwtchPeer) Listen() error { @@ -110,7 +116,6 @@ func (cp *CwtchPeer) Listen() error { return nil } - type CwtchPeerInstance struct { rai *application.ApplicationInstance ra *application.RicochetApplication @@ -121,7 +126,6 @@ func (cpi *CwtchPeerInstance) Init(rai *application.ApplicationInstance, ra *app cpi.ra = ra } - type CwtchPeerHandler struct { Onion string Peer *CwtchPeer diff --git a/peer/cwtch_peer_server_intergration_test.go b/peer/cwtch_peer_server_intergration_test.go new file mode 100644 index 0000000..36ab455 --- /dev/null +++ b/peer/cwtch_peer_server_intergration_test.go @@ -0,0 +1,21 @@ +package peer + +import ( + "testing" + "time" +) + +func TestCwtchPeerIntegration(t *testing.T) { + alice := NewCwtchPeer("Alice") + id, _ := alice.Profile.StartGroup("ylhbhtypevo4ympq") + alice.Profile.AddContact(alice.Profile.Onion, alice.Profile.PublicProfile) + alice.JoinServer("ylhbhtypevo4ympq") + // time.Sleep(time.Second *5) + alice.SendMessageToGroup(id, "Hello") + alice.SendMessageToGroup(id, "My") + alice.SendMessageToGroup(id, "Name Is") + alice.SendMessageToGroup(id, "ALICE!!!") + time.Sleep(time.Second * 5) + group := alice.Profile.Groups[id] + t.Logf("%v", group.Timeline) +} diff --git a/peer/send/peer_send_channel.go b/peer/send/peer_send_channel.go index 5409933..c1756cf 100644 --- a/peer/send/peer_send_channel.go +++ b/peer/send/peer_send_channel.go @@ -81,6 +81,7 @@ func (cplc *CwtchPeerSendChannel) SendGroupMessage(gm *protocol.GroupMessage) { } packet, _ := proto.Marshal(csp) cplc.channel.SendMessage(packet) + cplc.channel.CloseChannel() } // Packet should never be diff --git a/protocol/ControlChannel.pb.go b/protocol/ControlChannel.pb.go new file mode 100644 index 0000000..aa6789f --- /dev/null +++ b/protocol/ControlChannel.pb.go @@ -0,0 +1,325 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: ControlChannel.proto + +/* +Package protocol is a generated protocol buffer package. + +It is generated from these files: + ControlChannel.proto + cwtch-profile.proto + group_message.proto + +It has these top-level messages: + Packet + OpenChannel + ChannelResult + KeepAlive + EnableFeatures + FeaturesEnabled + CwtchPeerPacket + CwtchIdentity + GroupChatInvite + CwtchServerPacket + FetchMessage + GroupMessage + DecryptedGroupMessage +*/ +package protocol + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type ChannelResult_CommonError int32 + +const ( + ChannelResult_GenericError ChannelResult_CommonError = 0 + ChannelResult_UnknownTypeError ChannelResult_CommonError = 1 + ChannelResult_UnauthorizedError ChannelResult_CommonError = 2 + ChannelResult_BadUsageError ChannelResult_CommonError = 3 + ChannelResult_FailedError ChannelResult_CommonError = 4 +) + +var ChannelResult_CommonError_name = map[int32]string{ + 0: "GenericError", + 1: "UnknownTypeError", + 2: "UnauthorizedError", + 3: "BadUsageError", + 4: "FailedError", +} +var ChannelResult_CommonError_value = map[string]int32{ + "GenericError": 0, + "UnknownTypeError": 1, + "UnauthorizedError": 2, + "BadUsageError": 3, + "FailedError": 4, +} + +func (x ChannelResult_CommonError) Enum() *ChannelResult_CommonError { + p := new(ChannelResult_CommonError) + *p = x + return p +} +func (x ChannelResult_CommonError) String() string { + return proto.EnumName(ChannelResult_CommonError_name, int32(x)) +} +func (x *ChannelResult_CommonError) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(ChannelResult_CommonError_value, data, "ChannelResult_CommonError") + if err != nil { + return err + } + *x = ChannelResult_CommonError(value) + return nil +} +func (ChannelResult_CommonError) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} } + +type Packet struct { + // Must contain exactly one field + OpenChannel *OpenChannel `protobuf:"bytes,1,opt,name=open_channel,json=openChannel" json:"open_channel,omitempty"` + ChannelResult *ChannelResult `protobuf:"bytes,2,opt,name=channel_result,json=channelResult" json:"channel_result,omitempty"` + KeepAlive *KeepAlive `protobuf:"bytes,3,opt,name=keep_alive,json=keepAlive" json:"keep_alive,omitempty"` + EnableFeatures *EnableFeatures `protobuf:"bytes,4,opt,name=enable_features,json=enableFeatures" json:"enable_features,omitempty"` + FeaturesEnabled *FeaturesEnabled `protobuf:"bytes,5,opt,name=features_enabled,json=featuresEnabled" json:"features_enabled,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Packet) Reset() { *m = Packet{} } +func (m *Packet) String() string { return proto.CompactTextString(m) } +func (*Packet) ProtoMessage() {} +func (*Packet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Packet) GetOpenChannel() *OpenChannel { + if m != nil { + return m.OpenChannel + } + return nil +} + +func (m *Packet) GetChannelResult() *ChannelResult { + if m != nil { + return m.ChannelResult + } + return nil +} + +func (m *Packet) GetKeepAlive() *KeepAlive { + if m != nil { + return m.KeepAlive + } + return nil +} + +func (m *Packet) GetEnableFeatures() *EnableFeatures { + if m != nil { + return m.EnableFeatures + } + return nil +} + +func (m *Packet) GetFeaturesEnabled() *FeaturesEnabled { + if m != nil { + return m.FeaturesEnabled + } + return nil +} + +type OpenChannel struct { + ChannelIdentifier *int32 `protobuf:"varint,1,req,name=channel_identifier,json=channelIdentifier" json:"channel_identifier,omitempty"` + ChannelType *string `protobuf:"bytes,2,req,name=channel_type,json=channelType" json:"channel_type,omitempty"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *OpenChannel) Reset() { *m = OpenChannel{} } +func (m *OpenChannel) String() string { return proto.CompactTextString(m) } +func (*OpenChannel) ProtoMessage() {} +func (*OpenChannel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +var extRange_OpenChannel = []proto.ExtensionRange{ + {100, 536870911}, +} + +func (*OpenChannel) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_OpenChannel +} + +func (m *OpenChannel) GetChannelIdentifier() int32 { + if m != nil && m.ChannelIdentifier != nil { + return *m.ChannelIdentifier + } + return 0 +} + +func (m *OpenChannel) GetChannelType() string { + if m != nil && m.ChannelType != nil { + return *m.ChannelType + } + return "" +} + +type ChannelResult struct { + ChannelIdentifier *int32 `protobuf:"varint,1,req,name=channel_identifier,json=channelIdentifier" json:"channel_identifier,omitempty"` + Opened *bool `protobuf:"varint,2,req,name=opened" json:"opened,omitempty"` + CommonError *ChannelResult_CommonError `protobuf:"varint,3,opt,name=common_error,json=commonError,enum=protocol.ChannelResult_CommonError" json:"common_error,omitempty"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ChannelResult) Reset() { *m = ChannelResult{} } +func (m *ChannelResult) String() string { return proto.CompactTextString(m) } +func (*ChannelResult) ProtoMessage() {} +func (*ChannelResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +var extRange_ChannelResult = []proto.ExtensionRange{ + {100, 536870911}, +} + +func (*ChannelResult) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_ChannelResult +} + +func (m *ChannelResult) GetChannelIdentifier() int32 { + if m != nil && m.ChannelIdentifier != nil { + return *m.ChannelIdentifier + } + return 0 +} + +func (m *ChannelResult) GetOpened() bool { + if m != nil && m.Opened != nil { + return *m.Opened + } + return false +} + +func (m *ChannelResult) GetCommonError() ChannelResult_CommonError { + if m != nil && m.CommonError != nil { + return *m.CommonError + } + return ChannelResult_GenericError +} + +type KeepAlive struct { + ResponseRequested *bool `protobuf:"varint,1,req,name=response_requested,json=responseRequested" json:"response_requested,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *KeepAlive) Reset() { *m = KeepAlive{} } +func (m *KeepAlive) String() string { return proto.CompactTextString(m) } +func (*KeepAlive) ProtoMessage() {} +func (*KeepAlive) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *KeepAlive) GetResponseRequested() bool { + if m != nil && m.ResponseRequested != nil { + return *m.ResponseRequested + } + return false +} + +type EnableFeatures struct { + Feature []string `protobuf:"bytes,1,rep,name=feature" json:"feature,omitempty"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *EnableFeatures) Reset() { *m = EnableFeatures{} } +func (m *EnableFeatures) String() string { return proto.CompactTextString(m) } +func (*EnableFeatures) ProtoMessage() {} +func (*EnableFeatures) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +var extRange_EnableFeatures = []proto.ExtensionRange{ + {100, 536870911}, +} + +func (*EnableFeatures) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_EnableFeatures +} + +func (m *EnableFeatures) GetFeature() []string { + if m != nil { + return m.Feature + } + return nil +} + +type FeaturesEnabled struct { + Feature []string `protobuf:"bytes,1,rep,name=feature" json:"feature,omitempty"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FeaturesEnabled) Reset() { *m = FeaturesEnabled{} } +func (m *FeaturesEnabled) String() string { return proto.CompactTextString(m) } +func (*FeaturesEnabled) ProtoMessage() {} +func (*FeaturesEnabled) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +var extRange_FeaturesEnabled = []proto.ExtensionRange{ + {100, 536870911}, +} + +func (*FeaturesEnabled) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_FeaturesEnabled +} + +func (m *FeaturesEnabled) GetFeature() []string { + if m != nil { + return m.Feature + } + return nil +} + +func init() { + proto.RegisterType((*Packet)(nil), "protocol.Packet") + proto.RegisterType((*OpenChannel)(nil), "protocol.OpenChannel") + proto.RegisterType((*ChannelResult)(nil), "protocol.ChannelResult") + proto.RegisterType((*KeepAlive)(nil), "protocol.KeepAlive") + proto.RegisterType((*EnableFeatures)(nil), "protocol.EnableFeatures") + proto.RegisterType((*FeaturesEnabled)(nil), "protocol.FeaturesEnabled") + proto.RegisterEnum("protocol.ChannelResult_CommonError", ChannelResult_CommonError_name, ChannelResult_CommonError_value) +} + +func init() { proto.RegisterFile("ControlChannel.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 461 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x4d, 0x8f, 0xd3, 0x30, + 0x10, 0x25, 0xe9, 0xee, 0x92, 0x4e, 0xfa, 0x91, 0x9a, 0x5d, 0x30, 0xb7, 0x12, 0x2e, 0x15, 0x12, + 0x3d, 0x54, 0x20, 0x21, 0x0e, 0x48, 0x4b, 0xd9, 0x22, 0xc4, 0x01, 0x64, 0xd1, 0x73, 0x64, 0x92, + 0x29, 0x1b, 0x35, 0x6b, 0x1b, 0xc7, 0x05, 0x2d, 0xa7, 0xfe, 0x0e, 0xfe, 0x0c, 0x7f, 0x0d, 0xc5, + 0x89, 0x9b, 0x14, 0x09, 0x09, 0x4e, 0xc9, 0x9b, 0xf7, 0xde, 0x8c, 0xfc, 0x66, 0xe0, 0x7c, 0x29, + 0x85, 0xd1, 0xb2, 0x58, 0x5e, 0x73, 0x21, 0xb0, 0x98, 0x2b, 0x2d, 0x8d, 0x24, 0x81, 0xfd, 0xa4, + 0xb2, 0x88, 0x7f, 0xf9, 0x70, 0xf6, 0x91, 0xa7, 0x5b, 0x34, 0xe4, 0x05, 0x0c, 0xa4, 0x42, 0x91, + 0xa4, 0xb5, 0x94, 0x7a, 0x53, 0x6f, 0x16, 0x2e, 0x2e, 0xe6, 0x4e, 0x3b, 0xff, 0xa0, 0x50, 0x34, + 0x7d, 0x58, 0x28, 0x5b, 0x40, 0x5e, 0xc1, 0xa8, 0x31, 0x25, 0x1a, 0xcb, 0x5d, 0x61, 0xa8, 0x6f, + 0xbd, 0x0f, 0x5a, 0xaf, 0xf3, 0x59, 0x9a, 0x0d, 0xd3, 0x2e, 0x24, 0x0b, 0x80, 0x2d, 0xa2, 0x4a, + 0x78, 0x91, 0x7f, 0x43, 0xda, 0xb3, 0xde, 0x7b, 0xad, 0xf7, 0x3d, 0xa2, 0xba, 0xac, 0x28, 0xd6, + 0xdf, 0xba, 0x5f, 0x72, 0x09, 0x63, 0x14, 0xfc, 0x73, 0x81, 0xc9, 0x06, 0xb9, 0xd9, 0x69, 0x2c, + 0xe9, 0x89, 0x35, 0xd2, 0xd6, 0x78, 0x65, 0x05, 0xab, 0x86, 0x67, 0x23, 0x3c, 0xc2, 0xe4, 0x0d, + 0x44, 0xce, 0x9b, 0xd4, 0x54, 0x46, 0x4f, 0x6d, 0x8f, 0x87, 0x6d, 0x0f, 0xa7, 0xae, 0x7b, 0x65, + 0x6c, 0xbc, 0x39, 0x2e, 0xc4, 0x39, 0x84, 0x9d, 0x60, 0xc8, 0x53, 0x20, 0x2e, 0x8b, 0x3c, 0x43, + 0x61, 0xf2, 0x4d, 0x8e, 0x9a, 0x7a, 0x53, 0x7f, 0x76, 0xca, 0x26, 0x0d, 0xf3, 0xee, 0x40, 0x90, + 0x47, 0x30, 0x70, 0x72, 0x73, 0xab, 0x90, 0xfa, 0x53, 0x7f, 0xd6, 0x67, 0x61, 0x53, 0xfb, 0x74, + 0xab, 0xf0, 0x49, 0x10, 0x64, 0xd1, 0x7e, 0xbf, 0xdf, 0xfb, 0xf1, 0x4f, 0x1f, 0x86, 0x47, 0x41, + 0xfe, 0xef, 0xb4, 0xfb, 0x70, 0x56, 0xed, 0x0d, 0x33, 0x3b, 0x27, 0x60, 0x0d, 0x22, 0x2b, 0x18, + 0xa4, 0xf2, 0xe6, 0x46, 0x8a, 0x04, 0xb5, 0x96, 0xda, 0xae, 0x60, 0xb4, 0x78, 0xfc, 0x97, 0xf5, + 0xcd, 0x97, 0x56, 0x7b, 0x55, 0x49, 0x59, 0x98, 0xb6, 0x20, 0x56, 0x10, 0x76, 0x38, 0x12, 0xc1, + 0xe0, 0x2d, 0x0a, 0xd4, 0x79, 0x6a, 0x71, 0x74, 0x87, 0x9c, 0x43, 0xb4, 0x16, 0x5b, 0x21, 0xbf, + 0x8b, 0xea, 0x69, 0x75, 0xd5, 0x23, 0x17, 0x30, 0x59, 0x0b, 0xbe, 0x33, 0xd7, 0x52, 0xe7, 0x3f, + 0x30, 0xab, 0xcb, 0x3e, 0x99, 0xc0, 0xf0, 0x35, 0xcf, 0xd6, 0x25, 0xff, 0xd2, 0x28, 0x7b, 0x64, + 0x0c, 0xe1, 0x8a, 0xe7, 0x85, 0xd3, 0x9c, 0x74, 0xc2, 0x79, 0x09, 0xfd, 0xc3, 0xa1, 0x54, 0xb9, + 0x68, 0x2c, 0x95, 0x14, 0x25, 0x26, 0x1a, 0xbf, 0xee, 0xb0, 0x34, 0x98, 0xd9, 0x5c, 0x02, 0x36, + 0x71, 0x0c, 0x73, 0x44, 0xfc, 0x0c, 0x46, 0xc7, 0xb7, 0x42, 0x28, 0xdc, 0x6d, 0x16, 0x4d, 0xbd, + 0x69, 0x6f, 0xd6, 0x67, 0x0e, 0x76, 0x26, 0x3e, 0x87, 0xf1, 0x1f, 0xd7, 0xf1, 0x2f, 0xb6, 0xdf, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x32, 0x16, 0x1e, 0x93, 0x03, 0x00, 0x00, +} diff --git a/protocol/ControlChannel.proto b/protocol/ControlChannel.proto new file mode 100644 index 0000000..a9efedf --- /dev/null +++ b/protocol/ControlChannel.proto @@ -0,0 +1,53 @@ +syntax = "proto2"; +package protocol; + +message Packet { + // Must contain exactly one field + optional OpenChannel open_channel = 1; + optional ChannelResult channel_result = 2; + optional KeepAlive keep_alive = 3; + optional EnableFeatures enable_features = 4; + optional FeaturesEnabled features_enabled = 5; +} + +message OpenChannel { + required int32 channel_identifier = 1; // Arbitrary unique identifier for this channel instance + required string channel_type = 2; // String identifying channel type; e.g. im.ricochet.chat + + // It is valid to extend the OpenChannel message to add fields specific + // to the requested channel_type. + extensions 100 to max; +} + +message ChannelResult { + required int32 channel_identifier = 1; // Matching the value from OpenChannel + required bool opened = 2; // If the channel is now open + + enum CommonError { + GenericError = 0; + UnknownTypeError = 1; + UnauthorizedError = 2; + BadUsageError = 3; + FailedError = 4; + } + + optional CommonError common_error = 3; + + // As with OpenChannel, it is valid to extend this message with fields specific + // to the channel type. + extensions 100 to max; +} + +message KeepAlive { + required bool response_requested = 1; +} + +message EnableFeatures { + repeated string feature = 1; + extensions 100 to max; +} + +message FeaturesEnabled { + repeated string feature = 1; + extensions 100 to max; +} diff --git a/server/app/main.go b/server/app/main.go new file mode 100644 index 0000000..8687e5e --- /dev/null +++ b/server/app/main.go @@ -0,0 +1,13 @@ +package main + +import ( + cwtchserver "git.mascherari.press/cwtch/server" + "log" +) + +func main() { + server := new(cwtchserver.Server) + log.Printf("starting cwtch server...") + + server.Run("./private_key") +} diff --git a/server/server.go b/server/server.go index 7cb9f85..dd0e199 100644 --- a/server/server.go +++ b/server/server.go @@ -64,5 +64,6 @@ func (s *Server) Run(privateKeyFile string) { }) cwtchserver.Init(pk, af, new(application.AcceptAllContactManager)) + log.Printf("cwtch server running on cwtch:%s", l.Addr().String()[0:16]) cwtchserver.Run(l) } diff --git a/server/server_instance_test.go b/server/server_instance_test.go index 83bcc8f..cbd6bfc 100644 --- a/server/server_instance_test.go +++ b/server/server_instance_test.go @@ -6,6 +6,7 @@ import ( "github.com/s-rah/go-ricochet/application" "os" "testing" + "time" ) func TestServerInstance(t *testing.T) { @@ -27,4 +28,9 @@ func TestServerInstance(t *testing.T) { if len(res) != 1 { t.Errorf("Expected Group Messages Instead got %v", res) } + + // ra.HandleApplicationInstance(ai) + si.HandleGroupMessage(&gm) + + time.Sleep(time.Second * 2) }