Files
sousa-gecko/dom/webauthn/WinWebAuthnService.cpp
T
John M. Schanck e1b617cb11 Bug 2040462 - allow simultaneous conditional gets in different browsing contexts. r=keeler,credential-management-reviewers,dimi
Allow each browsing context to have a in-flight conditional get request.

A "conditionally mediated" WebAuthn request is a site's intention to submit a
request in response to user interaction with browser-presented autofill UI. The
WebAuthn spec only allows one active request at a time, but a conditional
request can stay pending while another request is active. Prior to this patch we
incorrectly applied the uniqueness requirement to conditional requests.

The previous design had some other deficiencies as well. We held pending
requests at the level of the platform nsIWebAuthnService, which meant we had
duplicate logic for dispatching a pending request at each service. This patch
hoists the pending request management up to the top level WebAuthnService, adds
a new GetAutoFillEntriesForRpId method which allows WebAuthnService to request
autofill entries from the platform-specific service, and simplifies the state
management at the platform level.

As part of this refactor it was natural to also remove some unnecessary
synchronization mechanisms from the platform-specific nsIWebAuthnServices. Back
when WebAuthnTransactionParent ran on the IPDL Background Thread we needed these
synchronization mechanisms to allow prompts / main thread UI to cancel WebAuthn
operations. This has not been necessary since Bug 1945969, which moved
WebAuthnTransactionParent to the main thread.

Differential Revision: https://phabricator.services.mozilla.com/D301134
2026-05-28 19:38:08 +00:00

1232 lines
48 KiB
C++

/* 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 "WinWebAuthnService.h"
#include "WebAuthnAutoFillEntry.h"
#include "WebAuthnEnumStrings.h"
#include "WebAuthnResult.h"
#include "WebAuthnTransportIdentifiers.h"
#include "mozilla/Assertions.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Preferences.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/dom/PWebAuthnTransactionParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "nsTextFormatter.h"
#include "nsWindowsHelpers.h"
#include "winwebauthn/webauthn.h"
namespace mozilla::dom {
namespace {
StaticRWLock gWinWebAuthnModuleLock;
static bool gWinWebAuthnModuleUnusable = false;
static HMODULE gWinWebAuthnModule = 0;
static const LPCWSTR gWebAuthnHintStrings[3] = {
WEBAUTHN_CREDENTIAL_HINT_SECURITY_KEY,
WEBAUTHN_CREDENTIAL_HINT_CLIENT_DEVICE, WEBAUTHN_CREDENTIAL_HINT_HYBRID};
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;
static decltype(WebAuthNGetPlatformCredentialList)*
gWinWebauthnGetPlatformCredentialList = nullptr;
static decltype(WebAuthNFreePlatformCredentialList)*
gWinWebauthnFreePlatformCredentialList = nullptr;
} // namespace
/***********************************************************************
* WinWebAuthnService Implementation
**********************************************************************/
constexpr uint32_t kMinWinWebAuthNApiVersion = WEBAUTHN_API_VERSION_1;
NS_IMPL_ISUPPORTS(WinWebAuthnService, nsIWebAuthnService)
/* static */
nsresult WinWebAuthnService::EnsureWinWebAuthnModuleLoaded() {
{
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
if (gWinWebAuthnModule) {
// The module is already loaded.
return NS_OK;
}
if (gWinWebAuthnModuleUnusable) {
// A previous attempt to load the module failed.
return NS_ERROR_NOT_AVAILABLE;
}
}
StaticAutoWriteLock lock(gWinWebAuthnModuleLock);
if (gWinWebAuthnModule) {
// Another thread successfully loaded the module while we were waiting.
return NS_OK;
}
if (gWinWebAuthnModuleUnusable) {
// Another thread failed to load the module while we were waiting.
return NS_ERROR_NOT_AVAILABLE;
}
gWinWebAuthnModule = LoadLibrarySystem32(L"webauthn.dll");
auto markModuleUnusable = MakeScopeExit([]() {
if (gWinWebAuthnModule) {
FreeLibrary(gWinWebAuthnModule);
gWinWebAuthnModule = 0;
}
gWinWebAuthnModuleUnusable = true;
});
if (!gWinWebAuthnModule) {
return NS_ERROR_NOT_AVAILABLE;
}
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)) {
return NS_ERROR_NOT_AVAILABLE;
}
DWORD version = gWinWebauthnGetApiVersionNumber();
if (version >= WEBAUTHN_API_VERSION_4) {
gWinWebauthnGetPlatformCredentialList =
reinterpret_cast<decltype(WebAuthNGetPlatformCredentialList)*>(
GetProcAddress(gWinWebAuthnModule,
"WebAuthNGetPlatformCredentialList"));
gWinWebauthnFreePlatformCredentialList =
reinterpret_cast<decltype(WebAuthNFreePlatformCredentialList)*>(
GetProcAddress(gWinWebAuthnModule,
"WebAuthNFreePlatformCredentialList"));
if (!(gWinWebauthnGetPlatformCredentialList &&
gWinWebauthnFreePlatformCredentialList)) {
return NS_ERROR_NOT_AVAILABLE;
}
}
// Bug 1869584: In some of our tests, a content process can end up here due to
// a call to WinWebAuthnService::AreWebAuthNApisAvailable. This causes us to
// fail an assertion in Preferences::SetBool, which is parent-process only.
if (XRE_IsParentProcess()) {
NS_DispatchToMainThread(NS_NewRunnableFunction(__func__, [version]() {
Preferences::SetBool("security.webauthn.show_ms_settings_link",
version >= WEBAUTHN_API_VERSION_7);
}));
}
markModuleUnusable.release();
return NS_OK;
}
WinWebAuthnService::~WinWebAuthnService() {
StaticAutoWriteLock lock(gWinWebAuthnModuleLock);
if (gWinWebAuthnModule) {
FreeLibrary(gWinWebAuthnModule);
}
gWinWebAuthnModule = 0;
}
// static
void PrunePublicKeyCredentialHints(const nsTArray<nsString>& aInHints,
/* out */ nsTArray<LPCWSTR>& aOutHints) {
for (const nsString& inputHint : aInHints) {
for (const LPCWSTR knownHint : gWebAuthnHintStrings) {
if (inputHint.Equals(knownHint)) {
aOutHints.AppendElement(knownHint);
}
}
}
}
// static
bool WinWebAuthnService::AreWebAuthNApisAvailable() {
nsresult rv = EnsureWinWebAuthnModuleLoaded();
NS_ENSURE_SUCCESS(rv, false);
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
return gWinWebAuthnModule &&
gWinWebauthnGetApiVersionNumber() >= kMinWinWebAuthNApiVersion;
}
NS_IMETHODIMP
WinWebAuthnService::GetIsUVPAA(bool* aAvailable) {
nsresult rv = EnsureWinWebAuthnModuleLoaded();
NS_ENSURE_SUCCESS(rv, rv);
if (WinWebAuthnService::AreWebAuthNApisAvailable()) {
BOOL isUVPAA = FALSE;
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
*aAvailable = gWinWebAuthnModule && gWinWebauthnIsUVPAA(&isUVPAA) == S_OK &&
isUVPAA == TRUE;
} else {
*aAvailable = false;
}
return NS_OK;
}
NS_IMETHODIMP
WinWebAuthnService::Cancel(uint64_t aTransactionId) {
MOZ_ASSERT(NS_IsMainThread());
if (mActiveTransaction.isSome() &&
mActiveTransaction.ref().transactionId == aTransactionId) {
Reset();
}
return NS_OK;
}
NS_IMETHODIMP
WinWebAuthnService::Reset() {
// Reset will never be the first function to use gWinWebAuthnModule, so
// we shouldn't try to initialize it here.
MOZ_ASSERT(NS_IsMainThread());
if (mActiveTransaction.isSome()) {
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
if (gWinWebAuthnModule) {
const GUID cancellationId = mActiveTransaction.ref().cancellationId;
gWinWebauthnCancelCurrentOperation(&cancellationId);
}
mActiveTransaction.reset();
}
return NS_OK;
}
NS_IMETHODIMP
WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
uint64_t aBrowsingContextId,
nsIWebAuthnRegisterArgs* aArgs,
nsIWebAuthnRegisterPromise* aPromise) {
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = EnsureWinWebAuthnModuleLoaded();
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(mActiveTransaction.isNothing(),
"WebAuthnService should reset the platform service before "
"dispatching MakeCredential");
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
GUID cancellationId;
if (gWinWebauthnGetCancellationId(&cancellationId) != S_OK) {
// caller will reject promise
return NS_ERROR_DOM_UNKNOWN_ERR;
}
mActiveTransaction = Some(TransactionState{aTransactionId, cancellationId});
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
"WinWebAuthnService::MakeCredential",
[self = RefPtr{this}, aArgs = RefPtr{aArgs}, aPromise = RefPtr{aPromise},
cancellationId]() mutable {
// Take a read lock on gWinWebAuthnModuleLock to prevent the module from
// being unloaded while the operation is in progress. This does not
// prevent the operation from being cancelled, so it does not block a
// clean shutdown.
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
if (!gWinWebAuthnModule) {
aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
return;
}
// RP Information
nsString rpId;
(void)aArgs->GetRpId(rpId);
WEBAUTHN_RP_ENTITY_INFORMATION rpInfo = {
WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, rpId.get(), nullptr,
nullptr};
// User Information
WEBAUTHN_USER_ENTITY_INFORMATION userInfo = {
WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION,
0,
nullptr,
nullptr,
nullptr,
nullptr};
// Client Data
nsCString clientDataJSON;
(void)aArgs->GetClientDataJSON(clientDataJSON);
WEBAUTHN_CLIENT_DATA WebAuthNClientData = {
WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
(DWORD)clientDataJSON.Length(), (BYTE*)(clientDataJSON.get()),
WEBAUTHN_HASH_ALGORITHM_SHA_256};
// User Verification Requirement
DWORD winUserVerificationReq =
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
// Resident Key Requirement.
BOOL winRequireResidentKey = FALSE; // Will be set to TRUE if and only
// if residentKey = "required"
BOOL winPreferResidentKey = FALSE; // Will be set to TRUE if and only
// if residentKey = "preferred"
// AttestationConveyance
DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
// Large Blob
DWORD largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_NONE;
bool largeBlobSupportRequired;
nsresult rv =
aArgs->GetLargeBlobSupportRequired(&largeBlobSupportRequired);
if (rv != NS_ERROR_NOT_AVAILABLE) {
if (NS_FAILED(rv)) {
aPromise->Reject(rv);
return;
}
if (largeBlobSupportRequired) {
largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_REQUIRED;
} else {
largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_PREFERRED;
}
}
// Prf
BOOL winEnablePrf = FALSE;
nsString rpName;
(void)aArgs->GetRpName(rpName);
rpInfo.pwszName = rpName.get();
rpInfo.pwszIcon = nullptr;
nsTArray<uint8_t> userId;
(void)aArgs->GetUserId(userId);
userInfo.cbId = static_cast<DWORD>(userId.Length());
userInfo.pbId = const_cast<unsigned char*>(userId.Elements());
nsString userName;
(void)aArgs->GetUserName(userName);
userInfo.pwszName = userName.get();
userInfo.pwszIcon = nullptr;
nsString userDisplayName;
(void)aArgs->GetUserDisplayName(userDisplayName);
userInfo.pwszDisplayName = userDisplayName.get();
// Algorithms
nsTArray<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> coseParams;
nsTArray<int32_t> coseAlgs;
(void)aArgs->GetCoseAlgs(coseAlgs);
for (const int32_t& coseAlg : coseAlgs) {
WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = {
WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION,
WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, coseAlg};
coseParams.AppendElement(coseAlgorithm);
}
nsString userVerificationReq;
(void)aArgs->GetUserVerification(userVerificationReq);
// This mapping needs to be reviewed if values are added to the
// UserVerificationRequirement enum.
static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
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;
}
// Attachment
DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
nsString authenticatorAttachment;
rv = aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
if (rv != NS_ERROR_NOT_AVAILABLE) {
if (NS_FAILED(rv)) {
aPromise->Reject(rv);
return;
}
// This mapping needs to be reviewed if values are added to the
// AuthenticatorAttachement enum.
static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
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;
}
}
nsString residentKey;
(void)aArgs->GetResidentKey(residentKey);
// This mapping needs to be reviewed if values are added to the
// ResidentKeyRequirement enum.
static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
if (residentKey.EqualsLiteral(
MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED)) {
winRequireResidentKey = TRUE;
winPreferResidentKey = FALSE;
} 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 {
// WebAuthnHandler::MakeCredential is supposed to assign one of the
// above values, so this shouldn't happen.
MOZ_ASSERT_UNREACHABLE();
aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
return;
}
// AttestationConveyance
nsString attestation;
(void)aArgs->GetAttestationConveyancePreference(attestation);
// This mapping needs to be reviewed if values are added to the
// AttestationConveyancePreference enum.
static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
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;
}
// Extensions that might require an entry in the extensions array:
// credProtect, hmac-secret, minPinLength.
nsTArray<WEBAUTHN_EXTENSION> rgExtension(3);
WEBAUTHN_CRED_PROTECT_EXTENSION_IN winCredProtect = {
.dwCredProtect = WEBAUTHN_USER_VERIFICATION_ANY,
.bRequireCredProtect = FALSE,
};
BOOL winHmacCreateSecret = FALSE;
BOOL winMinPinLength = FALSE;
nsCString credProtectPolicy;
if (NS_SUCCEEDED(
aArgs->GetCredentialProtectionPolicy(credProtectPolicy))) {
Maybe<CredentialProtectionPolicy> policy(
StringToEnum<CredentialProtectionPolicy>(credProtectPolicy));
if (policy.isNothing()) {
aPromise->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
switch (policy.ref()) {
case CredentialProtectionPolicy::UserVerificationOptional:
winCredProtect.dwCredProtect =
WEBAUTHN_USER_VERIFICATION_OPTIONAL;
break;
case CredentialProtectionPolicy::
UserVerificationOptionalWithCredentialIDList:
winCredProtect.dwCredProtect =
WEBAUTHN_USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST;
break;
case CredentialProtectionPolicy::UserVerificationRequired:
winCredProtect.dwCredProtect =
WEBAUTHN_USER_VERIFICATION_REQUIRED;
break;
}
bool enforceCredProtectPolicy;
if (NS_SUCCEEDED(aArgs->GetEnforceCredentialProtectionPolicy(
&enforceCredProtectPolicy)) &&
enforceCredProtectPolicy) {
winCredProtect.bRequireCredProtect = TRUE;
}
rgExtension.AppendElement(WEBAUTHN_EXTENSION{
.pwszExtensionIdentifier =
WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT,
.cbExtension = sizeof(WEBAUTHN_CRED_PROTECT_EXTENSION_IN),
.pvExtension = &winCredProtect,
});
}
bool requestedPrf;
bool requestedHmacCreateSecret;
if (NS_SUCCEEDED(aArgs->GetPrf(&requestedPrf)) &&
NS_SUCCEEDED(
aArgs->GetHmacCreateSecret(&requestedHmacCreateSecret)) &&
(requestedPrf || requestedHmacCreateSecret)) {
winEnablePrf = requestedPrf ? TRUE : FALSE;
winHmacCreateSecret = TRUE;
rgExtension.AppendElement(WEBAUTHN_EXTENSION{
.pwszExtensionIdentifier =
WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET,
.cbExtension = sizeof(BOOL),
.pvExtension = &winHmacCreateSecret,
});
}
nsTArray<uint8_t> prfEvalFirst;
nsTArray<uint8_t> prfEvalSecond;
WEBAUTHN_HMAC_SECRET_SALT prfGlobalEval = {0};
PWEBAUTHN_HMAC_SECRET_SALT pPrfGlobalEval = NULL;
if (requestedPrf) {
pPrfGlobalEval = &prfGlobalEval;
rv = aArgs->GetPrfEvalFirst(prfEvalFirst);
if (rv == NS_OK) {
prfGlobalEval.cbFirst = prfEvalFirst.Length();
prfGlobalEval.pbFirst = prfEvalFirst.Elements();
}
rv = aArgs->GetPrfEvalSecond(prfEvalSecond);
if (rv == NS_OK) {
prfGlobalEval.cbSecond = prfEvalSecond.Length();
prfGlobalEval.pbSecond = prfEvalSecond.Elements();
}
}
bool requestedMinPinLength;
if (NS_SUCCEEDED(aArgs->GetMinPinLength(&requestedMinPinLength)) &&
requestedMinPinLength) {
winMinPinLength = TRUE;
rgExtension.AppendElement(WEBAUTHN_EXTENSION{
.pwszExtensionIdentifier =
WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH,
.cbExtension = sizeof(BOOL),
.pvExtension = &winMinPinLength,
});
}
WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters = {
static_cast<DWORD>(coseParams.Length()), coseParams.Elements()};
// Exclude Credentials
nsTArray<nsTArray<uint8_t>> excludeList;
(void)aArgs->GetExcludeList(excludeList);
nsTArray<uint8_t> excludeListTransports;
(void)aArgs->GetExcludeListTransports(excludeListTransports);
if (excludeList.Length() != excludeListTransports.Length()) {
aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
return;
}
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 (size_t i = 0; i < excludeList.Length(); i++) {
nsTArray<uint8_t>& cred = excludeList[i];
uint8_t& transports = excludeListTransports[i];
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;
}
if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID) {
winTransports |= WEBAUTHN_CTAP_TRANSPORT_HYBRID;
}
WEBAUTHN_CREDENTIAL_EX credential = {
WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
static_cast<DWORD>(cred.Length()), (PBYTE)(cred.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;
}
uint32_t timeout_u32;
(void)aArgs->GetTimeoutMS(&timeout_u32);
DWORD timeout = timeout_u32;
bool privateBrowsing;
(void)aArgs->GetPrivateBrowsing(&privateBrowsing);
BOOL winPrivateBrowsing = FALSE;
if (privateBrowsing) {
winPrivateBrowsing = TRUE;
}
nsTArray<nsString> inputHints;
(void)aArgs->GetHints(inputHints);
nsTArray<LPCWSTR> hints;
PrunePublicKeyCredentialHints(inputHints, hints);
// MakeCredentialOptions
WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS
WebAuthNCredentialOptions = {
WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_8,
timeout,
{0, NULL},
{0, NULL},
winAttachment,
winRequireResidentKey,
winUserVerificationReq,
winAttestation,
0, // Flags
&cancellationId, // CancellationId
pExcludeCredentialList,
WEBAUTHN_ENTERPRISE_ATTESTATION_NONE,
largeBlobSupport, // LargeBlobSupport
winPreferResidentKey, // PreferResidentKey
winPrivateBrowsing, // BrowserInPrivateMode
winEnablePrf, // EnablePrf
NULL, // LinkedDevice
0, // size of JsonExt
NULL, // JsonExt
pPrfGlobalEval, // PRFGlobalEval
(DWORD)hints.Length(), // Size of CredentialHints
hints.Elements(), // CredentialHints
};
if (rgExtension.Length() != 0) {
WebAuthNCredentialOptions.Extensions.cExtensions =
rgExtension.Length();
WebAuthNCredentialOptions.Extensions.pExtensions =
rgExtension.Elements();
}
PWEBAUTHN_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);
if (hr == S_OK) {
RefPtr<WebAuthnRegisterResult> result = new WebAuthnRegisterResult(
clientDataJSON, pWebAuthNCredentialAttestation);
// WEBAUTHN_CREDENTIAL_ATTESTATION structs of version >= 4 always
// include a flag to indicate whether a resident key was created. We
// copy that flag to the credProps extension output only if the RP
// requested the credProps extension.
bool requestedCredProps;
(void)aArgs->GetCredProps(&requestedCredProps);
if (requestedCredProps &&
pWebAuthNCredentialAttestation->dwVersion >=
WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4) {
BOOL rk = pWebAuthNCredentialAttestation->bResidentKey;
(void)result->SetCredPropsRk(rk == TRUE);
}
gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation);
aPromise->Resolve(result);
} 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;
}
aPromise->Reject(aError);
}
}));
NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP
WinWebAuthnService::GetAssertion(uint64_t aTransactionId,
uint64_t aBrowsingContextId,
nsIWebAuthnSignArgs* aArgs,
nsIWebAuthnSignPromise* aPromise) {
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = EnsureWinWebAuthnModuleLoaded();
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(mActiveTransaction.isNothing(),
"WebAuthnService should reset the platform service before "
"dispatching GetAssertion");
GUID cancellationId;
{
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
if (gWinWebauthnGetCancellationId(&cancellationId) != S_OK) {
// caller will reject promise
return NS_ERROR_DOM_UNKNOWN_ERR;
}
}
mActiveTransaction = Some(TransactionState{aTransactionId, cancellationId});
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
"WinWebAuthnService::GetAssertion",
[aArgs = RefPtr{aArgs}, aPromise = RefPtr{aPromise},
aCancellationId = cancellationId]() mutable {
// Take a read lock on gWinWebAuthnModuleLock to prevent the module from
// being unloaded while the operation is in progress. This does not
// prevent the operation from being cancelled, so it does not block a
// clean shutdown.
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
if (!gWinWebAuthnModule) {
aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
return;
}
// Attachment
DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
// AppId
BOOL bAppIdUsed = FALSE;
BOOL* pbAppIdUsed = nullptr;
PCWSTR winAppIdentifier = nullptr;
// Client Data
nsCString clientDataJSON;
(void)aArgs->GetClientDataJSON(clientDataJSON);
WEBAUTHN_CLIENT_DATA WebAuthNClientData = {
WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
(DWORD)clientDataJSON.Length(), (BYTE*)(clientDataJSON.get()),
WEBAUTHN_HASH_ALGORITHM_SHA_256};
nsString appId;
nsresult rv = aArgs->GetAppId(appId);
if (rv != NS_ERROR_NOT_AVAILABLE) {
if (NS_FAILED(rv)) {
aPromise->Reject(rv);
return;
}
winAppIdentifier = appId.get();
pbAppIdUsed = &bAppIdUsed;
}
// RPID
nsString rpId;
(void)aArgs->GetRpId(rpId);
// User Verification Requirement
nsString userVerificationReq;
(void)aArgs->GetUserVerification(userVerificationReq);
DWORD winUserVerificationReq;
// This mapping needs to be reviewed if values are added to the
// UserVerificationRequirement enum.
static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
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;
}
// Large Blob
DWORD credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE;
DWORD credLargeBlobSize = 0;
PBYTE credLargeBlob = nullptr;
nsTArray<uint8_t> largeBlobWrite;
bool largeBlobRead;
rv = aArgs->GetLargeBlobRead(&largeBlobRead);
if (rv != NS_ERROR_NOT_AVAILABLE) {
if (NS_FAILED(rv)) {
aPromise->Reject(rv);
return;
}
if (largeBlobRead) {
credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_GET;
} else {
rv = aArgs->GetLargeBlobWrite(largeBlobWrite);
if (rv != NS_ERROR_NOT_AVAILABLE && NS_FAILED(rv)) {
aPromise->Reject(rv);
return;
}
credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_SET;
credLargeBlobSize = largeBlobWrite.Length();
credLargeBlob = largeBlobWrite.Elements();
}
}
// PRF inputs
WEBAUTHN_HMAC_SECRET_SALT_VALUES* pPrfInputs = nullptr;
WEBAUTHN_HMAC_SECRET_SALT_VALUES prfInputs = {0};
WEBAUTHN_HMAC_SECRET_SALT globalHmacSalt = {0};
nsTArray<uint8_t> prfEvalFirst;
nsTArray<uint8_t> prfEvalSecond;
nsTArray<nsTArray<uint8_t>> prfEvalByCredIds;
nsTArray<nsTArray<uint8_t>> prfEvalByCredFirsts;
nsTArray<bool> prfEvalByCredSecondMaybes;
nsTArray<nsTArray<uint8_t>> prfEvalByCredSeconds;
nsTArray<WEBAUTHN_HMAC_SECRET_SALT> hmacSecretSalts;
nsTArray<WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT>
credWithHmacSecretSaltList;
bool requestedPrf;
(void)aArgs->GetPrf(&requestedPrf);
if (requestedPrf) {
rv = aArgs->GetPrfEvalFirst(prfEvalFirst);
if (rv == NS_OK) {
globalHmacSalt.cbFirst = prfEvalFirst.Length();
globalHmacSalt.pbFirst = prfEvalFirst.Elements();
prfInputs.pGlobalHmacSalt = &globalHmacSalt;
}
rv = aArgs->GetPrfEvalSecond(prfEvalSecond);
if (rv == NS_OK) {
globalHmacSalt.cbSecond = prfEvalSecond.Length();
globalHmacSalt.pbSecond = prfEvalSecond.Elements();
}
if (NS_OK ==
aArgs->GetPrfEvalByCredentialCredentialId(prfEvalByCredIds) &&
NS_OK ==
aArgs->GetPrfEvalByCredentialEvalFirst(prfEvalByCredFirsts) &&
NS_OK == aArgs->GetPrfEvalByCredentialEvalSecondMaybe(
prfEvalByCredSecondMaybes) &&
NS_OK == aArgs->GetPrfEvalByCredentialEvalSecond(
prfEvalByCredSeconds) &&
prfEvalByCredIds.Length() == prfEvalByCredFirsts.Length() &&
prfEvalByCredIds.Length() == prfEvalByCredSecondMaybes.Length() &&
prfEvalByCredIds.Length() == prfEvalByCredSeconds.Length()) {
for (size_t i = 0; i < prfEvalByCredIds.Length(); i++) {
WEBAUTHN_HMAC_SECRET_SALT salt = {0};
salt.cbFirst = prfEvalByCredFirsts[i].Length();
salt.pbFirst = prfEvalByCredFirsts[i].Elements();
if (prfEvalByCredSecondMaybes[i]) {
salt.cbSecond = prfEvalByCredSeconds[i].Length();
salt.pbSecond = prfEvalByCredSeconds[i].Elements();
}
hmacSecretSalts.AppendElement(salt);
}
// The credWithHmacSecretSaltList array will contain raw pointers to
// elements of the hmacSecretSalts array, so we must not cause
// any re-allocations of hmacSecretSalts from this point.
for (size_t i = 0; i < prfEvalByCredIds.Length(); i++) {
WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT value = {0};
value.cbCredID = prfEvalByCredIds[i].Length();
value.pbCredID = prfEvalByCredIds[i].Elements();
value.pHmacSecretSalt = &hmacSecretSalts[i];
credWithHmacSecretSaltList.AppendElement(value);
}
prfInputs.cCredWithHmacSecretSaltList =
credWithHmacSecretSaltList.Length();
prfInputs.pCredWithHmacSecretSaltList =
credWithHmacSecretSaltList.Elements();
}
pPrfInputs = &prfInputs;
}
// https://w3c.github.io/webauthn/#prf-extension
// "The hmac-secret extension provides two PRFs per credential: one
// which is used for requests where user verification is performed and
// another for all other requests. This extension [PRF] only exposes a
// single PRF per credential and, when implementing on top of
// hmac-secret, that PRF MUST be the one used for when user verification
// is performed. This overrides the UserVerificationRequirement if
// neccessary."
if (pPrfInputs &&
winUserVerificationReq ==
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED) {
winUserVerificationReq =
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
}
// allow Credentials
nsTArray<nsTArray<uint8_t>> allowList;
nsTArray<uint8_t> allowListTransports;
(void)aArgs->GetAllowList(allowList);
(void)aArgs->GetAllowListTransports(allowListTransports);
if (allowList.Length() != allowListTransports.Length()) {
aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
return;
}
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 (size_t i = 0; i < allowList.Length(); i++) {
nsTArray<uint8_t>& cred = allowList[i];
uint8_t& transports = allowListTransports[i];
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;
}
if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID) {
winTransports |= WEBAUTHN_CTAP_TRANSPORT_HYBRID;
}
WEBAUTHN_CREDENTIAL_EX credential = {
WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
static_cast<DWORD>(cred.Length()), (PBYTE)(cred.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;
}
nsTArray<nsString> inputHints;
(void)aArgs->GetHints(inputHints);
nsTArray<LPCWSTR> hints;
PrunePublicKeyCredentialHints(inputHints, hints);
uint32_t timeout_u32;
(void)aArgs->GetTimeoutMS(&timeout_u32);
DWORD timeout = timeout_u32;
bool privateBrowsing;
(void)aArgs->GetPrivateBrowsing(&privateBrowsing);
BOOL winPrivateBrowsing = FALSE;
if (privateBrowsing) {
winPrivateBrowsing = TRUE;
}
WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS WebAuthNAssertionOptions =
{
WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_8,
timeout,
{0, NULL},
{0, NULL},
winAttachment,
winUserVerificationReq,
0, // dwFlags
winAppIdentifier,
pbAppIdUsed,
&aCancellationId, // CancellationId
pAllowCredentialList,
credLargeBlobOperation, // CredLargeBlobOperation
credLargeBlobSize, // Size of CredLargeBlob
credLargeBlob, // CredLargeBlob
pPrfInputs, // HmacSecretSaltValues
winPrivateBrowsing, // BrowserInPrivateMode
NULL, // LinkedDevice
FALSE, // AutoFill
0, // Size of JsonExt
NULL, // JsonExt
(DWORD)hints.Length(), // Size of CredentialHints
hints.Elements(), // CredentialHints
};
PWEBAUTHN_ASSERTION pWebAuthNAssertion = nullptr;
// Bug 1518876: Get Window Handle from Content process for Windows
// WebAuthN APIs
HWND hWnd = GetForegroundWindow();
HRESULT hr = gWinWebauthnGetAssertion(
hWnd, rpId.get(), &WebAuthNClientData, &WebAuthNAssertionOptions,
&pWebAuthNAssertion);
if (hr == S_OK) {
RefPtr<WebAuthnSignResult> result = new WebAuthnSignResult(
clientDataJSON, credLargeBlobOperation, pWebAuthNAssertion);
gWinWebauthnFreeAssertion(pWebAuthNAssertion);
if (winAppIdentifier != nullptr) {
// The gWinWebauthnGetAssertion call modified bAppIdUsed through
// a pointer provided in WebAuthNAssertionOptions.
(void)result->SetUsedAppId(bAppIdUsed == TRUE);
}
aPromise->Resolve(result);
} 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;
}
aPromise->Reject(aError);
}
}));
NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP
WinWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
const nsAString& aOrigin,
uint64_t* aRv) {
MOZ_ASSERT(NS_IsMainThread());
*aRv = 0;
return NS_OK;
}
NS_IMETHODIMP
WinWebAuthnService::GetAutoFillEntries(
uint64_t aTransactionId, nsIWebAuthnAutoFillEntriesCallback* aCallback) {
MOZ_ASSERT(NS_IsMainThread());
aCallback->Reject(NS_ERROR_NOT_AVAILABLE);
return NS_OK;
}
NS_IMETHODIMP
WinWebAuthnService::GetAutoFillEntriesForRpId(
const nsAString& aRpId, const nsTArray<nsTArray<uint8_t>>& aAllowList,
nsIWebAuthnAutoFillEntriesCallback* aCallback) {
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = EnsureWinWebAuthnModuleLoaded();
if (NS_FAILED(rv)) {
aCallback->Reject(rv);
return NS_OK;
}
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
if (!gWinWebAuthnModule) {
aCallback->Reject(NS_ERROR_NOT_AVAILABLE);
return NS_OK;
}
nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>> entries;
if (gWinWebauthnGetApiVersionNumber() < WEBAUTHN_API_VERSION_4) {
// GetPlatformCredentialList was added in version 4. Earlier versions
// can still present a generic "Use a Passkey" autofill entry, so we
// resolve with an empty list rather than rejecting.
aCallback->Resolve(entries);
return NS_OK;
}
nsString rpId(aRpId);
WEBAUTHN_GET_CREDENTIALS_OPTIONS getCredentialsOptions{
WEBAUTHN_GET_CREDENTIALS_OPTIONS_VERSION_1,
rpId.get(), // pwszRpId
FALSE, // bBrowserInPrivateMode
};
PWEBAUTHN_CREDENTIAL_DETAILS_LIST pCredentialList = nullptr;
HRESULT hr = gWinWebauthnGetPlatformCredentialList(&getCredentialsOptions,
&pCredentialList);
// WebAuthNGetPlatformCredentialList has an _Outptr_result_maybenull_
// annotation and a comment "Returns NTE_NOT_FOUND when credentials are
// not found."
if (pCredentialList == nullptr) {
if (hr != NTE_NOT_FOUND) {
aCallback->Reject(NS_ERROR_FAILURE);
return NS_OK;
}
} else {
MOZ_ASSERT(hr == S_OK);
for (size_t i = 0; i < pCredentialList->cCredentialDetails; i++) {
entries.AppendElement(
new WebAuthnAutoFillEntry(pCredentialList->ppCredentialDetails[i]));
}
gWinWebauthnFreePlatformCredentialList(pCredentialList);
}
aCallback->Resolve(entries);
return NS_OK;
}
NS_IMETHODIMP
WinWebAuthnService::SelectAutoFillEntry(
uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
WinWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
WinWebAuthnService::PinCallback(uint64_t aTransactionId,
const nsACString& aPin) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId,
bool aHasConsent) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::SelectionCallback(uint64_t aTransactionId,
uint64_t aIndex) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::AddVirtualAuthenticator(
const nsACString& aProtocol, const nsACString& aTransport,
bool aHasResidentKey, bool aHasUserVerification, bool aIsUserConsenting,
bool aIsUserVerified, nsACString& aRetval) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::RemoveVirtualAuthenticator(
const nsACString& aAuthenticatorId) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::AddCredential(const nsACString& aAuthenticatorId,
const nsACString& aCredentialId,
bool aIsResidentCredential,
const nsACString& aRpId,
const nsACString& aPrivateKey,
const nsACString& aUserHandle,
uint32_t aSignCount) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::GetCredentials(
const nsACString& aAuthenticatorId,
nsTArray<RefPtr<nsICredentialParameters>>& _aRetval) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::RemoveCredential(const nsACString& aAuthenticatorId,
const nsACString& aCredentialId) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::RemoveAllCredentials(const nsACString& aAuthenticatorId) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::SetUserVerified(const nsACString& aAuthenticatorId,
bool aIsUserVerified) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::Listen() {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
WinWebAuthnService::RunCommand(const nsACString& aCmd) {
MOZ_ASSERT(NS_IsMainThread());
return NS_ERROR_NOT_IMPLEMENTED;
}
} // namespace mozilla::dom