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
{
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
=
32 static decltype(WebAuthNFreeAssertion
)* gWinWebauthnFreeAssertion
= nullptr;
33 static decltype(WebAuthNGetCancellationId
)* gWinWebauthnGetCancellationId
=
35 static decltype(WebAuthNCancelCurrentOperation
)*
36 gWinWebauthnCancelCurrentOperation
= nullptr;
37 static decltype(WebAuthNGetErrorName
)* gWinWebauthnGetErrorName
= nullptr;
38 static decltype(WebAuthNGetApiVersionNumber
)* gWinWebauthnGetApiVersionNumber
=
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
)*>(
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;
109 void WinWebAuthnManager::Initialize() {
110 if (!gWinWebAuthnManager
) {
111 gWinWebAuthnManager
= new WinWebAuthnManager();
112 ClearOnShutdown(&gWinWebAuthnManager
);
117 WinWebAuthnManager
* WinWebAuthnManager::Get() {
118 MOZ_ASSERT(gWinWebAuthnManager
);
119 return gWinWebAuthnManager
;
122 uint32_t WinWebAuthnManager::GetWebAuthNApiVersion() {
123 return mWinWebAuthNApiVersion
;
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
);
139 bool WinWebAuthnManager::IsUserVerifyingPlatformAuthenticatorAvailable() {
140 if (WinWebAuthnManager::AreWebAuthNApisAvailable()) {
141 return WinWebAuthnManager::Get()
142 ->IsUserVerifyingPlatformAuthenticatorAvailableInternal();
147 void WinWebAuthnManager::AbortTransaction(const uint64_t& aTransactionId
,
148 const nsresult
& aError
) {
149 Unused
<< mTransactionParent
->SendAbort(aTransactionId
, aError
);
153 void WinWebAuthnManager::MaybeClearTransaction(
154 PWebAuthnTransactionParent
* aParent
) {
155 // Only clear if we've been requested to do so by our current transaction
157 if (mTransactionParent
== aParent
) {
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"));
170 mTransactionParent
= aTransactionParent
;
172 BYTE U2FUserId
= 0x01;
174 WEBAUTHN_EXTENSION rgExtension
[1] = {};
175 DWORD cExtensions
= 0;
176 BOOL HmacCreateSecret
= FALSE
;
179 WEBAUTHN_RP_ENTITY_INFORMATION rpInfo
= {
180 WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION
, aInfo
.RpId().get(),
184 WEBAUTHN_USER_ENTITY_INFORMATION userInfo
= {
185 WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION
,
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
};
199 nsTArray
<WEBAUTHN_COSE_CREDENTIAL_PARAMETER
> coseParams
;
201 // User Verification Requirement
202 DWORD winUserVerificationReq
= WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY
;
205 DWORD winAttachment
= WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY
;
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
;
241 case UserVerificationRequirement::Preferred
:
242 winUserVerificationReq
=
243 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED
;
245 case UserVerificationRequirement::Discouraged
:
246 winUserVerificationReq
=
247 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED
;
250 winUserVerificationReq
= WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY
;
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
;
261 case AuthenticatorAttachment::Cross_platform
:
262 winAttachment
= WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM
;
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
;
278 case AttestationConveyancePreference::Indirect
:
279 winAttestation
= WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT
;
281 case AttestationConveyancePreference::None
:
282 winAttestation
= WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE
;
285 winAttestation
= WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY
;
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
);
295 for (const WebAuthnExtension
& ext
: extra
.Extensions()) {
296 if (ext
.type() == WebAuthnExtension::TWebAuthnExtensionHmacSecret
) {
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
;
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
,
371 winRequireResidentKey
,
372 winUserVerificationReq
,
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
397 HWND hWnd
= GetForegroundWindow();
399 HRESULT hr
= gWinWebauthnMakeCredential(
400 hWnd
, &rpInfo
, &userInfo
, &WebAuthNCredentialParameters
,
401 &WebAuthNClientData
, &WebAuthNCredentialOptions
,
402 &pWebAuthNCredentialAttestation
);
404 mCancellationIds
.erase(aTransactionId
);
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
);
418 PWEBAUTHN_COMMON_ATTESTATION attestation
=
419 reinterpret_cast<PWEBAUTHN_COMMON_ATTESTATION
>(
420 pWebAuthNCredentialAttestation
->pvAttestationDecode
);
422 DWORD coseKeyOffset
= 32 + // RPIDHash
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
,
443 authenticatorData
.AppendElements(
444 pWebAuthNCredentialAttestation
->pbAuthenticatorData
+ yOffset
,
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
) {
460 const uint8_t zeroGuid
[16] = {0};
461 authenticatorData
.ReplaceElementsAt(32 + 1 + 4 /*AAGuid offset*/, 16,
464 CryptoBuffer authData
;
465 authData
.Assign(authenticatorData
);
466 CryptoBuffer noneAttObj
;
467 CBOREncodeNoneAttestationObj(authData
, noneAttObj
);
468 attObject
.AppendElements(noneAttObj
);
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
;
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
,
505 Unused
<< mTransactionParent
->SendConfirmRegister(aTransactionId
, result
);
507 gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation
);
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"));
539 mTransactionParent
= aTransactionParent
;
541 // User Verification Requirement
542 DWORD winUserVerificationReq
= WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY
;
545 PCWSTR rpID
= nullptr;
548 DWORD winAttachment
= WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY
;
551 BOOL bU2fAppIdUsed
= FALSE
;
552 BOOL
* pbU2fAppIdUsed
= nullptr;
553 PCWSTR winAppIdentifier
= nullptr;
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
) {
567 ext
.get_WebAuthnExtensionAppId().appIdentifier().get();
568 pbU2fAppIdUsed
= &bU2fAppIdUsed
;
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
;
585 case UserVerificationRequirement::Preferred
:
586 winUserVerificationReq
=
587 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED
;
589 case UserVerificationRequirement::Discouraged
:
590 winUserVerificationReq
=
591 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED
;
594 winUserVerificationReq
= WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY
;
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
;
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
,
651 winUserVerificationReq
,
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
669 HWND hWnd
= GetForegroundWindow();
672 gWinWebauthnGetAssertion(hWnd
, rpID
, &WebAuthNClientData
,
673 &WebAuthNAssertionOptions
, &pWebAuthNAssertion
);
675 mCancellationIds
.erase(aTransactionId
);
678 nsTArray
<uint8_t> signature
;
679 if (aInfo
.Extra().isSome()) {
680 signature
.AppendElements(pWebAuthNAssertion
->pbSignature
,
681 pWebAuthNAssertion
->cbSignature
);
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
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
);
725 gWinWebauthnFreeAssertion(pWebAuthNAssertion
);
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
) {
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