diff options
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.js | 417 |
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 + ); +}); |