diff --git a/app/app.go b/app/app.go index a43e2f8..054bd40 100644 --- a/app/app.go +++ b/app/app.go @@ -57,8 +57,11 @@ func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer log.Printf("CreatePeer(%v)\n", name) randomFileName := generateRandomFilename() - p := peer.NewCwtchPeer(name, password, path.Join(app.directory, "profiles", randomFileName)) - err := p.Save() + p, err := peer.NewCwtchPeer(name, password, path.Join(app.directory, "profiles", randomFileName)) + if err != nil { + return nil, err + } + err = p.Save() if err != nil { p.Shutdown() //attempt return nil, fmt.Errorf("Error attempting to save new profile: %v", err) @@ -131,7 +134,7 @@ func (app *application) startPeer(peer peer.CwtchPeer) { go func() { e := peer.Listen() if e != nil { - log.Panic(e) + log.Fatalf("ERROR: peer %v has crashed with: %v\n", peer.GetProfile().Onion, e) } }() } diff --git a/app/cli/main.go b/app/cli/main.go index 67cd068..279a34c 100644 --- a/app/cli/main.go +++ b/app/cli/main.go @@ -72,7 +72,7 @@ var usages = map[string]string{ "/timeline": "/timeline [groupid]", "/accept-invite": "/accept-invite [groupid]", "/invite": "/invite [peerid]", - "/invite-to-group": "/invite-to-group [peerid] [groupid]", + "/invite-to-group": "/invite-to-group [groupid] [peerid]", "/new-group": "/new-group [server]", "/help": "", "/trust": "/trust [peerid]", @@ -175,26 +175,26 @@ func completer(d prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) } - // suggest peerid AND groupid + // suggest groupid AND peerid if strings.HasPrefix(w, "/invite-to-group") { if d.FindStartOfPreviousWordWithSpace() == 0 { s = []prompt.Suggest{} - contacts := peer.GetContacts() - for _, onion := range contacts { - contact := peer.GetContact(onion) - s = append(s, prompt.Suggest{Text: contact.Onion, Description: contact.Name}) + groups := peer.GetGroups() + for _, groupID := range groups { + group := peer.GetGroup(groupID) + if group.Owner == "self" { + s = append(s, prompt.Suggest{Text: group.GroupID, Description: "Group owned by " + group.Owner + " on " + group.GroupServer}) + } } return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) } s = []prompt.Suggest{} - groups := peer.GetGroups() - for _, groupID := range groups { - group := peer.GetGroup(groupID) - if group.Owner == "self" { - s = append(s, prompt.Suggest{Text: group.GroupID, Description: "Group owned by " + group.Owner + " on " + group.GroupServer}) - } + contacts := peer.GetContacts() + for _, onion := range contacts { + contact := peer.GetContact(onion) + s = append(s, prompt.Suggest{Text: contact.Onion, Description: contact.Name}) } return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) } @@ -450,7 +450,7 @@ func main() { case "/invite-to-group": if len(commands) == 3 { fmt.Printf("Inviting %v to %v\n", commands[1], commands[2]) - err := peer.InviteOnionToGroup(commands[1], commands[2]) + err := peer.InviteOnionToGroup(commands[2], commands[1]) if err != nil { fmt.Printf("Error: %v\n", err) } diff --git a/model/group.go b/model/group.go index 4939a93..5ef655a 100644 --- a/model/group.go +++ b/model/group.go @@ -9,6 +9,7 @@ import ( "github.com/golang/protobuf/proto" "golang.org/x/crypto/nacl/secretbox" "io" + "log" "sync" "time" ) @@ -29,23 +30,25 @@ type Group struct { } // NewGroup initializes a new group associated with a given CwtchServer -func NewGroup(server string) *Group { +func NewGroup(server string) (*Group, error) { group := new(Group) group.GroupServer = server var groupID [16]byte if _, err := io.ReadFull(rand.Reader, groupID[:]); err != nil { - panic(err) + log.Printf("Error: Cannot read from random: %v\n", err) + return nil, err } group.GroupID = fmt.Sprintf("%x", groupID) var groupKey [32]byte if _, err := io.ReadFull(rand.Reader, groupKey[:]); err != nil { - panic(err) + log.Printf("Error: Cannot read from random: %v\n", err) + return nil, err } copy(group.GroupKey[:], groupKey[:]) group.Owner = "self" - return group + return group, nil } // SignGroup adds a signature to the group. @@ -117,15 +120,16 @@ func (g *Group) GetTimeline() (t []Message) { } //EncryptMessage takes a message and encrypts the message under the group key. -func (g *Group) EncryptMessage(message *protocol.DecryptedGroupMessage) []byte { +func (g *Group) EncryptMessage(message *protocol.DecryptedGroupMessage) ([]byte, error) { var nonce [24]byte if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { - panic(err) + log.Printf("Error: Cannot read from random: %v\n", err) + return nil, err } wire, err := proto.Marshal(message) utils.CheckError(err) encrypted := secretbox.Seal(nonce[:], []byte(wire), &nonce, &g.GroupKey) - return encrypted + return encrypted, nil } // DecryptMessage takes a ciphertext and returns true and the decrypted message if the diff --git a/model/group_test.go b/model/group_test.go index 911a0b6..07ed778 100644 --- a/model/group_test.go +++ b/model/group_test.go @@ -8,7 +8,7 @@ import ( ) func TestGroup(t *testing.T) { - g := NewGroup("server.onion") + g, _ := NewGroup("server.onion") dgm := &protocol.DecryptedGroupMessage{ Onion: proto.String("onion"), Text: proto.String("Hello World!"), @@ -17,7 +17,7 @@ func TestGroup(t *testing.T) { PreviousMessageSig: []byte{}, Padding: []byte{}, } - encMessage := g.EncryptMessage(dgm) + encMessage, _ := g.EncryptMessage(dgm) ok, message := g.DecryptMessage(encMessage) if !ok || message.GetText() != "Hello World!" { t.Errorf("group encryption was invalid, or returned wrong message decrypted:%v message:%v", ok, message) diff --git a/model/profile.go b/model/profile.go index 4637cee..c510899 100644 --- a/model/profile.go +++ b/model/profile.go @@ -222,7 +222,10 @@ func (p *Profile) StartGroup(server string) (groupID string, invite []byte, err //StartGroupWithMessage when given a server, and an initial message creates a new Group under this profile and returns the group id an a precomputed // invite which can be sent on the wire. func (p *Profile) StartGroupWithMessage(server string, initialMessage []byte) (groupID string, invite []byte, err error) { - group := NewGroup(server) + group, err := NewGroup(server) + if err != nil { + return "", nil, err + } groupID = group.GroupID signedGroupID := p.SignMessage(groupID + server) group.SignGroup(signedGroupID) @@ -336,7 +339,10 @@ func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte, PreviousMessageSig: prevSig, Padding: padding[:], } - ciphertext := group.EncryptMessage(dm) + ciphertext, err := group.EncryptMessage(dm) + if err != nil { + return nil, nil, err + } signature := p.SignMessage(groupID + group.GroupServer + string(ciphertext)) return ciphertext, signature, nil } diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index 29051ec..40fc518 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -75,24 +75,26 @@ type CwtchPeer interface { } // createKey derives a key and salt for use in encrypting cwtchPeers -func createKey(password string) ([32]byte, [128]byte) { +func createKey(password string) ([32]byte, [128]byte, error) { var salt [128]byte if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil { - panic(err) + log.Printf("Error: Cannot read from random: %v\n", err) + return [32]byte{}, salt, err } dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512) var dkr [32]byte copy(dkr[:], dk) - return dkr, salt + return dkr, salt, nil } //encryptProfile encrypts the cwtchPeer via the specified key. -func encryptProfile(p *cwtchPeer, key [32]byte) []byte { +func encryptProfile(p *cwtchPeer, key [32]byte) ([]byte, error) { var nonce [24]byte if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { - panic(err) + log.Printf("Error: Cannot read from random: %v\n", err) + return nil, err } //copy the struct, then remove the key and salt before saving the copy @@ -104,7 +106,7 @@ func encryptProfile(p *cwtchPeer, key [32]byte) []byte { cpc.salt = blanksalt bytes, _ := json.Marshal(cpc) encrypted := secretbox.Seal(nonce[:], []byte(bytes), &nonce, &key) - return encrypted + return encrypted, nil } //decryptProfile decrypts the passed ciphertext into a cwtchPeer via the specified key. @@ -145,26 +147,32 @@ func (cp *cwtchPeer) setup() { } // NewCwtchPeer creates and returns a new cwtchPeer with the given name. -func NewCwtchPeer(name string, password string, profilefile string) CwtchPeer { +func NewCwtchPeer(name string, password string, profilefile string) (CwtchPeer, error) { cp := new(cwtchPeer) cp.profilefile = profilefile cp.Profile = model.GenerateNewProfile(name) cp.setup() - key, salt := createKey(password) + key, salt, err := createKey(password) + if err != nil { + return nil, err + } cp.key = key cp.salt = salt - return cp + return cp, nil } // Save saves the cwtchPeer profile state to a file. func (cp *cwtchPeer) Save() error { cp.mutex.Lock() - encryptedbytes := encryptProfile(cp, cp.key) + defer cp.mutex.Unlock() + encryptedbytes, err := encryptProfile(cp, cp.key) + if err != nil { + return err + } // the salt for the derived key is appended to the front of the file encryptedbytes = append(cp.salt[:], encryptedbytes...) - err := ioutil.WriteFile(cp.profilefile, encryptedbytes, 0600) - cp.mutex.Unlock() + err = ioutil.WriteFile(cp.profilefile, encryptedbytes, 0600) return err } diff --git a/peer/cwtch_peer_test.go b/peer/cwtch_peer_test.go index 02d404e..6a6d075 100644 --- a/peer/cwtch_peer_test.go +++ b/peer/cwtch_peer_test.go @@ -6,7 +6,7 @@ import ( func TestCwtchPeerGenerate(t *testing.T) { - alice := NewCwtchPeer("alice", "testpass", "./alice.json") + alice, _ := NewCwtchPeer("alice", "testpass", "./alice.json") alice.Save() aliceLoaded, err := LoadCwtchPeer("./alice.json", "testpass") @@ -26,8 +26,8 @@ func TestCwtchPeerGenerate(t *testing.T) { func TestTrustPeer(t *testing.T) { groupName := "test.server" - alice := NewCwtchPeer("alice", "alicepass", "") - bob := NewCwtchPeer("bob", "bobpass", "") + alice, _ := NewCwtchPeer("alice", "alicepass", "") + bob, _ := NewCwtchPeer("bob", "bobpass", "") bobOnion := bob.GetProfile().Onion aliceOnion := alice.GetProfile().Onion diff --git a/testing/cwtch_peer_server_intergration_test.go b/testing/cwtch_peer_server_intergration_test.go index 03e4cbd..2c14ebf 100644 --- a/testing/cwtch_peer_server_intergration_test.go +++ b/testing/cwtch_peer_server_intergration_test.go @@ -143,17 +143,17 @@ func TestCwtchPeerIntegration(t *testing.T) { // ***** Peer setup ***** fmt.Println("Creating Alice...") - alice := peer.NewCwtchPeer("Alice", "alicepass", "") + alice, _ := peer.NewCwtchPeer("Alice", "alicepass", "") go alice.Listen() fmt.Println("Alice created:", alice.GetProfile().Onion) fmt.Println("Creating Bob...") - bob := peer.NewCwtchPeer("Bob", "bobpass", "") + bob, _ := peer.NewCwtchPeer("Bob", "bobpass", "") go bob.Listen() fmt.Println("Bob created:", bob.GetProfile().Onion) fmt.Println("Creating Carol...") - carol := peer.NewCwtchPeer("Carol", "carolpass", "") + carol, _ := peer.NewCwtchPeer("Carol", "carolpass", "") go carol.Listen() fmt.Println("Carol created:", carol.GetProfile().Onion)