fune/dom/webauthn/WinWebAuthnManager.cpp
Stanca Serban cefd926755 Backed out 9 changesets (bug 1826760, bug 1826758, bug 1826752, bug 1826756, bug 1826759, bug 1826761, bug 1826757, bug 1826753, bug 1826754) for causing bp-hybrid bustages in ClearKeyDecryptionManager.cpp.
CLOSED TREE

Backed out changeset 210012222277 (bug 1826761)
Backed out changeset e364bb149efa (bug 1826760)
Backed out changeset e456e2f9966c (bug 1826759)
Backed out changeset 2b6ff545f4a3 (bug 1826758)
Backed out changeset 95fe1de8ba00 (bug 1826757)
Backed out changeset f8af52d7f2a1 (bug 1826756)
Backed out changeset 2646e773f098 (bug 1826754)
Backed out changeset 58d5d74b1835 (bug 1826753)
Backed out changeset 8567e6595acc (bug 1826752)
2023-04-17 13:52:39 +03:00

769 lines
29 KiB
C++

/* -*- 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/dom/PWebAuthnTransactionParent.h"
#include "mozilla/MozPromise.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ClearOnShutdown.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;
BYTE U2FUserId = 0x01;
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;
// AttestationConveyance
DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
if (aInfo.Extra().isSome()) {
const auto& extra = aInfo.Extra().ref();
rpInfo.pwszName = extra.Rp().Name().get();
rpInfo.pwszIcon = extra.Rp().Icon().get();
userInfo.cbId = static_cast<DWORD>(extra.User().Id().Length());
userInfo.pbId = const_cast<unsigned char*>(extra.User().Id().Elements());
userInfo.pwszName = extra.User().Name().get();
userInfo.pwszIcon = extra.User().Icon().get();
userInfo.pwszDisplayName = extra.User().DisplayName().get();
for (const auto& coseAlg : extra.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 = extra.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;
}
}
winRequireResidentKey = sel.requireResidentKey();
// AttestationConveyance
const nsString& attestation = extra.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 (extra.Extensions().Length() >
(int)(sizeof(rgExtension) / sizeof(rgExtension[0]))) {
nsresult aError = NS_ERROR_DOM_INVALID_STATE_ERR;
MaybeAbortRegister(aTransactionId, aError);
return;
}
for (const WebAuthnExtension& ext : extra.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++;
}
}
}
} else {
userInfo.cbId = sizeof(BYTE);
userInfo.pbId = &U2FUserId;
WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = {
WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION,
WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY,
WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256};
coseParams.AppendElement(coseAlgorithm);
winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
}
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,
FALSE, // 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;
if (aInfo.Extra().isSome()) {
authenticatorData.AppendElements(
pWebAuthNCredentialAttestation->pbAuthenticatorData,
pWebAuthNCredentialAttestation->cbAuthenticatorData);
} else {
PWEBAUTHN_COMMON_ATTESTATION attestation =
reinterpret_cast<PWEBAUTHN_COMMON_ATTESTATION>(
pWebAuthNCredentialAttestation->pvAttestationDecode);
DWORD coseKeyOffset = 32 + // RPIDHash
1 + // Flags
4 + // Counter
16 + // AAGuid
2 + // Credential ID Length field
pWebAuthNCredentialAttestation->cbCredentialId;
// Hardcoding as couldn't finder decoder and it is an ECC key.
DWORD xOffset = coseKeyOffset + 10;
DWORD yOffset = coseKeyOffset + 45;
// Authenticator Data length check.
if (pWebAuthNCredentialAttestation->cbAuthenticatorData < yOffset + 32) {
MaybeAbortRegister(aTransactionId, NS_ERROR_DOM_INVALID_STATE_ERR);
}
authenticatorData.AppendElement(0x05); // Reserved Byte
authenticatorData.AppendElement(0x04); // ECC Uncompressed Key
authenticatorData.AppendElements(
pWebAuthNCredentialAttestation->pbAuthenticatorData + xOffset,
32); // X Coordinate
authenticatorData.AppendElements(
pWebAuthNCredentialAttestation->pbAuthenticatorData + yOffset,
32); // Y Coordinate
authenticatorData.AppendElement(
pWebAuthNCredentialAttestation->cbCredentialId);
authenticatorData.AppendElements(
pWebAuthNCredentialAttestation->pbCredentialId,
pWebAuthNCredentialAttestation->cbCredentialId);
authenticatorData.AppendElements(attestation->pX5c->pbData,
attestation->pX5c->cbData);
authenticatorData.AppendElements(attestation->pbSignature,
attestation->cbSignature);
}
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, authenticatorData,
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};
if (aInfo.Extra().isSome()) {
const auto& extra = aInfo.Extra().ref();
for (const WebAuthnExtension& ext : extra.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 = extra.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;
}
} else {
rpID = aInfo.Origin().get();
winAppIdentifier = aInfo.RpId().get();
pbU2fAppIdUsed = &bU2fAppIdUsed;
winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
}
// 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;
if (aInfo.Extra().isSome()) {
signature.AppendElements(pWebAuthNAssertion->pbSignature,
pWebAuthNAssertion->cbSignature);
} else {
// AuthenticatorData Length check.
// First 32 bytes: RPID Hash
// Next 1 byte: Flags
// Next 4 bytes: Counter
if (pWebAuthNAssertion->cbAuthenticatorData < 32 + 1 + 4) {
MaybeAbortRegister(aTransactionId, NS_ERROR_DOM_INVALID_STATE_ERR);
}
signature.AppendElement(0x01); // User Presence bit
signature.AppendElements(pWebAuthNAssertion->pbAuthenticatorData +
32 + // RPID Hash length
1, // Flags
4); // Counter length
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,
signature, 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