test_ev_certs.js (12817B)
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 6 "use strict"; 7 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. 11 // 12 // A quick note about the certificates in these tests: generally, an EV 13 // certificate chain will have an end-entity with a specific policy OID followed 14 // by an intermediate with the anyPolicy OID chaining to a root with no policy 15 // OID (since it's a trust anchor, it can be omitted). In these tests, the 16 // specific policy OID is 1.3.6.1.4.1.13769.666.666.666.1.500.9.1 and is 17 // referred to as the test OID. In order to reflect what will commonly be 18 // encountered, the end-entity of any given test path will have the test OID 19 // unless otherwise specified in the name of the test path. Similarly, the 20 // intermediate will have the anyPolicy OID, again unless otherwise specified. 21 // For example, for the path where the end-entity does not have an OCSP URI 22 // (referred to as "no-ocsp-ee-path-{ee,int}", the end-entity has the test OID 23 // whereas the intermediate has the anyPolicy OID. 24 // For another example, for the test OID path ("test-oid-path-{ee,int}"), both 25 // the end-entity and the intermediate have the test OID. 26 27 do_get_profile(); // must be called before getting nsIX509CertDB 28 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( 29 Ci.nsIX509CertDB 30 ); 31 32 registerCleanupFunction(() => { 33 Services.prefs.clearUserPref("network.dns.localDomains"); 34 Services.prefs.clearUserPref("security.OCSP.enabled"); 35 }); 36 37 Services.prefs.setCharPref("network.dns.localDomains", "www.example.com"); 38 Services.prefs.setIntPref("security.OCSP.enabled", 1); 39 const evroot = addCertFromFile(certdb, "test_ev_certs/evroot.pem", "CTu,,"); 40 addCertFromFile(certdb, "test_ev_certs/non-evroot-ca.pem", "CTu,,"); 41 42 const SERVER_PORT = 8888; 43 44 function failingOCSPResponder() { 45 return getFailingHttpServer(SERVER_PORT, ["www.example.com"]); 46 } 47 48 class EVCertVerificationResult { 49 constructor( 50 testcase, 51 expectedPRErrorCode, 52 expectedEV, 53 resolve, 54 ocspResponder 55 ) { 56 this.testcase = testcase; 57 this.expectedPRErrorCode = expectedPRErrorCode; 58 this.expectedEV = expectedEV; 59 this.resolve = resolve; 60 this.ocspResponder = ocspResponder; 61 } 62 63 verifyCertFinished(prErrorCode, verifiedChain, hasEVPolicy) { 64 equal( 65 prErrorCode, 66 this.expectedPRErrorCode, 67 `${this.testcase} should have expected error code` 68 ); 69 equal( 70 hasEVPolicy, 71 this.expectedEV, 72 `${this.testcase} should result in expected EV status` 73 ); 74 this.ocspResponder.stop(this.resolve); 75 } 76 } 77 78 function asyncTestEV( 79 cert, 80 expectedPRErrorCode, 81 expectedEV, 82 expectedOCSPRequestPaths, 83 ocspResponseTypes = undefined 84 ) { 85 let now = Date.now() / 1000; 86 return new Promise(resolve => { 87 let ocspResponder = expectedOCSPRequestPaths.length 88 ? startOCSPResponder( 89 SERVER_PORT, 90 "www.example.com", 91 "test_ev_certs", 92 expectedOCSPRequestPaths, 93 expectedOCSPRequestPaths.slice(), 94 null, 95 ocspResponseTypes 96 ) 97 : failingOCSPResponder(); 98 let result = new EVCertVerificationResult( 99 cert.subjectName, 100 expectedPRErrorCode, 101 expectedEV, 102 resolve, 103 ocspResponder 104 ); 105 certdb.asyncVerifyCertAtTime( 106 cert, 107 Ci.nsIX509CertDB.verifyUsageTLSServer, 108 0, 109 "ev-test.example.com", 110 now, 111 [], 112 result 113 ); 114 }); 115 } 116 117 function ensureVerifiesAsEVWithOneOCSPRequest(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`]; 121 return asyncTestEV( 122 cert, 123 PRErrorCodeSuccess, 124 gEVExpected, 125 expectedOCSPRequestPaths 126 ); 127 } 128 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, []); 133 } 134 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`, ",,"); 138 return asyncTestEV( 139 cert, 140 PRErrorCodeSuccess, 141 false, 142 expectedOCSPRequestPaths ? expectedOCSPRequestPaths : [`${testcase}-ee`] 143 ); 144 } 145 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, []); 150 } 151 152 function ensureVerifiesAsEVWithFLAG_LOCAL_ONLY(testcase) { 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 (gEVExpected) { 158 expectedErrorCode = PRErrorCodeSuccess; 159 } 160 return new Promise(resolve => { 161 let ocspResponder = failingOCSPResponder(); 162 let result = new EVCertVerificationResult( 163 cert.subjectName, 164 expectedErrorCode, 165 gEVExpected, 166 resolve, 167 ocspResponder 168 ); 169 let flags = 170 Ci.nsIX509CertDB.FLAG_LOCAL_ONLY | Ci.nsIX509CertDB.FLAG_MUST_BE_EV; 171 certdb.asyncVerifyCertAtTime( 172 cert, 173 Ci.nsIX509CertDB.verifyUsageTLSServer, 174 flags, 175 "ev-test.example.com", 176 now, 177 [], 178 result 179 ); 180 }); 181 } 182 183 function verifyWithOCSPResponseType(testcase, response, expectEV) { 184 let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`); 185 addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,"); 186 let expectedOCSPRequestPaths = [`${testcase}-ee`]; 187 let ocspResponseTypes = [response]; 188 return asyncTestEV( 189 cert, 190 PRErrorCodeSuccess, 191 gEVExpected && expectEV, 192 expectedOCSPRequestPaths, 193 ocspResponseTypes 194 ); 195 } 196 197 function ensureVerifiesAsEVWithOldEndEntityOCSPResponse(testcase) { 198 return verifyWithOCSPResponseType(testcase, "longvalidityalmostold", true); 199 } 200 201 function ensureVerifiesAsEVWithVeryOldEndEntityOCSPResponse(testcase) { 202 return verifyWithOCSPResponseType(testcase, "ancientstillvalid", true); 203 } 204 205 // These should all verify as EV. 206 add_task(async function plainExpectSuccessEVTests() { 207 await ensureVerifiesAsEVWithOneOCSPRequest("anyPolicy-int-path"); 208 await ensureVerifiesAsEVWithOneOCSPRequest("test-oid-path"); 209 await ensureVerifiesAsEVWithOneOCSPRequest("cabforum-oid-path"); 210 await ensureVerifiesAsEVWithOneOCSPRequest("cabforum-and-test-oid-ee-path"); 211 await ensureVerifiesAsEVWithOneOCSPRequest("test-and-cabforum-oid-ee-path"); 212 await ensureVerifiesAsEVWithOneOCSPRequest("reverse-order-oids-path"); 213 await ensureVerifiesAsEVWithNoOCSPRequests("no-ocsp-ee-path"); 214 await ensureVerifiesAsEVWithOneOCSPRequest("no-ocsp-int-path"); 215 // In this case, the end-entity has both the CA/B Forum OID and the test OID 216 // (in that order). The intermediate has the CA/B Forum OID. Since the 217 // implementation tries all EV policies it encounters, this successfully 218 // verifies as EV. 219 await ensureVerifiesAsEVWithOneOCSPRequest( 220 "cabforum-and-test-oid-ee-cabforum-oid-int-path" 221 ); 222 // In this case, the end-entity has both the test OID and the CA/B Forum OID 223 // (in that order). The intermediate has only the CA/B Forum OID. Since the 224 // implementation tries all EV policies it encounters, this successfully 225 // verifies as EV. 226 await ensureVerifiesAsEVWithOneOCSPRequest( 227 "test-and-cabforum-oid-ee-cabforum-oid-int-path" 228 ); 229 }); 230 231 // These fail for various reasons to verify as EV, but fallback to DV should 232 // succeed. 233 add_task(async function expectDVFallbackTests() { 234 await ensureVerifiesAsDV("anyPolicy-ee-path"); 235 await ensureVerifiesAsDV("non-ev-root-path"); 236 // In this case, the end-entity has the test OID and the intermediate has the 237 // CA/B Forum OID. Since the CA/B Forum OID is not treated the same as the 238 // anyPolicy OID, this will not verify as EV. 239 await ensureVerifiesAsDV("test-oid-ee-cabforum-oid-int-path"); 240 }); 241 242 // Test that removing the trust bits from an EV root causes verifications 243 // relying on that root to fail (and then test that adding back the trust bits 244 // causes the verifications to succeed again). 245 add_task(async function evRootTrustTests() { 246 clearOCSPCache(); 247 info("untrusting evroot"); 248 certdb.setCertTrust( 249 evroot, 250 Ci.nsIX509Cert.CA_CERT, 251 Ci.nsIX509CertDB.UNTRUSTED 252 ); 253 await ensureVerificationFails("test-oid-path", SEC_ERROR_UNKNOWN_ISSUER); 254 info("re-trusting evroot"); 255 certdb.setCertTrust( 256 evroot, 257 Ci.nsIX509Cert.CA_CERT, 258 Ci.nsIX509CertDB.TRUSTED_SSL 259 ); 260 await ensureVerifiesAsEVWithOneOCSPRequest("test-oid-path"); 261 }); 262 263 // Test that if FLAG_LOCAL_ONLY and FLAG_MUST_BE_EV are specified, that no OCSP 264 // requests are made. 265 add_task(async function expectEVWithFlagLocalOnly() { 266 clearOCSPCache(); 267 await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("anyPolicy-int-path"); 268 await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("no-ocsp-ee-path"); 269 await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("no-ocsp-int-path"); 270 await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("test-oid-path"); 271 }); 272 273 // Prime the OCSP cache and then ensure that we can validate certificates as EV 274 // without hitting the network. There's two cases here: one where we simply 275 // validate like normal and then check that the network was never accessed and 276 // another where we use flags to mandate that the network not be used. 277 add_task(async function ocspCachingTests() { 278 clearOCSPCache(); 279 280 await ensureVerifiesAsEVWithOneOCSPRequest("anyPolicy-int-path"); 281 await ensureVerifiesAsEVWithOneOCSPRequest("test-oid-path"); 282 283 await ensureVerifiesAsEVWithNoOCSPRequests("anyPolicy-int-path"); 284 await ensureVerifiesAsEVWithNoOCSPRequests("test-oid-path"); 285 286 await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("anyPolicy-int-path"); 287 await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("test-oid-path"); 288 }); 289 290 // It was once the case that old-but-still-valid OCSP responses were accepted 291 // for intermediates but not end-entity certificates (because of OCSP soft-fail 292 // this would result in DV fallback). Now that OCSP is not required for EV 293 // status these certificates should all verify as EV. 294 add_task(async function oldOCSPResponseTests() { 295 clearOCSPCache(); 296 297 clearOCSPCache(); 298 await ensureVerifiesAsEVWithOldEndEntityOCSPResponse("anyPolicy-int-path"); 299 await ensureVerifiesAsEVWithOldEndEntityOCSPResponse("test-oid-path"); 300 301 clearOCSPCache(); 302 await ensureVerifiesAsEVWithVeryOldEndEntityOCSPResponse( 303 "anyPolicy-int-path" 304 ); 305 await ensureVerifiesAsEVWithVeryOldEndEntityOCSPResponse("test-oid-path"); 306 }); 307 308 add_task( 309 { skip_if: () => !AppConstants.DEBUG }, 310 async function expectEVUsingBuiltInRoot() { 311 // security.test.built_in_root_hash only works in debug builds. 312 Services.prefs.setCharPref( 313 "security.test.built_in_root_hash", 314 evroot.sha256Fingerprint 315 ); 316 // When CRLite is enforced and OCSP is not required, a certificate that 317 // chains to a built-in root can get EV status without an OCSP check. 318 Services.prefs.setIntPref("security.pki.crlite_mode", 2); 319 Services.prefs.setBoolPref("security.OCSP.require", false); 320 321 clearOCSPCache(); 322 await ensureVerifiesAsEVWithNoOCSPRequests("anyPolicy-int-path"); 323 await ensureVerifiesAsEVWithNoOCSPRequests("test-oid-path"); 324 325 // When CRLite is disabled and OCSP is not required, we will perform an 326 // OCSP request while checking these same certificates. 327 Services.prefs.setIntPref("security.pki.crlite_mode", 0); 328 Services.prefs.setBoolPref("security.OCSP.require", false); 329 330 clearOCSPCache(); 331 await ensureVerifiesAsEVWithOneOCSPRequest("anyPolicy-int-path"); 332 await ensureVerifiesAsEVWithOneOCSPRequest("test-oid-path"); 333 334 // Likewise if CRLite is enforced and OCSP is required. 335 Services.prefs.setIntPref("security.pki.crlite_mode", 2); 336 Services.prefs.setBoolPref("security.OCSP.require", true); 337 338 clearOCSPCache(); 339 await ensureVerifiesAsEVWithOneOCSPRequest("anyPolicy-int-path"); 340 await ensureVerifiesAsEVWithOneOCSPRequest("test-oid-path"); 341 342 Services.prefs.clearUserPref("security.test.built_in_root_hash"); 343 Services.prefs.clearUserPref("security.pki.crlite_mode"); 344 Services.prefs.clearUserPref("security.OCSP.require"); 345 } 346 );