Bug 1940967 - Vendor glean_parser v16.2.0 r=TravisLong,mach-reviewers,ahal
[gecko.git] / security / manager / ssl / tests / unit / test_session_resumption.js
blobfe7252a6303e24eba921f9df0b0fa5ce23f507a5
1 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 // Any copyright is dedicated to the Public Domain.
3 // http://creativecommons.org/publicdomain/zero/1.0/
4 "use strict";
6 // Tests that PSM makes the correct determination of the security status of
7 // loads involving session resumption (i.e. when a TLS handshake bypasses the
8 // AuthCertificate callback).
10 do_get_profile();
11 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
12   Ci.nsIX509CertDB
15 registerCleanupFunction(() => {
16   Services.prefs.clearUserPref("security.OCSP.enabled");
17 });
19 Services.prefs.setIntPref("security.OCSP.enabled", 1);
21 addCertFromFile(certdb, "bad_certs/evroot.pem", "CTu,,");
22 addCertFromFile(certdb, "bad_certs/ev-test-intermediate.pem", ",,");
24 // For expired.example.com, the platform will make a connection that will fail.
25 // Using information gathered at that point, an override will be added and
26 // another connection will be made. This connection will succeed. At that point,
27 // as long as the session cache isn't cleared, subsequent new connections should
28 // use session resumption, thereby bypassing the AuthCertificate hook. We need
29 // to ensure that the correct security state is propagated to the new connection
30 // information object.
31 function add_resume_non_ev_with_override_test() {
32   // This adds the override and makes one successful connection.
33   add_cert_override_test("expired.example.com", SEC_ERROR_EXPIRED_CERTIFICATE);
35   // This connects again, using session resumption. Note that we don't clear
36   // the TLS session cache between these operations (that would defeat the
37   // purpose).
38   add_connection_test(
39     "expired.example.com",
40     PRErrorCodeSuccess,
41     null,
42     transportSecurityInfo => {
43       ok(transportSecurityInfo.resumed, "connection should be resumed");
44       ok(
45         transportSecurityInfo.securityState &
46           Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
47         "expired.example.com should have STATE_CERT_USER_OVERRIDDEN flag"
48       );
49       equal(
50         transportSecurityInfo.succeededCertChain.length,
51         0,
52         "expired.example.com should not have succeededCertChain set"
53       );
54       equal(
55         transportSecurityInfo.failedCertChain.length,
56         2,
57         "expired.example.com should have failedCertChain set"
58       );
59       equal(
60         transportSecurityInfo.overridableErrorCategory,
61         Ci.nsITransportSecurityInfo.ERROR_TIME,
62         "expired.example.com should have time overridable error category"
63       );
64       ok(
65         !transportSecurityInfo.isExtendedValidation,
66         "expired.example.com should not have isExtendedValidation set"
67       );
69       let certOverrideService = Cc[
70         "@mozilla.org/security/certoverride;1"
71       ].getService(Ci.nsICertOverrideService);
72       certOverrideService.clearValidityOverride(
73         "expired.example.com",
74         8443,
75         {}
76       );
77     }
78   );
81 // Helper function that adds a test that connects to ev-test.example.com and
82 // verifies that it validates as EV (or not, if we're running a non-debug
83 // build). This assumes that an appropriate OCSP responder is running or that
84 // good responses are cached.
85 function add_one_ev_test(resumed) {
86   add_connection_test(
87     "ev-test.example.com",
88     PRErrorCodeSuccess,
89     null,
90     transportSecurityInfo => {
91       equal(
92         transportSecurityInfo.resumed,
93         resumed,
94         "connection should be resumed or not resumed as expected"
95       );
96       ok(
97         !(
98           transportSecurityInfo.securityState &
99           Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN
100         ),
101         "ev-test.example.com should not have STATE_CERT_USER_OVERRIDDEN flag"
102       );
103       equal(
104         transportSecurityInfo.succeededCertChain.length,
105         3,
106         "ev-test.example.com should have succeededCertChain set"
107       );
108       equal(
109         transportSecurityInfo.failedCertChain.length,
110         0,
111         "ev-test.example.com should not have failedCertChain set"
112       );
113       equal(
114         transportSecurityInfo.overridableErrorCategory,
115         Ci.nsITransportSecurityInfo.ERROR_UNSET,
116         "ev-test.example.com should not have an overridable error category"
117       );
118       ok(
119         !gEVExpected || transportSecurityInfo.isExtendedValidation,
120         "ev-test.example.com should have isExtendedValidation set " +
121           "(or this is a non-debug build)"
122       );
123     }
124   );
127 // This test is similar, except with extended validation. We should connect
128 // successfully, and the certificate should be EV in debug builds. Without
129 // clearing the session cache, we should connect successfully again, this time
130 // with session resumption. The certificate should again be EV in debug builds.
131 function add_resume_ev_test() {
132   const SERVER_PORT = 8888;
133   let expectedRequestPaths = ["ev-test"];
134   let responseTypes = ["good"];
135   // Since we cache OCSP responses, we only ever actually serve one set.
136   let ocspResponder;
137   // If we don't wrap this in an `add_test`, the OCSP responder will be running
138   // while we are actually running unrelated testcases, which can disrupt them.
139   add_test(() => {
140     ocspResponder = startOCSPResponder(
141       SERVER_PORT,
142       "localhost",
143       "bad_certs",
144       expectedRequestPaths,
145       expectedRequestPaths.slice(),
146       null,
147       responseTypes
148     );
149     run_next_test();
150   });
151   // We should be able to connect and verify the certificate as EV (in debug
152   // builds).
153   add_one_ev_test(false);
154   // We should be able to connect again (using session resumption). In debug
155   // builds, the certificate should be noted as EV. Again, it's important that
156   // nothing clears the TLS cache in between these two operations.
157   add_one_ev_test(true);
159   add_test(() => {
160     ocspResponder.stop(run_next_test);
161   });
164 const GOOD_DOMAIN = "good.include-subdomains.pinning.example.com";
166 // Helper function that adds a test that connects to a domain that should
167 // succeed (but isn't EV) and verifies that its succeededCertChain gets set
168 // appropriately.
169 function add_one_non_ev_test() {
170   add_connection_test(
171     GOOD_DOMAIN,
172     PRErrorCodeSuccess,
173     null,
174     transportSecurityInfo => {
175       ok(
176         !(
177           transportSecurityInfo.securityState &
178           Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN
179         ),
180         `${GOOD_DOMAIN} should not have STATE_CERT_USER_OVERRIDDEN flag`
181       );
182       ok(
183         transportSecurityInfo.succeededCertChain,
184         `${GOOD_DOMAIN} should have succeededCertChain set`
185       );
186       equal(
187         transportSecurityInfo.overridableErrorCategory,
188         0,
189         `${GOOD_DOMAIN} should not have an overridable error category set`
190       );
191       ok(
192         !transportSecurityInfo.isExtendedValidation,
193         `${GOOD_DOMAIN} should not have isExtendedValidation set`
194       );
195     }
196   );
199 // This test is similar, except with non-extended validation. We should connect
200 // successfully, and the certificate should not be EV. Without clearing the
201 // session cache, we should connect successfully again, this time with session
202 // resumption. In this case, though, we want to ensure the succeededCertChain is
203 // set.
204 function add_resume_non_ev_test() {
205   add_one_non_ev_test();
206   add_one_non_ev_test();
209 const statsPtr = getSSLStatistics();
210 const toInt32 = ctypes.Int64.lo;
212 // Connect to the same domain with two origin attributes and check if any ssl
213 // session is resumed.
214 function add_origin_attributes_test(
215   originAttributes1,
216   originAttributes2,
217   resumeExpected
218 ) {
219   add_connection_test(
220     GOOD_DOMAIN,
221     PRErrorCodeSuccess,
222     clearSessionCache,
223     null,
224     null,
225     originAttributes1
226   );
228   let hitsBeforeConnect;
229   let missesBeforeConnect;
230   let expectedHits = resumeExpected ? 1 : 0;
231   let expectedMisses = 1 - expectedHits;
233   add_connection_test(
234     GOOD_DOMAIN,
235     PRErrorCodeSuccess,
236     function () {
237       // Add the hits and misses before connection.
238       let stats = statsPtr.contents;
239       hitsBeforeConnect = toInt32(stats.sch_sid_cache_hits);
240       missesBeforeConnect = toInt32(stats.sch_sid_cache_misses);
241     },
242     function () {
243       let stats = statsPtr.contents;
244       equal(
245         toInt32(stats.sch_sid_cache_hits),
246         hitsBeforeConnect + expectedHits,
247         "Unexpected cache hits"
248       );
249       equal(
250         toInt32(stats.sch_sid_cache_misses),
251         missesBeforeConnect + expectedMisses,
252         "Unexpected cache misses"
253       );
254     },
255     null,
256     originAttributes2
257   );
260 function add_resumption_tests() {
261   add_resume_ev_test();
262   add_resume_non_ev_test();
263   add_resume_non_ev_with_override_test();
264   add_origin_attributes_test({}, {}, true);
265   add_origin_attributes_test({ userContextId: 1 }, { userContextId: 2 }, false);
266   add_origin_attributes_test({ userContextId: 3 }, { userContextId: 3 }, true);
267   add_origin_attributes_test(
268     { firstPartyDomain: "foo.com" },
269     { firstPartyDomain: "bar.com" },
270     false
271   );
272   add_origin_attributes_test(
273     { firstPartyDomain: "baz.com" },
274     { firstPartyDomain: "baz.com" },
275     true
276   );
279 function run_test() {
280   add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
281   add_resumption_tests();
282   // Enable external session cache and reset the status.
283   add_test(function () {
284     Services.prefs.setBoolPref("network.ssl_tokens_cache_enabled", true);
285     certdb.clearOCSPCache();
286     run_next_test();
287   });
288   // Do tests again.
289   add_resumption_tests();
290   run_next_test();