func TestEncryptFirstMessage(t *testing.T) { alice := "alice" bob := "bob" denameConfig, denameTeardown := denameTestutil.SingleServer(t) defer denameTeardown() aliceDnmc, err := denameClient.NewClient(denameConfig, nil, nil) bobDnmc, err := denameClient.NewClient(denameConfig, nil, nil) if err != nil { t.Fatal(err) } _, serverPubkey, serverAddr, serverTeardown := server.CreateTestServer(t) defer serverTeardown() aliceDir, err := ioutil.TempDir("", "daemon-alice") if err != nil { t.Fatal(err) } defer shred.RemoveAll(aliceDir) bobDir, err := ioutil.TempDir("", "daemon-bob") if err != nil { t.Fatal(err) } defer shred.RemoveAll(bobDir) aliceConf := &Daemon{ Paths: persistence.Paths{ RootDir: aliceDir, Application: "daemon", }, Now: time.Now, foreignDenameClient: aliceDnmc, timelessDenameClient: aliceDnmc, inBuf: make([]byte, proto.SERVER_MESSAGE_SIZE), outBuf: make([]byte, proto.SERVER_MESSAGE_SIZE), LocalAccountConfig: proto.LocalAccountConfig{}, LocalAccount: proto.LocalAccount{ Dename: alice, }, cc: util.NewConnectionCache(util.NewAnonDialer("DANGEROUS_NO_TOR")), } bobConf := &Daemon{ Paths: persistence.Paths{ RootDir: bobDir, Application: "daemon", }, Now: time.Now, foreignDenameClient: bobDnmc, timelessDenameClient: bobDnmc, inBuf: make([]byte, proto.SERVER_MESSAGE_SIZE), outBuf: make([]byte, proto.SERVER_MESSAGE_SIZE), LocalAccountConfig: proto.LocalAccountConfig{}, LocalAccount: proto.LocalAccount{ Dename: bob, }, cc: util.NewConnectionCache(util.NewAnonDialer("DANGEROUS_NO_TOR")), } aliceHomeConn := util.CreateTestAccount(alice, aliceDnmc, &aliceConf.LocalAccountConfig, serverAddr, serverPubkey, t) defer aliceHomeConn.Close() bobHomeConn := util.CreateTestAccount(bob, bobDnmc, &bobConf.LocalAccountConfig, serverAddr, serverPubkey, t) defer bobHomeConn.Close() //fmt.Printf("CBob: %v\n", ([32]byte)(bobConf.TransportSecretKeyForServer)) aliceNotifies := make(chan *util.EnvelopeWithId) aliceReplies := make(chan *proto.ServerToClient) aliceConnToServer := &util.ConnectionToServer{ InBuf: aliceConf.inBuf, Conn: aliceHomeConn, ReadReply: aliceReplies, ReadEnvelope: aliceNotifies, } go aliceConnToServer.ReceiveMessages() bobNotifies := make(chan *util.EnvelopeWithId) bobReplies := make(chan *proto.ServerToClient) bobConnToServer := &util.ConnectionToServer{ InBuf: bobConf.inBuf, Conn: bobHomeConn, ReadReply: bobReplies, ReadEnvelope: bobNotifies, } go bobConnToServer.ReceiveMessages() if err := InitFs(aliceConf); err != nil { t.Fatal(err) } if err := InitFs(bobConf); err != nil { t.Fatal(err) } //Bob uploads keys bobPublicPrekeys, bobSecretPrekeys, err := GeneratePrekeys(maxPrekeys) var bobSigningKey [64]byte copy(bobSigningKey[:], bobConf.KeySigningSecretKey[:64]) err = util.UploadKeys(bobConnToServer, util.SignKeys(bobPublicPrekeys, &bobSigningKey)) if err != nil { t.Fatal(err) } //Bob enables notifications if err = util.EnablePush(bobConnToServer); err != nil { t.Fatal(err) } //Alice uploads keys alicePublicPrekeys, _, err := GeneratePrekeys(maxPrekeys) var aliceSigningKey [64]byte copy(aliceSigningKey[:], aliceConf.KeySigningSecretKey[:64]) err = util.UploadKeys(aliceConnToServer, util.SignKeys(alicePublicPrekeys, &aliceSigningKey)) if err != nil { t.Fatal(err) } //Alice enables notification if err = util.EnablePush(aliceConnToServer); err != nil { t.Fatal(err) } participants := make([]string, 0, 2) participants = append(participants, alice) participants = append(participants, bob) msg1 := []byte("Envelope") payload := proto.Message{ Subject: "Subject1", Participants: participants, Dename: alice, Contents: msg1, } envelope, err := payload.Marshal() if err != nil { t.Fatal(err) } err = aliceConf.sendFirstMessage(envelope, bob) if err != nil { t.Fatal(err) } incoming := <-bobConnToServer.ReadEnvelope out, bobRatch, _, err := bobConf.decryptFirstMessage(incoming.Envelope, bobPublicPrekeys, bobSecretPrekeys) if err != nil { t.Fatal(err) } fmt.Printf("Bob hears: %s\n", out) msg2 := []byte("Envelope2") payload2 := proto.Message{ Subject: "Subject3", Participants: participants, Dename: bob, Contents: msg2, } envelope2, err := payload2.Marshal() if err != nil { t.Fatal(err) } err = bobConf.sendMessage(envelope2, alice, bobRatch) if err != nil { t.Fatal(err) } incomingAlice := <-aliceConnToServer.ReadEnvelope aliceConf.fillAuth = util.FillAuthWith((*[32]byte)(&aliceConf.MessageAuthSecretKey)) aliceConf.checkAuth = util.CheckAuthWith(aliceConf.ProfileRatchet) aliceRatchets, err := AllRatchets(aliceConf, aliceConf.fillAuth, aliceConf.checkAuth) outAlice, _, err := decryptMessage(incomingAlice.Envelope, aliceRatchets) if err != nil { t.Fatal(err) } ha := sha256.Sum256(incomingAlice.Envelope) if err := aliceConf.saveMessage(outAlice); err != nil { t.Fatal(err) } if err := util.DeleteMessages(aliceConnToServer, []*[32]byte{&ha}); err != nil { t.Fatal(err) } fmt.Printf("Alice hears: %s\n", outAlice) //TODO: Confirm message is as expected within the test }
// run executes the main loop of the chatterbox daemon func (d *Daemon) run() error { profile := new(proto.Profile) if err := persistence.UnmarshalFromFile(d.OurChatterboxProfilePath(), profile); err != nil { return err } ourConn, err := d.cc.DialServer(d.Dename, d.ServerAddressTCP, int(d.ServerPortTCP), (*[32]byte)(&d.ServerTransportPK), (*[32]byte)(&profile.UserIDAtServer), (*[32]byte)(&d.TransportSecretKeyForServer)) if err != nil { return err } defer ourConn.Close() notifies := make(chan *util.EnvelopeWithId) replies := make(chan *proto.ServerToClient) connToServer := &util.ConnectionToServer{ InBuf: d.inBuf, Conn: ourConn, ReadReply: replies, ReadEnvelope: notifies, } go connToServer.ReceiveMessages() prekeyPublics, prekeySecrets, err := d.updatePrekeys(connToServer) if err != nil { return err } watcher, err := fsnotify.NewWatcher() if err != nil { return err } defer watcher.Close() initFn := func(path string, f os.FileInfo, err error) error { if f.IsDir() { return d.processOutboxDir(path) } return d.processOutboxDir(filepath.Dir(path)) } err = WatchDir(watcher, d.OutboxDir(), initFn) if err != nil { return err } if err = util.EnablePush(connToServer); err != nil { return err } d.requestAllMessages(connToServer) for { select { case <-d.stop: return nil case ev := <-watcher.Event: // event in the directory structure; watch any new directories if _, err = os.Stat(ev.Name); err == nil { err = WatchDir(watcher, ev.Name, initFn) if err != nil { log.Printf("watch %s: %s", ev.Name, err) // TODO } d.processOutboxDir(ev.Name) } case envelopewithid := <-connToServer.ReadEnvelope: envelope := envelopewithid.Envelope id := envelopewithid.Id msgHash := sha256.Sum256(envelope) // assume it's the first message we're receiving from the person; try to decrypt message, ratch, index, err := d.decryptFirstMessage(envelope, prekeyPublics, prekeySecrets) if err == nil { // assumption was correct, found a prekey that matched if err := StoreRatchet(d, message.Dename, ratch); err != nil { return err } newPrekeyPublics := append(prekeyPublics[:index], prekeyPublics[index+1:]...) newPrekeySecrets := append(prekeySecrets[:index], prekeySecrets[index+1:]...) if err = StorePrekeys(d, newPrekeyPublics, newPrekeySecrets); err != nil { return err } //TODO: Update prekeys by removing index, store // is this done? What does it mean? if err := d.saveMessage(message); err != nil { return err } if err := util.DeleteMessages(connToServer, []*[32]byte{id}); err != nil { return err } } else { // try decrypting with a ratchet ratchets, err := AllRatchets(d, d.fillAuth, d.checkAuth) if err != nil { return err } // TODO: figure out what here should be atomic and comment if message, ratch, err := decryptMessage(envelope, ratchets); err == nil { if err := d.saveMessage(message); err != nil { return err } if err := StoreRatchet(d, message.Dename, ratch); err != nil { return err } } else { log.Printf("failed to decrypt %x: %s", msgHash, err) } if err := util.DeleteMessages(connToServer, []*[32]byte{id}); err != nil { return err } } case err := <-watcher.Error: if err != nil { return err } } } }