Bug 1769952 - Fix running raptor on a Win10-64 VM r=sparky
[gecko.git] / dom / webauthn / WinWebAuthnManager.cpp
blobcf5c662b6ed798c20c5a8f0bfa5eaa5d62f37686
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/MozPromise.h"
9 #include "mozilla/ipc/BackgroundParent.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/Unused.h"
12 #include "nsTextFormatter.h"
13 #include "nsWindowsHelpers.h"
14 #include "winwebauthn/webauthn.h"
15 #include "WinWebAuthnManager.h"
17 namespace mozilla::dom {
19 namespace {
20 static mozilla::LazyLogModule gWinWebAuthnManagerLog("winwebauthnkeymanager");
21 StaticAutoPtr<WinWebAuthnManager> gWinWebAuthnManager;
22 static HMODULE gWinWebAuthnModule = 0;
24 static decltype(WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)*
25 gWinWebauthnIsUVPAA = nullptr;
26 static decltype(WebAuthNAuthenticatorMakeCredential)*
27 gWinWebauthnMakeCredential = nullptr;
28 static decltype(WebAuthNFreeCredentialAttestation)*
29 gWinWebauthnFreeCredentialAttestation = nullptr;
30 static decltype(WebAuthNAuthenticatorGetAssertion)* gWinWebauthnGetAssertion =
31 nullptr;
32 static decltype(WebAuthNFreeAssertion)* gWinWebauthnFreeAssertion = nullptr;
33 static decltype(WebAuthNGetCancellationId)* gWinWebauthnGetCancellationId =
34 nullptr;
35 static decltype(WebAuthNCancelCurrentOperation)*
36 gWinWebauthnCancelCurrentOperation = nullptr;
37 static decltype(WebAuthNGetErrorName)* gWinWebauthnGetErrorName = nullptr;
38 static decltype(WebAuthNGetApiVersionNumber)* gWinWebauthnGetApiVersionNumber =
39 nullptr;
41 } // namespace
43 /***********************************************************************
44 * WinWebAuthnManager Implementation
45 **********************************************************************/
47 constexpr uint32_t kMinWinWebAuthNApiVersion = WEBAUTHN_API_VERSION_1;
49 WinWebAuthnManager::WinWebAuthnManager() {
50 // Create on the main thread to make sure ClearOnShutdown() works.
51 MOZ_ASSERT(NS_IsMainThread());
52 MOZ_ASSERT(!gWinWebAuthnModule);
54 gWinWebAuthnModule = LoadLibrarySystem32(L"webauthn.dll");
56 if (gWinWebAuthnModule) {
57 gWinWebauthnIsUVPAA = reinterpret_cast<
58 decltype(WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)*>(
59 GetProcAddress(
60 gWinWebAuthnModule,
61 "WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable"));
62 gWinWebauthnMakeCredential =
63 reinterpret_cast<decltype(WebAuthNAuthenticatorMakeCredential)*>(
64 GetProcAddress(gWinWebAuthnModule,
65 "WebAuthNAuthenticatorMakeCredential"));
66 gWinWebauthnFreeCredentialAttestation =
67 reinterpret_cast<decltype(WebAuthNFreeCredentialAttestation)*>(
68 GetProcAddress(gWinWebAuthnModule,
69 "WebAuthNFreeCredentialAttestation"));
70 gWinWebauthnGetAssertion =
71 reinterpret_cast<decltype(WebAuthNAuthenticatorGetAssertion)*>(
72 GetProcAddress(gWinWebAuthnModule,
73 "WebAuthNAuthenticatorGetAssertion"));
74 gWinWebauthnFreeAssertion =
75 reinterpret_cast<decltype(WebAuthNFreeAssertion)*>(
76 GetProcAddress(gWinWebAuthnModule, "WebAuthNFreeAssertion"));
77 gWinWebauthnGetCancellationId =
78 reinterpret_cast<decltype(WebAuthNGetCancellationId)*>(
79 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetCancellationId"));
80 gWinWebauthnCancelCurrentOperation =
81 reinterpret_cast<decltype(WebAuthNCancelCurrentOperation)*>(
82 GetProcAddress(gWinWebAuthnModule,
83 "WebAuthNCancelCurrentOperation"));
84 gWinWebauthnGetErrorName =
85 reinterpret_cast<decltype(WebAuthNGetErrorName)*>(
86 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetErrorName"));
87 gWinWebauthnGetApiVersionNumber =
88 reinterpret_cast<decltype(WebAuthNGetApiVersionNumber)*>(
89 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetApiVersionNumber"));
91 if (gWinWebauthnIsUVPAA && gWinWebauthnMakeCredential &&
92 gWinWebauthnFreeCredentialAttestation && gWinWebauthnGetAssertion &&
93 gWinWebauthnFreeAssertion && gWinWebauthnGetCancellationId &&
94 gWinWebauthnCancelCurrentOperation && gWinWebauthnGetErrorName &&
95 gWinWebauthnGetApiVersionNumber) {
96 mWinWebAuthNApiVersion = gWinWebauthnGetApiVersionNumber();
101 WinWebAuthnManager::~WinWebAuthnManager() {
102 if (gWinWebAuthnModule) {
103 FreeLibrary(gWinWebAuthnModule);
105 gWinWebAuthnModule = 0;
108 // static
109 void WinWebAuthnManager::Initialize() {
110 if (!gWinWebAuthnManager) {
111 gWinWebAuthnManager = new WinWebAuthnManager();
112 ClearOnShutdown(&gWinWebAuthnManager);
116 // static
117 WinWebAuthnManager* WinWebAuthnManager::Get() {
118 MOZ_ASSERT(gWinWebAuthnManager);
119 return gWinWebAuthnManager;
122 uint32_t WinWebAuthnManager::GetWebAuthNApiVersion() {
123 return mWinWebAuthNApiVersion;
126 // static
127 bool WinWebAuthnManager::AreWebAuthNApisAvailable() {
128 WinWebAuthnManager* mgr = WinWebAuthnManager::Get();
129 return mgr->GetWebAuthNApiVersion() >= kMinWinWebAuthNApiVersion;
132 bool WinWebAuthnManager::
133 IsUserVerifyingPlatformAuthenticatorAvailableInternal() {
134 BOOL isUVPAA = FALSE;
135 return (gWinWebauthnIsUVPAA(&isUVPAA) == S_OK && isUVPAA == TRUE);
138 // static
139 bool WinWebAuthnManager::IsUserVerifyingPlatformAuthenticatorAvailable() {
140 if (WinWebAuthnManager::AreWebAuthNApisAvailable()) {
141 return WinWebAuthnManager::Get()
142 ->IsUserVerifyingPlatformAuthenticatorAvailableInternal();
144 return false;
147 void WinWebAuthnManager::AbortTransaction(const uint64_t& aTransactionId,
148 const nsresult& aError) {
149 Unused << mTransactionParent->SendAbort(aTransactionId, aError);
150 ClearTransaction();
153 void WinWebAuthnManager::MaybeClearTransaction(
154 PWebAuthnTransactionParent* aParent) {
155 // Only clear if we've been requested to do so by our current transaction
156 // parent.
157 if (mTransactionParent == aParent) {
158 ClearTransaction();
162 void WinWebAuthnManager::ClearTransaction() { mTransactionParent = nullptr; }
164 void WinWebAuthnManager::Register(
165 PWebAuthnTransactionParent* aTransactionParent,
166 const uint64_t& aTransactionId, const WebAuthnMakeCredentialInfo& aInfo) {
167 MOZ_LOG(gWinWebAuthnManagerLog, LogLevel::Debug, ("WinWebAuthNRegister"));
169 ClearTransaction();
170 mTransactionParent = aTransactionParent;
172 BYTE U2FUserId = 0x01;
174 WEBAUTHN_EXTENSION rgExtension[1] = {};
175 DWORD cExtensions = 0;
176 BOOL HmacCreateSecret = FALSE;
178 // RP Information
179 WEBAUTHN_RP_ENTITY_INFORMATION rpInfo = {
180 WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, aInfo.RpId().get(),
181 nullptr, nullptr};
183 // User Information
184 WEBAUTHN_USER_ENTITY_INFORMATION userInfo = {
185 WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION,
187 nullptr,
188 nullptr,
189 nullptr,
190 nullptr};
192 // Client Data
193 WEBAUTHN_CLIENT_DATA WebAuthNClientData = {
194 WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
195 (DWORD)aInfo.ClientDataJSON().Length(),
196 (BYTE*)(aInfo.ClientDataJSON().get()), WEBAUTHN_HASH_ALGORITHM_SHA_256};
198 // Algorithms
199 nsTArray<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> coseParams;
201 // User Verification Requirement
202 DWORD winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
204 // Attachment
205 DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
207 // Resident Key
208 BOOL winRequireResidentKey = FALSE;
210 // AttestationConveyance
211 DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
213 if (aInfo.Extra().isSome()) {
214 const auto& extra = aInfo.Extra().ref();
216 rpInfo.pwszName = extra.Rp().Name().get();
217 rpInfo.pwszIcon = extra.Rp().Icon().get();
219 userInfo.cbId = static_cast<DWORD>(extra.User().Id().Length());
220 userInfo.pbId = const_cast<unsigned char*>(extra.User().Id().Elements());
221 userInfo.pwszName = extra.User().Name().get();
222 userInfo.pwszIcon = extra.User().Icon().get();
223 userInfo.pwszDisplayName = extra.User().DisplayName().get();
225 for (const auto& coseAlg : extra.coseAlgs()) {
226 WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = {
227 WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION,
228 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, coseAlg.alg()};
229 coseParams.AppendElement(coseAlgorithm);
232 const auto& sel = extra.AuthenticatorSelection();
234 UserVerificationRequirement userVerificationReq =
235 sel.userVerificationRequirement();
236 switch (userVerificationReq) {
237 case UserVerificationRequirement::Required:
238 winUserVerificationReq =
239 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
240 break;
241 case UserVerificationRequirement::Preferred:
242 winUserVerificationReq =
243 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
244 break;
245 case UserVerificationRequirement::Discouraged:
246 winUserVerificationReq =
247 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
248 break;
249 default:
250 winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
251 break;
254 if (sel.authenticatorAttachment().isSome()) {
255 const AuthenticatorAttachment authenticatorAttachment =
256 sel.authenticatorAttachment().value();
257 switch (authenticatorAttachment) {
258 case AuthenticatorAttachment::Platform:
259 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM;
260 break;
261 case AuthenticatorAttachment::Cross_platform:
262 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
263 break;
264 default:
265 break;
269 winRequireResidentKey = sel.requireResidentKey();
271 // AttestationConveyance
272 AttestationConveyancePreference attestation =
273 extra.attestationConveyancePreference();
274 switch (attestation) {
275 case AttestationConveyancePreference::Direct:
276 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
277 break;
278 case AttestationConveyancePreference::Indirect:
279 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT;
280 break;
281 case AttestationConveyancePreference::None:
282 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
283 break;
284 default:
285 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
286 break;
289 if (extra.Extensions().Length() >
290 (int)(sizeof(rgExtension) / sizeof(rgExtension[0]))) {
291 nsresult aError = NS_ERROR_DOM_INVALID_STATE_ERR;
292 MaybeAbortRegister(aTransactionId, aError);
293 return;
295 for (const WebAuthnExtension& ext : extra.Extensions()) {
296 if (ext.type() == WebAuthnExtension::TWebAuthnExtensionHmacSecret) {
297 HmacCreateSecret =
298 ext.get_WebAuthnExtensionHmacSecret().hmacCreateSecret() == true;
299 if (HmacCreateSecret) {
300 rgExtension[cExtensions].pwszExtensionIdentifier =
301 WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET;
302 rgExtension[cExtensions].cbExtension = sizeof(BOOL);
303 rgExtension[cExtensions].pvExtension = &HmacCreateSecret;
304 cExtensions++;
308 } else {
309 userInfo.cbId = sizeof(BYTE);
310 userInfo.pbId = &U2FUserId;
312 WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = {
313 WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION,
314 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY,
315 WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256};
316 coseParams.AppendElement(coseAlgorithm);
318 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
321 WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters = {
322 static_cast<DWORD>(coseParams.Length()), coseParams.Elements()};
324 // Exclude Credentials
325 nsTArray<WEBAUTHN_CREDENTIAL_EX> excludeCredentials;
326 WEBAUTHN_CREDENTIAL_EX* pExcludeCredentials = nullptr;
327 nsTArray<WEBAUTHN_CREDENTIAL_EX*> excludeCredentialsPtrs;
328 WEBAUTHN_CREDENTIAL_LIST excludeCredentialList = {0};
329 WEBAUTHN_CREDENTIAL_LIST* pExcludeCredentialList = nullptr;
331 for (auto& cred : aInfo.ExcludeList()) {
332 uint8_t transports = cred.transports();
333 DWORD winTransports = 0;
334 if (transports & U2F_AUTHENTICATOR_TRANSPORT_USB) {
335 winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB;
337 if (transports & U2F_AUTHENTICATOR_TRANSPORT_NFC) {
338 winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC;
340 if (transports & U2F_AUTHENTICATOR_TRANSPORT_BLE) {
341 winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE;
343 if (transports & CTAP_AUTHENTICATOR_TRANSPORT_INTERNAL) {
344 winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
347 WEBAUTHN_CREDENTIAL_EX credential = {
348 WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
349 static_cast<DWORD>(cred.id().Length()), (PBYTE)(cred.id().Elements()),
350 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports};
351 excludeCredentials.AppendElement(credential);
354 if (!excludeCredentials.IsEmpty()) {
355 pExcludeCredentials = excludeCredentials.Elements();
356 for (DWORD i = 0; i < excludeCredentials.Length(); i++) {
357 excludeCredentialsPtrs.AppendElement(&pExcludeCredentials[i]);
359 excludeCredentialList.cCredentials = excludeCredentials.Length();
360 excludeCredentialList.ppCredentials = excludeCredentialsPtrs.Elements();
361 pExcludeCredentialList = &excludeCredentialList;
364 // MakeCredentialOptions
365 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS WebAuthNCredentialOptions = {
366 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_4,
367 aInfo.TimeoutMS(),
368 {0, NULL},
369 {0, NULL},
370 winAttachment,
371 winRequireResidentKey,
372 winUserVerificationReq,
373 winAttestation,
374 0, // Flags
375 NULL, // CancellationId
376 pExcludeCredentialList,
377 WEBAUTHN_ENTERPRISE_ATTESTATION_NONE,
378 WEBAUTHN_LARGE_BLOB_SUPPORT_NONE,
379 FALSE, // PreferResidentKey
382 GUID cancellationId = {0};
383 if (gWinWebauthnGetCancellationId(&cancellationId) == S_OK) {
384 WebAuthNCredentialOptions.pCancellationId = &cancellationId;
385 mCancellationIds.emplace(aTransactionId, &cancellationId);
388 if (cExtensions != 0) {
389 WebAuthNCredentialOptions.Extensions.cExtensions = cExtensions;
390 WebAuthNCredentialOptions.Extensions.pExtensions = rgExtension;
393 WEBAUTHN_CREDENTIAL_ATTESTATION* pWebAuthNCredentialAttestation = nullptr;
395 // Bug 1518876: Get Window Handle from Content process for Windows WebAuthN
396 // APIs
397 HWND hWnd = GetForegroundWindow();
399 HRESULT hr = gWinWebauthnMakeCredential(
400 hWnd, &rpInfo, &userInfo, &WebAuthNCredentialParameters,
401 &WebAuthNClientData, &WebAuthNCredentialOptions,
402 &pWebAuthNCredentialAttestation);
404 mCancellationIds.erase(aTransactionId);
406 if (hr == S_OK) {
407 nsTArray<uint8_t> credentialId;
408 credentialId.AppendElements(pWebAuthNCredentialAttestation->pbCredentialId,
409 pWebAuthNCredentialAttestation->cbCredentialId);
411 nsTArray<uint8_t> authenticatorData;
413 if (aInfo.Extra().isSome()) {
414 authenticatorData.AppendElements(
415 pWebAuthNCredentialAttestation->pbAuthenticatorData,
416 pWebAuthNCredentialAttestation->cbAuthenticatorData);
417 } else {
418 PWEBAUTHN_COMMON_ATTESTATION attestation =
419 reinterpret_cast<PWEBAUTHN_COMMON_ATTESTATION>(
420 pWebAuthNCredentialAttestation->pvAttestationDecode);
422 DWORD coseKeyOffset = 32 + // RPIDHash
423 1 + // Flags
424 4 + // Counter
425 16 + // AAGuid
426 2 + // Credential ID Length field
427 pWebAuthNCredentialAttestation->cbCredentialId;
429 // Hardcoding as couldn't finder decoder and it is an ECC key.
430 DWORD xOffset = coseKeyOffset + 10;
431 DWORD yOffset = coseKeyOffset + 45;
433 // Authenticator Data length check.
434 if (pWebAuthNCredentialAttestation->cbAuthenticatorData < yOffset + 32) {
435 MaybeAbortRegister(aTransactionId, NS_ERROR_DOM_INVALID_STATE_ERR);
438 authenticatorData.AppendElement(0x05); // Reserved Byte
439 authenticatorData.AppendElement(0x04); // ECC Uncompressed Key
440 authenticatorData.AppendElements(
441 pWebAuthNCredentialAttestation->pbAuthenticatorData + xOffset,
442 32); // X Coordinate
443 authenticatorData.AppendElements(
444 pWebAuthNCredentialAttestation->pbAuthenticatorData + yOffset,
445 32); // Y Coordinate
446 authenticatorData.AppendElement(
447 pWebAuthNCredentialAttestation->cbCredentialId);
448 authenticatorData.AppendElements(
449 pWebAuthNCredentialAttestation->pbCredentialId,
450 pWebAuthNCredentialAttestation->cbCredentialId);
451 authenticatorData.AppendElements(attestation->pX5c->pbData,
452 attestation->pX5c->cbData);
453 authenticatorData.AppendElements(attestation->pbSignature,
454 attestation->cbSignature);
457 nsTArray<uint8_t> attObject;
458 if (winAttestation == WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE) {
459 // Zero AAGuid
460 const uint8_t zeroGuid[16] = {0};
461 authenticatorData.ReplaceElementsAt(32 + 1 + 4 /*AAGuid offset*/, 16,
462 zeroGuid, 16);
464 CryptoBuffer authData;
465 authData.Assign(authenticatorData);
466 CryptoBuffer noneAttObj;
467 CBOREncodeNoneAttestationObj(authData, noneAttObj);
468 attObject.AppendElements(noneAttObj);
469 } else {
470 attObject.AppendElements(
471 pWebAuthNCredentialAttestation->pbAttestationObject,
472 pWebAuthNCredentialAttestation->cbAttestationObject);
475 nsTArray<WebAuthnExtensionResult> extensions;
477 if (pWebAuthNCredentialAttestation->dwVersion >=
478 WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_2) {
479 PCWEBAUTHN_EXTENSIONS pExtensionList =
480 &pWebAuthNCredentialAttestation->Extensions;
481 if (pExtensionList->cExtensions != 0 &&
482 pExtensionList->pExtensions != NULL) {
483 for (DWORD dwIndex = 0; dwIndex < pExtensionList->cExtensions;
484 dwIndex++) {
485 PWEBAUTHN_EXTENSION pExtension =
486 &pExtensionList->pExtensions[dwIndex];
487 if (pExtension->pwszExtensionIdentifier &&
488 (0 == _wcsicmp(pExtension->pwszExtensionIdentifier,
489 WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET)) &&
490 pExtension->cbExtension == sizeof(BOOL)) {
491 BOOL* pCredentialCreatedWithHmacSecret =
492 (BOOL*)pExtension->pvExtension;
493 if (*pCredentialCreatedWithHmacSecret) {
494 extensions.AppendElement(WebAuthnExtensionResultHmacSecret(true));
501 WebAuthnMakeCredentialResult result(aInfo.ClientDataJSON(), attObject,
502 credentialId, authenticatorData,
503 extensions);
505 Unused << mTransactionParent->SendConfirmRegister(aTransactionId, result);
506 ClearTransaction();
507 gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation);
509 } else {
510 PCWSTR errorName = gWinWebauthnGetErrorName(hr);
511 nsresult aError = NS_ERROR_DOM_ABORT_ERR;
513 if (_wcsicmp(errorName, L"InvalidStateError") == 0) {
514 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
515 } else if (_wcsicmp(errorName, L"ConstraintError") == 0 ||
516 _wcsicmp(errorName, L"UnknownError") == 0) {
517 aError = NS_ERROR_DOM_UNKNOWN_ERR;
518 } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) {
519 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
520 } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) {
521 aError = NS_ERROR_DOM_NOT_ALLOWED_ERR;
524 MaybeAbortRegister(aTransactionId, aError);
528 void WinWebAuthnManager::MaybeAbortRegister(const uint64_t& aTransactionId,
529 const nsresult& aError) {
530 AbortTransaction(aTransactionId, aError);
533 void WinWebAuthnManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
534 const uint64_t& aTransactionId,
535 const WebAuthnGetAssertionInfo& aInfo) {
536 MOZ_LOG(gWinWebAuthnManagerLog, LogLevel::Debug, ("WinWebAuthNSign"));
538 ClearTransaction();
539 mTransactionParent = aTransactionParent;
541 // User Verification Requirement
542 DWORD winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
544 // RPID
545 PCWSTR rpID = nullptr;
547 // Attachment
548 DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
550 // AppId
551 BOOL bU2fAppIdUsed = FALSE;
552 BOOL* pbU2fAppIdUsed = nullptr;
553 PCWSTR winAppIdentifier = nullptr;
555 // Client Data
556 WEBAUTHN_CLIENT_DATA WebAuthNClientData = {
557 WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
558 (DWORD)aInfo.ClientDataJSON().Length(),
559 (BYTE*)(aInfo.ClientDataJSON().get()), WEBAUTHN_HASH_ALGORITHM_SHA_256};
561 if (aInfo.Extra().isSome()) {
562 const auto& extra = aInfo.Extra().ref();
564 for (const WebAuthnExtension& ext : extra.Extensions()) {
565 if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
566 winAppIdentifier =
567 ext.get_WebAuthnExtensionAppId().appIdentifier().get();
568 pbU2fAppIdUsed = &bU2fAppIdUsed;
569 break;
573 // RPID
574 rpID = aInfo.RpId().get();
576 // User Verification Requirement
577 UserVerificationRequirement userVerificationReq =
578 extra.userVerificationRequirement();
580 switch (userVerificationReq) {
581 case UserVerificationRequirement::Required:
582 winUserVerificationReq =
583 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
584 break;
585 case UserVerificationRequirement::Preferred:
586 winUserVerificationReq =
587 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
588 break;
589 case UserVerificationRequirement::Discouraged:
590 winUserVerificationReq =
591 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
592 break;
593 default:
594 winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
595 break;
597 } else {
598 rpID = aInfo.Origin().get();
599 winAppIdentifier = aInfo.RpId().get();
600 pbU2fAppIdUsed = &bU2fAppIdUsed;
601 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
602 winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
605 // allow Credentials
606 nsTArray<WEBAUTHN_CREDENTIAL_EX> allowCredentials;
607 WEBAUTHN_CREDENTIAL_EX* pAllowCredentials = nullptr;
608 nsTArray<WEBAUTHN_CREDENTIAL_EX*> allowCredentialsPtrs;
609 WEBAUTHN_CREDENTIAL_LIST allowCredentialList = {0};
610 WEBAUTHN_CREDENTIAL_LIST* pAllowCredentialList = nullptr;
612 for (auto& cred : aInfo.AllowList()) {
613 uint8_t transports = cred.transports();
614 DWORD winTransports = 0;
615 if (transports & U2F_AUTHENTICATOR_TRANSPORT_USB) {
616 winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB;
618 if (transports & U2F_AUTHENTICATOR_TRANSPORT_NFC) {
619 winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC;
621 if (transports & U2F_AUTHENTICATOR_TRANSPORT_BLE) {
622 winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE;
624 if (transports & CTAP_AUTHENTICATOR_TRANSPORT_INTERNAL) {
625 winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
628 WEBAUTHN_CREDENTIAL_EX credential = {
629 WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
630 static_cast<DWORD>(cred.id().Length()), (PBYTE)(cred.id().Elements()),
631 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports};
632 allowCredentials.AppendElement(credential);
635 if (allowCredentials.Length()) {
636 pAllowCredentials = allowCredentials.Elements();
637 for (DWORD i = 0; i < allowCredentials.Length(); i++) {
638 allowCredentialsPtrs.AppendElement(&pAllowCredentials[i]);
640 allowCredentialList.cCredentials = allowCredentials.Length();
641 allowCredentialList.ppCredentials = allowCredentialsPtrs.Elements();
642 pAllowCredentialList = &allowCredentialList;
645 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS WebAuthNAssertionOptions = {
646 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION,
647 aInfo.TimeoutMS(),
648 {0, NULL},
649 {0, NULL},
650 winAttachment,
651 winUserVerificationReq,
652 0, // dwFlags
653 winAppIdentifier,
654 pbU2fAppIdUsed,
655 nullptr, // pCancellationId
656 pAllowCredentialList,
659 GUID cancellationId = {0};
660 if (gWinWebauthnGetCancellationId(&cancellationId) == S_OK) {
661 WebAuthNAssertionOptions.pCancellationId = &cancellationId;
662 mCancellationIds.emplace(aTransactionId, &cancellationId);
665 PWEBAUTHN_ASSERTION pWebAuthNAssertion = nullptr;
667 // Bug 1518876: Get Window Handle from Content process for Windows WebAuthN
668 // APIs
669 HWND hWnd = GetForegroundWindow();
671 HRESULT hr =
672 gWinWebauthnGetAssertion(hWnd, rpID, &WebAuthNClientData,
673 &WebAuthNAssertionOptions, &pWebAuthNAssertion);
675 mCancellationIds.erase(aTransactionId);
677 if (hr == S_OK) {
678 nsTArray<uint8_t> signature;
679 if (aInfo.Extra().isSome()) {
680 signature.AppendElements(pWebAuthNAssertion->pbSignature,
681 pWebAuthNAssertion->cbSignature);
682 } else {
683 // AuthenticatorData Length check.
684 // First 32 bytes: RPID Hash
685 // Next 1 byte: Flags
686 // Next 4 bytes: Counter
687 if (pWebAuthNAssertion->cbAuthenticatorData < 32 + 1 + 4) {
688 MaybeAbortRegister(aTransactionId, NS_ERROR_DOM_INVALID_STATE_ERR);
691 signature.AppendElement(0x01); // User Presence bit
692 signature.AppendElements(pWebAuthNAssertion->pbAuthenticatorData +
693 32 + // RPID Hash length
694 1, // Flags
695 4); // Counter length
696 signature.AppendElements(pWebAuthNAssertion->pbSignature,
697 pWebAuthNAssertion->cbSignature);
700 nsTArray<uint8_t> keyHandle;
701 keyHandle.AppendElements(pWebAuthNAssertion->Credential.pbId,
702 pWebAuthNAssertion->Credential.cbId);
704 nsTArray<uint8_t> userHandle;
705 userHandle.AppendElements(pWebAuthNAssertion->pbUserId,
706 pWebAuthNAssertion->cbUserId);
708 nsTArray<uint8_t> authenticatorData;
709 authenticatorData.AppendElements(pWebAuthNAssertion->pbAuthenticatorData,
710 pWebAuthNAssertion->cbAuthenticatorData);
712 nsTArray<WebAuthnExtensionResult> extensions;
714 if (pbU2fAppIdUsed && *pbU2fAppIdUsed) {
715 extensions.AppendElement(WebAuthnExtensionResultAppId(true));
718 WebAuthnGetAssertionResult result(aInfo.ClientDataJSON(), keyHandle,
719 signature, authenticatorData, extensions,
720 signature, userHandle);
722 Unused << mTransactionParent->SendConfirmSign(aTransactionId, result);
723 ClearTransaction();
725 gWinWebauthnFreeAssertion(pWebAuthNAssertion);
727 } else {
728 PCWSTR errorName = gWinWebauthnGetErrorName(hr);
729 nsresult aError = NS_ERROR_DOM_ABORT_ERR;
731 if (_wcsicmp(errorName, L"InvalidStateError") == 0) {
732 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
733 } else if (_wcsicmp(errorName, L"ConstraintError") == 0 ||
734 _wcsicmp(errorName, L"UnknownError") == 0) {
735 aError = NS_ERROR_DOM_UNKNOWN_ERR;
736 } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) {
737 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
738 } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) {
739 aError = NS_ERROR_DOM_NOT_ALLOWED_ERR;
742 MaybeAbortSign(aTransactionId, aError);
746 void WinWebAuthnManager::MaybeAbortSign(const uint64_t& aTransactionId,
747 const nsresult& aError) {
748 AbortTransaction(aTransactionId, aError);
751 void WinWebAuthnManager::Cancel(PWebAuthnTransactionParent* aParent,
752 const Tainted<uint64_t>& aTransactionId) {
753 if (mTransactionParent != aParent) {
754 return;
757 ClearTransaction();
759 auto iter = mCancellationIds.find(
760 MOZ_NO_VALIDATE(aTransactionId,
761 "Transaction ID is checked against a global container, "
762 "so an invalid entry can affect another origin's "
763 "request. This issue is filed as Bug 1696159."));
764 if (iter != mCancellationIds.end()) {
765 gWinWebauthnCancelCurrentOperation(iter->second);
769 } // namespace mozilla::dom