blob: 0ec868ff79f5b95ab1ee5d07f375abcd88996ff6 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/os_crypt/async/browser/os_crypt_async.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "base/callback_list.h"
#include "base/check_op.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/sequence_checker.h"
#include "components/os_crypt/async/browser/key_provider.h"
#include "components/os_crypt/async/common/encryptor.h"
namespace os_crypt_async {
namespace {
// Sorts `input_providers` by the `Precedence`, from lowest (beginning of
// vector) to highest (at the end), then returns a new vector that contains just
// the KeyProvider unique_ptrs in that new order.
std::vector<std::unique_ptr<KeyProvider>> SortProviders(
std::vector<std::pair<OSCryptAsync::Precedence,
std::unique_ptr<KeyProvider>>> input_providers) {
std::vector<std::unique_ptr<KeyProvider>> providers;
if (input_providers.empty()) {
return providers;
}
std::ranges::sort(input_providers, [](const auto& a, const auto& b) {
return a.first < b.first;
});
for (auto it = input_providers.cbegin(); it != input_providers.cend() - 1;
++it) {
CHECK_NE(it->first, (it + 1)->first)
<< "Cannot have two providers with same precedence.";
}
std::ranges::transform(std::make_move_iterator(input_providers.begin()),
std::make_move_iterator(input_providers.end()),
std::back_inserter(providers), [](auto provider) {
return std::move(provider.second);
});
return providers;
}
} // namespace
OSCryptAsync::OSCryptAsync(
std::vector<std::pair<Precedence, std::unique_ptr<KeyProvider>>> providers)
: providers_(SortProviders(std::move(providers))) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (providers_.empty()) {
encryptor_instance_ = base::WrapUnique<Encryptor>(new Encryptor());
is_initialized_ = true;
}
}
OSCryptAsync::~OSCryptAsync() = default;
// CallbackHelper is needed so the sequence checker member can be accessed in
// the callback, which it can't from a lambda without breaking the
// sequence_checker abstraction.
void OSCryptAsync::CallbackHelper(InitCallback callback,
Encryptor::Option option) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback).Run(encryptor_instance_->Clone(option), /*result=*/true);
}
void OSCryptAsync::HandleKey(ProviderIterator current,
const std::string& tag,
std::optional<Encryptor::Key> key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!tag.empty()) << "Tag cannot be empty.";
for (const auto& [key_name, key_value] : key_ring_) {
// Check for overlapping names. Two providers called TEST and TEST2 are
// likely incorrectly named, since TEST might try to decrypt TEST2's data.
if (tag.size() > key_name.size()) {
CHECK(!std::equal(key_name.begin(), key_name.end(), tag.begin()))
<< "Tags must not overlap.";
} else {
CHECK(!std::equal(tag.begin(), tag.end(), key_name.begin()))
<< "Tags must not overlap.";
}
}
if (key) {
key->is_os_crypt_sync_compatible_ =
((*current)->IsCompatibleWithOsCryptSync());
key_ring_.emplace(tag, std::move(*key));
if ((*current)->UseForEncryption()) {
provider_for_encryption_ = tag;
}
} else {
// TODO(crbug.com/40241934): Return errors back via a callback.
DVLOG(1) << "Provider " << tag << " failed to return a key.";
}
if (++current == providers_.end()) {
encryptor_instance_ = base::WrapUnique<Encryptor>(
new Encryptor(std::move(key_ring_), provider_for_encryption_));
callbacks_.Notify();
is_initialized_ = true;
is_initializing_ = false;
return;
}
(*current)->GetKey(base::BindOnce(&OSCryptAsync::HandleKey,
weak_factory_.GetWeakPtr(), current));
}
base::CallbackListSubscription OSCryptAsync::GetInstance(
InitCallback callback) {
return GetInstance(std::move(callback), Encryptor::Option::kNone);
}
base::CallbackListSubscription OSCryptAsync::GetInstance(
InitCallback callback,
Encryptor::Option option) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (is_initialized_) {
CHECK(!is_initializing_);
std::move(callback).Run(encryptor_instance_->Clone(option),
/*result=*/true);
return base::CallbackListSubscription();
}
auto subscription = callbacks_.Add(
base::BindOnce(&OSCryptAsync::CallbackHelper, weak_factory_.GetWeakPtr(),
std::move(callback), option));
if (is_initializing_) {
return subscription;
}
CHECK(key_ring_.empty());
is_initializing_ = true;
const ProviderIterator start = providers_.cbegin();
(*start)->GetKey(base::BindOnce(&OSCryptAsync::HandleKey,
weak_factory_.GetWeakPtr(), start));
return subscription;
}
} // namespace os_crypt_async