removing panics and reorder invite-to-group args

This commit is contained in:
Dan Ballard 2018-09-26 17:08:54 -07:00
parent aef41ed4fa
commit a5ad2a6644
8 changed files with 66 additions and 45 deletions

View File

@ -57,8 +57,11 @@ func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer
log.Printf("CreatePeer(%v)\n", name) log.Printf("CreatePeer(%v)\n", name)
randomFileName := generateRandomFilename() randomFileName := generateRandomFilename()
p := peer.NewCwtchPeer(name, password, path.Join(app.directory, "profiles", randomFileName)) p, err := peer.NewCwtchPeer(name, password, path.Join(app.directory, "profiles", randomFileName))
err := p.Save() if err != nil {
return nil, err
}
err = p.Save()
if err != nil { if err != nil {
p.Shutdown() //attempt p.Shutdown() //attempt
return nil, fmt.Errorf("Error attempting to save new profile: %v", err) 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() { go func() {
e := peer.Listen() e := peer.Listen()
if e != nil { if e != nil {
log.Panic(e) log.Fatalf("ERROR: peer %v has crashed with: %v\n", peer.GetProfile().Onion, e)
} }
}() }()
} }

View File

@ -72,7 +72,7 @@ var usages = map[string]string{
"/timeline": "/timeline [groupid]", "/timeline": "/timeline [groupid]",
"/accept-invite": "/accept-invite [groupid]", "/accept-invite": "/accept-invite [groupid]",
"/invite": "/invite [peerid]", "/invite": "/invite [peerid]",
"/invite-to-group": "/invite-to-group [peerid] [groupid]", "/invite-to-group": "/invite-to-group [groupid] [peerid]",
"/new-group": "/new-group [server]", "/new-group": "/new-group [server]",
"/help": "", "/help": "",
"/trust": "/trust [peerid]", "/trust": "/trust [peerid]",
@ -175,26 +175,26 @@ func completer(d prompt.Document) []prompt.Suggest {
return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
} }
// suggest peerid AND groupid // suggest groupid AND peerid
if strings.HasPrefix(w, "/invite-to-group") { if strings.HasPrefix(w, "/invite-to-group") {
if d.FindStartOfPreviousWordWithSpace() == 0 { if d.FindStartOfPreviousWordWithSpace() == 0 {
s = []prompt.Suggest{} s = []prompt.Suggest{}
contacts := peer.GetContacts() groups := peer.GetGroups()
for _, onion := range contacts { for _, groupID := range groups {
contact := peer.GetContact(onion) group := peer.GetGroup(groupID)
s = append(s, prompt.Suggest{Text: contact.Onion, Description: contact.Name}) 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) return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
} }
s = []prompt.Suggest{} s = []prompt.Suggest{}
groups := peer.GetGroups() contacts := peer.GetContacts()
for _, groupID := range groups { for _, onion := range contacts {
group := peer.GetGroup(groupID) contact := peer.GetContact(onion)
if group.Owner == "self" { s = append(s, prompt.Suggest{Text: contact.Onion, Description: contact.Name})
s = append(s, prompt.Suggest{Text: group.GroupID, Description: "Group owned by " + group.Owner + " on " + group.GroupServer})
}
} }
return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
} }
@ -450,7 +450,7 @@ func main() {
case "/invite-to-group": case "/invite-to-group":
if len(commands) == 3 { if len(commands) == 3 {
fmt.Printf("Inviting %v to %v\n", commands[1], commands[2]) 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 { if err != nil {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"golang.org/x/crypto/nacl/secretbox" "golang.org/x/crypto/nacl/secretbox"
"io" "io"
"log"
"sync" "sync"
"time" "time"
) )
@ -29,23 +30,25 @@ type Group struct {
} }
// NewGroup initializes a new group associated with a given CwtchServer // 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 := new(Group)
group.GroupServer = server group.GroupServer = server
var groupID [16]byte var groupID [16]byte
if _, err := io.ReadFull(rand.Reader, groupID[:]); err != nil { 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) group.GroupID = fmt.Sprintf("%x", groupID)
var groupKey [32]byte var groupKey [32]byte
if _, err := io.ReadFull(rand.Reader, groupKey[:]); err != nil { 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[:]) copy(group.GroupKey[:], groupKey[:])
group.Owner = "self" group.Owner = "self"
return group return group, nil
} }
// SignGroup adds a signature to the group. // 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. //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 var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { 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) wire, err := proto.Marshal(message)
utils.CheckError(err) utils.CheckError(err)
encrypted := secretbox.Seal(nonce[:], []byte(wire), &nonce, &g.GroupKey) 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 // DecryptMessage takes a ciphertext and returns true and the decrypted message if the

View File

@ -8,7 +8,7 @@ import (
) )
func TestGroup(t *testing.T) { func TestGroup(t *testing.T) {
g := NewGroup("server.onion") g, _ := NewGroup("server.onion")
dgm := &protocol.DecryptedGroupMessage{ dgm := &protocol.DecryptedGroupMessage{
Onion: proto.String("onion"), Onion: proto.String("onion"),
Text: proto.String("Hello World!"), Text: proto.String("Hello World!"),
@ -17,7 +17,7 @@ func TestGroup(t *testing.T) {
PreviousMessageSig: []byte{}, PreviousMessageSig: []byte{},
Padding: []byte{}, Padding: []byte{},
} }
encMessage := g.EncryptMessage(dgm) encMessage, _ := g.EncryptMessage(dgm)
ok, message := g.DecryptMessage(encMessage) ok, message := g.DecryptMessage(encMessage)
if !ok || message.GetText() != "Hello World!" { if !ok || message.GetText() != "Hello World!" {
t.Errorf("group encryption was invalid, or returned wrong message decrypted:%v message:%v", ok, message) t.Errorf("group encryption was invalid, or returned wrong message decrypted:%v message:%v", ok, message)

View File

@ -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 //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. // invite which can be sent on the wire.
func (p *Profile) StartGroupWithMessage(server string, initialMessage []byte) (groupID string, invite []byte, err error) { 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 groupID = group.GroupID
signedGroupID := p.SignMessage(groupID + server) signedGroupID := p.SignMessage(groupID + server)
group.SignGroup(signedGroupID) group.SignGroup(signedGroupID)
@ -336,7 +339,10 @@ func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte,
PreviousMessageSig: prevSig, PreviousMessageSig: prevSig,
Padding: padding[:], 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)) signature := p.SignMessage(groupID + group.GroupServer + string(ciphertext))
return ciphertext, signature, nil return ciphertext, signature, nil
} }

View File

@ -75,24 +75,26 @@ type CwtchPeer interface {
} }
// createKey derives a key and salt for use in encrypting cwtchPeers // 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 var salt [128]byte
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil { 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) dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512)
var dkr [32]byte var dkr [32]byte
copy(dkr[:], dk) copy(dkr[:], dk)
return dkr, salt return dkr, salt, nil
} }
//encryptProfile encrypts the cwtchPeer via the specified key. //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 var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { 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 //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 cpc.salt = blanksalt
bytes, _ := json.Marshal(cpc) bytes, _ := json.Marshal(cpc)
encrypted := secretbox.Seal(nonce[:], []byte(bytes), &nonce, &key) 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. //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. // 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 := new(cwtchPeer)
cp.profilefile = profilefile cp.profilefile = profilefile
cp.Profile = model.GenerateNewProfile(name) cp.Profile = model.GenerateNewProfile(name)
cp.setup() cp.setup()
key, salt := createKey(password) key, salt, err := createKey(password)
if err != nil {
return nil, err
}
cp.key = key cp.key = key
cp.salt = salt cp.salt = salt
return cp return cp, nil
} }
// Save saves the cwtchPeer profile state to a file. // Save saves the cwtchPeer profile state to a file.
func (cp *cwtchPeer) Save() error { func (cp *cwtchPeer) Save() error {
cp.mutex.Lock() 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 // the salt for the derived key is appended to the front of the file
encryptedbytes = append(cp.salt[:], encryptedbytes...) encryptedbytes = append(cp.salt[:], encryptedbytes...)
err := ioutil.WriteFile(cp.profilefile, encryptedbytes, 0600) err = ioutil.WriteFile(cp.profilefile, encryptedbytes, 0600)
cp.mutex.Unlock()
return err return err
} }

