1 // Copyright 2024 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 <atomic>
6 #include <functional>
7 #include <utility>
8
9 #include <windows.foundation.h>
10 #include <windows.security.credentials.h>
11 #include <windows.security.cryptography.core.h>
12 #include <windows.storage.streams.h>
13
14 #include "base/base64.h"
15 #include "base/functional/callback_helpers.h"
16 #include "base/logging.h"
17 #include "base/memory/scoped_refptr.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/strings/strcat.h"
20 #include "base/task/bind_post_task.h"
21 #include "base/task/single_thread_task_runner.h"
22 #include "base/task/thread_pool.h"
23 #include "base/threading/scoped_thread_priority.h"
24 #include "base/win/core_winrt_util.h"
25 #include "base/win/post_async_results.h"
26 #include "base/win/scoped_hstring.h"
27 #include "base/win/winrt_storage_util.h"
28 #include "crypto/random.h"
29 #include "crypto/user_verifying_key.h"
30
31 using ABI::Windows::Foundation::IAsyncAction;
32 using ABI::Windows::Foundation::IAsyncOperation;
33 using ABI::Windows::Security::Credentials::IKeyCredential;
34 using ABI::Windows::Security::Credentials::IKeyCredentialManagerStatics;
35 using ABI::Windows::Security::Credentials::IKeyCredentialOperationResult;
36 using ABI::Windows::Security::Credentials::IKeyCredentialRetrievalResult;
37 using ABI::Windows::Security::Credentials::KeyCredentialCreationOption;
38 using ABI::Windows::Security::Credentials::KeyCredentialOperationResult;
39 using ABI::Windows::Security::Credentials::KeyCredentialRetrievalResult;
40 using ABI::Windows::Security::Credentials::KeyCredentialStatus;
41 using ABI::Windows::Security::Credentials::KeyCredentialStatus_Success;
42 using ABI::Windows::Security::Cryptography::Core::
43 CryptographicPublicKeyBlobType_X509SubjectPublicKeyInfo;
44 using ABI::Windows::Storage::Streams::IBuffer;
45 using Microsoft::WRL::ComPtr;
46
47 namespace crypto {
48
49 namespace {
50
51 enum KeyCredentialManagerAvailability {
52 kUnknown = 0,
53 kAvailable = 1,
54 kUnavailable = 2,
55 };
56
FormatError(std::string message,HRESULT hr)57 std::string FormatError(std::string message, HRESULT hr) {
58 return base::StrCat(
59 {message, " (hr = ", logging::SystemErrorCodeToString(hr), ")"});
60 }
61
62 // This helper splits OnceCallback three ways for use with `PostAsyncHandlers`,
63 // which has three separate paths to outcomes: Invoke a success callback, invoke
64 // an error callback, or return an error.
65 template <typename... Args>
66 std::tuple<base::OnceCallback<void(Args...)>,
67 base::OnceCallback<void(Args...)>,
68 base::OnceCallback<void(Args...)>>
SplitOnceCallbackIntoThree(base::OnceCallback<void (Args...)> callback)69 SplitOnceCallbackIntoThree(base::OnceCallback<void(Args...)> callback) {
70 auto first_split = base::SplitOnceCallback(std::move(callback));
71 auto second_split = base::SplitOnceCallback(std::move(first_split.first));
72 return {std::move(first_split.second), std::move(second_split.first),
73 std::move(second_split.second)};
74 }
75
OnSigningSuccess(base::OnceCallback<void (std::optional<std::vector<uint8_t>>)> callback,ComPtr<IKeyCredentialOperationResult> sign_result)76 void OnSigningSuccess(
77 base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> callback,
78 ComPtr<IKeyCredentialOperationResult> sign_result) {
79 KeyCredentialStatus status;
80 HRESULT hr = sign_result->get_Status(&status);
81 if (FAILED(hr) || status != KeyCredentialStatus_Success) {
82 LOG(ERROR) << FormatError(
83 "Failed to obtain Status from IKeyCredentialOperationResult", hr);
84 std::move(callback).Run(std::nullopt);
85 return;
86 }
87
88 ComPtr<IBuffer> signature_buffer;
89 hr = sign_result->get_Result(&signature_buffer);
90 if (FAILED(hr)) {
91 LOG(ERROR) << FormatError(
92 "Failed to obtain Result from IKeyCredentialOperationResult", hr);
93 std::move(callback).Run(std::nullopt);
94 return;
95 }
96
97 uint8_t* signature_data = nullptr;
98 uint32_t signature_length = 0;
99 hr = base::win::GetPointerToBufferData(signature_buffer.Get(),
100 &signature_data, &signature_length);
101 if (FAILED(hr)) {
102 LOG(ERROR) << FormatError("Failed to obtain data from signature buffer",
103 hr);
104 std::move(callback).Run(std::nullopt);
105 return;
106 }
107 std::move(callback).Run(
108 std::vector<uint8_t>(signature_data, signature_data + signature_length));
109 }
110
OnSigningError(base::OnceCallback<void (std::optional<std::vector<uint8_t>>)> callback,HRESULT hr)111 void OnSigningError(
112 base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> callback,
113 HRESULT hr) {
114 LOG(ERROR) << FormatError("Failed to sign with user-verifying signature", hr);
115 std::move(callback).Run(std::nullopt);
116 }
117
SignInternal(std::vector<uint8_t> data,ComPtr<IKeyCredential> credential,base::OnceCallback<void (std::optional<std::vector<uint8_t>>)> callback)118 void SignInternal(
119 std::vector<uint8_t> data,
120 ComPtr<IKeyCredential> credential,
121 base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> callback) {
122 Microsoft::WRL::ComPtr<IBuffer> signing_buf;
123 HRESULT hr =
124 base::win::CreateIBufferFromData(data.data(), data.size(), &signing_buf);
125 if (FAILED(hr)) {
126 LOG(ERROR) << FormatError("SignInternal: IBuffer creation failed", hr);
127 std::move(callback).Run(std::nullopt);
128 return;
129 }
130
131 ComPtr<IAsyncOperation<KeyCredentialOperationResult*>> sign_result;
132 hr = credential->RequestSignAsync(signing_buf.Get(), &sign_result);
133 if (FAILED(hr)) {
134 LOG(ERROR) << FormatError("SignInternal: Call to RequestSignAsync failed",
135 hr);
136 std::move(callback).Run(std::nullopt);
137 return;
138 }
139
140 auto callback_splits = SplitOnceCallbackIntoThree(std::move(callback));
141 hr = base::win::PostAsyncHandlers(
142 sign_result.Get(),
143 base::BindOnce(&OnSigningSuccess,
144 std::move(std::get<0>(callback_splits))),
145 base::BindOnce(&OnSigningError, std::move(std::get<1>(callback_splits))));
146 if (FAILED(hr)) {
147 LOG(ERROR) << FormatError("SignInternal: Call to PostAsyncHandlers failed",
148 hr);
149 std::move(std::get<2>(callback_splits)).Run(std::nullopt);
150 return;
151 }
152 }
153
154 class UserVerifyingSigningKeyWin : public UserVerifyingSigningKey {
155 public:
UserVerifyingSigningKeyWin(std::string key_name,ComPtr<IKeyCredential> credential)156 UserVerifyingSigningKeyWin(std::string key_name,
157 ComPtr<IKeyCredential> credential)
158 : key_name_(std::move(key_name)), credential_(std::move(credential)) {}
159 ~UserVerifyingSigningKeyWin() override = default;
160
Sign(base::span<const uint8_t> data,base::OnceCallback<void (std::optional<std::vector<uint8_t>>)> callback)161 void Sign(base::span<const uint8_t> data,
162 base::OnceCallback<void(std::optional<std::vector<uint8_t>>)>
163 callback) override {
164 scoped_refptr<base::SequencedTaskRunner> task_runner =
165 base::ThreadPool::CreateSequencedTaskRunner(
166 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
167 std::vector<uint8_t> vec_data(data.begin(), data.end());
168 task_runner->PostTask(
169 FROM_HERE,
170 base::BindOnce(
171 &SignInternal, std::move(vec_data), credential_,
172 base::BindPostTaskToCurrentDefault(std::move(callback))));
173 }
174
GetPublicKey() const175 std::vector<uint8_t> GetPublicKey() const override {
176 ComPtr<IBuffer> key_buf;
177 HRESULT hr = credential_->RetrievePublicKeyWithBlobType(
178 CryptographicPublicKeyBlobType_X509SubjectPublicKeyInfo, &key_buf);
179 CHECK(SUCCEEDED(hr)) << FormatError(
180 "Failed to obtain public key from KeyCredential", hr);
181
182 uint8_t* pub_key_data = nullptr;
183 uint32_t pub_key_length = 0;
184 hr = base::win::GetPointerToBufferData(key_buf.Get(), &pub_key_data,
185 &pub_key_length);
186 CHECK(SUCCEEDED(hr)) << FormatError(
187 "Failed to access public key buffer data", hr);
188 return std::vector<uint8_t>(pub_key_data, pub_key_data + pub_key_length);
189 }
190
GetKeyLabel() const191 const UserVerifyingKeyLabel& GetKeyLabel() const override {
192 return key_name_;
193 }
194
195 private:
196 std::string key_name_;
197 ComPtr<IKeyCredential> credential_;
198 };
199
OnKeyCreationCompletionSuccess(base::OnceCallback<void (std::unique_ptr<UserVerifyingSigningKey>)> callback,std::string key_name,ComPtr<IKeyCredentialRetrievalResult> key_result)200 void OnKeyCreationCompletionSuccess(
201 base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)> callback,
202 std::string key_name,
203 ComPtr<IKeyCredentialRetrievalResult> key_result) {
204 KeyCredentialStatus status;
205 HRESULT hr = key_result->get_Status(&status);
206 if (FAILED(hr)) {
207 LOG(ERROR) << FormatError(
208 "Failed to obtain Status from IKeyCredentialRetrievalResult", hr);
209 std::move(callback).Run(nullptr);
210 return;
211 } else if (status != KeyCredentialStatus_Success) {
212 LOG(ERROR) << "IKeyCredentialRetrievalResult failed with status "
213 << static_cast<uint32_t>(status);
214 std::move(callback).Run(nullptr);
215 return;
216 }
217
218 ComPtr<IKeyCredential> credential;
219 hr = key_result->get_Credential(&credential);
220 if (FAILED(hr)) {
221 LOG(ERROR) << FormatError(
222 "Failed to obtain KeyCredential from KeyCredentialRetrievalResult", hr);
223 std::move(callback).Run(nullptr);
224 return;
225 }
226 auto key = std::make_unique<UserVerifyingSigningKeyWin>(
227 std::move(key_name), std::move(credential));
228 std::move(callback).Run(std::move(key));
229 }
230
OnKeyCreationCompletionError(base::OnceCallback<void (std::unique_ptr<UserVerifyingSigningKey>)> callback,HRESULT hr)231 void OnKeyCreationCompletionError(
232 base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)> callback,
233 HRESULT hr) {
234 LOG(ERROR) << FormatError("Failed to obtain user-verifying key from system",
235 hr);
236 std::move(callback).Run(nullptr);
237 }
238
GenerateUserVerifyingSigningKeyInternal(std::string key_label,base::OnceCallback<void (std::unique_ptr<UserVerifyingSigningKey>)> callback)239 void GenerateUserVerifyingSigningKeyInternal(
240 std::string key_label,
241 base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)>
242 callback) {
243 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
244 auto key_name = base::win::ScopedHString::Create(key_label);
245
246 ComPtr<IKeyCredentialManagerStatics> factory;
247 HRESULT hr = base::win::GetActivationFactory<
248 IKeyCredentialManagerStatics,
249 RuntimeClass_Windows_Security_Credentials_KeyCredentialManager>(&factory);
250 if (FAILED(hr)) {
251 LOG(ERROR) << FormatError(
252 "GenerateUserVerifyingSigningKeyInternal: Failed to obtain activation "
253 "factory for KeyCredentialManager",
254 hr);
255 std::move(callback).Run(nullptr);
256 return;
257 }
258
259 ComPtr<IAsyncOperation<KeyCredentialRetrievalResult*>> create_result;
260 hr = factory->RequestCreateAsync(
261 key_name.get(),
262 KeyCredentialCreationOption::KeyCredentialCreationOption_ReplaceExisting,
263 &create_result);
264 if (FAILED(hr)) {
265 LOG(ERROR) << FormatError(
266 "GenerateUserVerifyingSigningKeyInternal: Call to RequestCreateAsync "
267 "failed",
268 hr);
269 std::move(callback).Run(nullptr);
270 return;
271 }
272
273 auto callback_splits = SplitOnceCallbackIntoThree(std::move(callback));
274 hr = base::win::PostAsyncHandlers(
275 create_result.Get(),
276 base::BindOnce(&OnKeyCreationCompletionSuccess,
277 std::move(std::get<0>(callback_splits)),
278 std::move(key_label)),
279 base::BindOnce(&OnKeyCreationCompletionError,
280 std::move(std::get<1>(callback_splits))));
281 if (FAILED(hr)) {
282 LOG(ERROR) << FormatError(
283 "GenerateUserVerifyingSigningKeyInternal: Call to PostAsyncHandlers "
284 "failed",
285 hr);
286 std::move(std::get<2>(callback_splits)).Run(nullptr);
287 return;
288 }
289 }
290
GetUserVerifyingSigningKeyInternal(std::string key_label,base::OnceCallback<void (std::unique_ptr<UserVerifyingSigningKey>)> callback)291 void GetUserVerifyingSigningKeyInternal(
292 std::string key_label,
293 base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)>
294 callback) {
295 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
296 auto key_name = base::win::ScopedHString::Create(key_label);
297
298 ComPtr<IKeyCredentialManagerStatics> factory;
299 HRESULT hr = base::win::GetActivationFactory<
300 IKeyCredentialManagerStatics,
301 RuntimeClass_Windows_Security_Credentials_KeyCredentialManager>(&factory);
302 if (FAILED(hr)) {
303 LOG(ERROR) << FormatError(
304 "GetUserVerifyingSigningKeyInternal: Failed to obtain activation "
305 "factory for KeyCredentialManager",
306 hr);
307 std::move(callback).Run(nullptr);
308 return;
309 }
310
311 ComPtr<IAsyncOperation<KeyCredentialRetrievalResult*>> open_result;
312 hr = factory->OpenAsync(key_name.get(), &open_result);
313 if (FAILED(hr)) {
314 LOG(ERROR) << FormatError(
315 "GetUserVerifyingSigningKeyInternal: Call to OpenAsync failed", hr);
316 std::move(callback).Run(nullptr);
317 return;
318 }
319
320 auto callback_splits = SplitOnceCallbackIntoThree(std::move(callback));
321 hr = base::win::PostAsyncHandlers(
322 open_result.Get(),
323 base::BindOnce(&OnKeyCreationCompletionSuccess,
324 std::move(std::get<0>(callback_splits)),
325 std::move(key_label)),
326 base::BindOnce(&OnKeyCreationCompletionError,
327 std::move(std::get<1>(callback_splits))));
328 if (FAILED(hr)) {
329 LOG(ERROR) << FormatError(
330 "GetUserVerifyingSigningKeyInternal: Call to PostAsyncHandlers failed",
331 hr);
332 std::move(std::get<2>(callback_splits)).Run(nullptr);
333 return;
334 }
335 }
336
DeleteUserVerifyingKeyInternal(UserVerifyingKeyLabel key_label,base::OnceCallback<void (bool)> callback)337 void DeleteUserVerifyingKeyInternal(UserVerifyingKeyLabel key_label,
338 base::OnceCallback<void(bool)> callback) {
339 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
340
341 ComPtr<IKeyCredentialManagerStatics> factory;
342 HRESULT hr = base::win::GetActivationFactory<
343 IKeyCredentialManagerStatics,
344 RuntimeClass_Windows_Security_Credentials_KeyCredentialManager>(&factory);
345 if (FAILED(hr)) {
346 LOG(ERROR) << FormatError(
347 "DeleteUserVerifyingKeyInternal: Failed to obtain activation "
348 "factory for KeyCredentialManager",
349 hr);
350 std::move(callback).Run(false);
351 return;
352 }
353
354 ComPtr<IAsyncAction> delete_operation;
355 auto key_name = base::win::ScopedHString::Create(key_label);
356 hr = factory->DeleteAsync(key_name.get(), &delete_operation);
357 if (FAILED(hr)) {
358 LOG(ERROR) << FormatError(
359 "DeleteUserVerifyingKeyInternal: Call to DeleteAsync failed", hr);
360 std::move(callback).Run(false);
361 return;
362 }
363
364 // DeleteAsync does not report a value, so we have to assume success.
365 std::move(callback).Run(true);
366 }
367
SelectAlgorithm(base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms)368 std::optional<SignatureVerifier::SignatureAlgorithm> SelectAlgorithm(
369 base::span<const SignatureVerifier::SignatureAlgorithm>
370 acceptable_algorithms) {
371 // Windows keys come in any algorithm you want, as long as it's RSA 2048.
372 for (auto algorithm : acceptable_algorithms) {
373 if (algorithm == SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256) {
374 return algorithm;
375 }
376 }
377 return std::nullopt;
378 }
379
380 class UserVerifyingKeyProviderWin : public UserVerifyingKeyProvider {
381 public:
382 UserVerifyingKeyProviderWin() = default;
383 ~UserVerifyingKeyProviderWin() override = default;
384
GenerateUserVerifyingSigningKey(base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms,base::OnceCallback<void (std::unique_ptr<UserVerifyingSigningKey>)> callback)385 void GenerateUserVerifyingSigningKey(
386 base::span<const SignatureVerifier::SignatureAlgorithm>
387 acceptable_algorithms,
388 base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)>
389 callback) override {
390 // Ignore the non-empty return value of `SelectAlgorithm` unless in the
391 // future Windows supports more algorithms.
392 if (!SelectAlgorithm(acceptable_algorithms)) {
393 LOG(ERROR) << "Key generation does not include a supported algorithm.";
394 std::move(callback).Run(nullptr);
395 return;
396 }
397
398 std::vector<uint8_t> random(16);
399 crypto::RandBytes(random);
400 UserVerifyingKeyLabel key_label =
401 base::StrCat({"uvkey-", base::Base64Encode(random)});
402
403 scoped_refptr<base::SequencedTaskRunner> task_runner =
404 base::ThreadPool::CreateSequencedTaskRunner(
405 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
406 task_runner->PostTask(
407 FROM_HERE,
408 base::BindOnce(
409 &GenerateUserVerifyingSigningKeyInternal, std::move(key_label),
410 base::BindPostTaskToCurrentDefault(std::move(callback))));
411 }
412
GetUserVerifyingSigningKey(UserVerifyingKeyLabel key_label,base::OnceCallback<void (std::unique_ptr<UserVerifyingSigningKey>)> callback)413 void GetUserVerifyingSigningKey(
414 UserVerifyingKeyLabel key_label,
415 base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)>
416 callback) override {
417 scoped_refptr<base::SequencedTaskRunner> task_runner =
418 base::ThreadPool::CreateSequencedTaskRunner(
419 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
420 task_runner->PostTask(
421 FROM_HERE,
422 base::BindOnce(
423 &GetUserVerifyingSigningKeyInternal, key_label,
424 base::BindPostTaskToCurrentDefault(std::move(callback))));
425 }
426
DeleteUserVerifyingKey(UserVerifyingKeyLabel key_label,base::OnceCallback<void (bool)> callback)427 void DeleteUserVerifyingKey(
428 UserVerifyingKeyLabel key_label,
429 base::OnceCallback<void(bool)> callback) override {
430 scoped_refptr<base::SequencedTaskRunner> task_runner =
431 base::ThreadPool::CreateSequencedTaskRunner(
432 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
433 task_runner->PostTask(
434 FROM_HERE, base::BindOnce(DeleteUserVerifyingKeyInternal, key_label,
435 base::BindPostTaskToCurrentDefault(
436 std::move(callback))));
437 }
438 };
439
IsKeyCredentialManagerAvailableInternal(base::OnceCallback<void (bool)> callback)440 void IsKeyCredentialManagerAvailableInternal(
441 base::OnceCallback<void(bool)> callback) {
442 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
443 // Lookup requires an asynchronous system API call, so cache the value.
444 static std::atomic<KeyCredentialManagerAvailability> availability =
445 KeyCredentialManagerAvailability::kUnknown;
446
447 // Read once to ensure consistency.
448 const KeyCredentialManagerAvailability current_availability = availability;
449 if (current_availability != KeyCredentialManagerAvailability::kUnknown) {
450 std::move(callback).Run(current_availability ==
451 KeyCredentialManagerAvailability::kAvailable);
452 return;
453 }
454
455 ComPtr<IKeyCredentialManagerStatics> factory;
456 HRESULT hr = base::win::GetActivationFactory<
457 IKeyCredentialManagerStatics,
458 RuntimeClass_Windows_Security_Credentials_KeyCredentialManager>(&factory);
459 if (FAILED(hr)) {
460 // Don't cache API call failures, allowing the possibility of trying again
461 // if this was a one-time failure.
462 std::move(callback).Run(false);
463 return;
464 }
465
466 ComPtr<IAsyncOperation<bool>> is_supported_operation;
467 hr = factory->IsSupportedAsync(&is_supported_operation);
468 if (FAILED(hr)) {
469 std::move(callback).Run(false);
470 return;
471 }
472
473 auto callback_splits = SplitOnceCallbackIntoThree(std::move(callback));
474 hr = base::win::PostAsyncHandlers(
475 is_supported_operation.Get(),
476 base::BindOnce(
477 [](base::OnceCallback<void(bool)> callback,
478 std::atomic<KeyCredentialManagerAvailability>& availability,
479 boolean result) {
480 availability = result
481 ? KeyCredentialManagerAvailability::kAvailable
482 : KeyCredentialManagerAvailability::kUnavailable;
483 std::move(callback).Run(result);
484 },
485 std::move(std::get<0>(callback_splits)), std::ref(availability)),
486 base::BindOnce([](base::OnceCallback<void(bool)> callback,
487 HRESULT) { std::move(callback).Run(false); },
488 std::move(std::get<1>(callback_splits))));
489 if (FAILED(hr)) {
490 std::move(std::get<2>(callback_splits)).Run(false);
491 return;
492 }
493 }
494
495 } // namespace
496
GetUserVerifyingKeyProviderWin()497 std::unique_ptr<UserVerifyingKeyProvider> GetUserVerifyingKeyProviderWin() {
498 return std::make_unique<UserVerifyingKeyProviderWin>();
499 }
500
IsKeyCredentialManagerAvailable(base::OnceCallback<void (bool)> callback)501 void IsKeyCredentialManagerAvailable(base::OnceCallback<void(bool)> callback) {
502 scoped_refptr<base::SequencedTaskRunner> task_runner =
503 base::ThreadPool::CreateSequencedTaskRunner(
504 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
505 task_runner->PostTask(
506 FROM_HERE,
507 base::BindOnce(&IsKeyCredentialManagerAvailableInternal,
508 base::BindPostTaskToCurrentDefault(std::move(callback))));
509 }
510
511 } // namespace crypto
512