new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/frame_appid_facet.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <script type="text/javascript" src="frame_utils.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+</head>
+<body>
+<p>AppID / Facet checks</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+async function doTests() {
+ var version = "U2F_V2";
+ var challenge = new Uint8Array(16);
+ window.crypto.getRandomValues(challenge);
+
+ // Ensure the SpecialPowers push worked properly
+ local_isnot(window.u2f, undefined, "U2F API endpoint must exist");
+
+ await promiseU2FRegister(null, [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 0, "Null AppID should work.");
+ });
+
+ await promiseU2FRegister("", [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 0, "Empty AppID should work.");
+ });
+
+ await promiseU2FRegister("http://example.com/appId", [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_isnot(res.errorCode, 0, "HTTP scheme is disallowed");
+ });
+
+ await promiseU2FRegister("https://example.com/appId", [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 0, "HTTPS origin for example.com should work");
+ });
+
+ await promiseU2FRegister("https://test2.example.com/appId", [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 2, "HTTPS origin for test2.example.com shouldn't work");
+ });
+
+ await promiseU2FRegister("https://sub.test2.example.com/appId", [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 2, "HTTPS origin for sub.test2.example.com shouldn't work");
+ });
+
+ await promiseU2FRegister(window.location.origin + "/otherAppId", [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 0, "Direct window origin should work");
+ });
+ local_finished();
+};
+
+doTests();
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/frame_multiple_keys.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <script type="text/javascript" src="frame_utils.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+</head>
+<body>
+<p>Test for multiple simultaneous key</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+function keyHandleFromRegResponse(aRegResponse) {
+ // Parse the response data from the U2F token
+ var registrationData = base64ToBytesUrlSafe(aRegResponse.registrationData);
+ local_is(registrationData[0], 0x05, "Reserved byte is correct")
+
+ var keyHandleLength = registrationData[66];
+ var keyHandleBytes = registrationData.slice(67, 67 + keyHandleLength);
+
+ return {
+ version: "U2F_V2",
+ keyHandle: bytesToBase64UrlSafe(keyHandleBytes),
+ };
+}
+
+var challenge = new Uint8Array(16);
+window.crypto.getRandomValues(challenge);
+
+var regRequest = {
+ version: "U2F_V2",
+ challenge: bytesToBase64UrlSafe(challenge),
+};
+
+var testState = {
+ key1: null,
+ key2: null,
+}
+
+// Just a key that came from a random profile; syntactically valid but not
+// unwrappable.
+let invalidKey = {
+ "version": "U2F_V2",
+ "keyHandle": "rQdreHgHrmKfsnGPAElEP9yfTx6eq2eU3_Y8n0RRsGKML0DY2d1_a8_-sOtxDr3"
+};
+
+async function doTests() {
+ // Ensure the SpecialPowers push worked properly
+ local_isnot(window.u2f, undefined, "U2F API endpoint must exist");
+
+ // Get two valid keys and present them
+ await promiseU2FRegister(window.location.origin, [regRequest], [], function(aRegResponse) {
+ testState.key1 = keyHandleFromRegResponse(aRegResponse);
+ });
+
+ // Get the second key...
+ // It's OK to repeat the regRequest; not material for this test
+ await promiseU2FRegister(window.location.origin, [regRequest], [], function(aRegResponse) {
+ testState.key2 = keyHandleFromRegResponse(aRegResponse);
+ });
+
+ await promiseU2FRegister(window.location.origin, [regRequest],
+ [invalidKey], function(aRegResponse) {
+ // The invalid key shouldn't match anything, so we should register OK here, too
+ local_is(aRegResponse.errorCode, 0, "The register should have gone through with the invalid key");
+ });
+
+
+ await promiseU2FRegister(window.location.origin, [regRequest],
+ [invalidKey, testState.key1], function(aRegResponse) {
+ // Expect a failure response since key1 is already registered
+ local_is(aRegResponse.errorCode, 4, "The register should have skipped since there was a valid key");
+ });
+
+ await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
+ [testState.key1], function(aSignResponse) {
+ local_is(aSignResponse.errorCode, 0, "The signing did not error with one key");
+ local_isnot(aSignResponse.clientData, undefined, "The signing provided clientData with one key");
+ });
+
+ // It's OK to sign with either one
+ await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
+ [testState.key1, testState.key2], function(aSignResponse) {
+ local_is(aSignResponse.errorCode, 0, "The signing did not error with two keys");
+ local_isnot(aSignResponse.clientData, undefined, "The signing provided clientData with two keys");
+ });
+
+ await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
+ [invalidKey, testState.key2], function(aSignResponse) {
+ local_is(aSignResponse.errorCode, 0, "The signing did not error when given an invalid key");
+ local_isnot(aSignResponse.clientData, undefined, "The signing provided clientData even when given an invalid key");
+ });
+
+ await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
+ [testState.key2, invalidKey], function(aSignResponse) {
+ local_is(aSignResponse.errorCode, 0, "The signing did not error when given an invalid key");
+ local_isnot(aSignResponse.clientData, undefined, "The signing provided clientData even when given an invalid key");
+ });
+
+ await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
+ [invalidKey], function(aSignResponse) {
+ local_is(aSignResponse.errorCode, 4, "The signing couldn't complete with this invalid key");
+ local_is(aSignResponse.clientData, undefined, "The signing shouldn't provide clientData when there's no valid key");
+ });
+
+ local_finished();
+};
+
+doTests();
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/frame_register.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <script type="text/javascript" src="frame_utils.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+</head>
+<body>
+<p>Register behavior</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+var version = "U2F_V2";
+var challenge = new Uint8Array(16);
+
+async function doTests() {
+ local_is(window.location.origin, "https://example.com", "Is loaded correctly");
+
+ // basic check
+ await promiseU2FRegister("https://example.com/appId", [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 0, "AppID should work from the domain");
+ });
+
+ await promiseU2FRegister("https://example.net/appId", [{
+ version: version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 2, "AppID should not work from other domains");
+ });
+
+ await promiseU2FRegister("", [], [], function(res){
+ local_is(res.errorCode, 2, "Empty register requests");
+ });
+
+ local_doesThrow(function(){
+ u2f.register("", null, [], null);
+ }, "Non-array register requests");
+
+ local_doesThrow(function(){
+ u2f.register("", [], null, null);
+ }, "Non-array sign requests");
+
+ local_doesThrow(function(){
+ u2f.register("", null, null, null);
+ }, "Non-array for both arguments");
+
+ await promiseU2FRegister("", [{}], [], function(res){
+ local_is(res.errorCode, 2, "Empty request");
+ });
+
+ await promiseU2FRegister("https://example.net/appId", [{
+ version: version,
+ }], [], function(res){
+ local_is(res.errorCode, 2, "Missing challenge");
+ });
+
+ await promiseU2FRegister("https://example.net/appId", [{
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 2, "Missing version");
+ });
+
+ await promiseU2FRegister("https://example.net/appId", [{
+ version: "a_version_00",
+ challenge: bytesToBase64UrlSafe(challenge),
+ }], [], function(res){
+ local_is(res.errorCode, 2, "Invalid version");
+ });
+
+ local_finished();
+};
+
+doTests();
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/frame_register_sign.html
@@ -0,0 +1,185 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+ <script type="text/javascript" src="frame_utils.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+
+ <script type="text/javascript" src="pkijs/asn1.js"></script>
+ <script type="text/javascript" src="pkijs/common.js"></script>
+ <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+ <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+</head>
+<body>
+<p>Register and sign behavior</p>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var version = "U2F_V2";
+var challenge = new Uint8Array(16);
+var state = {
+ // Raw messages
+ regRequest: null,
+ regResponse: null,
+
+ regKey: null,
+ signChallenge: null,
+ signResponse: null,
+
+ // Parsed values
+ publicKey: null,
+ keyHandle: null,
+
+ // Constants
+ version: "U2F_V2",
+ appId: window.location.origin,
+};
+
+async function doTests() {
+ local_is(window.location.origin, "https://example.com", "Is loaded correctly");
+
+ local_isnot(window.u2f, undefined, "U2F API endpoint must exist");
+ local_isnot(window.u2f.register, undefined, "U2F Register API endpoint must exist");
+ local_isnot(window.u2f.sign, undefined, "U2F Sign API endpoint must exist");
+
+ var challenge = new Uint8Array(16);
+ window.crypto.getRandomValues(challenge);
+
+ state.regRequest = {
+ version: state.version,
+ challenge: bytesToBase64UrlSafe(challenge),
+ };
+
+ await SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+ ["security.webauth.webauthn_enable_softtoken", true],
+ ["security.webauth.webauthn_enable_usbtoken", false]]});
+
+ // Ensure the SpecialPowers push worked properly
+ local_isnot(window.u2f, undefined, "U2F API endpoint must exist");
+
+ await promiseU2FRegister(state.appId, [state.regRequest], [], function(regResponse) {
+ state.regResponse = regResponse;
+ });
+
+ local_is(state.regResponse.errorCode, 0, "The registration did not error");
+ local_isnot(state.regResponse.registrationData, undefined, "The registration did not provide registration data");
+ if (state.regResponse.errorCode > 0) {
+ return;
+ }
+
+ // Parse the response data from the U2F token
+ var registrationData = base64ToBytesUrlSafe(state.regResponse.registrationData);
+ local_is(registrationData[0], 0x05, "Reserved byte is correct")
+
+ state.publicKeyBytes = registrationData.slice(1, 66);
+ var keyHandleLength = registrationData[66];
+ state.keyHandleBytes = registrationData.slice(67, 67 + keyHandleLength);
+ state.keyHandle = bytesToBase64UrlSafe(state.keyHandleBytes);
+ state.attestation = registrationData.slice(67 + keyHandleLength);
+
+ local_is(state.attestation[0], 0x30, "Attestation Certificate has correct starting byte");
+ var asn1 = org.pkijs.fromBER(state.attestation.buffer);
+ console.log(asn1);
+ state.attestationCert = new org.pkijs.simpl.CERT({ schema: asn1.result });
+ console.log(state.attestationCert);
+ state.attestationSig = state.attestation.slice(asn1.offset);
+ local_is(state.attestationCert.subject.types_and_values[0].value.value_block.value, "Firefox U2F Soft Token", "Expected Subject");
+ local_is(state.attestationCert.issuer.types_and_values[0].value.value_block.value, "Firefox U2F Soft Token", "Expected Issuer");
+ local_is(state.attestationCert.notAfter.value - state.attestationCert.notBefore.value, 1000*60*60*48, "Valid 48 hours (in millis)");
+
+ // Verify that the clientData from the U2F token makes sense
+ var clientDataJSON = "";
+ base64ToBytesUrlSafe(state.regResponse.clientData).map(x => clientDataJSON += String.fromCharCode(x));
+ var clientData = JSON.parse(clientDataJSON);
+ local_is(clientData.typ, "navigator.id.finishEnrollment", "Register - Data type matches");
+ local_is(clientData.challenge, state.regRequest.challenge, "Register - Challenge matches");
+ local_is(clientData.origin, window.location.origin, "Register - Origins are the same");
+
+ // Verify the signature from the attestation certificate
+ await deriveAppAndChallengeParam(state.appId, string2buffer(clientDataJSON))
+ .then(function(params){
+ state.appParam = params.appParam;
+ state.challengeParam = params.challengeParam;
+ return state.attestationCert.getPublicKey();
+ }).then(function(attestationPublicKey) {
+ var signedData = assembleRegistrationSignedData(state.appParam, state.challengeParam, state.keyHandleBytes, state.publicKeyBytes);
+ return verifySignature(attestationPublicKey, signedData, state.attestationSig);
+ }).then(function(verified) {
+ local_ok(verified, "Attestation Certificate signature verified")
+ // Import the public key of the U2F token into WebCrypto
+ return importPublicKey(state.publicKeyBytes)
+ }).then(function(key) {
+ state.publicKey = key;
+ local_ok(true, "Imported public key")
+
+ // Ensure the attestation certificate is properly self-signed
+ return state.attestationCert.verify()
+ }).then(function(verified) {
+ local_ok(verified, "Register attestation signature verified")
+ });
+
+ state.regKey = {
+ version: state.version,
+ keyHandle: state.keyHandle,
+ };
+
+ // Test that we don't re-register if we provide regKey as an
+ // "already known" key handle. The U2F module should recognize regKey
+ // as being usable and, thus, give back errorCode 4.
+ await promiseU2FRegister(state.appId, [state.regRequest], [state.regKey], function(regResponse) {
+ // Since we attempted to register with state.regKey as a known key, expect
+ // ineligible (=4).
+ local_is(regResponse.errorCode, 4, "The re-registration should show device ineligible");
+ local_is(regResponse.registrationData, undefined, "The re-registration did not provide registration data");
+ });
+
+ window.crypto.getRandomValues(challenge);
+ state.signChallenge = bytesToBase64UrlSafe(challenge);
+
+ // Now try to sign the signature challenge
+ await promiseU2FSign(state.appId, state.signChallenge, [state.regKey], function(signResponse) {
+ state.signResponse = signResponse;
+ });
+
+ // Make sure this signature op worked, bailing early if it failed.
+ local_is(state.signResponse.errorCode, 0, "The signing did not error");
+ local_isnot(state.signResponse.clientData, undefined, "The signing did provide client data");
+
+ if (state.signResponse.errorCode > 0) {
+ return;
+ }
+
+ // Decode the clientData that was returned from the module
+ var clientDataJSON = "";
+ base64ToBytesUrlSafe(state.signResponse.clientData).map(x => clientDataJSON += String.fromCharCode(x));
+ var clientData = JSON.parse(clientDataJSON);
+ local_is(clientData.typ, "navigator.id.getAssertion", "Sign - Data type matches");
+ local_is(clientData.challenge, state.signChallenge, "Sign - Challenge matches");
+ local_is(clientData.origin, window.location.origin, "Sign - Origins are the same");
+
+ // Parse the signature data
+ var signatureData = base64ToBytesUrlSafe(state.signResponse.signatureData);
+ if (signatureData[0] != 0x01) {
+ throw "User presence byte not set";
+ }
+ var presenceAndCounter = signatureData.slice(0,5);
+ var signatureValue = signatureData.slice(5);
+
+ // Assemble the signed data and verify the signature
+ await deriveAppAndChallengeParam(state.appId, string2buffer(clientDataJSON))
+ .then(function(params){
+ return assembleSignedData(params.appParam, presenceAndCounter, params.challengeParam);
+ })
+ .then(function(signedData) {
+ return verifySignature(state.publicKey, signedData, signatureValue);
+ })
+ .then(function(verified) {
+ local_ok(verified, "Signing signature verified")
+ });
+
+ local_finished();
+};
+
+doTests();
+</script>
+</body>
+</html>
--- a/dom/u2f/tests/mochitest.ini
+++ b/dom/u2f/tests/mochitest.ini
@@ -1,25 +1,24 @@
[DEFAULT]
support-files =
+ frame_appid_facet.html
frame_appid_facet_insecure.html
frame_appid_facet_subdomain.html
+ frame_multiple_keys.html
frame_no_token.html
+ frame_register.html
+ frame_register_sign.html
frame_utils.js
pkijs/asn1.js
pkijs/common.js
pkijs/x509_schema.js
pkijs/x509_simpl.js
u2futil.js
-prefs =
- security.webauth.u2f=true
- security.webauth.webauthn_enable_softtoken=true
- security.webauth.webauthn_enable_usbtoken=false
-
scheme = https
# Feature does not function without e10s (Disabled in Bug 1297552)
skip-if = !e10s
[test_util_methods.html]
[test_no_token.html]
[test_register.html]
--- a/dom/u2f/tests/test_appid_facet.html
+++ b/dom/u2f/tests/test_appid_facet.html
@@ -1,68 +1,44 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>FIDO U2F: AppID / FacetID behavior</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="frame_utils.js"></script>
<script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.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>FIDO U2F: AppID / FacetID behavior</h1>
<ul>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681 (initial implementation)</a></li>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1245527">Mozilla Bug 1245527 (hardware rewrite)</a></li>
</ul>
+<div id="framediv">
+ <iframe id="testing_frame"></iframe>
+</div>
+
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
"use strict";
-var version = "U2F_V2";
-var challenge = new Uint8Array(16);
-
-add_task(async function(){
- await promiseU2FRegister(null, [{
- version: version,
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- is(res.errorCode, 0, "Null AppID should work.");
- });
-
- await promiseU2FRegister("", [{
- version: version,
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- is(res.errorCode, 0, "Empty AppID should work.");
- });
+SimpleTest.waitForExplicitFinish();
- await promiseU2FRegister("http://example.com/appId", [{
- version: version,
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- isnot(res.errorCode, 0, "HTTP scheme is disallowed");
+// listen for messages from the test harness
+window.addEventListener("message", handleEventMessage);
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+ ["security.webauth.webauthn_enable_softtoken", true],
+ ["security.webauth.webauthn_enable_usbtoken", false]]},
+ function(){
+ document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_appid_facet.html";
});
- await promiseU2FRegister("https://example.com/appId", [{
- version: version,
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- is(res.errorCode, 0, "HTTPS origin for example.com should work");
- });
-
- await promiseU2FRegister(window.location.origin + "/otherAppId", [{
- version: version,
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- is(res.errorCode, 0, "Direct window origin should work");
- });
-})
-
</script>
</body>
</html>
--- a/dom/u2f/tests/test_appid_facet_insecure.html
+++ b/dom/u2f/tests/test_appid_facet_insecure.html
@@ -1,18 +1,18 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>FIDO U2F: Insecure AppID / FacetID behavior</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="frame_utils.js"></script>
<script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.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>FIDO U2F: Insecure AppID / FacetID behavior</h1>
<ul>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681 (initial implementation)</a></li>
@@ -21,17 +21,24 @@
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
+"use strict";
SimpleTest.waitForExplicitFinish();
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage);
-document.getElementById('testing_frame').src = "http://test2.example.com/tests/dom/u2f/tests/frame_appid_facet_insecure.html";
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+ ["security.webauth.webauthn_enable_softtoken", true],
+ ["security.webauth.webauthn_enable_usbtoken", false]]},
+ function(){
+ document.getElementById('testing_frame').src = "http://test2.example.com/tests/dom/u2f/tests/frame_appid_facet_insecure.html";
+ });
+
</script>
</body>
</html>
--- a/dom/u2f/tests/test_appid_facet_subdomain.html
+++ b/dom/u2f/tests/test_appid_facet_subdomain.html
@@ -1,18 +1,18 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>FIDO U2F: Subdomain AppID / FacetID behavior</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="frame_utils.js"></script>
<script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.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>FIDO U2F: Subdomain AppID / FacetID behavior</h1>
<ul>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681 (initial implementation)</a></li>
@@ -21,19 +21,25 @@
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
+"use strict";
SimpleTest.waitForExplicitFinish();
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage);
-document.getElementById('testing_frame').src = "https://test1.example.com/tests/dom/u2f/tests/frame_appid_facet_subdomain.html";
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+ ["security.webauth.webauthn_enable_softtoken", true],
+ ["security.webauth.webauthn_enable_usbtoken", false]]},
+ function(){
+ document.getElementById('testing_frame').src = "https://test1.example.com/tests/dom/u2f/tests/frame_appid_facet_subdomain.html";
+ });
</script>
</body>
</html>
--- a/dom/u2f/tests/test_multiple_keys.html
+++ b/dom/u2f/tests/test_multiple_keys.html
@@ -1,124 +1,46 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>FIDO U2F: Multiple Keys</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="frame_utils.js"></script>
<script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.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>FIDO U2F: Multiple Keys</h1>
<ul>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681 (initial implementation)</a></li>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1245527">Mozilla Bug 1245527 (hardware rewrite)</a></li>
</ul>
+<div id="framediv">
+ <iframe id="testing_frame"></iframe>
+</div>
+
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
"use strict";
-function keyHandleFromRegResponse(aRegResponse) {
- // Parse the response data from the U2F token
- var registrationData = base64ToBytesUrlSafe(aRegResponse.registrationData);
- is(registrationData[0], 0x05, "Reserved byte is correct")
-
- var keyHandleLength = registrationData[66];
- var keyHandleBytes = registrationData.slice(67, 67 + keyHandleLength);
-
- return {
- version: "U2F_V2",
- keyHandle: bytesToBase64UrlSafe(keyHandleBytes),
- };
-}
-
-// Ensure the SpecialPowers push worked properly
-add_task(async function(){
- isnot(window.u2f, undefined, "U2F API endpoint must exist");
-});
-
-var challenge = new Uint8Array(16);
-window.crypto.getRandomValues(challenge);
-
-var regRequest = {
- version: "U2F_V2",
- challenge: bytesToBase64UrlSafe(challenge),
-};
+SimpleTest.waitForExplicitFinish();
-var testState = {
- key1: null,
- key2: null,
-}
-
-// Just a key that came from a random profile; syntactically valid but not
-// unwrappable.
-let invalidKey = {
- "version": "U2F_V2",
- "keyHandle": "rQdreHgHrmKfsnGPAElEP9yfTx6eq2eU3_Y8n0RRsGKML0DY2d1_a8_-sOtxDr3"
-};
-
-add_task(async function(){
- // Get two valid keys and present them
- await promiseU2FRegister(window.location.origin, [regRequest], [], function(aRegResponse) {
- testState.key1 = keyHandleFromRegResponse(aRegResponse);
- });
-
- // Get the second key...
- // It's OK to repeat the regRequest; not material for this test
- await promiseU2FRegister(window.location.origin, [regRequest], [], function(aRegResponse) {
- testState.key2 = keyHandleFromRegResponse(aRegResponse);
- });
-
- await promiseU2FRegister(window.location.origin, [regRequest],
- [invalidKey], function(aRegResponse) {
- // The invalid key shouldn't match anything, so we should register OK here, too
- is(aRegResponse.errorCode, 0, "The register should have gone through with the invalid key");
+// listen for messages from the test harness
+window.addEventListener("message", handleEventMessage);
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+ ["security.webauth.webauthn_enable_softtoken", true],
+ ["security.webauth.webauthn_enable_usbtoken", false]]},
+ function(){
+ document.getElementById('testing_frame').src = "https://test2.example.com/tests/dom/u2f/tests/frame_multiple_keys.html";
});
- await promiseU2FRegister(window.location.origin, [regRequest],
- [invalidKey, testState.key1], function(aRegResponse) {
- // Expect a failure response since key1 is already registered
- is(aRegResponse.errorCode, 4, "The register should have skipped since there was a valid key");
- });
-
- await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
- [testState.key1], function(aSignResponse) {
- is(aSignResponse.errorCode, 0, "The signing did not error with one key");
- isnot(aSignResponse.clientData, undefined, "The signing provided clientData with one key");
- });
-
- // It's OK to sign with either one
- await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
- [testState.key1, testState.key2], function(aSignResponse) {
- is(aSignResponse.errorCode, 0, "The signing did not error with two keys");
- isnot(aSignResponse.clientData, undefined, "The signing provided clientData with two keys");
- });
-
- await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
- [invalidKey, testState.key2], function(aSignResponse) {
- is(aSignResponse.errorCode, 0, "The signing did not error when given an invalid key");
- isnot(aSignResponse.clientData, undefined, "The signing provided clientData even when given an invalid key");
- });
-
- await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
- [testState.key2, invalidKey], function(aSignResponse) {
- is(aSignResponse.errorCode, 0, "The signing did not error when given an invalid key");
- isnot(aSignResponse.clientData, undefined, "The signing provided clientData even when given an invalid key");
- });
-
- await promiseU2FSign(window.location.origin, bytesToBase64UrlSafe(challenge),
- [invalidKey], function(aSignResponse) {
- is(aSignResponse.errorCode, 4, "The signing couldn't complete with this invalid key");
- is(aSignResponse.clientData, undefined, "The signing shouldn't provide clientData when there's no valid key");
- });
-});
</script>
</body>
</html>
--- a/dom/u2f/tests/test_no_token.html
+++ b/dom/u2f/tests/test_no_token.html
@@ -1,43 +1,43 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>FIDO U2F: No Token</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="frame_utils.js"></script>
+ <script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.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>
-
- <script type="text/javascript" src="frame_utils.js"></script>
- <script type="text/javascript" src="u2futil.js"></script>
-
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<h1>FIDO U2F: No Token</h1>
<ul>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681 (initial implementation)</a></li>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1245527">Mozilla Bug 1245527 (hardware rewrite)</a></li>
</ul>
-<pre id="log"></pre>
-
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
+<pre id="log"></pre>
+
<script class="testbody" type="text/javascript">
"use strict";
SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn_enable_softtoken", false]]},
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+ ["security.webauth.webauthn_enable_softtoken", false],
+ ["security.webauth.webauthn_enable_usbtoken", false]]},
function() {
// listen for messages from the test harness
window.addEventListener("message", handleEventMessage);
document.getElementById('testing_frame').src = "https://test1.example.com/tests/dom/u2f/tests/frame_no_token.html";
});
</script>
--- a/dom/u2f/tests/test_register.html
+++ b/dom/u2f/tests/test_register.html
@@ -1,92 +1,44 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>FIDO U2F: Register behavior</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="frame_utils.js"></script>
<script type="text/javascript" src="u2futil.js"></script>
+ <script type="text/javascript" src="pkijs/asn1.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>FIDO U2F: Register behavior</h1>
<ul>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681 (initial implementation)</a></li>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1245527">Mozilla Bug 1245527 (hardware rewrite)</a></li>
</ul>
+<div id="framediv">
+ <iframe id="testing_frame"></iframe>
+</div>
+
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
"use strict";
-var version = "U2F_V2";
-var challenge = new Uint8Array(16);
-
-add_task(async function(){
- is(window.location.origin, "https://example.com", "Is loaded correctly");
-});
-
-add_task(async function(){
- // basic check
- await promiseU2FRegister("https://example.com/appId", [{
- version: version,
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- is(res.errorCode, 0, "AppID should work from the domain");
- });
-
- await promiseU2FRegister("https://example.net/appId", [{
- version: version,
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- is(res.errorCode, 2, "AppID should not work from other domains");
- });
-
- await promiseU2FRegister("", [], [], function(res){
- is(res.errorCode, 2, "Empty register requests");
- });
-
- SimpleTest.doesThrow(function(){
- u2f.register("", null, [], null);
- }, "Non-array register requests");
+SimpleTest.waitForExplicitFinish();
- SimpleTest.doesThrow(function(){
- u2f.register("", [], null, null);
- }, "Non-array sign requests");
-
- SimpleTest.doesThrow(function(){
- u2f.register("", null, null, null);
- }, "Non-array for both arguments");
-
- await promiseU2FRegister("", [{}], [], function(res){
- is(res.errorCode, 2, "Empty request");
- });
-
- await promiseU2FRegister("https://example.net/appId", [{
- version: version,
- }], [], function(res){
- is(res.errorCode, 2, "Missing challenge");
- });
-
- await promiseU2FRegister("https://example.net/appId", [{
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- is(res.errorCode, 2, "Missing version");
- });
-
- await promiseU2FRegister("https://example.net/appId", [{
- version: "a_version_00",
- challenge: bytesToBase64UrlSafe(challenge),
- }], [], function(res){
- is(res.errorCode, 2, "Invalid version");
- });
-
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+ ["security.webauth.webauthn_enable_softtoken", true],
+ ["security.webauth.webauthn_enable_usbtoken", false]]},
+function() {
+ // listen for messages from the test harness
+ window.addEventListener("message", handleEventMessage);
+ document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_register.html";
});
</script>
</body>
</html>
--- a/dom/u2f/tests/test_register_sign.html
+++ b/dom/u2f/tests/test_register_sign.html
@@ -1,188 +1,40 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>FIDO U2F: Sign and Register behavior</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="frame_utils.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>FIDO U2F: Sign and Register behavior</h1>
<ul>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681 (initial implementation)</a></li>
<li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1245527">Mozilla Bug 1245527 (hardware rewrite)</a></li>
</ul>
+<div id="framediv">
+ <iframe id="testing_frame"></iframe>
+</div>
+
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
"use strict";
-var version = "U2F_V2";
-var challenge = new Uint8Array(16);
-
-add_task(async function(){
- is(window.location.origin, "https://example.com", "Is loaded correctly");
-});
-
-var state = {
- // Raw messages
- regRequest: null,
- regResponse: null,
-
- regKey: null,
- signChallenge: null,
- signResponse: null,
-
- // Parsed values
- publicKey: null,
- keyHandle: null,
-
- // Constants
- version: "U2F_V2",
- appId: window.location.origin,
-};
-
-add_task(async function(){
- isnot(window.u2f, undefined, "U2F API endpoint must exist");
- isnot(window.u2f.register, undefined, "U2F Register API endpoint must exist");
- isnot(window.u2f.sign, undefined, "U2F Sign API endpoint must exist");
-
- var challenge = new Uint8Array(16);
- window.crypto.getRandomValues(challenge);
-
- state.regRequest = {
- version: state.version,
- challenge: bytesToBase64UrlSafe(challenge),
- };
-
- await promiseU2FRegister(state.appId, [state.regRequest], [], function(regResponse) {
- state.regResponse = regResponse;
- });
-
- is(state.regResponse.errorCode, 0, "The registration did not error");
- isnot(state.regResponse.registrationData, undefined, "The registration did not provide registration data");
- if (state.regResponse.errorCode > 0) {
- return;
- }
-
- // Parse the response data from the U2F token
- var registrationData = base64ToBytesUrlSafe(state.regResponse.registrationData);
- is(registrationData[0], 0x05, "Reserved byte is correct")
-
- state.publicKeyBytes = registrationData.slice(1, 66);
- var keyHandleLength = registrationData[66];
- state.keyHandleBytes = registrationData.slice(67, 67 + keyHandleLength);
- state.keyHandle = bytesToBase64UrlSafe(state.keyHandleBytes);
- state.attestation = registrationData.slice(67 + keyHandleLength);
-
- is(state.attestation[0], 0x30, "Attestation Certificate has correct starting byte");
- var asn1 = org.pkijs.fromBER(state.attestation.buffer);
- console.log(asn1);
- state.attestationCert = new org.pkijs.simpl.CERT({ schema: asn1.result });
- console.log(state.attestationCert);
- state.attestationSig = state.attestation.slice(asn1.offset);
- is(state.attestationCert.subject.types_and_values[0].value.value_block.value, "Firefox U2F Soft Token", "Expected Subject");
- is(state.attestationCert.issuer.types_and_values[0].value.value_block.value, "Firefox U2F Soft Token", "Expected Issuer");
- is(state.attestationCert.notAfter.value - state.attestationCert.notBefore.value, 1000*60*60*48, "Valid 48 hours (in millis)");
-
- // Verify that the clientData from the U2F token makes sense
- var clientDataJSON = "";
- base64ToBytesUrlSafe(state.regResponse.clientData).map(x => clientDataJSON += String.fromCharCode(x));
- var clientData = JSON.parse(clientDataJSON);
- is(clientData.typ, "navigator.id.finishEnrollment", "Register - Data type matches");
- is(clientData.challenge, state.regRequest.challenge, "Register - Challenge matches");
- is(clientData.origin, window.location.origin, "Register - Origins are the same");
+SimpleTest.waitForExplicitFinish();
- // Verify the signature from the attestation certificate
- await deriveAppAndChallengeParam(state.appId, string2buffer(clientDataJSON))
- .then(function(params){
- state.appParam = params.appParam;
- state.challengeParam = params.challengeParam;
- return state.attestationCert.getPublicKey();
- }).then(function(attestationPublicKey) {
- var signedData = assembleRegistrationSignedData(state.appParam, state.challengeParam, state.keyHandleBytes, state.publicKeyBytes);
- return verifySignature(attestationPublicKey, signedData, state.attestationSig);
- }).then(function(verified) {
- ok(verified, "Attestation Certificate signature verified")
- // Import the public key of the U2F token into WebCrypto
- return importPublicKey(state.publicKeyBytes)
- }).then(function(key) {
- state.publicKey = key;
- ok(true, "Imported public key")
-
- // Ensure the attestation certificate is properly self-signed
- return state.attestationCert.verify()
- }).then(function(verified) {
- ok(verified, "Register attestation signature verified")
- });
-
- state.regKey = {
- version: state.version,
- keyHandle: state.keyHandle,
- };
-
- // Test that we don't re-register if we provide regKey as an
- // "already known" key handle. The U2F module should recognize regKey
- // as being usable and, thus, give back errorCode 4.
- await promiseU2FRegister(state.appId, [state.regRequest], [state.regKey], function(regResponse) {
- // Since we attempted to register with state.regKey as a known key, expect
- // ineligible (=4).
- is(regResponse.errorCode, 4, "The re-registration should show device ineligible");
- is(regResponse.registrationData, undefined, "The re-registration did not provide registration data");
- });
-
-
- window.crypto.getRandomValues(challenge);
- state.signChallenge = bytesToBase64UrlSafe(challenge);
-
- // Now try to sign the signature challenge
- await promiseU2FSign(state.appId, state.signChallenge, [state.regKey], function(signResponse) {
- state.signResponse = signResponse;
- });
-
- // Make sure this signature op worked, bailing early if it failed.
- is(state.signResponse.errorCode, 0, "The signing did not error");
- isnot(state.signResponse.clientData, undefined, "The signing did provide client data");
-
- if (state.signResponse.errorCode > 0) {
- return;
- }
-
- // Decode the clientData that was returned from the module
- var clientDataJSON = "";
- base64ToBytesUrlSafe(state.signResponse.clientData).map(x => clientDataJSON += String.fromCharCode(x));
- var clientData = JSON.parse(clientDataJSON);
- is(clientData.typ, "navigator.id.getAssertion", "Sign - Data type matches");
- is(clientData.challenge, state.signChallenge, "Sign - Challenge matches");
- is(clientData.origin, window.location.origin, "Sign - Origins are the same");
-
- // Parse the signature data
- var signatureData = base64ToBytesUrlSafe(state.signResponse.signatureData);
- if (signatureData[0] != 0x01) {
- throw "User presence byte not set";
- }
- var presenceAndCounter = signatureData.slice(0,5);
- var signatureValue = signatureData.slice(5);
-
- // Assemble the signed data and verify the signature
- await deriveAppAndChallengeParam(state.appId, string2buffer(clientDataJSON))
- .then(function(params){
- return assembleSignedData(params.appParam, presenceAndCounter, params.challengeParam);
- })
- .then(function(signedData) {
- return verifySignature(state.publicKey, signedData, signatureValue);
- })
- .then(function(verified) {
- ok(verified, "Signing signature verified")
- });
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+ ["security.webauth.webauthn_enable_softtoken", true],
+ ["security.webauth.webauthn_enable_usbtoken", false]]},
+function() {
+ // listen for messages from the test harness
+ window.addEventListener("message", handleEventMessage);
+ document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_register_sign.html";
});
</script>
</body>
</html>