summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_cert_storage.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/unit/test_cert_storage.js')
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage.js258
1 files changed, 258 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/test_cert_storage.js b/security/manager/ssl/tests/unit/test_cert_storage.js
new file mode 100644
index 0000000000..e6bd4d944b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage.js
@@ -0,0 +1,258 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+// This test checks a number of things:
+// * it ensures that data loaded from revocations.txt on startup is present
+// * it ensures that data served from OneCRL are persisted correctly
+// * it ensures that items in the CertBlocklist are seen as revoked by the
+// cert verifier
+// * it does a sanity check to ensure other cert verifier behavior is
+// unmodified
+
+const { RemoteSecuritySettings } = ChromeUtils.importESModule(
+ "resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs"
+);
+
+// First, we need to setup appInfo for the blocklist service to work
+var id = "xpcshell@tests.mozilla.org";
+var appName = "XPCShell";
+var version = "1";
+var platformVersion = "1.9.2";
+const { updateAppInfo } = ChromeUtils.importESModule(
+ "resource://testing-common/AppInfo.sys.mjs"
+);
+updateAppInfo({
+ name: appName,
+ ID: id,
+ version,
+ platformVersion: platformVersion ? platformVersion : "1.0",
+ crashReporter: true,
+});
+
+// we need to ensure we setup revocation data before certDB, or we'll start with
+// no revocation.txt in the profile
+var gProfile = do_get_profile();
+
+var gRevocations = gProfile.clone();
+gRevocations.append("revocations.txt");
+if (!gRevocations.exists()) {
+ let existing = do_get_file("test_onecrl/sample_revocations.txt", false);
+ existing.copyTo(gProfile, "revocations.txt");
+}
+
+var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const certBlocklist = [
+ // test with some bad data ...
+ {
+ issuerName: "Some nonsense in issuer",
+ serialNumber: "AkHVNA==",
+ },
+ {
+ issuerName: "MA0xCzAJBgNVBAMMAmNh",
+ serialNumber: "some nonsense in serial",
+ },
+ {
+ issuerName: "and serial",
+ serialNumber: "some nonsense in both issuer",
+ },
+ // some mixed
+ // In these case, the issuer name and the valid serialNumber correspond
+ // to test-int.pem in bad_certs/
+ {
+ issuerName: "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=",
+ serialNumber: "oops! more nonsense.",
+ },
+ {
+ issuerName: "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=",
+ serialNumber: "a0X7/7DlTaedpgrIJg25iBPOkIM=",
+ },
+ // ... and some good
+ // In this case, the issuer name and the valid serialNumber correspond
+ // to other-test-ca.pem in bad_certs/ (for testing root revocation)
+ {
+ issuerName: "MBgxFjAUBgNVBAMMDU90aGVyIHRlc3QgQ0E=",
+ serialNumber: "Rym6o+VN9xgZXT/QLrvN/nv1ZN4=",
+ },
+ // These items correspond to an entry in sample_revocations.txt where:
+ // isser name is the base-64 encoded subject DN for the shared Test
+ // Intermediate and the serialNumbers are base-64 encoded 78 and 31,
+ // respectively.
+ // We need this to ensure that existing items are retained if they're
+ // also in the blocklist
+ {
+ issuerName: "MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl",
+ serialNumber: "Tg==",
+ },
+ {
+ issuerName: "MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl",
+ serialNumber: "Hw==",
+ },
+ // This item revokes same-issuer-ee.pem by subject and pubKeyHash.
+ {
+ subject: "MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5",
+ pubKeyHash: "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=",
+ },
+];
+
+function verify_cert(file, expectedError) {
+ let ee = constructCertFromFile(file);
+ return checkCertErrorGeneric(
+ certDB,
+ ee,
+ expectedError,
+ certificateUsageSSLServer
+ );
+}
+
+// The certificate blocklist currently only applies to TLS server certificates.
+async function verify_non_tls_usage_succeeds(file) {
+ let ee = constructCertFromFile(file);
+ await checkCertErrorGeneric(
+ certDB,
+ ee,
+ PRErrorCodeSuccess,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certDB,
+ ee,
+ PRErrorCodeSuccess,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certDB,
+ ee,
+ PRErrorCodeSuccess,
+ certificateUsageEmailRecipient
+ );
+}
+
+function load_cert(cert, trust) {
+ let file = "bad_certs/" + cert + ".pem";
+ addCertFromFile(certDB, file, trust);
+}
+
+async function update_blocklist() {
+ const { OneCRLBlocklistClient } = RemoteSecuritySettings.init();
+
+ const fakeEvent = {
+ current: certBlocklist, // with old .txt revocations.
+ deleted: [],
+ created: certBlocklist, // with new cert storage.
+ updated: [],
+ };
+ await OneCRLBlocklistClient.emit("sync", { data: fakeEvent });
+ // Save the last check timestamp, used by cert_storage to assert
+ // if the blocklist is «fresh».
+ Services.prefs.setIntPref(
+ OneCRLBlocklistClient.lastCheckTimePref,
+ Math.floor(Date.now() / 1000)
+ );
+}
+
+function run_test() {
+ // import the certificates we need
+ load_cert("test-ca", "CTu,CTu,CTu");
+ load_cert("test-int", ",,");
+ load_cert("other-test-ca", "CTu,CTu,CTu");
+
+ add_task(async function () {
+ // check some existing items in revocations.txt are blocked.
+ // This test corresponds to:
+ // issuer: MBIxEDAOBgNVBAMMB1Rlc3QgQ0E= (CN=Test CA)
+ // serial: Kg== (42)
+ let file = "test_onecrl/ee-revoked-by-revocations-txt.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // This test corresponds to:
+ // issuer: MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl (CN=Test Intermediate)
+ // serial: Tg== (78)
+ file = "test_onecrl/another-ee-revoked-by-revocations-txt.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // And this test corresponds to:
+ // issuer: MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl (CN=Test Intermediate)
+ // serial: Hw== (31)
+ // (we test this issuer twice to ensure we can read multiple serials)
+ file = "test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // Test that a certificate revoked by subject and public key hash in
+ // revocations.txt is revoked
+ // subject: MCsxKTAnBgNVBAMMIEVFIFJldm9rZWQgQnkgU3ViamVjdCBhbmQgUHViS2V5
+ // (CN=EE Revoked By Subject and PubKey)
+ // pubkeyhash: VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8= (this is the
+ // shared RSA SPKI)
+ file = "test_onecrl/ee-revoked-by-subject-and-pubkey.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // Soon we'll load a blocklist which revokes test-int.pem, which issued
+ // test-int-ee.pem.
+ // Check the cert validates before we load the blocklist
+ file = "test_onecrl/test-int-ee.pem";
+ await verify_cert(file, PRErrorCodeSuccess);
+
+ // The blocklist also revokes other-test-ca.pem, which issued
+ // other-ca-ee.pem. Check the cert validates before we load the blocklist
+ file = "bad_certs/other-issuer-ee.pem";
+ await verify_cert(file, PRErrorCodeSuccess);
+
+ // The blocklist will revoke same-issuer-ee.pem via subject / pubKeyHash.
+ // Check the cert validates before we load the blocklist
+ file = "test_onecrl/same-issuer-ee.pem";
+ await verify_cert(file, PRErrorCodeSuccess);
+ });
+
+ // blocklist load is async so we must use add_test from here
+ add_task(update_blocklist);
+
+ add_task(async function () {
+ // The blocklist will be loaded now. Let's check the data is sane.
+ // In particular, we should still have the revoked issuer / serial pair
+ // that was in revocations.txt but not the blocklist.
+ let file = "test_onecrl/ee-revoked-by-revocations-txt.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // We should also still have the revoked issuer / serial pairs that were in
+ // revocations.txt and are also in the blocklist.
+ file = "test_onecrl/another-ee-revoked-by-revocations-txt.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+ file = "test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // The cert revoked by subject and pubkeyhash should still be revoked.
+ file = "test_onecrl/ee-revoked-by-subject-and-pubkey.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // Check the blocklisted intermediate now causes a failure
+ file = "test_onecrl/test-int-ee.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+ await verify_non_tls_usage_succeeds(file);
+
+ // Check the ee with the blocklisted root also causes a failure
+ file = "bad_certs/other-issuer-ee.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+ await verify_non_tls_usage_succeeds(file);
+
+ // Check the ee blocked by subject / pubKey causes a failure
+ file = "test_onecrl/same-issuer-ee.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+ await verify_non_tls_usage_succeeds(file);
+
+ // Check a non-blocklisted chain still validates OK
+ file = "bad_certs/default-ee.pem";
+ await verify_cert(file, PRErrorCodeSuccess);
+
+ // Check a bad cert is still bad (unknown issuer)
+ file = "bad_certs/unknownissuer.pem";
+ await verify_cert(file, SEC_ERROR_UNKNOWN_ISSUER);
+ });
+
+ run_next_test();
+}