Update web-platform-tests to revision 10168e9a5d44efbc6e7d416d1d454eb9c9f1396c

This commit is contained in:
Josh Matthews 2018-01-31 09:13:41 -05:00
parent c88dc51d03
commit 0e1caebaf4
791 changed files with 23381 additions and 5501 deletions

View file

@ -1,3 +1,15 @@
/* Useful constants for working with COSE key objects */
const cose_kty = 1;
const cose_kty_ec2 = 2;
const cose_alg = 3;
const cose_alg_ECDSA_w_SHA256 = -7;
const cose_alg_ECDSA_w_SHA512 = -36;
const cose_crv = -1;
const cose_crv_P256 = 1;
const cose_crv_x = -2;
const cose_crv_y = -3;
/**
* TestCase
*
@ -5,7 +17,7 @@
* Is intended to be overloaded with subclasses that override testObject, testFunction and argOrder
* The testObject is the default arguments for the testFunction
* The default testObject can be modified with the modify() method, making it easy to create new tests based on the default
* The testFunction is the target of the test and is called by the test() method. test() applies the testObject as arguments via toArgs()
* The testFunction is the target of the test and is called by the doIt() method. doIt() applies the testObject as arguments via toArgs()
* toArgs() uses argOrder to make sure the resulting array is in the right order of the arguments for the testFunction
*/
class TestCase {
@ -15,6 +27,7 @@ class TestCase {
};
this.testObject = {};
this.argOrder = [];
this.ctx = null;
}
/**
@ -70,7 +83,6 @@ class TestCase {
}
// iterate through each of the desired modifications, and call recursiveSetObject on them
var obj = this.testObject;
for (let idx in mods) {
var mod = mods[idx];
let paths = mod.path.split(".");
@ -94,61 +106,103 @@ class TestCase {
}
/**
* test
*
* run the test function with the top-level properties of the test object applied as arguments
* actually runs the test function with the supplied arguments
*/
test() {
return this.testFunction(...this.toArgs());
doIt() {
if (typeof this.testFunction !== "function") {
throw new Error("Test function not found");
}
return this.testFunction.call(this.ctx, ...this.toArgs());
}
/**
* testArgs
*
* calls test() with testObject() and expects it to fail with a TypeError()
* run the test function with the top-level properties of the test object applied as arguments
*/
test(desc) {
promise_test(() => {
return this.doIt()
.then((ret) => {
// check the result
this.validateRet(ret);
return ret;
});
}, desc);
}
/**
* validates the value returned from the test function
*/
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.test());
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));
}
/**
* MakeCredentialTest
* CreateCredentialTest
*
* tests the WebAuthn makeCredential() interface
* tests the WebAuthn navigator.credentials.create() interface
*/
class MakeCredentialTest extends TestCase {
class CreateCredentialsTest extends TestCase {
constructor() {
// initialize the parent class
super();
// the function to be tested
this.testFunction = navigator.authentication.makeCredential;
this.testFunction = navigator.credentials.create;
// the context to call the test function with (i.e. - the 'this' object for the function)
this.ctx = navigator.credentials;
// the default object to pass to makeCredential, to be modified with modify() for various tests
// var challenge = Uint8Array.from("Y2xpbWIgYSBtb3VudGFpbg");
this.testObject = {
accountInformation: {
rpDisplayName: "ACME",
displayName: "John P. Smith",
name: "johnpsmith@example.com",
id: "1098237235409872",
imageUri: "https://pics.acme.com/00/p/aBjjjpqPb.png"
},
cryptoParameters: [{
type: "ScopedCred",
algorithm: "RSASSA-PKCS1-v1_5",
}],
attestationChallenge: Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]).buffer
};
let challengeBytes = new Uint8Array(16);
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.challenge = challengeBytes;
// how to order the properties of testObject when passing them to makeCredential
this.argOrder = [
"accountInformation",
"cryptoParameters",
"attestationChallenge",
"options"
];
@ -156,43 +210,201 @@ class MakeCredentialTest extends TestCase {
// would prefer to do this in the super class, but have to call super() before using `this.*`
if (arguments.length) this.modify(...arguments);
}
validateRet(ret) {
validatePublicKeyCredential(ret);
validateAuthenticatorAttestationResponse(ret.response);
}
}
//************* BEGIN DELETE AFTER 1/1/2017 *************** //
/**
* GetCredentialsTest
*
* tests the WebAuthn navigator.credentials.get() interface
*/
class GetCredentialsTest extends TestCase {
constructor(...args) {
// initialize the parent class
super();
// the function to be tested
this.testFunction = navigator.credentials.get;
// the context to call the test function with (i.e. - the 'this' object for the function)
this.ctx = navigator.credentials;
// default arguments
let challengeBytes = new Uint8Array(16);
window.crypto.getRandomValues(challengeBytes);
this.testObject = {
options: {
publicKey: {
challenge: challengeBytes,
// timeout: 60000,
// allowCredentials: [newCredential]
}
}
};
// how to order the properties of testObject when passing them to makeCredential
this.argOrder = [
"options"
];
this.credentialPromiseList = [];
// enable the constructor to modify the default testObject
// would prefer to do this in the super class, but have to call super() before using `this.*`
if (arguments.length) {
if (args.cred instanceof Promise) this.credPromise = args.cred;
else if (typeof args.cred === "object") this.credPromise = Promise.resolve(args.cred);
delete args.cred;
this.modify(...arguments);
}
}
addCredential(arg) {
// if a Promise was passed in, add it to the list
if (arg instanceof Promise) {
this.credentialPromiseList.push(arg);
return;
}
// 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;
}
// 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);
this.credentialPromiseList.push(p);
return this;
}
test() {
if (!this.credentialPromiseList.length) {
throw new Error("Attempting list without defining credential to test");
}
Promise.all(this.credentialPromiseList)
.then((credList) => {
var idList = credList.map((cred) => {
return {
id: cred.rawId,
transports: ["usb", "nfc", "ble"],
type: "public-key"
};
});
this.testObject.options.publicKey.allowCredentials = idList;
return super.test();
});
}
validateRet(ret) {
validatePublicKeyCredential (ret);
validateAuthenticatorAssertionResponse(ret.response);
}
}
/**
* runs assertions against a PublicKeyCredential object to ensure it is properly formatted
*/
function validatePublicKeyCredential(cred) {
// class
assert_class_string(cred, "PublicKeyCredential", "Expected return to be instance of 'PublicKeyCredential' class");
// id
assert_idl_attribute(cred, "id", "should return PublicKeyCredential with id attribute");
assert_readonly(cred, "id", "should return PublicKeyCredential with readonly id attribute");
// rawId
assert_idl_attribute(cred, "rawId", "should return PublicKeyCredential with rawId attribute");
assert_readonly(cred, "rawId", "should return PublicKeyCredential with readonly rawId attribute");
// type
assert_idl_attribute(cred, "type", "should return PublicKeyCredential with type attribute");
assert_equals(cred.type, "public-key", "should return PublicKeyCredential with type 'public-key'");
}
/**
* runs assertions against a AuthenticatorAttestationResponse object to ensure it is properly formatted
*/
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");
// 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");
}
/**
* runs assertions against a AuthenticatorAssertionResponse object to ensure it is properly formatted
*/
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");
// 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");
}
//************* BEGIN DELETE AFTER 1/1/2018 *************** //
// XXX for development mode only!!
// debug() for debugging purposes... we can drop this later if it is considered ugly
// note that debug is currently an empty function (i.e. - prints no output)
// and debug only prints output if the polyfill is loaded
var debug = function() {};
// if the WebAuthn API doesn't exist load a polyfill for testing
// note that the polyfill only gets loaded if navigator.authentication doesn't exist
// 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() {
return new Promise(function(resolve, reject) {
if (typeof navigator.authentication !== "object") {
debug = console.log;
if (typeof navigator.credentials.create !== "function") {
debug = console.log;
// dynamic loading of polyfill script by creating new <script> tag and seeing the src=
var scriptElem = document.createElement("script");
if (typeof scriptElem !== "object") {
debug("ensureInterface: Error creating script element while attempting loading polyfill");
return reject(new Error("ensureInterface: Error creating script element while loading polyfill"));
}
scriptElem.type = "application/javascript";
scriptElem.onload = function() {
debug("!!! XXX - LOADING POLYFILL FOR WEBAUTHN TESTING - XXX !!!");
return resolve();
};
scriptElem.onerror = function() {
return reject(new Error("navigator.authentication does not exist"));
};
scriptElem.src = "/webauthn/webauthn-polyfill/webauthn-polyfill.js";
if (document.body) {
document.body.appendChild(scriptElem);
} else {
debug("ensureInterface: DOM has no body");
return reject(new Error("ensureInterface: DOM has no body"));
}
return loadJavaScript("/webauthn/webauthn-polyfill/webauthn-polyfill.js")
.then(() => {
return loadJavaScript("/webauthn/webauthn-soft-authn/soft-authn.js");
});
} else {
return Promise.resolve();
}
}
function loadJavaScript(path) {
return new Promise((resolve, reject) => {
// dynamic loading of polyfill script by creating new <script> tag and seeing the src=
var scriptElem = document.createElement("script");
if (typeof scriptElem !== "object") {
debug("ensureInterface: Error creating script element while attempting loading polyfill");
return reject(new Error("ensureInterface: Error creating script element while loading polyfill"));
}
scriptElem.type = "application/javascript";
scriptElem.onload = function() {
debug("!!! Loaded " + path + " ...");
return resolve();
};
scriptElem.onerror = function() {
debug("navigator.credentials.create does not exist");
resolve();
};
scriptElem.src = path;
if (document.body) {
document.body.appendChild(scriptElem);
} else {
debug("ensureInterface: DOM has no body");
return reject(new Error("ensureInterface: DOM has no body"));
}
});
}
@ -201,9 +413,10 @@ function standardSetup(cb) {
return ensureInterface()
.then(() => {
if (cb) return cb();
})
.catch((err) => {
return (err);
});
}
//************* END DELETE AFTER 1/1/2017 *************** //
//************* END DELETE AFTER 1/1/2018 *************** //
/* JSHINT */
/* globals promise_rejects, assert_class_string, assert_equals, assert_idl_attribute, assert_readonly, promise_test */
/* exported standardSetup, CreateCredentialsTest, GetCredentialsTest */