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
{
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
=
41 static decltype(WebAuthNFreeAssertion
)* gWinWebauthnFreeAssertion
= nullptr;
42 static decltype(WebAuthNGetCancellationId
)* gWinWebauthnGetCancellationId
=
44 static decltype(WebAuthNCancelCurrentOperation
)*
45 gWinWebauthnCancelCurrentOperation
= nullptr;
46 static decltype(WebAuthNGetErrorName
)* gWinWebauthnGetErrorName
= nullptr;
47 static decltype(WebAuthNGetApiVersionNumber
)* gWinWebauthnGetApiVersionNumber
=
49 static decltype(WebAuthNGetPlatformCredentialList
)*
50 gWinWebauthnGetPlatformCredentialList
= nullptr;
51 static decltype(WebAuthNFreePlatformCredentialList
)*
52 gWinWebauthnFreePlatformCredentialList
= nullptr;
56 /***********************************************************************
57 * WinWebAuthnService Implementation
58 **********************************************************************/
60 constexpr uint32_t kMinWinWebAuthNApiVersion
= WEBAUTHN_API_VERSION_1
;
62 NS_IMPL_ISUPPORTS(WinWebAuthnService
, nsIWebAuthnService
)
65 nsresult
WinWebAuthnService::EnsureWinWebAuthnModuleLoaded() {
67 StaticAutoReadLock
moduleLock(gWinWebAuthnModuleLock
);
68 if (gWinWebAuthnModule
) {
69 // The module is already loaded.
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.
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;
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
);
167 markModuleUnusable
.release();
171 WinWebAuthnService::~WinWebAuthnService() {
172 StaticAutoWriteLock
lock(gWinWebAuthnModuleLock
);
173 if (gWinWebAuthnModule
) {
174 FreeLibrary(gWinWebAuthnModule
);
176 gWinWebAuthnModule
= 0;
180 bool WinWebAuthnService::AreWebAuthNApisAvailable() {
181 nsresult rv
= EnsureWinWebAuthnModuleLoaded();
182 NS_ENSURE_SUCCESS(rv
, false);
184 StaticAutoReadLock
moduleLock(gWinWebAuthnModuleLock
);
185 return gWinWebAuthnModule
&&
186 gWinWebauthnGetApiVersionNumber() >= kMinWinWebAuthNApiVersion
;
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
&&
206 WinWebAuthnService::Cancel(uint64_t aTransactionId
) {
207 return NS_ERROR_NOT_IMPLEMENTED
;
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
);
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
);
242 auto guard
= mTransactionState
.Lock();
243 StaticAutoReadLock
moduleLock(gWinWebAuthnModuleLock
);
245 if (gWinWebauthnGetCancellationId(&cancellationId
) != S_OK
) {
246 // caller will reject promise
247 return NS_ERROR_DOM_UNKNOWN_ERR
;
249 *guard
= Some(TransactionState
{
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
265 StaticAutoReadLock
moduleLock(gWinWebAuthnModuleLock
);
266 if (!gWinWebAuthnModule
) {
267 aPromise
->Reject(NS_ERROR_DOM_UNKNOWN_ERR
);
271 BOOL HmacCreateSecret
= FALSE
;
272 BOOL MinPinLength
= FALSE
;
276 Unused
<< aArgs
->GetRpId(rpId
);
277 WEBAUTHN_RP_ENTITY_INFORMATION rpInfo
= {
278 WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION
, rpId
.get(), nullptr,
282 WEBAUTHN_USER_ENTITY_INFORMATION userInfo
= {
283 WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION
,
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
;
303 BOOL winRequireResidentKey
= FALSE
;
304 BOOL winPreferResidentKey
= FALSE
;
306 // AttestationConveyance
307 DWORD winAttestation
= WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY
;
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());
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();
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
;
358 winUserVerificationReq
= WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY
;
362 DWORD winAttachment
= WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY
;
363 nsString authenticatorAttachment
;
365 aArgs
->GetAuthenticatorAttachment(authenticatorAttachment
);
366 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
368 aPromise
->Reject(rv
);
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
;
378 authenticatorAttachment
.EqualsLiteral(
379 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM
)) {
380 winAttachment
= WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM
;
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
;
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
);
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
;
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
;
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
;
453 if (requestedMinPinLength
) {
455 rgExtension
[cExtensions
].pwszExtensionIdentifier
=
456 WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH
;
457 rgExtension
[cExtensions
].cbExtension
= sizeof(BOOL
);
458 rgExtension
[cExtensions
].pvExtension
= &MinPinLength
;
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
);
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
,
533 winRequireResidentKey
,
534 winUserVerificationReq
,
537 &cancellationId
, // CancellationId
538 pExcludeCredentialList
,
539 WEBAUTHN_ENTERPRISE_ATTESTATION_NONE
,
540 WEBAUTHN_LARGE_BLOB_SUPPORT_NONE
,
541 winPreferResidentKey
, // PreferResidentKey
542 FALSE
, // BrowserInPrivateMode
544 NULL
, // LinkedDevice
545 0, // size of JsonExt
549 if (cExtensions
!= 0) {
550 WebAuthNCredentialOptions
.Extensions
.cExtensions
= cExtensions
;
551 WebAuthNCredentialOptions
.Extensions
.pExtensions
= rgExtension
;
554 PWEBAUTHN_CREDENTIAL_ATTESTATION pWebAuthNCredentialAttestation
=
557 // Bug 1518876: Get Window Handle from Content process for Windows
559 HWND hWnd
= GetForegroundWindow();
561 HRESULT hr
= gWinWebauthnMakeCredential(
562 hWnd
, &rpInfo
, &userInfo
, &WebAuthNCredentialParameters
,
563 &WebAuthNClientData
, &WebAuthNCredentialOptions
,
564 &pWebAuthNCredentialAttestation
);
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
);
583 nsresult rv
= result
->Anonymize();
585 aPromise
->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
589 aPromise
->Resolve(result
);
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
);
609 NS_DispatchBackgroundTask(runnable
, NS_DISPATCH_EVENT_MAY_BLOCK
);
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
);
623 auto guard
= mTransactionState
.Lock();
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
{
638 Some(RefPtr
{aPromise
}),
642 bool conditionallyMediated
;
643 Unused
<< aArgs
->GetConditionallyMediated(&conditionallyMediated
);
644 if (!conditionallyMediated
) {
645 DoGetAssertion(Nothing(), guard
);
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()) {
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
673 StaticAutoReadLock
moduleLock(gWinWebAuthnModuleLock
);
674 if (!gWinWebAuthnModule
) {
675 aPromise
->Reject(NS_ERROR_DOM_UNKNOWN_ERR
);
680 DWORD winAttachment
= WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY
;
683 BOOL bAppIdUsed
= FALSE
;
684 BOOL
* pbAppIdUsed
= nullptr;
685 PCWSTR winAppIdentifier
= nullptr;
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
};
696 nsresult rv
= aArgs
->GetAppId(appId
);
697 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
699 aPromise
->Reject(rv
);
702 winAppIdentifier
= appId
.get();
703 pbAppIdUsed
= &bAppIdUsed
;
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
;
730 winUserVerificationReq
= WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY
;
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
);
741 Unused
<< aArgs
->GetAllowList(allowList
);
742 Unused
<< aArgs
->GetAllowListTransports(allowListTransports
);
745 if (allowList
.Length() != allowListTransports
.Length()) {
746 aPromise
->Reject(NS_ERROR_DOM_UNKNOWN_ERR
);
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
,
804 winUserVerificationReq
,
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
817 0, // Size of JsonExt
821 PWEBAUTHN_ASSERTION pWebAuthNAssertion
= nullptr;
823 // Bug 1518876: Get Window Handle from Content process for Windows
825 HWND hWnd
= GetForegroundWindow();
827 HRESULT hr
= gWinWebauthnGetAssertion(
828 hWnd
, rpId
.get(), &WebAuthNClientData
, &WebAuthNAssertionOptions
,
829 &pWebAuthNAssertion
);
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
);
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
);
860 NS_DispatchBackgroundTask(runnable
, NS_DISPATCH_EVENT_MAY_BLOCK
);
864 WinWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId
,
865 const nsAString
& aOrigin
,
867 auto guard
= mTransactionState
.Lock();
868 if (guard
->isNothing() ||
869 guard
->ref().browsingContextId
!= aBrowsingContextId
||
870 guard
->ref().pendingSignArgs
.isNothing()) {
876 Unused
<< guard
->ref().pendingSignArgs
.ref()->GetOrigin(origin
);
877 if (origin
!= aOrigin
) {
882 *aRv
= guard
->ref().transactionId
;
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
;
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.
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
,
920 // WebAuthNGetPlatformCredentialList has an _Outptr_result_maybenull_
921 // annotation and a comment "Returns NTE_NOT_FOUND when credentials are
923 if (pCredentialList
== nullptr) {
924 if (hr
!= NTE_NOT_FOUND
) {
925 return NS_ERROR_FAILURE
;
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
);
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
;
956 id
.ref().Assign(aCredentialId
);
957 DoGetAssertion(std::move(id
), guard
);
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
);
974 WinWebAuthnService::PinCallback(uint64_t aTransactionId
,
975 const nsACString
& aPin
) {
976 return NS_ERROR_NOT_IMPLEMENTED
;
980 WinWebAuthnService::ResumeMakeCredential(uint64_t aTransactionId
,
981 bool aForceNoneAttestation
) {
982 return NS_ERROR_NOT_IMPLEMENTED
;
986 WinWebAuthnService::SelectionCallback(uint64_t aTransactionId
,
988 return NS_ERROR_NOT_IMPLEMENTED
;
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
;
1000 WinWebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId
) {
1001 return NS_ERROR_NOT_IMPLEMENTED
;
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
;
1016 WinWebAuthnService::GetCredentials(
1017 uint64_t authenticatorId
,
1018 nsTArray
<RefPtr
<nsICredentialParameters
>>& _retval
) {
1019 return NS_ERROR_NOT_IMPLEMENTED
;
1023 WinWebAuthnService::RemoveCredential(uint64_t authenticatorId
,
1024 const nsACString
& credentialId
) {
1025 return NS_ERROR_NOT_IMPLEMENTED
;
1029 WinWebAuthnService::RemoveAllCredentials(uint64_t authenticatorId
) {
1030 return NS_ERROR_NOT_IMPLEMENTED
;
1034 WinWebAuthnService::SetUserVerified(uint64_t authenticatorId
,
1035 bool isUserVerified
) {
1036 return NS_ERROR_NOT_IMPLEMENTED
;
1040 WinWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED
; }
1043 WinWebAuthnService::RunCommand(const nsACString
& cmd
) {
1044 return NS_ERROR_NOT_IMPLEMENTED
;
1047 } // namespace mozilla::dom