From 985fed8d4c23c6c5905fed55736002f41b49148d Mon Sep 17 00:00:00 2001 From: Cosmin Sabou Date: Wed, 20 Sep 2023 01:28:28 +0300 Subject: [PATCH] Backed out changeset 793702f190e3 (bug 1853819) for bc failures on browser_webauthn_prompts.js. CLOSED TREE --- browser/base/content/browser.js | 4 +- dom/webauthn/U2FTokenManager.cpp | 332 +++++++++++++++++++++++++++-- dom/webauthn/U2FTokenManager.h | 58 ++++- dom/webauthn/WebAuthnController.cpp | 11 +- dom/webauthn/WebAuthnController.h | 1 + dom/webauthn/WebAuthnTransactionParent.cpp | 5 +- dom/webauthn/authrs_bridge/src/lib.rs | 6 +- dom/webauthn/moz.build | 6 +- dom/webauthn/nsIU2FTokenManager.idl | 40 ++++ dom/webauthn/nsIWebAuthnController.idl | 6 +- layout/build/nsLayoutStatics.cpp | 7 +- 11 files changed, 421 insertions(+), 55 deletions(-) create mode 100644 dom/webauthn/nsIU2FTokenManager.idl diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 19fe29b22a86..f2cdbaf95730 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -7539,7 +7539,9 @@ var WebAuthnPromptHelper = { return; } - let mgr = aSubject.QueryInterface(Ci.nsIWebAuthnController); + let mgr = aSubject.QueryInterface( + data.is_ctap2 ? Ci.nsIWebAuthnController : Ci.nsIU2FTokenManager + ); if (data.action == "presence") { this.presence_required(mgr, data); diff --git a/dom/webauthn/U2FTokenManager.cpp b/dom/webauthn/U2FTokenManager.cpp index 1937b9ffe63d..21704d276408 100644 --- a/dom/webauthn/U2FTokenManager.cpp +++ b/dom/webauthn/U2FTokenManager.cpp @@ -8,8 +8,10 @@ #include "mozilla/dom/U2FTokenTransport.h" #include "mozilla/dom/PWebAuthnTransactionParent.h" #include "mozilla/MozPromise.h" +#include "mozilla/dom/WebAuthnUtil.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/Unused.h" #include "nsEscape.h" @@ -20,29 +22,115 @@ #include "mozilla/Telemetry.h" #include "WebAuthnEnumStrings.h" -#include "mozilla/dom/AndroidWebAuthnTokenManager.h" +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/dom/AndroidWebAuthnTokenManager.h" +#endif +#define PREF_WEBAUTHN_USBTOKEN_ENABLED \ + "security.webauth.webauthn_enable_usbtoken" +#define PREF_WEBAUTHN_ALLOW_DIRECT_ATTESTATION \ + "security.webauth.webauthn_testing_allow_direct_attestation" +#define PREF_WEBAUTHN_ANDROID_FIDO2_ENABLED \ + "security.webauth.webauthn_enable_android_fido2" namespace mozilla::dom { /*********************************************************************** * Statics **********************************************************************/ +class U2FPrefManager; + namespace { static mozilla::LazyLogModule gU2FTokenManagerLog("u2fkeymanager"); -StaticAutoPtr gU2FTokenManager; +StaticRefPtr gU2FTokenManager; +StaticRefPtr gPrefManager; static nsIThread* gBackgroundThread; } // namespace +// Data for WebAuthn UI prompt notifications. +static const char16_t kPresencePromptNotificationU2F[] = + u"{\"is_ctap2\":false,\"action\":\"presence\",\"tid\":%llu," + u"\"origin\":\"%s\",\"browsingContextId\":%llu}"; +static const char16_t kRegisterDirectPromptNotificationU2F[] = + u"{\"is_ctap2\":false,\"action\":\"register-direct\",\"tid\":%llu," + u"\"origin\":\"%s\",\"browsingContextId\":%llu}"; +static const char16_t kCancelPromptNotificationU2F[] = + u"{\"is_ctap2\":false,\"action\":\"cancel\",\"tid\":%llu}"; + +class U2FPrefManager final : public nsIObserver { + private: + U2FPrefManager() : mPrefMutex("U2FPrefManager Mutex") { UpdateValues(); } + ~U2FPrefManager() = default; + + public: + NS_DECL_ISUPPORTS + + static U2FPrefManager* GetOrCreate() { + MOZ_ASSERT(NS_IsMainThread()); + if (!gPrefManager) { + gPrefManager = new U2FPrefManager(); + Preferences::AddStrongObserver(gPrefManager, + PREF_WEBAUTHN_USBTOKEN_ENABLED); + Preferences::AddStrongObserver(gPrefManager, + PREF_WEBAUTHN_ANDROID_FIDO2_ENABLED); + Preferences::AddStrongObserver(gPrefManager, + PREF_WEBAUTHN_ALLOW_DIRECT_ATTESTATION); + ClearOnShutdown(&gPrefManager, ShutdownPhase::XPCOMShutdownThreads); + } + return gPrefManager; + } + + static U2FPrefManager* Get() { return gPrefManager; } + + bool GetAndroidFido2Enabled() { + MutexAutoLock lock(mPrefMutex); + return mAndroidFido2Enabled; + } + + bool GetAllowDirectAttestationForTesting() { + MutexAutoLock lock(mPrefMutex); + return mAllowDirectAttestation; + } + + NS_IMETHODIMP + Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) override { + UpdateValues(); + return NS_OK; + } + + private: + void UpdateValues() { + MOZ_ASSERT(NS_IsMainThread()); + MutexAutoLock lock(mPrefMutex); + mUsbTokenEnabled = Preferences::GetBool(PREF_WEBAUTHN_USBTOKEN_ENABLED); + mAndroidFido2Enabled = + Preferences::GetBool(PREF_WEBAUTHN_ANDROID_FIDO2_ENABLED); + mAllowDirectAttestation = + Preferences::GetBool(PREF_WEBAUTHN_ALLOW_DIRECT_ATTESTATION); + } + + Mutex mPrefMutex MOZ_UNANNOTATED; + bool mUsbTokenEnabled; + bool mAndroidFido2Enabled; + bool mAllowDirectAttestation; +}; + +NS_IMPL_ISUPPORTS(U2FPrefManager, nsIObserver); + /*********************************************************************** * U2FManager Implementation **********************************************************************/ +NS_IMPL_ISUPPORTS(U2FTokenManager, nsIU2FTokenManager); + U2FTokenManager::U2FTokenManager() : mTransactionParent(nullptr), mLastTransactionId(0) { MOZ_ASSERT(XRE_IsParentProcess()); // Create on the main thread to make sure ClearOnShutdown() works. MOZ_ASSERT(NS_IsMainThread()); + // Create the preference manager while we're initializing. + U2FPrefManager::GetOrCreate(); } // static @@ -65,9 +153,10 @@ U2FTokenManager* U2FTokenManager::Get() { } void U2FTokenManager::AbortTransaction(const uint64_t& aTransactionId, - const nsresult& aError) { + const nsresult& aError, + bool shouldCancelActiveDialog) { Unused << mTransactionParent->SendAbort(aTransactionId, aError); - ClearTransaction(); + ClearTransaction(shouldCancelActiveDialog); } void U2FTokenManager::AbortOngoingTransaction() { @@ -76,7 +165,7 @@ void U2FTokenManager::AbortOngoingTransaction() { Unused << mTransactionParent->SendAbort(mLastTransactionId, NS_ERROR_DOM_ABORT_ERR); } - ClearTransaction(); + ClearTransaction(true); } void U2FTokenManager::MaybeClearTransaction( @@ -84,11 +173,15 @@ void U2FTokenManager::MaybeClearTransaction( // Only clear if we've been requested to do so by our current transaction // parent. if (mTransactionParent == aParent) { - ClearTransaction(); + ClearTransaction(true); } } -void U2FTokenManager::ClearTransaction() { +void U2FTokenManager::ClearTransaction(bool send_cancel) { + if (mLastTransactionId && send_cancel) { + // Remove any prompts we might be showing for the current transaction. + SendPromptNotification(kCancelPromptNotificationU2F, mLastTransactionId); + } mTransactionParent = nullptr; // Drop managers at the end of all transactions @@ -103,9 +196,44 @@ void U2FTokenManager::ClearTransaction() { // Clear transaction id. mLastTransactionId = 0; + + // Forget any pending registration. + mPendingRegisterInfo.reset(); + mPendingSignInfo.reset(); + mPendingSignResults.Clear(); +} + +template +void U2FTokenManager::SendPromptNotification(const char16_t* aFormat, + T... aArgs) { + mozilla::ipc::AssertIsOnBackgroundThread(); + + nsAutoString json; + nsTextFormatter::ssprintf(json, aFormat, aArgs...); + + nsCOMPtr r(NewRunnableMethod( + "U2FTokenManager::RunSendPromptNotification", this, + &U2FTokenManager::RunSendPromptNotification, json)); + + MOZ_ALWAYS_SUCCEEDS(GetMainThreadSerialEventTarget()->Dispatch( + r.forget(), NS_DISPATCH_NORMAL)); +} + +void U2FTokenManager::RunSendPromptNotification(const nsString& aJSON) { + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr os = services::GetObserverService(); + if (NS_WARN_IF(!os)) { + return; + } + + nsCOMPtr self = this; + MOZ_ALWAYS_SUCCEEDS( + os->NotifyObservers(self, "webauthn-prompt", aJSON.get())); } RefPtr U2FTokenManager::GetTokenManagerImpl() { + MOZ_ASSERT(U2FPrefManager::Get()); mozilla::ipc::AssertIsOnBackgroundThread(); if (mTokenManagerImpl) { @@ -117,7 +245,14 @@ RefPtr U2FTokenManager::GetTokenManagerImpl() { MOZ_ASSERT(gBackgroundThread, "This should never be null!"); } - return AndroidWebAuthnTokenManager::GetInstance(); +#ifdef MOZ_WIDGET_ANDROID + // On Android, prefer the platform support if enabled. + if (U2FPrefManager::Get()->GetAndroidFido2Enabled()) { + return AndroidWebAuthnTokenManager::GetInstance(); + } +#endif + + return nullptr; } void U2FTokenManager::Register( @@ -131,19 +266,68 @@ void U2FTokenManager::Register( mTokenManagerImpl = GetTokenManagerImpl(); if (!mTokenManagerImpl) { - AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR); + AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR, true); return; } mLastTransactionId = aTransactionId; + // Determine whether direct attestation was requested. + bool noneAttestationRequested = true; + +// On Android, let's always reject direct attestations until we have a +// mechanism to solicit user consent, from Bug 1550164 +#ifndef MOZ_WIDGET_ANDROID + // The default attestation type is "none", so set + // noneAttestationRequested=false only if the RP's preference matches one of + // the other known types. This needs to be reviewed if values are added to + // the AttestationConveyancePreference enum. + const nsString& attestation = + aTransactionInfo.attestationConveyancePreference(); + static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 2); + if (attestation.EqualsLiteral( + MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT) || + attestation.EqualsLiteral( + MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT) || + attestation.EqualsLiteral( + MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ENTERPRISE)) { + noneAttestationRequested = false; + } +#endif // not MOZ_WIDGET_ANDROID + + // Start a register request immediately if direct attestation + // wasn't requested or the test pref is set. + if (noneAttestationRequested || + U2FPrefManager::Get()->GetAllowDirectAttestationForTesting()) { + MOZ_ASSERT(mPendingRegisterInfo.isNothing()); + mPendingRegisterInfo = Some(aTransactionInfo); + DoRegister(aTransactionInfo, noneAttestationRequested); + return; + } + + // If the RP request direct attestation, ask the user for permission and + // store the transaction info until the user proceeds or cancels. + NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin()); + SendPromptNotification(kRegisterDirectPromptNotificationU2F, aTransactionId, + origin.get(), aTransactionInfo.BrowsingContextId()); + + MOZ_ASSERT(mPendingRegisterInfo.isNothing()); + mPendingRegisterInfo = Some(aTransactionInfo); +} + +void U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo, + bool aForceNoneAttestation) { mozilla::ipc::AssertIsOnBackgroundThread(); MOZ_ASSERT(mLastTransactionId > 0); + // Show a prompt that lets the user cancel the ongoing transaction. + NS_ConvertUTF16toUTF8 origin(aInfo.Origin()); + SendPromptNotification(kPresencePromptNotificationU2F, mLastTransactionId, + origin.get(), aInfo.BrowsingContextId(), "false"); + uint64_t tid = mLastTransactionId; - mTokenManagerImpl - ->Register(aTransactionInfo, /* aForceNoneAttestation */ true) + mTokenManagerImpl->Register(aInfo, aForceNoneAttestation) ->Then( GetCurrentSerialEventTarget(), __func__, [tid](WebAuthnMakeCredentialResult&& aResult) { @@ -155,9 +339,14 @@ void U2FTokenManager::Register( [tid](nsresult rv) { MOZ_ASSERT(NS_FAILED(rv)); U2FTokenManager* mgr = U2FTokenManager::Get(); + bool shouldCancelActiveDialog = true; + if (rv == NS_ERROR_DOM_OPERATION_ERR) { + // PIN-related errors. Let the dialog show to inform the user + shouldCancelActiveDialog = false; + } Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED, u"U2FRegisterAbort"_ns, 1); - mgr->MaybeAbortRegister(tid, rv); + mgr->MaybeAbortRegister(tid, rv, shouldCancelActiveDialog); }) ->Track(mRegisterPromise); } @@ -169,14 +358,15 @@ void U2FTokenManager::MaybeConfirmRegister( mRegisterPromise.Complete(); Unused << mTransactionParent->SendConfirmRegister(aTransactionId, aResult); - ClearTransaction(); + ClearTransaction(true); } void U2FTokenManager::MaybeAbortRegister(const uint64_t& aTransactionId, - const nsresult& aError) { + const nsresult& aError, + bool shouldCancelActiveDialog) { MOZ_ASSERT(mLastTransactionId == aTransactionId); mRegisterPromise.Complete(); - AbortTransaction(aTransactionId, aError); + AbortTransaction(aTransactionId, aError, shouldCancelActiveDialog); } void U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent, @@ -189,20 +379,31 @@ void U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent, mTokenManagerImpl = GetTokenManagerImpl(); if (!mTokenManagerImpl) { - AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR); + AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR, true); return; } mLastTransactionId = aTransactionId; + mPendingSignInfo = Some(aTransactionInfo); + DoSign(aTransactionInfo); +} +void U2FTokenManager::DoSign(const WebAuthnGetAssertionInfo& aTransactionInfo) { mozilla::ipc::AssertIsOnBackgroundThread(); MOZ_ASSERT(mLastTransactionId > 0); uint64_t tid = mLastTransactionId; + NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin()); + uint64_t browserCtxId = aTransactionInfo.BrowsingContextId(); + + // Show a prompt that lets the user cancel the ongoing transaction. + SendPromptNotification(kPresencePromptNotificationU2F, tid, origin.get(), + browserCtxId, "false"); + mTokenManagerImpl->Sign(aTransactionInfo) ->Then( GetCurrentSerialEventTarget(), __func__, - [tid](nsTArray&& aResult) { + [tid, origin](nsTArray&& aResult) { U2FTokenManager* mgr = U2FTokenManager::Get(); Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED, u"U2FSignFinish"_ns, 1); @@ -214,9 +415,14 @@ void U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent, [tid](nsresult rv) { MOZ_ASSERT(NS_FAILED(rv)); U2FTokenManager* mgr = U2FTokenManager::Get(); + bool shouldCancelActiveDialog = true; + if (rv == NS_ERROR_DOM_OPERATION_ERR) { + // PIN-related errors. Let the dialog show to inform the user + shouldCancelActiveDialog = false; + } Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED, u"U2FSignAbort"_ns, 1); - mgr->MaybeAbortSign(tid, rv); + mgr->MaybeAbortSign(tid, rv, shouldCancelActiveDialog); }) ->Track(mSignPromise); } @@ -227,14 +433,15 @@ void U2FTokenManager::MaybeConfirmSign( mSignPromise.Complete(); Unused << mTransactionParent->SendConfirmSign(aTransactionId, aResult); - ClearTransaction(); + ClearTransaction(true); } void U2FTokenManager::MaybeAbortSign(const uint64_t& aTransactionId, - const nsresult& aError) { + const nsresult& aError, + bool shouldCancelActiveDialog) { MOZ_ASSERT(mLastTransactionId == aTransactionId); mSignPromise.Complete(); - AbortTransaction(aTransactionId, aError); + AbortTransaction(aTransactionId, aError, shouldCancelActiveDialog); } void U2FTokenManager::Cancel(PWebAuthnTransactionParent* aParent, @@ -248,7 +455,88 @@ void U2FTokenManager::Cancel(PWebAuthnTransactionParent* aParent, } mTokenManagerImpl->Cancel(); - ClearTransaction(); + ClearTransaction(true); +} + +// nsIU2FTokenManager + +NS_IMETHODIMP +U2FTokenManager::ResumeRegister(uint64_t aTransactionId, + bool aForceNoneAttestation) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (!gBackgroundThread) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr r(NewRunnableMethod( + "U2FTokenManager::RunResumeRegister", this, + &U2FTokenManager::RunResumeRegister, aTransactionId, + aForceNoneAttestation)); + + return gBackgroundThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); +} + +void U2FTokenManager::RunResumeRegister(uint64_t aTransactionId, + bool aForceNoneAttestation) { + mozilla::ipc::AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(mPendingRegisterInfo.isNothing())) { + return; + } + + if (mLastTransactionId != aTransactionId) { + return; + } + + // Resume registration and cleanup. + DoRegister(mPendingRegisterInfo.ref(), aForceNoneAttestation); +} + +void U2FTokenManager::RunResumeSign(uint64_t aTransactionId) { + mozilla::ipc::AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(mPendingSignInfo.isNothing())) { + return; + } + + if (mLastTransactionId != aTransactionId) { + return; + } + + // Resume sign and cleanup. + DoSign(mPendingSignInfo.ref()); +} + +NS_IMETHODIMP +U2FTokenManager::Cancel(uint64_t aTransactionId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (!gBackgroundThread) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr r( + NewRunnableMethod("U2FTokenManager::RunCancel", this, + &U2FTokenManager::RunCancel, aTransactionId)); + + return gBackgroundThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); +} + +void U2FTokenManager::RunCancel(uint64_t aTransactionId) { + mozilla::ipc::AssertIsOnBackgroundThread(); + + if (mLastTransactionId != aTransactionId) { + return; + } + + // Cancel the request. + mTokenManagerImpl->Cancel(); + + // Reject the promise. + AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR, true); } } // namespace mozilla::dom diff --git a/dom/webauthn/U2FTokenManager.h b/dom/webauthn/U2FTokenManager.h index 936683eaf2a7..83c9b14b110e 100644 --- a/dom/webauthn/U2FTokenManager.h +++ b/dom/webauthn/U2FTokenManager.h @@ -7,6 +7,7 @@ #ifndef mozilla_dom_U2FTokenManager_h #define mozilla_dom_U2FTokenManager_h +#include "nsIU2FTokenManager.h" #include "mozilla/dom/U2FTokenTransport.h" #include "mozilla/dom/PWebAuthnTransaction.h" #include "mozilla/Tainting.h" @@ -26,8 +27,11 @@ namespace mozilla::dom { class U2FSoftTokenManager; class WebAuthnTransactionParent; -class U2FTokenManager final { +class U2FTokenManager final : public nsIU2FTokenManager { public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIU2FTOKENMANAGER + static U2FTokenManager* Get(); void Register(PWebAuthnTransactionParent* aTransactionParent, const uint64_t& aTransactionId, @@ -40,21 +44,56 @@ class U2FTokenManager final { void MaybeClearTransaction(PWebAuthnTransactionParent* aParent); static void Initialize(); - U2FTokenManager(); - ~U2FTokenManager() = default; + Maybe GetCurrentOrigin() { + if (mPendingRegisterInfo.isSome()) { + return Some(mPendingRegisterInfo.value().Origin()); + } + + if (mPendingSignInfo.isSome()) { + return Some(mPendingSignInfo.value().Origin()); + } + return Nothing(); + } + + uint64_t GetCurrentTransactionId() { return mLastTransactionId; } + + bool CurrentTransactionIsRegister() { return mPendingRegisterInfo.isSome(); } + + bool CurrentTransactionIsSign() { return mPendingSignInfo.isSome(); } + + // Sends a "webauthn-prompt" observer notification with the given data. + template + void SendPromptNotification(const char16_t* aFormat, T... aArgs); + // The main thread runnable function for "SendPromptNotification". + void RunSendPromptNotification(const nsString& aJSON); private: + U2FTokenManager(); + ~U2FTokenManager() = default; RefPtr GetTokenManagerImpl(); - void AbortTransaction(const uint64_t& aTransactionId, const nsresult& aError); + void AbortTransaction(const uint64_t& aTransactionId, const nsresult& aError, + bool shouldCancelActiveDialog); void AbortOngoingTransaction(); - void ClearTransaction(); + void ClearTransaction(bool send_cancel); + // Step two of "Register", kicking off the actual transaction. + void DoRegister(const WebAuthnMakeCredentialInfo& aInfo, + bool aForceNoneAttestation); + void DoSign(const WebAuthnGetAssertionInfo& aTransactionInfo); void MaybeConfirmRegister(const uint64_t& aTransactionId, const WebAuthnMakeCredentialResult& aResult); void MaybeAbortRegister(const uint64_t& aTransactionId, - const nsresult& aError); + const nsresult& aError, + bool shouldCancelActiveDialog); void MaybeConfirmSign(const uint64_t& aTransactionId, const WebAuthnGetAssertionResult& aResult); - void MaybeAbortSign(const uint64_t& aTransactionId, const nsresult& aError); + void MaybeAbortSign(const uint64_t& aTransactionId, const nsresult& aError, + bool shouldCancelActiveDialog); + // The main thread runnable function for "nsIU2FTokenManager.ResumeRegister". + void RunResumeRegister(uint64_t aTransactionId, bool aForceNoneAttestation); + void RunResumeSign(uint64_t aTransactionId); + void RunResumeWithSelectedSignResult(uint64_t aTransactionId, uint64_t idx); + // The main thread runnable function for "nsIU2FTokenManager.Cancel". + void RunCancel(uint64_t aTransactionId); // 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. @@ -66,6 +105,11 @@ class U2FTokenManager final { // guards any cancel messages to ensure we don't cancel newer transactions // due to a stale message. uint64_t mLastTransactionId; + // Pending registration info while we wait for user input. + Maybe mPendingRegisterInfo; + // Pending registration info while we wait for user input. + Maybe mPendingSignInfo; + nsTArray mPendingSignResults; }; } // namespace mozilla::dom diff --git a/dom/webauthn/WebAuthnController.cpp b/dom/webauthn/WebAuthnController.cpp index 06fc7a6e7ce1..8b1a05c37933 100644 --- a/dom/webauthn/WebAuthnController.cpp +++ b/dom/webauthn/WebAuthnController.cpp @@ -44,22 +44,23 @@ static nsIThread* gWebAuthnBackgroundThread; // Data for WebAuthn UI prompt notifications. static const char16_t kPresencePromptNotification[] = - u"{\"action\":\"presence\",\"tid\":%llu," + u"{\"is_ctap2\":true,\"action\":\"presence\",\"tid\":%llu," u"\"origin\":\"%s\",\"browsingContextId\":%llu}"; static const char16_t kRegisterDirectPromptNotification[] = - u"{\"action\":\"register-direct\",\"tid\":%llu," + u"{\"is_ctap2\":true,\"action\":\"register-direct\",\"tid\":%llu," u"\"origin\":\"%s\",\"browsingContextId\":%llu}"; static const char16_t kCancelPromptNotification[] = - u"{\"action\":\"cancel\",\"tid\":%llu}"; + u"{\"is_ctap2\":true,\"action\":\"cancel\",\"tid\":%llu}"; static const char16_t kSelectSignResultNotification[] = - u"{\"action\":\"select-sign-result\",\"tid\":%llu," + u"{\"is_ctap2\":true,\"action\":\"select-sign-result\",\"tid\":%llu," u"\"origin\":\"%s\",\"browsingContextId\":%llu,\"usernames\":[%s]}"; /*********************************************************************** * U2FManager Implementation **********************************************************************/ -NS_IMPL_ISUPPORTS(WebAuthnController, nsIWebAuthnController); +NS_IMPL_ISUPPORTS(WebAuthnController, nsIWebAuthnController, + nsIU2FTokenManager); WebAuthnController::WebAuthnController() : mTransactionParent(nullptr) { MOZ_ASSERT(XRE_IsParentProcess()); diff --git a/dom/webauthn/WebAuthnController.h b/dom/webauthn/WebAuthnController.h index 3dd65175da90..9497b146f628 100644 --- a/dom/webauthn/WebAuthnController.h +++ b/dom/webauthn/WebAuthnController.h @@ -23,6 +23,7 @@ namespace mozilla::dom { class WebAuthnController final : public nsIWebAuthnController { public: NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIU2FTOKENMANAGER NS_DECL_NSIWEBAUTHNCONTROLLER static void Initialize(); diff --git a/dom/webauthn/WebAuthnTransactionParent.cpp b/dom/webauthn/WebAuthnTransactionParent.cpp index 6151cc13f889..b868694c856a 100644 --- a/dom/webauthn/WebAuthnTransactionParent.cpp +++ b/dom/webauthn/WebAuthnTransactionParent.cpp @@ -6,16 +6,13 @@ #include "mozilla/dom/WebAuthnTransactionParent.h" #include "mozilla/dom/WebAuthnController.h" +#include "mozilla/dom/U2FTokenManager.h" #include "mozilla/ipc/PBackgroundParent.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/StaticPrefs_security.h" #include "nsThreadUtils.h" -#ifdef MOZ_WIDGET_ANDROID -# include "mozilla/dom/U2FTokenManager.h" -#endif - #ifdef XP_WIN # include "WinWebAuthnManager.h" #endif diff --git a/dom/webauthn/authrs_bridge/src/lib.rs b/dom/webauthn/authrs_bridge/src/lib.rs index 1574b793a7fc..0efc6d44b2c1 100644 --- a/dom/webauthn/authrs_bridge/src/lib.rs +++ b/dom/webauthn/authrs_bridge/src/lib.rs @@ -47,7 +47,7 @@ use test_token::TestTokenManager; fn make_prompt(action: &str, tid: u64, origin: &str, browsing_context_id: u64) -> String { format!( - r#"{{"action":"{action}","tid":{tid},"origin":"{origin}","browsingContextId":{browsing_context_id}}}"#, + r#"{{"is_ctap2":true,"action":"{action}","tid":{tid},"origin":"{origin}","browsingContextId":{browsing_context_id}}}"#, ) } @@ -58,7 +58,7 @@ fn make_uv_invalid_error_prompt( retries: i64, ) -> String { format!( - r#"{{"action":"uv-invalid","tid":{tid},"origin":"{origin}","browsingContextId":{browsing_context_id},"retriesLeft":{retries}}}"#, + r#"{{"is_ctap2":true,"action":"uv-invalid","tid":{tid},"origin":"{origin}","browsingContextId":{browsing_context_id},"retriesLeft":{retries}}}"#, ) } @@ -70,7 +70,7 @@ fn make_pin_required_prompt( retries: i64, ) -> String { format!( - r#"{{"action":"pin-required","tid":{tid},"origin":"{origin}","browsingContextId":{browsing_context_id},"wasInvalid":{was_invalid},"retriesLeft":{retries}}}"#, + r#"{{"is_ctap2":true,"action":"pin-required","tid":{tid},"origin":"{origin}","browsingContextId":{browsing_context_id},"wasInvalid":{was_invalid},"retriesLeft":{retries}}}"#, ) } diff --git a/dom/webauthn/moz.build b/dom/webauthn/moz.build index dae8e01cec16..b3639491269e 100644 --- a/dom/webauthn/moz.build +++ b/dom/webauthn/moz.build @@ -13,7 +13,7 @@ XPCOM_MANIFESTS += [ "components.conf", ] -XPIDL_SOURCES += ["nsIWebAuthnController.idl"] +XPIDL_SOURCES += ["nsIU2FTokenManager.idl", "nsIWebAuthnController.idl"] XPIDL_MODULE = "dom_webauthn" @@ -22,6 +22,7 @@ EXPORTS.mozilla.dom += [ "AuthenticatorAttestationResponse.h", "AuthenticatorResponse.h", "PublicKeyCredential.h", + "U2FTokenManager.h", "U2FTokenTransport.h", "WebAuthnController.h", "WebAuthnManager.h", @@ -39,6 +40,7 @@ UNIFIED_SOURCES += [ "AuthrsTransport.cpp", "CtapArgs.cpp", "PublicKeyCredential.cpp", + "U2FTokenManager.cpp", "WebAuthnController.cpp", "WebAuthnManager.cpp", "WebAuthnManagerBase.cpp", @@ -66,11 +68,9 @@ USE_LIBS += [ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": EXPORTS.mozilla.dom += [ "AndroidWebAuthnTokenManager.h", - "U2FTokenManager.h", ] UNIFIED_SOURCES += [ "AndroidWebAuthnTokenManager.cpp", - "U2FTokenManager.cpp", ] if CONFIG["OS_ARCH"] == "WINNT": diff --git a/dom/webauthn/nsIU2FTokenManager.idl b/dom/webauthn/nsIU2FTokenManager.idl new file mode 100644 index 000000000000..4f835dc85324 --- /dev/null +++ b/dom/webauthn/nsIU2FTokenManager.idl @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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 "nsISupports.idl" + +/** + * TODO(1737205,1819414) Fold this interface into nsIWebAuthnController when we + * remove the legacy U2F DOM API. + * + * nsIU2FTokenManager + * + * An interface to the U2FTokenManager singleton. + * + * This should be used only by the WebAuthn browser UI prompts. + */ + +[scriptable, uuid(745e1eac-e449-4342-bca1-ee0e6ead09fc)] +interface nsIU2FTokenManager : nsISupports +{ + /** + * Resumes the current WebAuthn/U2F transaction if that matches the given + * transaction ID. This is used only when direct attestation was requested + * and we have to wait for user input to proceed. + * + * @param aTransactionID : The ID of the transaction to resume. + * @param aForceNoneAttestation : The user might enforce none attestation. + */ + void resumeRegister(in uint64_t aTransactionID, + in bool aForceNoneAttestation); + + /** + * Cancels the current WebAuthn/U2F transaction if that matches the given + * transaction ID. + * + * @param aTransactionID : The ID of the transaction to cancel. + */ + void cancel(in uint64_t aTransactionID); +}; diff --git a/dom/webauthn/nsIWebAuthnController.idl b/dom/webauthn/nsIWebAuthnController.idl index 68a3f443dc5e..3d0f084fca0d 100644 --- a/dom/webauthn/nsIWebAuthnController.idl +++ b/dom/webauthn/nsIWebAuthnController.idl @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" +#include "nsIU2FTokenManager.idl" typedef long COSEAlgorithmIdentifier; @@ -178,17 +179,14 @@ interface nsICtapSignResult : nsISupports { // 3) return results to the content process. // [scriptable, uuid(c0744f48-ad64-11ed-b515-cf5149f4d6a6)] -interface nsIWebAuthnController : nsISupports +interface nsIWebAuthnController : nsIU2FTokenManager { // Prompt callbacks void pinCallback(in uint64_t aTransactionId, in ACString aPin); void signatureSelectionCallback(in uint64_t aTransactionId, in uint64_t aIndex); - void resumeRegister(in uint64_t aTransactionID, in bool aForceNoneAttestation); // Authenticator callbacks [noscript] void sendPromptNotificationPreformatted(in uint64_t aTransactionId, in ACString aJSON); - // Cancel the transaction with the given ID. - [noscript] void cancel(in uint64_t aTransactionID); [noscript] void finishRegister(in uint64_t aTransactionId, in nsICtapRegisterResult aResult); [noscript] void finishSign(in uint64_t aTransactionId, in Array aResult); }; diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 07038c4a56c8..1ca4603d1434 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -104,9 +104,7 @@ #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 +#include "mozilla/dom/U2FTokenManager.h" #include "mozilla/dom/WebAuthnController.h" #ifdef XP_WIN # include "mozilla/dom/WinWebAuthnManager.h" @@ -261,10 +259,7 @@ nsresult nsLayoutStatics::Initialize() { // This must be initialized on the main-thread. mozilla::RemoteLazyInputStreamStorage::Initialize(); -#ifdef MOZ_WIDGET_ANDROID mozilla::dom::U2FTokenManager::Initialize(); -#endif - mozilla::dom::WebAuthnController::Initialize(); #ifdef XP_WIN -- 2.11.4.GIT