Bug 1954818 - Add intermediate cert used until 2018 - ESR128 port r=jschanck,willdurand a=pascalc

This patch was modified from the original because this ESR branch
does not include the changes from bug 1914064.

The certificate was generated from the original in D242073 with:

```
$ openssl x509 \
  -in security/manager/ssl/addons-public-2018-intermediate.pem \
  -outform DER \
  -out security/manager/ssl/addons-public-2018-intermediate.crt
```

Original Revision: https://phabricator.services.mozilla.com/D242073

Differential Revision: https://phabricator.services.mozilla.com/D242078
This commit is contained in:
Rob Wu 2025-03-20 09:54:00 +00:00
parent e2904bee8c
commit 042609f471
8 changed files with 68 additions and 7 deletions

View file

@ -32,6 +32,12 @@
// Add-on signing Certificates // Add-on signing Certificates
#include "addons-public.inc" #include "addons-public.inc"
#include "addons-public-intermediate.inc" #include "addons-public-intermediate.inc"
#include "addons-public-2018-intermediate.inc"
const mozilla::Span<const uint8_t> addonsPublicIntermediates[] = {
mozilla::Span(addonsPublicIntermediate, sizeof(addonsPublicIntermediate)),
mozilla::Span(addonsPublic2018Intermediate,
sizeof(addonsPublic2018Intermediate)),
};
#include "addons-stage.inc" #include "addons-stage.inc"
#include "addons-stage-intermediate.inc" #include "addons-stage-intermediate.inc"
// Content signature root certificates // Content signature root certificates
@ -90,12 +96,15 @@ nsresult AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot) {
// The intermediate bundled with signed XPI files may have expired and be // The intermediate bundled with signed XPI files may have expired and be
// considered invalid, which can result in bug 1548973. // considered invalid, which can result in bug 1548973.
if (trustedRoot == nsIX509CertDB::AddonsPublicRoot) { if (trustedRoot == nsIX509CertDB::AddonsPublicRoot) {
mAddonsIntermediate = {addonsPublicIntermediate}; mAddonsIntermediates.AppendElements(
addonsPublicIntermediates, MOZ_ARRAY_LENGTH(addonsPublicIntermediates));
} }
// Similarly to the above logic for production, we hardcode the intermediate // Similarly to the above logic for production, we hardcode the intermediate
// stage certificate here, so that stage is equivalent to production. // stage certificate here, so that stage is equivalent to production.
if (trustedRoot == nsIX509CertDB::AddonsStageRoot) { if (trustedRoot == nsIX509CertDB::AddonsStageRoot) {
mAddonsIntermediate = {addonsStageIntermediate}; Span<const uint8_t> addonsStageIntermediateSpan = {
addonsStageIntermediate, sizeof(addonsStageIntermediate)};
mAddonsIntermediates.AppendElement(std::move(addonsStageIntermediateSpan));
} }
return NS_OK; return NS_OK;
@ -118,10 +127,10 @@ pkix::Result AppTrustDomain::FindIssuer(Input encodedIssuerName,
return rv; return rv;
} }
candidates.AppendElement(std::move(rootInput)); candidates.AppendElement(std::move(rootInput));
if (!mAddonsIntermediate.IsEmpty()) { for (const auto& intermediate : mAddonsIntermediates) {
Input intermediateInput; Input intermediateInput;
rv = intermediateInput.Init(mAddonsIntermediate.Elements(), pkix::Result rv =
mAddonsIntermediate.Length()); intermediateInput.Init(intermediate.Elements(), intermediate.Length());
// Again, this should never fail for the same reason as above. // Again, this should never fail for the same reason as above.
if (rv != Success) { if (rv != Success) {
return rv; return rv;

View file

@ -83,7 +83,7 @@ class AppTrustDomain final : public mozilla::pkix::TrustDomain {
private: private:
Span<const uint8_t> mTrustedRoot; Span<const uint8_t> mTrustedRoot;
Span<const uint8_t> mAddonsIntermediate; nsTArray<Span<const uint8_t>> mAddonsIntermediates;
nsTArray<Span<const uint8_t>> mIntermediates; nsTArray<Span<const uint8_t>> mIntermediates;
nsCOMPtr<nsICertStorage> mCertBlocklist; nsCOMPtr<nsICertStorage> mCertBlocklist;
}; };

View file

@ -32,6 +32,7 @@ def _create_header(array_name, cert_bytes):
# def arrayName(header, cert_filename): # def arrayName(header, cert_filename):
# header.write(_create_header("arrayName", cert_filename)) # header.write(_create_header("arrayName", cert_filename))
array_names = [ array_names = [
"addonsPublic2018Intermediate",
"addonsPublicIntermediate", "addonsPublicIntermediate",
"addonsPublicRoot", "addonsPublicRoot",
"addonsStageRoot", "addonsStageRoot",

View file

@ -238,6 +238,11 @@ headers_arrays_certs = [
"tests/unit/test_signed_apps/xpcshellTestRoot.der", "tests/unit/test_signed_apps/xpcshellTestRoot.der",
), ),
("addons-public.inc", "addonsPublicRoot", "addons-public.crt"), ("addons-public.inc", "addonsPublicRoot", "addons-public.crt"),
(
"addons-public-2018-intermediate.inc",
"addonsPublic2018Intermediate",
"addons-public-2018-intermediate.crt",
),
( (
"addons-public-intermediate.inc", "addons-public-intermediate.inc",
"addonsPublicIntermediate", "addonsPublicIntermediate",

View file

@ -23,7 +23,7 @@ function verifySignatures() {
}); });
} }
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4"); createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "48");
add_setup(async () => { add_setup(async () => {
await promiseStartupManager(); await promiseStartupManager();
@ -537,3 +537,49 @@ add_task(useAMOStageCert(), async function test_disable() {
await addon.uninstall(); await addon.uninstall();
AddonManager.removeAddonListener(listener); AddonManager.removeAddonListener(listener);
}); });
// Regression test for https://bugzilla.mozilla.org/show_bug.cgi?id=1954818
//
// Do NOT remove this test or the XPI files. If this test becomes obsolete due
// to dropped support for these XPI files (e.g. if support for add-ons with
// SHA-1 signatures were to be dropped entirely), don't forget to delete
// addons-public-2018-intermediate.pem (undo the patch to bug 1954818).
add_task(async function test_xpi_signed_in_or_before_feb_2018() {
// Disable schema warnings for two reasons:
// - The "commands" property in the manifest is not supported on Android.
// - The resigned version "2resigned1" results in the following warning:
// "version must be a version string consisting of at most 4 integers of at
// most 9 digits without leading zeros, and separated with dots"
ExtensionTestUtils.failOnSchemaWarnings(false);
async function checkAddonIsValid(xpiPath) {
let { addon } = await promiseInstallFile(do_get_file(xpiPath));
Assert.notEqual(addon, null);
Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_SIGNED);
Assert.ok(addon.isActive);
Assert.equal(addon.appDisabled, false);
await addon.uninstall();
}
// The test extension is chosen such that it was signed before 2018, because
// that was signed with CN=production-signing-ca.addons.mozilla.org
// instead of CN=signingca1.addons.mozilla.org (used after 8 feb 2018).
//
// "disable-ctrl-q-and-cmd-q@robwu.nl" is a simple extension consisting of
// one manifest.json. It was signed in 2016, and later resigned in 2024
// because of enforcing stronger signatures (starting with bug 1885004).
info("Checking add-on signed before 2018, 2016-12-22");
// Pre-2018 signed extensions only used SHA-1, so we need to relax the weak
// signature policy so we can verify that the signature validation passes.
// Otherwise installation may fail due to the restrictions from bug 1885004.
const resetWeakSignaturePref =
AddonTestUtils.setWeakSignatureInstallAllowed(true);
await checkAddonIsValid(`${DATA}/disable_ctrl_q_and_cmd_q-1.xpi`);
resetWeakSignaturePref();
info("Checking add-on signed after 2018, 2024-04-25");
await checkAddonIsValid(`${DATA}/disable_ctrl_q_and_cmd_q-2resigned1.xpi`);
ExtensionTestUtils.failOnSchemaWarnings(true);
});