tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 );