forked from cwtch.im/cwtch
Merge branch 'bugfix' of dan/cwtch into master
This commit is contained in:
commit
85885f183b
|
@ -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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue