no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / security / manager / ssl / tests / unit / test_ocsp_caching.js
blobb964018518c34fe468ef9bbf07fa112da4294a41
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 // Checks various aspects of the OCSP cache, mainly to to ensure we do not fetch
8 // responses more than necessary.
10 var gFetchCount = 0;
11 var gGoodOCSPResponse = null;
12 var gResponsePattern = [];
14 function respondWithGoodOCSP(request, response) {
15   info("returning 200 OK");
16   response.setStatusLine(request.httpVersion, 200, "OK");
17   response.setHeader("Content-Type", "application/ocsp-response");
18   response.write(gGoodOCSPResponse);
21 function respondWithSHA1OCSP(request, response) {
22   info("returning 200 OK with sha-1 delegated response");
23   response.setStatusLine(request.httpVersion, 200, "OK");
24   response.setHeader("Content-Type", "application/ocsp-response");
26   let args = [["good-delegated", "default-ee", "delegatedSHA1Signer", 0]];
27   let responses = generateOCSPResponses(args, "ocsp_certs");
28   response.write(responses[0]);
31 function respondWithError(request, response) {
32   info("returning 500 Internal Server Error");
33   response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
34   let body = "Refusing to return a response";
35   response.bodyOutputStream.write(body, body.length);
38 function generateGoodOCSPResponse(thisUpdateSkew) {
39   let args = [["good", "default-ee", "unused", thisUpdateSkew]];
40   let responses = generateOCSPResponses(args, "ocsp_certs");
41   return responses[0];
44 function add_ocsp_test(
45   aHost,
46   aExpectedResult,
47   aResponses,
48   aMessage,
49   aOriginAttributes
50 ) {
51   add_connection_test(
52     aHost,
53     aExpectedResult,
54     function () {
55       clearSessionCache();
56       gFetchCount = 0;
57       gResponsePattern = aResponses;
58     },
59     function () {
60       // check the number of requests matches the size of aResponses
61       equal(gFetchCount, aResponses.length, aMessage);
62     },
63     null,
64     aOriginAttributes
65   );
68 function run_test() {
69   do_get_profile();
70   Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
71   Services.prefs.setIntPref("security.OCSP.enabled", 1);
72   add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
74   let ocspResponder = new HttpServer();
75   ocspResponder.registerPrefixHandler("/", function (request, response) {
76     info("gFetchCount: " + gFetchCount);
77     let responseFunction = gResponsePattern[gFetchCount];
78     Assert.notEqual(undefined, responseFunction);
80     ++gFetchCount;
81     responseFunction(request, response);
82   });
83   ocspResponder.start(8888);
85   add_tests();
87   add_test(function () {
88     ocspResponder.stop(run_next_test);
89   });
90   run_next_test();
93 function add_tests() {
94   // Test that verifying a certificate with a "short lifetime" doesn't result
95   // in OCSP fetching. Due to longevity requirements in our testing
96   // infrastructure, the certificate we encounter is valid for a very long
97   // time, so we have to define a "short lifetime" as something very long.
98   add_test(function () {
99     Services.prefs.setIntPref(
100       "security.pki.cert_short_lifetime_in_days",
101       12000
102     );
103     run_next_test();
104   });
106   add_ocsp_test(
107     "ocsp-stapling-none.example.com",
108     PRErrorCodeSuccess,
109     [],
110     "expected zero OCSP requests for a short-lived certificate"
111   );
113   add_test(function () {
114     Services.prefs.setIntPref("security.pki.cert_short_lifetime_in_days", 100);
115     run_next_test();
116   });
118   // If a "short lifetime" is something more reasonable, ensure that we do OCSP
119   // fetching for this long-lived certificate.
121   add_ocsp_test(
122     "ocsp-stapling-none.example.com",
123     PRErrorCodeSuccess,
124     [respondWithError],
125     "expected one OCSP request for a long-lived certificate"
126   );
127   add_test(function () {
128     Services.prefs.clearUserPref("security.pki.cert_short_lifetime_in_days");
129     run_next_test();
130   });
131   // ---------------------------------------------------------------------------
133   // Reset state
134   add_test(function () {
135     clearOCSPCache();
136     run_next_test();
137   });
139   // This test assumes that OCSPStaplingServer uses the same cert for
140   // ocsp-stapling-unknown.example.com and ocsp-stapling-none.example.com.
142   // Get an Unknown response for the *.example.com cert and put it in the
143   // OCSP cache.
144   add_ocsp_test(
145     "ocsp-stapling-unknown.example.com",
146     SEC_ERROR_OCSP_UNKNOWN_CERT,
147     [],
148     "Stapled Unknown response -> a fetch should not have been attempted"
149   );
151   // A failure to retrieve an OCSP response must result in the cached Unknown
152   // response being recognized and honored.
153   add_ocsp_test(
154     "ocsp-stapling-none.example.com",
155     SEC_ERROR_OCSP_UNKNOWN_CERT,
156     [respondWithError, respondWithError],
157     "No stapled response -> a fetch should have been attempted"
158   );
160   // A valid Good response from the OCSP responder must override the cached
161   // Unknown response.
162   //
163   // Note that We need to make sure that the Unknown response and the Good
164   // response have different thisUpdate timestamps; otherwise, the Good
165   // response will be seen as "not newer" and it won't replace the existing
166   // entry.
167   add_test(function () {
168     gGoodOCSPResponse = generateGoodOCSPResponse(1200);
169     run_next_test();
170   });
171   add_ocsp_test(
172     "ocsp-stapling-none.example.com",
173     PRErrorCodeSuccess,
174     [respondWithGoodOCSP],
175     "Cached Unknown response, no stapled response -> a fetch" +
176       " should have been attempted"
177   );
179   // The Good response retrieved from the previous fetch must have replaced
180   // the Unknown response in the cache, resulting in the catched Good response
181   // being returned and no fetch.
182   add_ocsp_test(
183     "ocsp-stapling-none.example.com",
184     PRErrorCodeSuccess,
185     [],
186     "Cached Good response -> a fetch should not have been attempted"
187   );
189   // ---------------------------------------------------------------------------
191   // Reset state
192   add_test(function () {
193     clearOCSPCache();
194     run_next_test();
195   });
197   // A failure to retrieve an OCSP response will result in an error entry being
198   // added to the cache.
199   add_ocsp_test(
200     "ocsp-stapling-none.example.com",
201     PRErrorCodeSuccess,
202     [respondWithError],
203     "No stapled response -> a fetch should have been attempted"
204   );
206   // The error entry will prevent a fetch from happening for a while.
207   add_ocsp_test(
208     "ocsp-stapling-none.example.com",
209     PRErrorCodeSuccess,
210     [],
211     "Noted OCSP server failure -> a fetch should not have been attempted"
212   );
214   // The error entry must not prevent a stapled OCSP response from being
215   // honored.
216   add_ocsp_test(
217     "ocsp-stapling-revoked.example.com",
218     SEC_ERROR_REVOKED_CERTIFICATE,
219     [],
220     "Stapled Revoked response -> a fetch should not have been attempted"
221   );
223   // ---------------------------------------------------------------------------
225   // Ensure OCSP responses from signers with SHA1 certificates are OK. This
226   // is included in the OCSP caching tests since there were OCSP cache-related
227   // regressions when sha-1 telemetry probes were added.
228   add_test(function () {
229     clearOCSPCache();
230     // set security.OCSP.require so that checking the OCSP signature fails
231     Services.prefs.setBoolPref("security.OCSP.require", true);
232     run_next_test();
233   });
235   add_ocsp_test(
236     "ocsp-stapling-none.example.com",
237     SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
238     [respondWithSHA1OCSP],
239     "OCSP signing cert was issued with sha1 - should fail"
240   );
242   add_test(function () {
243     Services.prefs.setBoolPref("security.OCSP.require", false);
244     run_next_test();
245   });
247   // ---------------------------------------------------------------------------
249   // Reset state
250   add_test(function () {
251     clearOCSPCache();
252     run_next_test();
253   });
255   // This test makes sure that OCSP cache are isolated by firstPartyDomain.
257   let gObservedCnt = 0;
258   let protocolProxyService = Cc[
259     "@mozilla.org/network/protocol-proxy-service;1"
260   ].getService(Ci.nsIProtocolProxyService);
262   // Observe all channels and make sure the firstPartyDomain in their loadInfo's
263   // origin attributes are aFirstPartyDomain.
264   function startObservingChannels(aFirstPartyDomain) {
265     // We use a dummy proxy filter to catch all channels, even those that do not
266     // generate an "http-on-modify-request" notification.
267     let proxyFilter = {
268       applyFilter(aChannel, aProxy, aCallback) {
269         // We have the channel; provide it to the callback.
270         if (aChannel.originalURI.spec == "http://localhost:8888/") {
271           gObservedCnt++;
272           equal(
273             aChannel.loadInfo.originAttributes.firstPartyDomain,
274             aFirstPartyDomain,
275             "firstPartyDomain should match"
276           );
277         }
278         // Pass on aProxy unmodified.
279         aCallback.onProxyFilterResult(aProxy);
280       },
281     };
282     protocolProxyService.registerChannelFilter(proxyFilter, 0);
283     // Return the stop() function:
284     return () => protocolProxyService.unregisterChannelFilter(proxyFilter);
285   }
287   let stopObservingChannels;
288   add_test(function () {
289     stopObservingChannels = startObservingChannels("foo.com");
290     run_next_test();
291   });
293   // A good OCSP response will be cached.
294   add_ocsp_test(
295     "ocsp-stapling-none.example.com",
296     PRErrorCodeSuccess,
297     [respondWithGoodOCSP],
298     "No stapled response (firstPartyDomain = foo.com) -> a fetch " +
299       "should have been attempted",
300     { firstPartyDomain: "foo.com" }
301   );
303   // The cache will prevent a fetch from happening.
304   add_ocsp_test(
305     "ocsp-stapling-none.example.com",
306     PRErrorCodeSuccess,
307     [],
308     "Noted OCSP server failure (firstPartyDomain = foo.com) -> a " +
309       "fetch should not have been attempted",
310     { firstPartyDomain: "foo.com" }
311   );
313   add_test(function () {
314     stopObservingChannels();
315     equal(gObservedCnt, 1, "should have observed only 1 OCSP requests");
316     gObservedCnt = 0;
317     run_next_test();
318   });
320   add_test(function () {
321     stopObservingChannels = startObservingChannels("bar.com");
322     run_next_test();
323   });
325   // But using a different firstPartyDomain should result in a fetch.
326   add_ocsp_test(
327     "ocsp-stapling-none.example.com",
328     PRErrorCodeSuccess,
329     [respondWithGoodOCSP],
330     "No stapled response (firstPartyDomain = bar.com) -> a fetch " +
331       "should have been attempted",
332     { firstPartyDomain: "bar.com" }
333   );
335   add_test(function () {
336     stopObservingChannels();
337     equal(gObservedCnt, 1, "should have observed only 1 OCSP requests");
338     gObservedCnt = 0;
339     run_next_test();
340   });
342   // ---------------------------------------------------------------------------
344   // Reset state
345   add_test(function () {
346     clearOCSPCache();
347     run_next_test();
348   });
350   // Test that the OCSP cache is not isolated by userContextId.
352   // A good OCSP response will be cached.
353   add_ocsp_test(
354     "ocsp-stapling-none.example.com",
355     PRErrorCodeSuccess,
356     [respondWithGoodOCSP],
357     "No stapled response (userContextId = 1) -> a fetch " +
358       "should have been attempted",
359     { userContextId: 1 }
360   );
362   // The cache will prevent a fetch from happening.
363   add_ocsp_test(
364     "ocsp-stapling-none.example.com",
365     PRErrorCodeSuccess,
366     [],
367     "Noted OCSP server failure (userContextId = 1) -> a " +
368       "fetch should not have been attempted",
369     { userContextId: 1 }
370   );
372   // Fetching is prevented even if in a different userContextId.
373   add_ocsp_test(
374     "ocsp-stapling-none.example.com",
375     PRErrorCodeSuccess,
376     [],
377     "Noted OCSP server failure (userContextId = 2) -> a " +
378       "fetch should not have been attempted",
379     { userContextId: 2 }
380   );
382   // ---------------------------------------------------------------------------
384   // Reset state
385   add_test(function () {
386     clearOCSPCache();
387     run_next_test();
388   });
390   // This test makes sure that OCSP cache are isolated by partitionKey.
392   add_test(function () {
393     Services.prefs.setBoolPref(
394       "privacy.partition.network_state.ocsp_cache",
395       true
396     );
397     run_next_test();
398   });
400   // A good OCSP response will be cached.
401   add_ocsp_test(
402     "ocsp-stapling-none.example.com",
403     PRErrorCodeSuccess,
404     [respondWithGoodOCSP],
405     "No stapled response (partitionKey = (https,foo.com)) -> a fetch " +
406       "should have been attempted",
407     { partitionKey: "(https,foo.com)" }
408   );
410   // The cache will prevent a fetch from happening.
411   add_ocsp_test(
412     "ocsp-stapling-none.example.com",
413     PRErrorCodeSuccess,
414     [],
415     "Noted OCSP server failure (partitionKey = (https,foo.com)) -> a " +
416       "fetch should not have been attempted",
417     { partitionKey: "(https,foo.com)" }
418   );
420   // Using a different partitionKey should result in a fetch.
421   add_ocsp_test(
422     "ocsp-stapling-none.example.com",
423     PRErrorCodeSuccess,
424     [respondWithGoodOCSP],
425     "Noted OCSP server failure (partitionKey = (https,bar.com)) -> a " +
426       "fetch should have been attempted",
427     { partitionKey: "(https,bar.com)" }
428   );
430   // ---------------------------------------------------------------------------
432   // Reset state
433   add_test(function () {
434     Services.prefs.clearUserPref("privacy.partition.network_state.ocsp_cache");
435     clearOCSPCache();
436     run_next_test();
437   });
439   // This test makes sure that OCSP cache are isolated by partitionKey in
440   // private mode.
442   // A good OCSP response will be cached.
443   add_ocsp_test(
444     "ocsp-stapling-none.example.com",
445     PRErrorCodeSuccess,
446     [respondWithGoodOCSP],
447     "No stapled response (partitionKey = (https,foo.com)) -> a fetch " +
448       "should have been attempted",
449     { partitionKey: "(https,foo.com)", privateBrowsingId: 1 }
450   );
452   // The cache will prevent a fetch from happening.
453   add_ocsp_test(
454     "ocsp-stapling-none.example.com",
455     PRErrorCodeSuccess,
456     [],
457     "Noted OCSP server failure (partitionKey = (https,foo.com)) -> a " +
458       "fetch should not have been attempted",
459     { partitionKey: "(https,foo.com)", privateBrowsingId: 1 }
460   );
462   // Using a different partitionKey should result in a fetch.
463   add_ocsp_test(
464     "ocsp-stapling-none.example.com",
465     PRErrorCodeSuccess,
466     [respondWithGoodOCSP],
467     "Noted OCSP server failure (partitionKey = (https,bar.com)) -> a " +
468       "fetch should have been attempted",
469     { partitionKey: "(https,bar.com)", privateBrowsingId: 1 }
470   );
472   // ---------------------------------------------------------------------------
474   // Reset state
475   add_test(function () {
476     clearOCSPCache();
477     run_next_test();
478   });