mirror of
https://github.com/servo/servo.git
synced 2025-08-10 16:05:43 +01:00
Update web-platform-tests to revision 6d85a3b422cab97d032ad3db47cb741ca364185f
This commit is contained in:
parent
cd663ea332
commit
b524b7c279
37 changed files with 1446 additions and 178 deletions
|
@ -1,5 +1,4 @@
|
|||
|
||||
/* Useful constants for working with COSE key objects */
|
||||
// Useful constants for working with COSE key objects
|
||||
const cose_kty = 1;
|
||||
const cose_kty_ec2 = 2;
|
||||
const cose_alg = 3;
|
||||
|
@ -10,6 +9,117 @@ const cose_crv_P256 = 1;
|
|||
const cose_crv_x = -2;
|
||||
const cose_crv_y = -3;
|
||||
|
||||
/**
|
||||
* These are the default arguments that will be passed to navigator.credentials.create()
|
||||
* unless modified by a specific test case
|
||||
*/
|
||||
var createCredentialDefaultArgs = {
|
||||
options: {
|
||||
publicKey: {
|
||||
// Relying Party:
|
||||
rp: {
|
||||
name: "Acme",
|
||||
icon: "https://www.w3.org/StyleSheets/TR/2016/logos/W3C"
|
||||
},
|
||||
|
||||
// User:
|
||||
user: {
|
||||
id: new Uint8Array(16), // Won't survive the copy, must be rebuilt
|
||||
name: "john.p.smith@example.com",
|
||||
displayName: "John P. Smith",
|
||||
icon: "https://pics.acme.com/00/p/aBjjjpqPb.png"
|
||||
},
|
||||
|
||||
pubKeyCredParams: [{
|
||||
type: "public-key",
|
||||
alg: cose_alg_ECDSA_w_SHA256,
|
||||
}],
|
||||
|
||||
timeout: 60000, // 1 minute
|
||||
excludeCredentials: [] // No excludeList
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* These are the default arguments that will be passed to navigator.credentials.get()
|
||||
* unless modified by a specific test case
|
||||
*/
|
||||
var getCredentialDefaultArgs = {
|
||||
options: {
|
||||
publicKey: {
|
||||
timeout: 60000
|
||||
// allowCredentials: [newCredential]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function createCredential(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
// set the default options
|
||||
var createArgs = cloneObject(createCredentialDefaultArgs);
|
||||
let challengeBytes = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(challengeBytes);
|
||||
createArgs.options.publicKey.challenge = challengeBytes;
|
||||
createArgs.options.publicKey.user.id = new Uint8Array(16);
|
||||
|
||||
// change the defaults with any options that were passed in
|
||||
extendObject (createArgs, opts);
|
||||
|
||||
// create the credential, return the Promise
|
||||
return navigator.credentials.create(createArgs.options);
|
||||
}
|
||||
|
||||
function createRandomString(len) {
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for(var i = 0; i < len; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
function ab2str(buf) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buf));
|
||||
}
|
||||
|
||||
// Useful constants for working with attestation data
|
||||
const authenticator_data_user_present = 0x01;
|
||||
const authenticator_data_user_verified = 0x04;
|
||||
const authenticator_data_attested_cred_data = 0x40;
|
||||
const authenticator_data_extension_data = 0x80;
|
||||
|
||||
function parseAuthenticatorData(buf) {
|
||||
if (buf.byteLength < 37) {
|
||||
throw new TypeError ("parseAuthenticatorData: buffer must be at least 37 bytes");
|
||||
}
|
||||
|
||||
printHex ("authnrData", buf);
|
||||
|
||||
var authnrData = new DataView(buf);
|
||||
var authnrDataObj = {};
|
||||
authnrDataObj.length = buf.byteLength;
|
||||
|
||||
authnrDataObj.rpIdHash = new Uint8Array (buf.slice (0,32));
|
||||
authnrDataObj.rawFlags = authnrData.getUint8(32);
|
||||
authnrDataObj.counter = authnrData.getUint32(33, false);
|
||||
authnrDataObj.rawCounter = [];
|
||||
authnrDataObj.rawCounter[0] = authnrData.getUint8(33);
|
||||
authnrDataObj.rawCounter[1] = authnrData.getUint8(34);
|
||||
authnrDataObj.rawCounter[2] = authnrData.getUint8(35);
|
||||
authnrDataObj.rawCounter[3] = authnrData.getUint8(36);
|
||||
authnrDataObj.flags = {};
|
||||
|
||||
authnrDataObj.flags.userPresent = (authnrDataObj.rawFlags&authenticator_data_user_present)?true:false;
|
||||
authnrDataObj.flags.userVerified = (authnrDataObj.rawFlags&authenticator_data_user_verified)?true:false;
|
||||
authnrDataObj.flags.attestedCredentialData = (authnrDataObj.rawFlags&authenticator_data_attested_cred_data)?true:false;
|
||||
authnrDataObj.flags.extensionData = (authnrDataObj.rawFlags&authenticator_data_extension_data)?true:false;
|
||||
|
||||
return authnrDataObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* TestCase
|
||||
*
|
||||
|
@ -118,66 +228,127 @@ class TestCase {
|
|||
|
||||
/**
|
||||
* run the test function with the top-level properties of the test object applied as arguments
|
||||
* expects the test to pass, and then validates the results
|
||||
*/
|
||||
test(desc) {
|
||||
promise_test(() => {
|
||||
return this.doIt()
|
||||
.then((ret) => {
|
||||
// check the result
|
||||
this.validateRet(ret);
|
||||
return ret;
|
||||
});
|
||||
}, desc);
|
||||
testPasses(desc) {
|
||||
return this.doIt()
|
||||
.then((ret) => {
|
||||
// check the result
|
||||
this.validateRet(ret);
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* run the test function with the top-level properties of the test object applied as arguments
|
||||
* expects the test to fail
|
||||
*/
|
||||
testFails(t, testDesc, expectedErr) {
|
||||
return promise_rejects(t, expectedErr, this.doIt(), "Expected bad parameters to fail");
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test that's implemented by the class by calling the doIt() function
|
||||
* @param {String} desc A description of the test being run
|
||||
* @param [Error|String] expectedErr A string matching an error type, such as "SecurityError" or an object with a .name value that is an error type string
|
||||
*/
|
||||
runTest(desc, expectedErr) {
|
||||
promise_test((t) => {
|
||||
return Promise.resolve().then(() => {
|
||||
return this.testSetup();
|
||||
}).then(() => {
|
||||
if (expectedErr === undefined) {
|
||||
return this.testPasses(desc);
|
||||
} else {
|
||||
return this.testFails(t, desc, expectedErr);
|
||||
}
|
||||
}).then((res) => {
|
||||
return this.testTeardown(res);
|
||||
})
|
||||
}, desc)
|
||||
}
|
||||
|
||||
/**
|
||||
* called before runTest
|
||||
* virtual method expected to be overridden by child class if needed
|
||||
*/
|
||||
testSetup() {
|
||||
if (this.beforeTestFn) {
|
||||
this.beforeTestFn.call(this);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a callback function that gets called in the TestCase context
|
||||
* and within the testing process.
|
||||
*/
|
||||
beforeTest(fn) {
|
||||
if (typeof fn !== "function") {
|
||||
throw new Error ("Tried to call non-function before test");
|
||||
}
|
||||
|
||||
this.beforeTestFn = fn;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* called after runTest
|
||||
* virtual method expected to be overridden by child class if needed
|
||||
*/
|
||||
testTeardown(res) {
|
||||
if (this.afterTestFn) {
|
||||
this.afterTestFn.call(this, res);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a callback function that gets called in the TestCase context
|
||||
* and within the testing process. Good for validating results.
|
||||
*/
|
||||
afterTest(fn) {
|
||||
if (typeof fn !== "function") {
|
||||
throw new Error ("Tried to call non-function after test");
|
||||
}
|
||||
|
||||
this.afterTestFn = fn;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* validates the value returned from the test function
|
||||
* virtual method expected to be overridden by child class
|
||||
*/
|
||||
validateRet() {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* calls doIt() with testObject() and expects it to fail with a TypeError()
|
||||
*/
|
||||
testBadArgs(testDesc) {
|
||||
promise_test(function(t) {
|
||||
return promise_rejects(t, new TypeError(), this.doIt(), "Expected bad parameters to fail");
|
||||
}.bind(this), testDesc);
|
||||
}
|
||||
}
|
||||
|
||||
var createCredentialDefaultArgs = {
|
||||
options: {
|
||||
publicKey: {
|
||||
// Relying Party:
|
||||
rp: {
|
||||
name: "Acme"
|
||||
},
|
||||
|
||||
// User:
|
||||
user: {
|
||||
id: new Uint8Array(), // Won't survive the copy, must be rebuilt
|
||||
name: "john.p.smith@example.com",
|
||||
displayName: "John P. Smith",
|
||||
icon: "https://pics.acme.com/00/p/aBjjjpqPb.png"
|
||||
},
|
||||
|
||||
pubKeyCredParams: [{
|
||||
type: "public-key",
|
||||
alg: cose_alg_ECDSA_w_SHA256,
|
||||
}],
|
||||
|
||||
timeout: 60000, // 1 minute
|
||||
excludeCredentials: [] // No excludeList
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function cloneObject(o) {
|
||||
return JSON.parse(JSON.stringify(o));
|
||||
}
|
||||
|
||||
function extendObject(dst, src) {
|
||||
Object.keys(src).forEach(function(key) {
|
||||
if (isSimpleObject(src[key])) {
|
||||
extendObject (dst[key], src[key]);
|
||||
} else {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isSimpleObject(o) {
|
||||
return (typeof o === "object" &&
|
||||
!Array.isArray(o) &&
|
||||
!(o instanceof ArrayBuffer));
|
||||
}
|
||||
|
||||
/**
|
||||
* CreateCredentialTest
|
||||
*
|
||||
|
@ -198,7 +369,7 @@ class CreateCredentialsTest extends TestCase {
|
|||
window.crypto.getRandomValues(challengeBytes);
|
||||
this.testObject = cloneObject(createCredentialDefaultArgs);
|
||||
// cloneObject can't clone the BufferSource in user.id, so let's recreate it.
|
||||
this.testObject.options.publicKey.user.id = new Uint8Array();
|
||||
this.testObject.options.publicKey.user.id = new Uint8Array(16);
|
||||
this.testObject.options.publicKey.challenge = challengeBytes;
|
||||
|
||||
// how to order the properties of testObject when passing them to makeCredential
|
||||
|
@ -235,15 +406,8 @@ class GetCredentialsTest extends TestCase {
|
|||
// default arguments
|
||||
let challengeBytes = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(challengeBytes);
|
||||
this.testObject = {
|
||||
options: {
|
||||
publicKey: {
|
||||
challenge: challengeBytes,
|
||||
// timeout: 60000,
|
||||
// allowCredentials: [newCredential]
|
||||
}
|
||||
}
|
||||
};
|
||||
this.testObject = cloneObject(getCredentialDefaultArgs);
|
||||
this.testObject.options.publicKey.challenge = challengeBytes;
|
||||
|
||||
// how to order the properties of testObject when passing them to makeCredential
|
||||
this.argOrder = [
|
||||
|
@ -266,33 +430,28 @@ class GetCredentialsTest extends TestCase {
|
|||
// if a Promise was passed in, add it to the list
|
||||
if (arg instanceof Promise) {
|
||||
this.credentialPromiseList.push(arg);
|
||||
return;
|
||||
return this;
|
||||
}
|
||||
|
||||
// if a credential object was passed in, convert it to a Promise for consistency
|
||||
if (typeof arg === "object") {
|
||||
this.credentialPromiseList.push(Promise.resolve(arg));
|
||||
return;
|
||||
return this;
|
||||
}
|
||||
|
||||
// if a credential wasn't passed in, create one
|
||||
let challengeBytes = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(challengeBytes);
|
||||
var createArgs = cloneObject(createCredentialDefaultArgs);
|
||||
createArgs.options.publicKey.challenge = challengeBytes;
|
||||
createArgs.options.publicKey.user.id = new Uint8Array();
|
||||
var p = navigator.credentials.create(createArgs.options);
|
||||
// if no credential specified then create one
|
||||
var p = createCredential();
|
||||
this.credentialPromiseList.push(p);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
test() {
|
||||
testSetup(desc) {
|
||||
if (!this.credentialPromiseList.length) {
|
||||
throw new Error("Attempting list without defining credential to test");
|
||||
}
|
||||
|
||||
Promise.all(this.credentialPromiseList)
|
||||
return Promise.all(this.credentialPromiseList)
|
||||
.then((credList) => {
|
||||
var idList = credList.map((cred) => {
|
||||
return {
|
||||
|
@ -302,12 +461,15 @@ class GetCredentialsTest extends TestCase {
|
|||
};
|
||||
});
|
||||
this.testObject.options.publicKey.allowCredentials = idList;
|
||||
return super.test();
|
||||
// return super.test(desc);
|
||||
})
|
||||
.catch((err) => {
|
||||
throw Error(err);
|
||||
});
|
||||
}
|
||||
|
||||
validateRet(ret) {
|
||||
validatePublicKeyCredential (ret);
|
||||
validatePublicKeyCredential(ret);
|
||||
validateAuthenticatorAssertionResponse(ret.response);
|
||||
}
|
||||
}
|
||||
|
@ -335,12 +497,16 @@ function validatePublicKeyCredential(cred) {
|
|||
function validateAuthenticatorAttestationResponse(attr) {
|
||||
// class
|
||||
assert_class_string(attr, "AuthenticatorAttestationResponse", "Expected credentials.create() to return instance of 'AuthenticatorAttestationResponse' class");
|
||||
|
||||
// clientDataJSON
|
||||
assert_idl_attribute(attr, "clientDataJSON", "credentials.create() should return AuthenticatorAttestationResponse with clientDataJSON attribute");
|
||||
assert_readonly(attr, "clientDataJSON", "credentials.create() should return AuthenticatorAttestationResponse with readonly clientDataJSON attribute");
|
||||
// TODO: clientDataJSON() and make sure fields are correct
|
||||
|
||||
// attestationObject
|
||||
assert_idl_attribute(attr, "attestationObject", "credentials.create() should return AuthenticatorAttestationResponse with attestationObject attribute");
|
||||
assert_readonly(attr, "attestationObject", "credentials.create() should return AuthenticatorAttestationResponse with readonly attestationObject attribute");
|
||||
// TODO: parseAuthenticatorData() and make sure flags are correct
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,15 +515,20 @@ function validateAuthenticatorAttestationResponse(attr) {
|
|||
function validateAuthenticatorAssertionResponse(assert) {
|
||||
// class
|
||||
assert_class_string(assert, "AuthenticatorAssertionResponse", "Expected credentials.create() to return instance of 'AuthenticatorAssertionResponse' class");
|
||||
|
||||
// clientDataJSON
|
||||
assert_idl_attribute(assert, "clientDataJSON", "credentials.get() should return AuthenticatorAssertionResponse with clientDataJSON attribute");
|
||||
assert_readonly(assert, "clientDataJSON", "credentials.get() should return AuthenticatorAssertionResponse with readonly clientDataJSON attribute");
|
||||
// TODO: clientDataJSON() and make sure fields are correct
|
||||
|
||||
// signature
|
||||
assert_idl_attribute(assert, "signature", "credentials.get() should return AuthenticatorAssertionResponse with signature attribute");
|
||||
assert_readonly(assert, "signature", "credentials.get() should return AuthenticatorAssertionResponse with readonly signature attribute");
|
||||
|
||||
// authenticatorData
|
||||
assert_idl_attribute(assert, "authenticatorData", "credentials.get() should return AuthenticatorAssertionResponse with authenticatorData attribute");
|
||||
assert_readonly(assert, "authenticatorData", "credentials.get() should return AuthenticatorAssertionResponse with readonly authenticatorData attribute");
|
||||
// TODO: parseAuthenticatorData() and make sure flags are correct
|
||||
}
|
||||
|
||||
//************* BEGIN DELETE AFTER 1/1/2018 *************** //
|
||||
|
@ -370,8 +541,8 @@ var debug = function() {};
|
|||
// note that the polyfill only gets loaded if navigator.credentials create doesn't exist
|
||||
// AND if the polyfill script is found at the right path (i.e. - the polyfill is opt-in)
|
||||
function ensureInterface() {
|
||||
if (typeof navigator.credentials.create !== "function") {
|
||||
debug = console.log;
|
||||
if (typeof navigator.credentials === "object" && typeof navigator.credentials.create !== "function") {
|
||||
// debug = onsole.log;
|
||||
|
||||
return loadJavaScript("/webauthn/webauthn-polyfill/webauthn-polyfill.js")
|
||||
.then(() => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue