summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_signed_apps.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/unit/test_signed_apps.js')
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps.js1038
1 files changed, 1038 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/test_signed_apps.js b/security/manager/ssl/tests/unit/test_signed_apps.js
new file mode 100644
index 0000000000..f97edf350e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps.js
@@ -0,0 +1,1038 @@
+"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