Update web-platform-tests to revision 3137d1d2d7757366a69f8a449b458b5057e0e81e

This commit is contained in:
Ms2ger 2016-12-28 09:51:21 +01:00
parent 81ca858678
commit d6ba94ca28
2339 changed files with 89274 additions and 9328 deletions

View file

@ -5,16 +5,19 @@ function run_test() {
var wrappers = []; // Things we wrap (and upwrap) keys with
var keys = []; // Things to wrap and unwrap
var ecdhPeerKey; // ECDH peer public key needed for non-extractable ECDH key comparison
// Generate all the keys needed, then iterate over all combinations
// to test wrapping and unwrapping.
Promise.all([generateWrappingKeys(), generateKeysToWrap()])
Promise.all([generateWrappingKeys(), generateKeysToWrap(), generateEcdhPeerKey()])
.then(function(results) {
var promises = [];
wrappers.forEach(function(wrapper) {
keys.forEach(function(key) {
testWrapping(wrapper, key);
promises.push(testWrapping(wrapper, key));
})
});
return Promise.all(promises);
}, function(err) {
promise_test(function(test) {
assert_unreached("A key failed to generate: " + err.name + ": " + err.message)
@ -22,7 +25,7 @@ function run_test() {
})
.then(function() {
done();
}, function(error) {
}, function(err) {
promise_test(function(test) {
assert_unreached("A test failed to run: " + err.name + ": " + err.message)
}, "Could not run all tests")
@ -114,6 +117,12 @@ function run_test() {
}));
}
function generateEcdhPeerKey() {
return subtle.generateKey({name: "ECDH", namedCurve: "P-256"},true,["deriveBits"])
.then(function(result){
ecdhPeerKey = result.publicKey;
});
}
// Can we successfully "round-trip" (wrap, then unwrap, a key)?
function testWrapping(wrapper, toWrap) {
@ -127,37 +136,98 @@ function run_test() {
formats = ["raw", "jwk"]
}
formats.forEach(function(fmt) {
return Promise.all(formats.map(function(fmt) {
var originalExport;
return subtle.exportKey(fmt, toWrap.key).then(function(exportedKey) {
originalExport = exportedKey;
if (wrappingIsPossible(originalExport, wrapper.parameters.name)) {
promise_test(function(test) {
return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters)
.then(function(wrappedResult) {
return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages);
}).then(function(unwrappedResult) {
assert_true(unwrappedResult.extractable, "Unwrapped result is extractable");
return subtle.exportKey(fmt, unwrappedResult)
}).then(function(roundTripExport) {
assert_true(equalExport(originalExport, roundTripExport), "Post-wrap export matches original export");
}, function(err) {
assert_unreached("Round trip for extractable key threw an error - " + err.name + ': "' + err.message + '"');
});
}, "Can wrap and unwrap " + toWrap.name + " keys using " + fmt + " and " + wrapper.parameters.name);
promise_test(function(test) {
return subtle.exportKey(fmt, toWrap.key)
.then(function(exportedKey) {
originalExport = exportedKey;
return exportedKey;
}).then(function(exportedKey) {
return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters);
}).then(function(wrappedResult) {
return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages)
}).then(function(unwrappedResult) {
return subtle.exportKey(fmt, unwrappedResult)
}).then(function(roundTripExport) {
if ("byteLength" in originalExport) {
assert_true(equalBuffers(originalExport, roundTripExport), "Post-wrap export matches original export");
} else {
assert_true(equalJwk(originalExport, roundTripExport), "Post-wrap export matches original export.");
}
}, function(err) {
if (wrappingIsPossible(originalExport, wrapper.parameters.name)) {
assert_unreached("Round trip threw an error - " + err.name + ': "' + err.message + '"');
} else {
assert_true(true, "Skipped test due to key length restrictions");
}
})
}, "Can wrap and unwrap " + toWrap.name + " keys using " + fmt + " and " + wrapper.parameters.name);
if (canCompareNonExtractableKeys(toWrap.key)) {
promise_test(function(test){
return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters)
.then(function(wrappedResult) {
return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages);
}).then(function(unwrappedResult){
assert_false(unwrappedResult.extractable, "Unwrapped result is non-extractable");
return equalKeys(toWrap.key, unwrappedResult);
}).then(function(result){
assert_true(result, "Unwrapped key matches original");
}).catch(function(err){
assert_unreached("Round trip for key unwrapped non-extractable threw an error - " + err.name + ': "' + err.message + '"');
});
}, "Can wrap and unwrap " + toWrap.name + " keys as non-extractable using " + fmt + " and " + wrapper.parameters.name);
if (fmt === "jwk") {
promise_test(function(test){
var wrappedKey;
return wrapAsNonExtractableJwk(toWrap.key,wrapper).then(function(wrappedResult){
wrappedKey = wrappedResult;
return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages);
}).then(function(unwrappedResult){
assert_false(unwrappedResult.extractable, "Unwrapped key is non-extractable");
return equalKeys(toWrap.key,unwrappedResult);
}).then(function(result){
assert_true(result, "Unwrapped key matches original");
}).catch(function(err){
assert_unreached("Round trip for non-extractable key threw an error - " + err.name + ': "' + err.message + '"');
}).then(function(){
return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages);
}).then(function(unwrappedResult){
assert_unreached("Unwrapping a non-extractable JWK as extractable should fail");
}).catch(function(err){
assert_equals(err.name, "DataError", "Unwrapping a non-extractable JWK as extractable fails with DataError");
});
}, "Can unwrap " + toWrap.name + " non-extractable keys using jwk and " + wrapper.parameters.name);
}
}
}
});
}));
}
// Implement key wrapping by hand to wrap a key as non-extractable JWK
function wrapAsNonExtractableJwk(key, wrapper){
var wrappingKey = wrapper.wrappingKey,
encryptKey;
return subtle.exportKey("jwk",wrappingKey)
.then(function(jwkWrappingKey){
// Update the key generation parameters to work as key import parameters
var params = Object.create(wrapper.parameters.generateParameters);
if(params.name === "AES-KW") {
params.name = "AES-CBC";
jwkWrappingKey.alg = "A"+params.length+"CBC";
} else if (params.name === "RSA-OAEP") {
params.modulusLength = undefined;
params.publicExponent = undefined;
}
jwkWrappingKey.key_ops = ["encrypt"];
return subtle.importKey("jwk", jwkWrappingKey, params, true, ["encrypt"]);
}).then(function(importedWrappingKey){
encryptKey = importedWrappingKey;
return subtle.exportKey("jwk",key);
}).then(function(exportedKey){
exportedKey.ext = false;
var jwk = JSON.stringify(exportedKey)
if (wrappingKey.algorithm.name === "AES-KW") {
return aeskw(encryptKey, str2ab(jwk.slice(0,-1) + " ".repeat(jwk.length%8 ? 8-jwk.length%8 : 0) + "}"));
} else {
return subtle.encrypt(wrapper.parameters.wrapParameters,encryptKey,str2ab(jwk));
}
});
}
@ -189,6 +259,15 @@ function run_test() {
// Helper methods follow:
// Are two exported keys equal
function equalExport(originalExport, roundTripExport) {
if ("byteLength" in originalExport) {
return equalBuffers(originalExport, roundTripExport);
} else {
return equalJwk(originalExport, roundTripExport);
}
}
// Are two array buffers the same?
function equalBuffers(a, b) {
if (a.byteLength !== b.byteLength) {
@ -262,5 +341,176 @@ function run_test() {
return "{" + keyValuePairs.join(", ") + "}";
}
// Can we compare key values by using them
function canCompareNonExtractableKeys(key){
if (key.usages.indexOf("decrypt") !== -1) {
return true;
}
if (key.usages.indexOf("sign") !== -1) {
return true;
}
if (key.usages.indexOf("wrapKey") !== -1) {
return true;
}
if (key.usages.indexOf("deriveBits") !== -1) {
return true;
}
return false;
}
// Compare two keys by using them (works for non-extractable keys)
function equalKeys(expected, got){
if ( expected.algorithm.name !== got.algorithm.name ) {
return Promise.resolve(false);
}
var cryptParams, signParams, wrapParams, deriveParams;
switch(expected.algorithm.name){
case "AES-CTR" :
cryptParams = {name: "AES-CTR", counter: new Uint8Array(16), length: 64};
break;
case "AES-CBC" :
cryptParams = {name: "AES-CBC", iv: new Uint8Array(16) };
break;
case "AES-GCM" :
cryptParams = {name: "AES-GCM", iv: new Uint8Array(16) };
break;
case "RSA-OAEP" :
cryptParams = {name: "RSA-OAEP", label: new Uint8Array(8) };
break;
case "RSASSA-PKCS1-v1_5" :
signParams = {name: "RSASSA-PKCS1-v1_5"};
break;
case "RSA-PSS" :
signParams = {name: "RSA-PSS", saltLength: 32 };
break;
case "ECDSA" :
signParams = {name: "ECDSA", hash: "SHA-256"};
break;
case "HMAC" :
signParams = {name: "HMAC"};
break;
case "AES-KW" :
wrapParams = {name: "AES-KW"};
break;
case "ECDH" :
deriveParams = {name: "ECDH", public: ecdhPeerKey};
break;
default:
throw new Error("Unsupported algorithm for key comparison");
}
if (cryptParams) {
return subtle.exportKey("jwk",expected)
.then(function(jwkExpectedKey){
if (expected.algorithm.name === "RSA-OAEP") {
["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; });
}
jwkExpectedKey.key_ops = ["encrypt"];
return subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["encrypt"]);
}).then(function(expectedEncryptKey){
return subtle.encrypt(cryptParams, expectedEncryptKey, new Uint8Array(32));
}).then(function(encryptedData){
return subtle.decrypt(cryptParams, got, encryptedData);
}).then(function(decryptedData){
var result = new Uint8Array(decryptedData);
return !result.some(x => x);
});
} else if (signParams) {
var verifyKey;
return subtle.exportKey("jwk",expected)
.then(function(jwkExpectedKey){
if (expected.algorithm.name === "RSA-PSS" || expected.algorithm.name === "RSASSA-PKCS1-v1_5") {
["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; });
}
if (expected.algorithm.name === "ECDSA") {
delete jwkExpectedKey["d"];
}
jwkExpectedKey.key_ops = ["verify"];
return subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["verify"]);
}).then(function(expectedVerifyKey){
verifyKey = expectedVerifyKey;
return subtle.sign(signParams, got, new Uint8Array(32));
}).then(function(signature){
return subtle.verify(signParams, verifyKey, signature, new Uint8Array(32));
});
} else if (wrapParams) {
var aKeyToWrap, wrappedWithExpected;
return subtle.importKey("raw", new Uint8Array(16), "AES-CBC", true, ["encrypt"])
.then(function(key){
aKeyToWrap = key;
return subtle.wrapKey("raw", aKeyToWrap, expected, wrapParams);
}).then(function(wrapResult){
wrappedWithExpected = Array.from((new Uint8Array(wrapResult)).values());
return subtle.wrapKey("raw", aKeyToWrap, got, wrapParams);
}).then(function(wrapResult){
var wrappedWithGot = Array.from((new Uint8Array(wrapResult)).values());
return wrappedWithGot.every((x,i) => x === wrappedWithExpected[i]);
});
} else {
var expectedDerivedBits;
return subtle.deriveBits(deriveParams, expected, 128)
.then(function(result){
expectedDerivedBits = Array.from((new Uint8Array(result)).values());
return subtle.deriveBits(deriveParams, got, 128);
}).then(function(result){
var gotDerivedBits = Array.from((new Uint8Array(result)).values());
return gotDerivedBits.every((x,i) => x === expectedDerivedBits[i]);
});
}
}
// Raw AES encryption
function aes( k, p ) {
return subtle.encrypt({name: "AES-CBC", iv: new Uint8Array(16) }, k, p).then(function(ciphertext){return ciphertext.slice(0,16);});
}
// AES Key Wrap
function aeskw(key, data) {
if (data.byteLength % 8 !== 0) {
throw new Error("AES Key Wrap data must be a multiple of 8 bytes in length");
}
var A = Uint8Array.from([0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0, 0, 0, 0, 0, 0, 0, 0]),
Av = new DataView(A.buffer),
R = [],
n = data.byteLength / 8;
for(var i = 0; i<data.byteLength; i+=8) {
R.push(new Uint8Array(data.slice(i,i+8)));
}
function aeskw_step(j, i, final, B) {
A.set(new Uint8Array(B.slice(0,8)));
Av.setUint32(4,Av.getUint32(4) ^ (n*j+i+1));
R[i] = new Uint8Array(B.slice(8,16));
if (final) {
R.unshift(A.slice(0,8));
var result = new Uint8Array(R.length * 8);
R.forEach(function(Ri,i){ result.set(Ri, i*8); });
return result;
} else {
A.set(R[(i+1)%n],8);
return aes(key,A);
}
}
var p = new Promise(function(resolve){
A.set(R[0],8);
resolve(aes(key,A));
});
for(var j=0;j<6;++j) {
for(var i=0;i<n;++i) {
p = p.then(aeskw_step.bind(undefined, j, i,j===5 && i===(n-1)));
}
}
return p;
}
function str2ab(str) { return Uint8Array.from( str.split(''), function(s){return s.charCodeAt(0)} ); }
function ab2str(ab) { return String.fromCharCode.apply(null, new Uint8Array(ab)); }
}