Bug 1874684 - Part 6: Limit day length calculations to safe integers. r=mgaudet
[gecko.git] / dom / webauthn / WinWebAuthnService.cpp
bloba9f73a4cf7256fe67df612a378c75ec8a222c827
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 // 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;
420 } else if (
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;
427 } else {
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;
449 cExtensions++;
451 if (requestedMinPinLength) {
452 MinPinLength = TRUE;
453 rgExtension[cExtensions].pwszExtensionIdentifier =
454 WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH;
455 rgExtension[cExtensions].cbExtension = sizeof(BOOL);
456 rgExtension[cExtensions].pvExtension = &MinPinLength;
457 cExtensions++;
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);
472 return;
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,
527 timeout,
528 {0, NULL},
529 {0, NULL},
530 winAttachment,
531 winRequireResidentKey,
532 winUserVerificationReq,
533 winAttestation,
534 0, // Flags
535 &cancellationId, // CancellationId
536 pExcludeCredentialList,
537 WEBAUTHN_ENTERPRISE_ATTESTATION_NONE,
538 WEBAUTHN_LARGE_BLOB_SUPPORT_NONE,
539 winPreferResidentKey, // PreferResidentKey
540 FALSE, // BrowserInPrivateMode
541 FALSE, // EnablePrf
542 NULL, // LinkedDevice
543 0, // size of JsonExt
544 NULL, // JsonExt
547 if (cExtensions != 0) {
548 WebAuthNCredentialOptions.Extensions.cExtensions = cExtensions;
549 WebAuthNCredentialOptions.Extensions.pExtensions = rgExtension;
552 PWEBAUTHN_CREDENTIAL_ATTESTATION pWebAuthNCredentialAttestation =
553 nullptr;
555 // Bug 1518876: Get Window Handle from Content process for Windows
556 // WebAuthN APIs
557 HWND hWnd = GetForegroundWindow();
559 HRESULT hr = gWinWebauthnMakeCredential(
560 hWnd, &rpInfo, &userInfo, &WebAuthNCredentialParameters,
561 &WebAuthNClientData, &WebAuthNCredentialOptions,
562 &pWebAuthNCredentialAttestation);
564 if (hr == S_OK) {
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);
581 } else {
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);
598 }));
600 NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK);
601 return NS_OK;
604 NS_IMETHODIMP
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);
612 Reset();
614 auto guard = mTransactionState.Lock();
616 GUID cancellationId;
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{
626 aTransactionId,
627 aBrowsingContextId,
628 Some(RefPtr{aArgs}),
629 Some(RefPtr{aPromise}),
630 cancellationId,
633 bool conditionallyMediated;
634 Unused << aArgs->GetConditionallyMediated(&conditionallyMediated);
635 if (!conditionallyMediated) {
636 DoGetAssertion(Nothing(), guard);
638 return NS_OK;
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()) {
646 return;
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
663 // clean shutdown.
664 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
665 if (!gWinWebAuthnModule) {
666 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
667 return;
670 // Attachment
671 DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
673 // AppId
674 BOOL bAppIdUsed = FALSE;
675 BOOL* pbAppIdUsed = nullptr;
676 PCWSTR winAppIdentifier = nullptr;
678 // Client Data
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};
686 nsString appId;
687 nsresult rv = aArgs->GetAppId(appId);
688 if (rv != NS_ERROR_NOT_AVAILABLE) {
689 if (NS_FAILED(rv)) {
690 aPromise->Reject(rv);
691 return;
693 winAppIdentifier = appId.get();
694 pbAppIdUsed = &bAppIdUsed;
697 // RPID
698 nsString rpId;
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;
720 } else {
721 winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
724 // allow Credentials
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);
731 } else {
732 Unused << aArgs->GetAllowList(allowList);
733 Unused << aArgs->GetAllowListTransports(allowListTransports);
736 if (allowList.Length() != allowListTransports.Length()) {
737 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
738 return;
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,
791 timeout,
792 {0, NULL},
793 {0, NULL},
794 winAttachment,
795 winUserVerificationReq,
796 0, // dwFlags
797 winAppIdentifier,
798 pbAppIdUsed,
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
807 FALSE, // AutoFill
808 0, // Size of JsonExt
809 NULL, // JsonExt
812 PWEBAUTHN_ASSERTION pWebAuthNAssertion = nullptr;
814 // Bug 1518876: Get Window Handle from Content process for Windows
815 // WebAuthN APIs
816 HWND hWnd = GetForegroundWindow();
818 HRESULT hr = gWinWebauthnGetAssertion(
819 hWnd, rpId.get(), &WebAuthNClientData, &WebAuthNAssertionOptions,
820 &pWebAuthNAssertion);
822 if (hr == S_OK) {
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);
832 } else {
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);
849 }));
851 NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK);
854 NS_IMETHODIMP
855 WinWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
856 const nsAString& aOrigin,
857 uint64_t* aRv) {
858 auto guard = mTransactionState.Lock();
859 if (guard->isNothing() ||
860 guard->ref().browsingContextId != aBrowsingContextId ||
861 guard->ref().pendingSignArgs.isNothing()) {
862 *aRv = 0;
863 return NS_OK;
866 nsString origin;
867 Unused << guard->ref().pendingSignArgs.ref()->GetOrigin(origin);
868 if (origin != aOrigin) {
869 *aRv = 0;
870 return NS_OK;
873 *aRv = guard->ref().transactionId;
874 return NS_OK;
877 NS_IMETHODIMP
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;
891 aRv.Clear();
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.
897 return NS_OK;
900 nsString rpId;
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,
910 &pCredentialList);
911 // WebAuthNGetPlatformCredentialList has an _Outptr_result_maybenull_
912 // annotation and a comment "Returns NTE_NOT_FOUND when credentials are
913 // not found."
914 if (pCredentialList == nullptr) {
915 if (hr != NTE_NOT_FOUND) {
916 return NS_ERROR_FAILURE;
918 return NS_OK;
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);
927 return NS_OK;
930 NS_IMETHODIMP
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;
946 id.emplace();
947 id.ref().Assign(aCredentialId);
948 DoGetAssertion(std::move(id), guard);
950 return NS_OK;
953 NS_IMETHODIMP
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);
961 return NS_OK;
964 NS_IMETHODIMP
965 WinWebAuthnService::PinCallback(uint64_t aTransactionId,
966 const nsACString& aPin) {
967 return NS_ERROR_NOT_IMPLEMENTED;
970 NS_IMETHODIMP
971 WinWebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId,
972 bool aHasConsent) {
973 return NS_ERROR_NOT_IMPLEMENTED;
976 NS_IMETHODIMP
977 WinWebAuthnService::SelectionCallback(uint64_t aTransactionId,
978 uint64_t aIndex) {
979 return NS_ERROR_NOT_IMPLEMENTED;
982 NS_IMETHODIMP
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;
990 NS_IMETHODIMP
991 WinWebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId) {
992 return NS_ERROR_NOT_IMPLEMENTED;
995 NS_IMETHODIMP
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;
1006 NS_IMETHODIMP
1007 WinWebAuthnService::GetCredentials(
1008 uint64_t authenticatorId,
1009 nsTArray<RefPtr<nsICredentialParameters>>& _retval) {
1010 return NS_ERROR_NOT_IMPLEMENTED;
1013 NS_IMETHODIMP
1014 WinWebAuthnService::RemoveCredential(uint64_t authenticatorId,
1015 const nsACString& credentialId) {
1016 return NS_ERROR_NOT_IMPLEMENTED;
1019 NS_IMETHODIMP
1020 WinWebAuthnService::RemoveAllCredentials(uint64_t authenticatorId) {
1021 return NS_ERROR_NOT_IMPLEMENTED;
1024 NS_IMETHODIMP
1025 WinWebAuthnService::SetUserVerified(uint64_t authenticatorId,
1026 bool isUserVerified) {
1027 return NS_ERROR_NOT_IMPLEMENTED;
1030 NS_IMETHODIMP
1031 WinWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED; }
1033 NS_IMETHODIMP
1034 WinWebAuthnService::RunCommand(const nsACString& cmd) {
1035 return NS_ERROR_NOT_IMPLEMENTED;
1038 } // namespace mozilla::dom