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 // This mapping needs to be reviewed if values are added to the
415 // AttestationConveyancePreference enum.
416 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION
== 3);
417 if (attestation
.EqualsLiteral(
418 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE
)) {
419 winAttestation
= WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE
;
421 attestation
.EqualsLiteral(
422 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT
)) {
423 winAttestation
= WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT
;
424 } else if (attestation
.EqualsLiteral(
425 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT
)) {
426 winAttestation
= WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT
;
428 winAttestation
= WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY
;
431 bool requestedCredProps
;
432 Unused
<< aArgs
->GetCredProps(&requestedCredProps
);
434 bool requestedMinPinLength
;
435 Unused
<< aArgs
->GetMinPinLength(&requestedMinPinLength
);
437 bool requestedHmacCreateSecret
;
438 Unused
<< aArgs
->GetHmacCreateSecret(&requestedHmacCreateSecret
);
440 // Extensions that might require an entry: hmac-secret, minPinLength.
441 WEBAUTHN_EXTENSION rgExtension
[2] = {};
442 DWORD cExtensions
= 0;
443 if (requestedHmacCreateSecret
) {
444 HmacCreateSecret
= TRUE
;
445 rgExtension
[cExtensions
].pwszExtensionIdentifier
=
446 WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET
;
447 rgExtension
[cExtensions
].cbExtension
= sizeof(BOOL
);
448 rgExtension
[cExtensions
].pvExtension
= &HmacCreateSecret
;
451 if (requestedMinPinLength
) {
453 rgExtension
[cExtensions
].pwszExtensionIdentifier
=
454 WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH
;
455 rgExtension
[cExtensions
].cbExtension
= sizeof(BOOL
);
456 rgExtension
[cExtensions
].pvExtension
= &MinPinLength
;
460 WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters
= {
461 static_cast<DWORD
>(coseParams
.Length()), coseParams
.Elements()};
463 // Exclude Credentials
464 nsTArray
<nsTArray
<uint8_t>> excludeList
;
465 Unused
<< aArgs
->GetExcludeList(excludeList
);
467 nsTArray
<uint8_t> excludeListTransports
;
468 Unused
<< aArgs
->GetExcludeListTransports(excludeListTransports
);
470 if (excludeList
.Length() != excludeListTransports
.Length()) {
471 aPromise
->Reject(NS_ERROR_DOM_UNKNOWN_ERR
);
475 nsTArray
<WEBAUTHN_CREDENTIAL_EX
> excludeCredentials
;
476 WEBAUTHN_CREDENTIAL_EX
* pExcludeCredentials
= nullptr;
477 nsTArray
<WEBAUTHN_CREDENTIAL_EX
*> excludeCredentialsPtrs
;
478 WEBAUTHN_CREDENTIAL_LIST excludeCredentialList
= {0};
479 WEBAUTHN_CREDENTIAL_LIST
* pExcludeCredentialList
= nullptr;
481 for (size_t i
= 0; i
< excludeList
.Length(); i
++) {
482 nsTArray
<uint8_t>& cred
= excludeList
[i
];
483 uint8_t& transports
= excludeListTransports
[i
];
484 DWORD winTransports
= 0;
485 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB
) {
486 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_USB
;
488 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC
) {
489 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_NFC
;
491 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE
) {
492 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_BLE
;
494 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL
) {
495 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_INTERNAL
;
497 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID
) {
498 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_HYBRID
;
501 WEBAUTHN_CREDENTIAL_EX credential
= {
502 WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION
,
503 static_cast<DWORD
>(cred
.Length()), (PBYTE
)(cred
.Elements()),
504 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY
, winTransports
};
505 excludeCredentials
.AppendElement(credential
);
508 if (!excludeCredentials
.IsEmpty()) {
509 pExcludeCredentials
= excludeCredentials
.Elements();
510 for (DWORD i
= 0; i
< excludeCredentials
.Length(); i
++) {
511 excludeCredentialsPtrs
.AppendElement(&pExcludeCredentials
[i
]);
513 excludeCredentialList
.cCredentials
= excludeCredentials
.Length();
514 excludeCredentialList
.ppCredentials
=
515 excludeCredentialsPtrs
.Elements();
516 pExcludeCredentialList
= &excludeCredentialList
;
519 uint32_t timeout_u32
;
520 Unused
<< aArgs
->GetTimeoutMS(&timeout_u32
);
521 DWORD timeout
= timeout_u32
;
523 // MakeCredentialOptions
524 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS
525 WebAuthNCredentialOptions
= {
526 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7
,
531 winRequireResidentKey
,
532 winUserVerificationReq
,
535 &cancellationId
, // CancellationId
536 pExcludeCredentialList
,
537 WEBAUTHN_ENTERPRISE_ATTESTATION_NONE
,
538 WEBAUTHN_LARGE_BLOB_SUPPORT_NONE
,
539 winPreferResidentKey
, // PreferResidentKey
540 FALSE
, // BrowserInPrivateMode
542 NULL
, // LinkedDevice
543 0, // size of JsonExt
547 if (cExtensions
!= 0) {
548 WebAuthNCredentialOptions
.Extensions
.cExtensions
= cExtensions
;
549 WebAuthNCredentialOptions
.Extensions
.pExtensions
= rgExtension
;
552 PWEBAUTHN_CREDENTIAL_ATTESTATION pWebAuthNCredentialAttestation
=
555 // Bug 1518876: Get Window Handle from Content process for Windows
557 HWND hWnd
= GetForegroundWindow();
559 HRESULT hr
= gWinWebauthnMakeCredential(
560 hWnd
, &rpInfo
, &userInfo
, &WebAuthNCredentialParameters
,
561 &WebAuthNClientData
, &WebAuthNCredentialOptions
,
562 &pWebAuthNCredentialAttestation
);
565 RefPtr
<WebAuthnRegisterResult
> result
= new WebAuthnRegisterResult(
566 clientDataJSON
, pWebAuthNCredentialAttestation
);
568 // WEBAUTHN_CREDENTIAL_ATTESTATION structs of version >= 4 always
569 // include a flag to indicate whether a resident key was created. We
570 // copy that flag to the credProps extension output only if the RP
571 // requested the credProps extension.
572 if (requestedCredProps
&&
573 pWebAuthNCredentialAttestation
->dwVersion
>=
574 WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4
) {
575 BOOL rk
= pWebAuthNCredentialAttestation
->bResidentKey
;
576 Unused
<< result
->SetCredPropsRk(rk
== TRUE
);
578 gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation
);
580 aPromise
->Resolve(result
);
582 PCWSTR errorName
= gWinWebauthnGetErrorName(hr
);
583 nsresult aError
= NS_ERROR_DOM_ABORT_ERR
;
585 if (_wcsicmp(errorName
, L
"InvalidStateError") == 0) {
586 aError
= NS_ERROR_DOM_INVALID_STATE_ERR
;
587 } else if (_wcsicmp(errorName
, L
"ConstraintError") == 0 ||
588 _wcsicmp(errorName
, L
"UnknownError") == 0) {
589 aError
= NS_ERROR_DOM_UNKNOWN_ERR
;
590 } else if (_wcsicmp(errorName
, L
"NotSupportedError") == 0) {
591 aError
= NS_ERROR_DOM_INVALID_STATE_ERR
;
592 } else if (_wcsicmp(errorName
, L
"NotAllowedError") == 0) {
593 aError
= NS_ERROR_DOM_NOT_ALLOWED_ERR
;
596 aPromise
->Reject(aError
);
600 NS_DispatchBackgroundTask(runnable
, NS_DISPATCH_EVENT_MAY_BLOCK
);
605 WinWebAuthnService::GetAssertion(uint64_t aTransactionId
,
606 uint64_t aBrowsingContextId
,
607 nsIWebAuthnSignArgs
* aArgs
,
608 nsIWebAuthnSignPromise
* aPromise
) {
609 nsresult rv
= EnsureWinWebAuthnModuleLoaded();
610 NS_ENSURE_SUCCESS(rv
, rv
);
614 auto guard
= mTransactionState
.Lock();
618 StaticAutoReadLock
moduleLock(gWinWebAuthnModuleLock
);
619 if (gWinWebauthnGetCancellationId(&cancellationId
) != S_OK
) {
620 // caller will reject promise
621 return NS_ERROR_DOM_UNKNOWN_ERR
;
625 *guard
= Some(TransactionState
{
629 Some(RefPtr
{aPromise
}),
633 bool conditionallyMediated
;
634 Unused
<< aArgs
->GetConditionallyMediated(&conditionallyMediated
);
635 if (!conditionallyMediated
) {
636 DoGetAssertion(Nothing(), guard
);
641 void WinWebAuthnService::DoGetAssertion(
642 Maybe
<nsTArray
<uint8_t>>&& aSelectedCredentialId
,
643 const TransactionStateMutex::AutoLock
& aGuard
) {
644 if (aGuard
->isNothing() || aGuard
->ref().pendingSignArgs
.isNothing() ||
645 aGuard
->ref().pendingSignPromise
.isNothing()) {
649 // Take the pending Args and Promise to prevent repeated calls to
650 // DoGetAssertion for this transaction.
651 RefPtr
<nsIWebAuthnSignArgs
> aArgs
= aGuard
->ref().pendingSignArgs
.extract();
652 RefPtr
<nsIWebAuthnSignPromise
> aPromise
=
653 aGuard
->ref().pendingSignPromise
.extract();
655 nsCOMPtr
<nsIRunnable
> runnable(NS_NewRunnableFunction(
656 "WinWebAuthnService::MakeCredential",
657 [self
= RefPtr
{this}, aArgs
, aPromise
,
658 aSelectedCredentialId
= std::move(aSelectedCredentialId
),
659 aCancellationId
= aGuard
->ref().cancellationId
]() mutable {
660 // Take a read lock on gWinWebAuthnModuleLock to prevent the module from
661 // being unloaded while the operation is in progress. This does not
662 // prevent the operation from being cancelled, so it does not block a
664 StaticAutoReadLock
moduleLock(gWinWebAuthnModuleLock
);
665 if (!gWinWebAuthnModule
) {
666 aPromise
->Reject(NS_ERROR_DOM_UNKNOWN_ERR
);
671 DWORD winAttachment
= WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY
;
674 BOOL bAppIdUsed
= FALSE
;
675 BOOL
* pbAppIdUsed
= nullptr;
676 PCWSTR winAppIdentifier
= nullptr;
679 nsCString clientDataJSON
;
680 Unused
<< aArgs
->GetClientDataJSON(clientDataJSON
);
681 WEBAUTHN_CLIENT_DATA WebAuthNClientData
= {
682 WEBAUTHN_CLIENT_DATA_CURRENT_VERSION
,
683 (DWORD
)clientDataJSON
.Length(), (BYTE
*)(clientDataJSON
.get()),
684 WEBAUTHN_HASH_ALGORITHM_SHA_256
};
687 nsresult rv
= aArgs
->GetAppId(appId
);
688 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
690 aPromise
->Reject(rv
);
693 winAppIdentifier
= appId
.get();
694 pbAppIdUsed
= &bAppIdUsed
;
699 Unused
<< aArgs
->GetRpId(rpId
);
701 // User Verification Requirement
702 nsString userVerificationReq
;
703 Unused
<< aArgs
->GetUserVerification(userVerificationReq
);
704 DWORD winUserVerificationReq
;
705 // This mapping needs to be reviewed if values are added to the
706 // UserVerificationRequirement enum.
707 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION
== 3);
708 if (userVerificationReq
.EqualsLiteral(
709 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED
)) {
710 winUserVerificationReq
=
711 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED
;
712 } else if (userVerificationReq
.EqualsLiteral(
713 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED
)) {
714 winUserVerificationReq
=
715 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED
;
716 } else if (userVerificationReq
.EqualsLiteral(
717 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED
)) {
718 winUserVerificationReq
=
719 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED
;
721 winUserVerificationReq
= WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY
;
725 nsTArray
<nsTArray
<uint8_t>> allowList
;
726 nsTArray
<uint8_t> allowListTransports
;
727 if (aSelectedCredentialId
.isSome()) {
728 allowList
.AppendElement(aSelectedCredentialId
.extract());
729 allowListTransports
.AppendElement(
730 MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL
);
732 Unused
<< aArgs
->GetAllowList(allowList
);
733 Unused
<< aArgs
->GetAllowListTransports(allowListTransports
);
736 if (allowList
.Length() != allowListTransports
.Length()) {
737 aPromise
->Reject(NS_ERROR_DOM_UNKNOWN_ERR
);
741 nsTArray
<WEBAUTHN_CREDENTIAL_EX
> allowCredentials
;
742 WEBAUTHN_CREDENTIAL_EX
* pAllowCredentials
= nullptr;
743 nsTArray
<WEBAUTHN_CREDENTIAL_EX
*> allowCredentialsPtrs
;
744 WEBAUTHN_CREDENTIAL_LIST allowCredentialList
= {0};
745 WEBAUTHN_CREDENTIAL_LIST
* pAllowCredentialList
= nullptr;
747 for (size_t i
= 0; i
< allowList
.Length(); i
++) {
748 nsTArray
<uint8_t>& cred
= allowList
[i
];
749 uint8_t& transports
= allowListTransports
[i
];
750 DWORD winTransports
= 0;
751 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB
) {
752 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_USB
;
754 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC
) {
755 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_NFC
;
757 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE
) {
758 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_BLE
;
760 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL
) {
761 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_INTERNAL
;
763 if (transports
& MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID
) {
764 winTransports
|= WEBAUTHN_CTAP_TRANSPORT_HYBRID
;
767 WEBAUTHN_CREDENTIAL_EX credential
= {
768 WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION
,
769 static_cast<DWORD
>(cred
.Length()), (PBYTE
)(cred
.Elements()),
770 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY
, winTransports
};
771 allowCredentials
.AppendElement(credential
);
774 if (allowCredentials
.Length()) {
775 pAllowCredentials
= allowCredentials
.Elements();
776 for (DWORD i
= 0; i
< allowCredentials
.Length(); i
++) {
777 allowCredentialsPtrs
.AppendElement(&pAllowCredentials
[i
]);
779 allowCredentialList
.cCredentials
= allowCredentials
.Length();
780 allowCredentialList
.ppCredentials
= allowCredentialsPtrs
.Elements();
781 pAllowCredentialList
= &allowCredentialList
;
784 uint32_t timeout_u32
;
785 Unused
<< aArgs
->GetTimeoutMS(&timeout_u32
);
786 DWORD timeout
= timeout_u32
;
788 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS WebAuthNAssertionOptions
=
790 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_7
,
795 winUserVerificationReq
,
799 &aCancellationId
, // CancellationId
800 pAllowCredentialList
,
801 WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE
,
802 0, // Size of CredLargeBlob
803 NULL
, // CredLargeBlob
804 NULL
, // HmacSecretSaltValues
805 FALSE
, // BrowserInPrivateMode
806 NULL
, // LinkedDevice
808 0, // Size of JsonExt
812 PWEBAUTHN_ASSERTION pWebAuthNAssertion
= nullptr;
814 // Bug 1518876: Get Window Handle from Content process for Windows
816 HWND hWnd
= GetForegroundWindow();
818 HRESULT hr
= gWinWebauthnGetAssertion(
819 hWnd
, rpId
.get(), &WebAuthNClientData
, &WebAuthNAssertionOptions
,
820 &pWebAuthNAssertion
);
823 RefPtr
<WebAuthnSignResult
> result
=
824 new WebAuthnSignResult(clientDataJSON
, pWebAuthNAssertion
);
825 gWinWebauthnFreeAssertion(pWebAuthNAssertion
);
826 if (winAppIdentifier
!= nullptr) {
827 // The gWinWebauthnGetAssertion call modified bAppIdUsed through
828 // a pointer provided in WebAuthNAssertionOptions.
829 Unused
<< result
->SetUsedAppId(bAppIdUsed
== TRUE
);
831 aPromise
->Resolve(result
);
833 PCWSTR errorName
= gWinWebauthnGetErrorName(hr
);
834 nsresult aError
= NS_ERROR_DOM_ABORT_ERR
;
836 if (_wcsicmp(errorName
, L
"InvalidStateError") == 0) {
837 aError
= NS_ERROR_DOM_INVALID_STATE_ERR
;
838 } else if (_wcsicmp(errorName
, L
"ConstraintError") == 0 ||
839 _wcsicmp(errorName
, L
"UnknownError") == 0) {
840 aError
= NS_ERROR_DOM_UNKNOWN_ERR
;
841 } else if (_wcsicmp(errorName
, L
"NotSupportedError") == 0) {
842 aError
= NS_ERROR_DOM_INVALID_STATE_ERR
;
843 } else if (_wcsicmp(errorName
, L
"NotAllowedError") == 0) {
844 aError
= NS_ERROR_DOM_NOT_ALLOWED_ERR
;
847 aPromise
->Reject(aError
);
851 NS_DispatchBackgroundTask(runnable
, NS_DISPATCH_EVENT_MAY_BLOCK
);
855 WinWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId
,
856 const nsAString
& aOrigin
,
858 auto guard
= mTransactionState
.Lock();
859 if (guard
->isNothing() ||
860 guard
->ref().browsingContextId
!= aBrowsingContextId
||
861 guard
->ref().pendingSignArgs
.isNothing()) {
867 Unused
<< guard
->ref().pendingSignArgs
.ref()->GetOrigin(origin
);
868 if (origin
!= aOrigin
) {
873 *aRv
= guard
->ref().transactionId
;
878 WinWebAuthnService::GetAutoFillEntries(
879 uint64_t aTransactionId
, nsTArray
<RefPtr
<nsIWebAuthnAutoFillEntry
>>& aRv
) {
880 auto guard
= mTransactionState
.Lock();
881 if (guard
->isNothing() || guard
->ref().transactionId
!= aTransactionId
||
882 guard
->ref().pendingSignArgs
.isNothing()) {
883 return NS_ERROR_NOT_AVAILABLE
;
886 StaticAutoReadLock
moduleLock(gWinWebAuthnModuleLock
);
887 if (!gWinWebAuthnModule
) {
888 return NS_ERROR_NOT_AVAILABLE
;
893 if (gWinWebauthnGetApiVersionNumber() < WEBAUTHN_API_VERSION_4
) {
894 // GetPlatformCredentialList was added in version 4. Earlier versions
895 // can still present a generic "Use a Passkey" autofill entry, so
896 // this isn't an error.
901 Unused
<< guard
->ref().pendingSignArgs
.ref()->GetRpId(rpId
);
903 WEBAUTHN_GET_CREDENTIALS_OPTIONS getCredentialsOptions
{
904 WEBAUTHN_GET_CREDENTIALS_OPTIONS_VERSION_1
,
905 rpId
.get(), // pwszRpId
906 FALSE
, // bBrowserInPrivateMode
908 PWEBAUTHN_CREDENTIAL_DETAILS_LIST pCredentialList
= nullptr;
909 HRESULT hr
= gWinWebauthnGetPlatformCredentialList(&getCredentialsOptions
,
911 // WebAuthNGetPlatformCredentialList has an _Outptr_result_maybenull_
912 // annotation and a comment "Returns NTE_NOT_FOUND when credentials are
914 if (pCredentialList
== nullptr) {
915 if (hr
!= NTE_NOT_FOUND
) {
916 return NS_ERROR_FAILURE
;
920 MOZ_ASSERT(hr
== S_OK
);
921 for (size_t i
= 0; i
< pCredentialList
->cCredentialDetails
; i
++) {
922 RefPtr
<nsIWebAuthnAutoFillEntry
> entry(
923 new WebAuthnAutoFillEntry(pCredentialList
->ppCredentialDetails
[i
]));
924 aRv
.AppendElement(entry
);
926 gWinWebauthnFreePlatformCredentialList(pCredentialList
);
931 WinWebAuthnService::SelectAutoFillEntry(
932 uint64_t aTransactionId
, const nsTArray
<uint8_t>& aCredentialId
) {
933 auto guard
= mTransactionState
.Lock();
934 if (guard
->isNothing() || guard
->ref().transactionId
!= aTransactionId
||
935 guard
->ref().pendingSignArgs
.isNothing()) {
936 return NS_ERROR_NOT_AVAILABLE
;
939 nsTArray
<nsTArray
<uint8_t>> allowList
;
940 Unused
<< guard
->ref().pendingSignArgs
.ref()->GetAllowList(allowList
);
941 if (!allowList
.IsEmpty() && !allowList
.Contains(aCredentialId
)) {
942 return NS_ERROR_FAILURE
;
945 Maybe
<nsTArray
<uint8_t>> id
;
947 id
.ref().Assign(aCredentialId
);
948 DoGetAssertion(std::move(id
), guard
);
954 WinWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId
) {
955 auto guard
= mTransactionState
.Lock();
956 if (guard
->isNothing() || guard
->ref().transactionId
!= aTransactionId
||
957 guard
->ref().pendingSignArgs
.isNothing()) {
958 return NS_ERROR_NOT_AVAILABLE
;
960 DoGetAssertion(Nothing(), guard
);
965 WinWebAuthnService::PinCallback(uint64_t aTransactionId
,
966 const nsACString
& aPin
) {
967 return NS_ERROR_NOT_IMPLEMENTED
;
971 WinWebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId
,
973 return NS_ERROR_NOT_IMPLEMENTED
;
977 WinWebAuthnService::SelectionCallback(uint64_t aTransactionId
,
979 return NS_ERROR_NOT_IMPLEMENTED
;
983 WinWebAuthnService::AddVirtualAuthenticator(
984 const nsACString
& protocol
, const nsACString
& transport
,
985 bool hasResidentKey
, bool hasUserVerification
, bool isUserConsenting
,
986 bool isUserVerified
, uint64_t* _retval
) {
987 return NS_ERROR_NOT_IMPLEMENTED
;
991 WinWebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId
) {
992 return NS_ERROR_NOT_IMPLEMENTED
;
996 WinWebAuthnService::AddCredential(uint64_t authenticatorId
,
997 const nsACString
& credentialId
,
998 bool isResidentCredential
,
999 const nsACString
& rpId
,
1000 const nsACString
& privateKey
,
1001 const nsACString
& userHandle
,
1002 uint32_t signCount
) {
1003 return NS_ERROR_NOT_IMPLEMENTED
;
1007 WinWebAuthnService::GetCredentials(
1008 uint64_t authenticatorId
,
1009 nsTArray
<RefPtr
<nsICredentialParameters
>>& _retval
) {
1010 return NS_ERROR_NOT_IMPLEMENTED
;
1014 WinWebAuthnService::RemoveCredential(uint64_t authenticatorId
,
1015 const nsACString
& credentialId
) {
1016 return NS_ERROR_NOT_IMPLEMENTED
;
1020 WinWebAuthnService::RemoveAllCredentials(uint64_t authenticatorId
) {
1021 return NS_ERROR_NOT_IMPLEMENTED
;
1025 WinWebAuthnService::SetUserVerified(uint64_t authenticatorId
,
1026 bool isUserVerified
) {
1027 return NS_ERROR_NOT_IMPLEMENTED
;
1031 WinWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED
; }
1034 WinWebAuthnService::RunCommand(const nsACString
& cmd
) {
1035 return NS_ERROR_NOT_IMPLEMENTED
;
1038 } // namespace mozilla::dom