Source code
Revision control
Copy as Markdown
Other Tools
function registerDeriveTests(options) {
const {
operation,
algorithmName,
testName = algorithmName,
mixedCaseName,
size,
derivation,
privateKey,
publicKey,
noUsagePrivateKey,
invalidPublicKeys,
missingPublicLabel,
} = options;
const subtle = self.crypto.subtle;
const isDeriveBits = operation === "deriveBits";
const derivedKeyAlgorithm = {name: "HMAC", hash: "SHA-256", length: 256};
const derivedKeyUsages = ["sign", "verify"];
function derive(name, publicKey, baseKey, length = 8 * size, keyOptions = {}) {
const algorithm = {name, public: publicKey};
if (isDeriveBits) {
return subtle.deriveBits(algorithm, baseKey, length);
}
return subtle.deriveKey(
algorithm,
baseKey,
keyOptions.algorithm || derivedKeyAlgorithm,
true,
keyOptions.usages || derivedKeyUsages
).then(key => subtle.exportKey("raw", key));
}
function assertDerived(result, length) {
if (isDeriveBits) {
assert_true(equalBuffers(result, derivation, length), "Derived correct bits");
} else {
assert_array_equals(
new Uint8Array(result),
derivation.slice(0, 32),
"Derived correct key"
);
}
}
function successTest(name, algorithm, length = 8 * size) {
promise_test(() => {
return derive(algorithm, publicKey, privateKey, length).then(
result => assertDerived(result, length),
error => assert_unreached(
operation + " failed with error " + error.name + ": " + error.message
)
);
}, name);
}
function failureTest(name, expectedError, deriveOperation) {
promise_test(() => {
return Promise.resolve().then(deriveOperation).then(
() => assert_unreached(
operation + " succeeded but should have failed with " + expectedError
),
error => assert_equals(
error.name,
expectedError,
"Should throw correct error, not " + error.name + ": " + error.message
)
);
}, name);
}
successTest(testName + " good parameters", algorithmName);
successTest(testName + " mixed case parameters", mixedCaseName);
if (isDeriveBits) {
successTest(testName + " short result", algorithmName, 8 * size - 32);
successTest(
testName + " non-multiple of 8 bits",
algorithmName,
8 * size - 11
);
}
failureTest(missingPublicLabel, "TypeError", () => {
return subtle[operation](
{name: algorithmName},
privateKey,
...(isDeriveBits
? [8 * size]
: [derivedKeyAlgorithm, true, derivedKeyUsages])
);
});
failureTest(
testName + " public property of algorithm is not a CryptoKey",
"TypeError",
() => derive(algorithmName, {message: "Not a CryptoKey"}, privateKey)
);
invalidPublicKeys.forEach(test => {
failureTest(
test.name,
"InvalidAccessError",
() => derive(algorithmName, test.key, privateKey)
);
});
failureTest(
testName + " no " + operation + " usage for base key",
"InvalidAccessError",
() => derive(algorithmName, publicKey, noUsagePrivateKey)
);
failureTest(
testName + " base key is not a private key",
"InvalidAccessError",
() => derive(algorithmName, publicKey, publicKey)
);
failureTest(
testName + " public property value is a private key",
"InvalidAccessError",
() => derive(algorithmName, privateKey, privateKey)
);
promise_test(async() => {
const secretKey = isDeriveBits
? await subtle.generateKey(
{name: "AES-CBC", length: 128},
true,
["encrypt", "decrypt"]
)
: await subtle.generateKey(
derivedKeyAlgorithm,
true,
derivedKeyUsages
);
const keyOptions = isDeriveBits ? {} : {
algorithm: {name: "AES-CBC", length: 256},
usages: ["sign", "verify"],
};
return derive(
algorithmName,
secretKey,
privateKey,
8 * size,
keyOptions
).then(
() => assert_unreached(
operation + " succeeded but should have failed with InvalidAccessError"
),
error => assert_equals(
error.name,
"InvalidAccessError",
"Should throw correct error, not " + error.name + ": " + error.message
)
);
}, testName + " public property value is a secret key");
if (isDeriveBits) {
failureTest(
testName + " asking for too many bits",
"OperationError",
() => derive(algorithmName, publicKey, privateKey, 8 * size + 8)
);
}
}