Bug 1275479 - Refactor U2F Token Interface (Part 1) r=keeler draft
authorJ.C. Jones <jjones@mozilla.com>
Tue, 31 May 2016 20:51:24 -0700
changeset 373727 e17b15ae708c1a3ce59f13d1b31dbbc288070a58
parent 373501 25321494921c824703a605127fb1f99b1faf5910
child 373728 3f698ec46ebfc1dbdb03486a47b59c749e4d5748
push id19822
push userjjones@mozilla.com
push dateWed, 01 Jun 2016 03:52:23 +0000
reviewerskeeler
bugs1275479
milestone49.0a1
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'