summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_cert_storage_direct.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/unit/test_cert_storage_direct.js')
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct.js417
1 files changed, 417 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
+ );
+});