View File

@ -6,7 +6,7 @@ import (
func TestCwtchPeerGenerate(t *testing.T) { func TestCwtchPeerGenerate(t *testing.T) {
alice := NewCwtchPeer("alice", "testpass", "./alice.json") alice, _ := NewCwtchPeer("alice", "testpass", "./alice.json")
alice.Save() alice.Save()
aliceLoaded, err := LoadCwtchPeer("./alice.json", "testpass") aliceLoaded, err := LoadCwtchPeer("./alice.json", "testpass")
@ -26,8 +26,8 @@ func TestCwtchPeerGenerate(t *testing.T) {
func TestTrustPeer(t *testing.T) { func TestTrustPeer(t *testing.T) {
groupName := "test.server" groupName := "test.server"
alice := NewCwtchPeer("alice", "alicepass", "") alice, _ := NewCwtchPeer("alice", "alicepass", "")
bob := NewCwtchPeer("bob", "bobpass", "") bob, _ := NewCwtchPeer("bob", "bobpass", "")
bobOnion := bob.GetProfile().Onion bobOnion := bob.GetProfile().Onion
aliceOnion := alice.GetProfile().Onion aliceOnion := alice.GetProfile().Onion

View File

@ -143,17 +143,17 @@ func TestCwtchPeerIntegration(t *testing.T) {
// ***** Peer setup ***** // ***** Peer setup *****
fmt.Println("Creating Alice...") fmt.Println("Creating Alice...")
alice := peer.NewCwtchPeer("Alice", "alicepass", "") alice, _ := peer.NewCwtchPeer("Alice", "alicepass", "")
go alice.Listen() go alice.Listen()
fmt.Println("Alice created:", alice.GetProfile().Onion) fmt.Println("Alice created:", alice.GetProfile().Onion)
fmt.Println("Creating Bob...") fmt.Println("Creating Bob...")
bob := peer.NewCwtchPeer("Bob", "bobpass", "") bob, _ := peer.NewCwtchPeer("Bob", "bobpass", "")
go bob.Listen() go bob.Listen()
fmt.Println("Bob created:", bob.GetProfile().Onion) fmt.Println("Bob created:", bob.GetProfile().Onion)
fmt.Println("Creating Carol...") fmt.Println("Creating Carol...")
carol := peer.NewCwtchPeer("Carol", "carolpass", "") carol, _ := peer.NewCwtchPeer("Carol", "carolpass", "")
go carol.Listen() go carol.Listen()
fmt.Println("Carol created:", carol.GetProfile().Onion) fmt.Println("Carol created:", carol.GetProfile().Onion)