Bug 1330138 - Divide U2F and WebAuthn into separate directories; r=jcj draft
authorKyle Machulis <kyle@nonpolynomial.com>
Wed, 11 Jan 2017 12:38:08 -0800
changeset 459425 764af907f1ac68bf53276e3c78e853becc9ec88d
parent 459412 e9097e7043290682860539e9c7c38a5a089a7eaf
child 459426 490f48df5cf53c9aefdc9c8a21d094cb4f73484f
push id41224
push userbmo:kyle@nonpolynomial.com
push dateThu, 12 Jan 2017 00:30:40 +0000
reviewersjcj
bugs1330138
milestone53.0a1
Bug 1330138 - Divide U2F and WebAuthn into separate directories; r=jcj MozReview-Commit-ID: FCCSL6XWhTf
dom/moz.build
dom/u2f/NSSU2FTokenRemote.cpp
dom/u2f/NSSU2FTokenRemote.h
dom/u2f/ScopedCredential.cpp
dom/u2f/ScopedCredential.h
dom/u2f/ScopedCredentialInfo.cpp
dom/u2f/ScopedCredentialInfo.h
dom/u2f/U2F.cpp
dom/u2f/WebAuthentication.cpp
dom/u2f/WebAuthentication.h
dom/u2f/WebAuthnAssertion.cpp
dom/u2f/WebAuthnAssertion.h
dom/u2f/WebAuthnAttestation.cpp
dom/u2f/WebAuthnAttestation.h
dom/u2f/WebAuthnRequest.h
dom/u2f/moz.build
dom/u2f/tests/mochitest.ini
dom/u2f/tests/test_webauthn_get_assertion.html
dom/u2f/tests/test_webauthn_loopback.html
dom/u2f/tests/test_webauthn_make_credential.html
dom/u2f/tests/test_webauthn_no_token.html
dom/u2f/tests/test_webauthn_sameorigin.html
dom/webauthn/NSSU2FTokenRemote.cpp
dom/webauthn/NSSU2FTokenRemote.h
dom/webauthn/ScopedCredential.cpp
dom/webauthn/ScopedCredential.h
dom/webauthn/ScopedCredentialInfo.cpp
dom/webauthn/ScopedCredentialInfo.h
dom/webauthn/WebAuthentication.cpp
dom/webauthn/WebAuthentication.h
dom/webauthn/WebAuthnAssertion.cpp
dom/webauthn/WebAuthnAssertion.h
dom/webauthn/WebAuthnAttestation.cpp
dom/webauthn/WebAuthnAttestation.h
dom/webauthn/WebAuthnRequest.h
dom/webauthn/moz.build
dom/webauthn/tests/mochitest.ini
dom/webauthn/tests/test_webauthn_get_assertion.html
dom/webauthn/tests/test_webauthn_loopback.html
dom/webauthn/tests/test_webauthn_make_credential.html
dom/webauthn/tests/test_webauthn_no_token.html
dom/webauthn/tests/test_webauthn_sameorigin.html
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -81,16 +81,17 @@ DIRS += [
     'ipc',
     'workers',
     'audiochannel',
     'broadcastchannel',
     'messagechannel',
     'promise',
     'smil',
     'url',
+    'webauthn',
     'webidl',
     'xbl',
     'xml',
     'xslt',
     'xul',
     'manifest',
     'vr',
     'u2f',
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -37,17 +37,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 gWebauthLog("webauth_u2f");
+static mozilla::LazyLogModule gU2FLog("u2f");
 
 static nsresult
 AssembleClientData(const nsAString& aOrigin, const nsAString& aTyp,
                    const nsAString& aChallenge, CryptoBuffer& aClientData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   U2FClientData clientDataObject;
   clientDataObject.mTyp.Construct(aTyp); // "Typ" from the U2F specification
@@ -76,47 +76,47 @@ U2FStatus::~U2FStatus()
 {}
 
 void
 U2FStatus::WaitGroupAdd()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   mCount += 1;
-  MOZ_LOG(gWebauthLog, LogLevel::Debug,
+  MOZ_LOG(gU2FLog, LogLevel::Debug,
           ("U2FStatus::WaitGroupAdd, now %d", mCount));
 }
 
 void
 U2FStatus::WaitGroupDone()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   MOZ_ASSERT(mCount > 0);
   mCount -= 1;
-  MOZ_LOG(gWebauthLog, LogLevel::Debug,
+  MOZ_LOG(gU2FLog, LogLevel::Debug,
           ("U2FStatus::WaitGroupDone, now %d", mCount));
   if (mCount == 0) {
     mReentrantMonitor.NotifyAll();
   }
 }
 
 void
 U2FStatus::WaitGroupWait()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  MOZ_LOG(gWebauthLog, LogLevel::Debug,
+  MOZ_LOG(gU2FLog, LogLevel::Debug,
           ("U2FStatus::WaitGroupWait, now %d", mCount));
 
   while (mCount > 0) {
     mReentrantMonitor.Wait();
   }
 
   MOZ_ASSERT(mCount == 0);
-  MOZ_LOG(gWebauthLog, LogLevel::Debug,
+  MOZ_LOG(gU2FLog, LogLevel::Debug,
           ("U2FStatus::Wait completed, now count=%d stopped=%d", mCount,
            mIsStopped));
 }
 
 void
 U2FStatus::Stop(const ErrorCode aErrorCode)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
@@ -622,17 +622,17 @@ U2FRegisterRunnable::Run()
     }
 
     // Treat each call to Promise::All as a work unit, as it completes together
     status->WaitGroupAdd();
 
     U2FPrepPromise::All(AbstractThread::MainThread(), prepPromiseList)
     ->Then(AbstractThread::MainThread(), __func__,
       [status] (const nsTArray<Authenticator>& aTokens) {
-        MOZ_LOG(gWebauthLog, LogLevel::Debug,
+        MOZ_LOG(gU2FLog, LogLevel::Debug,
                 ("ALL: None of the RegisteredKeys were recognized. n=%d",
                  aTokens.Length()));
 
         status->WaitGroupDone();
       },
       [status] (ErrorCode aErrorCode) {
         status->Stop(aErrorCode);
         status->WaitGroupDone();
@@ -779,17 +779,17 @@ U2FSignRunnable::U2FSignRunnable(const n
 
     mRegisteredKeys.AppendElement(localKey);
   }
 
   // Assemble a clientData object
   nsresult rv = AssembleClientData(aOrigin, kGetAssertion, aChallenge,
                                    mClientData);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    MOZ_LOG(gWebauthLog, LogLevel::Warning,
+    MOZ_LOG(gU2FLog, LogLevel::Warning,
             ("Failed to AssembleClientData for the U2FSignRunnable."));
     return;
   }
 }
 
 U2FSignRunnable::~U2FSignRunnable()
 {
   nsNSSShutDownPreventionLock locker;
@@ -971,25 +971,25 @@ U2F::Init(nsPIDOMWindowInner* aParent, E
   }
 
   if (NS_WARN_IF(mOrigin.IsEmpty())) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (!EnsureNSSInitializedChromeOrContent()) {
-    MOZ_LOG(gWebauthLog, LogLevel::Debug,
+    MOZ_LOG(gU2FLog, LogLevel::Debug,
             ("Failed to get NSS context for U2F"));
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   // This only functions in e10s mode
   if (XRE_IsParentProcess()) {
-    MOZ_LOG(gWebauthLog, LogLevel::Debug,
+    MOZ_LOG(gU2FLog, LogLevel::Debug,
             ("Is non-e10s Process, U2F not available"));
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   // Monolithically insert compatible nsIU2FToken objects into mAuthenticators.
   // In future functionality expansions, this is where we could add a dynamic
   // add/remove interface.
deleted file mode 100644
--- a/dom/u2f/WebAuthentication.cpp
+++ /dev/null
@@ -1,1054 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "mozilla/dom/WebAuthentication.h"
-#include "mozilla/dom/WebAuthnAssertion.h"
-#include "mozilla/dom/WebAuthnAttestation.h"
-
-#include "mozilla/dom/Promise.h"
-#include "nsICryptoHash.h"
-#include "pkix/Input.h"
-#include "pkixutil.h"
-
-namespace mozilla {
-namespace dom {
-
-extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
-
-// Only needed for refcounted objects.
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthentication, mParent)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthentication)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthentication)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthentication)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-template<class OOS>
-static nsresult
-GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
-                 /* out */ nsString& aName)
-{
-  MOZ_ASSERT(aAlgorithm.IsString()); // TODO: remove assertion when we coerce.
-
-  if (aAlgorithm.IsString()) {
-    // If string, then treat as algorithm name
-    aName.Assign(aAlgorithm.GetAsString());
-  } else {
-    // TODO: Coerce to string and extract name. See WebCryptoTask.cpp
-  }
-
-  if (!NormalizeToken(aName, aName)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-HashCString(nsICryptoHash* aHashService, const nsACString& aIn,
-            /* out */ CryptoBuffer& aOut)
-{
-  MOZ_ASSERT(aHashService);
-
-  nsresult rv = aHashService->Init(nsICryptoHash::SHA256);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aHashService->Update(
-         reinterpret_cast<const uint8_t*>(aIn.BeginReading()),aIn.Length());
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsAutoCString fullHash;
-  // Passing false below means we will get a binary result rather than a
-  // base64-encoded string.
-  rv = aHashService->Finish(false, fullHash);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  aOut.Assign(fullHash);
-  return rv;
-}
-
-static nsresult
-AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
-                   /* out */ nsACString& aJsonOut)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsString challengeBase64;
-  nsresult rv = aChallenge.ToJwkBase64(challengeBase64);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  WebAuthnClientData clientDataObject;
-  clientDataObject.mOrigin.Assign(aOrigin);
-  clientDataObject.mHashAlg.SetAsString().Assign(NS_LITERAL_STRING("S256"));
-  clientDataObject.mChallenge.Assign(challengeBase64);
-
-  nsAutoString temp;
-  if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aJsonOut.Assign(NS_ConvertUTF16toUTF8(temp));
-  return NS_OK;
-}
-
-static nsresult
-ScopedCredentialGetData(const ScopedCredentialDescriptor& aSCD,
-                        /* out */ uint8_t** aBuf, /* out */ uint32_t* aBufLen)
-{
-  MOZ_ASSERT(aBuf);
-  MOZ_ASSERT(aBufLen);
-
-  if (aSCD.mId.IsArrayBufferView()) {
-    const ArrayBufferView& view = aSCD.mId.GetAsArrayBufferView();
-    view.ComputeLengthAndData();
-    *aBuf = view.Data();
-    *aBufLen = view.Length();
-  } else if (aSCD.mId.IsArrayBuffer()) {
-    const ArrayBuffer& buffer = aSCD.mId.GetAsArrayBuffer();
-    buffer.ComputeLengthAndData();
-    *aBuf = buffer.Data();
-    *aBufLen = buffer.Length();
-  } else {
-    MOZ_ASSERT(false);
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
-                   uint32_t aLen)
-{
-  if (aSrc.EnsureLength(aLen) != pkix::Success) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-
-  aDest.ClearAndRetainStorage();
-
-  for (uint32_t offset = 0; offset < aLen; ++offset) {
-    uint8_t b;
-    if (aSrc.Read(b) != pkix::Success) {
-      return NS_ERROR_DOM_UNKNOWN_ERR;
-    }
-    if (!aDest.AppendElement(b, mozilla::fallible)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-U2FAssembleAuthenticatorData(/* out */ CryptoBuffer& aAuthenticatorData,
-                             const CryptoBuffer& aRpIdHash,
-                             const CryptoBuffer& aSignatureData)
-{
-  // The AuthenticatorData for U2F devices is the concatenation of the
-  // RP ID with the output of the U2F Sign operation.
-  if (aRpIdHash.Length() != 32) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  if (!aAuthenticatorData.AppendElements(aRpIdHash, mozilla::fallible)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  if (!aAuthenticatorData.AppendElements(aSignatureData, mozilla::fallible)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
-                                 /* out */ CryptoBuffer& aPubKeyBuf,
-                                 /* out */ CryptoBuffer& aKeyHandleBuf,
-                                 /* out */ CryptoBuffer& aAttestationCertBuf,
-                                 /* out */ CryptoBuffer& aSignatureBuf)
-{
-  // U2F v1.1 Format via
-  // http://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html
-  //
-  // Bytes  Value
-  // 1      0x05
-  // 65     public key
-  // 1      key handle length
-  // *      key handle
-  // ASN.1  attestation certificate
-  // *      attestation signature
-
-  pkix::Input u2fResponse;
-  u2fResponse.Init(aResponse.Elements(), aResponse.Length());
-
-  pkix::Reader input(u2fResponse);
-
-  uint8_t b;
-  if (input.Read(b) != pkix::Success) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-  if (b != 0x05) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-
-  nsresult rv = ReadToCryptoBuffer(input, aPubKeyBuf, 65);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  uint8_t handleLen;
-  if (input.Read(handleLen) != pkix::Success) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-
-  rv = ReadToCryptoBuffer(input, aKeyHandleBuf, handleLen);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // We have to parse the ASN.1 SEQUENCE on the outside to determine the cert's
-  // length.
-  pkix::Input cert;
-  if (pkix::der::ExpectTagAndGetValue(input, pkix::der::SEQUENCE, cert)
-        != pkix::Success) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-
-  pkix::Reader certInput(cert);
-  rv = ReadToCryptoBuffer(certInput, aAttestationCertBuf, cert.GetLength());
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // The remainder of u2fResponse is the signature
-  pkix::Input u2fSig;
-  input.SkipToEnd(u2fSig);
-  pkix::Reader sigInput(u2fSig);
-  rv = ReadToCryptoBuffer(sigInput, aSignatureBuf, u2fSig.GetLength());
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-WebAuthentication::WebAuthentication(nsPIDOMWindowInner* aParent)
-  : mInitialized(false)
-{
-  mParent = do_QueryInterface(aParent);
-  MOZ_ASSERT(mParent);
-}
-
-WebAuthentication::~WebAuthentication()
-{}
-
-nsresult
-WebAuthentication::InitLazily()
-{
-  if (mInitialized) {
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(mParent);
-  if (!mParent) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
-  MOZ_ASSERT(doc);
-
-  nsIPrincipal* principal = doc->NodePrincipal();
-  nsresult rv = nsContentUtils::GetUTFOrigin(principal, mOrigin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (NS_WARN_IF(mOrigin.IsEmpty())) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // This only functions in e10s mode
-  // TODO: Remove in Bug 1323339
-  if (XRE_IsParentProcess()) {
-    MOZ_LOG(gWebauthLog, LogLevel::Debug,
-            ("Is non-e10s Process, WebAuthn not available"));
-    return NS_ERROR_FAILURE;
-  }
-
-  if (Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED)) {
-    if (!mAuthenticators.AppendElement(new NSSU2FTokenRemote(),
-                                       mozilla::fallible)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  mInitialized = true;
-  return NS_OK;
-}
-
-JSObject*
-WebAuthentication::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return WebAuthenticationBinding::Wrap(aCx, this, aGivenProto);
-}
-
-// NOTE: This method represents a theoretical way to use a U2F-compliant token
-// to produce the result of the WebAuthn MakeCredential method. The exact
-// mapping of U2F data fields to WebAuthn data fields is still a matter of
-// ongoing discussion, and this should not be taken as anything but a point-in-
-// time possibility.
-void
-WebAuthentication::U2FAuthMakeCredential(
-             const RefPtr<CredentialRequest>& aRequest,
-             const Authenticator& aToken, CryptoBuffer& aRpIdHash,
-             const nsACString& aClientData, CryptoBuffer& aClientDataHash,
-             const Account& aAccount,
-             const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
-             const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
-             const WebAuthnExtensions& aExtensions)
-{
-  MOZ_LOG(gWebauthLog, LogLevel::Debug, ("U2FAuthMakeCredential"));
-  aRequest->AddActiveToken(__func__);
-
-  // 5.1.1 When this operation is invoked, the authenticator must perform the
-  // following procedure:
-
-  // 5.1.1.a Check if all the supplied parameters are syntactically well-
-  // formed and of the correct length. If not, return an error code equivalent
-  // to UnknownError and terminate the operation.
-
-  if ((aRpIdHash.Length() != SHA256_LENGTH) ||
-      (aClientDataHash.Length() != SHA256_LENGTH)) {
-    aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
-    return;
-  }
-
-  // 5.1.1.b Check if at least one of the specified combinations of
-  // ScopedCredentialType and cryptographic parameters is supported. If not,
-  // return an error code equivalent to NotSupportedError and terminate the
-  // operation.
-
-  bool isValidCombination = false;
-
-  for (size_t a = 0; a < aNormalizedParams.Length(); ++a) {
-    if (aNormalizedParams[a].mType == ScopedCredentialType::ScopedCred &&
-        aNormalizedParams[a].mAlgorithm.IsString() &&
-        aNormalizedParams[a].mAlgorithm.GetAsString().EqualsLiteral(
-          WEBCRYPTO_NAMED_CURVE_P256)) {
-      isValidCombination = true;
-      break;
-    }
-  }
-  if (!isValidCombination) {
-    aRequest->SetFailure(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return;
-  }
-
-  // 5.1.1.c Check if a credential matching any of the supplied
-  // ScopedCredential identifiers is present on this authenticator. If so,
-  // return an error code equivalent to NotAllowedError and terminate the
-  // operation.
-
-  if (aExcludeList.WasPassed()) {
-    const Sequence<ScopedCredentialDescriptor>& list = aExcludeList.Value();
-
-    for (const ScopedCredentialDescriptor& scd : list) {
-      bool isRegistered = false;
-
-      uint8_t *data;
-      uint32_t len;
-
-      // data is owned by the Descriptor, do don't free it here.
-      if (NS_FAILED(ScopedCredentialGetData(scd, &data, &len))) {
-        aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
-        return;
-      }
-
-      nsresult rv = aToken->IsRegistered(data, len, &isRegistered);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        aRequest->SetFailure(rv);
-        return;
-      }
-
-      if (isRegistered) {
-        aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-        return;
-      }
-    }
-  }
-
-  // 5.1.1.d Prompt the user for consent to create a new credential. The
-  // prompt for obtaining this consent is shown by the authenticator if it has
-  // its own output capability, or by the user agent otherwise. If the user
-  // denies consent, return an error code equivalent to NotAllowedError and
-  // terminate the operation.
-
-  // 5.1.1.d Once user consent has been obtained, generate a new credential
-  // object
-
-  // 5.1.1.e If any error occurred while creating the new credential object,
-  // return an error code equivalent to UnknownError and terminate the
-  // operation.
-
-  // 5.1.1.f Process all the supported extensions requested by the client, and
-  // generate an attestation statement. If no authority key is available to
-  // sign such an attestation statement, then the authenticator performs self
-  // attestation of the credential with its own private key. For more details
-  // on attestation, see §5.3 Credential Attestation Statements.
-
-  // No extensions are supported
-
-  // 4.1.1.11 While issuedRequests is not empty, perform the following actions
-  // depending upon the adjustedTimeout timer and responses from the
-  // authenticators:
-
-  // 4.1.1.11.a If the adjustedTimeout timer expires, then for each entry in
-  // issuedRequests invoke the authenticatorCancel operation on that
-  // authenticator and remove its entry from the list.
-
-  uint8_t* buffer;
-  uint32_t bufferlen;
-
-  nsresult rv = aToken->Register(aRpIdHash.Elements(), aRpIdHash.Length(),
-                                 aClientDataHash.Elements(),
-                                 aClientDataHash.Length(), &buffer, &bufferlen);
-
-  // 4.1.1.11.b If any authenticator returns a status indicating that the user
-  // cancelled the operation, delete that authenticator’s entry from
-  // issuedRequests. For each remaining entry in issuedRequests invoke the
-  // authenticatorCancel operation on that authenticator and remove its entry
-  // from the list.
-
-  // 4.1.1.11.c If any authenticator returns an error status, delete the
-  // corresponding entry from issuedRequests.
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
-    return;
-  }
-
-  MOZ_ASSERT(buffer);
-  CryptoBuffer regData;
-  if (NS_WARN_IF(!regData.Assign(buffer, bufferlen))) {
-    free(buffer);
-    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-  free(buffer);
-
-  // Decompose the U2F registration packet
-  CryptoBuffer pubKeyBuf;
-  CryptoBuffer keyHandleBuf;
-  CryptoBuffer attestationCertBuf;
-  CryptoBuffer signatureBuf;
-
-  rv = U2FDecomposeRegistrationResponse(regData, pubKeyBuf, keyHandleBuf,
-                                        attestationCertBuf, signatureBuf);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRequest->SetFailure(rv);
-    return;
-  }
-
-  // Sign the aClientDataHash explicitly to get the format needed for
-  // the AuthenticatorData parameter of WebAuthnAttestation. This might
-  // be temporary while the spec settles down how to incorporate U2F.
-  rv = aToken->Sign(aRpIdHash.Elements(), aRpIdHash.Length(),
-                    aClientDataHash.Elements(), aClientDataHash.Length(),
-                    keyHandleBuf.Elements(), keyHandleBuf.Length(), &buffer,
-                    &bufferlen);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRequest->SetFailure(rv);
-    return;
-  }
-
-  MOZ_ASSERT(buffer);
-  CryptoBuffer signatureData;
-  if (NS_WARN_IF(!signatureData.Assign(buffer, bufferlen))) {
-    free(buffer);
-    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-  free(buffer);
-
-  CryptoBuffer clientDataBuf;
-  if (!clientDataBuf.Assign(aClientData)) {
-    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-
-  CryptoBuffer authenticatorDataBuf;
-  rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, aRpIdHash,
-                                    signatureData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRequest->SetFailure(rv);
-    return;
-  }
-
-  // 4.1.1.11.d If any authenticator indicates success:
-
-  // 4.1.1.11.d.1 Remove this authenticator’s entry from issuedRequests.
-
-  // 4.1.1.11.d.2 Create a new ScopedCredentialInfo object named value and
-  // populate its fields with the values returned from the authenticator as well
-  // as the clientDataJSON computed earlier.
-
-  RefPtr<ScopedCredential> credential = new ScopedCredential(this);
-  credential->SetType(ScopedCredentialType::ScopedCred);
-  credential->SetId(keyHandleBuf);
-
-  RefPtr<WebAuthnAttestation> attestation = new WebAuthnAttestation(this);
-  attestation->SetFormat(NS_LITERAL_STRING("u2f"));
-  attestation->SetClientData(clientDataBuf);
-  attestation->SetAuthenticatorData(authenticatorDataBuf);
-  attestation->SetAttestation(regData);
-
-  CredentialPtr info = new ScopedCredentialInfo(this);
-  info->SetCredential(credential);
-  info->SetAttestation(attestation);
-
-  // 4.1.1.11.d.3 For each remaining entry in issuedRequests invoke the
-  // authenticatorCancel operation on that authenticator and remove its entry
-  // from the list.
-
-  // 4.1.1.11.d.4 Resolve promise with value and terminate this algorithm.
-  aRequest->SetSuccess(info);
-}
-
-// NOTE: This method represents a theoretical way to use a U2F-compliant token
-// to produce the result of the WebAuthn GetAssertion method. The exact mapping
-// of U2F data fields to WebAuthn data fields is still a matter of ongoing
-// discussion, and this should not be taken as anything but a point-in- time
-// possibility.
-void
-WebAuthentication::U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
-                    const Authenticator& aToken, CryptoBuffer& aRpIdHash,
-                    const nsACString& aClientData, CryptoBuffer& aClientDataHash,
-                    nsTArray<CryptoBuffer>& aAllowList,
-                    const WebAuthnExtensions& aExtensions)
-{
-  MOZ_LOG(gWebauthLog, LogLevel::Debug, ("U2FAuthGetAssertion"));
-
-  // 4.1.2.7.e Add an entry to issuedRequests, corresponding to this request.
-  aRequest->AddActiveToken(__func__);
-
-  // 4.1.2.8 While issuedRequests is not empty, perform the following actions
-  // depending upon the adjustedTimeout timer and responses from the
-  // authenticators:
-
-  // 4.1.2.8.a If the timer for adjustedTimeout expires, then for each entry
-  // in issuedRequests invoke the authenticatorCancel operation on that
-  // authenticator and remove its entry from the list.
-
-  for (CryptoBuffer& allowedCredential : aAllowList) {
-    bool isRegistered = false;
-    nsresult rv = aToken->IsRegistered(allowedCredential.Elements(),
-                                       allowedCredential.Length(),
-                                       &isRegistered);
-
-    // 4.1.2.8.b If any authenticator returns a status indicating that the user
-    // cancelled the operation, delete that authenticator’s entry from
-    // issuedRequests. For each remaining entry in issuedRequests invoke the
-    // authenticatorCancel operation on that authenticator, and remove its entry
-    // from the list.
-
-    // 4.1.2.8.c If any authenticator returns an error status, delete the
-    // corresponding entry from issuedRequests.
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRequest->SetFailure(rv);
-      return;
-    }
-
-    if (!isRegistered) {
-      continue;
-    }
-
-    // Sign
-    uint8_t* buffer;
-    uint32_t bufferlen;
-    rv = aToken->Sign(aRpIdHash.Elements(), aRpIdHash.Length(),
-                      aClientDataHash.Elements(), aClientDataHash.Length(),
-                      allowedCredential.Elements(), allowedCredential.Length(),
-                      &buffer, &bufferlen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRequest->SetFailure(rv);
-      return;
-    }
-
-    MOZ_ASSERT(buffer);
-    CryptoBuffer signatureData;
-    if (NS_WARN_IF(!signatureData.Assign(buffer, bufferlen))) {
-      free(buffer);
-      aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-    free(buffer);
-
-    // 4.1.2.8.d If any authenticator returns success:
-
-    // 4.1.2.8.d.1 Remove this authenticator’s entry from issuedRequests.
-
-    // 4.1.2.8.d.2 Create a new WebAuthnAssertion object named value and
-    // populate its fields with the values returned from the authenticator as
-    // well as the clientDataJSON computed earlier.
-
-    CryptoBuffer clientDataBuf;
-    if (!clientDataBuf.Assign(aClientData)) {
-      aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-
-    CryptoBuffer authenticatorDataBuf;
-    rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, aRpIdHash,
-                                      signatureData);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRequest->SetFailure(rv);
-      return;
-    }
-
-    RefPtr<ScopedCredential> credential = new ScopedCredential(this);
-    credential->SetType(ScopedCredentialType::ScopedCred);
-    credential->SetId(allowedCredential);
-
-    AssertionPtr assertion = new WebAuthnAssertion(this);
-    assertion->SetCredential(credential);
-    assertion->SetClientData(clientDataBuf);
-    assertion->SetAuthenticatorData(authenticatorDataBuf);
-    assertion->SetSignature(signatureData);
-
-    // 4.1.2.8.d.3 For each remaining entry in issuedRequests invoke the
-    // authenticatorCancel operation on that authenticator and remove its entry
-    // from the list.
-
-    // 4.1.2.8.d.4 Resolve promise with value and terminate this algorithm.
-    aRequest->SetSuccess(assertion);
-    return;
-  }
-
-  // 4.1.2.9 Reject promise with a DOMException whose name is "NotAllowedError",
-  // and terminate this algorithm.
-  aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-}
-
-nsresult
-WebAuthentication::RelaxSameOrigin(const nsAString& aInputRpId,
-                                   /* out */ nsACString& aRelaxedRpId)
-{
-  MOZ_ASSERT(mParent);
-  nsCOMPtr<nsIDocument> document = mParent->GetDoc();
-  if (!document || !document->IsHTMLDocument()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // TODO: Bug 1329764: Invoke the Relax Algorithm, once properly defined
-  aRelaxedRpId.Assign(NS_ConvertUTF16toUTF8(aInputRpId));
-  return NS_OK;
-}
-
-already_AddRefed<Promise>
-WebAuthentication::MakeCredential(JSContext* aCx, const Account& aAccount,
-                  const Sequence<ScopedCredentialParameters>& aCryptoParameters,
-                  const ArrayBufferViewOrArrayBuffer& aChallenge,
-                  const ScopedCredentialOptions& aOptions)
-{
-  MOZ_ASSERT(mParent);
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  if (!global) {
-    return nullptr;
-  }
-
-  ErrorResult rv;
-  RefPtr<Promise> promise = Promise::Create(global, rv);
-
-  nsresult initRv = InitLazily();
-  if (NS_FAILED(initRv)) {
-    promise->MaybeReject(initRv);
-    return promise.forget();
-  }
-
-  // 4.1.1.1 If timeoutSeconds was specified, check if its value lies within a
-  // reasonable range as defined by the platform and if not, correct it to the
-  // closest value lying within that range.
-
-  double adjustedTimeout = 30.0;
-  if (aOptions.mTimeoutSeconds.WasPassed()) {
-    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
-    adjustedTimeout = std::max(15.0, adjustedTimeout);
-    adjustedTimeout = std::min(120.0, adjustedTimeout);
-  }
-
-  // 4.1.1.2 Let promise be a new Promise. Return promise and start a timer for
-  // adjustedTimeout seconds.
-
-  RefPtr<CredentialRequest> requestMonitor = new CredentialRequest();
-  requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout));
-
-  if (mOrigin.EqualsLiteral("null")) {
-    // 4.1.1.3 If callerOrigin is an opaque origin, reject promise with a
-    // DOMException whose name is "NotAllowedError", and terminate this
-    // algorithm
-    MOZ_LOG(gWebauthLog, LogLevel::Debug, ("Rejecting due to opaque origin"));
-    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-    return promise.forget();
-  }
-
-  nsCString rpId;
-  if (!aOptions.mRpId.WasPassed()) {
-    // 4.1.1.3.a If rpId is not specified, then set rpId to callerOrigin, and
-    // rpIdHash to the SHA-256 hash of rpId.
-    rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin));
-  } else {
-    // 4.1.1.3.b If rpId is specified, then invoke the procedure used for
-    // relaxing the same-origin restriction by setting the document.domain
-    // attribute, using rpId as the given value but without changing the current
-    // document’s domain. If no errors are thrown, set rpId to the value of host
-    // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
-    // Otherwise, reject promise with a DOMException whose name is
-    // "SecurityError", and terminate this algorithm.
-
-    if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) {
-      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-      return promise.forget();
-    }
-  }
-
-  CryptoBuffer rpIdHash;
-  if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
-    promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
-    return promise.forget();
-  }
-
-  nsresult srv;
-  nsCOMPtr<nsICryptoHash> hashService =
-    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  srv = HashCString(hashService, rpId, rpIdHash);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  // 4.1.1.4 Process each element of cryptoParameters using the following steps,
-  // to produce a new sequence normalizedParameters.
-  nsTArray<ScopedCredentialParameters> normalizedParams;
-  for (size_t a = 0; a < aCryptoParameters.Length(); ++a) {
-    // 4.1.1.4.a Let current be the currently selected element of
-    // cryptoParameters.
-
-    // 4.1.1.4.b If current.type does not contain a ScopedCredentialType
-    // supported by this implementation, then stop processing current and move
-    // on to the next element in cryptoParameters.
-    if (aCryptoParameters[a].mType != ScopedCredentialType::ScopedCred) {
-      continue;
-    }
-
-    // 4.1.1.4.c Let normalizedAlgorithm be the result of normalizing an
-    // algorithm using the procedure defined in [WebCryptoAPI], with alg set to
-    // current.algorithm and op set to 'generateKey'. If an error occurs during
-    // this procedure, then stop processing current and move on to the next
-    // element in cryptoParameters.
-
-    nsString algName;
-    if (NS_FAILED(GetAlgorithmName(aCx, aCryptoParameters[a].mAlgorithm,
-                                   algName))) {
-      continue;
-    }
-
-    // 4.1.1.4.d Add a new object of type ScopedCredentialParameters to
-    // normalizedParameters, with type set to current.type and algorithm set to
-    // normalizedAlgorithm.
-    ScopedCredentialParameters normalizedObj;
-    normalizedObj.mType = aCryptoParameters[a].mType;
-    normalizedObj.mAlgorithm.SetAsString().Assign(algName);
-
-    if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){
-      promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
-      return promise.forget();
-    }
-  }
-
-  // 4.1.1.5 If normalizedAlgorithm is empty and cryptoParameters was not empty,
-  // cancel the timer started in step 2, reject promise with a DOMException
-  // whose name is "NotSupportedError", and terminate this algorithm.
-  if (normalizedParams.IsEmpty() && !aCryptoParameters.IsEmpty()) {
-    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return promise.forget();
-  }
-
-  // 4.1.1.6 If excludeList is undefined, set it to the empty list.
-
-  // 4.1.1.7 If extensions was specified, process any extensions supported by
-  // this client platform, to produce the extension data that needs to be sent
-  // to the authenticator. If an error is encountered while processing an
-  // extension, skip that extension and do not produce any extension data for
-  // it. Call the result of this processing clientExtensions.
-
-  // Currently no extensions are supported
-
-  // 4.1.1.8 Use attestationChallenge, callerOrigin and rpId, along with the
-  // token binding key associated with callerOrigin (if any), to create a
-  // ClientData structure representing this request. Choose a hash algorithm for
-  // hashAlg and compute the clientDataJSON and clientDataHash.
-
-  CryptoBuffer challenge;
-  if (!challenge.Assign(aChallenge)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  nsAutoCString clientDataJSON;
-  srv = AssembleClientData(mOrigin, challenge, clientDataJSON);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  CryptoBuffer clientDataHash;
-  if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  srv = HashCString(hashService, clientDataJSON, clientDataHash);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  // 4.1.1.9 Initialize issuedRequests to an empty list.
-  RefPtr<CredentialPromise> monitorPromise = requestMonitor->Ensure();
-
-  // 4.1.1.10 For each authenticator currently available on this platform:
-  // asynchronously invoke the authenticatorMakeCredential operation on that
-  // authenticator with rpIdHash, clientDataHash, accountInformation,
-  // normalizedParameters, excludeList and clientExtensions as parameters. Add a
-  // corresponding entry to issuedRequests.
-  for (Authenticator u2ftoken : mAuthenticators) {
-    // 4.1.1.10.a For each credential C in excludeList that has a non-empty
-    // transports list, optionally use only the specified transports to test for
-    // the existence of C.
-    U2FAuthMakeCredential(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
-                          clientDataHash, aAccount, normalizedParams,
-                          aOptions.mExcludeList, aOptions.mExtensions);
-  }
-
-  requestMonitor->CompleteTask();
-
-  monitorPromise->Then(AbstractThread::MainThread(), __func__,
-    [promise] (CredentialPtr aInfo) {
-      promise->MaybeResolve(aInfo);
-    },
-    [promise] (nsresult aErrorCode) {
-      promise->MaybeReject(aErrorCode);
-  });
-
-  return promise.forget();
-}
-
-already_AddRefed<Promise>
-WebAuthentication::GetAssertion(const ArrayBufferViewOrArrayBuffer& aChallenge,
-                                const AssertionOptions& aOptions)
-{
-  MOZ_ASSERT(mParent);
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  if (!global) {
-    return nullptr;
-  }
-
-  // 4.1.2.1 If timeoutSeconds was specified, check if its value lies within a
-  // reasonable range as defined by the platform and if not, correct it to the
-  // closest value lying within that range.
-
-  double adjustedTimeout = 30.0;
-  if (aOptions.mTimeoutSeconds.WasPassed()) {
-    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
-    adjustedTimeout = std::max(15.0, adjustedTimeout);
-    adjustedTimeout = std::min(120.0, adjustedTimeout);
-  }
-
-  // 4.1.2.2 Let promise be a new Promise. Return promise and start a timer for
-  // adjustedTimeout seconds.
-
-  RefPtr<AssertionRequest> requestMonitor = new AssertionRequest();
-  requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout));
-
-  ErrorResult rv;
-  RefPtr<Promise> promise = Promise::Create(global, rv);
-
-  nsresult initRv = InitLazily();
-  if (NS_FAILED(initRv)) {
-    promise->MaybeReject(initRv);
-    return promise.forget();
-  }
-
-  if (mOrigin.EqualsLiteral("null")) {
-    // 4.1.2.3 If callerOrigin is an opaque origin, reject promise with a
-    // DOMException whose name is "NotAllowedError", and terminate this algorithm
-    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-    return promise.forget();
-  }
-
-  nsCString rpId;
-  if (!aOptions.mRpId.WasPassed()) {
-    // 4.1.2.3.a If rpId is not specified, then set rpId to callerOrigin, and
-    // rpIdHash to the SHA-256 hash of rpId.
-    rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin));
-  } else {
-    // 4.1.2.3.b If rpId is specified, then invoke the procedure used for
-    // relaxing the same-origin restriction by setting the document.domain
-    // attribute, using rpId as the given value but without changing the current
-    // document’s domain. If no errors are thrown, set rpId to the value of host
-    // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
-    // Otherwise, reject promise with a DOMException whose name is
-    // "SecurityError", and terminate this algorithm.
-
-    if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) {
-      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-      return promise.forget();
-    }
-  }
-
-  CryptoBuffer rpIdHash;
-  if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  nsresult srv;
-  nsCOMPtr<nsICryptoHash> hashService =
-    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  srv = HashCString(hashService, rpId, rpIdHash);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  // 4.1.2.4 If extensions was specified, process any extensions supported by
-  // this client platform, to produce the extension data that needs to be sent
-  // to the authenticator. If an error is encountered while processing an
-  // extension, skip that extension and do not produce any extension data for
-  // it. Call the result of this processing clientExtensions.
-
-  // TODO
-
-  // 4.1.2.5 Use assertionChallenge, callerOrigin and rpId, along with the token
-  // binding key associated with callerOrigin (if any), to create a ClientData
-  // structure representing this request. Choose a hash algorithm for hashAlg
-  // and compute the clientDataJSON and clientDataHash.
-  CryptoBuffer challenge;
-  if (!challenge.Assign(aChallenge)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  nsAutoCString clientDataJSON;
-  srv = AssembleClientData(mOrigin, challenge, clientDataJSON);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  CryptoBuffer clientDataHash;
-  if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  srv = HashCString(hashService, clientDataJSON, clientDataHash);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  // Note: we only support U2F-style authentication for now, so we effectively
-  // require an AllowList.
-  if (!aOptions.mAllowList.WasPassed()) {
-    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-    return promise.forget();
-  }
-
-  const Sequence<ScopedCredentialDescriptor>& allowList =
-    aOptions.mAllowList.Value();
-
-  // 4.1.2.6 Initialize issuedRequests to an empty list.
-  RefPtr<AssertionPromise> monitorPromise = requestMonitor->Ensure();
-
-  // 4.1.2.7 For each authenticator currently available on this platform,
-  // perform the following steps:
-  for(Authenticator u2ftoken : mAuthenticators) {
-    // 4.1.2.7.a If allowList is undefined or empty, let credentialList be an
-    // empty list. Otherwise, execute a platform-specific procedure to determine
-    // which, if any, credentials listed in allowList might be present on this
-    // authenticator, and set credentialList to this filtered list. If no such
-    // filtering is possible, set credentialList to an empty list.
-
-    nsTArray<CryptoBuffer> credentialList;
-
-    for (const ScopedCredentialDescriptor& scd : allowList) {
-      CryptoBuffer buf;
-      if (NS_WARN_IF(!buf.Assign(scd.mId))) {
-        continue;
-      }
-
-      // 4.1.2.7.b For each credential C within the credentialList that has a
-      // non- empty transports list, optionally use only the specified
-      // transports to get assertions using credential C.
-
-      // TODO: Filter using Transport
-      if (!credentialList.AppendElement(buf, mozilla::fallible)) {
-        requestMonitor->CancelNow();
-        promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
-        return promise.forget();
-      }
-    }
-
-    // 4.1.2.7.c If the above filtering process concludes that none of the
-    // credentials on allowList can possibly be on this authenticator, do not
-    // perform any of the following steps for this authenticator, and proceed to
-    // the next authenticator (if any).
-    if (credentialList.IsEmpty()) {
-      continue;
-    }
-
-    // 4.1.2.7.d Asynchronously invoke the authenticatorGetAssertion operation
-    // on this authenticator with rpIdHash, clientDataHash, credentialList, and
-    // clientExtensions as parameters.
-    U2FAuthGetAssertion(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
-                        clientDataHash, credentialList, aOptions.mExtensions);
-  }
-
-  requestMonitor->CompleteTask();
-
-  monitorPromise->Then(AbstractThread::MainThread(), __func__,
-    [promise] (AssertionPtr aAssertion) {
-      promise->MaybeResolve(aAssertion);
-    },
-    [promise] (nsresult aErrorCode) {
-      promise->MaybeReject(aErrorCode);
-  });
-
-  return promise.forget();
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/u2f/WebAuthentication.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef mozilla_dom_WebAuthentication_h
-#define mozilla_dom_WebAuthentication_h
-
-#include "js/TypeDecls.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/DOMException.h"
-#include "mozilla/dom/WebAuthenticationBinding.h"
-#include "mozilla/dom/WebCryptoCommon.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/MozPromise.h"
-#include "mozilla/ReentrantMonitor.h"
-#include "mozilla/SharedThreadPool.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsWrapperCache.h"
-
-#include "U2FAuthenticator.h"
-#include "WebAuthnRequest.h"
-
-namespace mozilla {
-namespace dom {
-
-struct Account;
-class ArrayBufferViewOrArrayBuffer;
-struct AssertionOptions;
-class OwningArrayBufferViewOrArrayBuffer;
-struct ScopedCredentialOptions;
-struct ScopedCredentialParameters;
-
-} // namespace dom
-} // namespace mozilla
-
-namespace mozilla {
-namespace dom {
-
-typedef RefPtr<ScopedCredentialInfo> CredentialPtr;
-typedef RefPtr<WebAuthnAssertion> AssertionPtr;
-typedef WebAuthnRequest<CredentialPtr> CredentialRequest;
-typedef WebAuthnRequest<AssertionPtr> AssertionRequest;
-typedef MozPromise<CredentialPtr, nsresult, false> CredentialPromise;
-typedef MozPromise<AssertionPtr, nsresult, false> AssertionPromise;
-
-class WebAuthentication final : public nsISupports
-                              , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthentication)
-
-public:
-  explicit WebAuthentication(nsPIDOMWindowInner* aParent);
-
-protected:
-  ~WebAuthentication();
-
-public:
-  nsPIDOMWindowInner*
-  GetParentObject() const
-  {
-    return mParent;
-  }
-
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  already_AddRefed<Promise>
-  MakeCredential(JSContext* aCx, const Account& accountInformation,
-                 const Sequence<ScopedCredentialParameters>& cryptoParameters,
-                 const ArrayBufferViewOrArrayBuffer& attestationChallenge,
-                 const ScopedCredentialOptions& options);
-
-  already_AddRefed<Promise>
-  GetAssertion(const ArrayBufferViewOrArrayBuffer& assertionChallenge,
-               const AssertionOptions& options);
-
-private:
-  nsresult
-  InitLazily();
-
-  void
-  U2FAuthMakeCredential(const RefPtr<CredentialRequest>& aRequest,
-             const Authenticator& aToken, CryptoBuffer& aRpIdHash,
-             const nsACString& aClientData, CryptoBuffer& aClientDataHash,
-             const Account& aAccount,
-             const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
-             const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
-             const WebAuthnExtensions& aExtensions);
-  void
-  U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
-                   const Authenticator& aToken, CryptoBuffer& aRpIdHash,
-                   const nsACString& aClientData, CryptoBuffer& aClientDataHash,
-                   nsTArray<CryptoBuffer>& aAllowList,
-                   const WebAuthnExtensions& aExtensions);
-
-  nsresult
-  RelaxSameOrigin(const nsAString& aInputRpId, nsACString& aRelaxedRpId);
-
-  nsCOMPtr<nsPIDOMWindowInner> mParent;
-  nsString mOrigin;
-  Sequence<Authenticator> mAuthenticators;
-  bool mInitialized;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_WebAuthentication_h
deleted file mode 100644
--- a/dom/u2f/WebAuthnRequest.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef mozilla_dom_WebAuthnAsync_h
-#define mozilla_dom_WebAuthnAsync_h
-
-#include "mozilla/MozPromise.h"
-#include "mozilla/ReentrantMonitor.h"
-#include "mozilla/SharedThreadPool.h"
-#include "mozilla/TimeStamp.h"
-
-namespace mozilla {
-namespace dom {
-
-extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
-
-// WebAuthnRequest tracks the completion of a single WebAuthn request that
-// may run on multiple kinds of authenticators, and be subject to a deadline.
-template<class Success>
-class WebAuthnRequest {
-public:
-  WebAuthnRequest()
-    : mCancelled(false)
-    , mSuccess(false)
-    , mCountTokens(0)
-    , mTokensFailed(0)
-    , mReentrantMonitor("WebAuthnRequest")
-  {}
-
-  void AddActiveToken(const char* aCallSite)
-  {
-    MOZ_LOG(gWebauthLog, LogLevel::Debug,
-           ("WebAuthnRequest is tracking a new token, called from [%s]",
-            aCallSite));
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    mCountTokens += 1;
-  }
-
-  bool IsComplete()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    return mCancelled || mSuccess ||
-      (mCountTokens > 0 && mTokensFailed == mCountTokens);
-  }
-
-  void CancelNow()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause CancelNow to get called after
-    // a success or a cancel. We only complete once.
-    if (IsComplete()) {
-      return;
-    }
-
-    mCancelled = true;
-    mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
-  }
-
-  void SetFailure(nsresult aError)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause SetFailure to get called after
-    // a success or a cancel. We only complete once.
-    if (IsComplete()) {
-      return;
-    }
-
-    mTokensFailed += 1;
-    MOZ_ASSERT(mTokensFailed <= mCountTokens);
-
-    if (mTokensFailed == mCountTokens) {
-      // Provide the final error as being indicitive of the whole set.
-      mPromise.Reject(aError, __func__);
-    }
-  }
-
-  void SetSuccess(const Success& aResult)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause multiple calls to SetSuccess
-    // in succession. We will only select the earliest.
-    if (IsComplete()) {
-      return;
-    }
-
-    mSuccess = true;
-    mPromise.Resolve(aResult, __func__);
-  }
-
-  void SetDeadline(TimeDuration aDeadline)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    // TODO: Monitor the deadline and stop with a timeout error if it expires.
-  }
-
-  RefPtr<MozPromise<Success, nsresult, false>> Ensure()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    return mPromise.Ensure(__func__);
-  }
-
-  void CompleteTask()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    if (mCountTokens == 0) {
-      // Special case for there being no tasks to complete
-      mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
-    }
-  }
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebAuthnRequest)
-
-private:
-  ~WebAuthnRequest() {};
-
-  bool mCancelled;
-  bool mSuccess;
-  int mCountTokens;
-  int mTokensFailed;
-  ReentrantMonitor mReentrantMonitor;
-  MozPromiseHolder<MozPromise<Success, nsresult, false>> mPromise;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_WebAuthnAsync_h
--- a/dom/u2f/moz.build
+++ b/dom/u2f/moz.build
@@ -1,36 +1,23 @@
 # -*- Mode: python; 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',
