Bug 1275479 - Refactor U2F Token Interface (Part 1). r=keeler
authorJ.C. Jones <jjones@mozilla.com>
Tue, 31 May 2016 20:51:24 -0700
changeset 340979 b7d82acfe62e5cd86b5bbc4111b08e1b5e0d0a6d
parent 340978 3a48b194e35bea1b0939791e3b46ac033d7a85a1
child 340980 52bd4c6b7f7c33ae50c6a4b5c7b75c998cd620b9
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1275479
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1275479 - Refactor U2F Token Interface (Part 1). r=keeler Rework U2F.cpp to use a collection of nsINSSU2FToken for U2F/WebAuth operations. MozReview-Commit-ID: 9qwllawzOWh
dom/u2f/NSSU2FTokenRemote.cpp
dom/u2f/NSSU2FTokenRemote.h
dom/u2f/U2F.cpp
dom/u2f/U2F.h
dom/u2f/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/u2f/NSSU2FTokenRemote.cpp
@@ -0,0 +1,156 @@
+/* -*- 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/dom/ContentChild.h"
+
+#include "NSSU2FTokenRemote.h"
+
+using mozilla::dom::ContentChild;
+
+NS_IMPL_ISUPPORTS(NSSU2FTokenRemote, nsINSSU2FToken)
+
+static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
+
+NSSU2FTokenRemote::NSSU2FTokenRemote()
+{}
+
+NSSU2FTokenRemote::~NSSU2FTokenRemote()
+{}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::Init()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::IsCompatibleVersion(const nsAString& aVersionString,
+                                       bool* aIsCompatible)
+{
+  NS_ENSURE_ARG_POINTER(aIsCompatible);
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+  if (!cc->SendNSSU2FTokenIsCompatibleVersion(
+        nsString(aVersionString), aIsCompatible)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::IsRegistered(uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
+                                bool* aIsRegistered)
+{
+  NS_ENSURE_ARG_POINTER(aKeyHandle);
+  NS_ENSURE_ARG_POINTER(aIsRegistered);
+
+  nsTArray<uint8_t> keyHandle;
+  if (!keyHandle.ReplaceElementsAt(0, keyHandle.Length(), aKeyHandle,
+                                   aKeyHandleLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+  if (!cc->SendNSSU2FTokenIsRegistered(keyHandle, aIsRegistered)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::Register(uint8_t* aApplication,
+                            uint32_t aApplicationLen,
+                            uint8_t* aChallenge,
+                            uint32_t aChallengeLen,
+                            uint8_t** aRegistration,
+                            uint32_t* aRegistrationLen)
+{
+  NS_ENSURE_ARG_POINTER(aApplication);
+  NS_ENSURE_ARG_POINTER(aChallenge);
+  NS_ENSURE_ARG_POINTER(aRegistration);
+  NS_ENSURE_ARG_POINTER(aRegistrationLen);
+
+  nsTArray<uint8_t> application;
+  if (!application.ReplaceElementsAt(0, application.Length(), aApplication,
+                                     aApplicationLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  nsTArray<uint8_t> challenge;
+  if (!challenge.ReplaceElementsAt(0, challenge.Length(), aChallenge,
+                                   aChallengeLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsTArray<uint8_t> registrationBuffer;
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+  if (!cc->SendNSSU2FTokenRegister(application, challenge,
+                                   &registrationBuffer)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  size_t dataLen = registrationBuffer.Length();
+  uint8_t* tmp = reinterpret_cast<uint8_t*>(moz_xmalloc(dataLen));
+  if (NS_WARN_IF(!tmp)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  memcpy(tmp, registrationBuffer.Elements(), dataLen);
+  *aRegistration = tmp;
+  *aRegistrationLen = dataLen;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::Sign(uint8_t* aApplication, uint32_t aApplicationLen,
+                        uint8_t* aChallenge, uint32_t aChallengeLen,
+                        uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
+                        uint8_t** aSignature, uint32_t* aSignatureLen)
+{
+  NS_ENSURE_ARG_POINTER(aApplication);
+  NS_ENSURE_ARG_POINTER(aChallenge);
+  NS_ENSURE_ARG_POINTER(aKeyHandle);
+  NS_ENSURE_ARG_POINTER(aSignature);
+  NS_ENSURE_ARG_POINTER(aSignatureLen);
+
+  nsTArray<uint8_t> application;
+  if (!application.ReplaceElementsAt(0, application.Length(), aApplication,
+                                     aApplicationLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsTArray<uint8_t> challenge;
+  if (!challenge.ReplaceElementsAt(0, challenge.Length(), aChallenge,
+                                   aChallengeLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  nsTArray<uint8_t> keyHandle;
+  if (!keyHandle.ReplaceElementsAt(0, keyHandle.Length(), aKeyHandle,
+                                   aKeyHandleLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsTArray<uint8_t> signatureBuffer;
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+  if (!cc->SendNSSU2FTokenSign(application, challenge, keyHandle,
+                               &signatureBuffer)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  size_t dataLen = signatureBuffer.Length();
+  uint8_t* tmp = reinterpret_cast<uint8_t*>(moz_xmalloc(dataLen));
+  if (NS_WARN_IF(!tmp)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  memcpy(tmp, signatureBuffer.Elements(), dataLen);
+  *aSignature = tmp;
+  *aSignatureLen = dataLen;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/u2f/NSSU2FTokenRemote.h
@@ -0,0 +1,24 @@
+/* -*- 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 NSSU2FTokenRemote_h
+#define NSSU2FTokenRemote_h
+
+#include "nsINSSU2FToken.h"
+
+class NSSU2FTokenRemote : public nsINSSU2FToken
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSINSSU2FTOKEN
+
+  NSSU2FTokenRemote();
+
+private:
+  virtual ~NSSU2FTokenRemote();
+};
+
+#endif // NSSU2FTokenRemote_h
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "hasht.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/CryptoBuffer.h"
+#include "mozilla/dom/NSSU2FTokenRemote.h"
 #include "mozilla/dom/U2F.h"
 #include "mozilla/dom/U2FBinding.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
 #include "nsNetCID.h"
 #include "nsNSSComponent.h"
 #include "nsURLParsers.h"
 #include "pk11pub.h"
@@ -32,17 +33,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(U2F)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
 
-static mozilla::LazyLogModule gU2FLog("webauth_u2f");
+static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
 
 template <class CB, class Rsp>
 void
 SendError(CB* aCallback, ErrorCode aErrorCode)
 {
   Rsp response;
   response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
 
@@ -69,143 +70,35 @@ AssembleClientData(const nsAString& aOri
 
   if (NS_WARN_IF(!aClientData.Assign(NS_ConvertUTF16toUTF8(json)))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-static nsresult
-NSSTokenIsCompatible(nsINSSU2FToken* aNSSToken, const nsString& aVersionString,
-                     bool* aIsCompatible)
-{
-  MOZ_ASSERT(aIsCompatible);
-
-  if (XRE_IsParentProcess()) {
-    MOZ_ASSERT(aNSSToken);
-    return aNSSToken->IsCompatibleVersion(aVersionString, aIsCompatible);
-  }
-
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  if (!cc->SendNSSU2FTokenIsCompatibleVersion(aVersionString, aIsCompatible)) {
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-static nsresult
-NSSTokenIsRegistered(nsINSSU2FToken* aNSSToken, CryptoBuffer& aKeyHandle,
-                     bool* aIsRegistered)
-{
-  MOZ_ASSERT(aIsRegistered);
-
-  if (XRE_IsParentProcess()) {
-    MOZ_ASSERT(aNSSToken);
-    return aNSSToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(),
-                                   aIsRegistered);
-  }
-
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  if (!cc->SendNSSU2FTokenIsRegistered(aKeyHandle, aIsRegistered)) {
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-static nsresult
-NSSTokenSign(nsINSSU2FToken* aNSSToken, CryptoBuffer& aKeyHandle,
-             CryptoBuffer& aApplication, CryptoBuffer& aChallenge,
-             CryptoBuffer& aSignatureData)
-{
-  if (XRE_IsParentProcess()) {
-    MOZ_ASSERT(aNSSToken);
-    uint8_t* buffer;
-    uint32_t bufferlen;
-    nsresult rv = aNSSToken->Sign(aApplication.Elements(), aApplication.Length(),
-                                  aChallenge.Elements(), aChallenge.Length(),
-                                  aKeyHandle.Elements(), aKeyHandle.Length(),
-                                  &buffer, &bufferlen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    MOZ_ASSERT(buffer);
-    aSignatureData.Assign(buffer, bufferlen);
-    free(buffer);
-    return NS_OK;
-  }
-
-  nsTArray<uint8_t> signatureBuffer;
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  if (!cc->SendNSSU2FTokenSign(aApplication, aChallenge, aKeyHandle,
-                               &signatureBuffer)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aSignatureData.Assign(signatureBuffer);
-  return NS_OK;
-}
-
-static nsresult
-NSSTokenRegister(nsINSSU2FToken* aNSSToken, CryptoBuffer& aApplication,
-                 CryptoBuffer& aChallenge, CryptoBuffer& aRegistrationData)
-{
-  if (XRE_IsParentProcess()) {
-    MOZ_ASSERT(aNSSToken);
-    uint8_t* buffer;
-    uint32_t bufferlen;
-    nsresult rv;
-    rv = aNSSToken->Register(aApplication.Elements(), aApplication.Length(),
-                             aChallenge.Elements(), aChallenge.Length(),
-                             &buffer, &bufferlen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    MOZ_ASSERT(buffer);
-    aRegistrationData.Assign(buffer, bufferlen);
-    free(buffer);
-    return NS_OK;
-  }
-
-  nsTArray<uint8_t> registrationBuffer;
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  if (!cc->SendNSSU2FTokenRegister(aApplication, aChallenge,
-                                   &registrationBuffer)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aRegistrationData.Assign(registrationBuffer);
-  return NS_OK;
-}
-
 U2FTask::U2FTask(const nsAString& aOrigin, const nsAString& aAppId)
   : mOrigin(aOrigin)
   , mAppId(aAppId)
 {}
 
 U2FTask::~U2FTask()
 {}
 
 U2FRegisterTask::U2FRegisterTask(const nsAString& aOrigin,
                                  const nsAString& aAppId,
                                  const Sequence<RegisterRequest>& aRegisterRequests,
                                  const Sequence<RegisteredKey>& aRegisteredKeys,
                                  U2FRegisterCallback* aCallback,
-                                 const nsCOMPtr<nsINSSU2FToken>& aNSSToken)
+                                 const Sequence<Authenticator>& aAuthenticators)
   : U2FTask(aOrigin, aAppId)
   , mRegisterRequests(aRegisterRequests)
   , mRegisteredKeys(aRegisteredKeys)
   , mCallback(aCallback)
-  , mNSSToken(aNSSToken)
+  , mAuthenticators(aAuthenticators)
 {}
 
 U2FRegisterTask::~U2FRegisterTask()
 {
   nsNSSShutDownPreventionLock locker;
 
   if (isAlreadyShutDown()) {
     return;
@@ -223,20 +116,16 @@ NS_IMETHODIMP
 U2FRegisterTask::Run()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     ReturnError(ErrorCode::OTHER_ERROR);
     return NS_ERROR_FAILURE;
   }
 
-  // TODO: Implement USB Tokens in Bug 1245527
-  const bool softTokenEnabled =
-    Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
-
   for (size_t i = 0; i < mRegisteredKeys.Length(); ++i) {
     RegisteredKey request(mRegisteredKeys[i]);
 
     // Check for required attributes
     if (!(request.mKeyHandle.WasPassed() &&
           request.mVersion.WasPassed())) {
       continue;
     }
@@ -257,25 +146,28 @@ U2FRegisterTask::Run()
     // We ignore mTransports, as it is intended to be used for sorting the
     // available devices by preference, but is not an exclusion factor.
 
     bool isCompatible = false;
     bool isRegistered = false;
 
     // Determine if the provided keyHandle is registered at any device. If so,
     // then we'll return DEVICE_INELIGIBLE to signify we're already registered.
-    if (softTokenEnabled) {
-      rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
-                                &isCompatible);
+    for (auto token : mAuthenticators) {
+      rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
+      if (!isCompatible) {
+        continue;
+      }
 
-      rv = NSSTokenIsRegistered(mNSSToken, keyHandle, &isRegistered);
+      rv = token->IsRegistered(keyHandle.Elements(), keyHandle.Length(),
+                               &isRegistered);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
 
       if (isCompatible && isRegistered) {
         ReturnError(ErrorCode::DEVICE_INELIGIBLE);
         return NS_OK;
@@ -328,31 +220,40 @@ U2FRegisterTask::Run()
       return NS_ERROR_FAILURE;
     }
 
     // Get the registration data from the token
     CryptoBuffer regData;
     bool registerSuccess = false;
     bool isCompatible = false;
 
-    if (!registerSuccess && softTokenEnabled) {
-      rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
-                                &isCompatible);
+    for (auto token : mAuthenticators) {
+      rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
 
       if (isCompatible) {
-        rv = NSSTokenRegister(mNSSToken, appParam, challengeParam, regData);
+        uint8_t* buffer;
+        uint32_t bufferlen;
+        nsresult rv;
+        rv = token->Register(appParam.Elements(), appParam.Length(),
+                             challengeParam.Elements(), challengeParam.Length(),
+                             &buffer, &bufferlen);
         if (NS_FAILED(rv)) {
           ReturnError(ErrorCode::OTHER_ERROR);
           return NS_ERROR_FAILURE;
         }
+
+        MOZ_ASSERT(buffer);
+        regData.Assign(buffer, bufferlen);
+        free(buffer);
         registerSuccess = true;
+        break;
       }
     }
 
     if (!registerSuccess) {
       // Try another request
       continue;
     }
 
@@ -386,22 +287,22 @@ U2FRegisterTask::Run()
   return NS_ERROR_FAILURE;
 }
 
 U2FSignTask::U2FSignTask(const nsAString& aOrigin,
                          const nsAString& aAppId,
                          const nsAString& aChallenge,
                          const Sequence<RegisteredKey>& aRegisteredKeys,
                          U2FSignCallback* aCallback,
-                         const nsCOMPtr<nsINSSU2FToken>& aNSSToken)
+                         const Sequence<Authenticator>& aAuthenticators)
   : U2FTask(aOrigin, aAppId)
   , mChallenge(aChallenge)
   , mRegisteredKeys(aRegisteredKeys)
   , mCallback(aCallback)
-  , mNSSToken(aNSSToken)
+  , mAuthenticators(aAuthenticators)
 {}
 
 U2FSignTask::~U2FSignTask()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return;
   }
@@ -418,20 +319,16 @@ NS_IMETHODIMP
 U2FSignTask::Run()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     ReturnError(ErrorCode::OTHER_ERROR);
     return NS_ERROR_FAILURE;
   }
 
-  // TODO: Implement USB Tokens in Bug 1245527
-  const bool softTokenEnabled =
-    Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
-
   // Search the requests for one a token can fulfill
   for (size_t i = 0; i < mRegisteredKeys.Length(); i += 1) {
     RegisteredKey request(mRegisteredKeys[i]);
 
     // Check for required attributes
     if (!(request.mVersion.WasPassed() &&
           request.mKeyHandle.WasPassed())) {
       continue;
@@ -487,40 +384,52 @@ U2FSignTask::Run()
 
     // Get the signature from the token
     CryptoBuffer signatureData;
     bool signSuccess = false;
 
     // We ignore mTransports, as it is intended to be used for sorting the
     // available devices by preference, but is not an exclusion factor.
 
-    if (!signSuccess && softTokenEnabled) {
+    for (size_t a = 0; a < mAuthenticators.Length() && !signSuccess; ++a) {
+      Authenticator token(mAuthenticators[a]);
       bool isCompatible = false;
       bool isRegistered = false;
 
-      rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
-                                &isCompatible);
+      rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
+      if (!isCompatible) {
+        continue;
+      }
 
-      rv = NSSTokenIsRegistered(mNSSToken, keyHandle, &isRegistered);
+      rv = token->IsRegistered(keyHandle.Elements(), keyHandle.Length(),
+                               &isRegistered);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
 
       if (isCompatible && isRegistered) {
-        rv = NSSTokenSign(mNSSToken, keyHandle, appParam, challengeParam,
-                          signatureData);
+        uint8_t* buffer;
+        uint32_t bufferlen;
+        nsresult rv = token->Sign(appParam.Elements(), appParam.Length(),
+                                  challengeParam.Elements(), challengeParam.Length(),
+                                  keyHandle.Elements(), keyHandle.Length(),
+                                  &buffer, &bufferlen);
         if (NS_FAILED(rv)) {
           ReturnError(ErrorCode::OTHER_ERROR);
           return NS_ERROR_FAILURE;
         }
+
+        MOZ_ASSERT(buffer);
+        signatureData.Assign(buffer, bufferlen);
+        free(buffer);
         signSuccess = true;
       }
     }
 
     if (!signSuccess) {
       // Try another request
       continue;
     }
@@ -671,61 +580,76 @@ U2F::Init(nsPIDOMWindowInner* aParent, E
   }
 
   if (NS_WARN_IF(mOrigin.IsEmpty())) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (!EnsureNSSInitializedChromeOrContent()) {
-    MOZ_LOG(gU2FLog, LogLevel::Debug, ("Failed to get NSS context for U2F"));
+    MOZ_LOG(gWebauthLog, LogLevel::Debug, ("Failed to get NSS context for U2F"));
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  if (XRE_IsParentProcess()) {
-    mNSSToken = do_GetService(NS_NSSU2FTOKEN_CONTRACTID);
-    if (NS_WARN_IF(!mNSSToken)) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
+  if (Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED)) {
+    if (!XRE_IsParentProcess()) {
+      MOZ_LOG(gWebauthLog, LogLevel::Debug,
+        ("Is e10s Process, getting remote U2F soft token"));
+
+      if (!mAuthenticators.AppendElement(new NSSU2FTokenRemote(),
+                                         mozilla::fallible)) {
+        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return;
+      }
+    } else {
+       MOZ_LOG(gWebauthLog, LogLevel::Debug,
+        ("Is non-e10s Process, getting direct U2F soft token"));
+
+      nsCOMPtr<nsINSSU2FToken> softToken =
+        do_GetService(NS_NSSU2FTOKEN_CONTRACTID);
+      if (NS_WARN_IF(!softToken)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+
+      if (!mAuthenticators.AppendElement(softToken, mozilla::fallible)) {
+        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return;
+      }
     }
   }
-
-  aRv = mUSBToken.Init();
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
 }
 
 void
 U2F::Register(const nsAString& aAppId,
               const Sequence<RegisterRequest>& aRegisterRequests,
               const Sequence<RegisteredKey>& aRegisteredKeys,
               U2FRegisterCallback& aCallback,
               const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
               ErrorResult& aRv)
 {
   RefPtr<U2FRegisterTask> registerTask = new U2FRegisterTask(mOrigin, aAppId,
                                                              aRegisterRequests,
                                                              aRegisteredKeys,
                                                              &aCallback,
-                                                             mNSSToken);
+                                                             mAuthenticators);
 
   EvaluateAppIDAndRunTask(registerTask);
 }
 
 void
 U2F::Sign(const nsAString& aAppId,
           const nsAString& aChallenge,
           const Sequence<RegisteredKey>& aRegisteredKeys,
           U2FSignCallback& aCallback,
           const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
           ErrorResult& aRv)
 {
   RefPtr<U2FSignTask> signTask = new U2FSignTask(mOrigin, aAppId, aChallenge,
                                                  aRegisteredKeys, &aCallback,
-                                                 mNSSToken);
+                                                 mAuthenticators);
 
   EvaluateAppIDAndRunTask(signTask);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/u2f/U2F.h
+++ b/dom/u2f/U2F.h
@@ -35,16 +35,18 @@ enum class ErrorCode {
   OK = 0,
   OTHER_ERROR = 1,
   BAD_REQUEST = 2,
   CONFIGURATION_UNSUPPORTED = 3,
   DEVICE_INELIGIBLE = 4,
   TIMEOUT = 5
 };
 
+typedef nsCOMPtr<nsINSSU2FToken> Authenticator;
+
 class U2FTask : public Runnable
 {
 public:
   U2FTask(const nsAString& aOrigin,
           const nsAString& aAppId);
 
   nsString mOrigin;
   nsString mAppId;
@@ -60,59 +62,59 @@ class U2FRegisterTask final : public nsN
                               public U2FTask
 {
 public:
   U2FRegisterTask(const nsAString& aOrigin,
                   const nsAString& aAppId,
                   const Sequence<RegisterRequest>& aRegisterRequests,
                   const Sequence<RegisteredKey>& aRegisteredKeys,
                   U2FRegisterCallback* aCallback,
-                  const nsCOMPtr<nsINSSU2FToken>& aNSSToken);
+                  const Sequence<Authenticator>& aAuthenticators);
 
   // No NSS resources to release.
   virtual
   void virtualDestroyNSSReference() override {};
 
   void ReturnError(ErrorCode code) override;
 
   NS_DECL_NSIRUNNABLE
 private:
   ~U2FRegisterTask();
 
   Sequence<RegisterRequest> mRegisterRequests;
   Sequence<RegisteredKey> mRegisteredKeys;
   RefPtr<U2FRegisterCallback> mCallback;
-  nsCOMPtr<nsINSSU2FToken> mNSSToken;
+  Sequence<Authenticator> mAuthenticators;
 };
 
 class U2FSignTask final : public nsNSSShutDownObject,
                           public U2FTask
 {
 public:
   U2FSignTask(const nsAString& aOrigin,
               const nsAString& aAppId,
               const nsAString& aChallenge,
               const Sequence<RegisteredKey>& aRegisteredKeys,
               U2FSignCallback* aCallback,
-              const nsCOMPtr<nsINSSU2FToken>& aNSSToken);
+              const Sequence<Authenticator>& aAuthenticators);
 
   // No NSS resources to release.
   virtual
   void virtualDestroyNSSReference() override {};
 
   void ReturnError(ErrorCode code) override;
 
   NS_DECL_NSIRUNNABLE
 private:
   ~U2FSignTask();
 
   nsString mChallenge;
   Sequence<RegisteredKey> mRegisteredKeys;
   RefPtr<U2FSignCallback> mCallback;
-  nsCOMPtr<nsINSSU2FToken> mNSSToken;
+  Sequence<Authenticator> mAuthenticators;
 };
 
 class U2F final : public nsISupports,
                   public nsWrapperCache,
                   public nsNSSShutDownObject
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -150,18 +152,17 @@ public:
 
   // No NSS resources to release.
   virtual
   void virtualDestroyNSSReference() override {};
 
 private:
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   nsString mOrigin;
-  USBToken mUSBToken;
-  nsCOMPtr<nsINSSU2FToken> mNSSToken;
+  Sequence<Authenticator> mAuthenticators;
 
   ~U2F();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_U2F_h
--- a/dom/u2f/moz.build
+++ b/dom/u2f/moz.build
@@ -1,20 +1,22 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla.dom += [
+    'NSSU2FTokenRemote.h',
     'U2F.h',
     'USBToken.h',
 ]
 
 UNIFIED_SOURCES += [
+    'NSSU2FTokenRemote.cpp',
     'U2F.cpp',
     'USBToken.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'