[go: nahoru, domu]

blob: b5fd3a124198dca12de4504197b1283a75356691 [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/unexportable_keys/unexportable_key_loader.h"
#include "base/check.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "components/unexportable_keys/background_task_priority.h"
#include "components/unexportable_keys/service_error.h"
#include "components/unexportable_keys/unexportable_key_service_impl.h"
#include "components/unexportable_keys/unexportable_key_task_manager.h"
#include "crypto/scoped_mock_unexportable_key_provider.h"
#include "crypto/signature_verifier.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace unexportable_keys {
namespace {
constexpr crypto::SignatureVerifier::SignatureAlgorithm
kAcceptableAlgorithms[] = {crypto::SignatureVerifier::ECDSA_SHA256};
constexpr BackgroundTaskPriority kTaskPriority =
BackgroundTaskPriority::kUserVisible;
} // namespace
class UnexportableKeyLoaderTest : public testing::Test {
public:
UnexportableKeyLoaderTest()
: task_manager_(std::make_unique<UnexportableKeyTaskManager>()),
service_(std::make_unique<UnexportableKeyServiceImpl>(*task_manager_)) {
}
UnexportableKeyServiceImpl& service() { return *service_; }
void RunBackgroundTasks() { task_environment_.RunUntilIdle(); }
void ResetService() {
task_manager_ = std::make_unique<UnexportableKeyTaskManager>();
service_ = std::make_unique<UnexportableKeyServiceImpl>(*task_manager_);
}
void DisableKeyProvider() {
// Using `emplace()` to destroy the existing scoped object before
// constructing a new one.
scoped_key_provider_.emplace<crypto::ScopedNullUnexportableKeyProvider>();
}
std::vector<uint8_t> GenerateNewKeyAndReturnWrappedKey() {
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> generate_future;
service().GenerateSigningKeySlowlyAsync(
kAcceptableAlgorithms, kTaskPriority, generate_future.GetCallback());
RunBackgroundTasks();
ServiceErrorOr<UnexportableKeyId> key_id = generate_future.Get();
CHECK(key_id.has_value());
ServiceErrorOr<std::vector<uint8_t>> wrapped_key =
service().GetWrappedKey(*key_id);
CHECK(wrapped_key.has_value());
return *wrapped_key;
}
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::ThreadPoolExecutionMode::
QUEUED}; // QUEUED - tasks don't run until `RunUntilIdle()` is
// called.
// Provides a mock key provider by default.
absl::variant<crypto::ScopedMockUnexportableKeyProvider,
crypto::ScopedNullUnexportableKeyProvider>
scoped_key_provider_;
std::unique_ptr<UnexportableKeyTaskManager> task_manager_;
std::unique_ptr<UnexportableKeyServiceImpl> service_;
};
TEST_F(UnexportableKeyLoaderTest, CreateFromWrappedKeySync) {
std::vector<uint8_t> wrapped_key = GenerateNewKeyAndReturnWrappedKey();
// `wrapped_key` is already registered in the service. The loader should
// return a key immediately.
auto key_loader = UnexportableKeyLoader::CreateFromWrappedKey(
service(), wrapped_key, kTaskPriority);
EXPECT_EQ(key_loader->GetStateForTesting(),
UnexportableKeyLoader::State::kReady);
EXPECT_TRUE(key_loader->GetKeyIdOrErrorForTesting().has_value());
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> on_load_future;
key_loader->InvokeCallbackAfterKeyLoaded(on_load_future.GetCallback());
EXPECT_TRUE(on_load_future.IsReady());
EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), on_load_future.Get());
}
TEST_F(UnexportableKeyLoaderTest, CreateFromWrappedKeyAsync) {
std::vector<uint8_t> wrapped_key = GenerateNewKeyAndReturnWrappedKey();
// A new key is still registered inside the service. Reset the service to
// remove the key.
ResetService();
auto key_loader = UnexportableKeyLoader::CreateFromWrappedKey(
service(), wrapped_key, kTaskPriority);
EXPECT_EQ(key_loader->GetStateForTesting(),
UnexportableKeyLoader::State::kLoading);
EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(),
base::unexpected(ServiceError::kKeyNotReady));
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> on_load_future;
key_loader->InvokeCallbackAfterKeyLoaded(on_load_future.GetCallback());
EXPECT_FALSE(on_load_future.IsReady());
RunBackgroundTasks();
EXPECT_EQ(key_loader->GetStateForTesting(),
UnexportableKeyLoader::State::kReady);
EXPECT_TRUE(key_loader->GetKeyIdOrErrorForTesting().has_value());
EXPECT_TRUE(on_load_future.IsReady());
EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), on_load_future.Get());
}
TEST_F(UnexportableKeyLoaderTest, CreateFromWrappedKeyMultipleCallbacks) {
std::vector<uint8_t> wrapped_key = GenerateNewKeyAndReturnWrappedKey();
// A new key is still registered inside the service. Reset the service to
// remove the key.
ResetService();
auto key_loader = UnexportableKeyLoader::CreateFromWrappedKey(
service(), wrapped_key, kTaskPriority);
std::array<base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>>, 5>
on_load_futures;
for (auto& future : on_load_futures) {
key_loader->InvokeCallbackAfterKeyLoaded(future.GetCallback());
EXPECT_FALSE(future.IsReady());
}
RunBackgroundTasks();
EXPECT_EQ(key_loader->GetStateForTesting(),
UnexportableKeyLoader::State::kReady);
EXPECT_TRUE(key_loader->GetKeyIdOrErrorForTesting().has_value());
for (auto& future : on_load_futures) {
EXPECT_TRUE(future.IsReady());
EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), future.Get());
}
}
TEST_F(UnexportableKeyLoaderTest, CreateWithNewKey) {
auto key_loader = UnexportableKeyLoader::CreateWithNewKey(
service(), kAcceptableAlgorithms, kTaskPriority);
EXPECT_EQ(key_loader->GetStateForTesting(),
UnexportableKeyLoader::State::kLoading);
EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(),
base::unexpected(ServiceError::kKeyNotReady));
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> on_load_future;
key_loader->InvokeCallbackAfterKeyLoaded(on_load_future.GetCallback());
EXPECT_FALSE(on_load_future.IsReady());
RunBackgroundTasks();
EXPECT_EQ(key_loader->GetStateForTesting(),
UnexportableKeyLoader::State::kReady);
EXPECT_TRUE(key_loader->GetKeyIdOrErrorForTesting().has_value());
EXPECT_TRUE(on_load_future.IsReady());
EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), on_load_future.Get());
}
TEST_F(UnexportableKeyLoaderTest, CreateWithNewKeyFailure) {
DisableKeyProvider();
auto key_loader = UnexportableKeyLoader::CreateWithNewKey(
service(), kAcceptableAlgorithms, kTaskPriority);
EXPECT_EQ(key_loader->GetStateForTesting(),
UnexportableKeyLoader::State::kReady);
EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(),
base::unexpected(ServiceError::kNoKeyProvider));
}
TEST_F(UnexportableKeyLoaderTest, SignDataAfterLoading) {
auto key_loader = UnexportableKeyLoader::CreateWithNewKey(
service(), kAcceptableAlgorithms, kTaskPriority);
base::test::TestFuture<ServiceErrorOr<std::vector<uint8_t>>> sign_future;
key_loader->InvokeCallbackAfterKeyLoaded(base::BindLambdaForTesting(
[&](ServiceErrorOr<UnexportableKeyId> key_id_or_error) {
ASSERT_TRUE(key_id_or_error.has_value());
service().SignSlowlyAsync(*key_id_or_error,
std::vector<uint8_t>({1, 2, 3}),
kTaskPriority, sign_future.GetCallback());
}));
EXPECT_FALSE(sign_future.IsReady());
RunBackgroundTasks();
EXPECT_TRUE(sign_future.IsReady());
EXPECT_TRUE(sign_future.Get().has_value());
}
} // namespace unexportable_keys