diff --git a/app/cli/main.go b/app/cli/main.go index 2dc0e7c..a058938 100644 --- a/app/cli/main.go +++ b/app/cli/main.go @@ -25,6 +25,8 @@ var suggestions = []prompt.Suggest{ {Text: "invite-to-group", Description: "invite an existing contact to join an existing group"}, {Text: "new-group", Description: "create a new group"}, {Text: "help", Description: "print list of commands"}, + {Text: "trust", Description: "trust a peer"}, + {Text: "block", Description: "block a peer - you will no longer see messages or connect to this peer"}, } var usages = map[string]string{ @@ -38,10 +40,12 @@ var usages = map[string]string{ "send": "send [groupid] [message]", "timeline": "timeline [groupid]", "accept-invite": "accept-invite [groupid]", - "invite": "invite [onion]", - "invite-to-group": "invite-to-group [onion] [groupid]", + "invite": "invite [peerid]", + "invite-to-group": "invite-to-group [peerid] [groupid]", "new-group": "new-group [server]", "help": "", + "trust": "trust [peerid]", + "block": "block [peerid]", } func completer(d prompt.Document) []prompt.Suggest { @@ -62,6 +66,15 @@ func completer(d prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) } + if strings.HasPrefix(w, "block") || strings.HasPrefix(w, "trust") { + s = []prompt.Suggest{} + contacts := app.Peer.Profile.Contacts + for _, contact := range contacts { + s = append(s, prompt.Suggest{Text: contact.Onion, Description: contact.Name}) + } + return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) + } + if strings.HasPrefix(w, "invite-to-group") { if d.FindStartOfPreviousWordWithSpace() == 0 { @@ -213,12 +226,32 @@ func main() { for gid, g := range app.Peer.Profile.Groups { fmt.Printf("Group Id: %v, Owner: %v Accepted:%v \n", gid, g.Owner, g.Accepted) } + case "trust": + if len(commands) == 2 { + app.Peer.TrustPeer(commands[1]) + } else { + fmt.Printf("Error trusting peer, usage: %s\n", usages["trust"]) + } + case "block": + if len(commands) == 2 { + app.Peer.BlockPeer(commands[1]) + } else { + fmt.Printf("Error blocking peer, usage: %s\n", usages["trust"]) + } case "accept-invite": if len(commands) == 2 { groupID := commands[1] err := app.Peer.AcceptInvite(groupID) - if err == nil { + if err != nil { fmt.Printf("Error: %v\n", err) + } else { + app.Peer.Save(profilefile) + group := app.Peer.Profile.GetGroupByGroupId(groupID) + if group == nil { + fmt.Printf("Error: group does not exist\n") + } else { + app.Peer.JoinServer(group.GroupServer) + } } } else { fmt.Printf("Error accepting invite, usage: %s\n", usages["accept-invite"]) @@ -239,8 +272,14 @@ func main() { id, _ := app.Peer.Profile.StartGroup(commands[1]) fmt.Printf("New Group [%v] created for server %v\n", id, commands[1]) app.Peer.Save(profilefile) + group := app.Peer.Profile.GetGroupByGroupId(id) + if group == nil { + fmt.Printf("Error: group does not exist\n") + } else { + app.Peer.JoinServer(group.GroupServer) + } } else { - fmt.Printf("Error inviting peer, usage: %s\n", usages["new-group"]) + fmt.Printf("Error creating a new group, usage: %s\n", usages["new-group"]) } case "send": if len(commands) > 2 { @@ -286,5 +325,5 @@ func main() { } } } - + app.Peer.Save(profilefile) } diff --git a/model/profile.go b/model/profile.go index 6975cb1..0fe6c8c 100644 --- a/model/profile.go +++ b/model/profile.go @@ -27,7 +27,7 @@ type PublicProfile struct { // Profile encapsulates all the attributes necessary to be a Cwtch Peer. type Profile struct { PublicProfile - Contacts map[string]PublicProfile + Contacts map[string]*PublicProfile Ed25519PrivateKey ed25519.PrivateKey OnionPrivateKey *rsa.PrivateKey Groups map[string]*Group @@ -49,7 +49,7 @@ func GenerateNewProfile(name string) *Profile { }) p.Onion = utils.GetTorHostname(publicKeyBytes) - p.Contacts = make(map[string]PublicProfile) + p.Contacts = make(map[string]*PublicProfile) p.Groups = make(map[string]*Group) return p } @@ -71,11 +71,11 @@ func (p *Profile) GetCwtchIdentityPacket() (message []byte) { // AddCwtchIdentity takes a wire message and if it is a CwtchIdentity message adds the identity as a contact // otherwise returns an error func (p *Profile) AddCwtchIdentity(onion string, ci *protocol.CwtchIdentity) { - p.AddContact(onion, PublicProfile{Name: ci.GetName(), Ed25519PublicKey: ci.GetEd25519PublicKey(), Onion: onion}) + p.AddContact(onion, &PublicProfile{Name: ci.GetName(), Ed25519PublicKey: ci.GetEd25519PublicKey(), Onion: onion}) } // AddContact allows direct manipulation of cwtch contacts -func (p *Profile) AddContact(onion string, profile PublicProfile) { +func (p *Profile) AddContact(onion string, profile *PublicProfile) { p.Contacts[onion] = profile } diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index f04e4c3..3187106 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -42,8 +42,10 @@ func (cp *CwtchPeer) setup() { go cp.connectionsManager.AttemptReconnections() - for onion := range cp.Profile.Contacts { - cp.PeerWithOnion(onion) + for onion, profile := range cp.Profile.Contacts { + if profile.Trusted && !profile.Blocked { + cp.PeerWithOnion(onion) + } } for _, group := range cp.Profile.Groups { @@ -69,6 +71,7 @@ func (cp *CwtchPeer) Save(profilefile string) error { return err } + func LoadCwtchPeer(profilefile string) (*CwtchPeer, error) { bytes, _ := ioutil.ReadFile(profilefile) cp := new(CwtchPeer) @@ -85,13 +88,21 @@ 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 { fmt.Printf("Constructing invite for group: %v\n", group) invite := group.Invite() ppc := cp.connectionsManager.GetPeerPeerConnectionForOnion(onion) - fmt.Printf("Got connection for group: %v - Sending Invite\n", ppc) - ppc.SendGroupInvite(invite) + if ppc == nil { + return errors.New("peer connection not setup for onion. peers must be trusted before sending") + } + if ppc.GetState() == connections.AUTHENTICATED { + fmt.Printf("Got connection for group: %v - Sending Invite\n", ppc) + ppc.SendGroupInvite(invite) + } else { + return errors.New("cannot send invite to onion: peer connection is not ready") + } return nil } return errors.New("group id could not be found") @@ -111,6 +122,9 @@ func (cp *CwtchPeer) SendMessageToGroup(groupid string, message string) error { return errors.New("group does not exit") } psc := cp.connectionsManager.GetPeerServerConnectionForOnion(group.GroupServer) + if psc == nil { + return errors.New("could not find server to send message to") + } ct := cp.Profile.EncryptMessageToGroup(message, groupid) gm := &protocol.GroupMessage{ Ciphertext: ct, @@ -127,6 +141,25 @@ func (cp *CwtchPeer) GetServers() map[string]connections.ConnectionState { return cp.connectionsManager.GetServers() } +func (cp *CwtchPeer) TrustPeer(peer string) error { + _,ok := cp.Profile.Contacts[peer] + if !ok { + return errors.New("peer does not exist") + } + cp.Profile.Contacts[peer].Trusted = true + cp.PeerWithOnion(peer) + return nil +} + +func (cp *CwtchPeer) BlockPeer(peer string) error { + _,ok := cp.Profile.Contacts[peer] + if !ok { + return errors.New("peer does not exist") + } + cp.Profile.Contacts[peer].Blocked = true + return nil +} + func (cp *CwtchPeer) AcceptInvite(groupID string) error { g := cp.Profile.GetGroupByGroupId(groupID) if g == nil { diff --git a/peer/test_profile b/peer/test_profile index d50a95f..e613e7c 100644 --- a/peer/test_profile +++ b/peer/test_profile @@ -1 +1 @@ -{"Profile":{"Name":"alice","Ed25519PublicKey":"zw62CDBry+8i5C0SEoN0Nj4BbZV3/WL8NOra9H9d0Rc=","Trusted":false,"Blocked":false,"Onion":"kzqdsapmr2yv2vd2","Contacts":{},"Ed25519PrivateKey":"QPdpHBOh0noZ4qnJbFoNe0/GZ8ad7FTjYcTXHnedEjHPDrYIMGvL7yLkLRISg3Q2PgFtlXf9Yvw06tr0f13RFw==","OnionPrivateKey":{"N":123164664477389835253620680122644142133034167846356214569188233954912552260600272847127308026339070666831186661931126823098497770011055113279368507758948331028994964297854804880407367476974939435091231218755171920902867841609756476585968731169532272132417713193102472875109807766299347704731437376645583569401,"E":65537,"D":95474837869368994619675777229209306998863769002219124597684816967688822993046304552430056756673387660207589239453851045880540491781308770922253967982633488453491426433469372420149066383652671754998024517772855722930373092680126685828740579513880385080148346315945547711747598115997648389837723796594848638621,"Primes":[11542678133022281192358295468401730074073008626984537974633501998980589167293816424678327803995971301193759072785039888972745795144308685183240956357855487,10670371560048081490791125355690298205739475562665387017071396147459233736780172095647046771702789510862828944882599608718851613825219072553675924318223623],"Precomputed":{"Dp":4601959762237197083710421628909910510176749659192196663551868009389563366529138186984757117210289365227150019268509502402747682703291905872299662002752735,"Dq":6190205940354731804627593359832539447674059857676396758915642789972077859413493798564180817860751288630922326082006151082453245611407741252891628279885593,"Qinv":2696633387876221595461120113944631143313663626921228123107813313410377087409781969143836679473288905741449629397529356977312616100311173108943456308317853,"CRTValues":[]}},"Groups":{}}} \ No newline at end of file +{"Profile":{"Name":"alice","Ed25519PublicKey":"zjwuZF+0Op1KvzzOhqeHSbvXu0U5djPgTwKpAXU//7I=","Trusted":false,"Blocked":false,"Onion":"cgb6lexebisn6vpt","Contacts":{},"Ed25519PrivateKey":"2lHp48zadAzBVtVEjviPijgcVx4Djle0DJ4A5thMgSnOPC5kX7Q6nUq/PM6Gp4dJu9e7RTl2M+BPAqkBdT//sg==","OnionPrivateKey":{"N":147157004733998659199661812977704646877524902240221974034556056889821412558230773103992139173929507205012402066934484772524414251527816699766158278653365683529647303492589077999418406692030398982986134250109986822607454250083546220790801592188854654036322143842183727153411958706262869490046244589596814484207,"E":65537,"D":69165161921072992345197107361524577532819621949517943546186584988284007065187336984028409340899806375574057721100797022263294508290146601200802195055611675878933653952917037806259598451163914910457010547243954518097830998995842836616848924363378592869926704087730180750445525914371624821666069356678694847337,"Primes":[11763039476614882926577453250579526149631348162928663166529586678426982975960723170685888779927126269335313361708713193749880911707050422735813878237869163,12510117391559317555937472239718194296052069062441314136877360692383786524485845690577241127778855759836876789006855569037256017877501872991377132347214989],"Precomputed":{"Dp":1278485896393301662969180760545614916212583623976392995333784669887779418309782735627135599423545789652798222003618781437667298382430080124924886013829471,"Dq":8164208322581015485411991511554498528192425558091078408139596209976876415647033281749066985597474111543452099818777372899635929087702444540354302920340953,"Qinv":2274603529611181793096917499650639342446510507100204826932356686902897257746923946968375987728128727917910029163224849482139316568187669695882958003364055,"CRTValues":[]}},"Groups":{}}} \ No newline at end of file