blob: 716d5d9b41edd2db592e2bcc0a4625fdbbc003a5 [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/metrics/histogram_functions.h"
#include "base/sequence_checker.h"
#include "base/types/expected.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()) {
SetEncryptorInstance(Encryptor());
}
}
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));
}
void OSCryptAsync::HandleKey(
ProviderIterator current,
const std::string& tag,
base::expected<Encryptor::Key, KeyProvider::KeyError> 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.has_value()) {
key_ring_.emplace(tag, std::move(*key));
if ((*current)->UseForEncryption()) {
provider_for_encryption_ = tag;
if ((*current)->IsCompatibleWithOsCryptSync()) {
provider_for_os_crypt_sync_compatible_encryption_ = tag;
}
}
} else {
switch (key.error()) {
case KeyProvider::KeyError::kPermanentlyUnavailable:
++number_of_failing_key_providers_;
DVLOG(1) << "Provider " << tag << " failed to return a key.";
break;
case KeyProvider::KeyError::kTemporarilyUnavailable:
key_ring_.emplace(tag, std::nullopt);
break;
}
}
if (++current == providers_.end()) {
SetEncryptorInstance(
Encryptor(std::move(key_ring_), provider_for_encryption_,
provider_for_os_crypt_sync_compatible_encryption_));
for (auto& callback : callbacks_) {
std::move(callback).Run();
}
is_initializing_ = false;
return;
}
(*current)->GetKey(base::BindOnce(&OSCryptAsync::HandleKey,
weak_factory_.GetWeakPtr(), current));
}
void OSCryptAsync::SetEncryptorInstance(Encryptor encryptor) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!is_initialized_);
is_initialized_ = true;
encryptor_instance_ = std::make_unique<Encryptor>(std::move(encryptor));
size_t available_keys = 0;
size_t unavailable_keys = 0;
for (const auto& key : encryptor_instance_->keys_) {
if (key.second) {
++available_keys;
} else {
++unavailable_keys;
}
}
base::UmaHistogramCounts100(
"OSCrypt.EncryptorKeyCount",
number_of_failing_key_providers_ + available_keys + unavailable_keys);
base::UmaHistogramCounts100("OSCrypt.EncryptorKeyCount.Available",
available_keys);
base::UmaHistogramCounts100(
"OSCrypt.EncryptorKeyCount.TemporarilyUnavailable", unavailable_keys);
base::UmaHistogramCounts100(
"OSCrypt.EncryptorKeyCount.PermanentlyUnavailable",
number_of_failing_key_providers_);
}
void OSCryptAsync::GetInstance(InitCallback callback) {
GetInstance(std::move(callback), Encryptor::Option::kNone);
}
void 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));
return;
}
callbacks_.emplace_back(base::BindOnce(&OSCryptAsync::CallbackHelper,
weak_factory_.GetWeakPtr(),
std::move(callback), option));
if (is_initializing_) {
return;
}
CHECK(key_ring_.empty());
is_initializing_ = true;
const ProviderIterator start = providers_.cbegin();
(*start)->GetKey(base::BindOnce(&OSCryptAsync::HandleKey,
weak_factory_.GetWeakPtr(), start));
}
} // namespace os_crypt_async