summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_cert_storage_direct
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct.js417
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert-issuer.pem27
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert.pem41
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.crlitebin0 -> 15244 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert-issuer.pem27
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert.pem34
6 files changed, 546 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct.js b/security/manager/ssl/tests/unit/test_cert_storage_direct.js
new file mode 100644
index 0000000000..a1ba818dd9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct.js
@@ -0,0 +1,417 @@
+/* -*- 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 file consists of unit tests for cert_storage (whereas test_cert_storage.js is more of an
+// integration test).
+
+do_get_profile();
+
+this.certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+);
+
+async function addCerts(certInfos) {
+ let result = await new Promise(resolve => {
+ certStorage.addCerts(certInfos, resolve);
+ });
+ Assert.equal(result, Cr.NS_OK, "addCerts should succeed");
+}
+
+async function removeCertsByHashes(hashesBase64) {
+ let result = await new Promise(resolve => {
+ certStorage.removeCertsByHashes(hashesBase64, resolve);
+ });
+ Assert.equal(result, Cr.NS_OK, "removeCertsByHashes should succeed");
+}
+
+function getLongString(uniquePart, length) {
+ return String(uniquePart).padStart(length, "0");
+}
+
+class CertInfo {
+ constructor(cert, subject) {
+ this.cert = btoa(cert);
+ this.subject = btoa(subject);
+ this.trust = Ci.nsICertStorage.TRUST_INHERIT;
+ }
+}
+CertInfo.prototype.QueryInterface = ChromeUtils.generateQI(["nsICertInfo"]);
+
+add_task(async function test_common_subject() {
+ let someCert1 = new CertInfo(
+ "some certificate bytes 1",
+ "some common subject"
+ );
+ let someCert2 = new CertInfo(
+ "some certificate bytes 2",
+ "some common subject"
+ );
+ let someCert3 = new CertInfo(
+ "some certificate bytes 3",
+ "some common subject"
+ );
+ await addCerts([someCert1, someCert2, someCert3]);
+ let storedCerts = certStorage.findCertsBySubject(
+ stringToArray("some common subject")
+ );
+ let storedCertsAsStrings = storedCerts.map(arrayToString);
+ let expectedCerts = [
+ "some certificate bytes 1",
+ "some certificate bytes 2",
+ "some certificate bytes 3",
+ ];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should find expected certs"
+ );
+
+ await addCerts([
+ new CertInfo("some other certificate bytes", "some other subject"),
+ ]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("some common subject")
+ );
+ storedCertsAsStrings = storedCerts.map(arrayToString);
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should still find expected certs"
+ );
+
+ let storedOtherCerts = certStorage.findCertsBySubject(
+ stringToArray("some other subject")
+ );
+ let storedOtherCertsAsStrings = storedOtherCerts.map(arrayToString);
+ let expectedOtherCerts = ["some other certificate bytes"];
+ Assert.deepEqual(
+ storedOtherCertsAsStrings,
+ expectedOtherCerts,
+ "should have other certificate"
+ );
+});
+
+add_task(async function test_many_entries() {
+ const NUM_CERTS = 500;
+ const CERT_LENGTH = 3000;
+ const SUBJECT_LENGTH = 40;
+ let certs = [];
+ for (let i = 0; i < NUM_CERTS; i++) {
+ certs.push(
+ new CertInfo(
+ getLongString(i, CERT_LENGTH),
+ getLongString(i, SUBJECT_LENGTH)
+ )
+ );
+ }
+ await addCerts(certs);
+ for (let i = 0; i < NUM_CERTS; i++) {
+ let subject = stringToArray(getLongString(i, SUBJECT_LENGTH));
+ let storedCerts = certStorage.findCertsBySubject(subject);
+ Assert.equal(
+ storedCerts.length,
+ 1,
+ "should have 1 certificate (lots of data test)"
+ );
+ let storedCertAsString = arrayToString(storedCerts[0]);
+ Assert.equal(
+ storedCertAsString,
+ getLongString(i, CERT_LENGTH),
+ "certificate should be as expected (lots of data test)"
+ );
+ }
+});
+
+add_task(async function test_removal() {
+ // As long as cert_storage is given valid base64, attempting to delete some nonexistent
+ // certificate will "succeed" (it'll do nothing).
+ await removeCertsByHashes([btoa("thishashisthewrongsize")]);
+
+ let removalCert1 = new CertInfo(
+ "removal certificate bytes 1",
+ "common subject to remove"
+ );
+ let removalCert2 = new CertInfo(
+ "removal certificate bytes 2",
+ "common subject to remove"
+ );
+ let removalCert3 = new CertInfo(
+ "removal certificate bytes 3",
+ "common subject to remove"
+ );
+ await addCerts([removalCert1, removalCert2, removalCert3]);
+
+ let storedCerts = certStorage.findCertsBySubject(
+ stringToArray("common subject to remove")
+ );
+ let storedCertsAsStrings = storedCerts.map(arrayToString);
+ let expectedCerts = [
+ "removal certificate bytes 1",
+ "removal certificate bytes 2",
+ "removal certificate bytes 3",
+ ];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should find expected certs before removing them"
+ );
+
+ // echo -n "removal certificate bytes 2" | sha256sum | xxd -r -p | base64
+ await removeCertsByHashes(["2nUPHwl5TVr1mAD1FU9FivLTlTb0BAdnVUhsYgBccN4="]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("common subject to remove")
+ );
+ storedCertsAsStrings = storedCerts.map(arrayToString);
+ expectedCerts = [
+ "removal certificate bytes 1",
+ "removal certificate bytes 3",
+ ];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should only have first and third certificates now"
+ );
+
+ // echo -n "removal certificate bytes 1" | sha256sum | xxd -r -p | base64
+ await removeCertsByHashes(["8zoRqHYrklr7Zx6UWpzrPuL+ol8KL1Ml6XHBQmXiaTY="]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("common subject to remove")
+ );
+ storedCertsAsStrings = storedCerts.map(arrayToString);
+ expectedCerts = ["removal certificate bytes 3"];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should only have third certificate now"
+ );
+
+ // echo -n "removal certificate bytes 3" | sha256sum | xxd -r -p | base64
+ await removeCertsByHashes(["vZn7GwDSabB/AVo0T+N26nUsfSXIIx4NgQtSi7/0p/w="]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("common subject to remove")
+ );
+ Assert.equal(storedCerts.length, 0, "shouldn't have any certificates now");
+
+ // echo -n "removal certificate bytes 3" | sha256sum | xxd -r -p | base64
+ // Again, removing a nonexistent certificate should "succeed".
+ await removeCertsByHashes(["vZn7GwDSabB/AVo0T+N26nUsfSXIIx4NgQtSi7/0p/w="]);
+});
+
+add_task(async function test_batched_removal() {
+ let removalCert1 = new CertInfo(
+ "batch removal certificate bytes 1",
+ "batch subject to remove"
+ );
+ let removalCert2 = new CertInfo(
+ "batch removal certificate bytes 2",
+ "batch subject to remove"
+ );
+ let removalCert3 = new CertInfo(
+ "batch removal certificate bytes 3",
+ "batch subject to remove"
+ );
+ await addCerts([removalCert1, removalCert2, removalCert3]);
+ let storedCerts = certStorage.findCertsBySubject(
+ stringToArray("batch subject to remove")
+ );
+ let storedCertsAsStrings = storedCerts.map(arrayToString);
+ let expectedCerts = [
+ "batch removal certificate bytes 1",
+ "batch removal certificate bytes 2",
+ "batch removal certificate bytes 3",
+ ];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should find expected certs before removing them"
+ );
+ // echo -n "batch removal certificate bytes 1" | sha256sum | xxd -r -p | base64
+ // echo -n "batch removal certificate bytes 2" | sha256sum | xxd -r -p | base64
+ // echo -n "batch removal certificate bytes 3" | sha256sum | xxd -r -p | base64
+ await removeCertsByHashes([
+ "EOEEUTuanHZX9NFVCoMKVT22puIJC6g+ZuNPpJgvaa8=",
+ "Xz6h/Kvn35cCLJEZXkjPqk1GG36b56sreLyAXpO+0zg=",
+ "Jr7XdiTT8ZONUL+ogNNMW2oxKxanvYOLQPKBPgH/has=",
+ ]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("batch subject to remove")
+ );
+ Assert.equal(storedCerts.length, 0, "shouldn't have any certificates now");
+});
+
+class CRLiteCoverage {
+ constructor(ctLogID, minTimestamp, maxTimestamp) {
+ this.b64LogID = ctLogID;
+ this.minTimestamp = minTimestamp;
+ this.maxTimestamp = maxTimestamp;
+ }
+}
+CRLiteCoverage.prototype.QueryInterface = ChromeUtils.generateQI([
+ "nsICRLiteCoverage",
+]);
+
+add_task(async function test_crlite_filter() {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ addCertFromFile(
+ certdb,
+ "test_cert_storage_direct/valid-cert-issuer.pem",
+ ",,"
+ );
+ let validCert = constructCertFromFile(
+ "test_cert_storage_direct/valid-cert.pem"
+ );
+ addCertFromFile(
+ certdb,
+ "test_cert_storage_direct/revoked-cert-issuer.pem",
+ ",,"
+ );
+ let revokedCert = constructCertFromFile(
+ "test_cert_storage_direct/revoked-cert.pem"
+ );
+ let filterFile = do_get_file(
+ "test_cert_storage_direct/test-filter.crlite",
+ false
+ );
+ ok(filterFile.exists(), "test filter file should exist");
+ let enrollment = [];
+ let coverage = [];
+ let filterBytes = stringToArray(readFile(filterFile));
+ // First simualte a filter that does not cover any certificates. With CRLite
+ // enabled, none of the certificates should appear to be revoked.
+ let setFullCRLiteFilterResult = await new Promise(resolve => {
+ certStorage.setFullCRLiteFilter(filterBytes, enrollment, coverage, resolve);
+ });
+ Assert.equal(
+ setFullCRLiteFilterResult,
+ Cr.NS_OK,
+ "setFullCRLiteFilter should succeed"
+ );
+
+ Services.prefs.setIntPref(
+ "security.pki.crlite_mode",
+ CRLiteModeEnforcePrefValue
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+
+ // Now replace the filter with one that covers the "valid" and "revoked"
+ // certificates. CRLite should flag the revoked certificate.
+ coverage.push(
+ new CRLiteCoverage(
+ "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=",
+ 0,
+ 1641612275000
+ )
+ );
+
+ // crlite_enrollment_id.py test_crlite_filters/issuer.pem
+ enrollment.push("UbH9/ZAnjuqf79Xhah1mFOWo6ZvgQCgsdheWfjvVUM8=");
+ // crlite_enrollment_id.py test_crlite_filters/no-sct-issuer.pem
+ enrollment.push("Myn7EasO1QikOtNmo/UZdh6snCAw0BOY6wgU8OsUeeY=");
+ // crlite_enrollment_id.py test_cert_storage_direct/revoked-cert-issuer.pem
+ enrollment.push("HTvSp2263dqBYtgYA2fldKAoTYcEVLPVTlRia9XaoCQ=");
+
+ setFullCRLiteFilterResult = await new Promise(resolve => {
+ certStorage.setFullCRLiteFilter(filterBytes, enrollment, coverage, resolve);
+ });
+ Assert.equal(
+ setFullCRLiteFilterResult,
+ Cr.NS_OK,
+ "setFullCRLiteFilter should succeed"
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+
+ // If we're only collecting telemetry, none of the certificates should appear to be revoked.
+ Services.prefs.setIntPref(
+ "security.pki.crlite_mode",
+ CRLiteModeTelemetryOnlyPrefValue
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+
+ // If CRLite is disabled, none of the certificates should appear to be revoked.
+ Services.prefs.setIntPref(
+ "security.pki.crlite_mode",
+ CRLiteModeDisabledPrefValue
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert-issuer.pem b/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert-issuer.pem
new file mode 100644
index 0000000000..d775817b33
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert-issuer.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEoDCCA4igAwIBAgIQBpaPlkroI1bHThfCtTZbADANBgkqhkiG9w0BAQsFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTE3MTEwNjEyMjI1N1oXDTI3MTEwNjEyMjI1N1owXzEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTEeMBwGA1UEAxMVVGhhd3RlIEVWIFJTQSBDQSAyMDE4MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0Cu52zmdJFnSezXMKvL0rso
+WgA/1X7OxjMQHsAllID1eDG836ptJXSTPg+DoEenHfkKyw++wXobgahr0cU/2v8R
+WR3fID53ZDhEGHzS+Ol7V+HRtZG5teMWCY7gldtBQH0r7xUEp/3ISVsZUVBqtUmL
+VJlf9nxJD6Cxp4LBlcJJ8+N6kSkV+fA+WdQc0HYhXSg3PxJP7XSU28Wc7gf6y9kZ
+zQhK4WrZLRrHHbHC2QXdqQYUxR927QV+UCNXnlbTcZy2QpxWTPLzK+/cKXX4cwP6
+MGF7+8RnUgHlij/5V2k/tIF9ep4B72ucqaS/UhEPpIN/T7A3OAw995yrB38glQID
+AQABo4IBSTCCAUUwHQYDVR0OBBYEFOcB/AwWGMp9sozshyejb2GBO4Q5MB8GA1Ud
+IwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9jZCvDMA4GA1UdDwEB/wQEAwIBhjAdBgNV
+HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADA0
+BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
+LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v
+RGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2MDQwMgYE
+VR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
+MA0GCSqGSIb3DQEBCwUAA4IBAQAWGka+5ffLpfFuzT+WlwDRwhyTZSunnvecZWZT
+PPKXipynjpXx5dK8YG+2XoH74285GR1UABuvHMFV94XeDET9Pzz5s/NHS1/eAr5e
+GdwfBl80XwPkwXaYqzRtw6J4RAxeLqcbibhUQv9Iev9QcP0kNPyJu413Xov76mSu
+JlGThKzcurJPive2eLmwmoIgTPH11N/IIO9nHLVe8KTkt+FGgZCOWHA3kbFBZR39
+Mn2hFS974rhUkM+VS9KbCiQQ5OwkfbZ/6BINkE1CMtiESZ2WkbxJKPsF3dN7p9DF
+YWiQSbYjFP+rCT0/MkaHHYUkEvLNPgyJ6z29eMf0DjLu/SXJ
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert.pem b/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert.pem
new file mode 100644
index 0000000000..81e01bd783
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert.pem
@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIHOzCCBiOgAwIBAgIQBi31aKBRMQgg1+xDJ+G6/TANBgkqhkiG9w0BAQsFADBf
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMR4wHAYDVQQDExVUaGF3dGUgRVYgUlNBIENBIDIwMTgw
+HhcNMTgwNTI4MDAwMDAwWhcNMjAwNTIxMTIwMDAwWjCB6zEdMBsGA1UEDwwUUHJp
+dmF0ZSBPcmdhbml6YXRpb24xEzARBgsrBgEEAYI3PAIBAxMCREUxFjAUBgsrBgEE
+AYI3PAIBAhMFSGVzc2UxGDAWBgsrBgEEAYI3PAIBAQwHR2llw59lbjERMA8GA1UE
+BRMISFJCIDY5MDIxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZIZXNzZW4xFDASBgNV
+BAcTC0hldWNoZWxoZWltMRQwEgYDVQQKEwtTY2h1bmsgR21iSDELMAkGA1UECxMC
+SVQxGTAXBgNVBAMTEHNjaHVuay1ncm91cC5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCvkuQZz2ExPv9paJb622OOk+o4bWnjDe1zHGK6qnK25mMT
+Zldk74sXF+Wfr9lbwqHTcjGhQFwmVDqvtr55KVX8FOv0CSqNaewOrnNrFz8Xg4rn
+OlIs3+MmqD5CIK+el0rA+xltEY8WvNlwZKG7yeJYrdsr+5DAThDuwCVe8bU7it4h
+sjsMsof5ocee9zDkFThNVGR4sMk5EgBxb1Gt4n9wXUj4OBT78whhlkLH/pVZrrhs
+tQwC3q90MOPC5RJcEolSCNjGdHCKRbexmRqJgbJj/qZ9JT+fQ+Ko6a+UAWvc2BUc
+POnzGV2GzCdFFGOubJb6RjU0nuPG4Lmdc/BuS9kFAgMBAAGjggNkMIIDYDAfBgNV
+HSMEGDAWgBTnAfwMFhjKfbKM7Icno29hgTuEOTAdBgNVHQ4EFgQUNb1SY8Bkil98
+tD8zoxE30jBA1NIwZwYDVR0RBGAwXoIQc2NodW5rLWdyb3VwLmNvbYIUd3d3LnNj
+aHVuay1ncm91cC5jb22CG3NjaHVuay1jYXJib250ZWNobm9sb2d5LmNvbYIXc2No
+dW5rLXNpbnRlcm1ldGFscy5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
+CCsGAQUFBwMBBggrBgEFBQcDAjA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY2Rw
+LnRoYXd0ZS5jb20vVGhhd3RlRVZSU0FDQTIwMTguY3JsMEsGA1UdIAREMEIwNwYJ
+YIZIAYb9bAIBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv
+bS9DUFMwBwYFZ4EMAQEwcQYIKwYBBQUHAQEEZTBjMCQGCCsGAQUFBzABhhhodHRw
+Oi8vc3RhdHVzLnRoYXd0ZS5jb20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9jYWNlcnRz
+LnRoYXd0ZS5jb20vVGhhd3RlRVZSU0FDQTIwMTguY3J0MAkGA1UdEwQCMAAwggF7
+BgorBgEEAdZ5AgQCBIIBawSCAWcBZQB1AKS5CZC0GFgUh7sTosxncAo8NZgE+Rvf
+uON3zQ7IDdwQAAABY6Ze9XAAAAQDAEYwRAIgNUeXL3GwlpGQtTS/wKBlOkHJvHR5
+knSop0OPumeCfQECIEdxY7qr/WRVbWkQFvP48fgWkZHkd4vTq70Y0aaSZTbPAHUA
+VhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFjpl71tQAABAMARjBE
+AiAtxKdc/wum3TE7r9BoRd/gkrjYLWyqeLuL/opRBRy9xwIgPF6uEZxyhEoLZ+9G
+AFBAP+X89zjZphVALjIXu0RRea4AdQC72d+8H4pxtZOUI5eqkntHOFeVCqtS6BqQ
+lmQ2jh7RhQAAAWOmXvY1AAAEAwBGMEQCIHc04ERlUbIkVrlC+I89C9xtugvRCwbR
+a7qZzSdqHltUAiBRVwTacf1dnO9AgLSgrxft5LV32DvH3qNT7pWYh8dFNjANBgkq
+hkiG9w0BAQsFAAOCAQEARJ+tbnM+yS6chgpyzfB3e7IWPq2Den46Ja1H6/4qaKrd
+nsbElcvd4cCQf1zYY6jlQkO6qtfMUChKrEar5aqqnyX8x/8T9PkpHp8XyUxgGlmT
+hrnHML0gDJFS8O4MB5pFnGkgoOQa+OIQokWCXr4/a4AwsTG3Ms+lC+R+vRYz90lg
+TEJLNHB2fSvQyvpXDUL9aAjACBp/9pKxfM9iq06MFO5jP483xJUfdqtVteHMw75w
+1mb8IrM9R1dP47GsblTrf2rZYdaoxdyLjtJQG2aaOdU5unE6QeFrXbz0qeTPePs8
+ftuXSW9xb053HjAkCcVo48j07b2cHfU1hxzGGptVbQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.crlite b/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.crlite
new file mode 100644
index 0000000000..34ced4b840
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.crlite
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert-issuer.pem b/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert-issuer.pem
new file mode 100644
index 0000000000..705827a85e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert-issuer.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEkDCCA3igAwIBAgIJIrmxUyPlrv3NMA0GCSqGSIb3DQEBCwUAMF0xCzAJBgNV
+BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw
+JQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTIwHhcNMTgwODIy
+MDczMjI0WhcNMjgwODIyMDczMjI0WjBQMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEaMBgGA1UEAxMRQ3Jvc3NUcnVz
+dCBEViBDQTUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnTNi5Kgrt
+FL8qBuEmpL2gvLFY7f9MEgjzClvic/45ebM+DxZ2CMuqtMtImgf8XPIpLaFFbozx
+3VgqH41cmGHbpAoDRKpwfF1f53peHYhRxpOVgcnsiVCPZJPBPCUM9St+cuEjfo0d
+YGbr3aG5urdT2zeKIFyxKbggdkU0LVRHwvLFsIpXCn/YK/8Rmx87yW9VB80OXkzf
+IQoZop83+aebq1VwzjNCN3u4bWSFLYDyJGqE40WlZ53NZh+TwBsa6gld9YXPGQfx
+k8x38zkFXberlMQOYhX9KyuTOMdlFkbx6LfIUqVKJavpcr54+XPzVyeroNPpKxtZ
+mEqUYiFjAqUVAgMBAAGjggFeMIIBWjAdBgNVHQ4EFgQUT4twz6lAHJbllF13rNZv
+TS2b8ncwHwYDVR0jBBgwFoAUCoWpd2UFmHxAgfgPlyw48QrsPM8wEgYDVR0TAQH/
+BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwSQYDVR0fBEIwQDA+oDygOoY4aHR0
+cDovL3JlcG9zaXRvcnkuc2Vjb210cnVzdC5uZXQvU0MtUm9vdDIvU0NSb290MkNS
+TC5jcmwwUgYDVR0gBEswSTBHBgoqgwiMmxtkhwUEMDkwNwYIKwYBBQUHAgEWK2h0
+dHBzOi8vcmVwb3NpdG9yeS5zZWNvbXRydXN0Lm5ldC9TQy1Sb290Mi8wQAYIKwYB
+BQUHAQEENDAyMDAGCCsGAQUFBzABhiRodHRwOi8vc2Nyb290Y2EyLm9jc3Auc2Vj
+b210cnVzdC5uZXQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQAD
+ggEBABEDSrrhhR+Js5q45yih2Ne4cMLZmrH0AZwU3eM+7HZplzi1EhppgvcYk/2k
+LM9haQGWnAZ5wiixLqKu7WlWrHgblZbXyCxALmMBK1rqeP0omxXExqKVqWNHU8KZ
+t3jahH1wDYSzfetM7guWR+PAPpb9oQCtAx8DVyI/3Ocswvti/uWb517Bdo6Nd0+9
+mf0LiphNKcSzSFX0s1Cb47cJROYHGBe2J6NUSWR7wE0asPtKsznGyNO+NJCUR+0h
+OLN2cA2KJwPhZjYJt8UkucAF/EE7qC0Fc8B9Q/gttQ52en5BZxdkDrHCi4qnsSvi
+gueQme/RzYkEaQlNT1WCZ9AIgVE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert.pem b/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert.pem
new file mode 100644
index 0000000000..195d2d8ca2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF4DCCBMigAwIBAgIQC3d196+a5UJlyc0yVxB3jjANBgkqhkiG9w0BAQsFADBQ
+MQswCQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4s
+TFRELjEaMBgGA1UEAxMRQ3Jvc3NUcnVzdCBEViBDQTUwHhcNMTkwNjExMDUyMjEy
+WhcNMjEwNjMwMTQ1OTU5WjAUMRIwEAYDVQQDEwlza3luZXcuanAwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeciw7C297026HA4oIwc29vL2h29GVrRF7
+HGdeXzAJA7kh+qwo8rTBFfdX7sgHy6nnE1+flEtFt91Ss8i3BZMEqoFUZFb1jGXd
+DbQtmIWxz7O5skkjR1gdKwt9GImy1hEPt8dwU52mwVsSUEKvlZlsjeofUPAEbnYY
++iA/nYaYXiXyCxJzk6Y09VlzghyMIhkLwDa7rL3S9FgUQI6tSUwsiNYNoQzlYgXF
+yPfQfd57LbBwZJtqVPC6rjPOZZd0sw7uvrDNuxnAM2k2mzlML9Vwt8EvSlZX60xD
+oGQCsQQ/ZgjEQTA8WRZ+fxW/LqQNYYm70KU/1M+e8o4MKmA9xkH5AgMBAAGjggLw
+MIIC7DAfBgNVHSMEGDAWgBRPi3DPqUAcluWUXXes1m9NLZvydzA8BggrBgEFBQcB
+AQQwMC4wLAYIKwYBBQUHMAGGIGh0dHA6Ly9kdmNhNS5vY3NwLnNlY29tdHJ1c3Qu
+bmV0MCMGA1UdEQQcMBqCCXNreW5ldy5qcIINd3d3LnNreW5ldy5qcDBaBgNVHSAE
+UzBRMEUGCiqDCIybG26BVQIwNzA1BggrBgEFBQcCARYpaHR0cHM6Ly9yZXBvMS5z
+ZWNvbXRydXN0Lm5ldC9zcHBjYS94dGR2NS8wCAYGZ4EMAQIBMBMGA1UdJQQMMAoG
+CCsGAQUFBwMBMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9yZXBvMS5zZWNvbXRy
+dXN0Lm5ldC9zcHBjYS94dGR2NS9mdWxsY3JsLmNybDAdBgNVHQ4EFgQUuj9305tQ
+JIeVAQtsz9JHx3PTqaQwDgYDVR0PAQH/BAQDAgWgMIIBfgYKKwYBBAHWeQIEAgSC
+AW4EggFqAWgAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWtF
+Bb6pAAAEAwBHMEUCIHUQkmFzUh01r1Px/zWMZSL21dNNQwM+rN1z0gutxV3JAiEA
+jSb2/GAm4+2qiNWDtx1EkHsMXjNW+5S4GhJePexjJR8AdgDuS723dc5guuFCaR+r
+4Z5mow9+X7By2IMAxHuJeqj9ywAAAWtFBcdeAAAEAwBHMEUCIDVHdfP9wnVgz45l
+eX80DpRCRNEV/OCDwfW+B0g/dveYAiEArbpLQb5Z9hul3r00kF2LrivNuI7kwEBy
+MpkYsLtSPJoAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZEwAAAWtF
+BdCQAAAEAwBHMEUCIE2GUo6x3qDrIhacnCmjikBCHF2yT6Fv5GAehZB569YCAiEA
+vXwMXV8+y3xNFys+A6u9EjKiy8CTKv+SQxqsJ4s6jK0wDQYJKoZIhvcNAQELBQAD
+ggEBAAzlm9W+N5fviTJ9wDsc5nXKYur3744V/cm75+8dUM61Rko1isK6IZt5aNPN
+wOfhBsTzHHSYmAFMR9Xjoq8iDYZtIk01IGI6LEWuls9F2hVcERiHMWJOLTiH35xN
+vRNTG0AbBdIpTX2sURsoCPJ+8DTnVUr3pTzXnIY4EQ4UXfANuYwceOHShF6UJo/L
+PK0uRdHcd5SmMa03gFUdkTc9gU6PIEO/UgubazGh9xDBHtHECeleL+gpSfOP3SkF
+7W1RgmbE6WJdVPlto7FRQtl2xIzHs/gNaPezqNKPHgFlx4c+ECTjPLqoW8LdeXu+
+N8dueJg1+h+lQifkmgl23DqEIiI=
+-----END CERTIFICATE-----