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