Bug 1309284 - WebAuthn JS API [part 3]: Support origin relax algorithm r?keeler draft
authorJ.C. Jones <jjones@mozilla.com>
Thu, 15 Dec 2016 16:40:06 -0700
changeset 450472 c7130d13d076e9494e9b877c433d80b40c955aa5
parent 450471 0a90dec919dbe4ced4bd3b4d3bf44ffa0a5fa080
child 450473 6022a889b2ac6f752fb84eda7d759389c364997e
push id38864
push userjjones@mozilla.com
push dateFri, 16 Dec 2016 17:47:57 +0000
reviewerskeeler
bugs1309284
milestone53.0a1
Bug 1309284 - WebAuthn JS API [part 3]: Support origin relax algorithm r?keeler The WebAuthn specification calls for running the HTML5.1 algorithm that occurs when you modify document.domain from JS, and use that algorithm's output for the "Relying Party ID" through the rest of the WebAuthn algorithm. This codes calls that method in the current nsHTMLDocument. MozReview-Commit-ID: GN1E4U8sIsn
dom/u2f/WebAuthentication.cpp
dom/u2f/WebAuthentication.h
dom/u2f/moz.build
--- a/dom/u2f/WebAuthentication.cpp
+++ b/dom/u2f/WebAuthentication.cpp
@@ -5,16 +5,17 @@
  * 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 "nsHTMLDocument.h"
 #include "pkix/Input.h"
 #include "pkixutil.h"
 
 namespace mozilla {
 namespace dom {
 
 extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
 
@@ -634,16 +635,38 @@ WebAuthentication::U2FAuthGetAssertion(c
     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;
+  }
+
+  RefPtr<nsHTMLDocument> htmlDoc = document->AsHTMLDocument();
+
+  ErrorResult rv;
+  nsCOMPtr<nsIURI> relaxedUri = htmlDoc->RelaxSameOriginForDomain(aInputRpId,
+                                                                  rv);
+  if (rv.Failed()) {
+    return rv.StealNSResult();
+  }
+
+  return relaxedUri->GetAsciiHost(aRelaxedRpId);
+}
+
 already_AddRefed<Promise>
 WebAuthentication::MakeCredential(JSContext* aCx, const Account& aAccount,
                   const Sequence<ScopedCredentialParameters>& aCryptoParameters,
                   const ArrayBufferViewOrArrayBuffer& aChallenge,
                   const ScopedCredentialOptions& aOptions)
 {
   MOZ_ASSERT(mParent);
   nsPIDOMWindowInner* window = GetParentObject();
@@ -695,19 +718,20 @@ WebAuthentication::MakeCredential(JSCont
     // 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.
 
-    // TODO: relax the same-origin restriction
-
-    rpId.Assign(NS_ConvertUTF16toUTF8(aOptions.mRpId.Value()));
+    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();
   }
 
@@ -896,19 +920,20 @@ WebAuthentication::GetAssertion(const Ar
     // 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.
 
-    // TODO: relax the same-origin restriction
-
-    rpId.Assign(NS_ConvertUTF16toUTF8(aOptions.mRpId.Value()));
+    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();
   }
 
--- a/dom/u2f/WebAuthentication.h
+++ b/dom/u2f/WebAuthentication.h
@@ -93,16 +93,19 @@ private:
              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
--- a/dom/u2f/moz.build
+++ b/dom/u2f/moz.build
@@ -30,14 +30,16 @@ UNIFIED_SOURCES += [
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/dom/crypto',
+    '/dom/html',
+    '/layout/style',
     '/security/manager/ssl',
     '/security/pkix/include',
     '/security/pkix/lib',
 ]
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']