Alex Ilin | 933326f0 | 2023-05-15 10:26:43 | [diff] [blame] | 1 | // Copyright 2023 The Chromium Authors |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "components/unexportable_keys/unexportable_key_loader.h" |
| 6 | |
| 7 | #include "base/check.h" |
| 8 | #include "base/test/bind.h" |
| 9 | #include "base/test/task_environment.h" |
| 10 | #include "base/test/test_future.h" |
| 11 | #include "components/unexportable_keys/background_task_priority.h" |
| 12 | #include "components/unexportable_keys/service_error.h" |
| 13 | #include "components/unexportable_keys/unexportable_key_service_impl.h" |
| 14 | #include "components/unexportable_keys/unexportable_key_task_manager.h" |
| 15 | #include "crypto/scoped_mock_unexportable_key_provider.h" |
| 16 | #include "crypto/signature_verifier.h" |
| 17 | #include "testing/gtest/include/gtest/gtest.h" |
| 18 | |
| 19 | namespace unexportable_keys { |
| 20 | |
| 21 | namespace { |
| 22 | |
| 23 | constexpr crypto::SignatureVerifier::SignatureAlgorithm |
| 24 | kAcceptableAlgorithms[] = {crypto::SignatureVerifier::ECDSA_SHA256}; |
| 25 | constexpr BackgroundTaskPriority kTaskPriority = |
| 26 | BackgroundTaskPriority::kUserVisible; |
| 27 | |
| 28 | } // namespace |
| 29 | |
| 30 | class UnexportableKeyLoaderTest : public testing::Test { |
| 31 | public: |
| 32 | UnexportableKeyLoaderTest() |
| 33 | : task_manager_(std::make_unique<UnexportableKeyTaskManager>()), |
| 34 | service_(std::make_unique<UnexportableKeyServiceImpl>(*task_manager_)) { |
| 35 | } |
| 36 | |
| 37 | UnexportableKeyServiceImpl& service() { return *service_; } |
| 38 | |
| 39 | void RunBackgroundTasks() { task_environment_.RunUntilIdle(); } |
| 40 | |
| 41 | void ResetService() { |
| 42 | task_manager_ = std::make_unique<UnexportableKeyTaskManager>(); |
| 43 | service_ = std::make_unique<UnexportableKeyServiceImpl>(*task_manager_); |
| 44 | } |
| 45 | |
| 46 | void DisableKeyProvider() { |
| 47 | // Using `emplace()` to destroy the existing scoped object before |
| 48 | // constructing a new one. |
| 49 | scoped_key_provider_.emplace<crypto::ScopedNullUnexportableKeyProvider>(); |
| 50 | } |
| 51 | |
| 52 | std::vector<uint8_t> GenerateNewKeyAndReturnWrappedKey() { |
| 53 | base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> generate_future; |
| 54 | service().GenerateSigningKeySlowlyAsync( |
| 55 | kAcceptableAlgorithms, kTaskPriority, generate_future.GetCallback()); |
| 56 | RunBackgroundTasks(); |
| 57 | ServiceErrorOr<UnexportableKeyId> key_id = generate_future.Get(); |
| 58 | CHECK(key_id.has_value()); |
| 59 | |
| 60 | ServiceErrorOr<std::vector<uint8_t>> wrapped_key = |
| 61 | service().GetWrappedKey(*key_id); |
| 62 | CHECK(wrapped_key.has_value()); |
| 63 | return *wrapped_key; |
| 64 | } |
| 65 | |
| 66 | private: |
| 67 | base::test::TaskEnvironment task_environment_{ |
| 68 | base::test::TaskEnvironment::ThreadPoolExecutionMode:: |
| 69 | QUEUED}; // QUEUED - tasks don't run until `RunUntilIdle()` is |
| 70 | // called. |
| 71 | // Provides a mock key provider by default. |
| 72 | absl::variant<crypto::ScopedMockUnexportableKeyProvider, |
| 73 | crypto::ScopedNullUnexportableKeyProvider> |
| 74 | scoped_key_provider_; |
| 75 | std::unique_ptr<UnexportableKeyTaskManager> task_manager_; |
| 76 | std::unique_ptr<UnexportableKeyServiceImpl> service_; |
| 77 | }; |
| 78 | |
| 79 | TEST_F(UnexportableKeyLoaderTest, CreateFromWrappedKeySync) { |
| 80 | std::vector<uint8_t> wrapped_key = GenerateNewKeyAndReturnWrappedKey(); |
| 81 | |
| 82 | // `wrapped_key` is already registered in the service. The loader should |
| 83 | // return a key immediately. |
| 84 | auto key_loader = UnexportableKeyLoader::CreateFromWrappedKey( |
| 85 | service(), wrapped_key, kTaskPriority); |
| 86 | EXPECT_EQ(key_loader->GetStateForTesting(), |
| 87 | UnexportableKeyLoader::State::kReady); |
| 88 | EXPECT_TRUE(key_loader->GetKeyIdOrErrorForTesting().has_value()); |
| 89 | |
| 90 | base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> on_load_future; |
| 91 | key_loader->InvokeCallbackAfterKeyLoaded(on_load_future.GetCallback()); |
| 92 | EXPECT_TRUE(on_load_future.IsReady()); |
| 93 | EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), on_load_future.Get()); |
| 94 | } |
| 95 | |
| 96 | TEST_F(UnexportableKeyLoaderTest, CreateFromWrappedKeyAsync) { |
| 97 | std::vector<uint8_t> wrapped_key = GenerateNewKeyAndReturnWrappedKey(); |
| 98 | // A new key is still registered inside the service. Reset the service to |
| 99 | // remove the key. |
| 100 | ResetService(); |
| 101 | |
| 102 | auto key_loader = UnexportableKeyLoader::CreateFromWrappedKey( |
| 103 | service(), wrapped_key, kTaskPriority); |
| 104 | EXPECT_EQ(key_loader->GetStateForTesting(), |
| 105 | UnexportableKeyLoader::State::kLoading); |
| 106 | EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), |
| 107 | base::unexpected(ServiceError::kKeyNotReady)); |
| 108 | |
| 109 | base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> on_load_future; |
| 110 | key_loader->InvokeCallbackAfterKeyLoaded(on_load_future.GetCallback()); |
| 111 | EXPECT_FALSE(on_load_future.IsReady()); |
| 112 | |
| 113 | RunBackgroundTasks(); |
| 114 | EXPECT_EQ(key_loader->GetStateForTesting(), |
| 115 | UnexportableKeyLoader::State::kReady); |
| 116 | EXPECT_TRUE(key_loader->GetKeyIdOrErrorForTesting().has_value()); |
| 117 | EXPECT_TRUE(on_load_future.IsReady()); |
| 118 | EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), on_load_future.Get()); |
| 119 | } |
| 120 | |
| 121 | TEST_F(UnexportableKeyLoaderTest, CreateFromWrappedKeyMultipleCallbacks) { |
| 122 | std::vector<uint8_t> wrapped_key = GenerateNewKeyAndReturnWrappedKey(); |
| 123 | // A new key is still registered inside the service. Reset the service to |
| 124 | // remove the key. |
| 125 | ResetService(); |
| 126 | |
| 127 | auto key_loader = UnexportableKeyLoader::CreateFromWrappedKey( |
| 128 | service(), wrapped_key, kTaskPriority); |
| 129 | |
| 130 | std::array<base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>>, 5> |
| 131 | on_load_futures; |
| 132 | for (auto& future : on_load_futures) { |
| 133 | key_loader->InvokeCallbackAfterKeyLoaded(future.GetCallback()); |
| 134 | EXPECT_FALSE(future.IsReady()); |
| 135 | } |
| 136 | |
| 137 | RunBackgroundTasks(); |
| 138 | EXPECT_EQ(key_loader->GetStateForTesting(), |
| 139 | UnexportableKeyLoader::State::kReady); |
| 140 | EXPECT_TRUE(key_loader->GetKeyIdOrErrorForTesting().has_value()); |
| 141 | for (auto& future : on_load_futures) { |
| 142 | EXPECT_TRUE(future.IsReady()); |
| 143 | EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), future.Get()); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | TEST_F(UnexportableKeyLoaderTest, CreateWithNewKey) { |
| 148 | auto key_loader = UnexportableKeyLoader::CreateWithNewKey( |
| 149 | service(), kAcceptableAlgorithms, kTaskPriority); |
| 150 | EXPECT_EQ(key_loader->GetStateForTesting(), |
| 151 | UnexportableKeyLoader::State::kLoading); |
| 152 | EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), |
| 153 | base::unexpected(ServiceError::kKeyNotReady)); |
| 154 | |
| 155 | base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> on_load_future; |
| 156 | key_loader->InvokeCallbackAfterKeyLoaded(on_load_future.GetCallback()); |
| 157 | EXPECT_FALSE(on_load_future.IsReady()); |
| 158 | |
| 159 | RunBackgroundTasks(); |
| 160 | EXPECT_EQ(key_loader->GetStateForTesting(), |
| 161 | UnexportableKeyLoader::State::kReady); |
| 162 | EXPECT_TRUE(key_loader->GetKeyIdOrErrorForTesting().has_value()); |
| 163 | EXPECT_TRUE(on_load_future.IsReady()); |
| 164 | EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), on_load_future.Get()); |
| 165 | } |
| 166 | |
| 167 | TEST_F(UnexportableKeyLoaderTest, CreateWithNewKeyFailure) { |
| 168 | DisableKeyProvider(); |
| 169 | auto key_loader = UnexportableKeyLoader::CreateWithNewKey( |
| 170 | service(), kAcceptableAlgorithms, kTaskPriority); |
| 171 | EXPECT_EQ(key_loader->GetStateForTesting(), |
| 172 | UnexportableKeyLoader::State::kReady); |
| 173 | EXPECT_EQ(key_loader->GetKeyIdOrErrorForTesting(), |
| 174 | base::unexpected(ServiceError::kNoKeyProvider)); |
| 175 | } |
| 176 | |
| 177 | TEST_F(UnexportableKeyLoaderTest, SignDataAfterLoading) { |
| 178 | auto key_loader = UnexportableKeyLoader::CreateWithNewKey( |
| 179 | service(), kAcceptableAlgorithms, kTaskPriority); |
| 180 | |
| 181 | base::test::TestFuture<ServiceErrorOr<std::vector<uint8_t>>> sign_future; |
| 182 | key_loader->InvokeCallbackAfterKeyLoaded(base::BindLambdaForTesting( |
| 183 | [&](ServiceErrorOr<UnexportableKeyId> key_id_or_error) { |
| 184 | ASSERT_TRUE(key_id_or_error.has_value()); |
| 185 | service().SignSlowlyAsync(*key_id_or_error, |
| 186 | std::vector<uint8_t>({1, 2, 3}), |
| 187 | kTaskPriority, sign_future.GetCallback()); |
| 188 | })); |
| 189 | EXPECT_FALSE(sign_future.IsReady()); |
| 190 | RunBackgroundTasks(); |
| 191 | EXPECT_TRUE(sign_future.IsReady()); |
| 192 | EXPECT_TRUE(sign_future.Get().has_value()); |
| 193 | } |
| 194 | |
| 195 | } // namespace unexportable_keys |