1 2function run_test() { 3 var subtle = self.crypto.subtle; // Change to test prefixed implementations 4 5 // When are all these tests really done? When all the promises they use have resolved. 6 var all_promises = []; 7 8 // Source file aes_XXX_vectors.js provides the getTestVectors method 9 // for the AES-XXX algorithm that drives these tests. 10 var vectors = getTestVectors(); 11 var passingVectors = vectors.passing; 12 var failingVectors = vectors.failing; 13 var decryptionFailingVectors = vectors.decryptionFailing; 14 15 // Check for successful encryption. 16 passingVectors.forEach(function(vector) { 17 var promise = importVectorKey(vector, ["encrypt", "decrypt"]) 18 .then(function(vector) { 19 promise_test(function(test) { 20 return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext) 21 .then(function(result) { 22 assert_true(equalBuffers(result, vector.result), "Should return expected result"); 23 }, function(err) { 24 assert_unreached("encrypt error for test " + vector.name + ": " + err.message); 25 }); 26 }, vector.name); 27 }, function(err) { 28 // We need a failed test if the importVectorKey operation fails, so 29 // we know we never tested encryption 30 promise_test(function(test) { 31 assert_unreached("importKey failed for " + vector.name); 32 }, "importKey step: " + vector.name); 33 }); 34 35 all_promises.push(promise); 36 }); 37 38 // Check for successful encryption even if the buffer is changed after calling encrypt. 39 passingVectors.forEach(function(vector) { 40 var plaintext = copyBuffer(vector.plaintext); 41 var promise = importVectorKey(vector, ["encrypt", "decrypt"]) 42 .then(function(vector) { 43 promise_test(function(test) { 44 var operation = subtle.encrypt(vector.algorithm, vector.key, plaintext) 45 .then(function(result) { 46 assert_true(equalBuffers(result, vector.result), "Should return expected result"); 47 }, function(err) { 48 assert_unreached("encrypt error for test " + vector.name + ": " + err.message); 49 }); 50 plaintext[0] = 255 - plaintext[0]; 51 return operation; 52 }, vector.name + " with altered plaintext"); 53 }, function(err) { 54 // We need a failed test if the importVectorKey operation fails, so 55 // we know we never tested encryption 56 promise_test(function(test) { 57 assert_unreached("importKey failed for " + vector.name); 58 }, "importKey step: " + vector.name + " with altered plaintext"); 59 }); 60 61 all_promises.push(promise); 62 }); 63 64 // Check for successful decryption. 65 passingVectors.forEach(function(vector) { 66 var promise = importVectorKey(vector, ["encrypt", "decrypt"]) 67 .then(function(vector) { 68 promise_test(function(test) { 69 return subtle.decrypt(vector.algorithm, vector.key, vector.result) 70 .then(function(result) { 71 assert_true(equalBuffers(result, vector.plaintext), "Should return expected result"); 72 }, function(err) { 73 assert_unreached("decrypt error for test " + vector.name + ": " + err.message); 74 }); 75 }, vector.name + " decryption"); 76 }, function(err) { 77 // We need a failed test if the importVectorKey operation fails, so 78 // we know we never tested encryption 79 promise_test(function(test) { 80 assert_unreached("importKey failed for " + vector.name); 81 }, "importKey step for decryption: " + vector.name); 82 }); 83 84 all_promises.push(promise); 85 }); 86 87 // Check for successful decryption even if ciphertext is altered. 88 passingVectors.forEach(function(vector) { 89 var ciphertext = copyBuffer(vector.result); 90 var promise = importVectorKey(vector, ["encrypt", "decrypt"]) 91 .then(function(vector) { 92 promise_test(function(test) { 93 var operation = subtle.decrypt(vector.algorithm, vector.key, ciphertext) 94 .then(function(result) { 95 assert_true(equalBuffers(result, vector.plaintext), "Should return expected result"); 96 }, function(err) { 97 assert_unreached("decrypt error for test " + vector.name + ": " + err.message); 98 }); 99 ciphertext[0] = 255 - ciphertext[0]; 100 return operation; 101 }, vector.name + " decryption with altered ciphertext"); 102 }, function(err) { 103 // We need a failed test if the importVectorKey operation fails, so 104 // we know we never tested encryption 105 promise_test(function(test) { 106 assert_unreached("importKey failed for " + vector.name); 107 }, "importKey step for decryption: " + vector.name + " with altered ciphertext"); 108 }); 109 110 all_promises.push(promise); 111 }); 112 113 // Everything that succeeded should fail if no "encrypt" usage. 114 passingVectors.forEach(function(vector) { 115 // Don't want to overwrite key being used for success tests! 116 var badVector = Object.assign({}, vector); 117 badVector.key = null; 118 119 var promise = importVectorKey(badVector, ["decrypt"]) 120 .then(function(vector) { 121 promise_test(function(test) { 122 return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext) 123 .then(function(result) { 124 assert_unreached("should have thrown exception for test " + vector.name); 125 }, function(err) { 126 assert_equals(err.name, "InvalidAccessError", "Should throw an InvalidAccessError instead of " + err.message) 127 }); 128 }, vector.name + " without encrypt usage"); 129 }, function(err) { 130 // We need a failed test if the importVectorKey operation fails, so 131 // we know we never tested encryption 132 promise_test(function(test) { 133 assert_unreached("importKey failed for " + vector.name); 134 }, "importKey step: " + vector.name + " without encrypt usage"); 135 }); 136 137 all_promises.push(promise); 138 }); 139 140 // Encryption should fail if algorithm of key doesn't match algorithm of function call. 141 passingVectors.forEach(function(vector) { 142 var algorithm = Object.assign({}, vector.algorithm); 143 if (algorithm.name === "AES-CBC") { 144 algorithm.name = "AES-CTR"; 145 algorithm.counter = new Uint8Array(16); 146 algorithm.length = 64; 147 } else { 148 algorithm.name = "AES-CBC"; 149 algorithm.iv = new Uint8Array(16); // Need syntactically valid parameter to get to error being checked. 150 } 151 152 var promise = importVectorKey(vector, ["encrypt", "decrypt"]) 153 .then(function(vector) { 154 promise_test(function(test) { 155 return subtle.encrypt(algorithm, vector.key, vector.plaintext) 156 .then(function(result) { 157 assert_unreached("encrypt succeeded despite mismatch " + vector.name + ": " + err.message); 158 }, function(err) { 159 assert_equals(err.name, "InvalidAccessError", "Mismatch should cause InvalidAccessError instead of " + err.message); 160 }); 161 }, vector.name + " with mismatched key and algorithm"); 162 }, function(err) { 163 // We need a failed test if the importVectorKey operation fails, so 164 // we know we never tested encryption 165 promise_test(function(test) { 166 assert_unreached("importKey failed for " + vector.name); 167 }, "importKey step: " + vector.name + " with mismatched key and algorithm"); 168 }); 169 170 all_promises.push(promise); 171 }); 172 173 // Everything that succeeded decrypting should fail if no "decrypt" usage. 174 passingVectors.forEach(function(vector) { 175 // Don't want to overwrite key being used for success tests! 176 var badVector = Object.assign({}, vector); 177 badVector.key = null; 178 179 var promise = importVectorKey(badVector, ["encrypt"]) 180 .then(function(vector) { 181 promise_test(function(test) { 182 return subtle.decrypt(vector.algorithm, vector.key, vector.result) 183 .then(function(result) { 184 assert_unreached("should have thrown exception for test " + vector.name); 185 }, function(err) { 186 assert_equals(err.name, "InvalidAccessError", "Should throw an InvalidAccessError instead of " + err.message) 187 }); 188 }, vector.name + " without decrypt usage"); 189 }, function(err) { 190 // We need a failed test if the importVectorKey operation fails, so 191 // we know we never tested encryption 192 promise_test(function(test) { 193 assert_unreached("importKey failed for " + vector.name); 194 }, "importKey step: " + vector.name + " without decrypt usage"); 195 }); 196 197 all_promises.push(promise); 198 }); 199 200 // Check for OperationError due to data lengths. 201 failingVectors.forEach(function(vector) { 202 var promise = importVectorKey(vector, ["encrypt", "decrypt"]) 203 .then(function(vector) { 204 promise_test(function(test) { 205 return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext) 206 .then(function(result) { 207 assert_unreached("should have thrown exception for test " + vector.name); 208 }, function(err) { 209 assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message) 210 }); 211 }, vector.name); 212 }, function(err) { 213 // We need a failed test if the importVectorKey operation fails, so 214 // we know we never tested encryption 215 promise_test(function(test) { 216 assert_unreached("importKey failed for " + vector.name); 217 }, "importKey step: " + vector.name); 218 }); 219 220 all_promises.push(promise); 221 }); 222 223 // Check for OperationError due to data lengths for decryption, too. 224 failingVectors.forEach(function(vector) { 225 var promise = importVectorKey(vector, ["encrypt", "decrypt"]) 226 .then(function(vector) { 227 promise_test(function(test) { 228 return subtle.decrypt(vector.algorithm, vector.key, vector.result) 229 .then(function(result) { 230 assert_unreached("should have thrown exception for test " + vector.name); 231 }, function(err) { 232 assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message) 233 }); 234 }, vector.name + " decryption"); 235 }, function(err) { 236 // We need a failed test if the importVectorKey operation fails, so 237 // we know we never tested encryption 238 promise_test(function(test) { 239 assert_unreached("importKey failed for " + vector.name); 240 }, "importKey step: decryption " + vector.name); 241 }); 242 243 all_promises.push(promise); 244 }); 245 246 // Check for decryption failing for algorithm-specific reasons (such as bad 247 // padding for AES-CBC). 248 decryptionFailingVectors.forEach(function(vector) { 249 var promise = importVectorKey(vector, ["encrypt", "decrypt"]) 250 .then(function(vector) { 251 promise_test(function(test) { 252 return subtle.decrypt(vector.algorithm, vector.key, vector.result) 253 .then(function(result) { 254 assert_unreached("should have thrown exception for test " + vector.name); 255 }, function(err) { 256 assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message) 257 }); 258 }, vector.name); 259 }, function(err) { 260 // We need a failed test if the importVectorKey operation fails, so 261 // we know we never tested encryption 262 promise_test(function(test) { 263 assert_unreached("importKey failed for " + vector.name); 264 }, "importKey step: decryption " + vector.name); 265 }); 266 267 all_promises.push(promise); 268 }); 269 270 promise_test(function() { 271 return Promise.all(all_promises) 272 .then(function() {done();}) 273 .catch(function() {done();}) 274 }, "setup"); 275 276 // A test vector has all needed fields for encryption, EXCEPT that the 277 // key field may be null. This function replaces that null with the Correct 278 // CryptoKey object. 279 // 280 // Returns a Promise that yields an updated vector on success. 281 function importVectorKey(vector, usages) { 282 if (vector.key !== null) { 283 return new Promise(function(resolve, reject) { 284 resolve(vector); 285 }); 286 } else { 287 return subtle.importKey("raw", vector.keyBuffer, {name: vector.algorithm.name}, false, usages) 288 .then(function(key) { 289 vector.key = key; 290 return vector; 291 }); 292 } 293 } 294 295 // Returns a copy of the sourceBuffer it is sent. 296 function copyBuffer(sourceBuffer) { 297 var source = new Uint8Array(sourceBuffer); 298 var copy = new Uint8Array(sourceBuffer.byteLength) 299 300 for (var i=0; i<source.byteLength; i++) { 301 copy[i] = source[i]; 302 } 303 304 return copy; 305 } 306 307 function equalBuffers(a, b) { 308 if (a.byteLength !== b.byteLength) { 309 return false; 310 } 311 312 var aBytes = new Uint8Array(a); 313 var bBytes = new Uint8Array(b); 314 315 for (var i=0; i<a.byteLength; i++) { 316 if (aBytes[i] !== bBytes[i]) { 317 return false; 318 } 319 } 320 321 return true; 322 } 323 324 return; 325} 326