Ejemplo n.º 1
0
func LoadRatchet(d *Daemon, name string, fillAuth func(tag, data []byte, theirAuthPublic *[32]byte), checkAuth func(tag, data, msg []byte, ourAuthPrivate *[32]byte) error) (*ratchet.Ratchet, error) {
	ratch := new(ratchet.Ratchet)
	if err := persistence.UnmarshalFromFile(d.ratchetPath(name), ratch); err != nil {
		return nil, err
	}
	ratch.FillAuth = fillAuth
	ratch.CheckAuth = checkAuth
	return ratch, nil
}
Ejemplo n.º 2
0
func (d *Daemon) LatestProfile(name string, received *dename.Profile) (*dename.Profile, error) {
	stored := new(dename.Profile)
	err := persistence.UnmarshalFromFile(d.profilePath(name), stored)
	if err != nil {
		stored = nil
	}
	if received != nil && (stored == nil || *received.Version > *stored.Version) {
		return received, d.MarshalToFile(d.profilePath(name), received)
	}
	return stored, nil
}
Ejemplo n.º 3
0
func PrepareTestAccountDaemon(name string, rootDir string, denameConfig *denameClient.Config, serverAddr string, serverPk *[32]byte, t testing.TB) *Daemon {
	dnmClient, err := denameClient.NewClient(denameConfig, nil, nil)

	//get port from serverAddr
	addr, portStr, err := net.SplitHostPort(serverAddr)
	if err != nil {
		t.Fatal(err)
	}
	var port int
	if _, err := fmt.Sscanf(portStr, "%d", &port); err != nil {
		t.Fatal(err)
	}

	//create the accounts with Init
	torAddr := "DANGEROUS_NO_TOR"
	err = Init(rootDir, name, addr, port, serverPk, torAddr)
	if err != nil {
		t.Fatal(err)
	}

	//initialize daemon with Load
	theDaemon, err := Load(rootDir, denameConfig)
	if err != nil {
		t.Fatal(err)
	}

	cbProfile := new(proto.Profile)
	if err := persistence.UnmarshalFromFile(theDaemon.OurChatterboxProfilePath(), cbProfile); err != nil {
		t.Fatal(err)
	}

	chatProfileBytes, err := protobuf.Marshal(cbProfile)
	if err != nil {
		t.Fatal(err)
	}

	denameProfile, sk, err := denameClient.NewProfile(nil, nil)
	if err != nil {
		t.Fatal(err)
	}

	if err := denameClient.SetProfileField(denameProfile, cbClient.PROFILE_FIELD_ID, chatProfileBytes); err != nil {
		t.Fatal(err)
	}

	err = dnmClient.Register(sk, name, denameProfile, testutil2.MakeToken())
	if err != nil {
		t.Fatal(err)
	}

	return theDaemon
}
Ejemplo n.º 4
0
func (d *Daemon) sendMessage(msg []byte, theirDename string, msgRatch *ratchet.Ratchet) error {
	profile := new(dename.Profile)
	err := persistence.UnmarshalFromFile(d.profilePath(theirDename), profile)
	if err != nil {
		return err
	}

	chatProfileBytes, err := client.GetProfileField(profile, util.PROFILE_FIELD_ID)
	if err != nil {
		return err
	}

	chatProfile := new(proto.Profile)
	if err := chatProfile.Unmarshal(chatProfileBytes); err != nil {
		return err
	}

	addr := chatProfile.ServerAddressTCP
	port := (int)(chatProfile.ServerPortTCP)
	pkTransport := (*[32]byte)(&chatProfile.ServerTransportPK)
	theirPk := (*[32]byte)(&chatProfile.UserIDAtServer)

	if err != nil {
		return err
	}

	theirInBuf := make([]byte, proto.SERVER_MESSAGE_SIZE)

	encMsg, ratch, err := util.EncryptAuth(msg, msgRatch)
	if err != nil {
		return err
	}

	theirConn, err := d.cc.DialServer(theirDename, addr, port, pkTransport, nil, nil)
	if err != nil {
		return err
	}
	if err := StoreRatchet(d, theirDename, ratch); err != nil {
		theirConn.Close()
		d.cc.PutClose(theirDename)
		return err
	}
	err = util.UploadMessageToUser(theirConn, theirInBuf, theirPk, encMsg)
	if err != nil {
		theirConn.Close()
		d.cc.PutClose(theirDename)
		return err
	}
	d.cc.Put(theirDename, theirConn)
	return nil
}
Ejemplo n.º 5
0
func AllRatchets(d *Daemon, fillAuth func(tag, data []byte, theirAuthPublic *[32]byte), checkAuth func(tag, data, msg []byte, ourAuthPrivate *[32]byte) error) ([]*ratchet.Ratchet, error) {
	files, err := ioutil.ReadDir(d.ratchetKeysDir())
	if err != nil {
		return nil, err
	}
	ret := make([]*ratchet.Ratchet, 0, len(files))
	for _, file := range files {
		if file.IsDir() {
			continue
		}
		ratch := new(ratchet.Ratchet)
		err := persistence.UnmarshalFromFile(filepath.Join(d.ratchetKeysDir(), file.Name()), ratch)
		if err != nil {
			return nil, fmt.Errorf("failed to parse ratchet for \"%s\": %s", file.Name(), err)
		}
		ratch.FillAuth = fillAuth
		ratch.CheckAuth = checkAuth
		ret = append(ret, ratch)
	}
	return ret, nil
}
Ejemplo n.º 6
0
func LoadPrekeys(d *Daemon) ([]*[32]byte, []*[32]byte, error) {
	prekeysProto := new(proto.Prekeys)
	err := persistence.UnmarshalFromFile(d.prekeysPath(), prekeysProto)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil, nil
		}
		return nil, nil, err
	}

	if len(prekeysProto.PrekeyPublics) != len(prekeysProto.PrekeySecrets) {
		return nil, nil, fmt.Errorf("len(prekeysProto.prekeyPublics) != len(prekeysProto.prekeySecrets)")
	}
	// convert protobuf proto.Byte32 to *[32]byte
	prekeySecrets := make([]*[32]byte, len(prekeysProto.PrekeySecrets))
	prekeyPublics := make([]*[32]byte, len(prekeysProto.PrekeyPublics))
	for i := 0; i < len(prekeySecrets); i++ {
		prekeySecrets[i] = (*[32]byte)(&prekeysProto.PrekeySecrets[i])
		prekeyPublics[i] = (*[32]byte)(&prekeysProto.PrekeyPublics[i])
	}
	return prekeyPublics, prekeySecrets, nil
}
Ejemplo n.º 7
0
func (d *Daemon) processOutboxDir(dirname string) error {
	// TODO: refactor: separate message assembly and filesystem access?
	// parse metadata
	metadataFile := filepath.Join(dirname, persistence.MetadataFileName)
	if _, err := os.Stat(metadataFile); err != nil {
		return nil // no metadata --> not an outgoing message
	}

	metadata := proto.ConversationMetadata{}
	err := persistence.UnmarshalFromFile(metadataFile, &metadata)
	if err != nil {
		return err
	}

	metadata.Participants = append(metadata.Participants, d.Dename)
	undupStrings(metadata.Participants)
	sort.Strings(metadata.Participants)
	convName := persistence.ConversationName(&metadata)

	// load messages
	potentialMessages, err := ioutil.ReadDir(dirname)
	if err != nil {
		return err
	}
	messages := make([][]byte, 0, len(potentialMessages))
	for _, finfo := range potentialMessages {
		if !finfo.IsDir() && finfo.Name() != persistence.MetadataFileName {
			msg, err := ioutil.ReadFile(filepath.Join(dirname, finfo.Name()))
			if err != nil {
				return err
			}

			// make protobuf for message; append it
			d.ourDenameLookupMu.Lock()
			payload := proto.Message{
				Dename:       d.Dename,
				DenameLookup: d.ourDenameLookup,
				Contents:     msg,
				Subject:      metadata.Subject,
				Participants: metadata.Participants,
				Date:         finfo.ModTime().UnixNano(),
			}
			d.ourDenameLookupMu.Unlock()
			payloadBytes, err := payload.Marshal()
			if err != nil {
				return err
			}
			messages = append(messages, payloadBytes)
		}
	}
	if len(messages) == 0 {
		return nil // no messages to send, just the metadata file
	}

	if err := d.conversationToConversations(&metadata); err != nil && !os.IsExist(err) && !strings.Contains(fmt.Sprint(err), "directory not empty") {
		log.Fatal(err)
	}

	for _, recipient := range metadata.Participants {
		if recipient == d.Dename {
			continue
		}
		for _, msg := range messages {
			if err != nil {
				return err
			}
			if msgRatch, err := LoadRatchet(d, recipient, d.fillAuth, d.checkAuth); err != nil { //First message to this recipien
				if err := d.sendFirstMessage(msg, recipient); err != nil {
					return err
				}
			} else {
				if err := d.sendMessage(msg, recipient, msgRatch); err != nil {
					return err
				}
			}
		}
	}

	// move the sent messages to the conversation folder
	for _, finfo := range potentialMessages {
		if !finfo.IsDir() && finfo.Name() != persistence.MetadataFileName {
			if err = os.Rename(filepath.Join(dirname, finfo.Name()), filepath.Join(d.ConversationDir(), persistence.ConversationName(&metadata), persistence.MessageName(finfo.ModTime(), string(d.Dename)))); err != nil {
				log.Fatal(err)
			}
		}
	}

	// canonicalize the outbox folder name
	if dirname != filepath.Join(d.OutboxDir(), convName) {
		if err := os.Rename(dirname, filepath.Join(d.OutboxDir(), convName)); err != nil {
			shred.RemoveAll(dirname)
		}
	}

	return nil
}
Ejemplo n.º 8
0
// 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
			}
		}
	}

}
Ejemplo n.º 9
0
// Load initializes a chatterbox daemon from rootDir
func Load(rootDir string, denameConfig *client.Config) (*Daemon, error) {
	d := &Daemon{
		Paths: persistence.Paths{
			RootDir:     rootDir,
			Application: "daemon",
		},
		Now:    time.Now,
		inBuf:  make([]byte, proto.SERVER_MESSAGE_SIZE),
		outBuf: make([]byte, proto.SERVER_MESSAGE_SIZE),
	}

	if err := persistence.UnmarshalFromFile(d.configPath(), &d.LocalAccountConfig); err != nil {
		return nil, err
	}
	d.cc = util.NewConnectionCache(util.NewAnonDialer(d.TorAddress))

	if err := persistence.UnmarshalFromFile(d.AccountPath(), &d.LocalAccount); err != nil {
		return nil, err
	}

	if err := persistence.UnmarshalFromFile(d.configPath(), &d.LocalAccountConfig); err != nil {
		return nil, err
	}

	d.ourDenameLookup = new(dename.ClientReply)
	persistence.UnmarshalFromFile(d.ourDenameLookupReplyPath(), d.ourDenameLookup)

	// ensure that we have a correct directory structure
	// including a correctly-populated outbox
	if err := InitFs(d); err != nil {
		return nil, err
	}

	ourDenameClient, err := client.NewClient(denameConfig, util.NewAnonDialer(d.TorAddress), nil)
	if err != nil {
		return nil, err
	}
	d.foreignDenameClient, err = client.NewClient(denameConfig, util.NewAnonDialer(d.TorAddress), nil)
	if err != nil {
		return nil, err
	}
	if denameConfig == nil {
		denameConfig = &client.DefaultConfig
	}
	timelessCfg := *denameConfig // TODO: make very sure this is a deep copy
	timelessCfg.Freshness.Threshold = fmt.Sprintf("%dh", 100*365*24)
	d.timelessDenameClient, err = client.NewClient(&timelessCfg, nil, nil)
	if err != nil {
		return nil, err
	}

	d.psd, err = profilesyncd.New(ourDenameClient, 10*time.Minute, d.Dename, d.onOurDenameProfileDownload, nil)
	if err != nil {
		return nil, err
	}

	d.fillAuth = util.FillAuthWith((*[32]byte)(&d.MessageAuthSecretKey))
	d.checkAuth = util.CheckAuthWith(d.ProfileRatchet)

	return d, nil
}