Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'win' && msix
- Manifest: security/manager/ssl/tests/unit/xpcshell.toml
/* -*- 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
"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
);
});