mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1857336 - expose an nsIWebAuthnService on Android. r=keeler
Differential Revision: https://phabricator.services.mozilla.com/D190232
This commit is contained in:
parent
93adfae975
commit
d5e1715d6b
@ -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();
|
||||
});
|
||||
|
||||
|
384
dom/webauthn/AndroidWebAuthnService.cpp
Normal file
384
dom/webauthn/AndroidWebAuthnService.cpp
Normal 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
|
@ -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_
|
@ -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(¤t)));
|
||||
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
|
@ -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
|
@ -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_
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
@ -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_
|
@ -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__);
|
||||
|
@ -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:
|
||||
|
111
dom/webauthn/WebAuthnResult.cpp
Normal file
111
dom/webauthn/WebAuthnResult.cpp
Normal 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
|
120
dom/webauthn/WebAuthnResult.h
Normal file
120
dom/webauthn/WebAuthnResult.h
Normal 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
|
163
dom/webauthn/WebAuthnService.cpp
Normal file
163
dom/webauthn/WebAuthnService.cpp
Normal 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
|
42
dom/webauthn/WebAuthnService.h
Normal file
42
dom/webauthn/WebAuthnService.h
Normal 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_
|
@ -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();
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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()?;
|
||||
|
@ -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',
|
||||
},
|
||||
]
|
||||
|
@ -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":
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
]
|
||||
|
@ -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],
|
||||
],
|
||||
});
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user