blob: 0403f382e1479c7c5018e91aba2ceb31b89998dc [file] [log] [blame]
function define_tests() {
// May want to test prefixed implementations.
var subtle = self.crypto.subtle;
// pbkdf2_vectors sets up test data with the correct derivations for each
// test case.
var testData = getTestData();
var passwords = testData.passwords;
var salts = testData.salts;
var derivations = testData.derivations;
// What kinds of keys can be created with deriveKey? The following:
var derivedKeyTypes = testData.derivedKeyTypes;
return setUpBaseKeys(passwords)
.then(function(allKeys) {
// We get several kinds of base keys. Normal ones that can be used for
// derivation operations, ones that lack the deriveBits usage, ones
// that lack the deriveKeys usage, and one key that is for the wrong
// algorithm (not PBKDF2 in this case).
var baseKeys = allKeys.baseKeys;
var noBits = allKeys.noBits;
var noKey = allKeys.noKey;
var wrongKey = allKeys.wrongKey;
// Test each combination of password size, salt size, hash function,
// and number of iterations. The derivations object is structured in
// that way, so navigate it to run tests and compare with correct results.
Object.keys(derivations).forEach(function(passwordSize) {
Object.keys(derivations[passwordSize]).forEach(function(saltSize) {
Object.keys(derivations[passwordSize][saltSize]).forEach(function(hashName) {
Object.keys(derivations[passwordSize][saltSize][hashName]).forEach(function(iterations) {
var testName = passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations";
// Check for correct deriveBits result
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
.then(function(derivation) {
assert_true(equalBuffers(derivation, derivations[passwordSize][saltSize][hashName][iterations]), "Derived correct key");
}, function(err) {
assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
});
}, testName);
// Check for correct deriveKey results for every kind of
// key that can be created by the deriveKeys operation.
derivedKeyTypes.forEach(function(derivedKeyType) {
var testName = "Derived key of type ";
Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
});
testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations";
// Test the particular key derivation.
subsetTest(promise_test, function(test) {
return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
.then(function(key) {
// Need to export the key to see that the correct bits were set.
return subtle.exportKey("raw", key)
.then(function(buffer) {
assert_true(equalBuffers(buffer, derivations[passwordSize][saltSize][hashName][iterations].slice(0, derivedKeyType.algorithm.length/8)), "Exported key matches correct value");
}, function(err) {
assert_unreached("Exporting derived key failed with error " + err.name + ": " + err.message);
});
}, function(err) {
assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
});
}, testName);
// Test various error conditions for deriveKey:
// - illegal name for hash algorithm (NotSupportedError)
var badHash = hashName.substring(0, 3) + hashName.substring(4);
subsetTest(promise_test, function(test) {
return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
.then(function(key) {
assert_unreached("bad hash name should have thrown an NotSupportedError");
}, function(err) {
assert_equals(err.name, "NotSupportedError", "deriveKey with bad hash name correctly threw NotSupportedError: " + err.message);
});
}, testName + " with bad hash name " + badHash);
// - baseKey usages missing "deriveKey" (InvalidAccessError)
subsetTest(promise_test, function(test) {
return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noKey[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
.then(function(key) {
assert_unreached("missing deriveKey usage should have thrown an InvalidAccessError");
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "deriveKey with missing deriveKey usage correctly threw InvalidAccessError: " + err.message);
});
}, testName + " with missing deriveKey usage");
// - baseKey algorithm does not match PBKDF2 (InvalidAccessError)
subsetTest(promise_test, function(test) {
return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, derivedKeyType.algorithm, true, derivedKeyType.usages)
.then(function(key) {
assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "deriveKey with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
});
}, testName + " with wrong (ECDH) key");
});
// Test various error conditions for deriveBits below:
// length null (OperationError)
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], null)
.then(function(derivation) {
assert_unreached("null length should have thrown an OperationError");
}, function(err) {
assert_equals(err.name, "OperationError", "deriveBits with null length correctly threw OperationError: " + err.message);
});
}, testName + " with null length");
// 0 length (OperationError)
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 0)
.then(function(derivation) {
assert_unreached("0 length should have thrown an OperationError");
}, function(err) {
assert_equals(err.name, "OperationError", "deriveBits with 0 length correctly threw OperationError: " + err.message);
});
}, testName + " with 0 length");
// length not multiple of 8 (OperationError)
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 44)
.then(function(derivation) {
assert_unreached("non-multiple of 8 length should have thrown an OperationError");
}, function(err) {
assert_equals(err.name, "OperationError", "deriveBits with non-multiple of 8 length correctly threw OperationError: " + err.message);
});
}, testName + " with non-multiple of 8 length");
// - illegal name for hash algorithm (NotSupportedError)
var badHash = hashName.substring(0, 3) + hashName.substring(4);
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
.then(function(derivation) {
assert_unreached("bad hash name should have thrown an NotSupportedError");
}, function(err) {
assert_equals(err.name, "NotSupportedError", "deriveBits with bad hash name correctly threw NotSupportedError: " + err.message);
});
}, testName + " with bad hash name " + badHash);
// - baseKey usages missing "deriveBits" (InvalidAccessError)
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noBits[passwordSize], 256)
.then(function(derivation) {
assert_unreached("missing deriveBits usage should have thrown an InvalidAccessError");
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "deriveBits with missing deriveBits usage correctly threw InvalidAccessError: " + err.message);
});
}, testName + " with missing deriveBits usage");
// - baseKey algorithm does not match PBKDF2 (InvalidAccessError)
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, 256)
.then(function(derivation) {
assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
}, function(err) {
assert_equals(err.name, "InvalidAccessError", "deriveBits with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
});
}, testName + " with wrong (ECDH) key");
});
// Check that 0 iterations throws proper error
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], 256)
.then(function(derivation) {
assert_unreached("0 iterations should have thrown an error");
}, function(err) {
assert_equals(err.name, "OperationError", "deriveBits with 0 iterations correctly threw OperationError: " + err.message);
});
}, passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations");
derivedKeyTypes.forEach(function(derivedKeyType) {
var testName = "Derived key of type ";
Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
});
testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations";
subsetTest(promise_test, function(test) {
return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
.then(function(derivation) {
assert_unreached("0 iterations should have thrown an error");
}, function(err) {
assert_equals(err.name, "OperationError", "derivekey with 0 iterations correctly threw OperationError: " + err.message);
});
}, testName);
});
});
// - legal algorithm name but not digest one (e.g., PBKDF2) (NotSupportedError)
var nonDigestHash = "PBKDF2";
[1, 1000, 100000].forEach(function(iterations) {
var testName = passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations";
subsetTest(promise_test, function(test) {
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
.then(function(derivation) {
assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
}, function(err) {
assert_equals(err.name, "NotSupportedError", "deriveBits with non-digest algorithm correctly threw NotSupportedError: " + err.message);
});
}, testName + " with non-digest algorithm " + nonDigestHash);
derivedKeyTypes.forEach(function(derivedKeyType) {
var testName = "Derived key of type ";
Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
});
testName += " using " + passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations";
subsetTest(promise_test, function(test) {
return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
.then(function(derivation) {
assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
}, function(err) {
assert_equals(err.name, "NotSupportedError", "derivekey with non-digest algorithm correctly threw NotSupportedError: " + err.message);
});
}, testName);
});
});
});
});
});
// Deriving bits and keys requires starting with a base key, which is created
// by importing a password. setUpBaseKeys returns a promise that yields the
// necessary base keys.
function setUpBaseKeys(passwords) {
var promises = [];
var baseKeys = {};
var noBits = {};
var noKey = {};
var wrongKey = null;
Object.keys(passwords).forEach(function(passwordSize) {
var promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey", "deriveBits"])
.then(function(baseKey) {
baseKeys[passwordSize] = baseKey;
}, function(err) {
baseKeys[passwordSize] = null;
});
promises.push(promise);
promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveBits"])
.then(function(baseKey) {
noKey[passwordSize] = baseKey;
}, function(err) {
noKey[passwordSize] = null;
});
promises.push(promise);
promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey"])
.then(function(baseKey) {
noBits[passwordSize] = baseKey;
}, function(err) {
noBits[passwordSize] = null;
});
promises.push(promise);
});
var promise = subtle.generateKey({name: "ECDH", namedCurve: "P-256"}, false, ["deriveKey", "deriveBits"])
.then(function(baseKey) {
wrongKey = baseKey.privateKey;
}, function(err) {
wrongKey = null;
});
promises.push(promise);
return Promise.all(promises).then(function() {
return {baseKeys: baseKeys, noBits: noBits, noKey: noKey, wrongKey: wrongKey};
});
}
function equalBuffers(a, b) {
if (a.byteLength !== b.byteLength) {
return false;
}
var aBytes = new Uint8Array(a);
var bBytes = new Uint8Array(b);
for (var i=0; i<a.byteLength; i++) {
if (aBytes[i] !== bBytes[i]) {
return false;
}
}
return true;
}
}