blob: 4d6898bab4290d9d33b33082a232bfcbe3c3bc06 [file] [log] [blame]
// Copyright 2025 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/keychain_key_provider.h"
#include <array>
#include <memory>
#include "base/apple/osstatus_logging.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/task/thread_pool.h"
#include "base/types/expected.h"
#include "components/os_crypt/async/common/algorithm.mojom.h"
#include "components/os_crypt/sync/keychain_password_mac.h"
#include "components/os_crypt/sync/os_crypt_switches.h"
#include "crypto/apple/keychain.h"
#include "crypto/apple/mock_keychain.h"
#include "crypto/kdf.h"
#include "crypto/subtle_passkey.h"
namespace os_crypt_async {
namespace {
// These constants are duplicated from the sync backend os_crypt_mac.mm.
constexpr char kKeyTag[] = "v10";
constexpr size_t kDerivedKeySize = 16;
constexpr auto kSalt =
std::to_array<uint8_t>({'s', 'a', 'l', 't', 'y', 's', 'a', 'l', 't'});
constexpr size_t kIterations = 1003;
// This function runs on a worker thread and performs blocking Keychain IO.
base::expected<Encryptor::Key, KeyProvider::KeyError> GetKeyTask(
crypto::SubtlePassKey subtle_passkey,
crypto::apple::Keychain* keychain_for_testing) {
std::unique_ptr<crypto::apple::Keychain> default_keychain;
if (!keychain_for_testing) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
os_crypt::switches::kUseMockKeychain)) {
default_keychain = std::make_unique<crypto::apple::MockKeychain>();
} else {
default_keychain = crypto::apple::Keychain::DefaultKeychain();
}
}
KeychainPassword keychain_password(
keychain_for_testing ? *keychain_for_testing : *default_keychain);
std::string password = keychain_password.GetPassword();
// `password` can be an empty string if keychain access is denied by the user
// or some other error occurs.
if (password.empty()) {
return base::unexpected(KeyProvider::KeyError::kTemporarilyUnavailable);
}
std::array<uint8_t, kDerivedKeySize> key_bytes;
crypto::kdf::DeriveKeyPbkdf2HmacSha1({.iterations = kIterations},
base::as_byte_span(password), kSalt,
key_bytes, subtle_passkey);
return Encryptor::Key(key_bytes, mojom::Algorithm::kAES128CBC);
}
} // namespace
KeychainKeyProvider::KeychainKeyProvider() = default;
KeychainKeyProvider::KeychainKeyProvider(
crypto::apple::Keychain* keychain_for_testing)
: keychain_for_testing_(keychain_for_testing) {}
KeychainKeyProvider::~KeychainKeyProvider() = default;
void KeychainKeyProvider::GetKey(KeyCallback callback) {
// Apple's documentation [1] recommends accessing the keychain on a worker
// thread. [1]
// https://developer.apple.com/documentation/security/secitemcopymatching%28_%3A_%3A%29#Performance-considerations
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
base::BindOnce(&GetKeyTask, crypto::SubtlePassKey{},
keychain_for_testing_),
base::BindOnce(std::move(callback), kKeyTag));
}
bool KeychainKeyProvider::UseForEncryption() {
return true;
}
bool KeychainKeyProvider::IsCompatibleWithOsCryptSync() {
return true;
}
} // namespace os_crypt_async