--- a/dom/u2f/tests/mochitest.ini
+++ b/dom/u2f/tests/mochitest.ini
@@ -22,8 +22,23 @@ 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
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_webauthn_get_assertion.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <title>Tests for GetAssertion for W3C Web Authentication</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/common.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.js"></script>
+ <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+ <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Tests for GetAssertion for W3C Web Authentication</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+// Execute the full-scope test
+SimpleTest.waitForExplicitFinish();
+
+function arrivingHereIsBad(aResult) {
+ ok(false, "Bad result! Received a: " + aResult);
+ return Promise.resolve();
+}
+
+function expectNotAllowedError(aResult) {
+ ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
+ return Promise.resolve();
+}
+
+function expectTypeError(aResult) {
+ ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
+ return Promise.resolve();
+}
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.w3c", true],
+ ["security.webauth.u2f_enable_softtoken", true],
+ ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+ isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+ isnot(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint must exist");
+ isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
+
+ let authn = navigator.authentication;
+
+ let gAssertionChallenge = new Uint8Array(16);
+ window.crypto.getRandomValues(gAssertionChallenge);
+
+ let invalidCred = { type: "Magic", id: base64ToBytes("AAA=") };
+ let unknownCred = { type: "ScopedCred", id: base64ToBytes("AAA=") };
+
+ Promise.all([
+ // Test basic good call, but without giving a credential so expect failures
+ // this is OK by the standard, but not supported by U2F-backed authenticators
+ // like the soft token in use here.
+ authn.getAssertion(gAssertionChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectNotAllowedError),
+
+ // Test with an unexpected option
+ authn.getAssertion(gAssertionChallenge, { unknownValue: "hi" })
+ .then(arrivingHereIsBad)
+ .catch(expectNotAllowedError),
+
+ // Test with an invalid credential
+ authn.getAssertion(gAssertionChallenge, { allowList: [invalidCred] })
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test with an unknown credential
+ authn.getAssertion(gAssertionChallenge, { allowList: [unknownCred] })
+ .then(arrivingHereIsBad)
+ .catch(expectNotAllowedError),
+
+ // Test with an unexpected option and an invalid credential
+ authn.getAssertion(gAssertionChallenge, { unknownValue: "hi" })
+ .then(arrivingHereIsBad)
+ .catch(expectNotAllowedError)
+ ])
+ .then(function(){
+ SimpleTest.finish();
+ });
+});
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_webauthn_loopback.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <title>Full-run test for MakeCredential/GetAssertion for W3C Web Authentication</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/common.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.js"></script>
+ <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+ <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Full-run test for MakeCredential/GetAssertion for W3C Web Authentication</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+// Execute the full-scope test
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.w3c", true],
+ ["security.webauth.u2f_enable_softtoken", true],
+ ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+ isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+ isnot(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint must exist");
+ isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
+
+ let authn = navigator.authentication;
+
+ let gCredentialChallenge = new Uint8Array(16);
+ window.crypto.getRandomValues(gCredentialChallenge);
+ let gAssertionChallenge = new Uint8Array(16);
+ window.crypto.getRandomValues(gAssertionChallenge);
+
+ testMakeCredential();
+
+ function checkCredentialValid(aCredInfo) {
+ /* ScopedCredentialInfo
+ - Credential
+ -- ID: Key Handle buffer pulled from U2F Register() Response
+ -- Type: "ScopedCred"
+ - WebAuthnAttestation
+ -- Format: "u2f"
+ -- ClientData: serialized JSON
+ -- AuthenticatorData: RP ID Hash || U2F Sign() Response
+ -- Attestation: U2F Register() Response */
+
+ is(aCredInfo.credential.type, "ScopedCred", "Type is correct");
+ ok(aCredInfo.credential.id.length > 0, "Key ID exists");
+
+ is(aCredInfo.attestation.format, "u2f", "Format is correct");
+ is(aCredInfo.attestation.attestation[0], 0x05, "Reserved byte is correct");
+ ok(aCredInfo.attestation.authenticatorData.length > 0, "Authenticator data exists");
+ let clientData = JSON.parse(buffer2string(aCredInfo.attestation.clientData));
+ is(clientData.challenge, bytesToBase64UrlSafe(gCredentialChallenge), "Challenge is correct");
+ is(clientData.origin, window.location.origin, "Origin is correct");
+ is(clientData.hashAlg, "S256", "Hash algorithm is correct");
+
+ return decodeU2FRegistration(aCredInfo.attestation.attestation)
+ .then(function(u2fObj) {
+ aCredInfo.u2fReg = u2fObj;
+ return aCredInfo;
+ });
+ }
+
+ function checkAssertionAndSigValid(aPublicKey, aAssertion) {
+ /* WebAuthnAssertion
+ - Credential
+ -- ID: ID of Credential from AllowList that succeeded
+ -- Type: "ScopedCred"
+ - ClientData: serialized JSON
+ - AuthenticatorData: RP ID Hash || U2F Sign() Response
+ - Signature: U2F Sign() Response */
+
+ is(aAssertion.credential.type, "ScopedCred", "Type is correct");
+ ok(aAssertion.credential.id.length > 0, "Key ID exists");
+
+ ok(aAssertion.authenticatorData.length > 0, "Authenticator data exists");
+ let clientData = JSON.parse(buffer2string(aAssertion.clientData));
+ is(clientData.challenge, bytesToBase64UrlSafe(gAssertionChallenge), "Challenge is correct");
+ is(clientData.origin, window.location.origin, "Origin is correct");
+ is(clientData.hashAlg, "S256", "Hash algorithm is correct");
+
+ // Parse the signature data
+ if (aAssertion.signature[0] != 0x01) {
+ throw "User presence byte not set";
+ }
+ let presenceAndCounter = aAssertion.signature.slice(0,5);
+ let signatureValue = aAssertion.signature.slice(5);
+
+ let rpIdHash = aAssertion.authenticatorData.slice(0,32);
+
+ // Assemble the signed data and verify the signature
+ return deriveAppAndChallengeParam(clientData.origin, aAssertion.clientData)
+ .then(function(aParams) {
+ console.log(aParams.appParam, rpIdHash, presenceAndCounter, aParams.challengeParam);
+ console.log("ClientData buffer: ", hexEncode(aAssertion.clientData));
+ console.log("ClientDataHash: ", hexEncode(aParams.challengeParam));
+ return assembleSignedData(aParams.appParam, presenceAndCounter, aParams.challengeParam);
+ })
+ .then(function(aSignedData) {
+ console.log(aPublicKey, aSignedData, signatureValue);
+ return verifySignature(aPublicKey, aSignedData, signatureValue);
+ })
+ }
+
+ function testMakeCredential() {
+ let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
+ let param = {type: "ScopedCred", algorithm: "p-256"};
+
+ authn.makeCredential(acct, [param], gCredentialChallenge)
+ .then(checkCredentialValid)
+ .then(testMakeDuplicate)
+ .catch(function(aReason) {
+ ok(false, aReason);
+ SimpleTest.finish();
+ });
+ }
+
+ function testMakeDuplicate(aCredInfo) {
+ let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
+ let param = {type: "ScopedCred", algorithm: "p-256"};
+ let options = {rpId: document.origin,
+ excludeList: [aCredInfo.credential]};
+
+ authn.makeCredential(acct, [param], gCredentialChallenge, options)
+ .then(function() {
+ // We should have errored here!
+ ok(false, "The excludeList didn't stop a duplicate being created!");
+ SimpleTest.finish();
+ })
+ .catch(function(aReason) {
+ ok(aReason.toString().startsWith("NotAllowedError"), "Expect NotAllowedError, got" + aReason);
+ testAssertion(aCredInfo);
+ });
+ }
+
+ function testAssertion(aCredInfo) {
+ let newCredential = {
+ type: aCredInfo.credential.type,
+ id: Uint8Array.from(aCredInfo.credential.id),
+ transports: [ "usb" ],
+ }
+
+ let assertOptions = {rpId: document.origin, timeoutSeconds: 5,
+ allowList: [ newCredential ]};
+ authn.getAssertion(gAssertionChallenge, assertOptions)
+ .then(function(aAssertion) {
+ /* Pass along the pubKey. */
+ return checkAssertionAndSigValid(aCredInfo.u2fReg.publicKey, aAssertion);
+ })
+ .then(function(aSigVerifyResult) {
+ ok(aSigVerifyResult, "Signing signature verified");
+ SimpleTest.finish();
+ })
+ .catch(function(reason) {
+ ok(false, "Signing signature invalid: " + reason);
+ SimpleTest.finish();
+ });
+ }
+});
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_webauthn_make_credential.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <title>Test for MakeCredential for W3C Web Authentication</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/common.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.js"></script>
+ <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+ <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Test for MakeCredential for W3C Web Authentication</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+// Execute the full-scope test
+SimpleTest.waitForExplicitFinish();
+
+function arrivingHereIsGood(aResult) {
+ ok(true, "Good result! Received a: " + aResult);
+ return Promise.resolve();
+}
+
+function arrivingHereIsBad(aResult) {
+ ok(false, "Bad result! Received a: " + aResult);
+ return Promise.resolve();
+}
+
+function expectNotAllowedError(aResult) {
+ ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
+ return Promise.resolve();
+}
+
+function expectTypeError(aResult) {
+ ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
+ return Promise.resolve();
+}
+
+function expectNotSupportedError(aResult) {
+ ok(aResult.toString().startsWith("NotSupportedError"), "Expecting a NotSupportedError");
+ return Promise.resolve();
+}
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.w3c", true],
+ ["security.webauth.u2f_enable_softtoken", true],
+ ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+ isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+ isnot(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint must exist");
+ isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
+
+ let authn = navigator.authentication;
+
+ let gCredentialChallenge = new Uint8Array(16);
+ window.crypto.getRandomValues(gCredentialChallenge);
+
+ let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
+ let param = {type: "ScopedCred", algorithm: "p-256"};
+ let unsupportedParam = {type: "ScopedCred", algorithm: "3DES"};
+ let badParam = {type: "SimplePassword", algorithm: "MaxLength=2"};
+
+ Promise.all([
+ // Test basic good call
+ authn.makeCredential(acct, [param], gCredentialChallenge)
+ .then(arrivingHereIsGood)
+ .catch(arrivingHereIsBad),
+
+ // Test empty account
+ authn.makeCredential({}, [param], gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test without a parameter
+ authn.makeCredential(acct, [], gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectNotSupportedError),
+
+ // Test without a parameter array at all
+ authn.makeCredential(acct, null, gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test with an unsupported parameter
+ authn.makeCredential(acct, [unsupportedParam], gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectNotSupportedError),
+
+ // Test with an unsupported parameter and a good one
+ authn.makeCredential(acct, [unsupportedParam, param], gCredentialChallenge)
+ .then(arrivingHereIsGood)
+ .catch(arrivingHereIsBad),
+
+ // Test with a bad parameter
+ authn.makeCredential(acct, [badParam], gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test with an unsupported parameter, and a bad one
+ authn.makeCredential(acct, [unsupportedParam, badParam],
+ gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test with an unsupported parameter, a bad one, and a good one. This
+ // should still fail, as anything with a badParam should fail.
+ authn.makeCredential(acct, [unsupportedParam, badParam, param],
+ gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test without a challenge
+ authn.makeCredential(acct, [param], null)
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test with an invalid challenge
+ authn.makeCredential(acct, [param], "begone, thou ill-fitting moist glove!")
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test with duplicate parameters
+ authn.makeCredential(acct, [param, param, param], gCredentialChallenge)
+ .then(arrivingHereIsGood)
+ .catch(arrivingHereIsBad),
+
+ // Test an incomplete account
+ authn.makeCredential({id: "none"}, [param], gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ authn.makeCredential({name: "none", imageURL: "http://example.com/404"},
+ [param], gCredentialChallenge)
+ .then(arrivingHereIsBad)
+ .catch(expectTypeError),
+
+ // Test a complete account
+ authn.makeCredential({rpDisplayName: "Foxxy", displayName: "Foxxy V",
+ id: "foxes_are_the_best@example.com",
+ name: "Fox F. Foxington",
+ imageURL: "https://example.com/fox.svg"},
+ [param], gCredentialChallenge)
+ .then(arrivingHereIsGood)
+ .catch(arrivingHereIsBad)
+ ])
+ .then(function() {
+ SimpleTest.finish();
+ });
+});
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_webauthn_no_token.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <title>Test for W3C Web Authentication with no token</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/common.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.js"></script>
+ <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+ <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Test for W3C Web Authentication with no token</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+// Execute the full-scope test
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.w3c", true],
+ ["security.webauth.u2f_enable_softtoken", false],
+ ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+ isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+ isnot(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint must exist");
+ isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
+
+ let authn = navigator.authentication;
+
+ let credentialChallenge = new Uint8Array(16);
+ window.crypto.getRandomValues(credentialChallenge);
+ let assertionChallenge = new Uint8Array(16);
+ window.crypto.getRandomValues(assertionChallenge);
+ let credentialId = new Uint8Array(128);
+ window.crypto.getRandomValues(credentialId);
+
+ testMakeCredential();
+
+ function testMakeCredential() {
+ let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
+ let param = {type: "ScopedCred", algorithm: "p-256"};
+ authn.makeCredential(acct, [param], credentialChallenge)
+ .then(function(aResult) {
+ ok(false, "Should have failed.");
+ testAssertion();
+ })
+ .catch(function(aReason) {
+ ok(aReason.toString().startsWith("NotAllowedError"), aReason);
+ testAssertion();
+ });
+ }
+
+ function testAssertion() {
+ let newCredential = {
+ type: "ScopedCred",
+ id: credentialId,
+ transports: [ "usb" ],
+ }
+ let assertOptions = {rpId: document.origin, timeoutSeconds: 5,
+ allowList: [ newCredential ]};
+ authn.getAssertion(assertionChallenge, assertOptions)
+ .then(function(aResult) {
+ ok(false, "Should have failed.");
+ SimpleTest.finish();
+ })
+ .catch(function(aReason) {
+ ok(aReason.toString().startsWith("NotAllowedError"), aReason);
+ SimpleTest.finish();
+ })
+ }
+});
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_webauthn_sameorigin.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <title>Test for MakeCredential for W3C Web Authentication</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/common.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.js"></script>
+ <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+ <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Test Same Origin Policy for W3C Web Authentication</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+// Execute the full-scope test
+SimpleTest.waitForExplicitFinish();
+
+var gTrackedCredential = {};
+
+function arrivingHereIsGood(aResult) {
+ ok(true, "Good result! Received a: " + aResult);
+ return Promise.resolve();
+}
+
+function arrivingHereIsBad(aResult) {
+ // TODO: Change to `ok` when Bug 1329764 lands
+ todo(false, "Bad result! Received a: " + aResult);
+ return Promise.resolve();
+}
+
+function expectSecurityError(aResult) {
+ // TODO: Change to `ok` when Bug 1329764 lands
+ todo(aResult.toString().startsWith("SecurityError"), "Expecting a SecurityError");
+ return Promise.resolve();
+}
+
+function keepThisScopedCredential(aScopedCredInfo) {
+ gTrackedCredential = {
+ type: aScopedCredInfo.credential.type,
+ id: Uint8Array.from(aScopedCredInfo.credential.id),
+ transports: [ "usb" ],
+ }
+ return Promise.resolve(aScopedCredInfo);
+}
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.w3c", true],
+ ["security.webauth.u2f_enable_softtoken", true],
+ ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+ isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+ isnot(navigator.authentication.makeCredential, undefined,
+ "WebAuthn makeCredential API endpoint must exist");
+ isnot(navigator.authentication.getAssertion, undefined,
+ "WebAuthn getAssertion API endpoint must exist");
+
+ let authn = navigator.authentication;
+
+ let chall = new Uint8Array(16);
+ window.crypto.getRandomValues(chall);
+
+ let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
+ let param = {type: "ScopedCred", algorithm: "p-256"};
+
+ Promise.all([
+ // Test basic good call
+ authn.makeCredential(acct, [param], chall, {rpId: document.origin})
+ .then(keepThisScopedCredential)
+ .then(arrivingHereIsGood)
+ .catch(arrivingHereIsBad),
+
+ // Test rpId being unset
+ authn.makeCredential(acct, [param], chall, {})
+ .then(arrivingHereIsGood)
+ .catch(arrivingHereIsBad),
+
+ // Test this origin with optional fields
+ authn.makeCredential(acct, [param], chall,
+ {rpId: "user:pass@" + document.origin + ":8888"})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError),
+
+ // Test blank rpId
+ authn.makeCredential(acct, [param], chall, {rpId: ""})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError),
+
+ // Test subdomain of this origin
+ authn.makeCredential(acct, [param], chall,
+ {rpId: "subdomain." + document.origin})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError),
+
+ // Test another origin
+ authn.makeCredential(acct, [param], chall, {rpId: "example.com"})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError),
+
+ // est a different domain within the same TLD
+ authn.makeCredential(acct, [param], chall, {rpId: "alt.test"})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError)
+
+ ])
+ .then(function(){
+ return Promise.all([
+ // Test basic good call
+ authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
+ rpId: document.origin})
+ .then(arrivingHereIsGood)
+ .catch(arrivingHereIsBad),
+
+ // Test rpId being unset
+ authn.getAssertion(chall, {allowList: [ gTrackedCredential ]})
+ .then(arrivingHereIsGood)
+ .catch(arrivingHereIsBad),
+
+ // Test this origin with optional fields
+ authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
+ rpId: "user:pass@" + document.origin + ":8888"})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError),
+
+ // Test blank rpId
+ authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
+ rpId: ""})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError),
+
+ // Test subdomain of this origin
+ authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
+ rpId: "subdomain." + document.origin})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError),
+
+ // Test another origin
+ authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
+ rpId: "example.com"})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError),
+
+ // Test a different domain within the same TLD
+ authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
+ rpId: "alt.test"})
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError)
+ ]);
+ })
+ .then(function(){
+ SimpleTest.finish();
+ });
+});
+
+</script>
+
+</body>
+</html>
--- a/dom/u2f/tests/u2futil.js
+++ b/dom/u2f/tests/u2futil.js
@@ -122,16 +122,37 @@ function hexEncode(buf) {
.map(x => ("0"+x.toString(16)).substr(-2))
.join("");
}
function hexDecode(str) {
return new Uint8Array(str.match(/../g).map(x => parseInt(x, 16)));
}
+function decodeU2FRegistration(aRegData) {
+ if (aRegData[0] != 0x05) {
+ return Promise.reject("Sentinal byte != 0x05");
+ }
+
+ let keyHandleLength = aRegData[66];
+ let u2fRegObj = {
+ publicKeyBytes: aRegData.slice(1, 66),
+ keyHandleBytes: aRegData.slice(67, 67 + keyHandleLength),
+ attestationBytes: aRegData.slice(67 + keyHandleLength)
+ }
+
+ u2fRegObj.keyHandle = bytesToBase64UrlSafe(u2fRegObj.keyHandleBytes);
+
+ return importPublicKey(u2fRegObj.publicKeyBytes)
+ .then(function(keyObj) {
+ u2fRegObj.publicKey = keyObj;
+ return u2fRegObj;
+ });
+}
+
function importPublicKey(keyBytes) {
if (keyBytes[0] != 0x04 || keyBytes.byteLength != 65) {
throw "Bad public key octet string";
}
var jwk = {
kty: "EC",
crv: "P-256",
x: bytesToBase64UrlSafe(keyBytes.slice(1, 33)),