xref: /aosp_15_r20/external/cronet/crypto/user_verifying_key_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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