test_signed_apps.js (35266B)
1 "use strict"; 2 3 // Tests the API nsIX509CertDB.openSignedAppFileAsync, which backs add-on 4 // signature verification. Testcases include various ways of tampering with 5 // add-ons as well as different hash algorithms used in the various 6 // signature/metadata files. 7 8 // from prio.h 9 const PR_RDWR = 0x04; 10 const PR_CREATE_FILE = 0x08; 11 const PR_TRUNCATE = 0x20; 12 const PR_USEC_PER_MSEC = 1000; 13 14 do_get_profile(); // must be called before getting nsIX509CertDB 15 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( 16 Ci.nsIX509CertDB 17 ); 18 19 // Creates a new app package based in the inFilePath package, with a set of 20 // modifications (including possibly deletions) applied to the existing entries, 21 // and/or a set of new entries to be included. 22 function tamper(inFilePath, outFilePath, modifications, newEntries) { 23 let writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter); 24 writer.open(outFilePath, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE); 25 try { 26 let reader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance( 27 Ci.nsIZipReader 28 ); 29 reader.open(inFilePath); 30 try { 31 for (let entryName of reader.findEntries("")) { 32 let inEntry = reader.getEntry(entryName); 33 let entryInput = reader.getInputStream(entryName); 34 try { 35 let f = modifications[entryName]; 36 let outEntry, outEntryInput; 37 if (f) { 38 [outEntry, outEntryInput] = f(inEntry, entryInput); 39 delete modifications[entryName]; 40 } else { 41 [outEntry, outEntryInput] = [inEntry, entryInput]; 42 } 43 // if f does not want the input entry to be copied to the output entry 44 // at all (i.e. it wants it to be deleted), it will return null. 45 if (outEntryInput) { 46 try { 47 writer.addEntryStream( 48 entryName, 49 outEntry.lastModifiedTime, 50 outEntry.compression, 51 outEntryInput, 52 false 53 ); 54 } finally { 55 if (entryInput != outEntryInput) { 56 outEntryInput.close(); 57 } 58 } 59 } 60 } finally { 61 entryInput.close(); 62 } 63 } 64 } finally { 65 reader.close(); 66 } 67 68 // Any leftover modification means that we were expecting to modify an entry 69 // in the input file that wasn't there. 70 for (let name in modifications) { 71 if (modifications.hasOwnProperty(name)) { 72 throw new Error("input file was missing expected entries: " + name); 73 } 74 } 75 76 // Now, append any new entries to the end 77 newEntries.forEach(function (newEntry) { 78 let sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( 79 Ci.nsIStringInputStream 80 ); 81 try { 82 sis.setByteStringData(newEntry.content); 83 writer.addEntryStream( 84 newEntry.name, 85 new Date() * PR_USEC_PER_MSEC, 86 Ci.nsIZipWriter.COMPRESSION_BEST, 87 sis, 88 false 89 ); 90 } finally { 91 sis.close(); 92 } 93 }); 94 } finally { 95 writer.close(); 96 } 97 } 98 99 function removeEntry() { 100 return [null, null]; 101 } 102 103 function truncateEntry(entry, entryInput) { 104 if (entryInput.available() == 0) { 105 throw new Error( 106 "Truncating already-zero length entry will result in " + 107 "identical entry." 108 ); 109 } 110 111 let content = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( 112 Ci.nsIStringInputStream 113 ); 114 content.setByteStringData(""); 115 116 return [entry, content]; 117 } 118 119 function check_open_result(name, expectedRv, expectedSignatureAlgorithms) { 120 return function openSignedAppFileCallback(rv, aZipReader, aSignatureInfos) { 121 info("openSignedAppFileCallback called for " + name); 122 equal(rv, expectedRv, "Actual and expected return value should match"); 123 equal( 124 aZipReader != null, 125 Components.isSuccessCode(expectedRv), 126 "ZIP reader should be null only if the return value denotes failure" 127 ); 128 equal( 129 aSignatureInfos.length, 130 expectedSignatureAlgorithms.length, 131 "Should have the same number of expected signature infos" 132 ); 133 for (let i = 0; i < expectedSignatureAlgorithms.length; i++) { 134 equal( 135 aSignatureInfos[i].signatureAlgorithm, 136 expectedSignatureAlgorithms[i], 137 "Should have expected signature algorithm" 138 ); 139 } 140 run_next_test(); 141 }; 142 } 143 144 function original_app_path(test_name) { 145 return do_get_file("test_signed_apps/" + test_name + ".zip", false); 146 } 147 148 function tampered_app_path(test_name) { 149 return new FileUtils.File( 150 PathUtils.join( 151 Services.dirsvc.get("TmpD", Ci.nsIFile).path, 152 `test_signed_app-${test_name}.zip` 153 ) 154 ); 155 } 156 157 var hashTestcases = [ 158 // SHA-256 in PKCS#7 + SHA-256 present elsewhere => OK 159 { 160 name: "app_mf-1-256_sf-1-256_p7-1-256", 161 expectedResult: Cr.NS_OK, 162 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256], 163 }, 164 { 165 name: "app_mf-1-256_sf-1-256_p7-256", 166 expectedResult: Cr.NS_OK, 167 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256], 168 }, 169 { 170 name: "app_mf-1-256_sf-256_p7-1-256", 171 expectedResult: Cr.NS_OK, 172 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256], 173 }, 174 { 175 name: "app_mf-1-256_sf-256_p7-256", 176 expectedResult: Cr.NS_OK, 177 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256], 178 }, 179 { 180 name: "app_mf-256_sf-1-256_p7-1-256", 181 expectedResult: Cr.NS_OK, 182 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256], 183 }, 184 { 185 name: "app_mf-256_sf-1-256_p7-256", 186 expectedResult: Cr.NS_OK, 187 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256], 188 }, 189 { 190 name: "app_mf-256_sf-256_p7-1-256", 191 expectedResult: Cr.NS_OK, 192 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256], 193 }, 194 { 195 name: "app_mf-256_sf-256_p7-256", 196 expectedResult: Cr.NS_OK, 197 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256], 198 }, 199 200 // SHA-1 in PKCS#7 + SHA-1 present elsewhere => OK 201 { 202 name: "app_mf-1-256_sf-1-256_p7-1", 203 expectedResult: Cr.NS_OK, 204 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1], 205 }, 206 { 207 name: "app_mf-1-256_sf-1_p7-1", 208 expectedResult: Cr.NS_OK, 209 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1], 210 }, 211 { 212 name: "app_mf-1_sf-1-256_p7-1", 213 expectedResult: Cr.NS_OK, 214 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1], 215 }, 216 { 217 name: "app_mf-1_sf-1_p7-1", 218 expectedResult: Cr.NS_OK, 219 expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1], 220 }, 221 222 // SHA-256 in PKCS#7 + SHA-256 not present elsewhere => INVALID 223 { 224 name: "app_mf-1-256_sf-1_p7-1-256", 225 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 226 expectedSignatureAlgorithms: [], 227 }, 228 { 229 name: "app_mf-1-256_sf-1_p7-256", 230 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 231 expectedSignatureAlgorithms: [], 232 }, 233 { 234 name: "app_mf-1_sf-1-256_p7-1-256", 235 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 236 expectedSignatureAlgorithms: [], 237 }, 238 { 239 name: "app_mf-1_sf-1-256_p7-256", 240 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 241 expectedSignatureAlgorithms: [], 242 }, 243 { 244 name: "app_mf-1_sf-1_p7-1-256", 245 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 246 expectedSignatureAlgorithms: [], 247 }, 248 { 249 name: "app_mf-1_sf-1_p7-256", 250 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 251 expectedSignatureAlgorithms: [], 252 }, 253 { 254 name: "app_mf-1_sf-256_p7-1-256", 255 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 256 expectedSignatureAlgorithms: [], 257 }, 258 { 259 name: "app_mf-1_sf-256_p7-256", 260 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 261 expectedSignatureAlgorithms: [], 262 }, 263 { 264 name: "app_mf-256_sf-1_p7-1-256", 265 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 266 expectedSignatureAlgorithms: [], 267 }, 268 { 269 name: "app_mf-256_sf-1_p7-256", 270 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 271 expectedSignatureAlgorithms: [], 272 }, 273 274 // SHA-1 in PKCS#7 + SHA-1 not present elsewhere => INVALID 275 { 276 name: "app_mf-1-256_sf-256_p7-1", 277 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 278 expectedSignatureAlgorithms: [], 279 }, 280 { 281 name: "app_mf-1_sf-256_p7-1", 282 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 283 expectedSignatureAlgorithms: [], 284 }, 285 { 286 name: "app_mf-256_sf-1-256_p7-1", 287 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 288 expectedSignatureAlgorithms: [], 289 }, 290 { 291 name: "app_mf-256_sf-1_p7-1", 292 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 293 expectedSignatureAlgorithms: [], 294 }, 295 { 296 name: "app_mf-256_sf-256_p7-1", 297 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 298 expectedSignatureAlgorithms: [], 299 }, 300 ]; 301 302 // Policy values for the preference "security.signed_app_signatures.policy" 303 const PKCS7WithSHA1OrSHA256 = 0b0; 304 const PKCS7_WITH_SHA256 = 0b1; 305 const COSEAndPKCS7WithSHA1OrSHA256 = 0b10; 306 const COSEAndPKCS7WithSHA256 = 0b11; 307 const COSERequiredAndPKCS7WithSHA1OrSHA256 = 0b100; 308 const COSERequiredAndPKCS7WithSHA256 = 0b101; 309 const COSEOnly = 0b110; 310 const COSEOnlyAgain = 0b111; 311 312 function add_signature_test(policy, test) { 313 // First queue up a test to set the desired policy: 314 add_test(function () { 315 Services.prefs.setIntPref("security.signed_app_signatures.policy", policy); 316 run_next_test(); 317 }); 318 // Then queue up the test itself: 319 add_test(test); 320 } 321 322 for (let testcase of hashTestcases) { 323 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 324 certdb.openSignedAppFileAsync( 325 Ci.nsIX509CertDB.AppXPCShellRoot, 326 original_app_path(testcase.name), 327 check_open_result( 328 testcase.name, 329 testcase.expectedResult, 330 testcase.expectedSignatureAlgorithms 331 ) 332 ); 333 }); 334 } 335 336 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 337 certdb.openSignedAppFileAsync( 338 Ci.nsIX509CertDB.AppXPCShellRoot, 339 original_app_path("empty_signerInfos"), 340 check_open_result( 341 "the signerInfos in the PKCS#7 signature is empty", 342 Cr.NS_ERROR_CMS_VERIFY_NOT_SIGNED, 343 [] 344 ) 345 ); 346 }); 347 348 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 349 certdb.openSignedAppFileAsync( 350 Ci.nsIX509CertDB.AppXPCShellRoot, 351 original_app_path("unsigned_app"), 352 check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED, []) 353 ); 354 }); 355 356 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 357 certdb.openSignedAppFileAsync( 358 Ci.nsIX509CertDB.AppXPCShellRoot, 359 original_app_path("unknown_issuer_app"), 360 check_open_result( 361 "unknown_issuer", 362 getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER), 363 [] 364 ) 365 ); 366 }); 367 368 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () { 369 certdb.openSignedAppFileAsync( 370 Ci.nsIX509CertDB.AppXPCShellRoot, 371 original_app_path("cose_signed_with_pkcs7"), 372 check_open_result("cose_signed_with_pkcs7", Cr.NS_OK, [ 373 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 374 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256, 375 ]) 376 ); 377 }); 378 379 add_signature_test(COSEAndPKCS7WithSHA256, function () { 380 certdb.openSignedAppFileAsync( 381 Ci.nsIX509CertDB.AppXPCShellRoot, 382 original_app_path("app_mf-256_sf-256_p7-256"), 383 check_open_result("no COSE but correct PK#7", Cr.NS_OK, [ 384 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256, 385 ]) 386 ); 387 }); 388 389 add_signature_test(COSEAndPKCS7WithSHA256, function () { 390 certdb.openSignedAppFileAsync( 391 Ci.nsIX509CertDB.AppXPCShellRoot, 392 original_app_path("app_mf-1_sf-256_p7-256"), 393 check_open_result( 394 "no COSE and wrong PK#7 hash", 395 Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 396 [] 397 ) 398 ); 399 }); 400 401 add_signature_test(COSERequiredAndPKCS7WithSHA1OrSHA256, function () { 402 certdb.openSignedAppFileAsync( 403 Ci.nsIX509CertDB.AppXPCShellRoot, 404 original_app_path("app_mf-256_sf-256_p7-256"), 405 check_open_result( 406 "COSE signature missing (SHA1 or 256)", 407 Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE, 408 [] 409 ) 410 ); 411 }); 412 413 add_signature_test(COSERequiredAndPKCS7WithSHA256, function () { 414 certdb.openSignedAppFileAsync( 415 Ci.nsIX509CertDB.AppXPCShellRoot, 416 original_app_path("app_mf-256_sf-256_p7-256"), 417 check_open_result( 418 "COSE signature missing (SHA256)", 419 Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE, 420 [] 421 ) 422 ); 423 }); 424 425 add_signature_test(COSERequiredAndPKCS7WithSHA256, function () { 426 certdb.openSignedAppFileAsync( 427 Ci.nsIX509CertDB.AppXPCShellRoot, 428 original_app_path("only_cose_signed"), 429 check_open_result( 430 "COSE signature only (PK#7 allowed, not present)", 431 Cr.NS_OK, 432 [Ci.nsIAppSignatureInfo.COSE_WITH_SHA256] 433 ) 434 ); 435 }); 436 437 add_signature_test(COSERequiredAndPKCS7WithSHA1OrSHA256, function () { 438 certdb.openSignedAppFileAsync( 439 Ci.nsIX509CertDB.AppXPCShellRoot, 440 original_app_path("only_cose_signed"), 441 check_open_result( 442 "COSE signature only (PK#7 allowed, not present)", 443 Cr.NS_OK, 444 [Ci.nsIAppSignatureInfo.COSE_WITH_SHA256] 445 ) 446 ); 447 }); 448 449 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () { 450 certdb.openSignedAppFileAsync( 451 Ci.nsIX509CertDB.AppXPCShellRoot, 452 original_app_path("cose_multiple_signed_with_pkcs7"), 453 check_open_result("cose_multiple_signed_with_pkcs7", Cr.NS_OK, [ 454 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 455 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256, 456 ]) 457 ); 458 }); 459 460 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () { 461 certdb.openSignedAppFileAsync( 462 Ci.nsIX509CertDB.AppXPCShellRoot, 463 original_app_path("cose_int_signed_with_pkcs7"), 464 check_open_result("COSE signed with an intermediate", Cr.NS_OK, [ 465 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 466 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256, 467 ]) 468 ); 469 }); 470 471 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () { 472 certdb.openSignedAppFileAsync( 473 Ci.nsIX509CertDB.AppXPCShellRoot, 474 original_app_path("only_cose_signed"), 475 check_open_result( 476 "PK7 signature missing", 477 Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED, 478 [] 479 ) 480 ); 481 }); 482 483 add_signature_test(COSEOnly, function () { 484 certdb.openSignedAppFileAsync( 485 Ci.nsIX509CertDB.AppXPCShellRoot, 486 original_app_path("cose_multiple_signed_with_pkcs7"), 487 check_open_result( 488 "Expected only COSE signature", 489 Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, 490 [] 491 ) 492 ); 493 }); 494 495 add_signature_test(COSEOnly, function () { 496 certdb.openSignedAppFileAsync( 497 Ci.nsIX509CertDB.AppXPCShellRoot, 498 original_app_path("only_cose_multiple_signed"), 499 check_open_result("only Multiple COSE signatures", Cr.NS_OK, [ 500 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 501 ]) 502 ); 503 }); 504 505 add_signature_test(COSEOnly, function () { 506 certdb.openSignedAppFileAsync( 507 Ci.nsIX509CertDB.AppXPCShellRoot, 508 original_app_path("only_cose_signed"), 509 check_open_result("only_cose_signed", Cr.NS_OK, [ 510 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 511 ]) 512 ); 513 }); 514 515 add_signature_test(COSEOnlyAgain, function () { 516 certdb.openSignedAppFileAsync( 517 Ci.nsIX509CertDB.AppXPCShellRoot, 518 original_app_path("only_cose_signed"), 519 check_open_result("only_cose_signed (again)", Cr.NS_OK, [ 520 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 521 ]) 522 ); 523 }); 524 525 add_signature_test(COSEOnly, function () { 526 certdb.openSignedAppFileAsync( 527 Ci.nsIX509CertDB.AppXPCShellRoot, 528 original_app_path("cose_signed_with_pkcs7"), 529 check_open_result( 530 "COSE only expected but also PK#7 signed", 531 Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, 532 [] 533 ) 534 ); 535 }); 536 537 // Sanity check to ensure a no-op tampering gives a valid result 538 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 539 let tampered = tampered_app_path("identity_tampering"); 540 tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, []); 541 certdb.openSignedAppFileAsync( 542 Ci.nsIX509CertDB.AppXPCShellRoot, 543 original_app_path("app_mf-1_sf-1_p7-1"), 544 check_open_result("identity_tampering", Cr.NS_OK, [ 545 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1, 546 ]) 547 ); 548 }); 549 550 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 551 let tampered = tampered_app_path("missing_rsa"); 552 tamper( 553 original_app_path("app_mf-1_sf-1_p7-1"), 554 tampered, 555 { "META-INF/A.RSA": removeEntry }, 556 [] 557 ); 558 certdb.openSignedAppFileAsync( 559 Ci.nsIX509CertDB.AppXPCShellRoot, 560 tampered, 561 check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED, []) 562 ); 563 }); 564 565 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 566 let tampered = tampered_app_path("missing_sf"); 567 tamper( 568 original_app_path("app_mf-1_sf-1_p7-1"), 569 tampered, 570 { "META-INF/A.SF": removeEntry }, 571 [] 572 ); 573 certdb.openSignedAppFileAsync( 574 Ci.nsIX509CertDB.AppXPCShellRoot, 575 tampered, 576 check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, []) 577 ); 578 }); 579 580 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 581 let tampered = tampered_app_path("missing_manifest_mf"); 582 tamper( 583 original_app_path("app_mf-1_sf-1_p7-1"), 584 tampered, 585 { "META-INF/MANIFEST.MF": removeEntry }, 586 [] 587 ); 588 certdb.openSignedAppFileAsync( 589 Ci.nsIX509CertDB.AppXPCShellRoot, 590 tampered, 591 check_open_result( 592 "missing_manifest_mf", 593 Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 594 [] 595 ) 596 ); 597 }); 598 599 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 600 let tampered = tampered_app_path("missing_entry"); 601 tamper( 602 original_app_path("app_mf-1_sf-1_p7-1"), 603 tampered, 604 { "manifest.json": removeEntry }, 605 [] 606 ); 607 certdb.openSignedAppFileAsync( 608 Ci.nsIX509CertDB.AppXPCShellRoot, 609 tampered, 610 check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING, []) 611 ); 612 }); 613 614 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 615 let tampered = tampered_app_path("truncated_entry"); 616 tamper( 617 original_app_path("app_mf-1_sf-1_p7-1"), 618 tampered, 619 { "manifest.json": truncateEntry }, 620 [] 621 ); 622 certdb.openSignedAppFileAsync( 623 Ci.nsIX509CertDB.AppXPCShellRoot, 624 tampered, 625 check_open_result( 626 "truncated_entry", 627 Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, 628 [] 629 ) 630 ); 631 }); 632 633 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 634 let tampered = tampered_app_path("truncated_manifestFile"); 635 tamper( 636 original_app_path("app_mf-1_sf-1_p7-1"), 637 tampered, 638 { "META-INF/MANIFEST.MF": truncateEntry }, 639 [] 640 ); 641 certdb.openSignedAppFileAsync( 642 Ci.nsIX509CertDB.AppXPCShellRoot, 643 tampered, 644 check_open_result( 645 "truncated_manifestFile", 646 Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 647 [] 648 ) 649 ); 650 }); 651 652 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 653 let tampered = tampered_app_path("truncated_signatureFile"); 654 tamper( 655 original_app_path("app_mf-1_sf-1_p7-1"), 656 tampered, 657 { "META-INF/A.SF": truncateEntry }, 658 [] 659 ); 660 certdb.openSignedAppFileAsync( 661 Ci.nsIX509CertDB.AppXPCShellRoot, 662 tampered, 663 check_open_result( 664 "truncated_signatureFile", 665 getXPCOMStatusFromNSS(SEC_ERROR_PKCS7_BAD_SIGNATURE), 666 [] 667 ) 668 ); 669 }); 670 671 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 672 let tampered = tampered_app_path("truncated_pkcs7File"); 673 tamper( 674 original_app_path("app_mf-1_sf-1_p7-1"), 675 tampered, 676 { "META-INF/A.RSA": truncateEntry }, 677 [] 678 ); 679 certdb.openSignedAppFileAsync( 680 Ci.nsIX509CertDB.AppXPCShellRoot, 681 tampered, 682 check_open_result( 683 "truncated_pkcs7File", 684 Cr.NS_ERROR_CMS_VERIFY_NOT_SIGNED, 685 [] 686 ) 687 ); 688 }); 689 690 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 691 let tampered = tampered_app_path("unsigned_entry"); 692 tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, [ 693 { name: "unsigned.txt", content: "unsigned content!" }, 694 ]); 695 certdb.openSignedAppFileAsync( 696 Ci.nsIX509CertDB.AppXPCShellRoot, 697 tampered, 698 check_open_result( 699 "unsigned_entry", 700 Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, 701 [] 702 ) 703 ); 704 }); 705 706 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 707 let tampered = tampered_app_path("unsigned_metainf_entry"); 708 tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, [ 709 { name: "META-INF/unsigned.txt", content: "unsigned content!" }, 710 ]); 711 certdb.openSignedAppFileAsync( 712 Ci.nsIX509CertDB.AppXPCShellRoot, 713 tampered, 714 check_open_result( 715 "unsigned_metainf_entry", 716 Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, 717 [] 718 ) 719 ); 720 }); 721 722 add_signature_test(PKCS7_WITH_SHA256, function testSHA1Disabled() { 723 certdb.openSignedAppFileAsync( 724 Ci.nsIX509CertDB.AppXPCShellRoot, 725 original_app_path("app_mf-1_sf-1_p7-1"), 726 check_open_result( 727 "SHA-1 should not be accepted if disabled by policy", 728 Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE, 729 [] 730 ) 731 ); 732 }); 733 734 add_signature_test( 735 PKCS7_WITH_SHA256, 736 function testSHA256WorksWithSHA1Disabled() { 737 certdb.openSignedAppFileAsync( 738 Ci.nsIX509CertDB.AppXPCShellRoot, 739 original_app_path("app_mf-256_sf-256_p7-256"), 740 check_open_result( 741 "SHA-256 should work if SHA-1 is disabled by policy", 742 Cr.NS_OK, 743 [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256] 744 ) 745 ); 746 } 747 ); 748 749 add_signature_test( 750 PKCS7_WITH_SHA256, 751 function testMultipleSignaturesWorkWithSHA1Disabled() { 752 certdb.openSignedAppFileAsync( 753 Ci.nsIX509CertDB.AppXPCShellRoot, 754 original_app_path("app_mf-1-256_sf-1-256_p7-1-256"), 755 check_open_result( 756 "Multiple signatures should work if SHA-1 is " + 757 "disabled by policy (if SHA-256 signature verifies)", 758 Cr.NS_OK, 759 [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256] 760 ) 761 ); 762 } 763 ); 764 765 var cosePolicies = [ 766 COSEAndPKCS7WithSHA1OrSHA256, 767 COSERequiredAndPKCS7WithSHA1OrSHA256, 768 ]; 769 770 // NOTE: The zip files referenced in coseTestcasesStage and coseTestcasesProd 771 // were originally generated with 772 // https://github.com/mozilla-services/autograph/blob/c890e14de5b04dcff9be0d07fdea4ae6bbb58557/tools/autograph-client/build_test_xpis.sh 773 // Since then, the mechanism to sign these packages have changed, see 774 // https://bugzilla.mozilla.org/show_bug.cgi?id=1885457 for details. 775 776 var coseTestcasesStage = [ 777 { 778 name: "addons-stage-tomato-clock-sha1-es256-es384", 779 expectedResult: Cr.NS_OK, 780 expectedSignatureAlgorithms: [ 781 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 782 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1, 783 ], 784 root: Ci.nsIX509CertDB.AddonsStageRoot, 785 }, 786 { 787 name: "addons-stage-tomato-clock-sha1-es256-ps256", 788 // PS256 is not yet supported. 789 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 790 expectedSignatureAlgorithms: [], 791 root: Ci.nsIX509CertDB.AddonsStageRoot, 792 }, 793 { 794 name: "addons-stage-tomato-clock-sha1-es256", 795 expectedResult: Cr.NS_OK, 796 expectedSignatureAlgorithms: [ 797 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 798 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1, 799 ], 800 root: Ci.nsIX509CertDB.AddonsStageRoot, 801 }, 802 { 803 name: "addons-stage-tomato-clock-sha1-ps256", 804 // PS256 is not yet supported. 805 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 806 expectedSignatureAlgorithms: [], 807 root: Ci.nsIX509CertDB.AddonsStageRoot, 808 }, 809 ]; 810 811 var coseTestcasesProd = [ 812 { 813 name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384", 814 expectedResult: Cr.NS_OK, 815 expectedSignatureAlgorithms: [ 816 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 817 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1, 818 ], 819 root: Ci.nsIX509CertDB.AddonsPublicRoot, 820 }, 821 { 822 name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256", 823 // PS256 is not yet supported. 824 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 825 expectedSignatureAlgorithms: [], 826 root: Ci.nsIX509CertDB.AddonsPublicRoot, 827 }, 828 { 829 name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256", 830 expectedResult: Cr.NS_OK, 831 expectedSignatureAlgorithms: [ 832 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 833 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1, 834 ], 835 root: Ci.nsIX509CertDB.AddonsPublicRoot, 836 }, 837 { 838 name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256", 839 // PS256 is not yet supported. 840 expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 841 expectedSignatureAlgorithms: [], 842 root: Ci.nsIX509CertDB.AddonsPublicRoot, 843 }, 844 ]; 845 846 for (let policy of cosePolicies) { 847 for (let testcase of [...coseTestcasesStage, ...coseTestcasesProd]) { 848 add_signature_test(policy, function () { 849 certdb.openSignedAppFileAsync( 850 testcase.root, 851 original_app_path(testcase.name), 852 check_open_result( 853 testcase.name, 854 testcase.expectedResult, 855 testcase.expectedSignatureAlgorithms 856 ) 857 ); 858 }); 859 } 860 } 861 862 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigTampered() { 863 let tampered = tampered_app_path("cose_sig_tampered"); 864 tamper( 865 original_app_path("cose_signed_with_pkcs7"), 866 tampered, 867 { "META-INF/cose.sig": truncateEntry }, 868 [] 869 ); 870 certdb.openSignedAppFileAsync( 871 Ci.nsIX509CertDB.AppXPCShellRoot, 872 tampered, 873 check_open_result( 874 "cose_sig_tampered", 875 Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, 876 [] 877 ) 878 ); 879 }); 880 881 // PKCS7 is processed before COSE, so if a COSE signature file is removed or 882 // tampered with, this appears as a PKCS7 signature verification failure. 883 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigRemoved() { 884 let tampered = tampered_app_path("cose_sig_removed"); 885 tamper( 886 original_app_path("cose_signed_with_pkcs7"), 887 tampered, 888 { "META-INF/cose.sig": removeEntry }, 889 [] 890 ); 891 certdb.openSignedAppFileAsync( 892 Ci.nsIX509CertDB.AppXPCShellRoot, 893 tampered, 894 check_open_result( 895 "cose_sig_removed", 896 Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING, 897 [] 898 ) 899 ); 900 }); 901 902 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestTampered() { 903 let tampered = tampered_app_path("cose_manifest_tampered"); 904 tamper( 905 original_app_path("cose_signed_with_pkcs7"), 906 tampered, 907 { "META-INF/cose.manifest": truncateEntry }, 908 [] 909 ); 910 certdb.openSignedAppFileAsync( 911 Ci.nsIX509CertDB.AppXPCShellRoot, 912 tampered, 913 check_open_result( 914 "cose_manifest_tampered", 915 Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, 916 [] 917 ) 918 ); 919 }); 920 921 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestRemoved() { 922 let tampered = tampered_app_path("cose_manifest_removed"); 923 tamper( 924 original_app_path("cose_signed_with_pkcs7"), 925 tampered, 926 { "META-INF/cose.manifest": removeEntry }, 927 [] 928 ); 929 certdb.openSignedAppFileAsync( 930 Ci.nsIX509CertDB.AppXPCShellRoot, 931 tampered, 932 check_open_result( 933 "cose_manifest_removed", 934 Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING, 935 [] 936 ) 937 ); 938 }); 939 940 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileAdded() { 941 let tampered = tampered_app_path("cose_file_added"); 942 tamper(original_app_path("cose_signed_with_pkcs7"), tampered, {}, [ 943 { name: "unsigned.txt", content: "unsigned content!" }, 944 ]); 945 certdb.openSignedAppFileAsync( 946 Ci.nsIX509CertDB.AppXPCShellRoot, 947 tampered, 948 check_open_result( 949 "cose_file_added", 950 Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, 951 [] 952 ) 953 ); 954 }); 955 956 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileRemoved() { 957 let tampered = tampered_app_path("cose_file_removed"); 958 tamper( 959 original_app_path("cose_signed_with_pkcs7"), 960 tampered, 961 { "manifest.json": removeEntry }, 962 [] 963 ); 964 certdb.openSignedAppFileAsync( 965 Ci.nsIX509CertDB.AppXPCShellRoot, 966 tampered, 967 check_open_result( 968 "cose_file_removed", 969 Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING, 970 [] 971 ) 972 ); 973 }); 974 975 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileTampered() { 976 let tampered = tampered_app_path("cose_file_tampered"); 977 tamper( 978 original_app_path("cose_signed_with_pkcs7"), 979 tampered, 980 { "manifest.json": truncateEntry }, 981 [] 982 ); 983 certdb.openSignedAppFileAsync( 984 Ci.nsIX509CertDB.AppXPCShellRoot, 985 tampered, 986 check_open_result( 987 "cose_file_tampered", 988 Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, 989 [] 990 ) 991 ); 992 }); 993 994 add_signature_test(COSEOnly, function testOnlyCOSESigTampered() { 995 let tampered = tampered_app_path("only_cose_sig_tampered"); 996 tamper( 997 original_app_path("only_cose_signed"), 998 tampered, 999 { "META-INF/cose.sig": truncateEntry }, 1000 [] 1001 ); 1002 certdb.openSignedAppFileAsync( 1003 Ci.nsIX509CertDB.AppXPCShellRoot, 1004 tampered, 1005 check_open_result( 1006 "only_cose_sig_tampered", 1007 Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 1008 [] 1009 ) 1010 ); 1011 }); 1012 1013 add_signature_test(COSEOnly, function testOnlyCOSESigRemoved() { 1014 let tampered = tampered_app_path("only_cose_sig_removed"); 1015 tamper( 1016 original_app_path("only_cose_signed"), 1017 tampered, 1018 { "META-INF/cose.sig": removeEntry }, 1019 [] 1020 ); 1021 certdb.openSignedAppFileAsync( 1022 Ci.nsIX509CertDB.AppXPCShellRoot, 1023 tampered, 1024 check_open_result( 1025 "only_cose_sig_removed", 1026 Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE, 1027 [] 1028 ) 1029 ); 1030 }); 1031 1032 add_signature_test(COSEOnly, function testOnlyCOSEManifestTampered() { 1033 let tampered = tampered_app_path("only_cose_manifest_tampered"); 1034 tamper( 1035 original_app_path("only_cose_signed"), 1036 tampered, 1037 { "META-INF/cose.manifest": truncateEntry }, 1038 [] 1039 ); 1040 certdb.openSignedAppFileAsync( 1041 Ci.nsIX509CertDB.AppXPCShellRoot, 1042 tampered, 1043 check_open_result( 1044 "only_cose_manifest_tampered", 1045 Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, 1046 [] 1047 ) 1048 ); 1049 }); 1050 1051 add_signature_test(COSEOnly, function testOnlyCOSEManifestRemoved() { 1052 let tampered = tampered_app_path("only_cose_manifest_removed"); 1053 tamper( 1054 original_app_path("only_cose_signed"), 1055 tampered, 1056 { "META-INF/cose.manifest": removeEntry }, 1057 [] 1058 ); 1059 certdb.openSignedAppFileAsync( 1060 Ci.nsIX509CertDB.AppXPCShellRoot, 1061 tampered, 1062 check_open_result( 1063 "only_cose_manifest_removed", 1064 Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE, 1065 [] 1066 ) 1067 ); 1068 }); 1069 1070 add_signature_test(COSEOnly, function testOnlyCOSEFileAdded() { 1071 let tampered = tampered_app_path("only_cose_file_added"); 1072 tamper(original_app_path("only_cose_signed"), tampered, {}, [ 1073 { name: "unsigned.txt", content: "unsigned content!" }, 1074 ]); 1075 certdb.openSignedAppFileAsync( 1076 Ci.nsIX509CertDB.AppXPCShellRoot, 1077 tampered, 1078 check_open_result( 1079 "only_cose_file_added", 1080 Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, 1081 [] 1082 ) 1083 ); 1084 }); 1085 1086 add_signature_test(COSEOnly, function testOnlyCOSEFileRemoved() { 1087 let tampered = tampered_app_path("only_cose_file_removed"); 1088 tamper( 1089 original_app_path("only_cose_signed"), 1090 tampered, 1091 { "manifest.json": removeEntry }, 1092 [] 1093 ); 1094 certdb.openSignedAppFileAsync( 1095 Ci.nsIX509CertDB.AppXPCShellRoot, 1096 tampered, 1097 check_open_result( 1098 "only_cose_file_removed", 1099 Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING, 1100 [] 1101 ) 1102 ); 1103 }); 1104 1105 add_signature_test(COSEOnly, function testOnlyCOSEFileTampered() { 1106 let tampered = tampered_app_path("only_cose_file_tampered"); 1107 tamper( 1108 original_app_path("only_cose_signed"), 1109 tampered, 1110 { "manifest.json": truncateEntry }, 1111 [] 1112 ); 1113 certdb.openSignedAppFileAsync( 1114 Ci.nsIX509CertDB.AppXPCShellRoot, 1115 tampered, 1116 check_open_result( 1117 "only_cose_file_tampered", 1118 Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, 1119 [] 1120 ) 1121 ); 1122 }); 1123 1124 // This was signed with only COSE first, and then the contents were tampered 1125 // with (making the signature invalid). Then, the file was signed with 1126 // PKCS7/SHA1. We need to ensure that if we're configured to process COSE, this 1127 // verification fails. 1128 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () { 1129 certdb.openSignedAppFileAsync( 1130 Ci.nsIX509CertDB.AppXPCShellRoot, 1131 original_app_path("cose_tampered_good_pkcs7"), 1132 check_open_result( 1133 "tampered COSE with good PKCS7 signature should fail " + 1134 "when COSE and PKCS7 is processed", 1135 Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, 1136 [] 1137 ) 1138 ); 1139 }); 1140 1141 add_signature_test(COSEOnly, function () { 1142 certdb.openSignedAppFileAsync( 1143 Ci.nsIX509CertDB.AppXPCShellRoot, 1144 original_app_path("cose_tampered_good_pkcs7"), 1145 check_open_result( 1146 "tampered COSE with good PKCS7 signature should fail " + 1147 "when only COSE is processed", 1148 Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, 1149 [] 1150 ) 1151 ); 1152 }); 1153 1154 // If we're not processing COSE, this should verify successfully. 1155 add_signature_test(PKCS7WithSHA1OrSHA256, function () { 1156 certdb.openSignedAppFileAsync( 1157 Ci.nsIX509CertDB.AppXPCShellRoot, 1158 original_app_path("cose_tampered_good_pkcs7"), 1159 check_open_result( 1160 "tampered COSE with good PKCS7 signature should succeed " + 1161 "when COSE is not processed", 1162 Cr.NS_OK, 1163 [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1] 1164 ) 1165 ); 1166 }); 1167 1168 add_test(function () { 1169 certdb.openSignedAppFileAsync( 1170 Ci.nsIX509CertDB.AppXPCShellRoot, 1171 original_app_path("bug_1411458"), 1172 check_open_result("bug 1411458", Cr.NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO, []) 1173 ); 1174 }); 1175 1176 // This has a big manifest file (~2MB). It should verify correctly. 1177 add_test(function () { 1178 certdb.openSignedAppFileAsync( 1179 Ci.nsIX509CertDB.AppXPCShellRoot, 1180 original_app_path("big_manifest"), 1181 check_open_result("add-on with big manifest file", Cr.NS_OK, [ 1182 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256, 1183 ]) 1184 ); 1185 }); 1186 1187 // This has a huge manifest file (~10MB). Manifest files this large are not 1188 // supported (8MB is the limit). It should not verify correctly. 1189 add_test(function () { 1190 certdb.openSignedAppFileAsync( 1191 Ci.nsIX509CertDB.AppXPCShellRoot, 1192 original_app_path("huge_manifest"), 1193 check_open_result( 1194 "add-on with huge manifest file", 1195 Cr.NS_ERROR_SIGNED_JAR_ENTRY_INVALID, 1196 [] 1197 ) 1198 ); 1199 }); 1200 1201 // Verification should pass despite a not-yet-valid EE certificate. 1202 // Regression test for bug 1713628 1203 add_test(function () { 1204 certdb.openSignedAppFileAsync( 1205 Ci.nsIX509CertDB.AppXPCShellRoot, 1206 original_app_path("validity_not_yet_valid"), 1207 check_open_result("validity_not_yet_valid", Cr.NS_OK, [ 1208 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256, 1209 ]) 1210 ); 1211 }); 1212 1213 // Verification should pass despite an expired EE certificate. 1214 // Regression test for bug 1267318 and bug 1548973 1215 add_test(function () { 1216 certdb.openSignedAppFileAsync( 1217 Ci.nsIX509CertDB.AppXPCShellRoot, 1218 original_app_path("validity_expired"), 1219 check_open_result("validity_expired", Cr.NS_OK, [ 1220 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256, 1221 ]) 1222 ); 1223 }); 1224 1225 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () { 1226 certdb.openSignedAppFileAsync( 1227 Ci.nsIX509CertDB.AppXPCShellRoot, 1228 original_app_path("alternate-root"), 1229 check_open_result("alternate-root", Cr.NS_OK, [ 1230 Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, 1231 Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256, 1232 ]) 1233 ); 1234 }); 1235 1236 // TODO: tampered MF, tampered SF 1237 // TODO: too-large MF, too-large RSA, too-large SF 1238 // TODO: MF and SF that end immediately after the last main header 1239 // (no CR nor LF) 1240 // TODO: broken headers to exercise the parser