mirror of
https://github.com/Feodor2/Mypal68.git
synced 2025-06-18 14:55:44 -04:00
1013 lines
29 KiB
JavaScript
1013 lines
29 KiB
JavaScript
"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;
|
|
|
|
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(),
|
|
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 FileUtils.getFile("TmpD", ["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) {
|
|
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
|
|
)
|
|
);
|
|
});
|
|
|
|
// 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
|