1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // This file consists of unit tests for cert_storage (whereas test_cert_storage.js is more of an
12 this.certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
16 async function addCerts(certInfos) {
17 let result = await new Promise(resolve => {
18 certStorage.addCerts(certInfos, resolve);
20 Assert.equal(result, Cr.NS_OK, "addCerts should succeed");
23 async function removeCertsByHashes(hashesBase64) {
24 let result = await new Promise(resolve => {
25 certStorage.removeCertsByHashes(hashesBase64, resolve);
27 Assert.equal(result, Cr.NS_OK, "removeCertsByHashes should succeed");
30 function getLongString(uniquePart, length) {
31 return String(uniquePart).padStart(length, "0");
35 constructor(cert, subject) {
36 this.cert = btoa(cert);
37 this.subject = btoa(subject);
38 this.trust = Ci.nsICertStorage.TRUST_INHERIT;
41 CertInfo.prototype.QueryInterface = ChromeUtils.generateQI(["nsICertInfo"]);
43 add_task(async function test_common_subject() {
44 let someCert1 = new CertInfo(
45 "some certificate bytes 1",
48 let someCert2 = new CertInfo(
49 "some certificate bytes 2",
52 let someCert3 = new CertInfo(
53 "some certificate bytes 3",
56 await addCerts([someCert1, someCert2, someCert3]);
57 let storedCerts = certStorage.findCertsBySubject(
58 stringToArray("some common subject")
60 let storedCertsAsStrings = storedCerts.map(arrayToString);
62 "some certificate bytes 1",
63 "some certificate bytes 2",
64 "some certificate bytes 3",
67 storedCertsAsStrings.sort(),
69 "should find expected certs"
73 new CertInfo("some other certificate bytes", "some other subject"),
75 storedCerts = certStorage.findCertsBySubject(
76 stringToArray("some common subject")
78 storedCertsAsStrings = storedCerts.map(arrayToString);
80 storedCertsAsStrings.sort(),
82 "should still find expected certs"
85 let storedOtherCerts = certStorage.findCertsBySubject(
86 stringToArray("some other subject")
88 let storedOtherCertsAsStrings = storedOtherCerts.map(arrayToString);
89 let expectedOtherCerts = ["some other certificate bytes"];
91 storedOtherCertsAsStrings,
93 "should have other certificate"
97 add_task(async function test_many_entries() {
98 const NUM_CERTS = 500;
99 const CERT_LENGTH = 3000;
100 const SUBJECT_LENGTH = 40;
102 for (let i = 0; i < NUM_CERTS; i++) {
105 getLongString(i, CERT_LENGTH),
106 getLongString(i, SUBJECT_LENGTH)
110 await addCerts(certs);
111 for (let i = 0; i < NUM_CERTS; i++) {
112 let subject = stringToArray(getLongString(i, SUBJECT_LENGTH));
113 let storedCerts = certStorage.findCertsBySubject(subject);
117 "should have 1 certificate (lots of data test)"
119 let storedCertAsString = arrayToString(storedCerts[0]);
122 getLongString(i, CERT_LENGTH),
123 "certificate should be as expected (lots of data test)"
128 add_task(async function test_removal() {
129 // As long as cert_storage is given valid base64, attempting to delete some nonexistent
130 // certificate will "succeed" (it'll do nothing).
131 await removeCertsByHashes([btoa("thishashisthewrongsize")]);
133 let removalCert1 = new CertInfo(
134 "removal certificate bytes 1",
135 "common subject to remove"
137 let removalCert2 = new CertInfo(
138 "removal certificate bytes 2",
139 "common subject to remove"
141 let removalCert3 = new CertInfo(
142 "removal certificate bytes 3",
143 "common subject to remove"
145 await addCerts([removalCert1, removalCert2, removalCert3]);
147 let storedCerts = certStorage.findCertsBySubject(
148 stringToArray("common subject to remove")
150 let storedCertsAsStrings = storedCerts.map(arrayToString);
151 let expectedCerts = [
152 "removal certificate bytes 1",
153 "removal certificate bytes 2",
154 "removal certificate bytes 3",
157 storedCertsAsStrings.sort(),
158 expectedCerts.sort(),
159 "should find expected certs before removing them"
162 // echo -n "removal certificate bytes 2" | sha256sum | xxd -r -p | base64
163 await removeCertsByHashes(["2nUPHwl5TVr1mAD1FU9FivLTlTb0BAdnVUhsYgBccN4="]);
164 storedCerts = certStorage.findCertsBySubject(
165 stringToArray("common subject to remove")
167 storedCertsAsStrings = storedCerts.map(arrayToString);
169 "removal certificate bytes 1",
170 "removal certificate bytes 3",
173 storedCertsAsStrings.sort(),
174 expectedCerts.sort(),
175 "should only have first and third certificates now"
178 // echo -n "removal certificate bytes 1" | sha256sum | xxd -r -p | base64
179 await removeCertsByHashes(["8zoRqHYrklr7Zx6UWpzrPuL+ol8KL1Ml6XHBQmXiaTY="]);
180 storedCerts = certStorage.findCertsBySubject(
181 stringToArray("common subject to remove")
183 storedCertsAsStrings = storedCerts.map(arrayToString);
184 expectedCerts = ["removal certificate bytes 3"];
186 storedCertsAsStrings.sort(),
187 expectedCerts.sort(),
188 "should only have third certificate now"
191 // echo -n "removal certificate bytes 3" | sha256sum | xxd -r -p | base64
192 await removeCertsByHashes(["vZn7GwDSabB/AVo0T+N26nUsfSXIIx4NgQtSi7/0p/w="]);
193 storedCerts = certStorage.findCertsBySubject(
194 stringToArray("common subject to remove")
196 Assert.equal(storedCerts.length, 0, "shouldn't have any certificates now");
198 // echo -n "removal certificate bytes 3" | sha256sum | xxd -r -p | base64
199 // Again, removing a nonexistent certificate should "succeed".
200 await removeCertsByHashes(["vZn7GwDSabB/AVo0T+N26nUsfSXIIx4NgQtSi7/0p/w="]);
203 add_task(async function test_batched_removal() {
204 let removalCert1 = new CertInfo(
205 "batch removal certificate bytes 1",
206 "batch subject to remove"
208 let removalCert2 = new CertInfo(
209 "batch removal certificate bytes 2",
210 "batch subject to remove"
212 let removalCert3 = new CertInfo(
213 "batch removal certificate bytes 3",
214 "batch subject to remove"
216 await addCerts([removalCert1, removalCert2, removalCert3]);
217 let storedCerts = certStorage.findCertsBySubject(
218 stringToArray("batch subject to remove")
220 let storedCertsAsStrings = storedCerts.map(arrayToString);
221 let expectedCerts = [
222 "batch removal certificate bytes 1",
223 "batch removal certificate bytes 2",
224 "batch removal certificate bytes 3",
227 storedCertsAsStrings.sort(),
228 expectedCerts.sort(),
229 "should find expected certs before removing them"
231 // echo -n "batch removal certificate bytes 1" | sha256sum | xxd -r -p | base64
232 // echo -n "batch removal certificate bytes 2" | sha256sum | xxd -r -p | base64
233 // echo -n "batch removal certificate bytes 3" | sha256sum | xxd -r -p | base64
234 await removeCertsByHashes([
235 "EOEEUTuanHZX9NFVCoMKVT22puIJC6g+ZuNPpJgvaa8=",
236 "Xz6h/Kvn35cCLJEZXkjPqk1GG36b56sreLyAXpO+0zg=",
237 "Jr7XdiTT8ZONUL+ogNNMW2oxKxanvYOLQPKBPgH/has=",
239 storedCerts = certStorage.findCertsBySubject(
240 stringToArray("batch subject to remove")
242 Assert.equal(storedCerts.length, 0, "shouldn't have any certificates now");
245 class CRLiteCoverage {
246 constructor(ctLogID, minTimestamp, maxTimestamp) {
247 this.b64LogID = ctLogID;
248 this.minTimestamp = minTimestamp;
249 this.maxTimestamp = maxTimestamp;
252 CRLiteCoverage.prototype.QueryInterface = ChromeUtils.generateQI([
256 add_task(async function test_crlite_filter() {
257 let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
262 "test_cert_storage_direct/valid-cert-issuer.pem",
265 let validCert = constructCertFromFile(
266 "test_cert_storage_direct/valid-cert.pem"
270 "test_cert_storage_direct/revoked-cert-issuer.pem",
273 let revokedCert = constructCertFromFile(
274 "test_cert_storage_direct/revoked-cert.pem"
276 let filterFile = do_get_file(
277 "test_cert_storage_direct/test-filter.crlite",
280 ok(filterFile.exists(), "test filter file should exist");
283 let filterBytes = stringToArray(readFile(filterFile));
284 // First simualte a filter that does not cover any certificates. With CRLite
285 // enabled, none of the certificates should appear to be revoked.
286 let setFullCRLiteFilterResult = await new Promise(resolve => {
287 certStorage.setFullCRLiteFilter(filterBytes, enrollment, coverage, resolve);
290 setFullCRLiteFilterResult,
292 "setFullCRLiteFilter should succeed"
295 Services.prefs.setIntPref(
296 "security.pki.crlite_mode",
297 CRLiteModeEnforcePrefValue
299 await checkCertErrorGenericAtTime(
303 certificateUsageSSLServer,
304 new Date("2019-11-04T00:00:00Z").getTime() / 1000,
307 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
309 await checkCertErrorGenericAtTime(
313 certificateUsageSSLServer,
314 new Date("2019-11-04T00:00:00Z").getTime() / 1000,
317 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
320 // Now replace the filter with one that covers the "valid" and "revoked"
321 // certificates. CRLite should flag the revoked certificate.
324 "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=",
330 // crlite_enrollment_id.py test_crlite_filters/issuer.pem
331 enrollment.push("UbH9/ZAnjuqf79Xhah1mFOWo6ZvgQCgsdheWfjvVUM8=");
332 // crlite_enrollment_id.py test_crlite_filters/no-sct-issuer.pem
333 enrollment.push("Myn7EasO1QikOtNmo/UZdh6snCAw0BOY6wgU8OsUeeY=");
334 // crlite_enrollment_id.py test_cert_storage_direct/revoked-cert-issuer.pem
335 enrollment.push("HTvSp2263dqBYtgYA2fldKAoTYcEVLPVTlRia9XaoCQ=");
337 setFullCRLiteFilterResult = await new Promise(resolve => {
338 certStorage.setFullCRLiteFilter(filterBytes, enrollment, coverage, resolve);
341 setFullCRLiteFilterResult,
343 "setFullCRLiteFilter should succeed"
345 await checkCertErrorGenericAtTime(
349 certificateUsageSSLServer,
350 new Date("2019-11-04T00:00:00Z").getTime() / 1000,
353 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
355 await checkCertErrorGenericAtTime(
358 SEC_ERROR_REVOKED_CERTIFICATE,
359 certificateUsageSSLServer,
360 new Date("2019-11-04T00:00:00Z").getTime() / 1000,
363 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
366 // If we're only collecting telemetry, none of the certificates should appear to be revoked.
367 Services.prefs.setIntPref(
368 "security.pki.crlite_mode",
369 CRLiteModeTelemetryOnlyPrefValue
371 await checkCertErrorGenericAtTime(
375 certificateUsageSSLServer,
376 new Date("2019-11-04T00:00:00Z").getTime() / 1000,
379 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
381 await checkCertErrorGenericAtTime(
385 certificateUsageSSLServer,
386 new Date("2019-11-04T00:00:00Z").getTime() / 1000,
389 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
392 // If CRLite is disabled, none of the certificates should appear to be revoked.
393 Services.prefs.setIntPref(
394 "security.pki.crlite_mode",
395 CRLiteModeDisabledPrefValue
397 await checkCertErrorGenericAtTime(
401 certificateUsageSSLServer,
402 new Date("2019-11-04T00:00:00Z").getTime() / 1000,
405 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
407 await checkCertErrorGenericAtTime(
411 certificateUsageSSLServer,
412 new Date("2019-11-04T00:00:00Z").getTime() / 1000,
415 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY