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 () => {
await SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
["security.webauth.webauthn_enable_softtoken", true],
["security.webauth.webauthn_enable_usbtoken", false],
["security.webauth.webauthn_enable_android_fido2", false]]});
["security.webauth.webauthn_enable_usbtoken", false]]});
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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_AndroidWebAuthnTokenManager_h
#define mozilla_dom_AndroidWebAuthnTokenManager_h
#ifndef mozilla_dom_AndroidWebAuthnService_h_
#define mozilla_dom_AndroidWebAuthnService_h_
#include "mozilla/dom/U2FTokenTransport.h"
#include "mozilla/java/WebAuthnTokenManagerNatives.h"
#include "nsIWebAuthnService.h"
namespace mozilla {
namespace dom {
// Collected from
@ -27,27 +28,13 @@ constexpr auto kTimeoutError = u"TIMEOUT_ERR"_ns;
constexpr auto kNetworkError = u"NETWORK_ERR"_ns;
constexpr auto kUnknownError = u"UNKNOWN_ERR"_ns;
class AndroidWebAuthnResult {
class AndroidWebAuthnError {
public:
explicit AndroidWebAuthnResult(const nsAString& aErrorCode)
explicit AndroidWebAuthnError(const nsAString& 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 {
if (mErrorCode.IsEmpty()) {
return NS_OK;
} else if (mErrorCode.Equals(kSecurityError)) {
if (mErrorCode.Equals(kSecurityError)) {
return NS_ERROR_DOM_SECURITY_ERR;
} else if (mErrorCode.Equals(kConstraintError)) {
// 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:
const nsString mErrorCode;
};
/*
* WebAuthnAndroidTokenManager is a token implementation communicating with
* Android Fido2 APIs.
*/
class AndroidWebAuthnTokenManager final : public U2FTokenTransport {
class AndroidWebAuthnService final : public nsIWebAuthnService {
public:
explicit AndroidWebAuthnTokenManager();
~AndroidWebAuthnTokenManager() {}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBAUTHNSERVICE
virtual RefPtr<U2FRegisterPromise> Register(
const WebAuthnMakeCredentialInfo& aInfo,
bool aForceNoneAttestation) override;
virtual RefPtr<U2FSignPromise> Sign(
const WebAuthnGetAssertionInfo& aInfo) override;
void Cancel() override;
void Drop() override;
static AndroidWebAuthnTokenManager* GetInstance();
AndroidWebAuthnService() = default;
private:
void HandleRegisterResult(AndroidWebAuthnResult&& aResult);
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;
~AndroidWebAuthnService() = default;
// The Android FIDO2 API doesn't accept the credProps extension. However, the
// appropriate value for CredentialPropertiesOutput.rk can be determined
@ -147,4 +90,4 @@ class AndroidWebAuthnTokenManager final : public U2FTokenTransport {
} // namespace dom
} // 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,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CtapArgs.h"
#include "WebAuthnArgs.h"
#include "WebAuthnEnumStrings.h"
#include "WebAuthnUtil.h"
#include "mozilla/dom/PWebAuthnTransactionParent.h"
namespace mozilla::dom {
NS_IMPL_ISUPPORTS(CtapRegisterArgs, nsICtapRegisterArgs)
NS_IMPL_ISUPPORTS(WebAuthnRegisterArgs, nsIWebAuthnRegisterArgs)
NS_IMETHODIMP
CtapRegisterArgs::GetOrigin(nsAString& aOrigin) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetOrigin(nsAString& aOrigin) {
aOrigin = mInfo.Origin();
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
nsresult rv = HashCString(mInfo.ClientDataJSON(), aClientDataHash);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE;
@ -33,44 +30,37 @@ CtapRegisterArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
}
NS_IMETHODIMP
CtapRegisterArgs::GetRpId(nsAString& aRpId) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetRpId(nsAString& aRpId) {
aRpId = mInfo.RpId();
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetRpName(nsAString& aRpName) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetRpName(nsAString& aRpName) {
aRpName = mInfo.Rp().Name();
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetUserId(nsTArray<uint8_t>& aUserId) {
mozilla::ipc::AssertIsOnBackgroundThread();
aUserId.Clear();
aUserId.AppendElements(mInfo.User().Id());
WebAuthnRegisterArgs::GetUserId(nsTArray<uint8_t>& aUserId) {
aUserId.Assign(mInfo.User().Id());
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetUserName(nsAString& aUserName) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetUserName(nsAString& aUserName) {
aUserName = mInfo.User().Name();
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetUserDisplayName(nsAString& aUserDisplayName) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetUserDisplayName(nsAString& aUserDisplayName) {
aUserDisplayName = mInfo.User().DisplayName();
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetCoseAlgs(nsTArray<int32_t>& aCoseAlgs) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetCoseAlgs(nsTArray<int32_t>& aCoseAlgs) {
aCoseAlgs.Clear();
for (const CoseAlg& coseAlg : mInfo.coseAlgs()) {
aCoseAlgs.AppendElement(coseAlg.alg());
@ -79,8 +69,8 @@ CtapRegisterArgs::GetCoseAlgs(nsTArray<int32_t>& aCoseAlgs) {
}
NS_IMETHODIMP
CtapRegisterArgs::GetExcludeList(nsTArray<nsTArray<uint8_t> >& aExcludeList) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetExcludeList(
nsTArray<nsTArray<uint8_t> >& aExcludeList) {
aExcludeList.Clear();
for (const WebAuthnScopedCredential& cred : mInfo.ExcludeList()) {
aExcludeList.AppendElement(cred.id().Clone());
@ -89,51 +79,43 @@ CtapRegisterArgs::GetExcludeList(nsTArray<nsTArray<uint8_t> >& aExcludeList) {
}
NS_IMETHODIMP
CtapRegisterArgs::GetCredProps(bool* aCredProps) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetCredProps(bool* aCredProps) {
*aCredProps = mCredProps;
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) {
*aHmacCreateSecret = mHmacCreateSecret;
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetMinPinLength(bool* aMinPinLength) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetMinPinLength(bool* aMinPinLength) {
*aMinPinLength = mMinPinLength;
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetResidentKey(nsAString& aResidentKey) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetResidentKey(nsAString& aResidentKey) {
aResidentKey = mInfo.AuthenticatorSelection().residentKey();
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetUserVerification(nsAString& aUserVerificationRequirement) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetUserVerification(
nsAString& aUserVerificationRequirement) {
aUserVerificationRequirement =
mInfo.AuthenticatorSelection().userVerificationRequirement();
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetAuthenticatorAttachment(
WebAuthnRegisterArgs::GetAuthenticatorAttachment(
nsAString& aAuthenticatorAttachment) {
mozilla::ipc::AssertIsOnBackgroundThread();
if (mInfo.AuthenticatorSelection().authenticatorAttachment().isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -143,40 +125,34 @@ CtapRegisterArgs::GetAuthenticatorAttachment(
}
NS_IMETHODIMP
CtapRegisterArgs::GetTimeoutMS(uint32_t* aTimeoutMS) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnRegisterArgs::GetTimeoutMS(uint32_t* aTimeoutMS) {
*aTimeoutMS = mInfo.TimeoutMS();
return NS_OK;
}
NS_IMETHODIMP
CtapRegisterArgs::GetAttestationConveyancePreference(
WebAuthnRegisterArgs::GetAttestationConveyancePreference(
nsAString& aAttestationConveyancePreference) {
mozilla::ipc::AssertIsOnBackgroundThread();
aAttestationConveyancePreference = mInfo.attestationConveyancePreference();
return NS_OK;
}
NS_IMPL_ISUPPORTS(CtapSignArgs, nsICtapSignArgs)
NS_IMPL_ISUPPORTS(WebAuthnSignArgs, nsIWebAuthnSignArgs)
NS_IMETHODIMP
CtapSignArgs::GetOrigin(nsAString& aOrigin) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnSignArgs::GetOrigin(nsAString& aOrigin) {
aOrigin = mInfo.Origin();
return NS_OK;
}
NS_IMETHODIMP
CtapSignArgs::GetRpId(nsAString& aRpId) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnSignArgs::GetRpId(nsAString& aRpId) {
aRpId = mInfo.RpId();
return NS_OK;
}
NS_IMETHODIMP
CtapSignArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnSignArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
nsresult rv = HashCString(mInfo.ClientDataJSON(), aClientDataHash);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE;
@ -186,8 +162,7 @@ CtapSignArgs::GetClientDataHash(nsTArray<uint8_t>& aClientDataHash) {
}
NS_IMETHODIMP
CtapSignArgs::GetAllowList(nsTArray<nsTArray<uint8_t> >& aAllowList) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnSignArgs::GetAllowList(nsTArray<nsTArray<uint8_t> >& aAllowList) {
aAllowList.Clear();
for (const WebAuthnScopedCredential& cred : mInfo.AllowList()) {
aAllowList.AppendElement(cred.id().Clone());
@ -196,9 +171,7 @@ CtapSignArgs::GetAllowList(nsTArray<nsTArray<uint8_t> >& aAllowList) {
}
NS_IMETHODIMP
CtapSignArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnSignArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) {
for (const WebAuthnExtension& ext : mInfo.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionHmacSecret) {
*aHmacCreateSecret =
@ -211,9 +184,7 @@ CtapSignArgs::GetHmacCreateSecret(bool* aHmacCreateSecret) {
}
NS_IMETHODIMP
CtapSignArgs::GetAppId(nsAString& aAppId) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnSignArgs::GetAppId(nsAString& aAppId) {
for (const WebAuthnExtension& ext : mInfo.Extensions()) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
aAppId = ext.get_WebAuthnExtensionAppId().appIdentifier();
@ -225,15 +196,13 @@ CtapSignArgs::GetAppId(nsAString& aAppId) {
}
NS_IMETHODIMP
CtapSignArgs::GetUserVerification(nsAString& aUserVerificationRequirement) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnSignArgs::GetUserVerification(nsAString& aUserVerificationRequirement) {
aUserVerificationRequirement = mInfo.userVerificationRequirement();
return NS_OK;
}
NS_IMETHODIMP
CtapSignArgs::GetTimeoutMS(uint32_t* aTimeoutMS) {
mozilla::ipc::AssertIsOnBackgroundThread();
WebAuthnSignArgs::GetTimeoutMS(uint32_t* aTimeoutMS) {
*aTimeoutMS = mInfo.TimeoutMS();
return NS_OK;
}

View File

@ -4,8 +4,8 @@
* 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 CtapArgs_h
#define CtapArgs_h
#ifndef mozilla_dom_WebAuthnArgs_H_
#define mozilla_dom_WebAuthnArgs_H_
#include "mozilla/dom/WebAuthnTransactionChild.h"
#include "mozilla/ipc/BackgroundParent.h"
@ -13,24 +13,16 @@
namespace mozilla::dom {
// These classes provide an FFI between C++ and Rust for the getters of IPC
// 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 {
class WebAuthnRegisterArgs final : public nsIWebAuthnRegisterArgs {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICTAPREGISTERARGS
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBAUTHNREGISTERARGS
explicit CtapRegisterArgs(const WebAuthnMakeCredentialInfo& aInfo)
explicit WebAuthnRegisterArgs(const WebAuthnMakeCredentialInfo& aInfo)
: mInfo(aInfo),
mCredProps(false),
mHmacCreateSecret(false),
mMinPinLength(false) {
mozilla::ipc::AssertIsOnBackgroundThread();
for (const WebAuthnExtension& ext : mInfo.Extensions()) {
switch (ext.type()) {
case WebAuthnExtension::TWebAuthnExtensionCredProps:
@ -53,9 +45,9 @@ class CtapRegisterArgs final : public nsICtapRegisterArgs {
}
private:
~CtapRegisterArgs() = default;
~WebAuthnRegisterArgs() = default;
const WebAuthnMakeCredentialInfo& mInfo;
const WebAuthnMakeCredentialInfo mInfo;
// Flags to indicate whether an extension is being requested.
bool mCredProps;
@ -63,21 +55,20 @@ class CtapRegisterArgs final : public nsICtapRegisterArgs {
bool mMinPinLength;
};
class CtapSignArgs final : public nsICtapSignArgs {
class WebAuthnSignArgs final : public nsIWebAuthnSignArgs {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICTAPSIGNARGS
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBAUTHNSIGNARGS
explicit CtapSignArgs(const WebAuthnGetAssertionInfo& aInfo) : mInfo(aInfo) {
mozilla::ipc::AssertIsOnBackgroundThread();
}
explicit WebAuthnSignArgs(const WebAuthnGetAssertionInfo& aInfo)
: mInfo(aInfo) {}
private:
~CtapSignArgs() = default;
~WebAuthnSignArgs() = default;
const WebAuthnGetAssertionInfo& mInfo;
const WebAuthnGetAssertionInfo mInfo;
};
} // namespace mozilla::dom
#endif // CtapArgs_h
#endif // mozilla_dom_WebAuthnArgs_H_

View File

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

View File

@ -24,11 +24,11 @@ namespace mozilla::dom {
* at least one of Resolve(), Reject(), or Disconnect().
*/
typedef MozPromise<RefPtr<nsICtapRegisterResult>, nsresult, true>
WebAuthnRegisterPromise;
using WebAuthnRegisterPromise =
MozPromise<RefPtr<nsIWebAuthnRegisterResult>, nsresult, true>;
typedef MozPromise<RefPtr<nsICtapSignResult>, nsresult, true>
WebAuthnSignPromise;
using WebAuthnSignPromise =
MozPromise<RefPtr<nsIWebAuthnSignResult>, nsresult, true>;
class WebAuthnRegisterPromiseHolder final : public nsIWebAuthnRegisterPromise {
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/StaticPrefs_security.h"
#include "CtapArgs.h"
#include "nsIWebAuthnService.h"
#include "nsThreadUtils.h"
#ifdef MOZ_WIDGET_ANDROID
# include "mozilla/dom/U2FTokenManager.h"
#endif
#include "WebAuthnArgs.h"
#ifdef XP_WIN
# include "WinWebAuthnManager.h"
@ -40,23 +36,6 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestRegister(
}
#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 (mTransactionId.isSome()) {
mRegisterPromiseRequest.DisconnectIfExists();
@ -142,11 +121,11 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestRegister(
nsCOMPtr<nsIWebAuthnService> webauthnService(
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(
aTransactionId, aTransactionInfo.BrowsingContextId(), args,
promiseHolder);
aTransactionId, browsingContextId, args, promiseHolder);
if (NS_FAILED(rv)) {
promiseHolder->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
}
@ -171,23 +150,6 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
}
#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()) {
mRegisterPromiseRequest.DisconnectIfExists();
mSignPromiseRequest.DisconnectIfExists();
@ -272,7 +234,7 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
})
->Track(mSignPromiseRequest);
RefPtr<CtapSignArgs> args(new CtapSignArgs(aTransactionInfo));
RefPtr<WebAuthnSignArgs> args(new WebAuthnSignArgs(aTransactionInfo));
nsCOMPtr<nsIWebAuthnService> webauthnService(
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.
#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() ||
!MOZ_IS_VALID(aTransactionId, mTransactionId.ref() == aTransactionId)) {
return IPC_OK();
@ -362,16 +314,6 @@ void WebAuthnTransactionParent::ActorDestroy(ActorDestroyReason aWhy) {
// fall through in case the virtual token was used.
#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();
mSignPromiseRequest.DisconnectIfExists();
mTransactionId.reset();

View File

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

View File

@ -26,7 +26,8 @@ use cstr::cstr;
use moz_task::{get_main_thread, RunnableBuilder};
use nserror::{
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 serde::Serialize;
@ -38,9 +39,9 @@ use std::sync::mpsc::{channel, Receiver, RecvError, Sender};
use std::sync::{Arc, Mutex};
use thin_vec::{thin_vec, ThinVec};
use xpcom::interfaces::{
nsICredentialParameters, nsICtapRegisterArgs, nsICtapRegisterResult, nsICtapSignArgs,
nsICtapSignResult, nsIObserverService, nsIWebAuthnAttObj, nsIWebAuthnRegisterPromise,
nsIWebAuthnService, nsIWebAuthnSignPromise,
nsICredentialParameters, nsIObserverService, nsIWebAuthnAttObj, nsIWebAuthnRegisterArgs,
nsIWebAuthnRegisterPromise, nsIWebAuthnRegisterResult, nsIWebAuthnService, nsIWebAuthnSignArgs,
nsIWebAuthnSignPromise, nsIWebAuthnSignResult,
};
use xpcom::{xpcom_method, RefPtr};
@ -135,12 +136,12 @@ fn cancel_prompts(tid: u64) -> Result<(), nsresult> {
Ok(())
}
#[xpcom(implement(nsICtapRegisterResult), atomic)]
pub struct CtapRegisterResult {
#[xpcom(implement(nsIWebAuthnRegisterResult), atomic)]
pub struct WebAuthnRegisterResult {
result: RegisterResult,
}
impl CtapRegisterResult {
impl WebAuthnRegisterResult {
xpcom_method!(get_attestation_object => GetAttestationObject() -> ThinVec<u8>);
fn get_attestation_object(&self) -> Result<ThinVec<u8>, nsresult> {
let mut out = ThinVec::new();
@ -172,6 +173,11 @@ impl CtapRegisterResult {
};
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)]
@ -215,12 +221,12 @@ impl WebAuthnAttObj {
}
}
#[xpcom(implement(nsICtapSignResult), atomic)]
pub struct CtapSignResult {
#[xpcom(implement(nsIWebAuthnSignResult), atomic)]
pub struct WebAuthnSignResult {
result: SignResult,
}
impl CtapSignResult {
impl WebAuthnSignResult {
xpcom_method!(get_credential_id => GetCredentialId() -> ThinVec<u8>);
fn get_credential_id(&self) -> Result<ThinVec<u8>, nsresult> {
let Some(cred) = &self.result.assertion.credentials else {
@ -396,8 +402,8 @@ impl RegisterPromise {
match result {
Ok(result) => {
let wrapped_result =
CtapRegisterResult::allocate(InitCtapRegisterResult { result })
.query_interface::<nsICtapRegisterResult>()
WebAuthnRegisterResult::allocate(InitWebAuthnRegisterResult { result })
.query_interface::<nsIWebAuthnRegisterResult>()
.ok_or(NS_ERROR_FAILURE)?;
unsafe { self.0.Resolve(wrapped_result.coerce()) };
}
@ -416,9 +422,10 @@ impl SignPromise {
fn resolve_or_reject(&self, result: Result<SignResult, nsresult>) -> Result<(), nsresult> {
match result {
Ok(result) => {
let wrapped_result = CtapSignResult::allocate(InitCtapSignResult { result })
.query_interface::<nsICtapSignResult>()
.ok_or(NS_ERROR_FAILURE)?;
let wrapped_result =
WebAuthnSignResult::allocate(InitWebAuthnSignResult { result })
.query_interface::<nsIWebAuthnSignResult>()
.ok_or(NS_ERROR_FAILURE)?;
unsafe { self.0.Resolve(wrapped_result.coerce()) };
}
Err(result) => {
@ -504,12 +511,12 @@ impl AuthrsService {
//
// 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.
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(
&self,
tid: u64,
browsing_context_id: u64,
args: &nsICtapRegisterArgs,
args: &nsIWebAuthnRegisterArgs,
promise: &nsIWebAuthnRegisterPromise,
) -> Result<(), nsresult> {
self.reset()?;
@ -762,12 +769,12 @@ impl AuthrsService {
//
// 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.
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(
&self,
tid: u64,
browsing_context_id: u64,
args: &nsICtapSignArgs,
args: &nsIWebAuthnSignArgs,
promise: &nsIWebAuthnSignPromise,
) -> Result<(), nsresult> {
self.reset()?;

View File

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

View File

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

View File

@ -7,15 +7,8 @@
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)]
interface nsICtapRegisterArgs : nsISupports {
interface nsIWebAuthnRegisterArgs : nsISupports {
// TODO(Bug 1820035) The origin is only used for prompt callbacks. Refactor and remove.
readonly attribute AString origin;
@ -48,8 +41,8 @@ interface nsICtapRegisterArgs : nsISupports {
[must_use] readonly attribute bool minPinLength;
// Options.
[must_use] readonly attribute AString residentKey;
[must_use] readonly attribute AString userVerification;
readonly attribute AString residentKey;
readonly attribute AString userVerification;
[must_use] readonly attribute AString authenticatorAttachment;
// This is the WebAuthn PublicKeyCredentialCreationOptions timeout.
@ -63,15 +56,8 @@ interface nsICtapRegisterArgs : nsISupports {
[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)]
interface nsICtapSignArgs : nsISupports {
interface nsIWebAuthnSignArgs : nsISupports {
// TODO(Bug 1820035) The origin is only used for prompt callbacks. Refactor and remove.
readonly attribute AString origin;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13709,20 +13709,14 @@
mirror: always
rust: true
# Dispatch WebAuthn requests to virtual authenticators. (mutually exclusive
# with webauthn_enable_android_fido2, and webauthn_enable_usbtoken)
# Dispatch WebAuthn requests to virtual authenticators (mutually exclusive
# with and webauthn_enable_usbtoken)
- name: security.webauth.webauthn_enable_softtoken
type: RelaxedAtomicBool
value: false
mirror: always
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
- name: security.webauthn.webauthn_enable_android_fido2.residentkey
type: RelaxedAtomicBool

View File

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