зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1329802 - WebAuthn Unit Tests: Add Unit Tests r=keeler
This uses the new mochitest "scheme" option from Bug 1286312. This cannot land until after Bug 1286312 does. For now, you can test locally by adding --setpref dom.securecontext.whitelist=mochi.test to your command line, such as: ~/hg/mozilla-central/mach mochitest \ --setpref dom.securecontext.whitelist=mochi.test ./dom/u2f/tests/ Updated: Review fixes (thanks keeler!) MozReview-Commit-ID: 7jTxF3Mrtcg --HG-- extra : rebase_source : 72c24bdc028e440705598c694f3c4119d5304d83
This commit is contained in:
Родитель
2e850d0248
Коммит
0851d956ab
|
@ -27,3 +27,18 @@ skip-if = !e10s
|
|||
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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -127,6 +127,27 @@ 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";
|
||||
|
|
Загрузка…
Ссылка в новой задаче