removing panics and reorder invite-to-group args
This commit is contained in:
parent
aef41ed4fa
commit
a5ad2a6644
|
@ -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)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue