Bug 1857336 - expose an nsIWebAuthnService on Android. r=keeler

Differential Revision: https://phabricator.services.mozilla.com/D190232
This commit is contained in:
John Schanck 2023-10-07 02:08:48 +00:00
parent 93adfae975
commit d5e1715d6b
32 changed files with 940 additions and 1196 deletions

View file

@ -72,8 +72,7 @@ async function addVirtualAuthenticator() {
add_task(async () => { add_task(async () => {
await SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true], await SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
["security.webauth.webauthn_enable_softtoken", true], ["security.webauth.webauthn_enable_softtoken", true],
["security.webauth.webauthn_enable_usbtoken", false], ["security.webauth.webauthn_enable_usbtoken", false]]});
["security.webauth.webauthn_enable_android_fido2", false]]});
await addVirtualAuthenticator(); await addVirtualAuthenticator();
}); });

View file

@ -0,0 +1,384 @@
/* -*- 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/StaticPtr.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/jni/GeckoBundleUtils.h"
#include "AndroidWebAuthnService.h"
#include "JavaBuiltins.h"
#include "JavaExceptions.h"
#include "WebAuthnPromiseHolder.h"
#include "WebAuthnEnumStrings.h"
#include "WebAuthnResult.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/java/WebAuthnTokenManagerWrappers.h"
#include "mozilla/jni/Conversions.h"
namespace mozilla {
namespace jni {
template <>
dom::AndroidWebAuthnError Java2Native(mozilla::jni::Object::Param aData,
JNIEnv* aEnv) {
MOZ_ASSERT(aData.IsInstanceOf<jni::Throwable>());
java::sdk::Throwable::LocalRef throwable(aData);
return dom::AndroidWebAuthnError(throwable->GetMessage()->ToString());
}
} // namespace jni
namespace dom {
NS_IMPL_ISUPPORTS(AndroidWebAuthnService, nsIWebAuthnService)
NS_IMETHODIMP
AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId,
uint64_t browsingContextId,
nsIWebAuthnRegisterArgs* aArgs,
nsIWebAuthnRegisterPromise* aPromise) {
Reset();
GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
"java::WebAuthnTokenManager::WebAuthnMakeCredential",
[aArgs = RefPtr{aArgs}, aPromise = RefPtr{aPromise}]() {
AssertIsOnMainThread();
GECKOBUNDLE_START(credentialBundle);
GECKOBUNDLE_PUT(credentialBundle, "isWebAuthn",
java::sdk::Integer::ValueOf(1));
nsString rpId;
Unused << aArgs->GetRpId(rpId);
GECKOBUNDLE_PUT(credentialBundle, "rpId", jni::StringParam(rpId));
nsString rpName;
Unused << aArgs->GetRpName(rpName);
GECKOBUNDLE_PUT(credentialBundle, "rpName", jni::StringParam(rpName));
nsString userName;
Unused << aArgs->GetUserName(userName);
GECKOBUNDLE_PUT(credentialBundle, "userName",
jni::StringParam(userName));
nsString userDisplayName;
Unused << aArgs->GetUserDisplayName(userDisplayName);
GECKOBUNDLE_PUT(credentialBundle, "userDisplayName",
jni::StringParam(userDisplayName));
nsString origin;
Unused << aArgs->GetOrigin(origin);
GECKOBUNDLE_PUT(credentialBundle, "origin", jni::StringParam(origin));
uint32_t timeout;
Unused << aArgs->GetTimeoutMS(&timeout);
GECKOBUNDLE_PUT(credentialBundle, "timeoutMS",
java::sdk::Double::New(timeout));
GECKOBUNDLE_FINISH(credentialBundle);
nsTArray<uint8_t> userId;
Unused << aArgs->GetUserId(userId);
jni::ByteBuffer::LocalRef uid = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(userId.Elements())),
userId.Length());
nsTArray<uint8_t> challBuf;
nsresult rv = aArgs->GetClientDataHash(challBuf);
if (NS_FAILED(rv)) {
aPromise->Reject(rv);
return;
}
jni::ByteBuffer::LocalRef challenge = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(challBuf.Elements())),
challBuf.Length());
nsTArray<nsTArray<uint8_t>> excludeList;
Unused << aArgs->GetExcludeList(excludeList);
jni::ObjectArray::LocalRef idList =
jni::ObjectArray::New(excludeList.Length());
int ix = 0;
for (const nsTArray<uint8_t>& credId : excludeList) {
jni::ByteBuffer::LocalRef id = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(credId.Elements())),
credId.Length());
idList->SetElement(ix, id);
ix += 1;
}
nsTArray<uint8_t> transportBuf;
/* Bug 1857335 - nsIWebAuthnRegisterArgs doesn't expose the transports
* associated with the allowList entries. They're optional, so it's
* not critical that we include them. */
jni::ByteBuffer::LocalRef transportList = jni::ByteBuffer::New(
const_cast<void*>(
static_cast<const void*>(transportBuf.Elements())),
transportBuf.Length());
GECKOBUNDLE_START(authSelBundle);
// Add UI support to consent to attestation, bug 1550164
GECKOBUNDLE_PUT(authSelBundle, "attestationPreference",
jni::StringParam(u"none"_ns));
nsString residentKey;
Unused << aArgs->GetResidentKey(residentKey);
// Get extensions
bool requestedCredProps;
Unused << aArgs->GetCredProps(&requestedCredProps);
// Unfortunately, GMS's FIDO2 API has no option for Passkey. If using
// residentKey, credential will be synced with Passkey via Google
// account or credential provider service. So this is experimental.
Maybe<bool> credPropsResponse;
if (requestedCredProps &&
StaticPrefs::
security_webauthn_webauthn_enable_android_fido2_residentkey()) {
GECKOBUNDLE_PUT(authSelBundle, "residentKey",
jni::StringParam(residentKey));
bool residentKeyRequired = residentKey.EqualsLiteral(
MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED);
credPropsResponse = Some(residentKeyRequired);
}
nsString userVerification;
Unused << aArgs->GetUserVerification(userVerification);
if (userVerification.EqualsLiteral(
MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) {
GECKOBUNDLE_PUT(authSelBundle, "requireUserVerification",
java::sdk::Integer::ValueOf(1));
}
nsString authenticatorAttachment;
rv = aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
if (rv != NS_ERROR_NOT_AVAILABLE) {
if (NS_FAILED(rv)) {
aPromise->Reject(rv);
return;
}
if (authenticatorAttachment.EqualsLiteral(
MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM)) {
GECKOBUNDLE_PUT(authSelBundle, "requirePlatformAttachment",
java::sdk::Integer::ValueOf(1));
} else if (
authenticatorAttachment.EqualsLiteral(
MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM)) {
GECKOBUNDLE_PUT(authSelBundle, "requireCrossPlatformAttachment",
java::sdk::Integer::ValueOf(1));
}
}
GECKOBUNDLE_FINISH(authSelBundle);
GECKOBUNDLE_START(extensionsBundle);
GECKOBUNDLE_FINISH(extensionsBundle);
auto result = java::WebAuthnTokenManager::WebAuthnMakeCredential(
credentialBundle, uid, challenge, idList, transportList,
authSelBundle, extensionsBundle);
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
MozPromise<RefPtr<WebAuthnRegisterResult>, AndroidWebAuthnError,
true>::FromGeckoResult(geckoResult)
->Then(
GetCurrentSerialEventTarget(), __func__,
[aPromise, credPropsResponse = std::move(credPropsResponse)](
RefPtr<WebAuthnRegisterResult>&& aValue) {
if (credPropsResponse.isSome()) {
Unused << aValue->SetCredPropsRk(credPropsResponse.ref());
}
aPromise->Resolve(aValue);
},
[aPromise](AndroidWebAuthnError&& aValue) {
aPromise->Reject(aValue.GetError());
});
}));
return NS_OK;
}
NS_IMETHODIMP
AndroidWebAuthnService::GetAssertion(uint64_t aTransactionId,
uint64_t browsingContextId,
nsIWebAuthnSignArgs* aArgs,
nsIWebAuthnSignPromise* aPromise) {
Reset();
GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
"java::WebAuthnTokenManager::WebAuthnGetAssertion",
[self = RefPtr{this}, aArgs = RefPtr{aArgs},
aPromise = RefPtr{aPromise}]() {
AssertIsOnMainThread();
nsTArray<uint8_t> challBuf;
nsresult rv = aArgs->GetClientDataHash(challBuf);
if (NS_FAILED(rv)) {
aPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
return;
}
jni::ByteBuffer::LocalRef challenge = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(challBuf.Elements())),
challBuf.Length());
nsTArray<nsTArray<uint8_t>> allowList;
Unused << aArgs->GetAllowList(allowList);
jni::ObjectArray::LocalRef idList =
jni::ObjectArray::New(allowList.Length());
int ix = 0;
for (const nsTArray<uint8_t>& credId : allowList) {
jni::ByteBuffer::LocalRef id = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(credId.Elements())),
credId.Length());
idList->SetElement(ix, id);
ix += 1;
}
nsTArray<uint8_t> transportBuf;
/* Bug 1857335 - nsIWebAuthnSignArgs doesn't expose the transports
* associated with the allowList entries. They're optional, so it's
* not critical that we include them. */
jni::ByteBuffer::LocalRef transportList = jni::ByteBuffer::New(
const_cast<void*>(
static_cast<const void*>(transportBuf.Elements())),
transportBuf.Length());
GECKOBUNDLE_START(assertionBundle);
GECKOBUNDLE_PUT(assertionBundle, "isWebAuthn",
java::sdk::Integer::ValueOf(1));
nsString rpId;
Unused << aArgs->GetRpId(rpId);
GECKOBUNDLE_PUT(assertionBundle, "rpId", jni::StringParam(rpId));
nsString origin;
Unused << aArgs->GetOrigin(origin);
GECKOBUNDLE_PUT(assertionBundle, "origin", jni::StringParam(origin));
uint32_t timeout;
Unused << aArgs->GetTimeoutMS(&timeout);
GECKOBUNDLE_PUT(assertionBundle, "timeoutMS",
java::sdk::Double::New(timeout));
// User Verification Requirement is not currently used in the
// Android FIDO API.
GECKOBUNDLE_FINISH(assertionBundle);
GECKOBUNDLE_START(extensionsBundle);
nsString appId;
rv = aArgs->GetAppId(appId);
if (rv != NS_ERROR_NOT_AVAILABLE) {
if (NS_FAILED(rv)) {
aPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
return;
}
GECKOBUNDLE_PUT(extensionsBundle, "fidoAppId",
jni::StringParam(appId));
}
GECKOBUNDLE_FINISH(extensionsBundle);
auto result = java::WebAuthnTokenManager::WebAuthnGetAssertion(
challenge, idList, transportList, assertionBundle,
extensionsBundle);
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
MozPromise<RefPtr<WebAuthnSignResult>, AndroidWebAuthnError,
true>::FromGeckoResult(geckoResult)
->Then(
GetCurrentSerialEventTarget(), __func__,
[aPromise](RefPtr<WebAuthnSignResult>&& aValue) {
aPromise->Resolve(aValue);
},
[aPromise](AndroidWebAuthnError&& aValue) {
aPromise->Reject(aValue.GetError());
});
}));
return NS_OK;
}
NS_IMETHODIMP
AndroidWebAuthnService::Reset() {
mRegisterCredPropsRk = Nothing();
return NS_OK;
}
NS_IMETHODIMP
AndroidWebAuthnService::Cancel(uint64_t aTransactionId) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::PinCallback(uint64_t aTransactionId,
const nsACString& aPin) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::ResumeMakeCredential(uint64_t aTransactionId,
bool aForceNoneAttestation) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::SelectionCallback(uint64_t aTransactionId,
uint64_t aIndex) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::AddVirtualAuthenticator(
const nsACString& protocol, const nsACString& transport,
bool hasResidentKey, bool hasUserVerification, bool isUserConsenting,
bool isUserVerified, uint64_t* _retval) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::AddCredential(uint64_t authenticatorId,
const nsACString& credentialId,
bool isResidentCredential,
const nsACString& rpId,
const nsACString& privateKey,
const nsACString& userHandle,
uint32_t signCount) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::GetCredentials(
uint64_t authenticatorId,
nsTArray<RefPtr<nsICredentialParameters>>& _retval) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::RemoveCredential(uint64_t authenticatorId,
const nsACString& credentialId) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::RemoveAllCredentials(uint64_t authenticatorId) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AndroidWebAuthnService::SetUserVerified(uint64_t authenticatorId,
bool isUserVerified) {
return NS_ERROR_NOT_IMPLEMENTED;
}
} // namespace dom
} // namespace mozilla

View file

@ -4,13 +4,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_AndroidWebAuthnTokenManager_h #ifndef mozilla_dom_AndroidWebAuthnService_h_
#define mozilla_dom_AndroidWebAuthnTokenManager_h #define mozilla_dom_AndroidWebAuthnService_h_
#include "mozilla/dom/U2FTokenTransport.h"
#include "mozilla/java/WebAuthnTokenManagerNatives.h" #include "mozilla/java/WebAuthnTokenManagerNatives.h"
#include "nsIWebAuthnService.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
// Collected from // Collected from
@ -27,27 +28,13 @@ constexpr auto kTimeoutError = u"TIMEOUT_ERR"_ns;
constexpr auto kNetworkError = u"NETWORK_ERR"_ns; constexpr auto kNetworkError = u"NETWORK_ERR"_ns;
constexpr auto kUnknownError = u"UNKNOWN_ERR"_ns; constexpr auto kUnknownError = u"UNKNOWN_ERR"_ns;
class AndroidWebAuthnResult { class AndroidWebAuthnError {
public: public:
explicit AndroidWebAuthnResult(const nsAString& aErrorCode) explicit AndroidWebAuthnError(const nsAString& aErrorCode)
: mErrorCode(aErrorCode) {} : mErrorCode(aErrorCode) {}
explicit AndroidWebAuthnResult(
const java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef&
aResponse);
explicit AndroidWebAuthnResult(
const java::WebAuthnTokenManager::GetAssertionResponse::LocalRef&
aResponse);
AndroidWebAuthnResult() = delete;
bool IsError() const { return NS_FAILED(GetError()); }
nsresult GetError() const { nsresult GetError() const {
if (mErrorCode.IsEmpty()) { if (mErrorCode.Equals(kSecurityError)) {
return NS_OK;
} else if (mErrorCode.Equals(kSecurityError)) {
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
} else if (mErrorCode.Equals(kConstraintError)) { } else if (mErrorCode.Equals(kConstraintError)) {
// TODO: The message is right, but it's not about indexeddb. // TODO: The message is right, but it's not about indexeddb.
@ -79,63 +66,19 @@ class AndroidWebAuthnResult {
} }
} }
AndroidWebAuthnResult(const AndroidWebAuthnResult&) = delete;
AndroidWebAuthnResult(AndroidWebAuthnResult&&) = default;
// Attestation-only
nsTArray<uint8_t> mAttObj;
nsTArray<nsString> mTransports;
// Attestations and assertions
nsTArray<uint8_t> mKeyHandle;
nsCString mClientDataJSON;
// Assertions-only
nsTArray<uint8_t> mAuthData;
nsTArray<uint8_t> mSignature;
nsTArray<uint8_t> mUserHandle;
private: private:
const nsString mErrorCode; const nsString mErrorCode;
}; };
/* class AndroidWebAuthnService final : public nsIWebAuthnService {
* WebAuthnAndroidTokenManager is a token implementation communicating with
* Android Fido2 APIs.
*/
class AndroidWebAuthnTokenManager final : public U2FTokenTransport {
public: public:
explicit AndroidWebAuthnTokenManager(); NS_DECL_THREADSAFE_ISUPPORTS
~AndroidWebAuthnTokenManager() {} NS_DECL_NSIWEBAUTHNSERVICE
virtual RefPtr<U2FRegisterPromise> Register( AndroidWebAuthnService() = default;
const WebAuthnMakeCredentialInfo& aInfo,
bool aForceNoneAttestation) override;
virtual RefPtr<U2FSignPromise> Sign(
const WebAuthnGetAssertionInfo& aInfo) override;
void Cancel() override;
void Drop() override;
static AndroidWebAuthnTokenManager* GetInstance();
private: private:
void HandleRegisterResult(AndroidWebAuthnResult&& aResult); ~AndroidWebAuthnService() = default;
void HandleSignResult(AndroidWebAuthnResult&& aResult);
void ClearPromises() {
mRegisterPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
mRegisterCredPropsRk = Nothing();
}
void AssertIsOnOwningThread() const;
MozPromiseHolder<U2FRegisterPromise> mRegisterPromise;
MozPromiseHolder<U2FSignPromise> mSignPromise;
// The Android FIDO2 API doesn't accept the credProps extension. However, the // The Android FIDO2 API doesn't accept the credProps extension. However, the
// appropriate value for CredentialPropertiesOutput.rk can be determined // appropriate value for CredentialPropertiesOutput.rk can be determined
@ -147,4 +90,4 @@ class AndroidWebAuthnTokenManager final : public U2FTokenTransport {
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla
#endif // mozilla_dom_AndroidWebAuthnTokenManager_h #endif // mozilla_dom_AndroidWebAuthnService_h_

View file

@ -1,467 +0,0 @@
/* -*- 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/ipc/BackgroundParent.h"
#include "mozilla/jni/GeckoBundleUtils.h"
#include "mozilla/StaticPtr.h"
#include "AndroidWebAuthnTokenManager.h"
#include "JavaBuiltins.h"
#include "JavaExceptions.h"
#include "mozilla/java/WebAuthnTokenManagerWrappers.h"
#include "mozilla/jni/Conversions.h"
#include "mozilla/StaticPrefs_security.h"
#include "WebAuthnEnumStrings.h"
namespace mozilla {
namespace jni {
template <>
dom::AndroidWebAuthnResult Java2Native(mozilla::jni::Object::Param aData,
JNIEnv* aEnv) {
// TODO:
// AndroidWebAuthnResult stores successful both result and failure result.
// We should split it into success and failure (Bug 1754157)
if (aData.IsInstanceOf<jni::Throwable>()) {
java::sdk::Throwable::LocalRef throwable(aData);
return dom::AndroidWebAuthnResult(throwable->GetMessage()->ToString());
}
if (aData
.IsInstanceOf<java::WebAuthnTokenManager::MakeCredentialResponse>()) {
java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef response(
aData);
return dom::AndroidWebAuthnResult(response);
}
MOZ_ASSERT(
aData.IsInstanceOf<java::WebAuthnTokenManager::GetAssertionResponse>());
java::WebAuthnTokenManager::GetAssertionResponse::LocalRef response(aData);
return dom::AndroidWebAuthnResult(response);
}
} // namespace jni
namespace dom {
static nsIThread* gAndroidPBackgroundThread;
StaticRefPtr<AndroidWebAuthnTokenManager> gAndroidWebAuthnManager;
/* static */ AndroidWebAuthnTokenManager*
AndroidWebAuthnTokenManager::GetInstance() {
if (!gAndroidWebAuthnManager) {
mozilla::ipc::AssertIsOnBackgroundThread();
gAndroidWebAuthnManager = new AndroidWebAuthnTokenManager();
}
return gAndroidWebAuthnManager;
}
AndroidWebAuthnTokenManager::AndroidWebAuthnTokenManager() {
mozilla::ipc::AssertIsOnBackgroundThread();
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(!gAndroidWebAuthnManager);
gAndroidPBackgroundThread = NS_GetCurrentThread();
MOZ_ASSERT(gAndroidPBackgroundThread, "This should never be null!");
gAndroidWebAuthnManager = this;
}
void AndroidWebAuthnTokenManager::AssertIsOnOwningThread() const {
mozilla::ipc::AssertIsOnBackgroundThread();
MOZ_ASSERT(gAndroidPBackgroundThread);
#ifdef DEBUG
bool current;
MOZ_ASSERT(
NS_SUCCEEDED(gAndroidPBackgroundThread->IsOnCurrentThread(&current)));
MOZ_ASSERT(current);
#endif
}
void AndroidWebAuthnTokenManager::Drop() {
AssertIsOnOwningThread();
ClearPromises();
gAndroidWebAuthnManager = nullptr;
gAndroidPBackgroundThread = nullptr;
}
RefPtr<U2FRegisterPromise> AndroidWebAuthnTokenManager::Register(
const WebAuthnMakeCredentialInfo& aInfo, bool aForceNoneAttestation) {
AssertIsOnOwningThread();
ClearPromises();
GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
"java::WebAuthnTokenManager::WebAuthnMakeCredential",
[self = RefPtr{this}, aInfo, aForceNoneAttestation]() {
AssertIsOnMainThread();
// Produce the credential exclusion list
jni::ObjectArray::LocalRef idList =
jni::ObjectArray::New(aInfo.ExcludeList().Length());
nsTArray<uint8_t> transportBuf;
int ix = 0;
for (const WebAuthnScopedCredential& cred : aInfo.ExcludeList()) {
jni::ByteBuffer::LocalRef id = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(cred.id().Elements())),
cred.id().Length());
idList->SetElement(ix, id);
transportBuf.AppendElement(cred.transports());
ix += 1;
}
jni::ByteBuffer::LocalRef transportList = jni::ByteBuffer::New(
const_cast<void*>(
static_cast<const void*>(transportBuf.Elements())),
transportBuf.Length());
const nsTArray<uint8_t>& challBuf = aInfo.Challenge();
jni::ByteBuffer::LocalRef challenge = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(challBuf.Elements())),
challBuf.Length());
nsTArray<uint8_t> uidBuf;
// Get authenticator selection criteria
GECKOBUNDLE_START(authSelBundle);
GECKOBUNDLE_START(extensionsBundle);
GECKOBUNDLE_START(credentialBundle);
const auto& rp = aInfo.Rp();
const auto& user = aInfo.User();
GECKOBUNDLE_PUT(credentialBundle, "isWebAuthn",
java::sdk::Integer::ValueOf(1));
// Get the attestation preference and override if the user asked
if (aForceNoneAttestation) {
// Add UI support to trigger this, bug 1550164
GECKOBUNDLE_PUT(authSelBundle, "attestationPreference",
jni::StringParam(u"none"_ns));
} else {
const nsString& attestation = aInfo.attestationConveyancePreference();
GECKOBUNDLE_PUT(authSelBundle, "attestationPreference",
jni::StringParam(attestation));
}
const WebAuthnAuthenticatorSelection& sel =
aInfo.AuthenticatorSelection();
// Get extensions
bool requestedCredProps = false;
for (const WebAuthnExtension& ext : aInfo.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionCredProps) {
requestedCredProps =
ext.get_WebAuthnExtensionCredProps().credProps();
}
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
GECKOBUNDLE_PUT(
extensionsBundle, "fidoAppId",
jni::StringParam(
ext.get_WebAuthnExtensionAppId().appIdentifier()));
}
}
// Unfortunately, GMS's FIDO2 API has no option for Passkey. If using
// residentKey, credential will be synced with Passkey via Google
// account or credential provider service. So this is experimental.
if (StaticPrefs::
security_webauthn_webauthn_enable_android_fido2_residentkey()) {
GECKOBUNDLE_PUT(authSelBundle, "residentKey",
jni::StringParam(sel.residentKey()));
if (requestedCredProps) {
// In WebAuthnTokenManager.java we set the "requireResidentKey"
// parameter to true if and only if "residentKey" here is
// "required". This determines the credProps extension output.
self->mRegisterCredPropsRk.emplace(sel.residentKey().EqualsLiteral(
MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED));
}
}
if (sel.userVerificationRequirement().EqualsLiteral(
MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) {
GECKOBUNDLE_PUT(authSelBundle, "requireUserVerification",
java::sdk::Integer::ValueOf(1));
}
if (sel.authenticatorAttachment().isSome()) {
const nsString& authenticatorAttachment =
sel.authenticatorAttachment().value();
if (authenticatorAttachment.EqualsLiteral(
MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM)) {
GECKOBUNDLE_PUT(authSelBundle, "requirePlatformAttachment",
java::sdk::Integer::ValueOf(1));
} else if (
authenticatorAttachment.EqualsLiteral(
MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM)) {
GECKOBUNDLE_PUT(authSelBundle, "requireCrossPlatformAttachment",
java::sdk::Integer::ValueOf(1));
}
}
uidBuf.Assign(user.Id());
GECKOBUNDLE_PUT(credentialBundle, "rpName",
jni::StringParam(rp.Name()));
GECKOBUNDLE_PUT(credentialBundle, "userName",
jni::StringParam(user.Name()));
GECKOBUNDLE_PUT(credentialBundle, "userDisplayName",
jni::StringParam(user.DisplayName()));
GECKOBUNDLE_PUT(credentialBundle, "rpId",
jni::StringParam(aInfo.RpId()));
GECKOBUNDLE_PUT(credentialBundle, "origin",
jni::StringParam(aInfo.Origin()));
GECKOBUNDLE_PUT(credentialBundle, "timeoutMS",
java::sdk::Double::New(aInfo.TimeoutMS()));
GECKOBUNDLE_FINISH(authSelBundle);
GECKOBUNDLE_FINISH(extensionsBundle);
GECKOBUNDLE_FINISH(credentialBundle);
// For non-WebAuthn cases, uidBuf is empty (and unused)
jni::ByteBuffer::LocalRef uid = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(uidBuf.Elements())),
uidBuf.Length());
auto result = java::WebAuthnTokenManager::WebAuthnMakeCredential(
credentialBundle, uid, challenge, idList, transportList,
authSelBundle, extensionsBundle);
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
// This is likely running on the main thread, so we'll always dispatch
// to the background for state updates.
MozPromise<AndroidWebAuthnResult, AndroidWebAuthnResult,
true>::FromGeckoResult(geckoResult)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self = std::move(self)](AndroidWebAuthnResult&& aValue) {
self->HandleRegisterResult(std::move(aValue));
},
[self = std::move(self)](AndroidWebAuthnResult&& aValue) {
self->HandleRegisterResult(std::move(aValue));
});
}));
return mRegisterPromise.Ensure(__func__);
}
void AndroidWebAuthnTokenManager::HandleRegisterResult(
AndroidWebAuthnResult&& aResult) {
if (!gAndroidPBackgroundThread) {
// Promise is already rejected when shutting down background thread
return;
}
// This is likely running on the main thread, so we'll always dispatch to the
// background for state updates.
if (aResult.IsError()) {
nsresult aError = aResult.GetError();
gAndroidPBackgroundThread->Dispatch(NS_NewRunnableFunction(
"AndroidWebAuthnTokenManager::RegisterAbort",
[self = RefPtr<AndroidWebAuthnTokenManager>(this), aError]() {
self->mRegisterPromise.RejectIfExists(aError, __func__);
}));
} else {
gAndroidPBackgroundThread->Dispatch(NS_NewRunnableFunction(
"AndroidWebAuthnTokenManager::RegisterComplete",
[self = RefPtr<AndroidWebAuthnTokenManager>(this),
aResult = std::move(aResult)]() {
nsTArray<WebAuthnExtensionResult> extensions;
if (self->mRegisterCredPropsRk.isSome()) {
extensions.AppendElement(WebAuthnExtensionResultCredProps(
self->mRegisterCredPropsRk.value()));
}
WebAuthnMakeCredentialResult result(
aResult.mClientDataJSON, aResult.mAttObj, aResult.mKeyHandle,
aResult.mTransports, extensions);
self->mRegisterPromise.Resolve(std::move(result), __func__);
}));
}
}
RefPtr<U2FSignPromise> AndroidWebAuthnTokenManager::Sign(
const WebAuthnGetAssertionInfo& aInfo) {
AssertIsOnOwningThread();
ClearPromises();
GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
"java::WebAuthnTokenManager::WebAuthnGetAssertion",
[self = RefPtr{this}, aInfo]() {
AssertIsOnMainThread();
jni::ObjectArray::LocalRef idList =
jni::ObjectArray::New(aInfo.AllowList().Length());
nsTArray<uint8_t> transportBuf;
int ix = 0;
for (const WebAuthnScopedCredential& cred : aInfo.AllowList()) {
jni::ByteBuffer::LocalRef id = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(cred.id().Elements())),
cred.id().Length());
idList->SetElement(ix, id);
transportBuf.AppendElement(cred.transports());
ix += 1;
}
jni::ByteBuffer::LocalRef transportList = jni::ByteBuffer::New(
const_cast<void*>(
static_cast<const void*>(transportBuf.Elements())),
transportBuf.Length());
const nsTArray<uint8_t>& challBuf = aInfo.Challenge();
jni::ByteBuffer::LocalRef challenge = jni::ByteBuffer::New(
const_cast<void*>(static_cast<const void*>(challBuf.Elements())),
challBuf.Length());
// Get extensions
GECKOBUNDLE_START(assertionBundle);
GECKOBUNDLE_START(extensionsBundle);
GECKOBUNDLE_PUT(assertionBundle, "isWebAuthn",
java::sdk::Integer::ValueOf(1));
// User Verification Requirement is not currently used in the
// Android FIDO API. Adding it should look like
// AttestationConveyancePreference
for (const WebAuthnExtension& ext : aInfo.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
GECKOBUNDLE_PUT(
extensionsBundle, "fidoAppId",
jni::StringParam(
ext.get_WebAuthnExtensionAppId().appIdentifier()));
}
}
GECKOBUNDLE_PUT(assertionBundle, "rpId",
jni::StringParam(aInfo.RpId()));
GECKOBUNDLE_PUT(assertionBundle, "origin",
jni::StringParam(aInfo.Origin()));
GECKOBUNDLE_PUT(assertionBundle, "timeoutMS",
java::sdk::Double::New(aInfo.TimeoutMS()));
GECKOBUNDLE_FINISH(assertionBundle);
GECKOBUNDLE_FINISH(extensionsBundle);
auto result = java::WebAuthnTokenManager::WebAuthnGetAssertion(
challenge, idList, transportList, assertionBundle,
extensionsBundle);
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
MozPromise<AndroidWebAuthnResult, AndroidWebAuthnResult,
true>::FromGeckoResult(geckoResult)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self = std::move(self)](AndroidWebAuthnResult&& aValue) {
self->HandleSignResult(std::move(aValue));
},
[self = std::move(self)](AndroidWebAuthnResult&& aValue) {
self->HandleSignResult(std::move(aValue));
});
}));
return mSignPromise.Ensure(__func__);
}
void AndroidWebAuthnTokenManager::HandleSignResult(
AndroidWebAuthnResult&& aResult) {
if (!gAndroidPBackgroundThread) {
// Promise is already rejected when shutting down background thread
return;
}
// This is likely running on the main thread, so we'll always dispatch to the
// background for state updates.
if (aResult.IsError()) {
nsresult aError = aResult.GetError();
gAndroidPBackgroundThread->Dispatch(NS_NewRunnableFunction(
"AndroidWebAuthnTokenManager::SignAbort",
[self = RefPtr<AndroidWebAuthnTokenManager>(this), aError]() {
self->mSignPromise.RejectIfExists(aError, __func__);
}));
} else {
gAndroidPBackgroundThread->Dispatch(NS_NewRunnableFunction(
"AndroidWebAuthnTokenManager::SignComplete",
[self = RefPtr<AndroidWebAuthnTokenManager>(this),
aResult = std::move(aResult)]() {
nsTArray<WebAuthnExtensionResult> emptyExtensions;
WebAuthnGetAssertionResult result(
aResult.mClientDataJSON, aResult.mKeyHandle, aResult.mSignature,
aResult.mAuthData, emptyExtensions, aResult.mUserHandle);
nsTArray<WebAuthnGetAssertionResultWrapper> results = {
{result, mozilla::Nothing()}};
self->mSignPromise.Resolve(std::move(results), __func__);
}));
}
}
void AndroidWebAuthnTokenManager::Cancel() {
AssertIsOnOwningThread();
ClearPromises();
}
AndroidWebAuthnResult::AndroidWebAuthnResult(
const java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef&
aResponse) {
mClientDataJSON.Assign(
reinterpret_cast<const char*>(
aResponse->ClientDataJson()->GetElements().Elements()),
aResponse->ClientDataJson()->Length());
mKeyHandle.Clear();
mKeyHandle.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->KeyHandle()->GetElements().Elements()),
aResponse->KeyHandle()->Length());
mAttObj.Clear();
mAttObj.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->AttestationObject()->GetElements().Elements()),
aResponse->AttestationObject()->Length());
auto transports = aResponse->Transports();
for (size_t i = 0; i < transports->Length(); i++) {
mTransports.AppendElement(
jni::String::LocalRef(transports->GetElement(i))->ToString());
}
}
AndroidWebAuthnResult::AndroidWebAuthnResult(
const java::WebAuthnTokenManager::GetAssertionResponse::LocalRef&
aResponse) {
mClientDataJSON.Assign(
reinterpret_cast<const char*>(
aResponse->ClientDataJson()->GetElements().Elements()),
aResponse->ClientDataJson()->Length());
mKeyHandle.Clear();
mKeyHandle.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->KeyHandle()->GetElements().Elements()),
aResponse->KeyHandle()->Length());
mAuthData.Clear();
mAuthData.AppendElements(reinterpret_cast<uint8_t*>(
aResponse->AuthData()->GetElements().Elements()),
aResponse->AuthData()->Length());
mSignature.Clear();
mSignature.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->Signature()->GetElements().Elements()),
aResponse->Signature()->Length());
mUserHandle.Clear();
mUserHandle.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->UserHandle()->GetElements().Elements()),
aResponse->UserHandle()->Length());
}
} // namespace dom
} // namespace mozilla

View file

@ -1,21 +0,0 @@
/* 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 "AuthrsService.h"
#include "AuthrsBridge_ffi.h"
#include "nsIWebAuthnService.h"
#include "nsCOMPtr.h"
namespace mozilla::dom {
already_AddRefed<nsIWebAuthnService> NewAuthrsService() {
nsCOMPtr<nsIWebAuthnService> webauthnService;
nsresult rv = authrs_service_constructor(getter_AddRefs(webauthnService));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return webauthnService.forget();
}
} // namespace mozilla::dom

View file

@ -1,17 +0,0 @@
/* 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/. */
#ifndef DOM_WEBAUTHN_AUTHRS_BRIDGE_H_
#define DOM_WEBAUTHN_AUTHRS_BRIDGE_H_
#include "mozilla/AlreadyAddRefed.h"
#include "nsIWebAuthnService.h"
namespace mozilla::dom {
already_AddRefed<nsIWebAuthnService> NewAuthrsService();
} // namespace mozilla::dom
#endif // DOM_WEBAUTHN_AUTHRS_BRIDGE_H_

View file

@ -1,254 +0,0 @@
/* -*- 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 "json/json.h"
#include "mozilla/dom/U2FTokenManager.h"
#include "mozilla/dom/U2FTokenTransport.h"
#include "mozilla/dom/PWebAuthnTransactionParent.h"
#include "mozilla/MozPromise.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Services.h"
#include "mozilla/Unused.h"
#include "nsEscape.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIThread.h"
#include "nsTextFormatter.h"
#include "mozilla/Telemetry.h"
#include "WebAuthnEnumStrings.h"
#include "mozilla/dom/AndroidWebAuthnTokenManager.h"
namespace mozilla::dom {
/***********************************************************************
* Statics
**********************************************************************/
namespace {
static mozilla::LazyLogModule gU2FTokenManagerLog("u2fkeymanager");
StaticAutoPtr<U2FTokenManager> gU2FTokenManager;
static nsIThread* gBackgroundThread;
} // namespace
/***********************************************************************
* U2FManager Implementation
**********************************************************************/
U2FTokenManager::U2FTokenManager()
: mTransactionParent(nullptr), mLastTransactionId(0) {
MOZ_ASSERT(XRE_IsParentProcess());
// Create on the main thread to make sure ClearOnShutdown() works.
MOZ_ASSERT(NS_IsMainThread());
}
// static
void U2FTokenManager::Initialize() {
if (!XRE_IsParentProcess()) {
return;
}
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!gU2FTokenManager);
gU2FTokenManager = new U2FTokenManager();
ClearOnShutdown(&gU2FTokenManager);
}
// static
U2FTokenManager* U2FTokenManager::Get() {
MOZ_ASSERT(XRE_IsParentProcess());
// We should only be accessing this on the background thread
MOZ_ASSERT(!NS_IsMainThread());
return gU2FTokenManager;
}
void U2FTokenManager::AbortTransaction(const uint64_t& aTransactionId,
const nsresult& aError) {
Unused << mTransactionParent->SendAbort(aTransactionId, aError);
ClearTransaction();
}
void U2FTokenManager::AbortOngoingTransaction() {
if (mLastTransactionId > 0 && mTransactionParent) {
// Send an abort to any other ongoing transaction
Unused << mTransactionParent->SendAbort(mLastTransactionId,
NS_ERROR_DOM_ABORT_ERR);
}
ClearTransaction();
}
void U2FTokenManager::MaybeClearTransaction(
PWebAuthnTransactionParent* aParent) {
// Only clear if we've been requested to do so by our current transaction
// parent.
if (mTransactionParent == aParent) {
ClearTransaction();
}
}
void U2FTokenManager::ClearTransaction() {
mTransactionParent = nullptr;
// Drop managers at the end of all transactions
if (mTokenManagerImpl) {
mTokenManagerImpl->Drop();
mTokenManagerImpl = nullptr;
}
// Forget promises, if necessary.
mRegisterPromise.DisconnectIfExists();
mSignPromise.DisconnectIfExists();
// Clear transaction id.
mLastTransactionId = 0;
}
RefPtr<U2FTokenTransport> U2FTokenManager::GetTokenManagerImpl() {
mozilla::ipc::AssertIsOnBackgroundThread();
if (mTokenManagerImpl) {
return mTokenManagerImpl;
}
if (!gBackgroundThread) {
gBackgroundThread = NS_GetCurrentThread();
MOZ_ASSERT(gBackgroundThread, "This should never be null!");
}
return AndroidWebAuthnTokenManager::GetInstance();
}
void U2FTokenManager::Register(
PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId,
const WebAuthnMakeCredentialInfo& aTransactionInfo) {
MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister"));
AbortOngoingTransaction();
mTransactionParent = aTransactionParent;
mTokenManagerImpl = GetTokenManagerImpl();
if (!mTokenManagerImpl) {
AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR);
return;
}
mLastTransactionId = aTransactionId;
mozilla::ipc::AssertIsOnBackgroundThread();
MOZ_ASSERT(mLastTransactionId > 0);
uint64_t tid = mLastTransactionId;
mTokenManagerImpl
->Register(aTransactionInfo, /* aForceNoneAttestation */ true)
->Then(
GetCurrentSerialEventTarget(), __func__,
[tid](WebAuthnMakeCredentialResult&& aResult) {
Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
u"U2FRegisterFinish"_ns, 1);
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeConfirmRegister(tid, aResult);
},
[tid](nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv));
U2FTokenManager* mgr = U2FTokenManager::Get();
Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
u"U2FRegisterAbort"_ns, 1);
mgr->MaybeAbortRegister(tid, rv);
})
->Track(mRegisterPromise);
}
void U2FTokenManager::MaybeConfirmRegister(
const uint64_t& aTransactionId,
const WebAuthnMakeCredentialResult& aResult) {
MOZ_ASSERT(mLastTransactionId == aTransactionId);
mRegisterPromise.Complete();
Unused << mTransactionParent->SendConfirmRegister(aTransactionId, aResult);
ClearTransaction();
}
void U2FTokenManager::MaybeAbortRegister(const uint64_t& aTransactionId,
const nsresult& aError) {
MOZ_ASSERT(mLastTransactionId == aTransactionId);
mRegisterPromise.Complete();
AbortTransaction(aTransactionId, aError);
}
void U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId,
const WebAuthnGetAssertionInfo& aTransactionInfo) {
MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthSign"));
AbortOngoingTransaction();
mTransactionParent = aTransactionParent;
mTokenManagerImpl = GetTokenManagerImpl();
if (!mTokenManagerImpl) {
AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR);
return;
}
mLastTransactionId = aTransactionId;
mozilla::ipc::AssertIsOnBackgroundThread();
MOZ_ASSERT(mLastTransactionId > 0);
uint64_t tid = mLastTransactionId;
mTokenManagerImpl->Sign(aTransactionInfo)
->Then(
GetCurrentSerialEventTarget(), __func__,
[tid](nsTArray<WebAuthnGetAssertionResultWrapper>&& aResult) {
U2FTokenManager* mgr = U2FTokenManager::Get();
Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
u"U2FSignFinish"_ns, 1);
if (aResult.Length() == 1) {
WebAuthnGetAssertionResult result = aResult[0].assertion;
mgr->MaybeConfirmSign(tid, result);
}
},
[tid](nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv));
U2FTokenManager* mgr = U2FTokenManager::Get();
Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
u"U2FSignAbort"_ns, 1);
mgr->MaybeAbortSign(tid, rv);
})
->Track(mSignPromise);
}
void U2FTokenManager::MaybeConfirmSign(
const uint64_t& aTransactionId, const WebAuthnGetAssertionResult& aResult) {
MOZ_ASSERT(mLastTransactionId == aTransactionId);
mSignPromise.Complete();
Unused << mTransactionParent->SendConfirmSign(aTransactionId, aResult);
ClearTransaction();
}
void U2FTokenManager::MaybeAbortSign(const uint64_t& aTransactionId,
const nsresult& aError) {
MOZ_ASSERT(mLastTransactionId == aTransactionId);
mSignPromise.Complete();
AbortTransaction(aTransactionId, aError);
}
void U2FTokenManager::Cancel(PWebAuthnTransactionParent* aParent,
const Tainted<uint64_t>& aTransactionId) {
// The last transaction ID also suffers from the issue described in Bug
// 1696159. A content process could cancel another content processes
// transaction by guessing the last transaction ID.
if (mTransactionParent != aParent ||
!MOZ_IS_VALID(aTransactionId, mLastTransactionId == aTransactionId)) {
return;
}
mTokenManagerImpl->Cancel();
ClearTransaction();
}
} // namespace mozilla::dom

View file

@ -1,73 +0,0 @@
/* -*- 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/. */
#ifndef mozilla_dom_U2FTokenManager_h
#define mozilla_dom_U2FTokenManager_h
#include "mozilla/dom/U2FTokenTransport.h"
#include "mozilla/dom/PWebAuthnTransaction.h"
#include "mozilla/Tainting.h"
/*
* Parent process manager for U2F and WebAuthn API transactions. Handles process
* transactions from all content processes, make sure only one transaction is
* live at any time. Manages access to hardware and software based key systems.
*
* U2FTokenManager is created on the first access to functions of either the U2F
* or WebAuthn APIs that require key registration or signing. It lives until the
* end of the browser process.
*/
namespace mozilla::dom {
class U2FSoftTokenManager;
class WebAuthnTransactionParent;
class U2FTokenManager final {
public:
static U2FTokenManager* Get();
void Register(PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId,
const WebAuthnMakeCredentialInfo& aTransactionInfo);
void Sign(PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId,
const WebAuthnGetAssertionInfo& aTransactionInfo);
void Cancel(PWebAuthnTransactionParent* aTransactionParent,
const Tainted<uint64_t>& aTransactionId);
void MaybeClearTransaction(PWebAuthnTransactionParent* aParent);
static void Initialize();
U2FTokenManager();
~U2FTokenManager() = default;
private:
RefPtr<U2FTokenTransport> GetTokenManagerImpl();
void AbortTransaction(const uint64_t& aTransactionId, const nsresult& aError);
void AbortOngoingTransaction();
void ClearTransaction();
void MaybeConfirmRegister(const uint64_t& aTransactionId,
const WebAuthnMakeCredentialResult& aResult);
void MaybeAbortRegister(const uint64_t& aTransactionId,
const nsresult& aError);
void MaybeConfirmSign(const uint64_t& aTransactionId,
const WebAuthnGetAssertionResult& aResult);
void MaybeAbortSign(const uint64_t& aTransactionId, const nsresult& aError);
// Using a raw pointer here, as the lifetime of the IPC object is managed by
// the PBackground protocol code. This means we cannot be left holding an
// invalid IPC protocol object after the transaction is finished.
PWebAuthnTransactionParent* mTransactionParent;
RefPtr<U2FTokenTransport> mTokenManagerImpl;
MozPromiseRequestHolder<U2FRegisterPromise> mRegisterPromise;
MozPromiseRequestHolder<U2FSignPromise> mSignPromise;
// The last transaction id, non-zero if there's an active transaction. This
// guards any cancel messages to ensure we don't cancel newer transactions
// due to a stale message.
uint64_t mLastTransactionId;
};
} // namespace mozilla::dom
#endif // mozilla_dom_U2FTokenManager_h

View file

@ -1,53 +0,0 @@
/* -*- 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/. */
#ifndef mozilla_dom_U2FTokenTransport_h
#define mozilla_dom_U2FTokenTransport_h
#include "mozilla/dom/PWebAuthnTransaction.h"
#include "mozilla/MozPromise.h"
/*
* Abstract class representing a transport manager for U2F Keys (software,
* bluetooth, usb, etc.). Hides the implementation details for specific key
* transport types.
*/
namespace mozilla::dom {
class WebAuthnGetAssertionResultWrapper {
public:
WebAuthnGetAssertionResult assertion;
mozilla::Maybe<nsCString> username;
};
typedef MozPromise<WebAuthnMakeCredentialResult, nsresult, true>
U2FRegisterPromise;
typedef MozPromise<nsTArray<WebAuthnGetAssertionResultWrapper>, nsresult, true>
U2FSignPromise;
class U2FTokenTransport {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(U2FTokenTransport);
U2FTokenTransport() = default;
virtual RefPtr<U2FRegisterPromise> Register(
const WebAuthnMakeCredentialInfo& aInfo, bool aForceNoneAttestation) = 0;
virtual RefPtr<U2FSignPromise> Sign(
const WebAuthnGetAssertionInfo& aInfo) = 0;
virtual void Cancel() = 0;
virtual void Drop() {}
protected:
virtual ~U2FTokenTransport() = default;
};
} // namespace mozilla::dom
#endif // mozilla_dom_U2FTokenTransport_h

View file

@ -4,26 +4,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. */ * You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CtapArgs.h" #include "WebAuthnArgs.h"
#include "WebAuthnEnumStrings.h" #include "WebAuthnEnumStrings.h"
#include "WebAuthnUtil.h" #include "WebAuthnUtil.h"
#include "mozilla/dom/PWebAuthnTransactionParent.h" #include "mozilla/dom/PWebAuthnTransactionParent.h"
namespace mozilla::dom { namespace mozilla::dom {
NS_IMPL_ISUPPORTS(CtapRegisterArgs, nsICtapRegisterArgs) NS_IMPL_ISUPPORTS(WebAuthnRegisterArgs, nsIWebAuthnRegisterArgs)
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetOrigin(nsAString& aOrigin) { WebAuthnRegisterArgs::GetOrigin(nsAString& aOrigin) {
mozilla::ipc::AssertIsOnBackgroundThread();
aOrigin = mInfo.Origin(); aOrigin = mInfo.Origin();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) { WebAuthnRegisterArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
mozilla::ipc::AssertIsOnBackgroundThread();
nsresult rv = HashCString(mInfo.ClientDataJSON(), aClientDataHash); nsresult rv = HashCString(mInfo.ClientDataJSON(), aClientDataHash);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -33,44 +30,37 @@ CtapRegisterArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetRpId(nsAString& aRpId) { WebAuthnRegisterArgs::GetRpId(nsAString& aRpId) {
mozilla::ipc::AssertIsOnBackgroundThread();
aRpId = mInfo.RpId(); aRpId = mInfo.RpId();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetRpName(nsAString& aRpName) { WebAuthnRegisterArgs::GetRpName(nsAString& aRpName) {
mozilla::ipc::AssertIsOnBackgroundThread();
aRpName = mInfo.Rp().Name(); aRpName = mInfo.Rp().Name();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetUserId(nsTArray<uint8_t>& aUserId) { WebAuthnRegisterArgs::GetUserId(nsTArray<uint8_t>& aUserId) {
mozilla::ipc::AssertIsOnBackgroundThread(); aUserId.Assign(mInfo.User().Id());
aUserId.Clear();
aUserId.AppendElements(mInfo.User().Id());
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetUserName(nsAString& aUserName) { WebAuthnRegisterArgs::GetUserName(nsAString& aUserName) {
mozilla::ipc::AssertIsOnBackgroundThread();
aUserName = mInfo.User().Name(); aUserName = mInfo.User().Name();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetUserDisplayName(nsAString& aUserDisplayName) { WebAuthnRegisterArgs::GetUserDisplayName(nsAString& aUserDisplayName) {
mozilla::ipc::AssertIsOnBackgroundThread();
aUserDisplayName = mInfo.User().DisplayName(); aUserDisplayName = mInfo.User().DisplayName();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetCoseAlgs(nsTArray<int32_t>& aCoseAlgs) { WebAuthnRegisterArgs::GetCoseAlgs(nsTArray<int32_t>& aCoseAlgs) {
mozilla::ipc::AssertIsOnBackgroundThread();
aCoseAlgs.Clear(); aCoseAlgs.Clear();
for (const CoseAlg& coseAlg : mInfo.coseAlgs()) { for (const CoseAlg& coseAlg : mInfo.coseAlgs()) {
aCoseAlgs.AppendElement(coseAlg.alg()); aCoseAlgs.AppendElement(coseAlg.alg());
@ -79,8 +69,8 @@ CtapRegisterArgs::GetCoseAlgs(nsTArray<int32_t>& aCoseAlgs) {
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetExcludeList(nsTArray<nsTArray<uint8_t> >& aExcludeList) { WebAuthnRegisterArgs::GetExcludeList(
mozilla::ipc::AssertIsOnBackgroundThread(); nsTArray<nsTArray<uint8_t> >& aExcludeList) {
aExcludeList.Clear(); aExcludeList.Clear();
for (const WebAuthnScopedCredential& cred : mInfo.ExcludeList()) { for (const WebAuthnScopedCredential& cred : mInfo.ExcludeList()) {
aExcludeList.AppendElement(cred.id().Clone()); aExcludeList.AppendElement(cred.id().Clone());
@ -89,51 +79,43 @@ CtapRegisterArgs::GetExcludeList(nsTArray<nsTArray<uint8_t> >& aExcludeList) {
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetCredProps(bool* aCredProps) { WebAuthnRegisterArgs::GetCredProps(bool* aCredProps) {
mozilla::ipc::AssertIsOnBackgroundThread();
*aCredProps = mCredProps; *aCredProps = mCredProps;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) { WebAuthnRegisterArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) {
mozilla::ipc::AssertIsOnBackgroundThread();
*aHmacCreateSecret = mHmacCreateSecret; *aHmacCreateSecret = mHmacCreateSecret;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetMinPinLength(bool* aMinPinLength) { WebAuthnRegisterArgs::GetMinPinLength(bool* aMinPinLength) {
mozilla::ipc::AssertIsOnBackgroundThread();
*aMinPinLength = mMinPinLength; *aMinPinLength = mMinPinLength;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetResidentKey(nsAString& aResidentKey) { WebAuthnRegisterArgs::GetResidentKey(nsAString& aResidentKey) {
mozilla::ipc::AssertIsOnBackgroundThread();
aResidentKey = mInfo.AuthenticatorSelection().residentKey(); aResidentKey = mInfo.AuthenticatorSelection().residentKey();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetUserVerification(nsAString& aUserVerificationRequirement) { WebAuthnRegisterArgs::GetUserVerification(
mozilla::ipc::AssertIsOnBackgroundThread(); nsAString& aUserVerificationRequirement) {
aUserVerificationRequirement = aUserVerificationRequirement =
mInfo.AuthenticatorSelection().userVerificationRequirement(); mInfo.AuthenticatorSelection().userVerificationRequirement();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetAuthenticatorAttachment( WebAuthnRegisterArgs::GetAuthenticatorAttachment(
nsAString& aAuthenticatorAttachment) { nsAString& aAuthenticatorAttachment) {
mozilla::ipc::AssertIsOnBackgroundThread();
if (mInfo.AuthenticatorSelection().authenticatorAttachment().isNothing()) { if (mInfo.AuthenticatorSelection().authenticatorAttachment().isNothing()) {
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
} }
@ -143,40 +125,34 @@ CtapRegisterArgs::GetAuthenticatorAttachment(
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetTimeoutMS(uint32_t* aTimeoutMS) { WebAuthnRegisterArgs::GetTimeoutMS(uint32_t* aTimeoutMS) {
mozilla::ipc::AssertIsOnBackgroundThread();
*aTimeoutMS = mInfo.TimeoutMS(); *aTimeoutMS = mInfo.TimeoutMS();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapRegisterArgs::GetAttestationConveyancePreference( WebAuthnRegisterArgs::GetAttestationConveyancePreference(
nsAString& aAttestationConveyancePreference) { nsAString& aAttestationConveyancePreference) {
mozilla::ipc::AssertIsOnBackgroundThread();
aAttestationConveyancePreference = mInfo.attestationConveyancePreference(); aAttestationConveyancePreference = mInfo.attestationConveyancePreference();
return NS_OK; return NS_OK;
} }
NS_IMPL_ISUPPORTS(CtapSignArgs, nsICtapSignArgs) NS_IMPL_ISUPPORTS(WebAuthnSignArgs, nsIWebAuthnSignArgs)
NS_IMETHODIMP NS_IMETHODIMP
CtapSignArgs::GetOrigin(nsAString& aOrigin) { WebAuthnSignArgs::GetOrigin(nsAString& aOrigin) {
mozilla::ipc::AssertIsOnBackgroundThread();
aOrigin = mInfo.Origin(); aOrigin = mInfo.Origin();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapSignArgs::GetRpId(nsAString& aRpId) { WebAuthnSignArgs::GetRpId(nsAString& aRpId) {
mozilla::ipc::AssertIsOnBackgroundThread();
aRpId = mInfo.RpId(); aRpId = mInfo.RpId();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapSignArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) { WebAuthnSignArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
mozilla::ipc::AssertIsOnBackgroundThread();
nsresult rv = HashCString(mInfo.ClientDataJSON(), aClientDataHash); nsresult rv = HashCString(mInfo.ClientDataJSON(), aClientDataHash);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -186,8 +162,7 @@ CtapSignArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapSignArgs::GetAllowList(nsTArray<nsTArray<uint8_t> >& aAllowList) { WebAuthnSignArgs::GetAllowList(nsTArray<nsTArray<uint8_t> >& aAllowList) {
mozilla::ipc::AssertIsOnBackgroundThread();
aAllowList.Clear(); aAllowList.Clear();
for (const WebAuthnScopedCredential& cred : mInfo.AllowList()) { for (const WebAuthnScopedCredential& cred : mInfo.AllowList()) {
aAllowList.AppendElement(cred.id().Clone()); aAllowList.AppendElement(cred.id().Clone());
@ -196,9 +171,7 @@ CtapSignArgs::GetAllowList(nsTArray<nsTArray<uint8_t> >& aAllowList) {
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapSignArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) { WebAuthnSignArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) {
mozilla::ipc::AssertIsOnBackgroundThread();
for (const WebAuthnExtension& ext : mInfo.Extensions()) { for (const WebAuthnExtension& ext : mInfo.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionHmacSecret) { if (ext.type() == WebAuthnExtension::TWebAuthnExtensionHmacSecret) {
*aHmacCreateSecret = *aHmacCreateSecret =
@ -211,9 +184,7 @@ CtapSignArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) {
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapSignArgs::GetAppId(nsAString& aAppId) { WebAuthnSignArgs::GetAppId(nsAString& aAppId) {
mozilla::ipc::AssertIsOnBackgroundThread();
for (const WebAuthnExtension& ext : mInfo.Extensions()) { for (const WebAuthnExtension& ext : mInfo.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) { if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
aAppId = ext.get_WebAuthnExtensionAppId().appIdentifier(); aAppId = ext.get_WebAuthnExtensionAppId().appIdentifier();
@ -225,15 +196,13 @@ CtapSignArgs::GetAppId(nsAString& aAppId) {
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapSignArgs::GetUserVerification(nsAString& aUserVerificationRequirement) { WebAuthnSignArgs::GetUserVerification(nsAString& aUserVerificationRequirement) {
mozilla::ipc::AssertIsOnBackgroundThread();
aUserVerificationRequirement = mInfo.userVerificationRequirement(); aUserVerificationRequirement = mInfo.userVerificationRequirement();
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
CtapSignArgs::GetTimeoutMS(uint32_t* aTimeoutMS) { WebAuthnSignArgs::GetTimeoutMS(uint32_t* aTimeoutMS) {
mozilla::ipc::AssertIsOnBackgroundThread();
*aTimeoutMS = mInfo.TimeoutMS(); *aTimeoutMS = mInfo.TimeoutMS();
return NS_OK; return NS_OK;
} }

View file

@ -4,8 +4,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CtapArgs_h #ifndef mozilla_dom_WebAuthnArgs_H_
#define CtapArgs_h #define mozilla_dom_WebAuthnArgs_H_
#include "mozilla/dom/WebAuthnTransactionChild.h" #include "mozilla/dom/WebAuthnTransactionChild.h"
#include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/BackgroundParent.h"
@ -13,24 +13,16 @@
namespace mozilla::dom { namespace mozilla::dom {
// These classes provide an FFI between C++ and Rust for the getters of IPC class WebAuthnRegisterArgs final : public nsIWebAuthnRegisterArgs {
// objects (WebAuthnMakeCredentialInfo and WebAuthnGetAssertionInfo). They hold
// non-owning references to IPC objects, and must only be used within the
// lifetime of the IPC transaction that created them. There are runtime
// assertions to ensure that these types are created and used on the IPC
// background thread, but that alone does not guarantee safety.
class CtapRegisterArgs final : public nsICtapRegisterArgs {
public: public:
NS_DECL_ISUPPORTS NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICTAPREGISTERARGS NS_DECL_NSIWEBAUTHNREGISTERARGS
explicit CtapRegisterArgs(const WebAuthnMakeCredentialInfo& aInfo) explicit WebAuthnRegisterArgs(const WebAuthnMakeCredentialInfo& aInfo)
: mInfo(aInfo), : mInfo(aInfo),
mCredProps(false), mCredProps(false),
mHmacCreateSecret(false), mHmacCreateSecret(false),
mMinPinLength(false) { mMinPinLength(false) {
mozilla::ipc::AssertIsOnBackgroundThread();
for (const WebAuthnExtension& ext : mInfo.Extensions()) { for (const WebAuthnExtension& ext : mInfo.Extensions()) {
switch (ext.type()) { switch (ext.type()) {
case WebAuthnExtension::TWebAuthnExtensionCredProps: case WebAuthnExtension::TWebAuthnExtensionCredProps:
@ -53,9 +45,9 @@ class CtapRegisterArgs final : public nsICtapRegisterArgs {
} }
private: private:
~CtapRegisterArgs() = default; ~WebAuthnRegisterArgs() = default;
const WebAuthnMakeCredentialInfo& mInfo; const WebAuthnMakeCredentialInfo mInfo;
// Flags to indicate whether an extension is being requested. // Flags to indicate whether an extension is being requested.
bool mCredProps; bool mCredProps;
@ -63,21 +55,20 @@ class CtapRegisterArgs final : public nsICtapRegisterArgs {
bool mMinPinLength; bool mMinPinLength;
}; };
class CtapSignArgs final : public nsICtapSignArgs { class WebAuthnSignArgs final : public nsIWebAuthnSignArgs {
public: public:
NS_DECL_ISUPPORTS NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICTAPSIGNARGS NS_DECL_NSIWEBAUTHNSIGNARGS
explicit CtapSignArgs(const WebAuthnGetAssertionInfo& aInfo) : mInfo(aInfo) { explicit WebAuthnSignArgs(const WebAuthnGetAssertionInfo& aInfo)
mozilla::ipc::AssertIsOnBackgroundThread(); : mInfo(aInfo) {}
}
private: private:
~CtapSignArgs() = default; ~WebAuthnSignArgs() = default;
const WebAuthnGetAssertionInfo& mInfo; const WebAuthnGetAssertionInfo mInfo;
}; };
} // namespace mozilla::dom } // namespace mozilla::dom
#endif // CtapArgs_h #endif // mozilla_dom_WebAuthnArgs_H_

View file

@ -15,14 +15,14 @@ WebAuthnRegisterPromiseHolder::Ensure() {
} }
NS_IMETHODIMP NS_IMETHODIMP
WebAuthnRegisterPromiseHolder::Resolve(nsICtapRegisterResult* aResult) { WebAuthnRegisterPromiseHolder::Resolve(nsIWebAuthnRegisterResult* aResult) {
if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads)) { if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
} }
// Resolve the promise on its owning thread if Disconnect() has not been // Resolve the promise on its owning thread if Disconnect() has not been
// called. // called.
RefPtr<nsICtapRegisterResult> result(aResult); RefPtr<nsIWebAuthnRegisterResult> result(aResult);
mEventTarget->Dispatch(NS_NewRunnableFunction( mEventTarget->Dispatch(NS_NewRunnableFunction(
"WebAuthnRegisterPromiseHolder::Resolve", "WebAuthnRegisterPromiseHolder::Resolve",
[self = RefPtr{this}, result]() { [self = RefPtr{this}, result]() {
@ -54,14 +54,14 @@ already_AddRefed<WebAuthnSignPromise> WebAuthnSignPromiseHolder::Ensure() {
} }
NS_IMETHODIMP NS_IMETHODIMP
WebAuthnSignPromiseHolder::Resolve(nsICtapSignResult* aResult) { WebAuthnSignPromiseHolder::Resolve(nsIWebAuthnSignResult* aResult) {
if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads)) { if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
} }
// Resolve the promise on its owning thread if Disconnect() has not been // Resolve the promise on its owning thread if Disconnect() has not been
// called. // called.
RefPtr<nsICtapSignResult> result(aResult); RefPtr<nsIWebAuthnSignResult> result(aResult);
mEventTarget->Dispatch(NS_NewRunnableFunction( mEventTarget->Dispatch(NS_NewRunnableFunction(
"WebAuthnSignPromiseHolder::Resolve", [self = RefPtr{this}, result]() { "WebAuthnSignPromiseHolder::Resolve", [self = RefPtr{this}, result]() {
self->mSignPromise.ResolveIfExists(result, __func__); self->mSignPromise.ResolveIfExists(result, __func__);

View file

@ -24,11 +24,11 @@ namespace mozilla::dom {
* at least one of Resolve(), Reject(), or Disconnect(). * at least one of Resolve(), Reject(), or Disconnect().
*/ */
typedef MozPromise<RefPtr<nsICtapRegisterResult>, nsresult, true> using WebAuthnRegisterPromise =
WebAuthnRegisterPromise; MozPromise<RefPtr<nsIWebAuthnRegisterResult>, nsresult, true>;
typedef MozPromise<RefPtr<nsICtapSignResult>, nsresult, true> using WebAuthnSignPromise =
WebAuthnSignPromise; MozPromise<RefPtr<nsIWebAuthnSignResult>, nsresult, true>;
class WebAuthnRegisterPromiseHolder final : public nsIWebAuthnRegisterPromise { class WebAuthnRegisterPromiseHolder final : public nsIWebAuthnRegisterPromise {
public: public:

View file

@ -0,0 +1,111 @@
/* 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 "nsString.h"
#include "WebAuthnResult.h"
#ifdef MOZ_WIDGET_ANDROID
namespace mozilla::jni {
template <>
RefPtr<dom::WebAuthnRegisterResult> Java2Native(
mozilla::jni::Object::Param aData, JNIEnv* aEnv) {
MOZ_ASSERT(
aData.IsInstanceOf<java::WebAuthnTokenManager::MakeCredentialResponse>());
java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef response(aData);
RefPtr<dom::WebAuthnRegisterResult> result =
new dom::WebAuthnRegisterResult(response);
return result;
}
template <>
RefPtr<dom::WebAuthnSignResult> Java2Native(mozilla::jni::Object::Param aData,
JNIEnv* aEnv) {
MOZ_ASSERT(
aData.IsInstanceOf<java::WebAuthnTokenManager::GetAssertionResponse>());
java::WebAuthnTokenManager::GetAssertionResponse::LocalRef response(aData);
RefPtr<dom::WebAuthnSignResult> result =
new dom::WebAuthnSignResult(response);
return result;
}
} // namespace mozilla::jni
#endif
namespace mozilla::dom {
NS_IMPL_ISUPPORTS(WebAuthnRegisterResult, nsIWebAuthnRegisterResult)
NS_IMETHODIMP
WebAuthnRegisterResult::GetAttestationObject(
nsTArray<uint8_t>& aAttestationObject) {
aAttestationObject.Assign(mAttestationObject);
return NS_OK;
}
NS_IMETHODIMP
WebAuthnRegisterResult::GetCredentialId(nsTArray<uint8_t>& aCredentialId) {
aCredentialId.Assign(mCredentialId);
return NS_OK;
}
NS_IMETHODIMP
WebAuthnRegisterResult::GetTransports(nsTArray<nsString>& aTransports) {
aTransports.Assign(mTransports);
return NS_OK;
}
NS_IMETHODIMP
WebAuthnRegisterResult::GetCredPropsRk(bool* aCredPropsRk) {
if (mCredPropsRk.isSome()) {
*aCredPropsRk = mCredPropsRk.ref();
return NS_OK;
}
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
WebAuthnRegisterResult::SetCredPropsRk(bool aCredPropsRk) {
mCredPropsRk = Some(aCredPropsRk);
return NS_OK;
}
NS_IMPL_ISUPPORTS(WebAuthnSignResult, nsIWebAuthnSignResult)
NS_IMETHODIMP
WebAuthnSignResult::GetAuthenticatorData(
nsTArray<uint8_t>& aAuthenticatorData) {
aAuthenticatorData.Assign(mAuthenticatorData);
return NS_OK;
}
NS_IMETHODIMP
WebAuthnSignResult::GetCredentialId(nsTArray<uint8_t>& aCredentialId) {
aCredentialId.Assign(mCredentialId);
return NS_OK;
}
NS_IMETHODIMP
WebAuthnSignResult::GetSignature(nsTArray<uint8_t>& aSignature) {
aSignature.Assign(mSignature);
return NS_OK;
}
NS_IMETHODIMP
WebAuthnSignResult::GetUserHandle(nsTArray<uint8_t>& aUserHandle) {
aUserHandle.Assign(mUserHandle);
return NS_OK;
}
NS_IMETHODIMP
WebAuthnSignResult::GetUserName(nsACString& aUserName) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
WebAuthnSignResult::GetUsedAppId(bool* aUsedAppId) {
return NS_ERROR_NOT_AVAILABLE;
}
} // namespace mozilla::dom

View file

@ -0,0 +1,120 @@
/* 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/. */
#ifndef mozilla_dom_WebAuthnResult_h_
#define mozilla_dom_WebAuthnResult_h_
#include "nsIWebAuthnResult.h"
#ifdef MOZ_WIDGET_ANDROID
# include "mozilla/java/WebAuthnTokenManagerNatives.h"
#endif
namespace mozilla::dom {
class WebAuthnRegisterResult final : public nsIWebAuthnRegisterResult {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBAUTHNREGISTERRESULT
WebAuthnRegisterResult(const nsTArray<uint8_t>& aAttestationObject,
const nsCString& aClientDataJSON,
const nsTArray<uint8_t>& aCredentialId,
const nsTArray<nsString>& aTransports)
: mClientDataJSON(aClientDataJSON), mCredPropsRk(Nothing()) {
mAttestationObject.AppendElements(aAttestationObject);
mCredentialId.AppendElements(aCredentialId);
mTransports.AppendElements(aTransports);
}
#ifdef MOZ_WIDGET_ANDROID
explicit WebAuthnRegisterResult(
const java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef&
aResponse) {
mAttestationObject.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->AttestationObject()->GetElements().Elements()),
aResponse->AttestationObject()->Length());
mClientDataJSON.Assign(
reinterpret_cast<const char*>(
aResponse->ClientDataJson()->GetElements().Elements()),
aResponse->ClientDataJson()->Length());
mCredentialId.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->KeyHandle()->GetElements().Elements()),
aResponse->KeyHandle()->Length());
auto transports = aResponse->Transports();
for (size_t i = 0; i < transports->Length(); i++) {
mTransports.AppendElement(
jni::String::LocalRef(transports->GetElement(i))->ToString());
}
}
#endif
private:
~WebAuthnRegisterResult() = default;
nsTArray<uint8_t> mAttestationObject;
nsTArray<uint8_t> mCredentialId;
nsTArray<nsString> mTransports;
nsCString mClientDataJSON;
Maybe<bool> mCredPropsRk;
};
class WebAuthnSignResult final : public nsIWebAuthnSignResult {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBAUTHNSIGNRESULT
WebAuthnSignResult(const nsTArray<uint8_t>& aAuthenticatorData,
const nsCString& aClientDataJSON,
const nsTArray<uint8_t>& aCredentialId,
const nsTArray<uint8_t>& aSignature,
const nsTArray<uint8_t>& aUserHandle)
: mClientDataJSON(aClientDataJSON) {
mAuthenticatorData.AppendElements(aAuthenticatorData);
mCredentialId.AppendElements(aCredentialId);
mSignature.AppendElements(aSignature);
mUserHandle.AppendElements(aUserHandle);
}
#ifdef MOZ_WIDGET_ANDROID
explicit WebAuthnSignResult(
const java::WebAuthnTokenManager::GetAssertionResponse::LocalRef&
aResponse) {
mAuthenticatorData.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->AuthData()->GetElements().Elements()),
aResponse->AuthData()->Length());
mClientDataJSON.Assign(
reinterpret_cast<const char*>(
aResponse->ClientDataJson()->GetElements().Elements()),
aResponse->ClientDataJson()->Length());
mCredentialId.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->KeyHandle()->GetElements().Elements()),
aResponse->KeyHandle()->Length());
mSignature.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->Signature()->GetElements().Elements()),
aResponse->Signature()->Length());
mUserHandle.AppendElements(
reinterpret_cast<uint8_t*>(
aResponse->UserHandle()->GetElements().Elements()),
aResponse->UserHandle()->Length());
}
#endif
private:
~WebAuthnSignResult() = default;
nsTArray<uint8_t> mAuthenticatorData;
nsCString mClientDataJSON;
nsTArray<uint8_t> mCredentialId;
nsTArray<uint8_t> mSignature;
nsTArray<uint8_t> mUserHandle;
};
} // namespace mozilla::dom
#endif // mozilla_dom_WebAuthnResult_h

View file

@ -0,0 +1,163 @@
/* 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/StaticPrefs_security.h"
#include "WebAuthnService.h"
namespace mozilla::dom {
already_AddRefed<nsIWebAuthnService> NewWebAuthnService() {
nsCOMPtr<nsIWebAuthnService> webauthnService(new WebAuthnService());
return webauthnService.forget();
}
NS_IMPL_ISUPPORTS(WebAuthnService, nsIWebAuthnService)
NS_IMETHODIMP
WebAuthnService::MakeCredential(uint64_t aTransactionId,
uint64_t browsingContextId,
nsIWebAuthnRegisterArgs* aArgs,
nsIWebAuthnRegisterPromise* aPromise) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->MakeCredential(aTransactionId, browsingContextId,
aArgs, aPromise);
}
return mPlatformService->MakeCredential(aTransactionId, browsingContextId,
aArgs, aPromise);
}
NS_IMETHODIMP
WebAuthnService::GetAssertion(uint64_t aTransactionId,
uint64_t browsingContextId,
nsIWebAuthnSignArgs* aArgs,
nsIWebAuthnSignPromise* aPromise) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->GetAssertion(aTransactionId, browsingContextId, aArgs,
aPromise);
}
return mPlatformService->GetAssertion(aTransactionId, browsingContextId,
aArgs, aPromise);
}
NS_IMETHODIMP
WebAuthnService::Reset() {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->Reset();
}
return mPlatformService->Reset();
}
NS_IMETHODIMP
WebAuthnService::Cancel(uint64_t aTransactionId) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->Cancel(aTransactionId);
}
return mPlatformService->Cancel(aTransactionId);
}
NS_IMETHODIMP
WebAuthnService::PinCallback(uint64_t aTransactionId, const nsACString& aPin) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->PinCallback(aTransactionId, aPin);
}
return mPlatformService->PinCallback(aTransactionId, aPin);
}
NS_IMETHODIMP
WebAuthnService::ResumeMakeCredential(uint64_t aTransactionId,
bool aForceNoneAttestation) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->ResumeMakeCredential(aTransactionId,
aForceNoneAttestation);
}
return mPlatformService->ResumeMakeCredential(aTransactionId,
aForceNoneAttestation);
}
NS_IMETHODIMP
WebAuthnService::SelectionCallback(uint64_t aTransactionId, uint64_t aIndex) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->SelectionCallback(aTransactionId, aIndex);
}
return mPlatformService->SelectionCallback(aTransactionId, aIndex);
}
NS_IMETHODIMP
WebAuthnService::AddVirtualAuthenticator(
const nsACString& protocol, const nsACString& transport,
bool hasResidentKey, bool hasUserVerification, bool isUserConsenting,
bool isUserVerified, uint64_t* retval) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->AddVirtualAuthenticator(
protocol, transport, hasResidentKey, hasUserVerification,
isUserConsenting, isUserVerified, retval);
}
return mPlatformService->AddVirtualAuthenticator(
protocol, transport, hasResidentKey, hasUserVerification,
isUserConsenting, isUserVerified, retval);
}
NS_IMETHODIMP
WebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->RemoveVirtualAuthenticator(authenticatorId);
}
return mPlatformService->RemoveVirtualAuthenticator(authenticatorId);
}
NS_IMETHODIMP
WebAuthnService::AddCredential(uint64_t authenticatorId,
const nsACString& credentialId,
bool isResidentCredential,
const nsACString& rpId,
const nsACString& privateKey,
const nsACString& userHandle,
uint32_t signCount) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->AddCredential(authenticatorId, credentialId,
isResidentCredential, rpId, privateKey,
userHandle, signCount);
}
return mPlatformService->AddCredential(authenticatorId, credentialId,
isResidentCredential, rpId, privateKey,
userHandle, signCount);
}
NS_IMETHODIMP
WebAuthnService::GetCredentials(
uint64_t authenticatorId,
nsTArray<RefPtr<nsICredentialParameters>>& retval) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->GetCredentials(authenticatorId, retval);
}
return mPlatformService->GetCredentials(authenticatorId, retval);
}
NS_IMETHODIMP
WebAuthnService::RemoveCredential(uint64_t authenticatorId,
const nsACString& credentialId) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->RemoveCredential(authenticatorId, credentialId);
}
return mPlatformService->RemoveCredential(authenticatorId, credentialId);
}
NS_IMETHODIMP
WebAuthnService::RemoveAllCredentials(uint64_t authenticatorId) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->RemoveAllCredentials(authenticatorId);
}
return mPlatformService->RemoveAllCredentials(authenticatorId);
}
NS_IMETHODIMP
WebAuthnService::SetUserVerified(uint64_t authenticatorId,
bool isUserVerified) {
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
return mTestService->SetUserVerified(authenticatorId, isUserVerified);
}
return mPlatformService->SetUserVerified(authenticatorId, isUserVerified);
}
} // namespace mozilla::dom

View file

@ -0,0 +1,42 @@
/* 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/. */
#ifndef mozilla_dom_WebAuthnService_h_
#define mozilla_dom_WebAuthnService_h_
#include "nsIWebAuthnService.h"
#include "AuthrsBridge_ffi.h"
#ifdef MOZ_WIDGET_ANDROID
# include "AndroidWebAuthnService.h"
#endif
namespace mozilla::dom {
already_AddRefed<nsIWebAuthnService> NewWebAuthnService();
class WebAuthnService final : public nsIWebAuthnService {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBAUTHNSERVICE
WebAuthnService() {
Unused << authrs_service_constructor(getter_AddRefs(mTestService));
#ifdef MOZ_WIDGET_ANDROID
mPlatformService = new AndroidWebAuthnService();
#else
mPlatformService = mTestService;
#endif
}
private:
~WebAuthnService() = default;
nsCOMPtr<nsIWebAuthnService> mPlatformService;
nsCOMPtr<nsIWebAuthnService> mTestService;
};
} // namespace mozilla::dom
#endif // mozilla_dom_WebAuthnService_h_

View file

@ -9,13 +9,9 @@
#include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/StaticPrefs_security.h" #include "mozilla/StaticPrefs_security.h"
#include "CtapArgs.h"
#include "nsIWebAuthnService.h" #include "nsIWebAuthnService.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "WebAuthnArgs.h"
#ifdef MOZ_WIDGET_ANDROID
# include "mozilla/dom/U2FTokenManager.h"
#endif
#ifdef XP_WIN #ifdef XP_WIN
# include "WinWebAuthnManager.h" # include "WinWebAuthnManager.h"
@ -40,23 +36,6 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestRegister(
} }
#endif #endif
// Bug 1819414 will reroute requests on Android through WebAuthnController and
// allow us to remove this.
#ifdef MOZ_WIDGET_ANDROID
bool usingTestToken =
StaticPrefs::security_webauth_webauthn_enable_softtoken();
bool androidFido2 =
StaticPrefs::security_webauth_webauthn_enable_android_fido2();
if (!usingTestToken && androidFido2) {
U2FTokenManager* mgr = U2FTokenManager::Get();
if (mgr) {
mgr->Register(this, aTransactionId, aTransactionInfo);
}
return IPC_OK();
}
#endif
// If there's an ongoing transaction, abort it. // If there's an ongoing transaction, abort it.
if (mTransactionId.isSome()) { if (mTransactionId.isSome()) {
mRegisterPromiseRequest.DisconnectIfExists(); mRegisterPromiseRequest.DisconnectIfExists();
@ -142,11 +121,11 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestRegister(
nsCOMPtr<nsIWebAuthnService> webauthnService( nsCOMPtr<nsIWebAuthnService> webauthnService(
do_GetService("@mozilla.org/webauthn/service;1")); do_GetService("@mozilla.org/webauthn/service;1"));
RefPtr<CtapRegisterArgs> args(new CtapRegisterArgs(aTransactionInfo)); uint64_t browsingContextId = aTransactionInfo.BrowsingContextId();
RefPtr<WebAuthnRegisterArgs> args(new WebAuthnRegisterArgs(aTransactionInfo));
nsresult rv = webauthnService->MakeCredential( nsresult rv = webauthnService->MakeCredential(
aTransactionId, aTransactionInfo.BrowsingContextId(), args, aTransactionId, browsingContextId, args, promiseHolder);
promiseHolder);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
promiseHolder->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR); promiseHolder->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
} }
@ -171,23 +150,6 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
} }
#endif #endif
// Bug 1819414 will reroute requests on Android through WebAuthnController and
// allow us to remove this.
#ifdef MOZ_WIDGET_ANDROID
bool usingTestToken =
StaticPrefs::security_webauth_webauthn_enable_softtoken();
bool androidFido2 =
StaticPrefs::security_webauth_webauthn_enable_android_fido2();
if (!usingTestToken && androidFido2) {
U2FTokenManager* mgr = U2FTokenManager::Get();
if (mgr) {
mgr->Sign(this, aTransactionId, aTransactionInfo);
}
return IPC_OK();
}
#endif
if (mTransactionId.isSome()) { if (mTransactionId.isSome()) {
mRegisterPromiseRequest.DisconnectIfExists(); mRegisterPromiseRequest.DisconnectIfExists();
mSignPromiseRequest.DisconnectIfExists(); mSignPromiseRequest.DisconnectIfExists();
@ -272,7 +234,7 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
}) })
->Track(mSignPromiseRequest); ->Track(mSignPromiseRequest);
RefPtr<CtapSignArgs> args(new CtapSignArgs(aTransactionInfo)); RefPtr<WebAuthnSignArgs> args(new WebAuthnSignArgs(aTransactionInfo));
nsCOMPtr<nsIWebAuthnService> webauthnService( nsCOMPtr<nsIWebAuthnService> webauthnService(
do_GetService("@mozilla.org/webauthn/service;1")); do_GetService("@mozilla.org/webauthn/service;1"));
@ -300,16 +262,6 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestCancel(
// fall through in case the virtual token was used. // fall through in case the virtual token was used.
#endif #endif
// Bug 1819414 will reroute requests on Android through WebAuthnController and
// allow us to remove this.
#ifdef MOZ_WIDGET_ANDROID
U2FTokenManager* mgr = U2FTokenManager::Get();
if (mgr) {
mgr->Cancel(this, aTransactionId);
}
// fall through in case the virtual token was used.
#endif
if (mTransactionId.isNothing() || if (mTransactionId.isNothing() ||
!MOZ_IS_VALID(aTransactionId, mTransactionId.ref() == aTransactionId)) { !MOZ_IS_VALID(aTransactionId, mTransactionId.ref() == aTransactionId)) {
return IPC_OK(); return IPC_OK();
@ -362,16 +314,6 @@ void WebAuthnTransactionParent::ActorDestroy(ActorDestroyReason aWhy) {
// fall through in case the virtual token was used. // fall through in case the virtual token was used.
#endif #endif
// Bug 1819414 will reroute requests on Android through WebAuthnController and
// allow us to remove this.
#ifdef MOZ_WIDGET_ANDROID
U2FTokenManager* mgr = U2FTokenManager::Get();
if (mgr) {
mgr->MaybeClearTransaction(this);
}
// fall through in case the virtual token was used.
#endif
mRegisterPromiseRequest.DisconnectIfExists(); mRegisterPromiseRequest.DisconnectIfExists();
mSignPromiseRequest.DisconnectIfExists(); mSignPromiseRequest.DisconnectIfExists();
mTransactionId.reset(); mTransactionId.reset();

View file

@ -7,7 +7,6 @@
#ifndef mozilla_dom_WinWebAuthnManager_h #ifndef mozilla_dom_WinWebAuthnManager_h
#define mozilla_dom_WinWebAuthnManager_h #define mozilla_dom_WinWebAuthnManager_h
#include "mozilla/dom/U2FTokenTransport.h"
#include "mozilla/dom/PWebAuthnTransaction.h" #include "mozilla/dom/PWebAuthnTransaction.h"
#include "mozilla/Tainting.h" #include "mozilla/Tainting.h"

View file

@ -26,7 +26,8 @@ use cstr::cstr;
use moz_task::{get_main_thread, RunnableBuilder}; use moz_task::{get_main_thread, RunnableBuilder};
use nserror::{ use nserror::{
nsresult, NS_ERROR_DOM_ABORT_ERR, NS_ERROR_DOM_INVALID_STATE_ERR, NS_ERROR_DOM_NOT_ALLOWED_ERR, nsresult, NS_ERROR_DOM_ABORT_ERR, NS_ERROR_DOM_INVALID_STATE_ERR, NS_ERROR_DOM_NOT_ALLOWED_ERR,
NS_ERROR_FAILURE, NS_ERROR_INVALID_ARG, NS_ERROR_NOT_AVAILABLE, NS_ERROR_NULL_POINTER, NS_OK, NS_ERROR_FAILURE, NS_ERROR_INVALID_ARG, NS_ERROR_NOT_AVAILABLE, NS_ERROR_NOT_IMPLEMENTED,
NS_ERROR_NULL_POINTER, NS_OK,
}; };
use nsstring::{nsACString, nsCString, nsString}; use nsstring::{nsACString, nsCString, nsString};
use serde::Serialize; use serde::Serialize;
@ -38,9 +39,9 @@ use std::sync::mpsc::{channel, Receiver, RecvError, Sender};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use thin_vec::{thin_vec, ThinVec}; use thin_vec::{thin_vec, ThinVec};
use xpcom::interfaces::{ use xpcom::interfaces::{
nsICredentialParameters, nsICtapRegisterArgs, nsICtapRegisterResult, nsICtapSignArgs, nsICredentialParameters, nsIObserverService, nsIWebAuthnAttObj, nsIWebAuthnRegisterArgs,
nsICtapSignResult, nsIObserverService, nsIWebAuthnAttObj, nsIWebAuthnRegisterPromise, nsIWebAuthnRegisterPromise, nsIWebAuthnRegisterResult, nsIWebAuthnService, nsIWebAuthnSignArgs,
nsIWebAuthnService, nsIWebAuthnSignPromise, nsIWebAuthnSignPromise, nsIWebAuthnSignResult,
}; };
use xpcom::{xpcom_method, RefPtr}; use xpcom::{xpcom_method, RefPtr};
@ -135,12 +136,12 @@ fn cancel_prompts(tid: u64) -> Result<(), nsresult> {
Ok(()) Ok(())
} }
#[xpcom(implement(nsICtapRegisterResult), atomic)] #[xpcom(implement(nsIWebAuthnRegisterResult), atomic)]
pub struct CtapRegisterResult { pub struct WebAuthnRegisterResult {
result: RegisterResult, result: RegisterResult,
} }
impl CtapRegisterResult { impl WebAuthnRegisterResult {
xpcom_method!(get_attestation_object => GetAttestationObject() -> ThinVec<u8>); xpcom_method!(get_attestation_object => GetAttestationObject() -> ThinVec<u8>);
fn get_attestation_object(&self) -> Result<ThinVec<u8>, nsresult> { fn get_attestation_object(&self) -> Result<ThinVec<u8>, nsresult> {
let mut out = ThinVec::new(); let mut out = ThinVec::new();
@ -172,6 +173,11 @@ impl CtapRegisterResult {
}; };
Ok(cred_props.rk) Ok(cred_props.rk)
} }
xpcom_method!(set_cred_props_rk => SetCredPropsRk(aCredPropsRk: bool));
fn set_cred_props_rk(&self, _cred_props_rk: bool) -> Result<(), nsresult> {
Err(NS_ERROR_NOT_IMPLEMENTED)
}
} }
#[xpcom(implement(nsIWebAuthnAttObj), atomic)] #[xpcom(implement(nsIWebAuthnAttObj), atomic)]
@ -215,12 +221,12 @@ impl WebAuthnAttObj {
} }
} }
#[xpcom(implement(nsICtapSignResult), atomic)] #[xpcom(implement(nsIWebAuthnSignResult), atomic)]
pub struct CtapSignResult { pub struct WebAuthnSignResult {
result: SignResult, result: SignResult,
} }
impl CtapSignResult { impl WebAuthnSignResult {
xpcom_method!(get_credential_id => GetCredentialId() -> ThinVec<u8>); xpcom_method!(get_credential_id => GetCredentialId() -> ThinVec<u8>);
fn get_credential_id(&self) -> Result<ThinVec<u8>, nsresult> { fn get_credential_id(&self) -> Result<ThinVec<u8>, nsresult> {
let Some(cred) = &self.result.assertion.credentials else { let Some(cred) = &self.result.assertion.credentials else {
@ -396,8 +402,8 @@ impl RegisterPromise {
match result { match result {
Ok(result) => { Ok(result) => {
let wrapped_result = let wrapped_result =
CtapRegisterResult::allocate(InitCtapRegisterResult { result }) WebAuthnRegisterResult::allocate(InitWebAuthnRegisterResult { result })
.query_interface::<nsICtapRegisterResult>() .query_interface::<nsIWebAuthnRegisterResult>()
.ok_or(NS_ERROR_FAILURE)?; .ok_or(NS_ERROR_FAILURE)?;
unsafe { self.0.Resolve(wrapped_result.coerce()) }; unsafe { self.0.Resolve(wrapped_result.coerce()) };
} }
@ -416,9 +422,10 @@ impl SignPromise {
fn resolve_or_reject(&self, result: Result<SignResult, nsresult>) -> Result<(), nsresult> { fn resolve_or_reject(&self, result: Result<SignResult, nsresult>) -> Result<(), nsresult> {
match result { match result {
Ok(result) => { Ok(result) => {
let wrapped_result = CtapSignResult::allocate(InitCtapSignResult { result }) let wrapped_result =
.query_interface::<nsICtapSignResult>() WebAuthnSignResult::allocate(InitWebAuthnSignResult { result })
.ok_or(NS_ERROR_FAILURE)?; .query_interface::<nsIWebAuthnSignResult>()
.ok_or(NS_ERROR_FAILURE)?;
unsafe { self.0.Resolve(wrapped_result.coerce()) }; unsafe { self.0.Resolve(wrapped_result.coerce()) };
} }
Err(result) => { Err(result) => {
@ -504,12 +511,12 @@ impl AuthrsService {
// //
// This will mutably borrow usb_token_manager through a RefCell. The caller must ensure that at // This will mutably borrow usb_token_manager through a RefCell. The caller must ensure that at
// most one WebAuthn transaction is active at any given time. // most one WebAuthn transaction is active at any given time.
xpcom_method!(make_credential => MakeCredential(aTid: u64, aBrowsingContextId: u64, aArgs: *const nsICtapRegisterArgs, aPromise: *const nsIWebAuthnRegisterPromise)); xpcom_method!(make_credential => MakeCredential(aTid: u64, aBrowsingContextId: u64, aArgs: *const nsIWebAuthnRegisterArgs, aPromise: *const nsIWebAuthnRegisterPromise));
fn make_credential( fn make_credential(
&self, &self,
tid: u64, tid: u64,
browsing_context_id: u64, browsing_context_id: u64,
args: &nsICtapRegisterArgs, args: &nsIWebAuthnRegisterArgs,
promise: &nsIWebAuthnRegisterPromise, promise: &nsIWebAuthnRegisterPromise,
) -> Result<(), nsresult> { ) -> Result<(), nsresult> {
self.reset()?; self.reset()?;
@ -762,12 +769,12 @@ impl AuthrsService {
// //
// This will mutably borrow usb_token_manager through a RefCell. The caller must ensure that at // This will mutably borrow usb_token_manager through a RefCell. The caller must ensure that at
// most one WebAuthn transaction is active at any given time. // most one WebAuthn transaction is active at any given time.
xpcom_method!(get_assertion => GetAssertion(aTid: u64, aBrowsingContextId: u64, aArgs: *const nsICtapSignArgs, aPromise: *const nsIWebAuthnSignPromise)); xpcom_method!(get_assertion => GetAssertion(aTid: u64, aBrowsingContextId: u64, aArgs: *const nsIWebAuthnSignArgs, aPromise: *const nsIWebAuthnSignPromise));
fn get_assertion( fn get_assertion(
&self, &self,
tid: u64, tid: u64,
browsing_context_id: u64, browsing_context_id: u64,
args: &nsICtapSignArgs, args: &nsIWebAuthnSignArgs,
promise: &nsIWebAuthnSignPromise, promise: &nsIWebAuthnSignPromise,
) -> Result<(), nsresult> { ) -> Result<(), nsresult> {
self.reset()?; self.reset()?;

View file

@ -8,7 +8,7 @@ Classes = [
{ {
'cid': '{ebe8a51d-bd54-4838-b031-cd2289990e14}', 'cid': '{ebe8a51d-bd54-4838-b031-cd2289990e14}',
'contract_ids': ['@mozilla.org/webauthn/service;1'], 'contract_ids': ['@mozilla.org/webauthn/service;1'],
'headers': ['/dom/webauthn/AuthrsService.h'], 'headers': ['/dom/webauthn/WebAuthnService.h'],
'constructor': 'mozilla::dom::NewAuthrsService', 'constructor': 'mozilla::dom::NewWebAuthnService',
}, },
] ]

View file

@ -28,7 +28,6 @@ EXPORTS.mozilla.dom += [
"AuthenticatorAttestationResponse.h", "AuthenticatorAttestationResponse.h",
"AuthenticatorResponse.h", "AuthenticatorResponse.h",
"PublicKeyCredential.h", "PublicKeyCredential.h",
"U2FTokenTransport.h",
"WebAuthnManager.h", "WebAuthnManager.h",
"WebAuthnManagerBase.h", "WebAuthnManagerBase.h",
"WebAuthnPromiseHolder.h", "WebAuthnPromiseHolder.h",
@ -42,12 +41,13 @@ UNIFIED_SOURCES += [
"AuthenticatorAssertionResponse.cpp", "AuthenticatorAssertionResponse.cpp",
"AuthenticatorAttestationResponse.cpp", "AuthenticatorAttestationResponse.cpp",
"AuthenticatorResponse.cpp", "AuthenticatorResponse.cpp",
"AuthrsService.cpp",
"CtapArgs.cpp",
"PublicKeyCredential.cpp", "PublicKeyCredential.cpp",
"WebAuthnArgs.cpp",
"WebAuthnManager.cpp", "WebAuthnManager.cpp",
"WebAuthnManagerBase.cpp", "WebAuthnManagerBase.cpp",
"WebAuthnPromiseHolder.cpp", "WebAuthnPromiseHolder.cpp",
"WebAuthnResult.cpp",
"WebAuthnService.cpp",
"WebAuthnTransactionChild.cpp", "WebAuthnTransactionChild.cpp",
"WebAuthnTransactionParent.cpp", "WebAuthnTransactionParent.cpp",
"WebAuthnUtil.cpp", "WebAuthnUtil.cpp",
@ -62,21 +62,11 @@ LOCAL_INCLUDES += [
"/dom/crypto", "/dom/crypto",
"/security/manager/ssl", "/security/manager/ssl",
"/third_party/rust", "/third_party/rust",
"/toolkit/components/jsoncpp/include",
]
USE_LIBS += [
"jsoncpp",
] ]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
EXPORTS.mozilla.dom += [
"AndroidWebAuthnTokenManager.h",
"U2FTokenManager.h",
]
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
"AndroidWebAuthnTokenManager.cpp", "AndroidWebAuthnService.cpp",
"U2FTokenManager.cpp",
] ]
if CONFIG["OS_ARCH"] == "WINNT": if CONFIG["OS_ARCH"] == "WINNT":

View file

@ -7,15 +7,8 @@
typedef long COSEAlgorithmIdentifier; typedef long COSEAlgorithmIdentifier;
// The nsICtapRegisterArgs interface encapsulates the arguments to the CTAP
// authenticatorMakeCredential command as defined in
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorMakeCredential
// It is essentially a shim that allows data to be copied from an IPDL-defined
// WebAuthnMakeCredentialInfo C++ struct to an authenticator-rs defined
// RegisterArgsCtap2 Rust struct.
//
[uuid(2fc8febe-a277-11ed-bda2-8f6495a5e75c)] [uuid(2fc8febe-a277-11ed-bda2-8f6495a5e75c)]
interface nsICtapRegisterArgs : nsISupports { interface nsIWebAuthnRegisterArgs : nsISupports {
// TODO(Bug 1820035) The origin is only used for prompt callbacks. Refactor and remove. // TODO(Bug 1820035) The origin is only used for prompt callbacks. Refactor and remove.
readonly attribute AString origin; readonly attribute AString origin;
@ -48,8 +41,8 @@ interface nsICtapRegisterArgs : nsISupports {
[must_use] readonly attribute bool minPinLength; [must_use] readonly attribute bool minPinLength;
// Options. // Options.
[must_use] readonly attribute AString residentKey; readonly attribute AString residentKey;
[must_use] readonly attribute AString userVerification; readonly attribute AString userVerification;
[must_use] readonly attribute AString authenticatorAttachment; [must_use] readonly attribute AString authenticatorAttachment;
// This is the WebAuthn PublicKeyCredentialCreationOptions timeout. // This is the WebAuthn PublicKeyCredentialCreationOptions timeout.
@ -63,15 +56,8 @@ interface nsICtapRegisterArgs : nsISupports {
[must_use] readonly attribute AString attestationConveyancePreference; [must_use] readonly attribute AString attestationConveyancePreference;
}; };
// The nsICtapSignArgs interface encapsulates the arguments to the CTAP
// authenticatorGetAssertion command as defined in
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorGetAssertion
// It is essentially a shim that allows data to be copied from an IPDL-defined
// WebAuthnGetAssertionInfo C++ struct to an authenticator-rs defined
// SignArgsCtap2 Rust struct.
//
[uuid(2e621cf4-a277-11ed-ae00-bf41a54ef553)] [uuid(2e621cf4-a277-11ed-ae00-bf41a54ef553)]
interface nsICtapSignArgs : nsISupports { interface nsIWebAuthnSignArgs : nsISupports {
// TODO(Bug 1820035) The origin is only used for prompt callbacks. Refactor and remove. // TODO(Bug 1820035) The origin is only used for prompt callbacks. Refactor and remove.
readonly attribute AString origin; readonly attribute AString origin;

View file

@ -9,13 +9,13 @@
[rust_sync, uuid(3c969aec-e0e0-4aa4-9422-394b321e6918)] [rust_sync, uuid(3c969aec-e0e0-4aa4-9422-394b321e6918)]
interface nsIWebAuthnRegisterPromise : nsISupports interface nsIWebAuthnRegisterPromise : nsISupports
{ {
[noscript] void resolve(in nsICtapRegisterResult aResult); [noscript] void resolve(in nsIWebAuthnRegisterResult aResult);
[noscript] void reject(in nsresult error); [noscript] void reject(in nsresult error);
}; };
[rust_sync, uuid(35e35bdc-5369-4bfe-8d5c-bdf7b782b735)] [rust_sync, uuid(35e35bdc-5369-4bfe-8d5c-bdf7b782b735)]
interface nsIWebAuthnSignPromise : nsISupports interface nsIWebAuthnSignPromise : nsISupports
{ {
[noscript] void resolve(in nsICtapSignResult aResult); [noscript] void resolve(in nsIWebAuthnSignResult aResult);
[noscript] void reject(in nsresult error); [noscript] void reject(in nsresult error);
}; };

View file

@ -6,7 +6,7 @@
#include "nsISupports.idl" #include "nsISupports.idl"
[uuid(0567c384-a728-11ed-85f7-030324a370f0)] [uuid(0567c384-a728-11ed-85f7-030324a370f0)]
interface nsICtapRegisterResult : nsISupports { interface nsIWebAuthnRegisterResult : nsISupports {
// The serialied attestation object as defined in // The serialied attestation object as defined in
// https://www.w3.org/TR/webauthn-2/#sctn-attestation // https://www.w3.org/TR/webauthn-2/#sctn-attestation
// Includes the format, the attestation statement, and // Includes the format, the attestation statement, and
@ -24,14 +24,14 @@ interface nsICtapRegisterResult : nsISupports {
// bug 1593571 // bug 1593571
// readonly attribute bool hmacCreateSecret; // readonly attribute bool hmacCreateSecret;
readonly attribute bool credPropsRk; [must_use] attribute bool credPropsRk;
}; };
// The nsICtapSignResult interface is used to construct IPDL-defined // The nsIWebAuthnSignResult interface is used to construct IPDL-defined
// WebAuthnGetAssertionResult from either Rust or C++. // WebAuthnGetAssertionResult from either Rust or C++.
// //
[uuid(05fff816-a728-11ed-b9ac-ff38cc2c8c28)] [uuid(05fff816-a728-11ed-b9ac-ff38cc2c8c28)]
interface nsICtapSignResult : nsISupports { interface nsIWebAuthnSignResult : nsISupports {
// The ID field of the PublicKeyCredentialDescriptor returned // The ID field of the PublicKeyCredentialDescriptor returned
// from authenticatorGetAssertion. // from authenticatorGetAssertion.
readonly attribute Array<octet> credentialId; readonly attribute Array<octet> credentialId;

View file

@ -25,13 +25,13 @@ interface nsIWebAuthnService : nsISupports
void makeCredential( void makeCredential(
in uint64_t aTransactionId, in uint64_t aTransactionId,
in uint64_t browsingContextId, in uint64_t browsingContextId,
in nsICtapRegisterArgs args, in nsIWebAuthnRegisterArgs args,
in nsIWebAuthnRegisterPromise promise); in nsIWebAuthnRegisterPromise promise);
void getAssertion( void getAssertion(
in uint64_t aTransactionId, in uint64_t aTransactionId,
in uint64_t browsingContextId, in uint64_t browsingContextId,
in nsICtapSignArgs args, in nsIWebAuthnSignArgs args,
in nsIWebAuthnSignPromise promise); in nsIWebAuthnSignPromise promise);
// Cancel the ongoing transaction and any prompts that are shown, but do not reject // Cancel the ongoing transaction and any prompts that are shown, but do not reject

View file

@ -9,7 +9,6 @@ support-files = [
prefs = [ prefs = [
"security.webauth.webauthn=true", "security.webauth.webauthn=true",
"security.webauth.webauthn_enable_softtoken=true", "security.webauth.webauthn_enable_softtoken=true",
"security.webauth.webauthn_enable_android_fido2=false",
"security.webauth.webauthn_enable_usbtoken=false", "security.webauth.webauthn_enable_usbtoken=false",
"security.webauthn.ctap2=true", "security.webauthn.ctap2=true",
] ]

View file

@ -46,7 +46,6 @@ add_task(async function test_setup() {
["security.webauth.webauthn", true], ["security.webauth.webauthn", true],
["security.webauth.webauthn_enable_softtoken", true], ["security.webauth.webauthn_enable_softtoken", true],
["security.webauth.webauthn_enable_usbtoken", false], ["security.webauth.webauthn_enable_usbtoken", false],
["security.webauth.webauthn_enable_android_fido2", false],
["security.webauth.webauthn_testing_allow_direct_attestation", true], ["security.webauth.webauthn_testing_allow_direct_attestation", true],
], ],
}); });

View file

@ -10,7 +10,6 @@ prefs =
dom.security.featurePolicy.webidl.enabled=true dom.security.featurePolicy.webidl.enabled=true
security.webauth.webauthn=true security.webauth.webauthn=true
security.webauth.webauthn_enable_softtoken=true security.webauth.webauthn_enable_softtoken=true
security.webauth.webauthn_enable_android_fido2=false
security.webauth.webauthn_enable_usbtoken=false security.webauth.webauthn_enable_usbtoken=false
security.webauthn.ctap2=true security.webauthn.ctap2=true

View file

@ -104,9 +104,6 @@
#include "mozilla/dom/AbstractRange.h" #include "mozilla/dom/AbstractRange.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "mozilla/dom/WebIDLGlobalNameHash.h" #include "mozilla/dom/WebIDLGlobalNameHash.h"
#ifdef MOZ_WIDGET_ANDROID
# include "mozilla/dom/U2FTokenManager.h"
#endif
#ifdef XP_WIN #ifdef XP_WIN
# include "mozilla/dom/WinWebAuthnManager.h" # include "mozilla/dom/WinWebAuthnManager.h"
#endif #endif
@ -260,10 +257,6 @@ nsresult nsLayoutStatics::Initialize() {
// This must be initialized on the main-thread. // This must be initialized on the main-thread.
mozilla::RemoteLazyInputStreamStorage::Initialize(); mozilla::RemoteLazyInputStreamStorage::Initialize();
#ifdef MOZ_WIDGET_ANDROID
mozilla::dom::U2FTokenManager::Initialize();
#endif
#ifdef XP_WIN #ifdef XP_WIN
mozilla::dom::WinWebAuthnManager::Initialize(); mozilla::dom::WinWebAuthnManager::Initialize();
#endif #endif

View file

@ -13709,20 +13709,14 @@
mirror: always mirror: always
rust: true rust: true
# Dispatch WebAuthn requests to virtual authenticators. (mutually exclusive # Dispatch WebAuthn requests to virtual authenticators (mutually exclusive
# with webauthn_enable_android_fido2, and webauthn_enable_usbtoken) # with and webauthn_enable_usbtoken)
- name: security.webauth.webauthn_enable_softtoken - name: security.webauth.webauthn_enable_softtoken
type: RelaxedAtomicBool type: RelaxedAtomicBool
value: false value: false
mirror: always mirror: always
rust: true rust: true
# Dispatch WebAuthn requests to the Android platform API
- name: security.webauth.webauthn_enable_android_fido2
type: RelaxedAtomicBool
value: @IS_ANDROID@
mirror: always
# residentKey support when using Android platform API # residentKey support when using Android platform API
- name: security.webauthn.webauthn_enable_android_fido2.residentkey - name: security.webauthn.webauthn_enable_android_fido2.residentkey
type: RelaxedAtomicBool type: RelaxedAtomicBool

View file

@ -87,7 +87,6 @@ user_pref("gecko.handlerService.defaultHandlersVersion", 100);
user_pref("security.webauth.webauthn_enable_softtoken", true); user_pref("security.webauth.webauthn_enable_softtoken", true);
// Disable hardware WebAuthn authenticators. // Disable hardware WebAuthn authenticators.
user_pref("security.webauth.webauthn_enable_usbtoken", false); user_pref("security.webauth.webauthn_enable_usbtoken", false);
user_pref("security.webauth.webauthn_enable_android_fido2", false);
// Disable the WebAuthn direct attestation consent prompt. // Disable the WebAuthn direct attestation consent prompt.
user_pref("security.webauth.webauthn_testing_allow_direct_attestation", true); user_pref("security.webauth.webauthn_testing_allow_direct_attestation", true);
// Disable captive portal service // Disable captive portal service