diff options
Diffstat (limited to 'dom/webauthn/WinWebAuthnManager.cpp')
-rw-r--r-- | dom/webauthn/WinWebAuthnManager.cpp | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/dom/webauthn/WinWebAuthnManager.cpp b/dom/webauthn/WinWebAuthnManager.cpp new file mode 100644 index 0000000000..aa330212bf --- /dev/null +++ b/dom/webauthn/WinWebAuthnManager.cpp @@ -0,0 +1,706 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/dom/PWebAuthnTransactionParent.h" +#include "mozilla/dom/WebAuthnCBORUtil.h" +#include "mozilla/MozPromise.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/CryptoBuffer.h" +#include "mozilla/Unused.h" +#include "nsTextFormatter.h" +#include "nsWindowsHelpers.h" +#include "WebAuthnEnumStrings.h" +#include "WebAuthnTransportIdentifiers.h" +#include "winwebauthn/webauthn.h" +#include "WinWebAuthnManager.h" + +namespace mozilla::dom { + +namespace { +static mozilla::LazyLogModule gWinWebAuthnManagerLog("winwebauthnkeymanager"); +StaticAutoPtr<WinWebAuthnManager> gWinWebAuthnManager; +static HMODULE gWinWebAuthnModule = 0; + +static decltype(WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)* + gWinWebauthnIsUVPAA = nullptr; +static decltype(WebAuthNAuthenticatorMakeCredential)* + gWinWebauthnMakeCredential = nullptr; +static decltype(WebAuthNFreeCredentialAttestation)* + gWinWebauthnFreeCredentialAttestation = nullptr; +static decltype(WebAuthNAuthenticatorGetAssertion)* gWinWebauthnGetAssertion = + nullptr; +static decltype(WebAuthNFreeAssertion)* gWinWebauthnFreeAssertion = nullptr; +static decltype(WebAuthNGetCancellationId)* gWinWebauthnGetCancellationId = + nullptr; +static decltype(WebAuthNCancelCurrentOperation)* + gWinWebauthnCancelCurrentOperation = nullptr; +static decltype(WebAuthNGetErrorName)* gWinWebauthnGetErrorName = nullptr; +static decltype(WebAuthNGetApiVersionNumber)* gWinWebauthnGetApiVersionNumber = + nullptr; + +} // namespace + +/*********************************************************************** + * WinWebAuthnManager Implementation + **********************************************************************/ + +constexpr uint32_t kMinWinWebAuthNApiVersion = WEBAUTHN_API_VERSION_1; + +WinWebAuthnManager::WinWebAuthnManager() { + // Create on the main thread to make sure ClearOnShutdown() works. + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!gWinWebAuthnModule); + + gWinWebAuthnModule = LoadLibrarySystem32(L"webauthn.dll"); + + if (gWinWebAuthnModule) { + gWinWebauthnIsUVPAA = reinterpret_cast< + decltype(WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)*>( + GetProcAddress( + gWinWebAuthnModule, + "WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable")); + gWinWebauthnMakeCredential = + reinterpret_cast<decltype(WebAuthNAuthenticatorMakeCredential)*>( + GetProcAddress(gWinWebAuthnModule, + "WebAuthNAuthenticatorMakeCredential")); + gWinWebauthnFreeCredentialAttestation = + reinterpret_cast<decltype(WebAuthNFreeCredentialAttestation)*>( + GetProcAddress(gWinWebAuthnModule, + "WebAuthNFreeCredentialAttestation")); + gWinWebauthnGetAssertion = + reinterpret_cast<decltype(WebAuthNAuthenticatorGetAssertion)*>( + GetProcAddress(gWinWebAuthnModule, + "WebAuthNAuthenticatorGetAssertion")); + gWinWebauthnFreeAssertion = + reinterpret_cast<decltype(WebAuthNFreeAssertion)*>( + GetProcAddress(gWinWebAuthnModule, "WebAuthNFreeAssertion")); + gWinWebauthnGetCancellationId = + reinterpret_cast<decltype(WebAuthNGetCancellationId)*>( + GetProcAddress(gWinWebAuthnModule, "WebAuthNGetCancellationId")); + gWinWebauthnCancelCurrentOperation = + reinterpret_cast<decltype(WebAuthNCancelCurrentOperation)*>( + GetProcAddress(gWinWebAuthnModule, + "WebAuthNCancelCurrentOperation")); + gWinWebauthnGetErrorName = + reinterpret_cast<decltype(WebAuthNGetErrorName)*>( + GetProcAddress(gWinWebAuthnModule, "WebAuthNGetErrorName")); + gWinWebauthnGetApiVersionNumber = + reinterpret_cast<decltype(WebAuthNGetApiVersionNumber)*>( + GetProcAddress(gWinWebAuthnModule, "WebAuthNGetApiVersionNumber")); + + if (gWinWebauthnIsUVPAA && gWinWebauthnMakeCredential && + gWinWebauthnFreeCredentialAttestation && gWinWebauthnGetAssertion && + gWinWebauthnFreeAssertion && gWinWebauthnGetCancellationId && + gWinWebauthnCancelCurrentOperation && gWinWebauthnGetErrorName && + gWinWebauthnGetApiVersionNumber) { + mWinWebAuthNApiVersion = gWinWebauthnGetApiVersionNumber(); + } + } +} + +WinWebAuthnManager::~WinWebAuthnManager() { + if (gWinWebAuthnModule) { + FreeLibrary(gWinWebAuthnModule); + } + gWinWebAuthnModule = 0; +} + +// static +void WinWebAuthnManager::Initialize() { + if (!gWinWebAuthnManager) { + gWinWebAuthnManager = new WinWebAuthnManager(); + ClearOnShutdown(&gWinWebAuthnManager); + } +} + +// static +WinWebAuthnManager* WinWebAuthnManager::Get() { + MOZ_ASSERT(gWinWebAuthnManager); + return gWinWebAuthnManager; +} + +uint32_t WinWebAuthnManager::GetWebAuthNApiVersion() { + return mWinWebAuthNApiVersion; +} + +// static +bool WinWebAuthnManager::AreWebAuthNApisAvailable() { + WinWebAuthnManager* mgr = WinWebAuthnManager::Get(); + return mgr->GetWebAuthNApiVersion() >= kMinWinWebAuthNApiVersion; +} + +bool WinWebAuthnManager:: + IsUserVerifyingPlatformAuthenticatorAvailableInternal() { + BOOL isUVPAA = FALSE; + return (gWinWebauthnIsUVPAA(&isUVPAA) == S_OK && isUVPAA == TRUE); +} + +// static +bool WinWebAuthnManager::IsUserVerifyingPlatformAuthenticatorAvailable() { + if (WinWebAuthnManager::AreWebAuthNApisAvailable()) { + return WinWebAuthnManager::Get() + ->IsUserVerifyingPlatformAuthenticatorAvailableInternal(); + } + return false; +} + +void WinWebAuthnManager::AbortTransaction(const uint64_t& aTransactionId, + const nsresult& aError) { + Unused << mTransactionParent->SendAbort(aTransactionId, aError); + ClearTransaction(); +} + +void WinWebAuthnManager::MaybeClearTransaction( + PWebAuthnTransactionParent* aParent) { + // Only clear if we've been requested to do so by our current transaction + // parent. + if (mTransactionParent == aParent) { + ClearTransaction(); + } +} + +void WinWebAuthnManager::ClearTransaction() { mTransactionParent = nullptr; } + +void WinWebAuthnManager::Register( + PWebAuthnTransactionParent* aTransactionParent, + const uint64_t& aTransactionId, const WebAuthnMakeCredentialInfo& aInfo) { + MOZ_LOG(gWinWebAuthnManagerLog, LogLevel::Debug, ("WinWebAuthNRegister")); + + ClearTransaction(); + mTransactionParent = aTransactionParent; + + WEBAUTHN_EXTENSION rgExtension[1] = {}; + DWORD cExtensions = 0; + BOOL HmacCreateSecret = FALSE; + + // RP Information + WEBAUTHN_RP_ENTITY_INFORMATION rpInfo = { + WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, aInfo.RpId().get(), + nullptr, nullptr}; + + // User Information + WEBAUTHN_USER_ENTITY_INFORMATION userInfo = { + WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION, + 0, + nullptr, + nullptr, + nullptr, + nullptr}; + + // Client Data + WEBAUTHN_CLIENT_DATA WebAuthNClientData = { + WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, + (DWORD)aInfo.ClientDataJSON().Length(), + (BYTE*)(aInfo.ClientDataJSON().get()), WEBAUTHN_HASH_ALGORITHM_SHA_256}; + + // Algorithms + nsTArray<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> coseParams; + + // User Verification Requirement + DWORD winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; + + // Attachment + DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; + + // Resident Key + BOOL winRequireResidentKey = FALSE; + BOOL winPreferResidentKey = FALSE; + + // AttestationConveyance + DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY; + + rpInfo.pwszName = aInfo.Rp().Name().get(); + rpInfo.pwszIcon = aInfo.Rp().Icon().get(); + + userInfo.cbId = static_cast<DWORD>(aInfo.User().Id().Length()); + userInfo.pbId = const_cast<unsigned char*>(aInfo.User().Id().Elements()); + userInfo.pwszName = aInfo.User().Name().get(); + userInfo.pwszIcon = aInfo.User().Icon().get(); + userInfo.pwszDisplayName = aInfo.User().DisplayName().get(); + + for (const auto& coseAlg : aInfo.coseAlgs()) { + WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = { + WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION, + WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, coseAlg.alg()}; + coseParams.AppendElement(coseAlgorithm); + } + + const auto& sel = aInfo.AuthenticatorSelection(); + + const nsString& userVerificationRequirement = + sel.userVerificationRequirement(); + // This mapping needs to be reviewed if values are added to the + // UserVerificationRequirement enum. + static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 2); + if (userVerificationRequirement.EqualsLiteral( + MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) { + winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; + } else if (userVerificationRequirement.EqualsLiteral( + MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) { + winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; + } else if (userVerificationRequirement.EqualsLiteral( + MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) { + winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; + } else { + winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; + } + + if (sel.authenticatorAttachment().isSome()) { + const nsString& authenticatorAttachment = + sel.authenticatorAttachment().value(); + // This mapping needs to be reviewed if values are added to the + // AuthenticatorAttachement enum. + static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 2); + if (authenticatorAttachment.EqualsLiteral( + MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM)) { + winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM; + } else if (authenticatorAttachment.EqualsLiteral( + MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM)) { + winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM; + } else { + winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; + } + } + + const nsString& residentKey = sel.residentKey(); + // This mapping needs to be reviewed if values are added to the + // ResidentKeyRequirement enum. + static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 2); + if (residentKey.EqualsLiteral( + MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED)) { + winRequireResidentKey = TRUE; + winPreferResidentKey = TRUE; + } else if (residentKey.EqualsLiteral( + MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED)) { + winRequireResidentKey = FALSE; + winPreferResidentKey = TRUE; + } else if (residentKey.EqualsLiteral( + MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) { + winRequireResidentKey = FALSE; + winPreferResidentKey = FALSE; + } else { + // WebAuthnManager::MakeCredential is supposed to assign one of the above + // values, so this shouldn't happen. + MOZ_ASSERT_UNREACHABLE(); + MaybeAbortRegister(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR); + return; + } + + // AttestationConveyance + const nsString& attestation = aInfo.attestationConveyancePreference(); + // This mapping needs to be reviewed if values are added to the + // AttestationConveyancePreference enum. + static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 2); + if (attestation.EqualsLiteral( + MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE)) { + winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE; + } else if (attestation.EqualsLiteral( + MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT)) { + winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT; + } else if (attestation.EqualsLiteral( + MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT)) { + winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; + } else { + winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY; + } + + if (aInfo.Extensions().Length() > + (int)(sizeof(rgExtension) / sizeof(rgExtension[0]))) { + nsresult aError = NS_ERROR_DOM_INVALID_STATE_ERR; + MaybeAbortRegister(aTransactionId, aError); + return; + } + for (const WebAuthnExtension& ext : aInfo.Extensions()) { + if (ext.type() == WebAuthnExtension::TWebAuthnExtensionHmacSecret) { + HmacCreateSecret = + ext.get_WebAuthnExtensionHmacSecret().hmacCreateSecret() == true; + if (HmacCreateSecret) { + rgExtension[cExtensions].pwszExtensionIdentifier = + WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET; + rgExtension[cExtensions].cbExtension = sizeof(BOOL); + rgExtension[cExtensions].pvExtension = &HmacCreateSecret; + cExtensions++; + } + } + } + + WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters = { + static_cast<DWORD>(coseParams.Length()), coseParams.Elements()}; + + // Exclude Credentials + nsTArray<WEBAUTHN_CREDENTIAL_EX> excludeCredentials; + WEBAUTHN_CREDENTIAL_EX* pExcludeCredentials = nullptr; + nsTArray<WEBAUTHN_CREDENTIAL_EX*> excludeCredentialsPtrs; + WEBAUTHN_CREDENTIAL_LIST excludeCredentialList = {0}; + WEBAUTHN_CREDENTIAL_LIST* pExcludeCredentialList = nullptr; + + for (auto& cred : aInfo.ExcludeList()) { + uint8_t transports = cred.transports(); + DWORD winTransports = 0; + if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) { + winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB; + } + if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) { + winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC; + } + if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) { + winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE; + } + if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL) { + winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL; + } + + WEBAUTHN_CREDENTIAL_EX credential = { + WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION, + static_cast<DWORD>(cred.id().Length()), (PBYTE)(cred.id().Elements()), + WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports}; + excludeCredentials.AppendElement(credential); + } + + if (!excludeCredentials.IsEmpty()) { + pExcludeCredentials = excludeCredentials.Elements(); + for (DWORD i = 0; i < excludeCredentials.Length(); i++) { + excludeCredentialsPtrs.AppendElement(&pExcludeCredentials[i]); + } + excludeCredentialList.cCredentials = excludeCredentials.Length(); + excludeCredentialList.ppCredentials = excludeCredentialsPtrs.Elements(); + pExcludeCredentialList = &excludeCredentialList; + } + + // MakeCredentialOptions + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS WebAuthNCredentialOptions = { + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_4, + aInfo.TimeoutMS(), + {0, NULL}, + {0, NULL}, + winAttachment, + winRequireResidentKey, + winUserVerificationReq, + winAttestation, + 0, // Flags + NULL, // CancellationId + pExcludeCredentialList, + WEBAUTHN_ENTERPRISE_ATTESTATION_NONE, + WEBAUTHN_LARGE_BLOB_SUPPORT_NONE, + winPreferResidentKey, // PreferResidentKey + }; + + GUID cancellationId = {0}; + if (gWinWebauthnGetCancellationId(&cancellationId) == S_OK) { + WebAuthNCredentialOptions.pCancellationId = &cancellationId; + mCancellationIds.emplace(aTransactionId, &cancellationId); + } + + if (cExtensions != 0) { + WebAuthNCredentialOptions.Extensions.cExtensions = cExtensions; + WebAuthNCredentialOptions.Extensions.pExtensions = rgExtension; + } + + WEBAUTHN_CREDENTIAL_ATTESTATION* pWebAuthNCredentialAttestation = nullptr; + + // Bug 1518876: Get Window Handle from Content process for Windows WebAuthN + // APIs + HWND hWnd = GetForegroundWindow(); + + HRESULT hr = gWinWebauthnMakeCredential( + hWnd, &rpInfo, &userInfo, &WebAuthNCredentialParameters, + &WebAuthNClientData, &WebAuthNCredentialOptions, + &pWebAuthNCredentialAttestation); + + mCancellationIds.erase(aTransactionId); + + if (hr == S_OK) { + nsTArray<uint8_t> credentialId; + credentialId.AppendElements(pWebAuthNCredentialAttestation->pbCredentialId, + pWebAuthNCredentialAttestation->cbCredentialId); + + nsTArray<uint8_t> authenticatorData; + + authenticatorData.AppendElements( + pWebAuthNCredentialAttestation->pbAuthenticatorData, + pWebAuthNCredentialAttestation->cbAuthenticatorData); + + nsTArray<uint8_t> attObject; + if (winAttestation == WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE) { + // Zero AAGuid + const uint8_t zeroGuid[16] = {0}; + authenticatorData.ReplaceElementsAt(32 + 1 + 4 /*AAGuid offset*/, 16, + zeroGuid, 16); + + CryptoBuffer authData; + authData.Assign(authenticatorData); + CryptoBuffer noneAttObj; + CBOREncodeNoneAttestationObj(authData, noneAttObj); + attObject.AppendElements(noneAttObj); + } else { + attObject.AppendElements( + pWebAuthNCredentialAttestation->pbAttestationObject, + pWebAuthNCredentialAttestation->cbAttestationObject); + } + + nsTArray<WebAuthnExtensionResult> extensions; + + if (pWebAuthNCredentialAttestation->dwVersion >= + WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_2) { + PCWEBAUTHN_EXTENSIONS pExtensionList = + &pWebAuthNCredentialAttestation->Extensions; + if (pExtensionList->cExtensions != 0 && + pExtensionList->pExtensions != NULL) { + for (DWORD dwIndex = 0; dwIndex < pExtensionList->cExtensions; + dwIndex++) { + PWEBAUTHN_EXTENSION pExtension = + &pExtensionList->pExtensions[dwIndex]; + if (pExtension->pwszExtensionIdentifier && + (0 == _wcsicmp(pExtension->pwszExtensionIdentifier, + WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET)) && + pExtension->cbExtension == sizeof(BOOL)) { + BOOL* pCredentialCreatedWithHmacSecret = + (BOOL*)pExtension->pvExtension; + if (*pCredentialCreatedWithHmacSecret) { + extensions.AppendElement(WebAuthnExtensionResultHmacSecret(true)); + } + } + } + } + } + + WebAuthnMakeCredentialResult result(aInfo.ClientDataJSON(), attObject, + credentialId, extensions); + + Unused << mTransactionParent->SendConfirmRegister(aTransactionId, result); + ClearTransaction(); + gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation); + + } else { + PCWSTR errorName = gWinWebauthnGetErrorName(hr); + nsresult aError = NS_ERROR_DOM_ABORT_ERR; + + if (_wcsicmp(errorName, L"InvalidStateError") == 0) { + aError = NS_ERROR_DOM_INVALID_STATE_ERR; + } else if (_wcsicmp(errorName, L"ConstraintError") == 0 || + _wcsicmp(errorName, L"UnknownError") == 0) { + aError = NS_ERROR_DOM_UNKNOWN_ERR; + } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) { + aError = NS_ERROR_DOM_INVALID_STATE_ERR; + } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) { + aError = NS_ERROR_DOM_NOT_ALLOWED_ERR; + } + + MaybeAbortRegister(aTransactionId, aError); + } +} + +void WinWebAuthnManager::MaybeAbortRegister(const uint64_t& aTransactionId, + const nsresult& aError) { + AbortTransaction(aTransactionId, aError); +} + +void WinWebAuthnManager::Sign(PWebAuthnTransactionParent* aTransactionParent, + const uint64_t& aTransactionId, + const WebAuthnGetAssertionInfo& aInfo) { + MOZ_LOG(gWinWebAuthnManagerLog, LogLevel::Debug, ("WinWebAuthNSign")); + + ClearTransaction(); + mTransactionParent = aTransactionParent; + + // User Verification Requirement + DWORD winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; + + // RPID + PCWSTR rpID = nullptr; + + // Attachment + DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; + + // AppId + BOOL bU2fAppIdUsed = FALSE; + BOOL* pbU2fAppIdUsed = nullptr; + PCWSTR winAppIdentifier = nullptr; + + // Client Data + WEBAUTHN_CLIENT_DATA WebAuthNClientData = { + WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, + (DWORD)aInfo.ClientDataJSON().Length(), + (BYTE*)(aInfo.ClientDataJSON().get()), WEBAUTHN_HASH_ALGORITHM_SHA_256}; + + for (const WebAuthnExtension& ext : aInfo.Extensions()) { + if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) { + winAppIdentifier = ext.get_WebAuthnExtensionAppId().appIdentifier().get(); + pbU2fAppIdUsed = &bU2fAppIdUsed; + break; + } + } + + // RPID + rpID = aInfo.RpId().get(); + + // User Verification Requirement + const nsString& userVerificationReq = aInfo.userVerificationRequirement(); + // This mapping needs to be reviewed if values are added to the + // UserVerificationRequirement enum. + static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 2); + if (userVerificationReq.EqualsLiteral( + MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) { + winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; + } else if (userVerificationReq.EqualsLiteral( + MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) { + winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; + } else if (userVerificationReq.EqualsLiteral( + MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) { + winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; + } else { + winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; + } + + // allow Credentials + nsTArray<WEBAUTHN_CREDENTIAL_EX> allowCredentials; + WEBAUTHN_CREDENTIAL_EX* pAllowCredentials = nullptr; + nsTArray<WEBAUTHN_CREDENTIAL_EX*> allowCredentialsPtrs; + WEBAUTHN_CREDENTIAL_LIST allowCredentialList = {0}; + WEBAUTHN_CREDENTIAL_LIST* pAllowCredentialList = nullptr; + + for (auto& cred : aInfo.AllowList()) { + uint8_t transports = cred.transports(); + DWORD winTransports = 0; + if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) { + winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB; + } + if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) { + winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC; + } + if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) { + winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE; + } + if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL) { + winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL; + } + + WEBAUTHN_CREDENTIAL_EX credential = { + WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION, + static_cast<DWORD>(cred.id().Length()), (PBYTE)(cred.id().Elements()), + WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports}; + allowCredentials.AppendElement(credential); + } + + if (allowCredentials.Length()) { + pAllowCredentials = allowCredentials.Elements(); + for (DWORD i = 0; i < allowCredentials.Length(); i++) { + allowCredentialsPtrs.AppendElement(&pAllowCredentials[i]); + } + allowCredentialList.cCredentials = allowCredentials.Length(); + allowCredentialList.ppCredentials = allowCredentialsPtrs.Elements(); + pAllowCredentialList = &allowCredentialList; + } + + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS WebAuthNAssertionOptions = { + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION, + aInfo.TimeoutMS(), + {0, NULL}, + {0, NULL}, + winAttachment, + winUserVerificationReq, + 0, // dwFlags + winAppIdentifier, + pbU2fAppIdUsed, + nullptr, // pCancellationId + pAllowCredentialList, + }; + + GUID cancellationId = {0}; + if (gWinWebauthnGetCancellationId(&cancellationId) == S_OK) { + WebAuthNAssertionOptions.pCancellationId = &cancellationId; + mCancellationIds.emplace(aTransactionId, &cancellationId); + } + + PWEBAUTHN_ASSERTION pWebAuthNAssertion = nullptr; + + // Bug 1518876: Get Window Handle from Content process for Windows WebAuthN + // APIs + HWND hWnd = GetForegroundWindow(); + + HRESULT hr = + gWinWebauthnGetAssertion(hWnd, rpID, &WebAuthNClientData, + &WebAuthNAssertionOptions, &pWebAuthNAssertion); + + mCancellationIds.erase(aTransactionId); + + if (hr == S_OK) { + nsTArray<uint8_t> signature; + signature.AppendElements(pWebAuthNAssertion->pbSignature, + pWebAuthNAssertion->cbSignature); + + nsTArray<uint8_t> keyHandle; + keyHandle.AppendElements(pWebAuthNAssertion->Credential.pbId, + pWebAuthNAssertion->Credential.cbId); + + nsTArray<uint8_t> userHandle; + userHandle.AppendElements(pWebAuthNAssertion->pbUserId, + pWebAuthNAssertion->cbUserId); + + nsTArray<uint8_t> authenticatorData; + authenticatorData.AppendElements(pWebAuthNAssertion->pbAuthenticatorData, + pWebAuthNAssertion->cbAuthenticatorData); + + nsTArray<WebAuthnExtensionResult> extensions; + + if (pbU2fAppIdUsed && *pbU2fAppIdUsed) { + extensions.AppendElement(WebAuthnExtensionResultAppId(true)); + } + + WebAuthnGetAssertionResult result(aInfo.ClientDataJSON(), keyHandle, + signature, authenticatorData, extensions, + userHandle); + + Unused << mTransactionParent->SendConfirmSign(aTransactionId, result); + ClearTransaction(); + + gWinWebauthnFreeAssertion(pWebAuthNAssertion); + + } else { + PCWSTR errorName = gWinWebauthnGetErrorName(hr); + nsresult aError = NS_ERROR_DOM_ABORT_ERR; + + if (_wcsicmp(errorName, L"InvalidStateError") == 0) { + aError = NS_ERROR_DOM_INVALID_STATE_ERR; + } else if (_wcsicmp(errorName, L"ConstraintError") == 0 || + _wcsicmp(errorName, L"UnknownError") == 0) { + aError = NS_ERROR_DOM_UNKNOWN_ERR; + } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) { + aError = NS_ERROR_DOM_INVALID_STATE_ERR; + } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) { + aError = NS_ERROR_DOM_NOT_ALLOWED_ERR; + } + + MaybeAbortSign(aTransactionId, aError); + } +} + +void WinWebAuthnManager::MaybeAbortSign(const uint64_t& aTransactionId, + const nsresult& aError) { + AbortTransaction(aTransactionId, aError); +} + +void WinWebAuthnManager::Cancel(PWebAuthnTransactionParent* aParent, + const Tainted<uint64_t>& aTransactionId) { + if (mTransactionParent != aParent) { + return; + } + + ClearTransaction(); + + auto iter = mCancellationIds.find( + MOZ_NO_VALIDATE(aTransactionId, + "Transaction ID is checked against a global container, " + "so an invalid entry can affect another origin's " + "request. This issue is filed as Bug 1696159.")); + if (iter != mCancellationIds.end()) { + gWinWebauthnCancelCurrentOperation(iter->second); + } +} + +} // namespace mozilla::dom |