ecdsa.js (24116B)
1 function run_test() { 2 setup({explicit_done: true}); 3 4 var subtle = self.crypto.subtle; // Change to test prefixed implementations 5 6 // When are all these tests really done? When all the promises they use have resolved. 7 var all_promises = []; 8 9 // Source file [algorithm_name]_vectors.js provides the getTestVectors method 10 // for the algorithm that drives these tests. 11 var testVectors = getTestVectors(); 12 var invalidTestVectors = getInvalidTestVectors(); 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, hash: vector.hashName}; 19 promise_test(function(test) { 20 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 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, hash: vector.hashName}; 46 promise_test(function(test) { 47 var signature = copyBuffer(vector.signature); 48 var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) 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 plaintext 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, hash: vector.hashName}; 72 promise_test(function(test) { 73 var plaintext = copyBuffer(vector.plaintext); 74 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext) 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 plaintext[0] = 255 - plaintext[0]; 82 return operation; 83 }, vector.name + " with altered plaintext 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 plaintext 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, hash: vector.hashName}; 98 promise_test(function(test) { 99 return subtle.verify(algorithm, vector.privateKey, vector.signature, vector.plaintext) 100 .then(function(plaintext) { 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, hash: vector.hashName}; 121 promise_test(function(test) { 122 return subtle.sign(algorithm, vector.publicKey, vector.plaintext) 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, hash: vector.hashName}; 145 promise_test(function(test) { 146 return subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 147 .then(function(plaintext) { 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, hash: vector.hashName}; 167 promise_test(function(test) { 168 return subtle.sign(algorithm, vector.privateKey, vector.plaintext) 169 .then(function(signature) { 170 // Can we verify the signature? 171 return subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) 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, hash: vector.hashName}; 200 return importVectorKeys(vector, ["verify"], ["sign"]) 201 .then(function(vectors) { 202 promise_test(function(test) { 203 var operation = subtle.sign(algorithm, wrongKey, vector.plaintext) 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, hash: vector.hashName}; 237 promise_test(function(test) { 238 var operation = subtle.verify(algorithm, wrongKey, vector.signature, vector.plaintext) 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, hash: vector.hashName}; 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.plaintext) 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 wrong hash 294 testVectors.forEach(function(vector) { 295 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 296 .then(function(vectors) { 297 var hashName = "SHA-1"; 298 if (vector.hashName === "SHA-1") { 299 hashName = "SHA-256" 300 } 301 var algorithm = {name: vector.algorithmName, hash: hashName}; 302 promise_test(function(test) { 303 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 304 .then(function(is_verified) { 305 assert_false(is_verified, "Signature NOT verified"); 306 }, function(err) { 307 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 308 }); 309 310 return operation; 311 }, vector.name + " verification failure due to wrong hash"); 312 313 }, function(err) { 314 // We need a failed test if the importVectorKey operation fails, so 315 // we know we never tested verification. 316 promise_test(function(test) { 317 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 318 }, "importVectorKeys step: " + vector.name + " verification failure due to wrong hash"); 319 }); 320 321 all_promises.push(promise); 322 }); 323 324 // Test verification fails with bad hash name 325 testVectors.forEach(function(vector) { 326 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 327 .then(function(vectors) { 328 // use the wrong name for the hash 329 var hashName = vector.hashName.substring(0, 3) + vector.hashName.substring(4); 330 var algorithm = {name: vector.algorithmName, hash: hashName}; 331 promise_test(function(test) { 332 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 333 .then(function(is_verified) { 334 assert_unreached("Verification should throw an error"); 335 }, function(err) { 336 assert_equals(err.name, "NotSupportedError", "Correctly throws NotSupportedError for illegal hash name") 337 }); 338 339 return operation; 340 }, vector.name + " verification failure due to bad hash name"); 341 342 }, function(err) { 343 // We need a failed test if the importVectorKey operation fails, so 344 // we know we never tested verification. 345 promise_test(function(test) { 346 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 347 }, "importVectorKeys step: " + vector.name + " verification failure due to bad hash name"); 348 }); 349 350 all_promises.push(promise); 351 }); 352 353 // Test verification fails with short (odd length) signature 354 testVectors.forEach(function(vector) { 355 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 356 .then(function(vectors) { 357 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 358 var signature = vector.signature.slice(1); // Skip the first byte 359 promise_test(function(test) { 360 var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) 361 .then(function(is_verified) { 362 assert_false(is_verified, "Signature NOT verified"); 363 }, function(err) { 364 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 365 }); 366 367 return operation; 368 }, vector.name + " verification failure due to shortened signature"); 369 370 }, function(err) { 371 // We need a failed test if the importVectorKey operation fails, so 372 // we know we never tested verification. 373 promise_test(function(test) { 374 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 375 }, "importVectorKeys step: " + vector.name + " verification failure due to shortened signature"); 376 }); 377 378 all_promises.push(promise); 379 }); 380 381 // Test verification fails with wrong plaintext 382 testVectors.forEach(function(vector) { 383 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 384 .then(function(vectors) { 385 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 386 var plaintext = copyBuffer(vector.plaintext); 387 plaintext[0] = 255 - plaintext[0]; 388 promise_test(function(test) { 389 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext) 390 .then(function(is_verified) { 391 assert_false(is_verified, "Signature NOT verified"); 392 }, function(err) { 393 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 394 }); 395 396 return operation; 397 }, vector.name + " verification failure due to altered plaintext"); 398 399 }, function(err) { 400 // We need a failed test if the importVectorKey operation fails, so 401 // we know we never tested verification. 402 promise_test(function(test) { 403 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 404 }, "importVectorKeys step: " + vector.name + " verification failure due to altered plaintext"); 405 }); 406 407 all_promises.push(promise); 408 }); 409 410 // Test invalid signatures 411 invalidTestVectors.forEach(function(vector) { 412 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 413 .then(function(vectors) { 414 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 415 promise_test(function(test) { 416 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 417 .then(function(is_verified) { 418 assert_false(is_verified, "Signature unexpectedly verified"); 419 }, function(err) { 420 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 421 }); 422 423 return operation; 424 }, vector.name + " verification"); 425 426 }, function(err) { 427 // We need a failed test if the importVectorKey operation fails, so 428 // we know we never tested verification. 429 promise_test(function(test) { 430 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 431 }, "importVectorKeys step: " + vector.name + " verification"); 432 }); 433 434 all_promises.push(promise); 435 }); 436 437 promise_test(function() { 438 return Promise.all(all_promises) 439 .then(function() {done();}) 440 .catch(function() {done();}) 441 }, "setup"); 442 443 // A test vector has all needed fields for signing and verifying, EXCEPT that the 444 // key field may be null. This function replaces that null with the Correct 445 // CryptoKey object. 446 // 447 // Returns a Promise that yields an updated vector on success. 448 function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { 449 var publicPromise, privatePromise; 450 451 if (vector.publicKey !== null) { 452 publicPromise = new Promise(function(resolve, reject) { 453 resolve(vector); 454 }); 455 } else { 456 publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithmName, namedCurve: vector.namedCurve}, false, publicKeyUsages) 457 .then(function(key) { 458 vector.publicKey = key; 459 return vector; 460 }); // Returns a copy of the sourceBuffer it is sent. 461 } 462 463 if (vector.privateKey !== null) { 464 privatePromise = new Promise(function(resolve, reject) { 465 resolve(vector); 466 }); 467 } else { 468 privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithmName, namedCurve: vector.namedCurve}, false, privateKeyUsages) 469 .then(function(key) { 470 vector.privateKey = key; 471 return vector; 472 }); 473 } 474 475 return Promise.all([publicPromise, privatePromise]); 476 } 477 478 // Returns a copy of the sourceBuffer it is sent. 479 function copyBuffer(sourceBuffer) { 480 var source = new Uint8Array(sourceBuffer); 481 var copy = new Uint8Array(sourceBuffer.byteLength) 482 483 for (var i=0; i<source.byteLength; i++) { 484 copy[i] = source[i]; 485 } 486 487 return copy; 488 } 489 490 function equalBuffers(a, b) { 491 if (a.byteLength !== b.byteLength) { 492 return false; 493 } 494 495 var aBytes = new Uint8Array(a); 496 var bBytes = new Uint8Array(b); 497 498 for (var i=0; i<a.byteLength; i++) { 499 if (aBytes[i] !== bBytes[i]) { 500 return false; 501 } 502 } 503 504 return true; 505 } 506 507 return; 508 }