blob: 8221b4d24b9f72194f11ca1802b74518f501b964 [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_service.h"
#include "base/containers/span.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/test/gmock_move_support.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/ref_counted_unexportable_signing_key.h"
#include "components/unexportable_keys/service_error.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/gmock/include/gmock/gmock.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 UnexportableKeyServiceTest : public testing::Test {
public:
UnexportableKeyServiceTest()
: task_manager_(std::make_unique<UnexportableKeyTaskManager>()),
service_(std::make_unique<UnexportableKeyService>(*task_manager_)) {}
UnexportableKeyService& service() { return *service_; }
void RunBackgroundTasks() { task_environment_.RunUntilIdle(); }
void ResetService() {
task_manager_ = std::make_unique<UnexportableKeyTaskManager>();
service_ = std::make_unique<UnexportableKeyService>(*task_manager_);
}
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::ThreadPoolExecutionMode::
QUEUED}; // QUEUED - tasks don't run until `RunUntilIdle()` is
// called.
crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider_;
std::unique_ptr<UnexportableKeyTaskManager> task_manager_;
std::unique_ptr<UnexportableKeyService> service_;
};
TEST_F(UnexportableKeyServiceTest, GenerateKey) {
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> future;
service().GenerateSigningKeySlowlyAsync(kAcceptableAlgorithms, kTaskPriority,
future.GetCallback());
EXPECT_FALSE(future.IsReady());
RunBackgroundTasks();
EXPECT_TRUE(future.IsReady());
ServiceErrorOr<UnexportableKeyId> key_id = future.Get();
ASSERT_TRUE(key_id.has_value());
// Verify that we can get info about the generated key.
EXPECT_TRUE(service().GetSubjectPublicKeyInfo(*key_id).has_value());
EXPECT_TRUE(service().GetWrappedKey(*key_id).has_value());
EXPECT_THAT(kAcceptableAlgorithms,
testing::Contains(service().GetAlgorithm(*key_id)));
}
TEST_F(UnexportableKeyServiceTest, GenerateKeyMultiplePendingRequests) {
constexpr size_t kPendingRequests = 5;
std::array<base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>>,
kPendingRequests>
futures;
for (auto& future : futures) {
service().GenerateSigningKeySlowlyAsync(
kAcceptableAlgorithms, kTaskPriority, future.GetCallback());
EXPECT_FALSE(future.IsReady());
}
RunBackgroundTasks();
std::set<UnexportableKeyId> key_ids;
for (auto& future : futures) {
EXPECT_TRUE(future.IsReady());
ServiceErrorOr<UnexportableKeyId> key_id = future.Get();
ASSERT_TRUE(key_id.has_value());
// Verify that we can get info about the generated key.
EXPECT_TRUE(service().GetSubjectPublicKeyInfo(*key_id).has_value());
EXPECT_TRUE(service().GetWrappedKey(*key_id).has_value());
key_ids.insert(*key_id);
}
// All key IDs should be unique.
EXPECT_EQ(key_ids.size(), kPendingRequests);
}
TEST_F(UnexportableKeyServiceTest, GenerateKeyFails) {
// RSA is not supported by the mock key provider, so the key generation should
// fail.
auto unsupported_algorithm = {crypto::SignatureVerifier::RSA_PKCS1_SHA256};
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> future;
service().GenerateSigningKeySlowlyAsync(unsupported_algorithm, kTaskPriority,
future.GetCallback());
RunBackgroundTasks();
EXPECT_EQ(future.Get(),
base::unexpected(ServiceError::kAlgorithmNotSupported));
}
TEST_F(UnexportableKeyServiceTest, FromWrappedKey) {
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> generate_future;
service().GenerateSigningKeySlowlyAsync(kAcceptableAlgorithms, kTaskPriority,
generate_future.GetCallback());
RunBackgroundTasks();
ServiceErrorOr<UnexportableKeyId> key_id = generate_future.Get();
ASSERT_TRUE(key_id.has_value());
ServiceErrorOr<std::vector<uint8_t>> wrapped_key =
service().GetWrappedKey(*key_id);
ASSERT_TRUE(wrapped_key.has_value());
ResetService();
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> from_wrapped_future;
service().FromWrappedSigningKeySlowlyAsync(*wrapped_key, kTaskPriority,
from_wrapped_future.GetCallback());
EXPECT_FALSE(from_wrapped_future.IsReady());
RunBackgroundTasks();
EXPECT_TRUE(from_wrapped_future.IsReady());
EXPECT_TRUE(from_wrapped_future.Get().has_value());
}
TEST_F(UnexportableKeyServiceTest, FromWrappedKeyMultiplePendingRequests) {
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> generate_future;
service().GenerateSigningKeySlowlyAsync(kAcceptableAlgorithms, kTaskPriority,
generate_future.GetCallback());
RunBackgroundTasks();
ServiceErrorOr<UnexportableKeyId> key_id = generate_future.Get();
ASSERT_TRUE(key_id.has_value());
ServiceErrorOr<std::vector<uint8_t>> wrapped_key =
service().GetWrappedKey(*key_id);
ASSERT_TRUE(wrapped_key.has_value());
ResetService();
constexpr size_t kPendingRequests = 5;
std::array<base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>>,
kPendingRequests>
from_wrapped_key_futures;
for (auto& future : from_wrapped_key_futures) {
service().FromWrappedSigningKeySlowlyAsync(*wrapped_key, kTaskPriority,
future.GetCallback());
EXPECT_FALSE(future.IsReady());
}
RunBackgroundTasks();
// All callbacks should return the same key ID.
ServiceErrorOr<UnexportableKeyId> unwrapped_key_id =
from_wrapped_key_futures[0].Get();
EXPECT_TRUE(unwrapped_key_id.has_value());
for (auto& future : from_wrapped_key_futures) {
EXPECT_TRUE(future.IsReady());
EXPECT_EQ(future.Get(), unwrapped_key_id);
}
}
TEST_F(UnexportableKeyServiceTest, FromWrappedKeyMultiplePendingRequestsFail) {
std::vector<uint8_t> empty_wrapped_key;
constexpr size_t kPendingRequests = 5;
std::array<base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>>,
kPendingRequests>
from_wrapped_key_futures;
for (auto& future : from_wrapped_key_futures) {
service().FromWrappedSigningKeySlowlyAsync(empty_wrapped_key, kTaskPriority,
future.GetCallback());
EXPECT_FALSE(future.IsReady());
}
RunBackgroundTasks();
// All callbacks should return failure.
for (auto& future : from_wrapped_key_futures) {
EXPECT_TRUE(future.IsReady());
EXPECT_EQ(future.Get(), base::unexpected(ServiceError::kCryptoApiFailed));
}
}
TEST_F(UnexportableKeyServiceTest, FromWrappedKeyReturnsTheSameIdWhenExists) {
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> generate_future;
service().GenerateSigningKeySlowlyAsync(kAcceptableAlgorithms, kTaskPriority,
generate_future.GetCallback());
RunBackgroundTasks();
ServiceErrorOr<UnexportableKeyId> key_id = generate_future.Get();
ASSERT_TRUE(key_id.has_value());
ServiceErrorOr<std::vector<uint8_t>> wrapped_key =
service().GetWrappedKey(*key_id);
ASSERT_TRUE(wrapped_key.has_value());
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> from_wrapped_future;
service().FromWrappedSigningKeySlowlyAsync(*wrapped_key, kTaskPriority,
from_wrapped_future.GetCallback());
// `service()` should return the result immediately.
EXPECT_TRUE(from_wrapped_future.IsReady());
ServiceErrorOr<UnexportableKeyId> unwrapped_key_id =
from_wrapped_future.Get();
// Key IDs should be the same.
EXPECT_EQ(key_id, unwrapped_key_id);
}
TEST_F(UnexportableKeyServiceTest, Sign) {
base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> generate_future;
service().GenerateSigningKeySlowlyAsync(kAcceptableAlgorithms, kTaskPriority,
generate_future.GetCallback());
RunBackgroundTasks();
ServiceErrorOr<UnexportableKeyId> key_id = generate_future.Get();
ASSERT_TRUE(key_id.has_value());
base::test::TestFuture<ServiceErrorOr<std::vector<uint8_t>>> sign_future;
std::vector<uint8_t> data = {1, 2, 3};
service().SignSlowlyAsync(*key_id, data, kTaskPriority,
sign_future.GetCallback());
EXPECT_FALSE(sign_future.IsReady());
RunBackgroundTasks();
EXPECT_TRUE(sign_future.IsReady());
EXPECT_TRUE(sign_future.Get().has_value());
}
TEST_F(UnexportableKeyServiceTest, NonExistingKeyId) {
UnexportableKeyId fake_key_id;
// `service()` does not return any info about non-existing key ID.
EXPECT_EQ(service().GetSubjectPublicKeyInfo(fake_key_id),
base::unexpected(ServiceError::kKeyNotFound));
EXPECT_EQ(service().GetWrappedKey(fake_key_id),
base::unexpected(ServiceError::kKeyNotFound));
// `SignSlowlyAsync()` should fail.
base::test::TestFuture<ServiceErrorOr<std::vector<uint8_t>>> sign_future;
std::vector<uint8_t> data = {1, 2, 3};
service().SignSlowlyAsync(fake_key_id, data, kTaskPriority,
sign_future.GetCallback());
EXPECT_TRUE(sign_future.IsReady());
EXPECT_EQ(sign_future.Get(), base::unexpected(ServiceError::kKeyNotFound));
}
} // namespace unexportable_keys