Bug 1863873 - Block ability to perform audio decoding outside of Utility on release...
[gecko.git] / dom / webauthn / WinWebAuthnService.cpp
blob8667cf5615651e64c280585e8f3df066956fe4b5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/PWebAuthnTransactionParent.h"
8 #include "mozilla/ipc/BackgroundParent.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/MozPromise.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/ScopeExit.h"
13 #include "mozilla/StaticMutex.h"
14 #include "mozilla/Unused.h"
16 #include "nsTextFormatter.h"
17 #include "nsWindowsHelpers.h"
18 #include "WebAuthnAutoFillEntry.h"
19 #include "WebAuthnEnumStrings.h"
20 #include "WebAuthnResult.h"
21 #include "WebAuthnTransportIdentifiers.h"
22 #include "winwebauthn/webauthn.h"
23 #include "WinWebAuthnService.h"
25 namespace mozilla::dom {
27 namespace {
28 StaticRWLock gWinWebAuthnModuleLock;
30 static bool gWinWebAuthnModuleUnusable = false;
31 static HMODULE gWinWebAuthnModule = 0;
33 static decltype(WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)*
34 gWinWebauthnIsUVPAA = nullptr;
35 static decltype(WebAuthNAuthenticatorMakeCredential)*
36 gWinWebauthnMakeCredential = nullptr;
37 static decltype(WebAuthNFreeCredentialAttestation)*
38 gWinWebauthnFreeCredentialAttestation = nullptr;
39 static decltype(WebAuthNAuthenticatorGetAssertion)* gWinWebauthnGetAssertion =
40 nullptr;
41 static decltype(WebAuthNFreeAssertion)* gWinWebauthnFreeAssertion = nullptr;
42 static decltype(WebAuthNGetCancellationId)* gWinWebauthnGetCancellationId =
43 nullptr;
44 static decltype(WebAuthNCancelCurrentOperation)*
45 gWinWebauthnCancelCurrentOperation = nullptr;
46 static decltype(WebAuthNGetErrorName)* gWinWebauthnGetErrorName = nullptr;
47 static decltype(WebAuthNGetApiVersionNumber)* gWinWebauthnGetApiVersionNumber =
48 nullptr;
49 static decltype(WebAuthNGetPlatformCredentialList)*
50 gWinWebauthnGetPlatformCredentialList = nullptr;
51 static decltype(WebAuthNFreePlatformCredentialList)*
52 gWinWebauthnFreePlatformCredentialList = nullptr;
54 } // namespace
56 /***********************************************************************
57 * WinWebAuthnService Implementation
58 **********************************************************************/
60 constexpr uint32_t kMinWinWebAuthNApiVersion = WEBAUTHN_API_VERSION_1;
62 NS_IMPL_ISUPPORTS(WinWebAuthnService, nsIWebAuthnService)
64 /* static */
65 nsresult WinWebAuthnService::EnsureWinWebAuthnModuleLoaded() {
67 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
68 if (gWinWebAuthnModule) {
69 // The module is already loaded.
70 return NS_OK;
72 if (gWinWebAuthnModuleUnusable) {
73 // A previous attempt to load the module failed.
74 return NS_ERROR_NOT_AVAILABLE;
78 StaticAutoWriteLock lock(gWinWebAuthnModuleLock);
79 if (gWinWebAuthnModule) {
80 // Another thread successfully loaded the module while we were waiting.
81 return NS_OK;
83 if (gWinWebAuthnModuleUnusable) {
84 // Another thread failed to load the module while we were waiting.
85 return NS_ERROR_NOT_AVAILABLE;
88 gWinWebAuthnModule = LoadLibrarySystem32(L"webauthn.dll");
89 auto markModuleUnusable = MakeScopeExit([]() {
90 if (gWinWebAuthnModule) {
91 FreeLibrary(gWinWebAuthnModule);
92 gWinWebAuthnModule = 0;
94 gWinWebAuthnModuleUnusable = true;
95 });
97 if (!gWinWebAuthnModule) {
98 return NS_ERROR_NOT_AVAILABLE;
101 gWinWebauthnIsUVPAA = reinterpret_cast<
102 decltype(WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)*>(
103 GetProcAddress(gWinWebAuthnModule,
104 "WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable"));
105 gWinWebauthnMakeCredential =
106 reinterpret_cast<decltype(WebAuthNAuthenticatorMakeCredential)*>(
107 GetProcAddress(gWinWebAuthnModule,
108 "WebAuthNAuthenticatorMakeCredential"));
109 gWinWebauthnFreeCredentialAttestation =
110 reinterpret_cast<decltype(WebAuthNFreeCredentialAttestation)*>(
111 GetProcAddress(gWinWebAuthnModule,
112 "WebAuthNFreeCredentialAttestation"));
113 gWinWebauthnGetAssertion =
114 reinterpret_cast<decltype(WebAuthNAuthenticatorGetAssertion)*>(
115 GetProcAddress(gWinWebAuthnModule,
116 "WebAuthNAuthenticatorGetAssertion"));
117 gWinWebauthnFreeAssertion =
118 reinterpret_cast<decltype(WebAuthNFreeAssertion)*>(
119 GetProcAddress(gWinWebAuthnModule, "WebAuthNFreeAssertion"));
120 gWinWebauthnGetCancellationId =
121 reinterpret_cast<decltype(WebAuthNGetCancellationId)*>(
122 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetCancellationId"));
123 gWinWebauthnCancelCurrentOperation =
124 reinterpret_cast<decltype(WebAuthNCancelCurrentOperation)*>(
125 GetProcAddress(gWinWebAuthnModule, "WebAuthNCancelCurrentOperation"));
126 gWinWebauthnGetErrorName = reinterpret_cast<decltype(WebAuthNGetErrorName)*>(
127 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetErrorName"));
128 gWinWebauthnGetApiVersionNumber =
129 reinterpret_cast<decltype(WebAuthNGetApiVersionNumber)*>(
130 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetApiVersionNumber"));
132 if (!(gWinWebauthnIsUVPAA && gWinWebauthnMakeCredential &&
133 gWinWebauthnFreeCredentialAttestation && gWinWebauthnGetAssertion &&
134 gWinWebauthnFreeAssertion && gWinWebauthnGetCancellationId &&
135 gWinWebauthnCancelCurrentOperation && gWinWebauthnGetErrorName &&
136 gWinWebauthnGetApiVersionNumber)) {
137 return NS_ERROR_NOT_AVAILABLE;
140 DWORD version = gWinWebauthnGetApiVersionNumber();
142 if (version >= WEBAUTHN_API_VERSION_4) {
143 gWinWebauthnGetPlatformCredentialList =
144 reinterpret_cast<decltype(WebAuthNGetPlatformCredentialList)*>(
145 GetProcAddress(gWinWebAuthnModule,
146 "WebAuthNGetPlatformCredentialList"));
147 gWinWebauthnFreePlatformCredentialList =
148 reinterpret_cast<decltype(WebAuthNFreePlatformCredentialList)*>(
149 GetProcAddress(gWinWebAuthnModule,
150 "WebAuthNFreePlatformCredentialList"));
151 if (!(gWinWebauthnGetPlatformCredentialList &&
152 gWinWebauthnFreePlatformCredentialList)) {
153 return NS_ERROR_NOT_AVAILABLE;
157 // Bug 1869584: In some of our tests, a content process can end up here due to
158 // a call to WinWebAuthnService::AreWebAuthNApisAvailable. This causes us to
159 // fail an assertion in Preferences::SetBool, which is parent-process only.
160 if (XRE_IsParentProcess()) {
161 NS_DispatchToMainThread(NS_NewRunnableFunction(__func__, [version]() {
162 Preferences::SetBool("security.webauthn.show_ms_settings_link",
163 version >= WEBAUTHN_API_VERSION_7);
164 }));
167 markModuleUnusable.release();
168 return NS_OK;
171 WinWebAuthnService::~WinWebAuthnService() {
172 StaticAutoWriteLock lock(gWinWebAuthnModuleLock);
173 if (gWinWebAuthnModule) {
174 FreeLibrary(gWinWebAuthnModule);
176 gWinWebAuthnModule = 0;
179 // static
180 bool WinWebAuthnService::AreWebAuthNApisAvailable() {
181 nsresult rv = EnsureWinWebAuthnModuleLoaded();
182 NS_ENSURE_SUCCESS(rv, false);
184 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
185 return gWinWebAuthnModule &&
186 gWinWebauthnGetApiVersionNumber() >= kMinWinWebAuthNApiVersion;
189 NS_IMETHODIMP
190 WinWebAuthnService::GetIsUVPAA(bool* aAvailable) {
191 nsresult rv = EnsureWinWebAuthnModuleLoaded();
192 NS_ENSURE_SUCCESS(rv, rv);
194 if (WinWebAuthnService::AreWebAuthNApisAvailable()) {
195 BOOL isUVPAA = FALSE;
196 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
197 *aAvailable = gWinWebAuthnModule && gWinWebauthnIsUVPAA(&isUVPAA) == S_OK &&
198 isUVPAA == TRUE;
199 } else {
200 *aAvailable = false;
202 return NS_OK;
205 NS_IMETHODIMP
206 WinWebAuthnService::Cancel(uint64_t aTransactionId) {
207 return NS_ERROR_NOT_IMPLEMENTED;
210 NS_IMETHODIMP
211 WinWebAuthnService::Reset() {
212 // Reset will never be the first function to use gWinWebAuthnModule, so
213 // we shouldn't try to initialize it here.
214 auto guard = mTransactionState.Lock();
215 if (guard->isSome()) {
216 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
217 if (gWinWebAuthnModule) {
218 const GUID cancellationId = guard->ref().cancellationId;
219 gWinWebauthnCancelCurrentOperation(&cancellationId);
221 if (guard->ref().pendingSignPromise.isSome()) {
222 // This request was never dispatched to the platform API, so
223 // we need to reject the promise ourselves.
224 guard->ref().pendingSignPromise.ref()->Reject(
225 NS_ERROR_DOM_NOT_ALLOWED_ERR);
227 guard->reset();
230 return NS_OK;
233 NS_IMETHODIMP
234 WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
235 uint64_t aBrowsingContextId,
236 nsIWebAuthnRegisterArgs* aArgs,
237 nsIWebAuthnRegisterPromise* aPromise) {
238 nsresult rv = EnsureWinWebAuthnModuleLoaded();
239 NS_ENSURE_SUCCESS(rv, rv);
241 Reset();
242 auto guard = mTransactionState.Lock();
243 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
244 GUID cancellationId;
245 if (gWinWebauthnGetCancellationId(&cancellationId) != S_OK) {
246 // caller will reject promise
247 return NS_ERROR_DOM_UNKNOWN_ERR;
249 *guard = Some(TransactionState{
250 aTransactionId,
251 aBrowsingContextId,
252 Nothing(),
253 Nothing(),
254 cancellationId,
257 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
258 "WinWebAuthnService::MakeCredential",
259 [self = RefPtr{this}, aArgs = RefPtr{aArgs}, aPromise = RefPtr{aPromise},
260 cancellationId]() mutable {
261 // Take a read lock on gWinWebAuthnModuleLock to prevent the module from
262 // being unloaded while the operation is in progress. This does not
263 // prevent the operation from being cancelled, so it does not block a
264 // clean shutdown.
265 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
266 if (!gWinWebAuthnModule) {
267 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
268 return;
271 BOOL HmacCreateSecret = FALSE;
272 BOOL MinPinLength = FALSE;
274 // RP Information
275 nsString rpId;
276 Unused << aArgs->GetRpId(rpId);
277 WEBAUTHN_RP_ENTITY_INFORMATION rpInfo = {
278 WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, rpId.get(), nullptr,
279 nullptr};
281 // User Information
282 WEBAUTHN_USER_ENTITY_INFORMATION userInfo = {
283 WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION,
285 nullptr,
286 nullptr,
287 nullptr,
288 nullptr};
290 // Client Data
291 nsCString clientDataJSON;
292 Unused << aArgs->GetClientDataJSON(clientDataJSON);
293 WEBAUTHN_CLIENT_DATA WebAuthNClientData = {
294 WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
295 (DWORD)clientDataJSON.Length(), (BYTE*)(clientDataJSON.get()),
296 WEBAUTHN_HASH_ALGORITHM_SHA_256};
298 // User Verification Requirement
299 DWORD winUserVerificationReq =
300 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
302 // Resident Key
303 BOOL winRequireResidentKey = FALSE;
304 BOOL winPreferResidentKey = FALSE;
306 // AttestationConveyance
307 DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
309 nsString rpName;
310 Unused << aArgs->GetRpName(rpName);
311 rpInfo.pwszName = rpName.get();
312 rpInfo.pwszIcon = nullptr;
314 nsTArray<uint8_t> userId;
315 Unused << aArgs->GetUserId(userId);
316 userInfo.cbId = static_cast<DWORD>(userId.Length());
317 userInfo.pbId = const_cast<unsigned char*>(userId.Elements());
319 nsString userName;
320 Unused << aArgs->GetUserName(userName);
321 userInfo.pwszName = userName.get();
323 userInfo.pwszIcon = nullptr;
325 nsString userDisplayName;
326 Unused << aArgs->GetUserDisplayName(userDisplayName);
327 userInfo.pwszDisplayName = userDisplayName.get();
329 // Algorithms
330 nsTArray<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> coseParams;
331 nsTArray<int32_t> coseAlgs;
332 Unused << aArgs->GetCoseAlgs(coseAlgs);
333 for (const int32_t& coseAlg : coseAlgs) {
334 WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = {
335 WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION,
336 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, coseAlg};
337 coseParams.AppendElement(coseAlgorithm);
340 nsString userVerificationReq;
341 Unused << aArgs->GetUserVerification(userVerificationReq);
342 // This mapping needs to be reviewed if values are added to the
343 // UserVerificationRequirement enum.
344 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
345 if (userVerificationReq.EqualsLiteral(
346 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) {
347 winUserVerificationReq =
348 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
349 } else if (userVerificationReq.EqualsLiteral(
350 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) {
351 winUserVerificationReq =
352 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
353 } else if (userVerificationReq.EqualsLiteral(
354 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) {
355 winUserVerificationReq =
356 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
357 } else {
358 winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
361 // Attachment
362 DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
363 nsString authenticatorAttachment;
364 nsresult rv =
365 aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
366 if (rv != NS_ERROR_NOT_AVAILABLE) {
367 if (NS_FAILED(rv)) {
368 aPromise->Reject(rv);
369 return;
371 // This mapping needs to be reviewed if values are added to the
372 // AuthenticatorAttachement enum.
373 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
374 if (authenticatorAttachment.EqualsLiteral(
375 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM)) {
376 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM;
377 } else if (
378 authenticatorAttachment.EqualsLiteral(
379 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM)) {
380 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
381 } else {
382 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
386 nsString residentKey;
387 Unused << aArgs->GetResidentKey(residentKey);
388 // This mapping needs to be reviewed if values are added to the
389 // ResidentKeyRequirement enum.
390 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
391 if (residentKey.EqualsLiteral(
392 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED)) {
393 winRequireResidentKey = TRUE;
394 winPreferResidentKey = TRUE;
395 } else if (residentKey.EqualsLiteral(
396 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED)) {
397 winRequireResidentKey = FALSE;
398 winPreferResidentKey = TRUE;
399 } else if (residentKey.EqualsLiteral(
400 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) {
401 winRequireResidentKey = FALSE;
402 winPreferResidentKey = FALSE;
403 } else {
404 // WebAuthnManager::MakeCredential is supposed to assign one of the
405 // above values, so this shouldn't happen.
406 MOZ_ASSERT_UNREACHABLE();
407 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
408 return;
411 // AttestationConveyance
412 nsString attestation;
413 Unused << aArgs->GetAttestationConveyancePreference(attestation);
414 bool anonymize = false;
415 // This mapping needs to be reviewed if values are added to the
416 // AttestationConveyancePreference enum.
417 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
418 if (attestation.EqualsLiteral(
419 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE)) {
420 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
421 anonymize = true;
422 } else if (
423 attestation.EqualsLiteral(
424 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT)) {
425 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT;
426 } else if (attestation.EqualsLiteral(
427 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT)) {
428 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
429 } else {
430 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
433 bool requestedCredProps;
434 Unused << aArgs->GetCredProps(&requestedCredProps);
436 bool requestedMinPinLength;
437 Unused << aArgs->GetMinPinLength(&requestedMinPinLength);
439 bool requestedHmacCreateSecret;
440 Unused << aArgs->GetHmacCreateSecret(&requestedHmacCreateSecret);
442 // Extensions that might require an entry: hmac-secret, minPinLength.
443 WEBAUTHN_EXTENSION rgExtension[2] = {};
444 DWORD cExtensions = 0;
445 if (requestedHmacCreateSecret) {
446 HmacCreateSecret = TRUE;
447 rgExtension[cExtensions].pwszExtensionIdentifier =
448 WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET;
449 rgExtension[cExtensions].cbExtension = sizeof(BOOL);
450 rgExtension[cExtensions].pvExtension = &HmacCreateSecret;
451 cExtensions++;
453 if (requestedMinPinLength) {
454 MinPinLength = TRUE;
455 rgExtension[cExtensions].pwszExtensionIdentifier =
456 WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH;
457 rgExtension[cExtensions].cbExtension = sizeof(BOOL);
458 rgExtension[cExtensions].pvExtension = &MinPinLength;
459 cExtensions++;
462 WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters = {
463 static_cast<DWORD>(coseParams.Length()), coseParams.Elements()};
465 // Exclude Credentials
466 nsTArray<nsTArray<uint8_t>> excludeList;
467 Unused << aArgs->GetExcludeList(excludeList);
469 nsTArray<uint8_t> excludeListTransports;
470 Unused << aArgs->GetExcludeListTransports(excludeListTransports);
472 if (excludeList.Length() != excludeListTransports.Length()) {
473 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
474 return;
477 nsTArray<WEBAUTHN_CREDENTIAL_EX> excludeCredentials;
478 WEBAUTHN_CREDENTIAL_EX* pExcludeCredentials = nullptr;
479 nsTArray<WEBAUTHN_CREDENTIAL_EX*> excludeCredentialsPtrs;
480 WEBAUTHN_CREDENTIAL_LIST excludeCredentialList = {0};
481 WEBAUTHN_CREDENTIAL_LIST* pExcludeCredentialList = nullptr;
483 for (size_t i = 0; i < excludeList.Length(); i++) {
484 nsTArray<uint8_t>& cred = excludeList[i];
485 uint8_t& transports = excludeListTransports[i];
486 DWORD winTransports = 0;
487 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) {
488 winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB;
490 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) {
491 winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC;
493 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) {
494 winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE;
496 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL) {
497 winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
499 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID) {
500 winTransports |= WEBAUTHN_CTAP_TRANSPORT_HYBRID;
503 WEBAUTHN_CREDENTIAL_EX credential = {
504 WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
505 static_cast<DWORD>(cred.Length()), (PBYTE)(cred.Elements()),
506 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports};
507 excludeCredentials.AppendElement(credential);
510 if (!excludeCredentials.IsEmpty()) {
511 pExcludeCredentials = excludeCredentials.Elements();
512 for (DWORD i = 0; i < excludeCredentials.Length(); i++) {
513 excludeCredentialsPtrs.AppendElement(&pExcludeCredentials[i]);
515 excludeCredentialList.cCredentials = excludeCredentials.Length();
516 excludeCredentialList.ppCredentials =
517 excludeCredentialsPtrs.Elements();
518 pExcludeCredentialList = &excludeCredentialList;
521 uint32_t timeout_u32;
522 Unused << aArgs->GetTimeoutMS(&timeout_u32);
523 DWORD timeout = timeout_u32;
525 // MakeCredentialOptions
526 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS
527 WebAuthNCredentialOptions = {
528 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7,
529 timeout,
530 {0, NULL},
531 {0, NULL},
532 winAttachment,
533 winRequireResidentKey,
534 winUserVerificationReq,
535 winAttestation,
536 0, // Flags
537 &cancellationId, // CancellationId
538 pExcludeCredentialList,
539 WEBAUTHN_ENTERPRISE_ATTESTATION_NONE,
540 WEBAUTHN_LARGE_BLOB_SUPPORT_NONE,
541 winPreferResidentKey, // PreferResidentKey
542 FALSE, // BrowserInPrivateMode
543 FALSE, // EnablePrf
544 NULL, // LinkedDevice
545 0, // size of JsonExt
546 NULL, // JsonExt
549 if (cExtensions != 0) {
550 WebAuthNCredentialOptions.Extensions.cExtensions = cExtensions;
551 WebAuthNCredentialOptions.Extensions.pExtensions = rgExtension;
554 PWEBAUTHN_CREDENTIAL_ATTESTATION pWebAuthNCredentialAttestation =
555 nullptr;
557 // Bug 1518876: Get Window Handle from Content process for Windows
558 // WebAuthN APIs
559 HWND hWnd = GetForegroundWindow();
561 HRESULT hr = gWinWebauthnMakeCredential(
562 hWnd, &rpInfo, &userInfo, &WebAuthNCredentialParameters,
563 &WebAuthNClientData, &WebAuthNCredentialOptions,
564 &pWebAuthNCredentialAttestation);
566 if (hr == S_OK) {
567 RefPtr<WebAuthnRegisterResult> result = new WebAuthnRegisterResult(
568 clientDataJSON, pWebAuthNCredentialAttestation);
570 // WEBAUTHN_CREDENTIAL_ATTESTATION structs of version >= 4 always
571 // include a flag to indicate whether a resident key was created. We
572 // copy that flag to the credProps extension output only if the RP
573 // requested the credProps extension.
574 if (requestedCredProps &&
575 pWebAuthNCredentialAttestation->dwVersion >=
576 WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4) {
577 BOOL rk = pWebAuthNCredentialAttestation->bResidentKey;
578 Unused << result->SetCredPropsRk(rk == TRUE);
580 gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation);
582 if (anonymize) {
583 nsresult rv = result->Anonymize();
584 if (NS_FAILED(rv)) {
585 aPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
586 return;
589 aPromise->Resolve(result);
590 } else {
591 PCWSTR errorName = gWinWebauthnGetErrorName(hr);
592 nsresult aError = NS_ERROR_DOM_ABORT_ERR;
594 if (_wcsicmp(errorName, L"InvalidStateError") == 0) {
595 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
596 } else if (_wcsicmp(errorName, L"ConstraintError") == 0 ||
597 _wcsicmp(errorName, L"UnknownError") == 0) {
598 aError = NS_ERROR_DOM_UNKNOWN_ERR;
599 } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) {
600 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
601 } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) {
602 aError = NS_ERROR_DOM_NOT_ALLOWED_ERR;
605 aPromise->Reject(aError);
607 }));
609 NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK);
610 return NS_OK;
613 NS_IMETHODIMP
614 WinWebAuthnService::GetAssertion(uint64_t aTransactionId,
615 uint64_t aBrowsingContextId,
616 nsIWebAuthnSignArgs* aArgs,
617 nsIWebAuthnSignPromise* aPromise) {
618 nsresult rv = EnsureWinWebAuthnModuleLoaded();
619 NS_ENSURE_SUCCESS(rv, rv);
621 Reset();
623 auto guard = mTransactionState.Lock();
625 GUID cancellationId;
627 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
628 if (gWinWebauthnGetCancellationId(&cancellationId) != S_OK) {
629 // caller will reject promise
630 return NS_ERROR_DOM_UNKNOWN_ERR;
634 *guard = Some(TransactionState{
635 aTransactionId,
636 aBrowsingContextId,
637 Some(RefPtr{aArgs}),
638 Some(RefPtr{aPromise}),
639 cancellationId,
642 bool conditionallyMediated;
643 Unused << aArgs->GetConditionallyMediated(&conditionallyMediated);
644 if (!conditionallyMediated) {
645 DoGetAssertion(Nothing(), guard);
647 return NS_OK;
650 void WinWebAuthnService::DoGetAssertion(
651 Maybe<nsTArray<uint8_t>>&& aSelectedCredentialId,
652 const TransactionStateMutex::AutoLock& aGuard) {
653 if (aGuard->isNothing() || aGuard->ref().pendingSignArgs.isNothing() ||
654 aGuard->ref().pendingSignPromise.isNothing()) {
655 return;
658 // Take the pending Args and Promise to prevent repeated calls to
659 // DoGetAssertion for this transaction.
660 RefPtr<nsIWebAuthnSignArgs> aArgs = aGuard->ref().pendingSignArgs.extract();
661 RefPtr<nsIWebAuthnSignPromise> aPromise =
662 aGuard->ref().pendingSignPromise.extract();
664 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
665 "WinWebAuthnService::MakeCredential",
666 [self = RefPtr{this}, aArgs, aPromise,
667 aSelectedCredentialId = std::move(aSelectedCredentialId),
668 aCancellationId = aGuard->ref().cancellationId]() mutable {
669 // Take a read lock on gWinWebAuthnModuleLock to prevent the module from
670 // being unloaded while the operation is in progress. This does not
671 // prevent the operation from being cancelled, so it does not block a
672 // clean shutdown.
673 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
674 if (!gWinWebAuthnModule) {
675 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
676 return;
679 // Attachment
680 DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
682 // AppId
683 BOOL bAppIdUsed = FALSE;
684 BOOL* pbAppIdUsed = nullptr;
685 PCWSTR winAppIdentifier = nullptr;
687 // Client Data
688 nsCString clientDataJSON;
689 Unused << aArgs->GetClientDataJSON(clientDataJSON);
690 WEBAUTHN_CLIENT_DATA WebAuthNClientData = {
691 WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
692 (DWORD)clientDataJSON.Length(), (BYTE*)(clientDataJSON.get()),
693 WEBAUTHN_HASH_ALGORITHM_SHA_256};
695 nsString appId;
696 nsresult rv = aArgs->GetAppId(appId);
697 if (rv != NS_ERROR_NOT_AVAILABLE) {
698 if (NS_FAILED(rv)) {
699 aPromise->Reject(rv);
700 return;
702 winAppIdentifier = appId.get();
703 pbAppIdUsed = &bAppIdUsed;
706 // RPID
707 nsString rpId;
708 Unused << aArgs->GetRpId(rpId);
710 // User Verification Requirement
711 nsString userVerificationReq;
712 Unused << aArgs->GetUserVerification(userVerificationReq);
713 DWORD winUserVerificationReq;
714 // This mapping needs to be reviewed if values are added to the
715 // UserVerificationRequirement enum.
716 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
717 if (userVerificationReq.EqualsLiteral(
718 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) {
719 winUserVerificationReq =
720 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
721 } else if (userVerificationReq.EqualsLiteral(
722 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) {
723 winUserVerificationReq =
724 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
725 } else if (userVerificationReq.EqualsLiteral(
726 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) {
727 winUserVerificationReq =
728 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
729 } else {
730 winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
733 // allow Credentials
734 nsTArray<nsTArray<uint8_t>> allowList;
735 nsTArray<uint8_t> allowListTransports;
736 if (aSelectedCredentialId.isSome()) {
737 allowList.AppendElement(aSelectedCredentialId.extract());
738 allowListTransports.AppendElement(
739 MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL);
740 } else {
741 Unused << aArgs->GetAllowList(allowList);
742 Unused << aArgs->GetAllowListTransports(allowListTransports);
745 if (allowList.Length() != allowListTransports.Length()) {
746 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
747 return;
750 nsTArray<WEBAUTHN_CREDENTIAL_EX> allowCredentials;
751 WEBAUTHN_CREDENTIAL_EX* pAllowCredentials = nullptr;
752 nsTArray<WEBAUTHN_CREDENTIAL_EX*> allowCredentialsPtrs;
753 WEBAUTHN_CREDENTIAL_LIST allowCredentialList = {0};
754 WEBAUTHN_CREDENTIAL_LIST* pAllowCredentialList = nullptr;
756 for (size_t i = 0; i < allowList.Length(); i++) {
757 nsTArray<uint8_t>& cred = allowList[i];
758 uint8_t& transports = allowListTransports[i];
759 DWORD winTransports = 0;
760 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) {
761 winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB;
763 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) {
764 winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC;
766 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) {
767 winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE;
769 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL) {
770 winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
772 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID) {
773 winTransports |= WEBAUTHN_CTAP_TRANSPORT_HYBRID;
776 WEBAUTHN_CREDENTIAL_EX credential = {
777 WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
778 static_cast<DWORD>(cred.Length()), (PBYTE)(cred.Elements()),
779 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports};
780 allowCredentials.AppendElement(credential);
783 if (allowCredentials.Length()) {
784 pAllowCredentials = allowCredentials.Elements();
785 for (DWORD i = 0; i < allowCredentials.Length(); i++) {
786 allowCredentialsPtrs.AppendElement(&pAllowCredentials[i]);
788 allowCredentialList.cCredentials = allowCredentials.Length();
789 allowCredentialList.ppCredentials = allowCredentialsPtrs.Elements();
790 pAllowCredentialList = &allowCredentialList;
793 uint32_t timeout_u32;
794 Unused << aArgs->GetTimeoutMS(&timeout_u32);
795 DWORD timeout = timeout_u32;
797 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS WebAuthNAssertionOptions =
799 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_7,
800 timeout,
801 {0, NULL},
802 {0, NULL},
803 winAttachment,
804 winUserVerificationReq,
805 0, // dwFlags
806 winAppIdentifier,
807 pbAppIdUsed,
808 &aCancellationId, // CancellationId
809 pAllowCredentialList,
810 WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE,
811 0, // Size of CredLargeBlob
812 NULL, // CredLargeBlob
813 NULL, // HmacSecretSaltValues
814 FALSE, // BrowserInPrivateMode
815 NULL, // LinkedDevice
816 FALSE, // AutoFill
817 0, // Size of JsonExt
818 NULL, // JsonExt
821 PWEBAUTHN_ASSERTION pWebAuthNAssertion = nullptr;
823 // Bug 1518876: Get Window Handle from Content process for Windows
824 // WebAuthN APIs
825 HWND hWnd = GetForegroundWindow();
827 HRESULT hr = gWinWebauthnGetAssertion(
828 hWnd, rpId.get(), &WebAuthNClientData, &WebAuthNAssertionOptions,
829 &pWebAuthNAssertion);
831 if (hr == S_OK) {
832 RefPtr<WebAuthnSignResult> result =
833 new WebAuthnSignResult(clientDataJSON, pWebAuthNAssertion);
834 gWinWebauthnFreeAssertion(pWebAuthNAssertion);
835 if (winAppIdentifier != nullptr) {
836 // The gWinWebauthnGetAssertion call modified bAppIdUsed through
837 // a pointer provided in WebAuthNAssertionOptions.
838 Unused << result->SetUsedAppId(bAppIdUsed == TRUE);
840 aPromise->Resolve(result);
841 } else {
842 PCWSTR errorName = gWinWebauthnGetErrorName(hr);
843 nsresult aError = NS_ERROR_DOM_ABORT_ERR;
845 if (_wcsicmp(errorName, L"InvalidStateError") == 0) {
846 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
847 } else if (_wcsicmp(errorName, L"ConstraintError") == 0 ||
848 _wcsicmp(errorName, L"UnknownError") == 0) {
849 aError = NS_ERROR_DOM_UNKNOWN_ERR;
850 } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) {
851 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
852 } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) {
853 aError = NS_ERROR_DOM_NOT_ALLOWED_ERR;
856 aPromise->Reject(aError);
858 }));
860 NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK);
863 NS_IMETHODIMP
864 WinWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
865 const nsAString& aOrigin,
866 uint64_t* aRv) {
867 auto guard = mTransactionState.Lock();
868 if (guard->isNothing() ||
869 guard->ref().browsingContextId != aBrowsingContextId ||
870 guard->ref().pendingSignArgs.isNothing()) {
871 *aRv = 0;
872 return NS_OK;
875 nsString origin;
876 Unused << guard->ref().pendingSignArgs.ref()->GetOrigin(origin);
877 if (origin != aOrigin) {
878 *aRv = 0;
879 return NS_OK;
882 *aRv = guard->ref().transactionId;
883 return NS_OK;
886 NS_IMETHODIMP
887 WinWebAuthnService::GetAutoFillEntries(
888 uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) {
889 auto guard = mTransactionState.Lock();
890 if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
891 guard->ref().pendingSignArgs.isNothing()) {
892 return NS_ERROR_NOT_AVAILABLE;
895 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
896 if (!gWinWebAuthnModule) {
897 return NS_ERROR_NOT_AVAILABLE;
900 aRv.Clear();
902 if (gWinWebauthnGetApiVersionNumber() < WEBAUTHN_API_VERSION_4) {
903 // GetPlatformCredentialList was added in version 4. Earlier versions
904 // can still present a generic "Use a Passkey" autofill entry, so
905 // this isn't an error.
906 return NS_OK;
909 nsString rpId;
910 Unused << guard->ref().pendingSignArgs.ref()->GetRpId(rpId);
912 WEBAUTHN_GET_CREDENTIALS_OPTIONS getCredentialsOptions{
913 WEBAUTHN_GET_CREDENTIALS_OPTIONS_VERSION_1,
914 rpId.get(), // pwszRpId
915 FALSE, // bBrowserInPrivateMode
917 PWEBAUTHN_CREDENTIAL_DETAILS_LIST pCredentialList = nullptr;
918 HRESULT hr = gWinWebauthnGetPlatformCredentialList(&getCredentialsOptions,
919 &pCredentialList);
920 // WebAuthNGetPlatformCredentialList has an _Outptr_result_maybenull_
921 // annotation and a comment "Returns NTE_NOT_FOUND when credentials are
922 // not found."
923 if (pCredentialList == nullptr) {
924 if (hr != NTE_NOT_FOUND) {
925 return NS_ERROR_FAILURE;
927 return NS_OK;
929 MOZ_ASSERT(hr == S_OK);
930 for (size_t i = 0; i < pCredentialList->cCredentialDetails; i++) {
931 RefPtr<nsIWebAuthnAutoFillEntry> entry(
932 new WebAuthnAutoFillEntry(pCredentialList->ppCredentialDetails[i]));
933 aRv.AppendElement(entry);
935 gWinWebauthnFreePlatformCredentialList(pCredentialList);
936 return NS_OK;
939 NS_IMETHODIMP
940 WinWebAuthnService::SelectAutoFillEntry(
941 uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) {
942 auto guard = mTransactionState.Lock();
943 if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
944 guard->ref().pendingSignArgs.isNothing()) {
945 return NS_ERROR_NOT_AVAILABLE;
948 nsTArray<nsTArray<uint8_t>> allowList;
949 Unused << guard->ref().pendingSignArgs.ref()->GetAllowList(allowList);
950 if (!allowList.IsEmpty() && !allowList.Contains(aCredentialId)) {
951 return NS_ERROR_FAILURE;
954 Maybe<nsTArray<uint8_t>> id;
955 id.emplace();
956 id.ref().Assign(aCredentialId);
957 DoGetAssertion(std::move(id), guard);
959 return NS_OK;
962 NS_IMETHODIMP
963 WinWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
964 auto guard = mTransactionState.Lock();
965 if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
966 guard->ref().pendingSignArgs.isNothing()) {
967 return NS_ERROR_NOT_AVAILABLE;
969 DoGetAssertion(Nothing(), guard);
970 return NS_OK;
973 NS_IMETHODIMP
974 WinWebAuthnService::PinCallback(uint64_t aTransactionId,
975 const nsACString& aPin) {
976 return NS_ERROR_NOT_IMPLEMENTED;
979 NS_IMETHODIMP
980 WinWebAuthnService::ResumeMakeCredential(uint64_t aTransactionId,
981 bool aForceNoneAttestation) {
982 return NS_ERROR_NOT_IMPLEMENTED;
985 NS_IMETHODIMP
986 WinWebAuthnService::SelectionCallback(uint64_t aTransactionId,
987 uint64_t aIndex) {
988 return NS_ERROR_NOT_IMPLEMENTED;
991 NS_IMETHODIMP
992 WinWebAuthnService::AddVirtualAuthenticator(
993 const nsACString& protocol, const nsACString& transport,
994 bool hasResidentKey, bool hasUserVerification, bool isUserConsenting,
995 bool isUserVerified, uint64_t* _retval) {
996 return NS_ERROR_NOT_IMPLEMENTED;
999 NS_IMETHODIMP
1000 WinWebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId) {
1001 return NS_ERROR_NOT_IMPLEMENTED;
1004 NS_IMETHODIMP
1005 WinWebAuthnService::AddCredential(uint64_t authenticatorId,
1006 const nsACString& credentialId,
1007 bool isResidentCredential,
1008 const nsACString& rpId,
1009 const nsACString& privateKey,
1010 const nsACString& userHandle,
1011 uint32_t signCount) {
1012 return NS_ERROR_NOT_IMPLEMENTED;
1015 NS_IMETHODIMP
1016 WinWebAuthnService::GetCredentials(
1017 uint64_t authenticatorId,
1018 nsTArray<RefPtr<nsICredentialParameters>>& _retval) {
1019 return NS_ERROR_NOT_IMPLEMENTED;
1022 NS_IMETHODIMP
1023 WinWebAuthnService::RemoveCredential(uint64_t authenticatorId,
1024 const nsACString& credentialId) {
1025 return NS_ERROR_NOT_IMPLEMENTED;
1028 NS_IMETHODIMP
1029 WinWebAuthnService::RemoveAllCredentials(uint64_t authenticatorId) {
1030 return NS_ERROR_NOT_IMPLEMENTED;
1033 NS_IMETHODIMP
1034 WinWebAuthnService::SetUserVerified(uint64_t authenticatorId,
1035 bool isUserVerified) {
1036 return NS_ERROR_NOT_IMPLEMENTED;
1039 NS_IMETHODIMP
1040 WinWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED; }
1042 NS_IMETHODIMP
1043 WinWebAuthnService::RunCommand(const nsACString& cmd) {
1044 return NS_ERROR_NOT_IMPLEMENTED;
1047 } // namespace mozilla::dom