no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / security / manager / ssl / tests / unit / test_cert_storage_direct.js
bloba1ba818dd9096ef88a68f27efa7c9a144e6f022c
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/. */
5 "use strict";
7 // This file consists of unit tests for cert_storage (whereas test_cert_storage.js is more of an
8 // integration test).
10 do_get_profile();
12 this.certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
13   Ci.nsICertStorage
16 async function addCerts(certInfos) {
17   let result = await new Promise(resolve => {
18     certStorage.addCerts(certInfos, resolve);
19   });
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);
26   });
27   Assert.equal(result, Cr.NS_OK, "removeCertsByHashes should succeed");
30 function getLongString(uniquePart, length) {
31   return String(uniquePart).padStart(length, "0");
34 class CertInfo {
35   constructor(cert, subject) {
36     this.cert = btoa(cert);
37     this.subject = btoa(subject);
38     this.trust = Ci.nsICertStorage.TRUST_INHERIT;
39   }
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",
46     "some common subject"
47   );
48   let someCert2 = new CertInfo(
49     "some certificate bytes 2",
50     "some common subject"
51   );
52   let someCert3 = new CertInfo(
53     "some certificate bytes 3",
54     "some common subject"
55   );
56   await addCerts([someCert1, someCert2, someCert3]);
57   let storedCerts = certStorage.findCertsBySubject(
58     stringToArray("some common subject")
59   );
60   let storedCertsAsStrings = storedCerts.map(arrayToString);
61   let expectedCerts = [
62     "some certificate bytes 1",
63     "some certificate bytes 2",
64     "some certificate bytes 3",
65   ];
66   Assert.deepEqual(
67     storedCertsAsStrings.sort(),
68     expectedCerts.sort(),
69     "should find expected certs"
70   );
72   await addCerts([
73     new CertInfo("some other certificate bytes", "some other subject"),
74   ]);
75   storedCerts = certStorage.findCertsBySubject(
76     stringToArray("some common subject")
77   );
78   storedCertsAsStrings = storedCerts.map(arrayToString);
79   Assert.deepEqual(
80     storedCertsAsStrings.sort(),
81     expectedCerts.sort(),
82     "should still find expected certs"
83   );
85   let storedOtherCerts = certStorage.findCertsBySubject(
86     stringToArray("some other subject")
87   );
88   let storedOtherCertsAsStrings = storedOtherCerts.map(arrayToString);
89   let expectedOtherCerts = ["some other certificate bytes"];
90   Assert.deepEqual(
91     storedOtherCertsAsStrings,
92     expectedOtherCerts,
93     "should have other certificate"
94   );
95 });
97 add_task(async function test_many_entries() {
98   const NUM_CERTS = 500;
99   const CERT_LENGTH = 3000;
100   const SUBJECT_LENGTH = 40;
101   let certs = [];
102   for (let i = 0; i < NUM_CERTS; i++) {
103     certs.push(
104       new CertInfo(
105         getLongString(i, CERT_LENGTH),
106         getLongString(i, SUBJECT_LENGTH)
107       )
108     );
109   }
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);
114     Assert.equal(
115       storedCerts.length,
116       1,
117       "should have 1 certificate (lots of data test)"
118     );
119     let storedCertAsString = arrayToString(storedCerts[0]);
120     Assert.equal(
121       storedCertAsString,
122       getLongString(i, CERT_LENGTH),
123       "certificate should be as expected (lots of data test)"
124     );
125   }
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"
136   );
137   let removalCert2 = new CertInfo(
138     "removal certificate bytes 2",
139     "common subject to remove"
140   );
141   let removalCert3 = new CertInfo(
142     "removal certificate bytes 3",
143     "common subject to remove"
144   );
145   await addCerts([removalCert1, removalCert2, removalCert3]);
147   let storedCerts = certStorage.findCertsBySubject(
148     stringToArray("common subject to remove")
149   );
150   let storedCertsAsStrings = storedCerts.map(arrayToString);
151   let expectedCerts = [
152     "removal certificate bytes 1",
153     "removal certificate bytes 2",
154     "removal certificate bytes 3",
155   ];
156   Assert.deepEqual(
157     storedCertsAsStrings.sort(),
158     expectedCerts.sort(),
159     "should find expected certs before removing them"
160   );
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")
166   );
167   storedCertsAsStrings = storedCerts.map(arrayToString);
168   expectedCerts = [
169     "removal certificate bytes 1",
170     "removal certificate bytes 3",
171   ];
172   Assert.deepEqual(
173     storedCertsAsStrings.sort(),
174     expectedCerts.sort(),
175     "should only have first and third certificates now"
176   );
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")
182   );
183   storedCertsAsStrings = storedCerts.map(arrayToString);
184   expectedCerts = ["removal certificate bytes 3"];
185   Assert.deepEqual(
186     storedCertsAsStrings.sort(),
187     expectedCerts.sort(),
188     "should only have third certificate now"
189   );
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")
195   );
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"
207   );
208   let removalCert2 = new CertInfo(
209     "batch removal certificate bytes 2",
210     "batch subject to remove"
211   );
212   let removalCert3 = new CertInfo(
213     "batch removal certificate bytes 3",
214     "batch subject to remove"
215   );
216   await addCerts([removalCert1, removalCert2, removalCert3]);
217   let storedCerts = certStorage.findCertsBySubject(
218     stringToArray("batch subject to remove")
219   );
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",
225   ];
226   Assert.deepEqual(
227     storedCertsAsStrings.sort(),
228     expectedCerts.sort(),
229     "should find expected certs before removing them"
230   );
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=",
238   ]);
239   storedCerts = certStorage.findCertsBySubject(
240     stringToArray("batch subject to remove")
241   );
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;
250   }
252 CRLiteCoverage.prototype.QueryInterface = ChromeUtils.generateQI([
253   "nsICRLiteCoverage",
256 add_task(async function test_crlite_filter() {
257   let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
258     Ci.nsIX509CertDB
259   );
260   addCertFromFile(
261     certdb,
262     "test_cert_storage_direct/valid-cert-issuer.pem",
263     ",,"
264   );
265   let validCert = constructCertFromFile(
266     "test_cert_storage_direct/valid-cert.pem"
267   );
268   addCertFromFile(
269     certdb,
270     "test_cert_storage_direct/revoked-cert-issuer.pem",
271     ",,"
272   );
273   let revokedCert = constructCertFromFile(
274     "test_cert_storage_direct/revoked-cert.pem"
275   );
276   let filterFile = do_get_file(
277     "test_cert_storage_direct/test-filter.crlite",
278     false
279   );
280   ok(filterFile.exists(), "test filter file should exist");
281   let enrollment = [];
282   let coverage = [];
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);
288   });
289   Assert.equal(
290     setFullCRLiteFilterResult,
291     Cr.NS_OK,
292     "setFullCRLiteFilter should succeed"
293   );
295   Services.prefs.setIntPref(
296     "security.pki.crlite_mode",
297     CRLiteModeEnforcePrefValue
298   );
299   await checkCertErrorGenericAtTime(
300     certdb,
301     validCert,
302     PRErrorCodeSuccess,
303     certificateUsageSSLServer,
304     new Date("2019-11-04T00:00:00Z").getTime() / 1000,
305     false,
306     "skynew.jp",
307     Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
308   );
309   await checkCertErrorGenericAtTime(
310     certdb,
311     revokedCert,
312     PRErrorCodeSuccess,
313     certificateUsageSSLServer,
314     new Date("2019-11-04T00:00:00Z").getTime() / 1000,
315     false,
316     "schunk-group.com",
317     Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
318   );
320   // Now replace the filter with one that covers the "valid" and "revoked"
321   // certificates. CRLite should flag the revoked certificate.
322   coverage.push(
323     new CRLiteCoverage(
324       "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=",
325       0,
326       1641612275000
327     )
328   );
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);
339   });
340   Assert.equal(
341     setFullCRLiteFilterResult,
342     Cr.NS_OK,
343     "setFullCRLiteFilter should succeed"
344   );
345   await checkCertErrorGenericAtTime(
346     certdb,
347     validCert,
348     PRErrorCodeSuccess,
349     certificateUsageSSLServer,
350     new Date("2019-11-04T00:00:00Z").getTime() / 1000,
351     false,
352     "skynew.jp",
353     Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
354   );
355   await checkCertErrorGenericAtTime(
356     certdb,
357     revokedCert,
358     SEC_ERROR_REVOKED_CERTIFICATE,
359     certificateUsageSSLServer,
360     new Date("2019-11-04T00:00:00Z").getTime() / 1000,
361     false,
362     "schunk-group.com",
363     Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
364   );
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
370   );
371   await checkCertErrorGenericAtTime(
372     certdb,
373     validCert,
374     PRErrorCodeSuccess,
375     certificateUsageSSLServer,
376     new Date("2019-11-04T00:00:00Z").getTime() / 1000,
377     false,
378     "skynew.jp",
379     Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
380   );
381   await checkCertErrorGenericAtTime(
382     certdb,
383     revokedCert,
384     PRErrorCodeSuccess,
385     certificateUsageSSLServer,
386     new Date("2019-11-04T00:00:00Z").getTime() / 1000,
387     false,
388     "schunk-group.com",
389     Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
390   );
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
396   );
397   await checkCertErrorGenericAtTime(
398     certdb,
399     validCert,
400     PRErrorCodeSuccess,
401     certificateUsageSSLServer,
402     new Date("2019-11-04T00:00:00Z").getTime() / 1000,
403     false,
404     "skynew.jp",
405     Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
406   );
407   await checkCertErrorGenericAtTime(
408     certdb,
409     revokedCert,
410     PRErrorCodeSuccess,
411     certificateUsageSSLServer,
412     new Date("2019-11-04T00:00:00Z").getTime() / 1000,
413     false,
414     "schunk-group.com",
415     Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
416   );