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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "crypto/user_verifying_key.h"
11
12 #include <windows.h>
13
14 #include <windows.foundation.h>
15 #include <windows.security.credentials.h>
16 #include <windows.security.cryptography.core.h>
17 #include <windows.storage.streams.h>
18
19 #include <atomic>
20 #include <functional>
21 #include <utility>
22
23 #include "base/functional/callback_helpers.h"
24 #include "base/logging.h"
25 #include "base/memory/ref_counted.h"
26 #include "base/memory/scoped_refptr.h"
27 #include "base/memory/weak_ptr.h"
28 #include "base/metrics/histogram_functions.h"
29 #include "base/strings/strcat.h"
30 #include "base/strings/string_number_conversions.h"
31 #include "base/synchronization/atomic_flag.h"
32 #include "base/task/bind_post_task.h"
33 #include "base/task/single_thread_task_runner.h"
34 #include "base/task/thread_pool.h"
35 #include "base/threading/scoped_thread_priority.h"
36 #include "base/win/core_winrt_util.h"
37 #include "base/win/post_async_results.h"
38 #include "base/win/scoped_hstring.h"
39 #include "base/win/winrt_storage_util.h"
40 #include "crypto/random.h"
41
42 using ABI::Windows::Foundation::IAsyncAction;
43 using ABI::Windows::Foundation::IAsyncOperation;
44 using ABI::Windows::Security::Credentials::IKeyCredential;
45 using ABI::Windows::Security::Credentials::IKeyCredentialManagerStatics;
46 using ABI::Windows::Security::Credentials::IKeyCredentialOperationResult;
47 using ABI::Windows::Security::Credentials::IKeyCredentialRetrievalResult;
48 using ABI::Windows::Security::Credentials::KeyCredentialCreationOption;
49 using ABI::Windows::Security::Credentials::KeyCredentialOperationResult;
50 using ABI::Windows::Security::Credentials::KeyCredentialRetrievalResult;
51 using ABI::Windows::Security::Credentials::KeyCredentialStatus;
52 using ABI::Windows::Security::Credentials::
53 KeyCredentialStatus_CredentialAlreadyExists;
54 using ABI::Windows::Security::Credentials::KeyCredentialStatus_NotFound;
55 using ABI::Windows::Security::Credentials::KeyCredentialStatus_Success;
56 using ABI::Windows::Security::Credentials::KeyCredentialStatus_UserCanceled;
57 using ABI::Windows::Security::Credentials::
58 KeyCredentialStatus_UserPrefersPassword;
59 using ABI::Windows::Security::Cryptography::Core::
60 CryptographicPublicKeyBlobType_X509SubjectPublicKeyInfo;
61 using ABI::Windows::Storage::Streams::IBuffer;
62 using Microsoft::WRL::ComPtr;
63
64 namespace crypto {
65
66 namespace {
67
68 // Possible outcomes for WinRT API calls. These are recorded for signing
69 // and key creation.
70 // Do not delete or reorder entries, this must be kept in sync with the
71 // corresponding metrics enum.
72 enum class KeyCredentialCreateResult {
73 kSucceeded = 0,
74 kAPIReturnedError = 1,
75 kNoActivationFactory = 2,
76 kRequestCreateAsyncFailed = 3,
77 kPostAsyncHandlersFailed = 4,
78 kInvalidStatusReturned = 5,
79 kInvalidResultReturned = 6,
80 kInvalidCredentialReturned = 7,
81
82 kMaxValue = 7,
83 };
84
85 enum class KeyCredentialSignResult {
86 kSucceeded = 0,
87 kAPIReturnedError = 1,
88 kRequestSignAsyncFailed = 2,
89 kPostAsyncHandlersFailed = 3,
90 kIBufferCreationFailed = 4,
91 kInvalidStatusReturned = 5,
92 kInvalidResultReturned = 6,
93 kInvalidSignatureBufferReturned = 7,
94
95 kMaxValue = 7,
96 };
97
RecordCreateAsyncResult(KeyCredentialCreateResult result)98 void RecordCreateAsyncResult(KeyCredentialCreateResult result) {
99 base::UmaHistogramEnumeration(
100 "WebAuthentication.Windows.KeyCredentialCreation", result);
101 }
102
RecordSignAsyncResult(KeyCredentialSignResult result)103 void RecordSignAsyncResult(KeyCredentialSignResult result) {
104 base::UmaHistogramEnumeration("WebAuthentication.Windows.KeyCredentialSign",
105 result);
106 }
107
108 // Due to a Windows bug (http://task.ms/49689617), the system UI for
109 // KeyCredentialManager appears under all other windows, at least when invoked
110 // from a Win32 app. Therefore this code polls the visible windows and
111 // foregrounds the correct window when it appears.
112 class HelloDialogForegrounder
113 : public base::RefCountedThreadSafe<HelloDialogForegrounder> {
114 public:
115 HelloDialogForegrounder() = default;
116 HelloDialogForegrounder(const HelloDialogForegrounder&) = delete;
117 HelloDialogForegrounder& operator=(const HelloDialogForegrounder&) = delete;
118
Start()119 void Start() {
120 CHECK_EQ(state_, State::kNotStarted);
121 state_ = State::kPollingForFirstAppearance;
122 BringHelloDialogToFront(/*iteration=*/0);
123 }
124
Stop()125 void Stop() { stopping_.Set(); }
126
127 private:
128 friend class base::RefCountedThreadSafe<HelloDialogForegrounder>;
129 ~HelloDialogForegrounder() = default;
130
131 // Values to report the results of attempts to bring the Windows Hello
132 // user verification dialog to the foreground.
133 // Do not delete or reorder entries, this must be kept in sync with the
134 // corresponding metrics enum.
135 enum class ForegroundHelloDialogResult {
136 kSucceeded = 0,
137 kForegroundingFailed = 1,
138 kWindowNotFound = 2,
139 kAbortedWithoutFindingWindow = 3,
140
141 kMaxValue = 3,
142 };
143
144 enum class State {
145 kNotStarted,
146 kPollingForFirstAppearance,
147 kPollingForAuthRetry,
148 };
149
RecordForegroundingOutcome(ForegroundHelloDialogResult result)150 void RecordForegroundingOutcome(ForegroundHelloDialogResult result) {
151 base::UmaHistogramEnumeration(
152 "WebAuthentication.Windows.ForegroundedWindowsHelloDialog", result);
153 }
154
BringHelloDialogToFront(int iteration)155 void BringHelloDialogToFront(int iteration) {
156 int interval = 100;
157
158 if (stopping_.IsSet()) {
159 if (state_ == State::kPollingForFirstAppearance) {
160 // In State::kPollingForAuthRetry, success has already been reported.
161 RecordForegroundingOutcome(
162 ForegroundHelloDialogResult::kAbortedWithoutFindingWindow);
163 }
164 return;
165 }
166
167 constexpr wchar_t kTargetWindowName[] = L"Windows Security";
168 constexpr wchar_t kTargetClassName[] = L"Credential Dialog Xaml Host";
169 if (state_ == State::kPollingForFirstAppearance) {
170 constexpr int kMaxIterations = 40;
171 if (iteration > kMaxIterations) {
172 RecordForegroundingOutcome(
173 ForegroundHelloDialogResult::kWindowNotFound);
174 return;
175 }
176
177 if (HWND hwnd = FindWindowW(kTargetClassName, kTargetWindowName)) {
178 base::UmaHistogramExactLinear(
179 "WebAuthentication.Windows.FindHelloDialogIterationCount",
180 iteration,
181 /*exclusive_max=*/kMaxIterations + 1);
182 if (SetForegroundWindow(hwnd)) {
183 RecordForegroundingOutcome(ForegroundHelloDialogResult::kSucceeded);
184 } else {
185 RecordForegroundingOutcome(
186 ForegroundHelloDialogResult::kForegroundingFailed);
187 }
188 state_ = State::kPollingForAuthRetry;
189 }
190 } else {
191 CHECK_EQ(state_, State::kPollingForAuthRetry);
192 if (HWND hwnd = FindWindowW(kTargetClassName, kTargetWindowName)) {
193 SetForegroundWindow(hwnd);
194 }
195 interval = 500;
196 }
197 base::ThreadPool::PostDelayedTask(
198 FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
199 base::BindOnce(&HelloDialogForegrounder::BringHelloDialogToFront,
200 base::WrapRefCounted<HelloDialogForegrounder>(this),
201 iteration + 1),
202 base::Milliseconds(interval));
203 }
204
205 State state_ = State::kNotStarted;
206 base::AtomicFlag stopping_;
207 };
208
209 enum KeyCredentialManagerAvailability {
210 kUnknown = 0,
211 kAvailable = 1,
212 kUnavailable = 2,
213 };
214
FormatError(std::string message,HRESULT hr)215 std::string FormatError(std::string message, HRESULT hr) {
216 return base::StrCat(
217 {message, " (hr = ", logging::SystemErrorCodeToString(hr), ")"});
218 }
219
220 // This helper splits OnceCallback three ways for use with `PostAsyncHandlers`,
221 // which has three separate paths to outcomes: Invoke a success callback, invoke
222 // an error callback, or return an error.
223 template <typename... Args>
224 std::tuple<base::OnceCallback<void(Args...)>,
225 base::OnceCallback<void(Args...)>,
226 base::OnceCallback<void(Args...)>>
SplitOnceCallbackIntoThree(base::OnceCallback<void (Args...)> callback)227 SplitOnceCallbackIntoThree(base::OnceCallback<void(Args...)> callback) {
228 auto first_split = base::SplitOnceCallback(std::move(callback));
229 auto second_split = base::SplitOnceCallback(std::move(first_split.first));
230 return {std::move(first_split.second), std::move(second_split.first),
231 std::move(second_split.second)};
232 }
233
OnSigningSuccess(UserVerifyingSigningKey::UserVerifyingKeySignatureCallback callback,scoped_refptr<HelloDialogForegrounder> foregrounder,ComPtr<IKeyCredentialOperationResult> sign_result)234 void OnSigningSuccess(
235 UserVerifyingSigningKey::UserVerifyingKeySignatureCallback callback,
236 scoped_refptr<HelloDialogForegrounder> foregrounder,
237 ComPtr<IKeyCredentialOperationResult> sign_result) {
238 foregrounder->Stop();
239
240 KeyCredentialStatus status;
241 HRESULT hr = sign_result->get_Status(&status);
242 if (FAILED(hr) || status != KeyCredentialStatus_Success) {
243 LOG(ERROR) << FormatError(
244 "Failed to obtain Status from IKeyCredentialOperationResult", hr);
245 RecordSignAsyncResult(KeyCredentialSignResult::kInvalidStatusReturned);
246 UserVerifyingKeySigningError sign_error;
247 switch (status) {
248 case KeyCredentialStatus_UserCanceled:
249 case KeyCredentialStatus_UserPrefersPassword:
250 sign_error = UserVerifyingKeySigningError::kUserCancellation;
251 break;
252 default:
253 sign_error = UserVerifyingKeySigningError::kUnknownError;
254 }
255 std::move(callback).Run(base::unexpected(sign_error));
256 return;
257 }
258
259 ComPtr<IBuffer> signature_buffer;
260 hr = sign_result->get_Result(&signature_buffer);
261 if (FAILED(hr)) {
262 LOG(ERROR) << FormatError(
263 "Failed to obtain Result from IKeyCredentialOperationResult", hr);
264 RecordSignAsyncResult(KeyCredentialSignResult::kInvalidResultReturned);
265 std::move(callback).Run(
266 base::unexpected(UserVerifyingKeySigningError::kPlatformApiError));
267 return;
268 }
269
270 uint8_t* signature_data = nullptr;
271 uint32_t signature_length = 0;
272 hr = base::win::GetPointerToBufferData(signature_buffer.Get(),
273 &signature_data, &signature_length);
274 if (FAILED(hr)) {
275 LOG(ERROR) << FormatError("Failed to obtain data from signature buffer",
276 hr);
277 RecordSignAsyncResult(
278 KeyCredentialSignResult::kInvalidSignatureBufferReturned);
279 std::move(callback).Run(
280 base::unexpected(UserVerifyingKeySigningError::kPlatformApiError));
281 return;
282 }
283
284 RecordSignAsyncResult(KeyCredentialSignResult::kSucceeded);
285 std::move(callback).Run(base::ok(
286 std::vector<uint8_t>(signature_data, signature_data + signature_length)));
287 }
288
OnSigningError(UserVerifyingSigningKey::UserVerifyingKeySignatureCallback callback,scoped_refptr<HelloDialogForegrounder> foregrounder,HRESULT hr)289 void OnSigningError(
290 UserVerifyingSigningKey::UserVerifyingKeySignatureCallback callback,
291 scoped_refptr<HelloDialogForegrounder> foregrounder,
292 HRESULT hr) {
293 foregrounder->Stop();
294 LOG(ERROR) << FormatError("Failed to sign with user-verifying signature", hr);
295 RecordSignAsyncResult(KeyCredentialSignResult::kAPIReturnedError);
296 std::move(callback).Run(
297 base::unexpected(UserVerifyingKeySigningError::kPlatformApiError));
298 }
299
SignInternal(std::vector<uint8_t> data,ComPtr<IKeyCredential> credential,UserVerifyingSigningKey::UserVerifyingKeySignatureCallback callback)300 void SignInternal(
301 std::vector<uint8_t> data,
302 ComPtr<IKeyCredential> credential,
303 UserVerifyingSigningKey::UserVerifyingKeySignatureCallback callback) {
304 Microsoft::WRL::ComPtr<IBuffer> signing_buf;
305 HRESULT hr =
306 base::win::CreateIBufferFromData(data.data(), data.size(), &signing_buf);
307 if (FAILED(hr)) {
308 LOG(ERROR) << FormatError("SignInternal: IBuffer creation failed", hr);
309 RecordSignAsyncResult(KeyCredentialSignResult::kIBufferCreationFailed);
310 std::move(callback).Run(
311 base::unexpected(UserVerifyingKeySigningError::kPlatformApiError));
312 return;
313 }
314
315 ComPtr<IAsyncOperation<KeyCredentialOperationResult*>> sign_result;
316 hr = credential->RequestSignAsync(signing_buf.Get(), &sign_result);
317 if (FAILED(hr)) {
318 LOG(ERROR) << FormatError("SignInternal: Call to RequestSignAsync failed",
319 hr);
320 RecordSignAsyncResult(KeyCredentialSignResult::kRequestSignAsyncFailed);
321 std::move(callback).Run(
322 base::unexpected(UserVerifyingKeySigningError::kPlatformApiError));
323 return;
324 }
325
326 auto foregrounder = base::MakeRefCounted<HelloDialogForegrounder>();
327 auto callback_splits = SplitOnceCallbackIntoThree(std::move(callback));
328 hr = base::win::PostAsyncHandlers(
329 sign_result.Get(),
330 base::BindOnce(&OnSigningSuccess, std::move(std::get<0>(callback_splits)),
331 foregrounder),
332 base::BindOnce(&OnSigningError, std::move(std::get<1>(callback_splits)),
333 foregrounder));
334 if (FAILED(hr)) {
335 LOG(ERROR) << FormatError("SignInternal: Call to PostAsyncHandlers failed",
336 hr);
337 RecordSignAsyncResult(KeyCredentialSignResult::kPostAsyncHandlersFailed);
338 std::move(std::get<2>(callback_splits))
339 .Run(base::unexpected(UserVerifyingKeySigningError::kPlatformApiError));
340 return;
341 }
342
343 foregrounder->Start();
344 }
345
346 class UserVerifyingSigningKeyWin : public UserVerifyingSigningKey {
347 public:
UserVerifyingSigningKeyWin(std::string key_name,ComPtr<IKeyCredential> credential)348 UserVerifyingSigningKeyWin(std::string key_name,
349 ComPtr<IKeyCredential> credential)
350 : key_name_(std::move(key_name)), credential_(std::move(credential)) {}
351 ~UserVerifyingSigningKeyWin() override = default;
352
Sign(base::span<const uint8_t> data,UserVerifyingKeySignatureCallback callback)353 void Sign(base::span<const uint8_t> data,
354 UserVerifyingKeySignatureCallback callback) override {
355 scoped_refptr<base::SequencedTaskRunner> task_runner =
356 base::ThreadPool::CreateSequencedTaskRunner(
357 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
358 std::vector<uint8_t> vec_data(data.begin(), data.end());
359 task_runner->PostTask(
360 FROM_HERE,
361 base::BindOnce(
362 &SignInternal, std::move(vec_data), credential_,
363 base::BindPostTaskToCurrentDefault(std::move(callback))));
364 }
365
GetPublicKey() const366 std::vector<uint8_t> GetPublicKey() const override {
367 ComPtr<IBuffer> key_buf;
368 HRESULT hr = credential_->RetrievePublicKeyWithBlobType(
369 CryptographicPublicKeyBlobType_X509SubjectPublicKeyInfo, &key_buf);
370 CHECK(SUCCEEDED(hr)) << FormatError(
371 "Failed to obtain public key from KeyCredential", hr);
372
373 uint8_t* pub_key_data = nullptr;
374 uint32_t pub_key_length = 0;
375 hr = base::win::GetPointerToBufferData(key_buf.Get(), &pub_key_data,
376 &pub_key_length);
377 CHECK(SUCCEEDED(hr)) << FormatError(
378 "Failed to access public key buffer data", hr);
379 return std::vector<uint8_t>(pub_key_data, pub_key_data + pub_key_length);
380 }
381
GetKeyLabel() const382 const UserVerifyingKeyLabel& GetKeyLabel() const override {
383 return key_name_;
384 }
385
IsHardwareBacked() const386 bool IsHardwareBacked() const override { return true; }
387
388 private:
389 std::string key_name_;
390 ComPtr<IKeyCredential> credential_;
391 };
392
OnKeyCreationCompletionSuccess(UserVerifyingKeyProvider::UserVerifyingKeyCreationCallback callback,std::string key_name,scoped_refptr<HelloDialogForegrounder> foregrounder,ComPtr<IKeyCredentialRetrievalResult> key_result)393 void OnKeyCreationCompletionSuccess(
394 UserVerifyingKeyProvider::UserVerifyingKeyCreationCallback callback,
395 std::string key_name,
396 scoped_refptr<HelloDialogForegrounder> foregrounder,
397 ComPtr<IKeyCredentialRetrievalResult> key_result) {
398 if (foregrounder) {
399 foregrounder->Stop();
400 }
401
402 KeyCredentialStatus status;
403 HRESULT hr = key_result->get_Status(&status);
404 if (FAILED(hr)) {
405 LOG(ERROR) << FormatError(
406 "Failed to obtain Status from IKeyCredentialRetrievalResult", hr);
407 RecordCreateAsyncResult(KeyCredentialCreateResult::kInvalidStatusReturned);
408 std::move(callback).Run(
409 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
410 return;
411 } else if (status != KeyCredentialStatus_Success) {
412 LOG(ERROR) << "IKeyCredentialRetrievalResult failed with status "
413 << static_cast<uint32_t>(status);
414 RecordCreateAsyncResult(KeyCredentialCreateResult::kInvalidResultReturned);
415 UserVerifyingKeyCreationError uv_key_error;
416 switch (status) {
417 case KeyCredentialStatus_CredentialAlreadyExists:
418 uv_key_error = UserVerifyingKeyCreationError::kDuplicateCredential;
419 break;
420 case KeyCredentialStatus_NotFound:
421 uv_key_error = UserVerifyingKeyCreationError::kNotFound;
422 break;
423 case KeyCredentialStatus_UserCanceled:
424 case KeyCredentialStatus_UserPrefersPassword:
425 uv_key_error = UserVerifyingKeyCreationError::kUserCancellation;
426 break;
427 default:
428 uv_key_error = UserVerifyingKeyCreationError::kUnknownError;
429 }
430 std::move(callback).Run(base::unexpected(uv_key_error));
431 return;
432 }
433
434 ComPtr<IKeyCredential> credential;
435 hr = key_result->get_Credential(&credential);
436 if (FAILED(hr)) {
437 LOG(ERROR) << FormatError(
438 "Failed to obtain KeyCredential from KeyCredentialRetrievalResult", hr);
439 RecordCreateAsyncResult(
440 KeyCredentialCreateResult::kInvalidCredentialReturned);
441 std::move(callback).Run(
442 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
443 return;
444 }
445
446 RecordCreateAsyncResult(KeyCredentialCreateResult::kSucceeded);
447 auto key = std::make_unique<UserVerifyingSigningKeyWin>(
448 std::move(key_name), std::move(credential));
449 std::move(callback).Run(base::ok(std::move(key)));
450 }
451
OnKeyCreationCompletionError(UserVerifyingKeyProvider::UserVerifyingKeyCreationCallback callback,scoped_refptr<HelloDialogForegrounder> foregrounder,HRESULT hr)452 void OnKeyCreationCompletionError(
453 UserVerifyingKeyProvider::UserVerifyingKeyCreationCallback callback,
454 scoped_refptr<HelloDialogForegrounder> foregrounder,
455 HRESULT hr) {
456 if (foregrounder) {
457 foregrounder->Stop();
458 }
459 LOG(ERROR) << FormatError("Failed to obtain user-verifying key from system",
460 hr);
461 RecordCreateAsyncResult(KeyCredentialCreateResult::kAPIReturnedError);
462 std::move(callback).Run(
463 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
464 }
465
GenerateUserVerifyingSigningKeyInternal(std::string key_label,UserVerifyingKeyProvider::UserVerifyingKeyCreationCallback callback)466 void GenerateUserVerifyingSigningKeyInternal(
467 std::string key_label,
468 UserVerifyingKeyProvider::UserVerifyingKeyCreationCallback callback) {
469 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
470 auto key_name = base::win::ScopedHString::Create(key_label);
471
472 ComPtr<IKeyCredentialManagerStatics> factory;
473 HRESULT hr = base::win::GetActivationFactory<
474 IKeyCredentialManagerStatics,
475 RuntimeClass_Windows_Security_Credentials_KeyCredentialManager>(&factory);
476 if (FAILED(hr)) {
477 LOG(ERROR) << FormatError(
478 "GenerateUserVerifyingSigningKeyInternal: Failed to obtain activation "
479 "factory for KeyCredentialManager",
480 hr);
481 RecordCreateAsyncResult(KeyCredentialCreateResult::kNoActivationFactory);
482 std::move(callback).Run(
483 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
484 return;
485 }
486 ComPtr<IAsyncOperation<KeyCredentialRetrievalResult*>> create_result;
487 hr = factory->RequestCreateAsync(
488 key_name.get(),
489 KeyCredentialCreationOption::KeyCredentialCreationOption_ReplaceExisting,
490 &create_result);
491 if (FAILED(hr)) {
492 LOG(ERROR) << FormatError(
493 "GenerateUserVerifyingSigningKeyInternal: Call to RequestCreateAsync "
494 "failed",
495 hr);
496 RecordCreateAsyncResult(
497 KeyCredentialCreateResult::kRequestCreateAsyncFailed);
498 std::move(callback).Run(
499 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
500 return;
501 }
502
503 auto foregrounder = base::MakeRefCounted<HelloDialogForegrounder>();
504 auto callback_splits = SplitOnceCallbackIntoThree(std::move(callback));
505 hr = base::win::PostAsyncHandlers(
506 create_result.Get(),
507 base::BindOnce(&OnKeyCreationCompletionSuccess,
508 std::move(std::get<0>(callback_splits)),
509 std::move(key_label), foregrounder),
510 base::BindOnce(&OnKeyCreationCompletionError,
511 std::move(std::get<1>(callback_splits)), foregrounder));
512 if (FAILED(hr)) {
513 LOG(ERROR) << FormatError(
514 "GenerateUserVerifyingSigningKeyInternal: Call to PostAsyncHandlers "
515 "failed",
516 hr);
517 RecordCreateAsyncResult(
518 KeyCredentialCreateResult::kPostAsyncHandlersFailed);
519 std::move(std::get<2>(callback_splits))
520 .Run(
521 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
522 return;
523 }
524
525 foregrounder->Start();
526 }
527
GetUserVerifyingSigningKeyInternal(std::string key_label,UserVerifyingKeyProvider::UserVerifyingKeyCreationCallback callback)528 void GetUserVerifyingSigningKeyInternal(
529 std::string key_label,
530 UserVerifyingKeyProvider::UserVerifyingKeyCreationCallback callback) {
531 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
532 auto key_name = base::win::ScopedHString::Create(key_label);
533
534 ComPtr<IKeyCredentialManagerStatics> factory;
535 HRESULT hr = base::win::GetActivationFactory<
536 IKeyCredentialManagerStatics,
537 RuntimeClass_Windows_Security_Credentials_KeyCredentialManager>(&factory);
538 if (FAILED(hr)) {
539 LOG(ERROR) << FormatError(
540 "GetUserVerifyingSigningKeyInternal: Failed to obtain activation "
541 "factory for KeyCredentialManager",
542 hr);
543 std::move(callback).Run(
544 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
545 return;
546 }
547
548 ComPtr<IAsyncOperation<KeyCredentialRetrievalResult*>> open_result;
549 hr = factory->OpenAsync(key_name.get(), &open_result);
550 if (FAILED(hr)) {
551 LOG(ERROR) << FormatError(
552 "GetUserVerifyingSigningKeyInternal: Call to OpenAsync failed", hr);
553 std::move(callback).Run(
554 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
555 return;
556 }
557
558 auto callback_splits = SplitOnceCallbackIntoThree(std::move(callback));
559 hr = base::win::PostAsyncHandlers(
560 open_result.Get(),
561 base::BindOnce(&OnKeyCreationCompletionSuccess,
562 std::move(std::get<0>(callback_splits)),
563 std::move(key_label),
564 /*HelloDialogForegrounder=*/nullptr),
565 base::BindOnce(&OnKeyCreationCompletionError,
566 std::move(std::get<1>(callback_splits)),
567 /*HelloDialogForegrounder=*/nullptr));
568 if (FAILED(hr)) {
569 LOG(ERROR) << FormatError(
570 "GetUserVerifyingSigningKeyInternal: Call to PostAsyncHandlers failed",
571 hr);
572 std::move(std::get<2>(callback_splits))
573 .Run(
574 base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError));
575 return;
576 }
577 }
578
DeleteUserVerifyingKeyInternal(UserVerifyingKeyLabel key_label,base::OnceCallback<void (bool)> callback)579 void DeleteUserVerifyingKeyInternal(UserVerifyingKeyLabel key_label,
580 base::OnceCallback<void(bool)> callback) {
581 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
582
583 ComPtr<IKeyCredentialManagerStatics> factory;
584 HRESULT hr = base::win::GetActivationFactory<
585 IKeyCredentialManagerStatics,
586 RuntimeClass_Windows_Security_Credentials_KeyCredentialManager>(&factory);
587 if (FAILED(hr)) {
588 LOG(ERROR) << FormatError(
589 "DeleteUserVerifyingKeyInternal: Failed to obtain activation "
590 "factory for KeyCredentialManager",
591 hr);
592 std::move(callback).Run(false);
593 return;
594 }
595
596 ComPtr<IAsyncAction> delete_operation;
597 auto key_name = base::win::ScopedHString::Create(key_label);
598 hr = factory->DeleteAsync(key_name.get(), &delete_operation);
599 if (FAILED(hr)) {
600 LOG(ERROR) << FormatError(
601 "DeleteUserVerifyingKeyInternal: Call to DeleteAsync failed", hr);
602 std::move(callback).Run(false);
603 return;
604 }
605
606 // DeleteAsync does not report a value, so we have to assume success.
607 std::move(callback).Run(true);
608 }
609
SelectAlgorithm(base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms)610 std::optional<SignatureVerifier::SignatureAlgorithm> SelectAlgorithm(
611 base::span<const SignatureVerifier::SignatureAlgorithm>
612 acceptable_algorithms) {
613 // Windows keys come in any algorithm you want, as long as it's RSA 2048.
614 for (auto algorithm : acceptable_algorithms) {
615 if (algorithm == SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256) {
616 return algorithm;
617 }
618 }
619 return std::nullopt;
620 }
621
622 class UserVerifyingKeyProviderWin : public UserVerifyingKeyProvider {
623 public:
624 UserVerifyingKeyProviderWin() = default;
625 ~UserVerifyingKeyProviderWin() override = default;
626
GenerateUserVerifyingSigningKey(base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms,UserVerifyingKeyCreationCallback callback)627 void GenerateUserVerifyingSigningKey(
628 base::span<const SignatureVerifier::SignatureAlgorithm>
629 acceptable_algorithms,
630 UserVerifyingKeyCreationCallback callback) override {
631 // Ignore the non-empty return value of `SelectAlgorithm` unless in the
632 // future Windows supports more algorithms.
633 if (!SelectAlgorithm(acceptable_algorithms)) {
634 LOG(ERROR) << "Key generation does not include a supported algorithm.";
635 std::move(callback).Run(base::unexpected(
636 UserVerifyingKeyCreationError::kNoMatchingAlgorithm));
637 return;
638 }
639
640 std::vector<uint8_t> random(16);
641 crypto::RandBytes(random);
642 UserVerifyingKeyLabel key_label =
643 base::StrCat({"uvkey-", base::HexEncode(random)});
644
645 scoped_refptr<base::SequencedTaskRunner> task_runner =
646 base::ThreadPool::CreateSequencedTaskRunner(
647 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
648 task_runner->PostTask(
649 FROM_HERE,
650 base::BindOnce(
651 &GenerateUserVerifyingSigningKeyInternal, std::move(key_label),
652 base::BindPostTaskToCurrentDefault(std::move(callback))));
653 }
654
GetUserVerifyingSigningKey(UserVerifyingKeyLabel key_label,UserVerifyingKeyCreationCallback callback)655 void GetUserVerifyingSigningKey(
656 UserVerifyingKeyLabel key_label,
657 UserVerifyingKeyCreationCallback callback) override {
658 scoped_refptr<base::SequencedTaskRunner> task_runner =
659 base::ThreadPool::CreateSequencedTaskRunner(
660 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
661 task_runner->PostTask(
662 FROM_HERE,
663 base::BindOnce(
664 &GetUserVerifyingSigningKeyInternal, key_label,
665 base::BindPostTaskToCurrentDefault(std::move(callback))));
666 }
667
DeleteUserVerifyingKey(UserVerifyingKeyLabel key_label,base::OnceCallback<void (bool)> callback)668 void DeleteUserVerifyingKey(
669 UserVerifyingKeyLabel key_label,
670 base::OnceCallback<void(bool)> callback) override {
671 scoped_refptr<base::SequencedTaskRunner> task_runner =
672 base::ThreadPool::CreateSequencedTaskRunner(
673 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
674 task_runner->PostTask(
675 FROM_HERE, base::BindOnce(DeleteUserVerifyingKeyInternal, key_label,
676 base::BindPostTaskToCurrentDefault(
677 std::move(callback))));
678 }
679 };
680
IsKeyCredentialManagerAvailableInternal(base::OnceCallback<void (bool)> callback)681 void IsKeyCredentialManagerAvailableInternal(
682 base::OnceCallback<void(bool)> callback) {
683 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
684 // Lookup requires an asynchronous system API call, so cache the value.
685 static std::atomic<KeyCredentialManagerAvailability> availability =
686 KeyCredentialManagerAvailability::kUnknown;
687
688 // Read once to ensure consistency.
689 const KeyCredentialManagerAvailability current_availability = availability;
690 if (current_availability != KeyCredentialManagerAvailability::kUnknown) {
691 std::move(callback).Run(current_availability ==
692 KeyCredentialManagerAvailability::kAvailable);
693 return;
694 }
695
696 ComPtr<IKeyCredentialManagerStatics> factory;
697 HRESULT hr = base::win::GetActivationFactory<
698 IKeyCredentialManagerStatics,
699 RuntimeClass_Windows_Security_Credentials_KeyCredentialManager>(&factory);
700 if (FAILED(hr)) {
701 // Don't cache API call failures, allowing the possibility of trying again
702 // if this was a one-time failure.
703 std::move(callback).Run(false);
704 return;
705 }
706
707 ComPtr<IAsyncOperation<bool>> is_supported_operation;
708 hr = factory->IsSupportedAsync(&is_supported_operation);
709 if (FAILED(hr)) {
710 std::move(callback).Run(false);
711 return;
712 }
713
714 auto callback_splits = SplitOnceCallbackIntoThree(std::move(callback));
715 hr = base::win::PostAsyncHandlers(
716 is_supported_operation.Get(),
717 base::BindOnce(
718 [](base::OnceCallback<void(bool)> callback,
719 std::atomic<KeyCredentialManagerAvailability>& availability,
720 boolean result) {
721 availability = result
722 ? KeyCredentialManagerAvailability::kAvailable
723 : KeyCredentialManagerAvailability::kUnavailable;
724 std::move(callback).Run(result);
725 },
726 std::move(std::get<0>(callback_splits)), std::ref(availability)),
727 base::BindOnce([](base::OnceCallback<void(bool)> callback,
728 HRESULT) { std::move(callback).Run(false); },
729 std::move(std::get<1>(callback_splits))));
730 if (FAILED(hr)) {
731 std::move(std::get<2>(callback_splits)).Run(false);
732 return;
733 }
734 }
735
736 } // namespace
737
GetUserVerifyingKeyProviderWin()738 std::unique_ptr<UserVerifyingKeyProvider> GetUserVerifyingKeyProviderWin() {
739 return std::make_unique<UserVerifyingKeyProviderWin>();
740 }
741
IsKeyCredentialManagerAvailable(base::OnceCallback<void (bool)> callback)742 void IsKeyCredentialManagerAvailable(base::OnceCallback<void(bool)> callback) {
743 scoped_refptr<base::SequencedTaskRunner> task_runner =
744 base::ThreadPool::CreateSequencedTaskRunner(
745 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
746 task_runner->PostTask(
747 FROM_HERE,
748 base::BindOnce(&IsKeyCredentialManagerAvailableInternal,
749 base::BindPostTaskToCurrentDefault(std::move(callback))));
750 }
751
752 } // namespace crypto
753