1function define_tests() { 2 // May want to test prefixed implementations. 3 var subtle = self.crypto.subtle; 4 5 // pbkdf2_vectors sets up test data with the correct derivations for each 6 // test case. 7 var testData = getTestData(); 8 var passwords = testData.passwords; 9 var salts = testData.salts; 10 var derivations = testData.derivations; 11 12 // What kinds of keys can be created with deriveKey? The following: 13 var derivedKeyTypes = testData.derivedKeyTypes; 14 15 return setUpBaseKeys(passwords) 16 .then(function(allKeys) { 17 // We get several kinds of base keys. Normal ones that can be used for 18 // derivation operations, ones that lack the deriveBits usage, ones 19 // that lack the deriveKeys usage, and one key that is for the wrong 20 // algorithm (not PBKDF2 in this case). 21 var baseKeys = allKeys.baseKeys; 22 var noBits = allKeys.noBits; 23 var noKey = allKeys.noKey; 24 var wrongKey = allKeys.wrongKey; 25 26 // Test each combination of password size, salt size, hash function, 27 // and number of iterations. The derivations object is structured in 28 // that way, so navigate it to run tests and compare with correct results. 29 Object.keys(derivations).forEach(function(passwordSize) { 30 Object.keys(derivations[passwordSize]).forEach(function(saltSize) { 31 Object.keys(derivations[passwordSize][saltSize]).forEach(function(hashName) { 32 Object.keys(derivations[passwordSize][saltSize][hashName]).forEach(function(iterations) { 33 var testName = passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations"; 34 35 // Check for correct deriveBits result 36 subsetTest(promise_test, function(test) { 37 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256) 38 .then(function(derivation) { 39 assert_true(equalBuffers(derivation, derivations[passwordSize][saltSize][hashName][iterations]), "Derived correct key"); 40 }, function(err) { 41 assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); 42 }); 43 }, testName); 44 45 // Check for correct deriveKey results for every kind of 46 // key that can be created by the deriveKeys operation. 47 derivedKeyTypes.forEach(function(derivedKeyType) { 48 var testName = "Derived key of type "; 49 Object.keys(derivedKeyType.algorithm).forEach(function(prop) { 50 testName += prop + ": " + derivedKeyType.algorithm[prop] + " "; 51 }); 52 testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations"; 53 54 // Test the particular key derivation. 55 subsetTest(promise_test, function(test) { 56 return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) 57 .then(function(key) { 58 // Need to export the key to see that the correct bits were set. 59 return subtle.exportKey("raw", key) 60 .then(function(buffer) { 61 assert_true(equalBuffers(buffer, derivations[passwordSize][saltSize][hashName][iterations].slice(0, derivedKeyType.algorithm.length/8)), "Exported key matches correct value"); 62 }, function(err) { 63 assert_unreached("Exporting derived key failed with error " + err.name + ": " + err.message); 64 }); 65 }, function(err) { 66 assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); 67 68 }); 69 }, testName); 70 71 // Test various error conditions for deriveKey: 72 73 // - illegal name for hash algorithm (NotSupportedError) 74 var badHash = hashName.substring(0, 3) + hashName.substring(4); 75 subsetTest(promise_test, function(test) { 76 return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) 77 .then(function(key) { 78 assert_unreached("bad hash name should have thrown an NotSupportedError"); 79 }, function(err) { 80 assert_equals(err.name, "NotSupportedError", "deriveKey with bad hash name correctly threw NotSupportedError: " + err.message); 81 }); 82 }, testName + " with bad hash name " + badHash); 83 84 // - baseKey usages missing "deriveKey" (InvalidAccessError) 85 subsetTest(promise_test, function(test) { 86 return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noKey[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) 87 .then(function(key) { 88 assert_unreached("missing deriveKey usage should have thrown an InvalidAccessError"); 89 }, function(err) { 90 assert_equals(err.name, "InvalidAccessError", "deriveKey with missing deriveKey usage correctly threw InvalidAccessError: " + err.message); 91 }); 92 }, testName + " with missing deriveKey usage"); 93 94 // - baseKey algorithm does not match PBKDF2 (InvalidAccessError) 95 subsetTest(promise_test, function(test) { 96 return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, derivedKeyType.algorithm, true, derivedKeyType.usages) 97 .then(function(key) { 98 assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError"); 99 }, function(err) { 100 assert_equals(err.name, "InvalidAccessError", "deriveKey with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message); 101 }); 102 }, testName + " with wrong (ECDH) key"); 103 104 }); 105 106 // Test various error conditions for deriveBits below: 107 // length null (OperationError) 108 subsetTest(promise_test, function(test) { 109 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], null) 110 .then(function(derivation) { 111 assert_unreached("null length should have thrown an OperationError"); 112 }, function(err) { 113 assert_equals(err.name, "OperationError", "deriveBits with null length correctly threw OperationError: " + err.message); 114 }); 115 }, testName + " with null length"); 116 117 // 0 length (OperationError) 118 subsetTest(promise_test, function(test) { 119 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 0) 120 .then(function(derivation) { 121 assert_unreached("0 length should have thrown an OperationError"); 122 }, function(err) { 123 assert_equals(err.name, "OperationError", "deriveBits with 0 length correctly threw OperationError: " + err.message); 124 }); 125 }, testName + " with 0 length"); 126 127 // length not multiple of 8 (OperationError) 128 subsetTest(promise_test, function(test) { 129 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 44) 130 .then(function(derivation) { 131 assert_unreached("non-multiple of 8 length should have thrown an OperationError"); 132 }, function(err) { 133 assert_equals(err.name, "OperationError", "deriveBits with non-multiple of 8 length correctly threw OperationError: " + err.message); 134 }); 135 }, testName + " with non-multiple of 8 length"); 136 137 // - illegal name for hash algorithm (NotSupportedError) 138 var badHash = hashName.substring(0, 3) + hashName.substring(4); 139 subsetTest(promise_test, function(test) { 140 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256) 141 .then(function(derivation) { 142 assert_unreached("bad hash name should have thrown an NotSupportedError"); 143 }, function(err) { 144 assert_equals(err.name, "NotSupportedError", "deriveBits with bad hash name correctly threw NotSupportedError: " + err.message); 145 }); 146 }, testName + " with bad hash name " + badHash); 147 148 // - baseKey usages missing "deriveBits" (InvalidAccessError) 149 subsetTest(promise_test, function(test) { 150 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noBits[passwordSize], 256) 151 .then(function(derivation) { 152 assert_unreached("missing deriveBits usage should have thrown an InvalidAccessError"); 153 }, function(err) { 154 assert_equals(err.name, "InvalidAccessError", "deriveBits with missing deriveBits usage correctly threw InvalidAccessError: " + err.message); 155 }); 156 }, testName + " with missing deriveBits usage"); 157 158 // - baseKey algorithm does not match PBKDF2 (InvalidAccessError) 159 subsetTest(promise_test, function(test) { 160 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, 256) 161 .then(function(derivation) { 162 assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError"); 163 }, function(err) { 164 assert_equals(err.name, "InvalidAccessError", "deriveBits with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message); 165 }); 166 }, testName + " with wrong (ECDH) key"); 167 }); 168 169 // Check that 0 iterations throws proper error 170 subsetTest(promise_test, function(test) { 171 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], 256) 172 .then(function(derivation) { 173 assert_unreached("0 iterations should have thrown an error"); 174 }, function(err) { 175 assert_equals(err.name, "OperationError", "deriveBits with 0 iterations correctly threw OperationError: " + err.message); 176 }); 177 }, passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations"); 178 179 derivedKeyTypes.forEach(function(derivedKeyType) { 180 var testName = "Derived key of type "; 181 Object.keys(derivedKeyType.algorithm).forEach(function(prop) { 182 testName += prop + ": " + derivedKeyType.algorithm[prop] + " "; 183 }); 184 testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations"; 185 186 subsetTest(promise_test, function(test) { 187 return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) 188 .then(function(derivation) { 189 assert_unreached("0 iterations should have thrown an error"); 190 }, function(err) { 191 assert_equals(err.name, "OperationError", "derivekey with 0 iterations correctly threw OperationError: " + err.message); 192 }); 193 }, testName); 194 }); 195 }); 196 197 // - legal algorithm name but not digest one (e.g., PBKDF2) (NotSupportedError) 198 var nonDigestHash = "PBKDF2"; 199 [1, 1000, 100000].forEach(function(iterations) { 200 var testName = passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations"; 201 202 subsetTest(promise_test, function(test) { 203 return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256) 204 .then(function(derivation) { 205 assert_unreached("non-digest algorithm should have thrown an NotSupportedError"); 206 }, function(err) { 207 assert_equals(err.name, "NotSupportedError", "deriveBits with non-digest algorithm correctly threw NotSupportedError: " + err.message); 208 }); 209 }, testName + " with non-digest algorithm " + nonDigestHash); 210 211 derivedKeyTypes.forEach(function(derivedKeyType) { 212 var testName = "Derived key of type "; 213 Object.keys(derivedKeyType.algorithm).forEach(function(prop) { 214 testName += prop + ": " + derivedKeyType.algorithm[prop] + " "; 215 }); 216 testName += " using " + passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations"; 217 218 subsetTest(promise_test, function(test) { 219 return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) 220 .then(function(derivation) { 221 assert_unreached("non-digest algorithm should have thrown an NotSupportedError"); 222 }, function(err) { 223 assert_equals(err.name, "NotSupportedError", "derivekey with non-digest algorithm correctly threw NotSupportedError: " + err.message); 224 }); 225 }, testName); 226 }); 227 228 }); 229 230 }); 231 }); 232 }); 233 234 // Deriving bits and keys requires starting with a base key, which is created 235 // by importing a password. setUpBaseKeys returns a promise that yields the 236 // necessary base keys. 237 function setUpBaseKeys(passwords) { 238 var promises = []; 239 240 var baseKeys = {}; 241 var noBits = {}; 242 var noKey = {}; 243 var wrongKey = null; 244 245 Object.keys(passwords).forEach(function(passwordSize) { 246 var promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey", "deriveBits"]) 247 .then(function(baseKey) { 248 baseKeys[passwordSize] = baseKey; 249 }, function(err) { 250 baseKeys[passwordSize] = null; 251 }); 252 promises.push(promise); 253 254 promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveBits"]) 255 .then(function(baseKey) { 256 noKey[passwordSize] = baseKey; 257 }, function(err) { 258 noKey[passwordSize] = null; 259 }); 260 promises.push(promise); 261 262 promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey"]) 263 .then(function(baseKey) { 264 noBits[passwordSize] = baseKey; 265 }, function(err) { 266 noBits[passwordSize] = null; 267 }); 268 promises.push(promise); 269 }); 270 271 var promise = subtle.generateKey({name: "ECDH", namedCurve: "P-256"}, false, ["deriveKey", "deriveBits"]) 272 .then(function(baseKey) { 273 wrongKey = baseKey.privateKey; 274 }, function(err) { 275 wrongKey = null; 276 }); 277 promises.push(promise); 278 279 280 return Promise.all(promises).then(function() { 281 return {baseKeys: baseKeys, noBits: noBits, noKey: noKey, wrongKey: wrongKey}; 282 }); 283 } 284 285 function equalBuffers(a, b) { 286 if (a.byteLength !== b.byteLength) { 287 return false; 288 } 289 290 var aBytes = new Uint8Array(a); 291 var bBytes = new Uint8Array(b); 292 293 for (var i=0; i<a.byteLength; i++) { 294 if (aBytes[i] !== bBytes[i]) { 295 return false; 296 } 297 } 298 299 return true; 300 } 301 302} 303