• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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