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/.
8 // Tests that end-entity certificates that should successfully verify as EV
9 // (Extended Validation) do so and that end-entity certificates that should not
10 // successfully verify as EV do not. Also tests related situations (e.g. that
11 // failure to fetch an OCSP response results in no EV treatment).
13 // A quick note about the certificates in these tests: generally, an EV
14 // certificate chain will have an end-entity with a specific policy OID followed
15 // by an intermediate with the anyPolicy OID chaining to a root with no policy
16 // OID (since it's a trust anchor, it can be omitted). In these tests, the
17 // specific policy OID is 1.3.6.1.4.1.13769.666.666.666.1.500.9.1 and is
18 // referred to as the test OID. In order to reflect what will commonly be
19 // encountered, the end-entity of any given test path will have the test OID
20 // unless otherwise specified in the name of the test path. Similarly, the
21 // intermediate will have the anyPolicy OID, again unless otherwise specified.
22 // For example, for the path where the end-entity does not have an OCSP URI
23 // (referred to as "no-ocsp-ee-path-{ee,int}", the end-entity has the test OID
24 // whereas the intermediate has the anyPolicy OID.
25 // For another example, for the test OID path ("test-oid-path-{ee,int}"), both
26 // the end-entity and the intermediate have the test OID.
28 do_get_profile(); // must be called before getting nsIX509CertDB
29 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
33 registerCleanupFunction(() => {
34 Services.prefs.clearUserPref("network.dns.localDomains");
35 Services.prefs.clearUserPref("security.OCSP.enabled");
38 Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
39 Services.prefs.setIntPref("security.OCSP.enabled", 1);
40 const evroot = addCertFromFile(certdb, "test_ev_certs/evroot.pem", "CTu,,");
41 addCertFromFile(certdb, "test_ev_certs/non-evroot-ca.pem", "CTu,,");
43 const SERVER_PORT = 8888;
45 function failingOCSPResponder() {
46 return getFailingHttpServer(SERVER_PORT, ["www.example.com"]);
49 class EVCertVerificationResult {
57 this.testcase = testcase;
58 this.expectedPRErrorCode = expectedPRErrorCode;
59 this.expectedEV = expectedEV;
60 this.resolve = resolve;
61 this.ocspResponder = ocspResponder;
64 verifyCertFinished(prErrorCode, verifiedChain, hasEVPolicy) {
67 this.expectedPRErrorCode,
68 `${this.testcase} should have expected error code`
73 `${this.testcase} should result in expected EV status`
75 this.ocspResponder.stop(this.resolve);
83 expectedOCSPRequestPaths,
84 ocspResponseTypes = undefined
86 let now = Date.now() / 1000;
87 return new Promise(resolve => {
88 let ocspResponder = expectedOCSPRequestPaths.length
93 expectedOCSPRequestPaths,
94 expectedOCSPRequestPaths.slice(),
98 : failingOCSPResponder();
99 let result = new EVCertVerificationResult(
106 certdb.asyncVerifyCertAtTime(
108 certificateUsageSSLServer,
110 "ev-test.example.com",
117 function ensureVerifiesAsEV(testcase) {
118 let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
119 addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
120 let expectedOCSPRequestPaths = [`${testcase}-ee`];
125 expectedOCSPRequestPaths
129 function ensureVerifiesAsEVWithNoOCSPRequests(testcase) {
130 let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
131 addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
132 return asyncTestEV(cert, PRErrorCodeSuccess, gEVExpected, []);
135 function ensureVerifiesAsDV(testcase, expectedOCSPRequestPaths = undefined) {
136 let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
137 addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
142 expectedOCSPRequestPaths ? expectedOCSPRequestPaths : [`${testcase}-ee`]
146 function ensureVerificationFails(testcase, expectedPRErrorCode) {
147 let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
148 addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
149 return asyncTestEV(cert, expectedPRErrorCode, false, []);
152 function verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, expectSuccess) {
153 let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
154 addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
155 let now = Date.now() / 1000;
156 let expectedErrorCode = SEC_ERROR_POLICY_VALIDATION_FAILED;
157 if (expectSuccess && gEVExpected) {
158 expectedErrorCode = PRErrorCodeSuccess;
160 return new Promise(resolve => {
161 let ocspResponder = failingOCSPResponder();
162 let result = new EVCertVerificationResult(
165 expectSuccess && gEVExpected,
170 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY | Ci.nsIX509CertDB.FLAG_MUST_BE_EV;
171 certdb.asyncVerifyCertAtTime(
173 certificateUsageSSLServer,
175 "ev-test.example.com",
182 function ensureNoOCSPMeansNoEV(testcase) {
183 return verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, false);
186 function ensureVerifiesAsEVWithFLAG_LOCAL_ONLY(testcase) {
187 return verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, true);
190 function verifyWithOCSPResponseType(testcase, response, expectEV) {
191 let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
192 addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
193 let expectedOCSPRequestPaths = [`${testcase}-ee`];
194 let ocspResponseTypes = [response];
198 gEVExpected && expectEV,
199 expectedOCSPRequestPaths,
204 function ensureVerifiesAsDVWithOldEndEntityOCSPResponse(testcase) {
205 return verifyWithOCSPResponseType(testcase, "longvalidityalmostold", false);
208 function ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse(testcase) {
209 return verifyWithOCSPResponseType(testcase, "ancientstillvalid", false);
212 // These should all verify as EV.
213 add_task(async function plainExpectSuccessEVTests() {
214 await ensureVerifiesAsEV("anyPolicy-int-path");
215 await ensureVerifiesAsEV("test-oid-path");
216 await ensureVerifiesAsEV("cabforum-oid-path");
217 await ensureVerifiesAsEV("cabforum-and-test-oid-ee-path");
218 await ensureVerifiesAsEV("test-and-cabforum-oid-ee-path");
219 await ensureVerifiesAsEV("reverse-order-oids-path");
220 // In this case, the end-entity has both the CA/B Forum OID and the test OID
221 // (in that order). The intermediate has the CA/B Forum OID. Since the
222 // implementation tries all EV policies it encounters, this successfully
224 await ensureVerifiesAsEV("cabforum-and-test-oid-ee-cabforum-oid-int-path");
225 // In this case, the end-entity has both the test OID and the CA/B Forum OID
226 // (in that order). The intermediate has only the CA/B Forum OID. Since the
227 // implementation tries all EV policies it encounters, this successfully
229 await ensureVerifiesAsEV("test-and-cabforum-oid-ee-cabforum-oid-int-path");
232 // These fail for various reasons to verify as EV, but fallback to DV should
234 add_task(async function expectDVFallbackTests() {
235 await ensureVerifiesAsDV("anyPolicy-ee-path");
236 await ensureVerifiesAsDV("non-ev-root-path");
237 await ensureVerifiesAsDV("no-ocsp-ee-path", []);
238 await ensureVerifiesAsEV("no-ocsp-int-path");
239 // In this case, the end-entity has the test OID and the intermediate has the
240 // CA/B Forum OID. Since the CA/B Forum OID is not treated the same as the
241 // anyPolicy OID, this will not verify as EV.
242 await ensureVerifiesAsDV("test-oid-ee-cabforum-oid-int-path");
245 // Test that removing the trust bits from an EV root causes verifications
246 // relying on that root to fail (and then test that adding back the trust bits
247 // causes the verifications to succeed again).
248 add_task(async function evRootTrustTests() {
250 info("untrusting evroot");
253 Ci.nsIX509Cert.CA_CERT,
254 Ci.nsIX509CertDB.UNTRUSTED
256 await ensureVerificationFails("test-oid-path", SEC_ERROR_UNKNOWN_ISSUER);
257 info("re-trusting evroot");
260 Ci.nsIX509Cert.CA_CERT,
261 Ci.nsIX509CertDB.TRUSTED_SSL
263 await ensureVerifiesAsEV("test-oid-path");
266 // Test that if FLAG_LOCAL_ONLY and FLAG_MUST_BE_EV are specified, that no OCSP
267 // requests are made (this also means that nothing will verify as EV).
268 add_task(async function localOnlyMustBeEVTests() {
270 await ensureNoOCSPMeansNoEV("anyPolicy-ee-path");
271 await ensureNoOCSPMeansNoEV("anyPolicy-int-path");
272 await ensureNoOCSPMeansNoEV("non-ev-root-path");
273 await ensureNoOCSPMeansNoEV("no-ocsp-ee-path");
274 await ensureNoOCSPMeansNoEV("no-ocsp-int-path");
275 await ensureNoOCSPMeansNoEV("test-oid-path");
278 // Prime the OCSP cache and then ensure that we can validate certificates as EV
279 // without hitting the network. There's two cases here: one where we simply
280 // validate like normal and then check that the network was never accessed and
281 // another where we use flags to mandate that the network not be used.
282 add_task(async function ocspCachingTests() {
285 await ensureVerifiesAsEV("anyPolicy-int-path");
286 await ensureVerifiesAsEV("test-oid-path");
288 await ensureVerifiesAsEVWithNoOCSPRequests("anyPolicy-int-path");
289 await ensureVerifiesAsEVWithNoOCSPRequests("test-oid-path");
291 await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("anyPolicy-int-path");
292 await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("test-oid-path");
295 // Old-but-still-valid OCSP responses are accepted for intermediates but not
296 // end-entity certificates (because of OCSP soft-fail this results in DV
298 add_task(async function oldOCSPResponseTests() {
302 await ensureVerifiesAsDVWithOldEndEntityOCSPResponse("anyPolicy-int-path");
303 await ensureVerifiesAsDVWithOldEndEntityOCSPResponse("test-oid-path");
306 await ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse(
309 await ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse("test-oid-path");