-    'ScopedCredential.h',
-    'ScopedCredentialInfo.h',
     'U2F.h',
     'U2FAuthenticator.h',
     'USBToken.h',
-    'WebAuthentication.h',
-    'WebAuthnAssertion.h',
-    'WebAuthnAttestation.h',
-    'WebAuthnRequest.h',
 ]
 
 UNIFIED_SOURCES += [
-    'NSSU2FTokenRemote.cpp',
-    'ScopedCredential.cpp',
-    'ScopedCredentialInfo.cpp',
     'U2F.cpp',
     'USBToken.cpp',
-    'WebAuthentication.cpp',
-    'WebAuthnAssertion.cpp',
-    'WebAuthnAttestation.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/dom/base',
--- a/dom/u2f/tests/mochitest.ini
+++ b/dom/u2f/tests/mochitest.ini
@@ -22,23 +22,8 @@ skip-if = !e10s
 [test_register_sign.html]
 skip-if = !e10s
 [test_appid_facet.html]
 skip-if = !e10s
 [test_appid_facet_insecure.html]
 skip-if = !e10s
 [test_appid_facet_subdomain.html]
 skip-if = !e10s
-[test_webauthn_loopback.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_no_token.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_make_credential.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_get_assertion.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_sameorigin.html]
-skip-if = !e10s
-scheme = https
\ No newline at end of file
rename from dom/u2f/NSSU2FTokenRemote.cpp
rename to dom/webauthn/NSSU2FTokenRemote.cpp
rename from dom/u2f/NSSU2FTokenRemote.h
rename to dom/webauthn/NSSU2FTokenRemote.h
rename from dom/u2f/ScopedCredential.cpp
rename to dom/webauthn/ScopedCredential.cpp
rename from dom/u2f/ScopedCredential.h
rename to dom/webauthn/ScopedCredential.h
rename from dom/u2f/ScopedCredentialInfo.cpp
rename to dom/webauthn/ScopedCredentialInfo.cpp
rename from dom/u2f/ScopedCredentialInfo.h
rename to dom/webauthn/ScopedCredentialInfo.h
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/WebAuthentication.cpp
@@ -0,0 +1,1057 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "mozilla/dom/WebAuthentication.h"
+#include "mozilla/dom/WebAuthnAssertion.h"
+#include "mozilla/dom/WebAuthnAttestation.h"
+
+#include "mozilla/dom/Promise.h"
+#include "nsICryptoHash.h"
+#include "pkix/Input.h"
+#include "pkixutil.h"
+
+#define PREF_U2F_SOFTTOKEN_ENABLED "security.webauth.u2f_enable_softtoken"
+#define PREF_U2F_USBTOKEN_ENABLED  "security.webauth.u2f_enable_usbtoken"
+
+namespace mozilla {
+namespace dom {
+
+static mozilla::LazyLogModule gWebauthLog("webauthn");
+
+// Only needed for refcounted objects.
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthentication, mParent)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthentication)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthentication)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthentication)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+template<class OOS>
+static nsresult
+GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
+                 /* out */ nsString& aName)
+{
+  MOZ_ASSERT(aAlgorithm.IsString()); // TODO: remove assertion when we coerce.
+
+  if (aAlgorithm.IsString()) {
+    // If string, then treat as algorithm name
+    aName.Assign(aAlgorithm.GetAsString());
+  } else {
+    // TODO: Coerce to string and extract name. See WebCryptoTask.cpp
+  }
+
+  if (!NormalizeToken(aName, aName)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+HashCString(nsICryptoHash* aHashService, const nsACString& aIn,
+            /* out */ CryptoBuffer& aOut)
+{
+  MOZ_ASSERT(aHashService);
+
+  nsresult rv = aHashService->Init(nsICryptoHash::SHA256);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aHashService->Update(
+         reinterpret_cast<const uint8_t*>(aIn.BeginReading()),aIn.Length());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoCString fullHash;
+  // Passing false below means we will get a binary result rather than a
+  // base64-encoded string.
+  rv = aHashService->Finish(false, fullHash);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aOut.Assign(fullHash);
+  return rv;
+}
+
+static nsresult
+AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
+                   /* out */ nsACString& aJsonOut)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsString challengeBase64;
+  nsresult rv = aChallenge.ToJwkBase64(challengeBase64);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  WebAuthnClientData clientDataObject;
+  clientDataObject.mOrigin.Assign(aOrigin);
+  clientDataObject.mHashAlg.SetAsString().Assign(NS_LITERAL_STRING("S256"));
+  clientDataObject.mChallenge.Assign(challengeBase64);
+
+  nsAutoString temp;
+  if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aJsonOut.Assign(NS_ConvertUTF16toUTF8(temp));
+  return NS_OK;
+}
+
+static nsresult
+ScopedCredentialGetData(const ScopedCredentialDescriptor& aSCD,
+                        /* out */ uint8_t** aBuf, /* out */ uint32_t* aBufLen)
+{
+  MOZ_ASSERT(aBuf);
+  MOZ_ASSERT(aBufLen);
+
+  if (aSCD.mId.IsArrayBufferView()) {
+    const ArrayBufferView& view = aSCD.mId.GetAsArrayBufferView();
+    view.ComputeLengthAndData();
+    *aBuf = view.Data();
+    *aBufLen = view.Length();
+  } else if (aSCD.mId.IsArrayBuffer()) {
+    const ArrayBuffer& buffer = aSCD.mId.GetAsArrayBuffer();
+    buffer.ComputeLengthAndData();
+    *aBuf = buffer.Data();
+    *aBufLen = buffer.Length();
+  } else {
+    MOZ_ASSERT(false);
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
+                   uint32_t aLen)
+{
+  if (aSrc.EnsureLength(aLen) != pkix::Success) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+
+  aDest.ClearAndRetainStorage();
+
+  for (uint32_t offset = 0; offset < aLen; ++offset) {
+    uint8_t b;
+    if (aSrc.Read(b) != pkix::Success) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+    if (!aDest.AppendElement(b, mozilla::fallible)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+U2FAssembleAuthenticatorData(/* out */ CryptoBuffer& aAuthenticatorData,
+                             const CryptoBuffer& aRpIdHash,
+                             const CryptoBuffer& aSignatureData)
+{
+  // The AuthenticatorData for U2F devices is the concatenation of the
+  // RP ID with the output of the U2F Sign operation.
+  if (aRpIdHash.Length() != 32) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (!aAuthenticatorData.AppendElements(aRpIdHash, mozilla::fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!aAuthenticatorData.AppendElements(aSignatureData, mozilla::fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
+                                 /* out */ CryptoBuffer& aPubKeyBuf,
+                                 /* out */ CryptoBuffer& aKeyHandleBuf,
+                                 /* out */ CryptoBuffer& aAttestationCertBuf,
+                                 /* out */ CryptoBuffer& aSignatureBuf)
+{
+  // U2F v1.1 Format via
+  // http://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html
+  //
+  // Bytes  Value
+  // 1      0x05
+  // 65     public key
+  // 1      key handle length
+  // *      key handle
+  // ASN.1  attestation certificate
+  // *      attestation signature
+
+  pkix::Input u2fResponse;
+  u2fResponse.Init(aResponse.Elements(), aResponse.Length());
+
+  pkix::Reader input(u2fResponse);
+
+  uint8_t b;
+  if (input.Read(b) != pkix::Success) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+  if (b != 0x05) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+
+  nsresult rv = ReadToCryptoBuffer(input, aPubKeyBuf, 65);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  uint8_t handleLen;
+  if (input.Read(handleLen) != pkix::Success) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+
+  rv = ReadToCryptoBuffer(input, aKeyHandleBuf, handleLen);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // We have to parse the ASN.1 SEQUENCE on the outside to determine the cert's
+  // length.
+  pkix::Input cert;
+  if (pkix::der::ExpectTagAndGetValue(input, pkix::der::SEQUENCE, cert)
+        != pkix::Success) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+
+  pkix::Reader certInput(cert);
+  rv = ReadToCryptoBuffer(certInput, aAttestationCertBuf, cert.GetLength());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // The remainder of u2fResponse is the signature
+  pkix::Input u2fSig;
+  input.SkipToEnd(u2fSig);
+  pkix::Reader sigInput(u2fSig);
+  rv = ReadToCryptoBuffer(sigInput, aSignatureBuf, u2fSig.GetLength());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+WebAuthentication::WebAuthentication(nsPIDOMWindowInner* aParent)
+  : mInitialized(false)
+{
+  mParent = do_QueryInterface(aParent);
+  MOZ_ASSERT(mParent);
+}
+
+WebAuthentication::~WebAuthentication()
+{}
+
+nsresult
+WebAuthentication::InitLazily()
+{
+  if (mInitialized) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mParent);
+  if (!mParent) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
+  MOZ_ASSERT(doc);
+
+  nsIPrincipal* principal = doc->NodePrincipal();
+  nsresult rv = nsContentUtils::GetUTFOrigin(principal, mOrigin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (NS_WARN_IF(mOrigin.IsEmpty())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // This only functions in e10s mode
+  // TODO: Remove in Bug 1323339
+  if (XRE_IsParentProcess()) {
+    MOZ_LOG(gWebauthLog, LogLevel::Debug,
+            ("Is non-e10s Process, WebAuthn not available"));
+    return NS_ERROR_FAILURE;
+  }
+
+  if (Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED)) {
+    if (!mAuthenticators.AppendElement(new NSSU2FTokenRemote(),
+                                       mozilla::fallible)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  mInitialized = true;
+  return NS_OK;
+}
+
+JSObject*
+WebAuthentication::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return WebAuthenticationBinding::Wrap(aCx, this, aGivenProto);
+}
+
+// NOTE: This method represents a theoretical way to use a U2F-compliant token
+// to produce the result of the WebAuthn MakeCredential method. The exact
+// mapping of U2F data fields to WebAuthn data fields is still a matter of
+// ongoing discussion, and this should not be taken as anything but a point-in-
+// time possibility.
+void
+WebAuthentication::U2FAuthMakeCredential(
+             const RefPtr<CredentialRequest>& aRequest,
+             const Authenticator& aToken, CryptoBuffer& aRpIdHash,
+             const nsACString& aClientData, CryptoBuffer& aClientDataHash,
+             const Account& aAccount,
+             const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
+             const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
+             const WebAuthnExtensions& aExtensions)
+{
+  MOZ_LOG(gWebauthLog, LogLevel::Debug, ("U2FAuthMakeCredential"));
+  aRequest->AddActiveToken(__func__);
+
+  // 5.1.1 When this operation is invoked, the authenticator must perform the
+  // following procedure:
+
+  // 5.1.1.a Check if all the supplied parameters are syntactically well-
+  // formed and of the correct length. If not, return an error code equivalent
+  // to UnknownError and terminate the operation.
+
+  if ((aRpIdHash.Length() != SHA256_LENGTH) ||
+      (aClientDataHash.Length() != SHA256_LENGTH)) {
+    aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
+    return;
+  }
+
+  // 5.1.1.b Check if at least one of the specified combinations of
+  // ScopedCredentialType and cryptographic parameters is supported. If not,
+  // return an error code equivalent to NotSupportedError and terminate the
+  // operation.
+
+  bool isValidCombination = false;
+
+  for (size_t a = 0; a < aNormalizedParams.Length(); ++a) {
+    if (aNormalizedParams[a].mType == ScopedCredentialType::ScopedCred &&
+        aNormalizedParams[a].mAlgorithm.IsString() &&
+        aNormalizedParams[a].mAlgorithm.GetAsString().EqualsLiteral(
+          WEBCRYPTO_NAMED_CURVE_P256)) {
+      isValidCombination = true;
+      break;
+    }
+  }
+  if (!isValidCombination) {
+    aRequest->SetFailure(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  // 5.1.1.c Check if a credential matching any of the supplied
+  // ScopedCredential identifiers is present on this authenticator. If so,
+  // return an error code equivalent to NotAllowedError and terminate the
+  // operation.
+
+  if (aExcludeList.WasPassed()) {
+    const Sequence<ScopedCredentialDescriptor>& list = aExcludeList.Value();
+
+    for (const ScopedCredentialDescriptor& scd : list) {
+      bool isRegistered = false;
+
+      uint8_t *data;
+      uint32_t len;
+
+      // data is owned by the Descriptor, do don't free it here.
+      if (NS_FAILED(ScopedCredentialGetData(scd, &data, &len))) {
+        aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
+        return;
+      }
+
+      nsresult rv = aToken->IsRegistered(data, len, &isRegistered);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        aRequest->SetFailure(rv);
+        return;
+      }
+
+      if (isRegistered) {
+        aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+        return;
+      }
+    }
+  }
+
+  // 5.1.1.d Prompt the user for consent to create a new credential. The
+  // prompt for obtaining this consent is shown by the authenticator if it has
+  // its own output capability, or by the user agent otherwise. If the user
+  // denies consent, return an error code equivalent to NotAllowedError and
+  // terminate the operation.
+
+  // 5.1.1.d Once user consent has been obtained, generate a new credential
+  // object
+
+  // 5.1.1.e If any error occurred while creating the new credential object,
+  // return an error code equivalent to UnknownError and terminate the
+  // operation.
+
+  // 5.1.1.f Process all the supported extensions requested by the client, and
+  // generate an attestation statement. If no authority key is available to
+  // sign such an attestation statement, then the authenticator performs self
+  // attestation of the credential with its own private key. For more details
+  // on attestation, see §5.3 Credential Attestation Statements.
+
+  // No extensions are supported
+
+  // 4.1.1.11 While issuedRequests is not empty, perform the following actions
+  // depending upon the adjustedTimeout timer and responses from the
+  // authenticators:
+
+  // 4.1.1.11.a If the adjustedTimeout timer expires, then for each entry in
+  // issuedRequests invoke the authenticatorCancel operation on that
+  // authenticator and remove its entry from the list.
+
+  uint8_t* buffer;
+  uint32_t bufferlen;
+
+  nsresult rv = aToken->Register(aRpIdHash.Elements(), aRpIdHash.Length(),
+                                 aClientDataHash.Elements(),
+                                 aClientDataHash.Length(), &buffer, &bufferlen);
+
+  // 4.1.1.11.b If any authenticator returns a status indicating that the user
+  // cancelled the operation, delete that authenticator’s entry from
+  // issuedRequests. For each remaining entry in issuedRequests invoke the
+  // authenticatorCancel operation on that authenticator and remove its entry
+  // from the list.
+
+  // 4.1.1.11.c If any authenticator returns an error status, delete the
+  // corresponding entry from issuedRequests.
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
+    return;
+  }
+
+  MOZ_ASSERT(buffer);
+  CryptoBuffer regData;
+  if (NS_WARN_IF(!regData.Assign(buffer, bufferlen))) {
+    free(buffer);
+    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+  free(buffer);
+
+  // Decompose the U2F registration packet
+  CryptoBuffer pubKeyBuf;
+  CryptoBuffer keyHandleBuf;
+  CryptoBuffer attestationCertBuf;
+  CryptoBuffer signatureBuf;
+
+  rv = U2FDecomposeRegistrationResponse(regData, pubKeyBuf, keyHandleBuf,
+                                        attestationCertBuf, signatureBuf);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRequest->SetFailure(rv);
+    return;
+  }
+
+  // Sign the aClientDataHash explicitly to get the format needed for
+  // the AuthenticatorData parameter of WebAuthnAttestation. This might
+  // be temporary while the spec settles down how to incorporate U2F.
+  rv = aToken->Sign(aRpIdHash.Elements(), aRpIdHash.Length(),
+                    aClientDataHash.Elements(), aClientDataHash.Length(),
+                    keyHandleBuf.Elements(), keyHandleBuf.Length(), &buffer,
+                    &bufferlen);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRequest->SetFailure(rv);
+    return;
+  }
+
+  MOZ_ASSERT(buffer);
+  CryptoBuffer signatureData;
+  if (NS_WARN_IF(!signatureData.Assign(buffer, bufferlen))) {
+    free(buffer);
+    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+  free(buffer);
+
+  CryptoBuffer clientDataBuf;
+  if (!clientDataBuf.Assign(aClientData)) {
+    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+
+  CryptoBuffer authenticatorDataBuf;
+  rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, aRpIdHash,
+                                    signatureData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRequest->SetFailure(rv);
+    return;
+  }
+
+  // 4.1.1.11.d If any authenticator indicates success:
+
+  // 4.1.1.11.d.1 Remove this authenticator’s entry from issuedRequests.
+
+  // 4.1.1.11.d.2 Create a new ScopedCredentialInfo object named value and
+  // populate its fields with the values returned from the authenticator as well
+  // as the clientDataJSON computed earlier.
+
+  RefPtr<ScopedCredential> credential = new ScopedCredential(this);
+  credential->SetType(ScopedCredentialType::ScopedCred);
+  credential->SetId(keyHandleBuf);
+
+  RefPtr<WebAuthnAttestation> attestation = new WebAuthnAttestation(this);
+  attestation->SetFormat(NS_LITERAL_STRING("u2f"));
+  attestation->SetClientData(clientDataBuf);
+  attestation->SetAuthenticatorData(authenticatorDataBuf);
+  attestation->SetAttestation(regData);
+
+  CredentialPtr info = new ScopedCredentialInfo(this);
+  info->SetCredential(credential);
+  info->SetAttestation(attestation);
+
+  // 4.1.1.11.d.3 For each remaining entry in issuedRequests invoke the
+  // authenticatorCancel operation on that authenticator and remove its entry
+  // from the list.
+
+  // 4.1.1.11.d.4 Resolve promise with value and terminate this algorithm.
+  aRequest->SetSuccess(info);
+}
+
+// NOTE: This method represents a theoretical way to use a U2F-compliant token
+// to produce the result of the WebAuthn GetAssertion method. The exact mapping
+// of U2F data fields to WebAuthn data fields is still a matter of ongoing
+// discussion, and this should not be taken as anything but a point-in- time
+// possibility.
+void
+WebAuthentication::U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
+                    const Authenticator& aToken, CryptoBuffer& aRpIdHash,
+                    const nsACString& aClientData, CryptoBuffer& aClientDataHash,
+                    nsTArray<CryptoBuffer>& aAllowList,
+                    const WebAuthnExtensions& aExtensions)
+{
+  MOZ_LOG(gWebauthLog, LogLevel::Debug, ("U2FAuthGetAssertion"));
+
+  // 4.1.2.7.e Add an entry to issuedRequests, corresponding to this request.
+  aRequest->AddActiveToken(__func__);
+
+  // 4.1.2.8 While issuedRequests is not empty, perform the following actions
+  // depending upon the adjustedTimeout timer and responses from the
+  // authenticators:
+
+  // 4.1.2.8.a If the timer for adjustedTimeout expires, then for each entry
+  // in issuedRequests invoke the authenticatorCancel operation on that
+  // authenticator and remove its entry from the list.
+
+  for (CryptoBuffer& allowedCredential : aAllowList) {
+    bool isRegistered = false;
+    nsresult rv = aToken->IsRegistered(allowedCredential.Elements(),
+                                       allowedCredential.Length(),
+                                       &isRegistered);
+
+    // 4.1.2.8.b If any authenticator returns a status indicating that the user
+    // cancelled the operation, delete that authenticator’s entry from
+    // issuedRequests. For each remaining entry in issuedRequests invoke the
+    // authenticatorCancel operation on that authenticator, and remove its entry
+    // from the list.
+
+    // 4.1.2.8.c If any authenticator returns an error status, delete the
+    // corresponding entry from issuedRequests.
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRequest->SetFailure(rv);
+      return;
+    }
+
+    if (!isRegistered) {
+      continue;
+    }
+
+    // Sign
+    uint8_t* buffer;
+    uint32_t bufferlen;
+    rv = aToken->Sign(aRpIdHash.Elements(), aRpIdHash.Length(),
+                      aClientDataHash.Elements(), aClientDataHash.Length(),
+                      allowedCredential.Elements(), allowedCredential.Length(),
+                      &buffer, &bufferlen);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRequest->SetFailure(rv);
+      return;
+    }
+
+    MOZ_ASSERT(buffer);
+    CryptoBuffer signatureData;
+    if (NS_WARN_IF(!signatureData.Assign(buffer, bufferlen))) {
+      free(buffer);
+      aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+    free(buffer);
+
+    // 4.1.2.8.d If any authenticator returns success:
+
+    // 4.1.2.8.d.1 Remove this authenticator’s entry from issuedRequests.
+
+    // 4.1.2.8.d.2 Create a new WebAuthnAssertion object named value and
+    // populate its fields with the values returned from the authenticator as
+    // well as the clientDataJSON computed earlier.
+
+    CryptoBuffer clientDataBuf;
+    if (!clientDataBuf.Assign(aClientData)) {
+      aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+
+    CryptoBuffer authenticatorDataBuf;
+    rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, aRpIdHash,
+                                      signatureData);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRequest->SetFailure(rv);
+      return;
+    }
+
+    RefPtr<ScopedCredential> credential = new ScopedCredential(this);
+    credential->SetType(ScopedCredentialType::ScopedCred);
+    credential->SetId(allowedCredential);
+
+    AssertionPtr assertion = new WebAuthnAssertion(this);
+    assertion->SetCredential(credential);
+    assertion->SetClientData(clientDataBuf);
+    assertion->SetAuthenticatorData(authenticatorDataBuf);
+    assertion->SetSignature(signatureData);
+
+    // 4.1.2.8.d.3 For each remaining entry in issuedRequests invoke the
+    // authenticatorCancel operation on that authenticator and remove its entry
+    // from the list.
+
+    // 4.1.2.8.d.4 Resolve promise with value and terminate this algorithm.
+    aRequest->SetSuccess(assertion);
+    return;
+  }
+
+  // 4.1.2.9 Reject promise with a DOMException whose name is "NotAllowedError",
+  // and terminate this algorithm.
+  aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+}
+
+nsresult
+WebAuthentication::RelaxSameOrigin(const nsAString& aInputRpId,
+                                   /* out */ nsACString& aRelaxedRpId)
+{
+  MOZ_ASSERT(mParent);
+  nsCOMPtr<nsIDocument> document = mParent->GetDoc();
+  if (!document || !document->IsHTMLDocument()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // TODO: Bug 1329764: Invoke the Relax Algorithm, once properly defined
+  aRelaxedRpId.Assign(NS_ConvertUTF16toUTF8(aInputRpId));
+  return NS_OK;
+}
+
+already_AddRefed<Promise>
+WebAuthentication::MakeCredential(JSContext* aCx, const Account& aAccount,
+                  const Sequence<ScopedCredentialParameters>& aCryptoParameters,
+                  const ArrayBufferViewOrArrayBuffer& aChallenge,
+                  const ScopedCredentialOptions& aOptions)
+{
+  MOZ_ASSERT(mParent);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+  if (!global) {
+    return nullptr;
+  }
+
+  ErrorResult rv;
+  RefPtr<Promise> promise = Promise::Create(global, rv);
+
+  nsresult initRv = InitLazily();
+  if (NS_FAILED(initRv)) {
+    promise->MaybeReject(initRv);
+    return promise.forget();
+  }
+
+  // 4.1.1.1 If timeoutSeconds was specified, check if its value lies within a
+  // reasonable range as defined by the platform and if not, correct it to the
+  // closest value lying within that range.
+
+  double adjustedTimeout = 30.0;
+  if (aOptions.mTimeoutSeconds.WasPassed()) {
+    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
+    adjustedTimeout = std::max(15.0, adjustedTimeout);
+    adjustedTimeout = std::min(120.0, adjustedTimeout);
+  }
+
+  // 4.1.1.2 Let promise be a new Promise. Return promise and start a timer for
+  // adjustedTimeout seconds.
+
+  RefPtr<CredentialRequest> requestMonitor = new CredentialRequest();
+  requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout));
+
+  if (mOrigin.EqualsLiteral("null")) {
+    // 4.1.1.3 If callerOrigin is an opaque origin, reject promise with a
+    // DOMException whose name is "NotAllowedError", and terminate this
+    // algorithm
+    MOZ_LOG(gWebauthLog, LogLevel::Debug, ("Rejecting due to opaque origin"));
+    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+    return promise.forget();
+  }
+
+  nsCString rpId;
+  if (!aOptions.mRpId.WasPassed()) {
+    // 4.1.1.3.a If rpId is not specified, then set rpId to callerOrigin, and
+    // rpIdHash to the SHA-256 hash of rpId.
+    rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin));
+  } else {
+    // 4.1.1.3.b If rpId is specified, then invoke the procedure used for
+    // relaxing the same-origin restriction by setting the document.domain
+    // attribute, using rpId as the given value but without changing the current
+    // document’s domain. If no errors are thrown, set rpId to the value of host
+    // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
+    // Otherwise, reject promise with a DOMException whose name is
+    // "SecurityError", and terminate this algorithm.
+
+    if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) {
+      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+      return promise.forget();
+    }
+  }
+
+  CryptoBuffer rpIdHash;
+  if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
+    promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    return promise.forget();
+  }
+
+  nsresult srv;
+  nsCOMPtr<nsICryptoHash> hashService =
+    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  srv = HashCString(hashService, rpId, rpIdHash);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  // 4.1.1.4 Process each element of cryptoParameters using the following steps,
+  // to produce a new sequence normalizedParameters.
+  nsTArray<ScopedCredentialParameters> normalizedParams;
+  for (size_t a = 0; a < aCryptoParameters.Length(); ++a) {
+    // 4.1.1.4.a Let current be the currently selected element of
+    // cryptoParameters.
+
+    // 4.1.1.4.b If current.type does not contain a ScopedCredentialType
+    // supported by this implementation, then stop processing current and move
+    // on to the next element in cryptoParameters.
+    if (aCryptoParameters[a].mType != ScopedCredentialType::ScopedCred) {
+      continue;
+    }
+
+    // 4.1.1.4.c Let normalizedAlgorithm be the result of normalizing an
+    // algorithm using the procedure defined in [WebCryptoAPI], with alg set to
+    // current.algorithm and op set to 'generateKey'. If an error occurs during
+    // this procedure, then stop processing current and move on to the next
+    // element in cryptoParameters.
+
+    nsString algName;
+    if (NS_FAILED(GetAlgorithmName(aCx, aCryptoParameters[a].mAlgorithm,
+                                   algName))) {
+      continue;
+    }
+
+    // 4.1.1.4.d Add a new object of type ScopedCredentialParameters to
+    // normalizedParameters, with type set to current.type and algorithm set to
+    // normalizedAlgorithm.
+    ScopedCredentialParameters normalizedObj;
+    normalizedObj.mType = aCryptoParameters[a].mType;
+    normalizedObj.mAlgorithm.SetAsString().Assign(algName);
+
+    if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){
+      promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+      return promise.forget();
+    }
+  }
+
+  // 4.1.1.5 If normalizedAlgorithm is empty and cryptoParameters was not empty,
+  // cancel the timer started in step 2, reject promise with a DOMException
+  // whose name is "NotSupportedError", and terminate this algorithm.
+  if (normalizedParams.IsEmpty() && !aCryptoParameters.IsEmpty()) {
+    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return promise.forget();
+  }
+
+  // 4.1.1.6 If excludeList is undefined, set it to the empty list.
+
+  // 4.1.1.7 If extensions was specified, process any extensions supported by
+  // this client platform, to produce the extension data that needs to be sent
+  // to the authenticator. If an error is encountered while processing an
+  // extension, skip that extension and do not produce any extension data for
+  // it. Call the result of this processing clientExtensions.
+
+  // Currently no extensions are supported
+
+  // 4.1.1.8 Use attestationChallenge, callerOrigin and rpId, along with the
+  // token binding key associated with callerOrigin (if any), to create a
+  // ClientData structure representing this request. Choose a hash algorithm for
+  // hashAlg and compute the clientDataJSON and clientDataHash.
+
+  CryptoBuffer challenge;
+  if (!challenge.Assign(aChallenge)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  nsAutoCString clientDataJSON;
+  srv = AssembleClientData(mOrigin, challenge, clientDataJSON);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  CryptoBuffer clientDataHash;
+  if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  srv = HashCString(hashService, clientDataJSON, clientDataHash);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  // 4.1.1.9 Initialize issuedRequests to an empty list.
+  RefPtr<CredentialPromise> monitorPromise = requestMonitor->Ensure();
+
+  // 4.1.1.10 For each authenticator currently available on this platform:
+  // asynchronously invoke the authenticatorMakeCredential operation on that
+  // authenticator with rpIdHash, clientDataHash, accountInformation,
+  // normalizedParameters, excludeList and clientExtensions as parameters. Add a
+  // corresponding entry to issuedRequests.
+  for (Authenticator u2ftoken : mAuthenticators) {
+    // 4.1.1.10.a For each credential C in excludeList that has a non-empty
+    // transports list, optionally use only the specified transports to test for
+    // the existence of C.
+    U2FAuthMakeCredential(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
+                          clientDataHash, aAccount, normalizedParams,
+                          aOptions.mExcludeList, aOptions.mExtensions);
+  }
+
+  requestMonitor->CompleteTask();
+
+  monitorPromise->Then(AbstractThread::MainThread(), __func__,
+    [promise] (CredentialPtr aInfo) {
+      promise->MaybeResolve(aInfo);
+    },
+    [promise] (nsresult aErrorCode) {
+      promise->MaybeReject(aErrorCode);
+  });
+
+  return promise.forget();
+}
+
+already_AddRefed<Promise>
+WebAuthentication::GetAssertion(const ArrayBufferViewOrArrayBuffer& aChallenge,
+                                const AssertionOptions& aOptions)
+{
+  MOZ_ASSERT(mParent);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+  if (!global) {
+    return nullptr;
+  }
+
+  // 4.1.2.1 If timeoutSeconds was specified, check if its value lies within a
+  // reasonable range as defined by the platform and if not, correct it to the
+  // closest value lying within that range.
+
+  double adjustedTimeout = 30.0;
+  if (aOptions.mTimeoutSeconds.WasPassed()) {
+    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
+    adjustedTimeout = std::max(15.0, adjustedTimeout);
+    adjustedTimeout = std::min(120.0, adjustedTimeout);
+  }
+
+  // 4.1.2.2 Let promise be a new Promise. Return promise and start a timer for
+  // adjustedTimeout seconds.
+
+  RefPtr<AssertionRequest> requestMonitor = new AssertionRequest();
+  requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout));
+
+  ErrorResult rv;
+  RefPtr<Promise> promise = Promise::Create(global, rv);
+
+  nsresult initRv = InitLazily();
+  if (NS_FAILED(initRv)) {
+    promise->MaybeReject(initRv);
+    return promise.forget();
+  }
+
+  if (mOrigin.EqualsLiteral("null")) {
+    // 4.1.2.3 If callerOrigin is an opaque origin, reject promise with a
+    // DOMException whose name is "NotAllowedError", and terminate this algorithm
+    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+    return promise.forget();
+  }
+
+  nsCString rpId;
+  if (!aOptions.mRpId.WasPassed()) {
+    // 4.1.2.3.a If rpId is not specified, then set rpId to callerOrigin, and
+    // rpIdHash to the SHA-256 hash of rpId.
+    rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin));
+  } else {
+    // 4.1.2.3.b If rpId is specified, then invoke the procedure used for
+    // relaxing the same-origin restriction by setting the document.domain
+    // attribute, using rpId as the given value but without changing the current
+    // document’s domain. If no errors are thrown, set rpId to the value of host
+    // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
+    // Otherwise, reject promise with a DOMException whose name is
+    // "SecurityError", and terminate this algorithm.
+
+    if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) {
+      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+      return promise.forget();
+    }
+  }
+
+  CryptoBuffer rpIdHash;
+  if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  nsresult srv;
+  nsCOMPtr<nsICryptoHash> hashService =
+    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  srv = HashCString(hashService, rpId, rpIdHash);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  // 4.1.2.4 If extensions was specified, process any extensions supported by
+  // this client platform, to produce the extension data that needs to be sent
+  // to the authenticator. If an error is encountered while processing an
+  // extension, skip that extension and do not produce any extension data for
+  // it. Call the result of this processing clientExtensions.
+
+  // TODO
+
+  // 4.1.2.5 Use assertionChallenge, callerOrigin and rpId, along with the token
+  // binding key associated with callerOrigin (if any), to create a ClientData
+  // structure representing this request. Choose a hash algorithm for hashAlg
+  // and compute the clientDataJSON and clientDataHash.
+  CryptoBuffer challenge;
+  if (!challenge.Assign(aChallenge)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  nsAutoCString clientDataJSON;
+  srv = AssembleClientData(mOrigin, challenge, clientDataJSON);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  CryptoBuffer clientDataHash;
+  if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  srv = HashCString(hashService, clientDataJSON, clientDataHash);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  // Note: we only support U2F-style authentication for now, so we effectively
+  // require an AllowList.
+  if (!aOptions.mAllowList.WasPassed()) {
+    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+    return promise.forget();
+  }
+
+  const Sequence<ScopedCredentialDescriptor>& allowList =
+    aOptions.mAllowList.Value();
+
+  // 4.1.2.6 Initialize issuedRequests to an empty list.
+  RefPtr<AssertionPromise> monitorPromise = requestMonitor->Ensure();
+
+  // 4.1.2.7 For each authenticator currently available on this platform,
+  // perform the following steps:
+  for(Authenticator u2ftoken : mAuthenticators) {
+    // 4.1.2.7.a If allowList is undefined or empty, let credentialList be an
+    // empty list. Otherwise, execute a platform-specific procedure to determine
+    // which, if any, credentials listed in allowList might be present on this
+    // authenticator, and set credentialList to this filtered list. If no such
+    // filtering is possible, set credentialList to an empty list.
+
+    nsTArray<CryptoBuffer> credentialList;
+
+    for (const ScopedCredentialDescriptor& scd : allowList) {
+      CryptoBuffer buf;
+      if (NS_WARN_IF(!buf.Assign(scd.mId))) {
+        continue;
+      }
+
+      // 4.1.2.7.b For each credential C within the credentialList that has a
+      // non- empty transports list, optionally use only the specified
+      // transports to get assertions using credential C.
+
+      // TODO: Filter using Transport
+      if (!credentialList.AppendElement(buf, mozilla::fallible)) {
+        requestMonitor->CancelNow();
+        promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+        return promise.forget();
+      }
+    }
+
+    // 4.1.2.7.c If the above filtering process concludes that none of the
+    // credentials on allowList can possibly be on this authenticator, do not
+    // perform any of the following steps for this authenticator, and proceed to
+    // the next authenticator (if any).
+    if (credentialList.IsEmpty()) {
+      continue;
+    }
+
+    // 4.1.2.7.d Asynchronously invoke the authenticatorGetAssertion operation
+    // on this authenticator with rpIdHash, clientDataHash, credentialList, and
+    // clientExtensions as parameters.
+    U2FAuthGetAssertion(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
+                        clientDataHash, credentialList, aOptions.mExtensions);
+  }
+
+  requestMonitor->CompleteTask();
+
+  monitorPromise->Then(AbstractThread::MainThread(), __func__,
+    [promise] (AssertionPtr aAssertion) {
+      promise->MaybeResolve(aAssertion);
+    },
+    [promise] (nsresult aErrorCode) {
+      promise->MaybeReject(aErrorCode);
+  });
+
+  return promise.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/WebAuthentication.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_WebAuthentication_h
+#define mozilla_dom_WebAuthentication_h
+
+#include "hasht.h"
+#include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/WebAuthenticationBinding.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/SharedThreadPool.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsNetCID.h"
+#include "nsWrapperCache.h"
+
+#include "U2FAuthenticator.h"
+#include "WebAuthnRequest.h"
+
+namespace mozilla {
+namespace dom {
+
+struct Account;
+class ArrayBufferViewOrArrayBuffer;
+struct AssertionOptions;
+class OwningArrayBufferViewOrArrayBuffer;
+struct ScopedCredentialOptions;
+struct ScopedCredentialParameters;
+
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla {
+namespace dom {
+
+typedef RefPtr<ScopedCredentialInfo> CredentialPtr;
+typedef RefPtr<WebAuthnAssertion> AssertionPtr;
+typedef WebAuthnRequest<CredentialPtr> CredentialRequest;
+typedef WebAuthnRequest<AssertionPtr> AssertionRequest;
+typedef MozPromise<CredentialPtr, nsresult, false> CredentialPromise;
+typedef MozPromise<AssertionPtr, nsresult, false> AssertionPromise;
+
+class WebAuthentication final : public nsISupports
+                              , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthentication)
+
+public:
+  explicit WebAuthentication(nsPIDOMWindowInner* aParent);
+
+protected:
+  ~WebAuthentication();
+
+public:
+  nsPIDOMWindowInner*
+  GetParentObject() const
+  {
+    return mParent;
+  }
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<Promise>
+  MakeCredential(JSContext* aCx, const Account& accountInformation,
+                 const Sequence<ScopedCredentialParameters>& cryptoParameters,
+                 const ArrayBufferViewOrArrayBuffer& attestationChallenge,
+                 const ScopedCredentialOptions& options);
+
+  already_AddRefed<Promise>
+  GetAssertion(const ArrayBufferViewOrArrayBuffer& assertionChallenge,
+               const AssertionOptions& options);
+
+private:
+  nsresult
+  InitLazily();
+
+  void
+  U2FAuthMakeCredential(const RefPtr<CredentialRequest>& aRequest,
+             const Authenticator& aToken, CryptoBuffer& aRpIdHash,
+             const nsACString& aClientData, CryptoBuffer& aClientDataHash,
+             const Account& aAccount,
+             const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
+             const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
+             const WebAuthnExtensions& aExtensions);
+  void
+  U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
+                   const Authenticator& aToken, CryptoBuffer& aRpIdHash,
+                   const nsACString& aClientData, CryptoBuffer& aClientDataHash,
+                   nsTArray<CryptoBuffer>& aAllowList,
+                   const WebAuthnExtensions& aExtensions);
+
+  nsresult
+  RelaxSameOrigin(const nsAString& aInputRpId, nsACString& aRelaxedRpId);
+
+  nsCOMPtr<nsPIDOMWindowInner> mParent;
+  nsString mOrigin;
+  Sequence<Authenticator> mAuthenticators;
+  bool mInitialized;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebAuthentication_h
rename from dom/u2f/WebAuthnAssertion.cpp
rename to dom/webauthn/WebAuthnAssertion.cpp
rename from dom/u2f/WebAuthnAssertion.h
rename to dom/webauthn/WebAuthnAssertion.h
rename from dom/u2f/WebAuthnAttestation.cpp
rename to dom/webauthn/WebAuthnAttestation.cpp
rename from dom/u2f/WebAuthnAttestation.h
rename to dom/webauthn/WebAuthnAttestation.h
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/WebAuthnRequest.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_WebAuthnAsync_h
+#define mozilla_dom_WebAuthnAsync_h
+
+#include "mozilla/MozPromise.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+namespace dom {
+
+//extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
+
+// WebAuthnRequest tracks the completion of a single WebAuthn request that
+// may run on multiple kinds of authenticators, and be subject to a deadline.
+template<class Success>
+class WebAuthnRequest {
+public:
+  WebAuthnRequest()
+    : mCancelled(false)
+    , mSuccess(false)
+    , mCountTokens(0)
+    , mTokensFailed(0)
+    , mReentrantMonitor("WebAuthnRequest")
+  {}
+
+  void AddActiveToken(const char* aCallSite)
+  {
+    // MOZ_LOG(gWebauthLog, LogLevel::Debug,
+    //        ("WebAuthnRequest is tracking a new token, called from [%s]",
+    //         aCallSite));
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    MOZ_ASSERT(!IsComplete());
+    mCountTokens += 1;
+  }
+
+  bool IsComplete()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    return mCancelled || mSuccess ||
+      (mCountTokens > 0 && mTokensFailed == mCountTokens);
+  }
+
+  void CancelNow()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // It's possible for a race to cause CancelNow to get called after
+    // a success or a cancel. We only complete once.
+    if (IsComplete()) {
+      return;
+    }
+
+    mCancelled = true;
+    mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
+  }
+
+  void SetFailure(nsresult aError)
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // It's possible for a race to cause SetFailure to get called after
+    // a success or a cancel. We only complete once.
+    if (IsComplete()) {
+      return;
+    }
+
+    mTokensFailed += 1;
+    MOZ_ASSERT(mTokensFailed <= mCountTokens);
+
+    if (mTokensFailed == mCountTokens) {
+      // Provide the final error as being indicitive of the whole set.
+      mPromise.Reject(aError, __func__);
+    }
+  }
+
+  void SetSuccess(const Success& aResult)
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // It's possible for a race to cause multiple calls to SetSuccess
+    // in succession. We will only select the earliest.
+    if (IsComplete()) {
+      return;
+    }
+
+    mSuccess = true;
+    mPromise.Resolve(aResult, __func__);
+  }
+
+  void SetDeadline(TimeDuration aDeadline)
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    MOZ_ASSERT(!IsComplete());
+    // TODO: Monitor the deadline and stop with a timeout error if it expires.
+  }
+
+  RefPtr<MozPromise<Success, nsresult, false>> Ensure()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    MOZ_ASSERT(!IsComplete());
+    return mPromise.Ensure(__func__);
+  }
+
+  void CompleteTask()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    if (mCountTokens == 0) {
+      // Special case for there being no tasks to complete
+      mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
+    }
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebAuthnRequest)
+
+private:
+  ~WebAuthnRequest() {};
+
+  bool mCancelled;
+  bool mSuccess;
+  int mCountTokens;
+  int mTokensFailed;
+  ReentrantMonitor mReentrantMonitor;
+  MozPromiseHolder<MozPromise<Success, nsresult, false>> mPromise;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebAuthnAsync_h
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/moz.build
@@ -0,0 +1,38 @@
+# -*- Mode: python; 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',
+    'ScopedCredential.h',
+    'ScopedCredentialInfo.h',
+    'WebAuthentication.h',
+    'WebAuthnAssertion.h',
+    'WebAuthnAttestation.h',
+    'WebAuthnRequest.h',
+]
+
+UNIFIED_SOURCES += [
+    'NSSU2FTokenRemote.cpp',
+    'ScopedCredential.cpp',
+    'ScopedCredentialInfo.cpp',
+    'WebAuthentication.cpp',
+    'WebAuthnAssertion.cpp',
+    'WebAuthnAttestation.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+    '/dom/base',
+    '/dom/crypto',
+    '/security/manager/ssl',
+    '/security/pkix/include',
+    '/security/pkix/lib',
+]
+
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/tests/mochitest.ini
@@ -0,0 +1,16 @@
+[DEFAULT]
+[test_webauthn_loopback.html]
+skip-if = !e10s
+scheme = https
+[test_webauthn_no_token.html]
+skip-if = !e10s
+scheme = https
+[test_webauthn_make_credential.html]
+skip-if = !e10s
+scheme = https
+[test_webauthn_get_assertion.html]
+skip-if = !e10s
+scheme = https
+[test_webauthn_sameorigin.html]
+skip-if = !e10s
+scheme = https
\ No newline at end of file
rename from dom/u2f/tests/test_webauthn_get_assertion.html
rename to dom/webauthn/tests/test_webauthn_get_assertion.html
rename from dom/u2f/tests/test_webauthn_loopback.html
rename to dom/webauthn/tests/test_webauthn_loopback.html
rename from dom/u2f/tests/test_webauthn_make_credential.html
rename to dom/webauthn/tests/test_webauthn_make_credential.html
rename from dom/u2f/tests/test_webauthn_no_token.html
rename to dom/webauthn/tests/test_webauthn_no_token.html
rename from dom/u2f/tests/test_webauthn_sameorigin.html
rename to dom/webauthn/tests/test_webauthn_sameorigin.html