Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'win' && msix
- Manifest: security/manager/ssl/tests/unit/xpcshell.toml
"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() {
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, expectedSignatureAlgorithms) {
return function openSignedAppFileCallback(rv, aZipReader, aSignatureInfos) {
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(
aSignatureInfos.length,
expectedSignatureAlgorithms.length,
"Should have the same number of expected signature infos"
);
for (let i = 0; i < expectedSignatureAlgorithms.length; i++) {
equal(
aSignatureInfos[i].signatureAlgorithm,
expectedSignatureAlgorithms[i],
"Should have expected signature algorithm"
);
}
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,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
},
{
name: "app_mf-1-256_sf-1-256_p7-256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
},
{
name: "app_mf-1-256_sf-256_p7-1-256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
},
{
name: "app_mf-1-256_sf-256_p7-256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
},
{
name: "app_mf-256_sf-1-256_p7-1-256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
},
{
name: "app_mf-256_sf-1-256_p7-256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
},
{
name: "app_mf-256_sf-256_p7-1-256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
},
{
name: "app_mf-256_sf-256_p7-256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
},
// SHA-1 in PKCS#7 + SHA-1 present elsewhere => OK
{
name: "app_mf-1-256_sf-1-256_p7-1",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1],
},
{
name: "app_mf-1-256_sf-1_p7-1",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1],
},
{
name: "app_mf-1_sf-1-256_p7-1",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1],
},
{
name: "app_mf-1_sf-1_p7-1",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1],
},
// 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,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-1-256_sf-1_p7-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-1_sf-1-256_p7-1-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-1_sf-1-256_p7-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-1_sf-1_p7-1-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-1_sf-1_p7-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-1_sf-256_p7-1-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-1_sf-256_p7-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-256_sf-1_p7-1-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-256_sf-1_p7-256",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
// 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,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-1_sf-256_p7-1",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-256_sf-1-256_p7-1",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-256_sf-1_p7-1",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
{
name: "app_mf-256_sf-256_p7-1",
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
},
];
// Policy values for the preference "security.signed_app_signatures.policy"
const PKCS7WithSHA1OrSHA256 = 0b0;
const PKCS7_WITH_SHA256 = 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,
testcase.expectedSignatureAlgorithms
)
);
});
}
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, [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
])
);
});
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, [
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
])
);
});
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,
[Ci.nsIAppSignatureInfo.COSE_WITH_SHA256]
)
);
});
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,
[Ci.nsIAppSignatureInfo.COSE_WITH_SHA256]
)
);
});
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, [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
])
);
});
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, [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
])
);
});
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, [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
])
);
});
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, [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
])
);
});
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, [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
])
);
});
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, [
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
])
);
});
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(PKCS7_WITH_SHA256, 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(
PKCS7_WITH_SHA256,
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,
[Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256]
)
);
}
);
add_signature_test(
PKCS7_WITH_SHA256,
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,
[Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256]
)
);
}
);
var cosePolicies = [
COSEAndPKCS7WithSHA1OrSHA256,
COSERequiredAndPKCS7WithSHA1OrSHA256,
];
// NOTE: The zip files referenced in coseTestcasesStage and coseTestcasesProd
// were originally generated with
// Since then, the mechanism to sign these packages have changed, see
var coseTestcasesStage = [
{
name: "addons-stage-tomato-clock-sha1-es256-es384",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
],
root: Ci.nsIX509CertDB.AddonsStageRoot,
},
{
name: "addons-stage-tomato-clock-sha1-es256-ps256",
// PS256 is not yet supported.
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
root: Ci.nsIX509CertDB.AddonsStageRoot,
},
{
name: "addons-stage-tomato-clock-sha1-es256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
],
root: Ci.nsIX509CertDB.AddonsStageRoot,
},
{
name: "addons-stage-tomato-clock-sha1-ps256",
// PS256 is not yet supported.
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
root: Ci.nsIX509CertDB.AddonsStageRoot,
},
];
var coseTestcasesProd = [
{
name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
],
root: Ci.nsIX509CertDB.AddonsPublicRoot,
},
{
name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256",
// PS256 is not yet supported.
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
root: Ci.nsIX509CertDB.AddonsPublicRoot,
},
{
name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256",
expectedResult: Cr.NS_OK,
expectedSignatureAlgorithms: [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
],
root: Ci.nsIX509CertDB.AddonsPublicRoot,
},
{
name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256",
// PS256 is not yet supported.
expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
expectedSignatureAlgorithms: [],
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,
testcase.expectedSignatureAlgorithms
)
);
});
}
}
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,
[Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1]
)
);
});
add_test(function () {
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot,
original_app_path("bug_1411458"),
);
});
// 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, [
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
])
);
});
// 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.
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, [
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
])
);
});
// Verification should pass despite an expired EE certificate.
add_test(function () {
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot,
original_app_path("validity_expired"),
check_open_result("validity_expired", Cr.NS_OK, [
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
])
);
});
add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () {
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot,
original_app_path("alternate-root"),
check_open_result("alternate-root", Cr.NS_OK, [
Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
])
);
});
// 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