"use strict"; // Tests the API nsIX509CertDB.openSignedAppFileAsync, which backs add-on // signature verification. Testcases include various ways of tampering with // add-ons as well as different hash algorithms used in the various // signature/metadata files. // from prio.h const PR_RDWR = 0x04; const PR_CREATE_FILE = 0x08; const PR_TRUNCATE = 0x20; const PR_USEC_PER_MSEC = 1000; do_get_profile(); // must be called before getting nsIX509CertDB const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( Ci.nsIX509CertDB ); // Creates a new app package based in the inFilePath package, with a set of // modifications (including possibly deletions) applied to the existing entries, // and/or a set of new entries to be included. function tamper(inFilePath, outFilePath, modifications, newEntries) { let writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter); writer.open(outFilePath, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE); try { let reader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance( Ci.nsIZipReader ); reader.open(inFilePath); try { for (let entryName of reader.findEntries("")) { let inEntry = reader.getEntry(entryName); let entryInput = reader.getInputStream(entryName); try { let f = modifications[entryName]; let outEntry, outEntryInput; if (f) { [outEntry, outEntryInput] = f(inEntry, entryInput); delete modifications[entryName]; } else { [outEntry, outEntryInput] = [inEntry, entryInput]; } // if f does not want the input entry to be copied to the output entry // at all (i.e. it wants it to be deleted), it will return null. if (outEntryInput) { try { writer.addEntryStream( entryName, outEntry.lastModifiedTime, outEntry.compression, outEntryInput, false ); } finally { if (entryInput != outEntryInput) { outEntryInput.close(); } } } } finally { entryInput.close(); } } } finally { reader.close(); } // Any leftover modification means that we were expecting to modify an entry // in the input file that wasn't there. for (let name in modifications) { if (modifications.hasOwnProperty(name)) { throw new Error("input file was missing expected entries: " + name); } } // Now, append any new entries to the end newEntries.forEach(function(newEntry) { let sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( Ci.nsIStringInputStream ); try { sis.setData(newEntry.content, newEntry.content.length); writer.addEntryStream( newEntry.name, new Date() * PR_USEC_PER_MSEC, Ci.nsIZipWriter.COMPRESSION_BEST, sis, false ); } finally { sis.close(); } }); } finally { writer.close(); } } function removeEntry(entry, entryInput) { return [null, null]; } function truncateEntry(entry, entryInput) { if (entryInput.available() == 0) { throw new Error( "Truncating already-zero length entry will result in " + "identical entry." ); } let content = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( Ci.nsIStringInputStream ); content.data = ""; return [entry, content]; } function check_open_result(name, expectedRv) { return function openSignedAppFileCallback(rv, aZipReader, aSignerCert) { info("openSignedAppFileCallback called for " + name); equal(rv, expectedRv, "Actual and expected return value should match"); equal( aZipReader != null, Components.isSuccessCode(expectedRv), "ZIP reader should be null only if the return value denotes failure" ); equal( aSignerCert != null, Components.isSuccessCode(expectedRv), "Signer cert should be null only if the return value denotes failure" ); run_next_test(); }; } function original_app_path(test_name) { return do_get_file("test_signed_apps/" + test_name + ".zip", false); } function tampered_app_path(test_name) { return new FileUtils.File( PathUtils.join( Services.dirsvc.get("TmpD", Ci.nsIFile).path, `test_signed_app-${test_name}.zip` ) ); } var hashTestcases = [ // SHA-256 in PKCS#7 + SHA-256 present elsewhere => OK { name: "app_mf-1-256_sf-1-256_p7-1-256", expectedResult: Cr.NS_OK }, { name: "app_mf-1-256_sf-1-256_p7-256", expectedResult: Cr.NS_OK }, { name: "app_mf-1-256_sf-256_p7-1-256", expectedResult: Cr.NS_OK }, { name: "app_mf-1-256_sf-256_p7-256", expectedResult: Cr.NS_OK }, { name: "app_mf-256_sf-1-256_p7-1-256", expectedResult: Cr.NS_OK }, { name: "app_mf-256_sf-1-256_p7-256", expectedResult: Cr.NS_OK }, { name: "app_mf-256_sf-256_p7-1-256", expectedResult: Cr.NS_OK }, { name: "app_mf-256_sf-256_p7-256", expectedResult: Cr.NS_OK }, // SHA-1 in PKCS#7 + SHA-1 present elsewhere => OK { name: "app_mf-1-256_sf-1-256_p7-1", expectedResult: Cr.NS_OK }, { name: "app_mf-1-256_sf-1_p7-1", expectedResult: Cr.NS_OK }, { name: "app_mf-1_sf-1-256_p7-1", expectedResult: Cr.NS_OK }, { name: "app_mf-1_sf-1_p7-1", expectedResult: Cr.NS_OK }, // SHA-256 in PKCS#7 + SHA-256 not present elsewhere => INVALID { name: "app_mf-1-256_sf-1_p7-1-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-1-256_sf-1_p7-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-1_sf-1-256_p7-1-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-1_sf-1-256_p7-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-1_sf-1_p7-1-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-1_sf-1_p7-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-1_sf-256_p7-1-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-1_sf-256_p7-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-256_sf-1_p7-1-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-256_sf-1_p7-256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, // SHA-1 in PKCS#7 + SHA-1 not present elsewhere => INVALID { name: "app_mf-1-256_sf-256_p7-1", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-1_sf-256_p7-1", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-256_sf-1-256_p7-1", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-256_sf-1_p7-1", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, { name: "app_mf-256_sf-256_p7-1", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, }, ]; // Policy values for the preference "security.signed_app_signatures.policy" const PKCS7WithSHA1OrSHA256 = 0b0; const PKCS7WithSHA256 = 0b1; const COSEAndPKCS7WithSHA1OrSHA256 = 0b10; const COSEAndPKCS7WithSHA256 = 0b11; const COSERequiredAndPKCS7WithSHA1OrSHA256 = 0b100; const COSERequiredAndPKCS7WithSHA256 = 0b101; const COSEOnly = 0b110; const COSEOnlyAgain = 0b111; function add_signature_test(policy, test) { // First queue up a test to set the desired policy: add_test(function() { Services.prefs.setIntPref("security.signed_app_signatures.policy", policy); run_next_test(); }); // Then queue up the test itself: add_test(test); } for (let testcase of hashTestcases) { add_signature_test(PKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path(testcase.name), check_open_result(testcase.name, testcase.expectedResult) ); }); } add_signature_test(PKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("empty_signerInfos"), check_open_result( "the signerInfos in the PKCS#7 signature is empty", Cr.NS_ERROR_CMS_VERIFY_NOT_SIGNED ) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unsigned_app"), check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unknown_issuer_app"), check_open_result( "unknown_issuer", getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER) ) ); }); add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("cose_signed_with_pkcs7"), check_open_result("cose_signed_with_pkcs7", Cr.NS_OK) ); }); add_signature_test(COSEAndPKCS7WithSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("app_mf-256_sf-256_p7-256"), check_open_result("no COSE but correct PK#7", Cr.NS_OK) ); }); add_signature_test(COSEAndPKCS7WithSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("app_mf-1_sf-256_p7-256"), check_open_result( "no COSE and wrong PK#7 hash", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID ) ); }); add_signature_test(COSERequiredAndPKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("app_mf-256_sf-256_p7-256"), check_open_result( "COSE signature missing (SHA1 or 256)", Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE ) ); }); add_signature_test(COSERequiredAndPKCS7WithSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("app_mf-256_sf-256_p7-256"), check_open_result( "COSE signature missing (SHA256)", Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE ) ); }); add_signature_test(COSERequiredAndPKCS7WithSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("only_cose_signed"), check_open_result( "COSE signature only (PK#7 allowed, not present)", Cr.NS_OK ) ); }); add_signature_test(COSERequiredAndPKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("only_cose_signed"), check_open_result( "COSE signature only (PK#7 allowed, not present)", Cr.NS_OK ) ); }); add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("cose_multiple_signed_with_pkcs7"), check_open_result("cose_multiple_signed_with_pkcs7", Cr.NS_OK) ); }); add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("cose_int_signed_with_pkcs7"), check_open_result("COSE signed with an intermediate", Cr.NS_OK) ); }); add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("only_cose_signed"), check_open_result( "PK7 signature missing", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED ) ); }); add_signature_test(COSEOnly, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("cose_multiple_signed_with_pkcs7"), check_open_result( "Expected only COSE signature", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY ) ); }); add_signature_test(COSEOnly, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("only_cose_multiple_signed"), check_open_result("only Multiple COSE signatures", Cr.NS_OK) ); }); add_signature_test(COSEOnly, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("only_cose_signed"), check_open_result("only_cose_signed", Cr.NS_OK) ); }); add_signature_test(COSEOnlyAgain, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("only_cose_signed"), check_open_result("only_cose_signed (again)", Cr.NS_OK) ); }); add_signature_test(COSEOnly, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("cose_signed_with_pkcs7"), check_open_result( "COSE only expected but also PK#7 signed", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY ) ); }); // Sanity check to ensure a no-op tampering gives a valid result add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("identity_tampering"); tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, []); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("app_mf-1_sf-1_p7-1"), check_open_result("identity_tampering", Cr.NS_OK) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("missing_rsa"); tamper( original_app_path("app_mf-1_sf-1_p7-1"), tampered, { "META-INF/A.RSA": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("missing_sf"); tamper( original_app_path("app_mf-1_sf-1_p7-1"), tampered, { "META-INF/A.SF": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("missing_manifest_mf"); tamper( original_app_path("app_mf-1_sf-1_p7-1"), tampered, { "META-INF/MANIFEST.MF": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "missing_manifest_mf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID ) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("missing_entry"); tamper( original_app_path("app_mf-1_sf-1_p7-1"), tampered, { "manifest.json": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("truncated_entry"); tamper( original_app_path("app_mf-1_sf-1_p7-1"), tampered, { "manifest.json": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("truncated_entry", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("truncated_manifestFile"); tamper( original_app_path("app_mf-1_sf-1_p7-1"), tampered, { "META-INF/MANIFEST.MF": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "truncated_manifestFile", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID ) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("truncated_signatureFile"); tamper( original_app_path("app_mf-1_sf-1_p7-1"), tampered, { "META-INF/A.SF": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "truncated_signatureFile", getXPCOMStatusFromNSS(SEC_ERROR_PKCS7_BAD_SIGNATURE) ) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("truncated_pkcs7File"); tamper( original_app_path("app_mf-1_sf-1_p7-1"), tampered, { "META-INF/A.RSA": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("truncated_pkcs7File", Cr.NS_ERROR_CMS_VERIFY_NOT_SIGNED) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("unsigned_entry"); tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, [ { name: "unsigned.txt", content: "unsigned content!" }, ]); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("unsigned_entry", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY) ); }); add_signature_test(PKCS7WithSHA1OrSHA256, function() { let tampered = tampered_app_path("unsigned_metainf_entry"); tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, [ { name: "META-INF/unsigned.txt", content: "unsigned content!" }, ]); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "unsigned_metainf_entry", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY ) ); }); add_signature_test(PKCS7WithSHA256, function testSHA1Disabled() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("app_mf-1_sf-1_p7-1"), check_open_result( "SHA-1 should not be accepted if disabled by policy", Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE ) ); }); add_signature_test(PKCS7WithSHA256, function testSHA256WorksWithSHA1Disabled() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("app_mf-256_sf-256_p7-256"), check_open_result( "SHA-256 should work if SHA-1 is disabled by policy", Cr.NS_OK ) ); }); add_signature_test( PKCS7WithSHA256, function testMultipleSignaturesWorkWithSHA1Disabled() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("app_mf-1-256_sf-1-256_p7-1-256"), check_open_result( "Multiple signatures should work if SHA-1 is " + "disabled by policy (if SHA-256 signature verifies)", Cr.NS_OK ) ); } ); var cosePolicies = [ COSEAndPKCS7WithSHA1OrSHA256, COSERequiredAndPKCS7WithSHA1OrSHA256, ]; // PS256 is not yet supported. var coseTestcasesStage = [ { name: "autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-ES384", expectedResult: Cr.NS_OK, root: Ci.nsIX509CertDB.AddonsStageRoot, }, { name: "autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-PS256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, root: Ci.nsIX509CertDB.AddonsStageRoot, }, { name: "autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256", expectedResult: Cr.NS_OK, root: Ci.nsIX509CertDB.AddonsStageRoot, }, { name: "autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-PS256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, root: Ci.nsIX509CertDB.AddonsStageRoot, }, ]; var coseTestcasesProd = [ { name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384", expectedResult: Cr.NS_OK, root: Ci.nsIX509CertDB.AddonsPublicRoot, }, { name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, root: Ci.nsIX509CertDB.AddonsPublicRoot, }, { name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256", expectedResult: Cr.NS_OK, root: Ci.nsIX509CertDB.AddonsPublicRoot, }, { name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256", expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, root: Ci.nsIX509CertDB.AddonsPublicRoot, }, ]; for (let policy of cosePolicies) { for (let testcase of [...coseTestcasesStage, ...coseTestcasesProd]) { add_signature_test(policy, function() { certdb.openSignedAppFileAsync( testcase.root, original_app_path(testcase.name), check_open_result(testcase.name, testcase.expectedResult) ); }); } } add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigTampered() { let tampered = tampered_app_path("cose_sig_tampered"); tamper( original_app_path("cose_signed_with_pkcs7"), tampered, { "META-INF/cose.sig": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "cose_sig_tampered", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY ) ); }); // PKCS7 is processed before COSE, so if a COSE signature file is removed or // tampered with, this appears as a PKCS7 signature verification failure. add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigRemoved() { let tampered = tampered_app_path("cose_sig_removed"); tamper( original_app_path("cose_signed_with_pkcs7"), tampered, { "META-INF/cose.sig": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("cose_sig_removed", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING) ); }); add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestTampered() { let tampered = tampered_app_path("cose_manifest_tampered"); tamper( original_app_path("cose_signed_with_pkcs7"), tampered, { "META-INF/cose.manifest": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "cose_manifest_tampered", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY ) ); }); add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestRemoved() { let tampered = tampered_app_path("cose_manifest_removed"); tamper( original_app_path("cose_signed_with_pkcs7"), tampered, { "META-INF/cose.manifest": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "cose_manifest_removed", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING ) ); }); add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileAdded() { let tampered = tampered_app_path("cose_file_added"); tamper(original_app_path("cose_signed_with_pkcs7"), tampered, {}, [ { name: "unsigned.txt", content: "unsigned content!" }, ]); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("cose_file_added", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY) ); }); add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileRemoved() { let tampered = tampered_app_path("cose_file_removed"); tamper( original_app_path("cose_signed_with_pkcs7"), tampered, { "manifest.json": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result("cose_file_removed", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING) ); }); add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileTampered() { let tampered = tampered_app_path("cose_file_tampered"); tamper( original_app_path("cose_signed_with_pkcs7"), tampered, { "manifest.json": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "cose_file_tampered", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY ) ); }); add_signature_test(COSEOnly, function testOnlyCOSESigTampered() { let tampered = tampered_app_path("only_cose_sig_tampered"); tamper( original_app_path("only_cose_signed"), tampered, { "META-INF/cose.sig": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "only_cose_sig_tampered", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID ) ); }); add_signature_test(COSEOnly, function testOnlyCOSESigRemoved() { let tampered = tampered_app_path("only_cose_sig_removed"); tamper( original_app_path("only_cose_signed"), tampered, { "META-INF/cose.sig": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "only_cose_sig_removed", Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE ) ); }); add_signature_test(COSEOnly, function testOnlyCOSEManifestTampered() { let tampered = tampered_app_path("only_cose_manifest_tampered"); tamper( original_app_path("only_cose_signed"), tampered, { "META-INF/cose.manifest": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "only_cose_manifest_tampered", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID ) ); }); add_signature_test(COSEOnly, function testOnlyCOSEManifestRemoved() { let tampered = tampered_app_path("only_cose_manifest_removed"); tamper( original_app_path("only_cose_signed"), tampered, { "META-INF/cose.manifest": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "only_cose_manifest_removed", Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE ) ); }); add_signature_test(COSEOnly, function testOnlyCOSEFileAdded() { let tampered = tampered_app_path("only_cose_file_added"); tamper(original_app_path("only_cose_signed"), tampered, {}, [ { name: "unsigned.txt", content: "unsigned content!" }, ]); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "only_cose_file_added", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY ) ); }); add_signature_test(COSEOnly, function testOnlyCOSEFileRemoved() { let tampered = tampered_app_path("only_cose_file_removed"); tamper( original_app_path("only_cose_signed"), tampered, { "manifest.json": removeEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "only_cose_file_removed", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING ) ); }); add_signature_test(COSEOnly, function testOnlyCOSEFileTampered() { let tampered = tampered_app_path("only_cose_file_tampered"); tamper( original_app_path("only_cose_signed"), tampered, { "manifest.json": truncateEntry }, [] ); certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, tampered, check_open_result( "only_cose_file_tampered", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY ) ); }); // This was signed with only COSE first, and then the contents were tampered // with (making the signature invalid). Then, the file was signed with // PKCS7/SHA1. We need to ensure that if we're configured to process COSE, this // verification fails. add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("cose_tampered_good_pkcs7"), check_open_result( "tampered COSE with good PKCS7 signature should fail " + "when COSE and PKCS7 is processed", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY ) ); }); add_signature_test(COSEOnly, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("cose_tampered_good_pkcs7"), check_open_result( "tampered COSE with good PKCS7 signature should fail " + "when only COSE is processed", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY ) ); }); // If we're not processing COSE, this should verify successfully. add_signature_test(PKCS7WithSHA1OrSHA256, function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("cose_tampered_good_pkcs7"), check_open_result( "tampered COSE with good PKCS7 signature should succeed" + "when COSE is not processed", Cr.NS_OK ) ); }); add_test(function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("bug_1411458"), check_open_result("bug 1411458", Cr.NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO) ); }); // This has a big manifest file (~2MB). It should verify correctly. add_test(function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("big_manifest"), check_open_result("add-on with big manifest file", Cr.NS_OK) ); }); // This has a huge manifest file (~10MB). Manifest files this large are not // supported (8MB is the limit). It should not verify correctly. add_test(function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("huge_manifest"), check_open_result( "add-on with huge manifest file", Cr.NS_ERROR_SIGNED_JAR_ENTRY_INVALID ) ); }); // Verification should pass despite a not-yet-valid EE certificate. // Regression test for bug 1713628 add_test(function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("validity_not_yet_valid"), check_open_result("validity_not_yet_valid", Cr.NS_OK) ); }); // Verification should pass despite an expired EE certificate. // Regression test for bug 1267318 and bug 1548973 add_test(function() { certdb.openSignedAppFileAsync( Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("validity_expired"), check_open_result("validity_expired", Cr.NS_OK) ); }); // TODO: tampered MF, tampered SF // TODO: too-large MF, too-large RSA, too-large SF // TODO: MF and SF that end immediately after the last main header // (no CR nor LF) // TODO: broken headers to exercise the parser