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