1 2function run_test() { 3 setup({explicit_done: true}); 4 5 var subtle = self.crypto.subtle; // Change to test prefixed implementations 6 7 // When are all these tests really done? When all the promises they use have resolved. 8 var all_promises = []; 9 10 // Source file [algorithm_name]_vectors.js provides the getTestVectors method 11 // for the algorithm that drives these tests. 12 var testVectors = getTestVectors(); 13 14 // Test verification first, because signing tests rely on that working 15 testVectors.forEach(function(vector) { 16 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 17 .then(function(vectors) { 18 var algorithm = {name: vector.algorithmName}; 19 promise_test(function(test) { 20 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.data) 21 .then(function(is_verified) { 22 assert_true(is_verified, "Signature verified"); 23 }, function(err) { 24 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 25 }); 26 27 return operation; 28 }, vector.name + " verification"); 29 30 }, function(err) { 31 // We need a failed test if the importVectorKey operation fails, so 32 // we know we never tested verification. 33 promise_test(function(test) { 34 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 35 }, "importVectorKeys step: " + vector.name + " verification"); 36 }); 37 38 all_promises.push(promise); 39 }); 40 41 // Test verification with an altered buffer after call 42 testVectors.forEach(function(vector) { 43 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 44 .then(function(vectors) { 45 var algorithm = {name: vector.algorithmName}; 46 promise_test(function(test) { 47 var signature = copyBuffer(vector.signature); 48 var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) 49 .then(function(is_verified) { 50 assert_true(is_verified, "Signature verified"); 51 }, function(err) { 52 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 53 }); 54 55 signature[0] = 255 - signature[0]; 56 return operation; 57 }, vector.name + " verification with altered signature after call"); 58 }, function(err) { 59 promise_test(function(test) { 60 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 61 }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); 62 }); 63 64 all_promises.push(promise); 65 }); 66 67 // Check for successful verification even if data is altered after call. 68 testVectors.forEach(function(vector) { 69 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 70 .then(function(vectors) { 71 var algorithm = {name: vector.algorithmName}; 72 promise_test(function(test) { 73 var data = copyBuffer(vector.data); 74 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, data) 75 .then(function(is_verified) { 76 assert_true(is_verified, "Signature verified"); 77 }, function(err) { 78 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 79 }); 80 81 data[0] = 255 - data[0]; 82 return operation; 83 }, vector.name + " with altered data after call"); 84 }, function(err) { 85 promise_test(function(test) { 86 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 87 }, "importVectorKeys step: " + vector.name + " with altered data after call"); 88 }); 89 90 all_promises.push(promise); 91 }); 92 93 // Check for failures due to using privateKey to verify. 94 testVectors.forEach(function(vector) { 95 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 96 .then(function(vectors) { 97 var algorithm = {name: vector.algorithmName}; 98 promise_test(function(test) { 99 return subtle.verify(algorithm, vector.privateKey, vector.signature, vector.data) 100 .then(function(data) { 101 assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": " + err.message + "'"); 102 }, function(err) { 103 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); 104 }); 105 }, vector.name + " using privateKey to verify"); 106 107 }, function(err) { 108 promise_test(function(test) { 109 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 110 }, "importVectorKeys step: " + vector.name + " using privateKey to verify"); 111 }); 112 113 all_promises.push(promise); 114 }); 115 116 // Check for failures due to using publicKey to sign. 117 testVectors.forEach(function(vector) { 118 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 119 .then(function(vectors) { 120 var algorithm = {name: vector.algorithmName}; 121 promise_test(function(test) { 122 return subtle.sign(algorithm, vector.publicKey, vector.data) 123 .then(function(signature) { 124 assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": " + err.message + "'"); 125 }, function(err) { 126 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); 127 }); 128 }, vector.name + " using publicKey to sign"); 129 }, function(err) { 130 promise_test(function(test) { 131 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 132 }, "importVectorKeys step: " + vector.name + " using publicKey to sign"); 133 }); 134 135 all_promises.push(promise); 136 }); 137 138 // Check for failures due to no "verify" usage. 139 testVectors.forEach(function(originalVector) { 140 var vector = Object.assign({}, originalVector); 141 142 var promise = importVectorKeys(vector, [], ["sign"]) 143 .then(function(vectors) { 144 var algorithm = {name: vector.algorithmName}; 145 promise_test(function(test) { 146 return subtle.verify(algorithm, vector.publicKey, vector.signature, vector.data) 147 .then(function(data) { 148 assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); 149 }, function(err) { 150 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); 151 }); 152 }, vector.name + " no verify usage"); 153 }, function(err) { 154 promise_test(function(test) { 155 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 156 }, "importVectorKeys step: " + vector.name + " no verify usage"); 157 }); 158 159 all_promises.push(promise); 160 }); 161 162 // Check for successful signing and verification. 163 testVectors.forEach(function(vector) { 164 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 165 .then(function(vectors) { 166 var algorithm = {name: vector.algorithmName}; 167 promise_test(function(test) { 168 return subtle.sign(algorithm, vector.privateKey, vector.data) 169 .then(function(signature) { 170 // Can we verify the signature? 171 return subtle.verify(algorithm, vector.publicKey, signature, vector.data) 172 .then(function(is_verified) { 173 assert_true(is_verified, "Round trip verification works"); 174 return signature; 175 }, function(err) { 176 assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); 177 }); 178 }, function(err) { 179 assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'"); 180 }); 181 }, vector.name + " round trip"); 182 183 }, function(err) { 184 // We need a failed test if the importVectorKey operation fails, so 185 // we know we never tested signing or verifying 186 promise_test(function(test) { 187 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 188 }, "importVectorKeys step: " + vector.name + " round trip"); 189 }); 190 191 all_promises.push(promise); 192 }); 193 194 // Test signing with the wrong algorithm 195 testVectors.forEach(function(vector) { 196 // Want to get the key for the wrong algorithm 197 var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) 198 .then(function(wrongKey) { 199 var algorithm = {name: vector.algorithmName}; 200 return importVectorKeys(vector, ["verify"], ["sign"]) 201 .then(function(vectors) { 202 promise_test(function(test) { 203 var operation = subtle.sign(algorithm, wrongKey, vector.data) 204 .then(function(signature) { 205 assert_unreached("Signing should not have succeeded for " + vector.name); 206 }, function(err) { 207 assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); 208 }); 209 210 return operation; 211 }, vector.name + " signing with wrong algorithm name"); 212 213 }, function(err) { 214 // We need a failed test if the importVectorKey operation fails, so 215 // we know we never tested verification. 216 promise_test(function(test) { 217 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 218 }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name"); 219 }); 220 }, function(err) { 221 promise_test(function(test) { 222 assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); 223 }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name"); 224 }); 225 226 all_promises.push(promise); 227 }); 228 229 // Test verification with the wrong algorithm 230 testVectors.forEach(function(vector) { 231 // Want to get the key for the wrong algorithm 232 var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) 233 .then(function(wrongKey) { 234 return importVectorKeys(vector, ["verify"], ["sign"]) 235 .then(function(vectors) { 236 var algorithm = {name: vector.algorithmName}; 237 promise_test(function(test) { 238 var operation = subtle.verify(algorithm, wrongKey, vector.signature, vector.data) 239 .then(function(signature) { 240 assert_unreached("Verifying should not have succeeded for " + vector.name); 241 }, function(err) { 242 assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); 243 }); 244 245 return operation; 246 }, vector.name + " verifying with wrong algorithm name"); 247 248 }, function(err) { 249 // We need a failed test if the importVectorKey operation fails, so 250 // we know we never tested verification. 251 promise_test(function(test) { 252 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 253 }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name"); 254 }); 255 }, function(err) { 256 promise_test(function(test) { 257 assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); 258 }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name"); 259 }); 260 261 all_promises.push(promise); 262 }); 263 264 // Test verification fails with wrong signature 265 testVectors.forEach(function(vector) { 266 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 267 .then(function(vectors) { 268 var algorithm = {name: vector.algorithmName}; 269 var signature = copyBuffer(vector.signature); 270 signature[0] = 255 - signature[0]; 271 promise_test(function(test) { 272 var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) 273 .then(function(is_verified) { 274 assert_false(is_verified, "Signature NOT verified"); 275 }, function(err) { 276 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 277 }); 278 279 return operation; 280 }, vector.name + " verification failure due to altered signature"); 281 282 }, function(err) { 283 // We need a failed test if the importVectorKey operation fails, so 284 // we know we never tested verification. 285 promise_test(function(test) { 286 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 287 }, "importVectorKeys step: " + vector.name + " verification failure due to altered signature"); 288 }); 289 290 all_promises.push(promise); 291 }); 292 293 // Test verification fails with short (odd length) signature 294 testVectors.forEach(function(vector) { 295 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 296 .then(function(vectors) { 297 var algorithm = {name: vector.algorithmName}; 298 var signature = vector.signature.slice(1); // Skip the first byte 299 promise_test(function(test) { 300 var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) 301 .then(function(is_verified) { 302 assert_false(is_verified, "Signature NOT verified"); 303 }, function(err) { 304 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 305 }); 306 307 return operation; 308 }, vector.name + " verification failure due to shortened signature"); 309 310 }, function(err) { 311 // We need a failed test if the importVectorKey operation fails, so 312 // we know we never tested verification. 313 promise_test(function(test) { 314 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 315 }, "importVectorKeys step: " + vector.name + " verification failure due to shortened signature"); 316 }); 317 318 all_promises.push(promise); 319 }); 320 321 // Test verification fails with wrong data 322 testVectors.forEach(function(vector) { 323 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 324 .then(function(vectors) { 325 var algorithm = {name: vector.algorithmName}; 326 var data = copyBuffer(vector.data); 327 data[0] = 255 - data[0]; 328 promise_test(function(test) { 329 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, data) 330 .then(function(is_verified) { 331 assert_false(is_verified, "Signature NOT verified"); 332 }, function(err) { 333 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 334 }); 335 336 return operation; 337 }, vector.name + " verification failure due to altered data"); 338 339 }, function(err) { 340 // We need a failed test if the importVectorKey operation fails, so 341 // we know we never tested verification. 342 promise_test(function(test) { 343 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 344 }, "importVectorKeys step: " + vector.name + " verification failure due to altered data"); 345 }); 346 347 all_promises.push(promise); 348 }); 349 350 351 promise_test(function() { 352 return Promise.all(all_promises) 353 .then(function() {done();}) 354 .catch(function() {done();}) 355 }, "setup"); 356 357 // Test that generated keys are valid for signing and verifying. 358 testVectors.forEach(function(vector) { 359 var algorithm = {name: vector.algorithmName}; 360 promise_test(async() => { 361 let key = await subtle.generateKey(algorithm, false, ["sign", "verify"]); 362 let signature = await subtle.sign(algorithm, key.privateKey, vector.data); 363 let isVerified = await subtle.verify(algorithm, key.publicKey, signature, vector.data); 364 assert_true(isVerified, "Verificaton failed."); 365 }, "Sign and verify using generated " + vector.algorithmName + " keys."); 366 }); 367 368 369 // A test vector has all needed fields for signing and verifying, EXCEPT that the 370 // key field may be null. This function replaces that null with the Correct 371 // CryptoKey object. 372 // 373 // Returns a Promise that yields an updated vector on success. 374 function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { 375 var publicPromise, privatePromise; 376 377 if (vector.publicKey !== null) { 378 publicPromise = new Promise(function(resolve, reject) { 379 resolve(vector); 380 }); 381 } else { 382 publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithmName}, false, publicKeyUsages) 383 .then(function(key) { 384 vector.publicKey = key; 385 return vector; 386 }); // Returns a copy of the sourceBuffer it is sent. 387 } 388 389 if (vector.privateKey !== null) { 390 privatePromise = new Promise(function(resolve, reject) { 391 resolve(vector); 392 }); 393 } else { 394 privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithmName}, false, privateKeyUsages) 395 .then(function(key) { 396 vector.privateKey = key; 397 return vector; 398 }); 399 } 400 401 return Promise.all([publicPromise, privatePromise]); 402 } 403 404 // Returns a copy of the sourceBuffer it is sent. 405 function copyBuffer(sourceBuffer) { 406 var source = new Uint8Array(sourceBuffer); 407 var copy = new Uint8Array(sourceBuffer.byteLength) 408 409 for (var i=0; i<source.byteLength; i++) { 410 copy[i] = source[i]; 411 } 412 413 return copy; 414 } 415 416 function equalBuffers(a, b) { 417 if (a.byteLength !== b.byteLength) { 418 return false; 419 } 420 421 var aBytes = new Uint8Array(a); 422 var bBytes = new Uint8Array(b); 423 424 for (var i=0; i<a.byteLength; i++) { 425 if (aBytes[i] !== bBytes[i]) { 426 return false; 427 } 428 } 429 430 return true; 431 } 432 433 return; 434} 435