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 rsa_vectors.js provides the getTestVectors method 9 // for the RSA-OAEP algorithm that drives these tests. 10 var vectors = getTestVectors(); 11 var passingVectors = vectors.passing; 12 var failingVectors = vectors.failing; 13 14 // Test decryption, first, because encryption tests rely on that working 15 passingVectors.forEach(function(vector) { 16 var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) 17 .then(function(vectors) { 18 // Get a one byte longer plaintext to encrypt 19 if (!("ciphertext" in vector)) { 20 return; 21 } 22 23 promise_test(function(test) { 24 return subtle.decrypt(vector.algorithm, vector.privateKey, vector.ciphertext) 25 .then(function(plaintext) { 26 assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works")); 27 }, function(err) { 28 assert_unreached("Decryption should not throw error " + vector.name + ": " + err.message + "'"); 29 }); 30 }, vector.name + " decryption"); 31 32 }, function(err) { 33 // We need a failed test if the importVectorKey operation fails, so 34 // we know we never tested encryption 35 promise_test(function(test) { 36 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 37 }, "importVectorKeys step: " + vector.name + " decryption"); 38 }); 39 40 all_promises.push(promise); 41 }); 42 43 // Test decryption with an altered buffer 44 passingVectors.forEach(function(vector) { 45 var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) 46 .then(function(vectors) { 47 // Get a one byte longer plaintext to encrypt 48 if (!("ciphertext" in vector)) { 49 return; 50 } 51 52 promise_test(function(test) { 53 var ciphertext = copyBuffer(vector.ciphertext); 54 var operation = subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext) 55 .then(function(plaintext) { 56 assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works")); 57 }, function(err) { 58 assert_unreached("Decryption should not throw error " + vector.name + ": " + err.message + "'"); 59 }); 60 ciphertext[0] = 255 - ciphertext[0]; 61 return operation; 62 }, vector.name + " decryption with altered ciphertext"); 63 64 }, function(err) { 65 // We need a failed test if the importVectorKey operation fails, so 66 // we know we never tested encryption 67 promise_test(function(test) { 68 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 69 }, "importVectorKeys step: " + vector.name + " decryption with altered ciphertext"); 70 }); 71 72 all_promises.push(promise); 73 }); 74 75 // Check for failures due to using publicKey to decrypt. 76 passingVectors.forEach(function(vector) { 77 var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) 78 .then(function(vectors) { 79 promise_test(function(test) { 80 return subtle.decrypt(vector.algorithm, vector.publicKey, vector.ciphertext) 81 .then(function(plaintext) { 82 assert_unreached("Should have thrown error for using publicKey to decrypt in " + vector.name + ": " + err.message + "'"); 83 }, function(err) { 84 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message); 85 }); 86 }, vector.name + " using publicKey to decrypt"); 87 88 }, function(err) { 89 // We need a failed test if the importVectorKey operation fails, so 90 // we know we never tested encryption 91 promise_test(function(test) { 92 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 93 }, "importVectorKeys step: " + vector.name + " using publicKey to decrypt"); 94 }); 95 96 all_promises.push(promise); 97 }); 98 99 100 // Check for failures due to no "decrypt" usage. 101 passingVectors.forEach(function(originalVector) { 102 var vector = Object.assign({}, originalVector); 103 104 var promise = importVectorKeys(vector, ["encrypt"], ["unwrapKey"]) 105 .then(function(vectors) { 106 // Get a one byte longer plaintext to encrypt 107 promise_test(function(test) { 108 return subtle.decrypt(vector.algorithm, vector.publicKey, vector.ciphertext) 109 .then(function(plaintext) { 110 assert_unreached("Should have thrown error for no decrypt usage in " + vector.name + ": " + err.message + "'"); 111 }, function(err) { 112 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message); 113 }); 114 }, vector.name + " no decrypt usage"); 115 116 }, function(err) { 117 // We need a failed test if the importVectorKey operation fails, so 118 // we know we never tested encryption 119 promise_test(function(test) { 120 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 121 }, "importVectorKeys step: " + vector.name + " no decrypt usage"); 122 }); 123 124 all_promises.push(promise); 125 }); 126 127 128 // Check for successful encryption even if plaintext is altered after call. 129 passingVectors.forEach(function(vector) { 130 var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) 131 .then(function(vectors) { 132 promise_test(function(test) { 133 var plaintext = copyBuffer(vector.plaintext); 134 var operation = subtle.encrypt(vector.algorithm, vector.publicKey, plaintext) 135 .then(function(ciphertext) { 136 assert_equals(ciphertext.byteLength * 8, vector.privateKey.algorithm.modulusLength, "Ciphertext length matches modulus length"); 137 // Can we get the original plaintext back via decrypt? 138 return subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext) 139 .then(function(result) { 140 assert_true(equalBuffers(result, vector.plaintext), "Round trip returns original plaintext"); 141 return ciphertext; 142 }, function(err) { 143 assert_unreached("decrypt error for test " + vector.name + ": " + err.message + "'"); 144 }); 145 }) 146 .then(function(priorCiphertext) { 147 // Will a second encrypt give us different ciphertext, as it should? 148 return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) 149 .then(function(ciphertext) { 150 assert_false(equalBuffers(priorCiphertext, ciphertext), "Two encrypts give different results") 151 }, function(err) { 152 assert_unreached("second time encrypt error for test " + vector.name + ": '" + err.message + "'"); 153 }); 154 }, function(err) { 155 assert_unreached("decrypt error for test " + vector.name + ": '" + err.message + "'"); 156 }); 157 158 plaintext[0] = 255 - plaintext[0]; 159 return operation; 160 }, vector.name + " with altered plaintext"); 161 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("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 167 }, "importVectorKeys step: " + vector.name + " with altered plaintext"); 168 }); 169 170 all_promises.push(promise); 171 }); 172 173 // Check for successful encryption. 174 passingVectors.forEach(function(vector) { 175 var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) 176 .then(function(vectors) { 177 promise_test(function(test) { 178 return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) 179 .then(function(ciphertext) { 180 assert_equals(ciphertext.byteLength * 8, vector.privateKey.algorithm.modulusLength, "Ciphertext length matches modulus length"); 181 182 // Can we get the original plaintext back via decrypt? 183 return subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext) 184 .then(function(result) { 185 assert_true(equalBuffers(result, vector.plaintext), "Round trip returns original plaintext"); 186 return ciphertext; 187 }, function(err) { 188 assert_unreached("decrypt error for test " + vector.name + ": " + err.message + "'"); 189 }); 190 }) 191 .then(function(priorCiphertext) { 192 // Will a second encrypt give us different ciphertext, as it should? 193 return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) 194 .then(function(ciphertext) { 195 assert_false(equalBuffers(priorCiphertext, ciphertext), "Two encrypts give different results") 196 }, function(err) { 197 assert_unreached("second time encrypt error for test " + vector.name + ": '" + err.message + "'"); 198 }); 199 }, function(err) { 200 assert_unreached("decrypt error for test " + vector.name + ": '" + err.message + "'"); 201 }); 202 }, vector.name); 203 204 }, function(err) { 205 // We need a failed test if the importVectorKey operation fails, so 206 // we know we never tested encryption 207 promise_test(function(test) { 208 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 209 }, "importVectorKeys step: " + vector.name); 210 }); 211 212 all_promises.push(promise); 213 }); 214 215 // Check for failures due to too long plaintext. 216 passingVectors.forEach(function(vector) { 217 var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) 218 .then(function(vectors) { 219 // Get a one byte longer plaintext to encrypt 220 var plaintext = new Uint8Array(vector.plaintext.byteLength + 1); 221 plaintext.set(plaintext, 0); 222 plaintext.set(new Uint8Array([32]), vector.plaintext.byteLength); 223 promise_test(function(test) { 224 return subtle.encrypt(vector.algorithm, vector.publicKey, plaintext) 225 .then(function(ciphertext) { 226 assert_unreached("Should have thrown error for too long plaintext in " + vector.name + ": " + err.message + "'"); 227 }, function(err) { 228 assert_equals(err.name, "OperationError", "Should throw OperationError instead of " + err.message); 229 }); 230 }, vector.name + " too long plaintext"); 231 232 }, function(err) { 233 // We need a failed test if the importVectorKey operation fails, so 234 // we know we never tested encryption 235 promise_test(function(test) { 236 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 237 }, "importVectorKeys step: " + vector.name + " too long plaintext"); 238 }); 239 240 all_promises.push(promise); 241 }); 242 243 244 // Check for failures due to using privateKey to encrypt. 245 passingVectors.forEach(function(vector) { 246 var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) 247 .then(function(vectors) { 248 promise_test(function(test) { 249 return subtle.encrypt(vector.algorithm, vector.privateKey, vector.plaintext) 250 .then(function(ciphertext) { 251 assert_unreached("Should have thrown error for using privateKey to encrypt in " + vector.name + ": " + err.message + "'"); 252 }, function(err) { 253 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message); 254 }); 255 }, vector.name + " using privateKey to encrypt"); 256 257 }, function(err) { 258 // We need a failed test if the importVectorKey operation fails, so 259 // we know we never tested encryption 260 promise_test(function(test) { 261 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 262 }, "importVectorKeys step: " + vector.name + " using privateKey to encrypt"); 263 }); 264 265 all_promises.push(promise); 266 }); 267 268 269 // Check for failures due to no "encrypt usage". 270 passingVectors.forEach(function(originalVector) { 271 var vector = Object.assign({}, originalVector); 272 273 var promise = importVectorKeys(vector, [], ["decrypt"]) 274 .then(function(vectors) { 275 // Get a one byte longer plaintext to encrypt 276 promise_test(function(test) { 277 return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) 278 .then(function(ciphertext) { 279 assert_unreached("Should have thrown error for no encrypt usage in " + vector.name + ": " + err.message + "'"); 280 }, function(err) { 281 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message); 282 }); 283 }, vector.name + " no encrypt usage"); 284 285 }, function(err) { 286 // We need a failed test if the importVectorKey operation fails, so 287 // we know we never tested encryption 288 promise_test(function(test) { 289 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 290 }, "importVectorKeys step: " + vector.name + " no encrypt usage"); 291 }); 292 293 all_promises.push(promise); 294 }); 295 296 promise_test(function() { 297 return Promise.all(all_promises) 298 .then(function() {done();}) 299 .catch(function() {done();}) 300 }, "setup"); 301 302 // A test vector has all needed fields for encryption, EXCEPT that the 303 // key field may be null. This function replaces that null with the Correct 304 // CryptoKey object. 305 // 306 // Returns a Promise that yields an updated vector on success. 307 function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { 308 var publicPromise, privatePromise; 309 310 if (vector.publicKey !== null) { 311 publicPromise = new Promise(function(resolve, reject) { 312 resolve(vector); 313 }); 314 } else { 315 publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, publicKeyUsages) 316 .then(function(key) { 317 vector.publicKey = key; 318 return vector; 319 }); // Returns a copy of the sourceBuffer it is sent. 320 function copyBuffer(sourceBuffer) { 321 var source = new Uint8Array(sourceBuffer); 322 var copy = new Uint8Array(sourceBuffer.byteLength) 323 324 for (var i=0; i<source.byteLength; i++) { 325 copy[i] = source[i]; 326 } 327 328 return copy; 329 } 330 331 } 332 333 if (vector.privateKey !== null) { 334 privatePromise = new Promise(function(resolve, reject) { 335 resolve(vector); 336 }); 337 } else { 338 privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, privateKeyUsages) 339 .then(function(key) { 340 vector.privateKey = key; 341 return vector; 342 }); 343 } 344 345 return Promise.all([publicPromise, privatePromise]); 346 } 347 348 // Returns a copy of the sourceBuffer it is sent. 349 function copyBuffer(sourceBuffer) { 350 var source = new Uint8Array(sourceBuffer); 351 var copy = new Uint8Array(sourceBuffer.byteLength) 352 353 for (var i=0; i<source.byteLength; i++) { 354 copy[i] = source[i]; 355 } 356 357 return copy; 358 } 359 360 function equalBuffers(a, b) { 361 if (a.byteLength !== b.byteLength) { 362 return false; 363 } 364 365 var aBytes = new Uint8Array(a); 366 var bBytes = new Uint8Array(b); 367 368 for (var i=0; i<a.byteLength; i++) { 369 if (aBytes[i] !== bBytes[i]) { 370 return false; 371 } 372 } 373 374 return true; 375 } 376 377 return; 378} 379