blob: ca8ece854ade83d17693d16f62858d2bcc06c16b [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sync/driver/file_based_trusted_vault_client.h"
#include <utility>
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task_runner_util.h"
#include "components/os_crypt/os_crypt.h"
#include "components/sync/protocol/local_trusted_vault.pb.h"
namespace syncer {
namespace {
constexpr base::TaskTraits kTaskTraits = {
base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
sync_pb::LocalTrustedVault ReadEncryptedFile(const base::FilePath& file_path) {
sync_pb::LocalTrustedVault proto;
std::string ciphertext;
std::string decrypted_content;
if (base::ReadFileToString(file_path, &ciphertext) &&
OSCrypt::DecryptString(ciphertext, &decrypted_content)) {
proto.ParseFromString(decrypted_content);
}
return proto;
}
void WriteToDisk(const sync_pb::LocalTrustedVault& data,
const base::FilePath& file_path) {
std::string encrypted_data;
if (!OSCrypt::EncryptString(data.SerializeAsString(), &encrypted_data)) {
DLOG(ERROR) << "Failed to encrypt trusted vault file.";
return;
}
if (!base::ImportantFileWriter::WriteFileAtomically(file_path,
encrypted_data)) {
DLOG(ERROR) << "Failed to write trusted vault file.";
}
}
} // namespace
//
class FileBasedTrustedVaultClient::Backend
: public base::RefCountedThreadSafe<Backend> {
public:
explicit Backend(const base::FilePath& file_path) : file_path_(file_path) {}
void ReadDataFromDisk() { data_ = ReadEncryptedFile(file_path_); }
std::vector<std::vector<uint8_t>> FetchKeys(const std::string& gaia_id) {
const sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(gaia_id);
std::vector<std::vector<uint8_t>> keys;
if (per_user_vault) {
for (const sync_pb::LocalTrustedVaultKey& key : per_user_vault->key()) {
const std::string& key_material = key.key_material();
keys.emplace_back(key_material.begin(), key_material.end());
}
}
return keys;
}
void StoreKeys(const std::string& gaia_id,
const std::vector<std::vector<uint8_t>>& keys) {
// Find or create user for |gaid_id|.
sync_pb::LocalTrustedVaultPerUser* per_user_vault = FindUserVault(gaia_id);
if (!per_user_vault) {
per_user_vault = data_.add_user();
per_user_vault->set_gaia_id(gaia_id);
}
// Replace all keys.
per_user_vault->clear_key();
for (const std::vector<uint8_t>& key : keys) {
per_user_vault->add_key()->set_key_material(key.data(), key.size());
}
WriteToDisk(data_, file_path_);
}
private:
friend class base::RefCountedThreadSafe<Backend>;
~Backend() = default;
// Finds the per-user vault in |data_| for |gaia_id|. Returns null if not
// found.
sync_pb::LocalTrustedVaultPerUser* FindUserVault(const std::string& gaia_id) {
for (int i = 0; i < data_.user_size(); ++i) {
if (data_.user(i).gaia_id() == gaia_id) {
return data_.mutable_user(i);
}
}
return nullptr;
}
const base::FilePath file_path_;
sync_pb::LocalTrustedVault data_;
DISALLOW_COPY_AND_ASSIGN(Backend);
};
FileBasedTrustedVaultClient::FileBasedTrustedVaultClient(
const base::FilePath& file_path)
: file_path_(file_path),
backend_task_runner_(base::CreateSequencedTaskRunner(kTaskTraits)) {}
FileBasedTrustedVaultClient::~FileBasedTrustedVaultClient() = default;
std::unique_ptr<FileBasedTrustedVaultClient::Subscription>
FileBasedTrustedVaultClient::AddKeysChangedObserver(
const base::RepeatingClosure& cb) {
return observer_list_.Add(cb);
}
void FileBasedTrustedVaultClient::FetchKeys(
const std::string& gaia_id,
base::OnceCallback<void(const std::vector<std::vector<uint8_t>>&)> cb) {
TriggerLazyInitializationIfNeeded();
base::PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&Backend::FetchKeys, backend_, gaia_id), std::move(cb));
}
void FileBasedTrustedVaultClient::StoreKeys(
const std::string& gaia_id,
const std::vector<std::vector<uint8_t>>& keys) {
TriggerLazyInitializationIfNeeded();
backend_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Backend::StoreKeys, backend_, gaia_id, keys));
observer_list_.Notify();
}
void FileBasedTrustedVaultClient::WaitForFlushForTesting(
base::OnceClosure cb) const {
backend_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
std::move(cb));
}
void FileBasedTrustedVaultClient::TriggerLazyInitializationIfNeeded() {
if (backend_) {
return;
}
backend_ = base::MakeRefCounted<Backend>(file_path_);
backend_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Backend::ReadDataFromDisk, backend_));
}
bool FileBasedTrustedVaultClient::IsInitializationTriggeredForTesting() const {
return backend_ != nullptr;
}
} // namespace syncer