mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
test,crypto: update WebCryptoAPI WPT
PR-URL: https://github.com/nodejs/node/pull/46575 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
2787e2dfc2
commit
eb2a1ab38a
2
test/fixtures/wpt/README.md
vendored
2
test/fixtures/wpt/README.md
vendored
@ -32,7 +32,7 @@ Last update:
|
||||
- user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing
|
||||
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/d8dbe6990b/wasm/jsapi
|
||||
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
|
||||
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/450f829d25/WebCryptoAPI
|
||||
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/238d9d9bac/WebCryptoAPI
|
||||
- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions
|
||||
|
||||
[Web Platform Tests]: https://github.com/web-platform-tests/wpt
|
||||
|
@ -64,7 +64,7 @@ function run_test(algorithmNames, slowTest) {
|
||||
.then(function(result) {
|
||||
if (resultType === "CryptoKeyPair") {
|
||||
assert_goodCryptoKey(result.privateKey, algorithm, extractable, usages, "private");
|
||||
assert_goodCryptoKey(result.publicKey, algorithm, extractable, usages, "public");
|
||||
assert_goodCryptoKey(result.publicKey, algorithm, true, usages, "public");
|
||||
} else {
|
||||
assert_goodCryptoKey(result, algorithm, extractable, usages, "secret");
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
// META: title=WebCryptoAPI: importKey() for EC keys
|
||||
// META: timeout=long
|
||||
// META: script=../util/helpers.js
|
||||
|
||||
// Test importKey and exportKey for EC algorithms. Only "happy paths" are
|
||||
// currently tested - those where the operation should succeed.
|
||||
@ -110,6 +111,7 @@
|
||||
return subtle.importKey(format, keyData, algorithm, extractable, usages).
|
||||
then(function(key) {
|
||||
assert_equals(key.constructor, CryptoKey, "Imported a CryptoKey object");
|
||||
assert_goodCryptoKey(key, algorithm, extractable, usages, (format === 'pkcs8' || (format === 'jwk' && keyData.d)) ? 'private' : 'public');
|
||||
if (!extractable) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
// META: title=WebCryptoAPI: importKey() for OKP keys
|
||||
// META: timeout=long
|
||||
// META: script=../util/helpers.js
|
||||
|
||||
// Test importKey and exportKey for OKP algorithms. Only "happy paths" are
|
||||
// currently tested - those where the operation should succeed.
|
||||
@ -104,6 +105,7 @@
|
||||
return subtle.importKey(format, keyData[format], algorithm, extractable, usages).
|
||||
then(function(key) {
|
||||
assert_equals(key.constructor, CryptoKey, "Imported a CryptoKey object");
|
||||
assert_goodCryptoKey(key, algorithm, extractable, usages, (format === 'pkcs8' || (format === 'jwk' && keyData[format].d)) ? 'private' : 'public');
|
||||
if (!extractable) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
// META: title=WebCryptoAPI: importKey() for RSA keys
|
||||
// META: timeout=long
|
||||
// META: script=../util/helpers.js
|
||||
|
||||
// Test importKey and exportKey for RSA algorithms. Only "happy paths" are
|
||||
// currently tested - those where the operation should succeed.
|
||||
@ -113,6 +114,7 @@
|
||||
return subtle.importKey(format, keyData[format], algorithm, extractable, usages).
|
||||
then(function(key) {
|
||||
assert_equals(key.constructor, CryptoKey, "Imported a CryptoKey object");
|
||||
assert_goodCryptoKey(key, algorithm, extractable, usages, (format === 'pkcs8' || (format === 'jwk' && keyData[format].d)) ? 'private' : 'public');
|
||||
if (!extractable) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
// META: title=WebCryptoAPI: importKey() for symmetric keys
|
||||
// META: timeout=long
|
||||
// META: script=../util/helpers.js
|
||||
|
||||
// Test importKey and exportKey for non-PKC algorithms. Only "happy paths" are
|
||||
// currently tested - those where the operation should succeed.
|
||||
@ -57,6 +58,10 @@
|
||||
});
|
||||
});
|
||||
|
||||
function hasLength(algorithm) {
|
||||
return algorithm.name === 'HMAC' || algorithm.name.startsWith('AES');
|
||||
}
|
||||
|
||||
// Test importKey with a given key format and other parameters. If
|
||||
// extrable is true, export the key and verify that it matches the input.
|
||||
function testFormat(format, algorithm, keyData, keySize, usages, extractable) {
|
||||
@ -64,6 +69,7 @@
|
||||
return subtle.importKey(format, keyData, algorithm, extractable, usages).
|
||||
then(function(key) {
|
||||
assert_equals(key.constructor, CryptoKey, "Imported a CryptoKey object");
|
||||
assert_goodCryptoKey(key, hasLength(key.algorithm) ? { length: keySize, ...algorithm } : algorithm, extractable, usages, 'secret');
|
||||
if (!extractable) {
|
||||
return;
|
||||
}
|
||||
|
@ -306,6 +306,35 @@ function run_test() {
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// [RSA-PSS] Verification should fail with wrong saltLength
|
||||
testVectors.forEach(function(vector) {
|
||||
if (vector.algorithm.name === "RSA-PSS") {
|
||||
var promise = importVectorKeys(vector, ["verify"], ["sign"])
|
||||
.then(function(vectors) {
|
||||
promise_test(function(test) {
|
||||
const saltLength = vector.algorithm.saltLength === 32 ? 48 : 32;
|
||||
var operation = subtle.verify({ ...vector.algorithm, saltLength }, vector.publicKey, vector.signature, vector.plaintext)
|
||||
.then(function(is_verified) {
|
||||
assert_false(is_verified, "Signature NOT verified");
|
||||
}, function(err) {
|
||||
assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
|
||||
});
|
||||
|
||||
return operation;
|
||||
}, vector.name + " verification failure with wrong saltLength");
|
||||
|
||||
}, function(err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested verification.
|
||||
promise_test(function(test) {
|
||||
assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
|
||||
}, "importVectorKeys step: " + vector.name + " verification failure with wrong saltLength");
|
||||
});
|
||||
|
||||
all_promises.push(promise);
|
||||
}
|
||||
});
|
||||
|
||||
// Verification should fail with wrong plaintext
|
||||
testVectors.forEach(function(vector) {
|
||||
var promise = importVectorKeys(vector, ["verify"], ["sign"])
|
||||
|
@ -19,7 +19,7 @@ var registeredAlgorithmNames = [
|
||||
"SHA-256",
|
||||
"SHA-384",
|
||||
"SHA-512",
|
||||
"HKDF-CTR",
|
||||
"HKDF",
|
||||
"PBKDF2",
|
||||
"Ed25519",
|
||||
"Ed448",
|
||||
@ -104,9 +104,6 @@ function assert_goodCryptoKey(key, algorithm, extractable, usages, kind) {
|
||||
|
||||
assert_equals(key.constructor, CryptoKey, "Is a CryptoKey");
|
||||
assert_equals(key.type, kind, "Is a " + kind + " key");
|
||||
if (key.type === "public") {
|
||||
extractable = true; // public keys are always extractable
|
||||
}
|
||||
assert_equals(key.extractable, extractable, "Extractability is correct");
|
||||
|
||||
assert_equals(key.algorithm.name, registeredAlgorithmName, "Correct algorithm name");
|
||||
@ -130,6 +127,10 @@ function assert_goodCryptoKey(key, algorithm, extractable, usages, kind) {
|
||||
assert_equals(key.algorithm.hash.name.toUpperCase(), algorithm.hash.toUpperCase(), "Correct hash function");
|
||||
}
|
||||
|
||||
if (/^(?:Ed|X)(?:25519|448)$/.test(key.algorithm.name)) {
|
||||
assert_false('namedCurve' in key.algorithm, "Does not have a namedCurve property");
|
||||
}
|
||||
|
||||
// usages is expected to be provided for a key pair, but we are checking
|
||||
// only a single key. The publicKey and privateKey portions of a key pair
|
||||
// recognize only some of the usages appropriate for a key pair.
|
||||
|
@ -7,12 +7,11 @@
|
||||
|
||||
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_test(function() {
|
||||
return Promise.all([generateWrappingKeys(), generateKeysToWrap(), generateEcdhPeerKey()])
|
||||
return Promise.all([generateWrappingKeys(), generateKeysToWrap()])
|
||||
.then(function(results) {
|
||||
var promises = [];
|
||||
wrappers.forEach(function(wrapper) {
|
||||
@ -83,6 +82,9 @@
|
||||
{algorithm: {name: "ECDSA", namedCurve: "P-256"}, privateUsages: ["sign"], publicUsages: ["verify"]},
|
||||
{algorithm: {name: "ECDH", namedCurve: "P-256"}, privateUsages: ["deriveBits"], publicUsages: []},
|
||||
{algorithm: {name: "Ed25519" }, privateUsages: ["sign"], publicUsages: ["verify"]},
|
||||
{algorithm: {name: "Ed448" }, privateUsages: ["sign"], publicUsages: ["verify"]},
|
||||
{algorithm: {name: "X25519" }, privateUsages: ["deriveBits"], publicUsages: []},
|
||||
{algorithm: {name: "X448" }, privateUsages: ["deriveBits"], publicUsages: []},
|
||||
{algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]},
|
||||
{algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]},
|
||||
{algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]},
|
||||
@ -112,13 +114,6 @@
|
||||
}));
|
||||
}
|
||||
|
||||
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) {
|
||||
var formats;
|
||||
@ -135,58 +130,72 @@
|
||||
var originalExport;
|
||||
return subtle.exportKey(fmt, toWrap.key).then(function(exportedKey) {
|
||||
originalExport = exportedKey;
|
||||
if (wrappingIsPossible(originalExport, wrapper.parameters.name)) {
|
||||
promise_test(function(test) {
|
||||
const isPossible = wrappingIsPossible(originalExport, wrapper.parameters.name);
|
||||
promise_test(function(test) {
|
||||
if (!isPossible) {
|
||||
return Promise.resolve().then(() => {
|
||||
assert_false(false, "Wrapping is not possible");
|
||||
})
|
||||
}
|
||||
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);
|
||||
|
||||
if (canCompareNonExtractableKeys(toWrap.key)) {
|
||||
promise_test(function(test){
|
||||
if (!isPossible) {
|
||||
return Promise.resolve().then(() => {
|
||||
assert_false(false, "Wrapping is not possible");
|
||||
})
|
||||
}
|
||||
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 + '"');
|
||||
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 using " + fmt + " and " + wrapper.parameters.name);
|
||||
}, "Can wrap and unwrap " + toWrap.name + " keys as non-extractable using " + fmt + " and " + wrapper.parameters.name);
|
||||
|
||||
if (canCompareNonExtractableKeys(toWrap.key)) {
|
||||
if (fmt === "jwk") {
|
||||
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);
|
||||
if (!isPossible) {
|
||||
return Promise.resolve().then(() => {
|
||||
assert_false(false, "Wrapping is not possible");
|
||||
})
|
||||
}
|
||||
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 result is non-extractable");
|
||||
return equalKeys(toWrap.key, 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 key unwrapped non-extractable threw an error - " + err.name + ': "' + err.message + '"');
|
||||
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 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);
|
||||
}
|
||||
}, "Can unwrap " + toWrap.name + " non-extractable keys using jwk and " + wrapper.parameters.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -386,6 +395,15 @@
|
||||
case "Ed25519" :
|
||||
signParams = {name: "Ed25519"};
|
||||
break;
|
||||
case "Ed448" :
|
||||
signParams = {name: "Ed448"};
|
||||
break;
|
||||
case "X25519" :
|
||||
deriveParams = {name: "X25519"};
|
||||
break;
|
||||
case "X448" :
|
||||
deriveParams = {name: "X448"};
|
||||
break;
|
||||
case "HMAC" :
|
||||
signParams = {name: "HMAC"};
|
||||
break;
|
||||
@ -393,7 +411,7 @@
|
||||
wrapParams = {name: "AES-KW"};
|
||||
break;
|
||||
case "ECDH" :
|
||||
deriveParams = {name: "ECDH", public: ecdhPeerKey};
|
||||
deriveParams = {name: "ECDH"};
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported algorithm for key comparison");
|
||||
@ -422,7 +440,7 @@
|
||||
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" || expected.algorithm.name === "Ed25519") {
|
||||
if (expected.algorithm.name === "ECDSA" || expected.algorithm.name.startsWith("Ed")) {
|
||||
delete jwkExpectedKey["d"];
|
||||
}
|
||||
jwkExpectedKey.key_ops = ["verify"];
|
||||
@ -446,9 +464,12 @@
|
||||
var wrappedWithGot = Array.from((new Uint8Array(wrapResult)).values());
|
||||
return wrappedWithGot.every((x,i) => x === wrappedWithExpected[i]);
|
||||
});
|
||||
} else {
|
||||
} else if (deriveParams) {
|
||||
var expectedDerivedBits;
|
||||
return subtle.deriveBits(deriveParams, expected, 128)
|
||||
return subtle.generateKey(expected.algorithm, true, ['deriveBits']).then(({ publicKey }) => {
|
||||
deriveParams.public = publicKey;
|
||||
return subtle.deriveBits(deriveParams, expected, 128)
|
||||
})
|
||||
.then(function(result){
|
||||
expectedDerivedBits = Array.from((new Uint8Array(result)).values());
|
||||
return subtle.deriveBits(deriveParams, got, 128);
|
||||
|
2
test/fixtures/wpt/versions.json
vendored
2
test/fixtures/wpt/versions.json
vendored
@ -88,7 +88,7 @@
|
||||
"path": "wasm/webapi"
|
||||
},
|
||||
"WebCryptoAPI": {
|
||||
"commit": "450f829d2567ed9c44bd9b10f7e8f34a2ad15315",
|
||||
"commit": "238d9d9bac54d4f1ae8844fc8dd4389b1ad99b4e",
|
||||
"path": "WebCryptoAPI"
|
||||
},
|
||||
"webidl/ecmascript-binding/es-exceptions": {
|
||||
|
Loading…
Reference in New Issue
Block a user