mirror of
https://github.com/ManchildProductions/UXP-Fixed.git
synced 2026-06-15 10:31:02 +00:00
357 lines
16 KiB
JavaScript
357 lines
16 KiB
JavaScript
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
"use strict";
|
|
|
|
// Tests that end-entity certificates that should successfully verify as EV
|
|
// (Extended Validation) do so and that end-entity certificates that should not
|
|
// successfully verify as EV do not. Also tests related situations (e.g. that
|
|
// failure to fetch an OCSP response results in no EV treatment).
|
|
//
|
|
// A quick note about the certificates in these tests: generally, an EV
|
|
// certificate chain will have an end-entity with a specific policy OID followed
|
|
// by an intermediate with the anyPolicy OID chaining to a root with no policy
|
|
// OID (since it's a trust anchor, it can be omitted). In these tests, the
|
|
// specific policy OID is 1.3.6.1.4.1.13769.666.666.666.1.500.9.1 and is
|
|
// referred to as the test OID. In order to reflect what will commonly be
|
|
// encountered, the end-entity of any given test path will have the test OID
|
|
// unless otherwise specified in the name of the test path. Similarly, the
|
|
// intermediate will have the anyPolicy OID, again unless otherwise specified.
|
|
// For example, for the path where the end-entity does not have an OCSP URI
|
|
// (referred to as "no-ocsp-ee-path-{ee,int}", the end-entity has the test OID
|
|
// whereas the intermediate has the anyPolicy OID.
|
|
// For another example, for the test OID path ("test-oid-path-{ee,int}"), both
|
|
// the end-entity and the intermediate have the test OID.
|
|
|
|
do_get_profile(); // must be called before getting nsIX509CertDB
|
|
const certdb = Cc["@mozilla.org/security/x509certdb;1"]
|
|
.getService(Ci.nsIX509CertDB);
|
|
|
|
do_register_cleanup(() => {
|
|
Services.prefs.clearUserPref("network.dns.localDomains");
|
|
Services.prefs.clearUserPref("security.OCSP.enabled");
|
|
});
|
|
|
|
Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
|
|
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
|
addCertFromFile(certdb, "test_ev_certs/evroot.pem", "CTu,,");
|
|
addCertFromFile(certdb, "test_ev_certs/non-evroot-ca.pem", "CTu,,");
|
|
|
|
const SERVER_PORT = 8888;
|
|
|
|
function failingOCSPResponder() {
|
|
return getFailingHttpServer(SERVER_PORT, ["www.example.com"]);
|
|
}
|
|
|
|
class EVCertVerificationResult {
|
|
constructor(testcase, expectedPRErrorCode, expectedEV, resolve,
|
|
ocspResponder) {
|
|
this.testcase = testcase;
|
|
this.expectedPRErrorCode = expectedPRErrorCode;
|
|
this.expectedEV = expectedEV;
|
|
this.resolve = resolve;
|
|
this.ocspResponder = ocspResponder;
|
|
}
|
|
|
|
verifyCertFinished(prErrorCode, verifiedChain, hasEVPolicy) {
|
|
equal(prErrorCode, this.expectedPRErrorCode,
|
|
`${this.testcase} should have expected error code`);
|
|
equal(hasEVPolicy, this.expectedEV,
|
|
`${this.testcase} should result in expected EV status`);
|
|
this.ocspResponder.stop(this.resolve);
|
|
}
|
|
}
|
|
|
|
function asyncTestEV(cert, expectedPRErrorCode, expectedEV,
|
|
expectedOCSPRequestPaths, ocspResponseTypes = undefined)
|
|
{
|
|
let now = Date.now() / 1000;
|
|
return new Promise((resolve, reject) => {
|
|
let ocspResponder = expectedOCSPRequestPaths.length > 0
|
|
? startOCSPResponder(SERVER_PORT, "www.example.com",
|
|
"test_ev_certs",
|
|
expectedOCSPRequestPaths,
|
|
expectedOCSPRequestPaths.slice(),
|
|
null, ocspResponseTypes)
|
|
: failingOCSPResponder();
|
|
let result = new EVCertVerificationResult(cert.subjectName,
|
|
expectedPRErrorCode, expectedEV,
|
|
resolve, ocspResponder);
|
|
certdb.asyncVerifyCertAtTime(cert, certificateUsageSSLServer, 0,
|
|
"ev-test.example.com", now, result);
|
|
});
|
|
}
|
|
|
|
function ensureVerifiesAsEV(testcase) {
|
|
let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
|
|
addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
|
|
let expectedOCSPRequestPaths = gEVExpected
|
|
? [ `${testcase}-int`, `${testcase}-ee` ]
|
|
: [ `${testcase}-ee` ];
|
|
return asyncTestEV(cert, PRErrorCodeSuccess, gEVExpected,
|
|
expectedOCSPRequestPaths);
|
|
}
|
|
|
|
function ensureVerifiesAsEVWithNoOCSPRequests(testcase) {
|
|
let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
|
|
addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
|
|
return asyncTestEV(cert, PRErrorCodeSuccess, gEVExpected, []);
|
|
}
|
|
|
|
function ensureVerifiesAsDV(testcase, expectedOCSPRequestPaths = undefined) {
|
|
let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
|
|
addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
|
|
return asyncTestEV(cert, PRErrorCodeSuccess, false,
|
|
expectedOCSPRequestPaths ? expectedOCSPRequestPaths
|
|
: [ `${testcase}-ee` ]);
|
|
}
|
|
|
|
function ensureVerificationFails(testcase, expectedPRErrorCode) {
|
|
let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
|
|
addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
|
|
return asyncTestEV(cert, expectedPRErrorCode, false, []);
|
|
}
|
|
|
|
function verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, expectSuccess) {
|
|
let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
|
|
addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
|
|
let now = Date.now() / 1000;
|
|
let expectedErrorCode = SEC_ERROR_POLICY_VALIDATION_FAILED;
|
|
if (expectSuccess && gEVExpected) {
|
|
expectedErrorCode = PRErrorCodeSuccess;
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
let ocspResponder = failingOCSPResponder();
|
|
let result = new EVCertVerificationResult(
|
|
cert.subjectName, expectedErrorCode, expectSuccess && gEVExpected,
|
|
resolve, ocspResponder);
|
|
let flags = Ci.nsIX509CertDB.FLAG_LOCAL_ONLY |
|
|
Ci.nsIX509CertDB.FLAG_MUST_BE_EV;
|
|
certdb.asyncVerifyCertAtTime(cert, certificateUsageSSLServer, flags,
|
|
"ev-test.example.com", now, result);
|
|
});
|
|
}
|
|
|
|
function ensureNoOCSPMeansNoEV(testcase) {
|
|
return verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, false);
|
|
}
|
|
|
|
function ensureVerifiesAsEVWithFLAG_LOCAL_ONLY(testcase) {
|
|
return verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, true);
|
|
}
|
|
|
|
function ensureOneCRLSkipsOCSPForIntermediates(testcase) {
|
|
let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
|
|
addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
|
|
return asyncTestEV(cert, PRErrorCodeSuccess, gEVExpected,
|
|
[ `${testcase}-ee` ]);
|
|
}
|
|
|
|
function verifyWithDifferentOCSPResponseTypes(testcase, responses, expectEV) {
|
|
let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
|
|
addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
|
|
let expectedOCSPRequestPaths = gEVExpected
|
|
? [ `${testcase}-int`, `${testcase}-ee` ]
|
|
: [ `${testcase}-ee` ];
|
|
let ocspResponseTypes = gEVExpected ? responses : responses.slice(1);
|
|
return asyncTestEV(cert, PRErrorCodeSuccess, gEVExpected && expectEV,
|
|
expectedOCSPRequestPaths, ocspResponseTypes);
|
|
}
|
|
|
|
function ensureVerifiesAsEVWithOldIntermediateOCSPResponse(testcase) {
|
|
return verifyWithDifferentOCSPResponseTypes(
|
|
testcase, [ "longvalidityalmostold", "good" ], true);
|
|
}
|
|
|
|
function ensureVerifiesAsDVWithOldEndEntityOCSPResponse(testcase) {
|
|
return verifyWithDifferentOCSPResponseTypes(
|
|
testcase, [ "good", "longvalidityalmostold" ], false);
|
|
}
|
|
|
|
function ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse(testcase) {
|
|
return verifyWithDifferentOCSPResponseTypes(
|
|
testcase, [ "good", "ancientstillvalid" ], false);
|
|
}
|
|
|
|
// These should all verify as EV.
|
|
add_task(function* plainExpectSuccessEVTests() {
|
|
yield ensureVerifiesAsEV("anyPolicy-int-path");
|
|
yield ensureVerifiesAsEV("test-oid-path");
|
|
yield ensureVerifiesAsEV("cabforum-oid-path");
|
|
yield ensureVerifiesAsEV("cabforum-and-test-oid-ee-path");
|
|
yield ensureVerifiesAsEV("test-and-cabforum-oid-ee-path");
|
|
yield ensureVerifiesAsEV("reverse-order-oids-path");
|
|
// In this case, the end-entity has both the CA/B Forum OID and the test OID
|
|
// (in that order). The intermediate has the CA/B Forum OID. Since the
|
|
// implementation uses the first EV policy it encounters in the end-entity as
|
|
// the required one, this successfully verifies as EV.
|
|
yield ensureVerifiesAsEV("cabforum-and-test-oid-ee-cabforum-oid-int-path");
|
|
});
|
|
|
|
// These fail for various reasons to verify as EV, but fallback to DV should
|
|
// succeed.
|
|
add_task(function* expectDVFallbackTests() {
|
|
yield ensureVerifiesAsDV("anyPolicy-ee-path");
|
|
yield ensureVerifiesAsDV("non-ev-root-path");
|
|
yield ensureVerifiesAsDV("no-ocsp-ee-path",
|
|
gEVExpected ? [ "no-ocsp-ee-path-int" ] : []);
|
|
yield ensureVerifiesAsDV("no-ocsp-int-path");
|
|
// In this case, the end-entity has the test OID and the intermediate has the
|
|
// CA/B Forum OID. Since the CA/B Forum OID is not treated the same as the
|
|
// anyPolicy OID, this will not verify as EV.
|
|
yield ensureVerifiesAsDV("test-oid-ee-cabforum-oid-int-path");
|
|
// In this case, the end-entity has both the test OID and the CA/B Forum OID
|
|
// (in that order). The intermediate has only the CA/B Forum OID. Since the
|
|
// implementation uses the first EV policy it encounters in the end-entity as
|
|
// the required one, this fails to verify as EV.
|
|
yield ensureVerifiesAsDV("test-and-cabforum-oid-ee-cabforum-oid-int-path");
|
|
});
|
|
|
|
// Test that removing the trust bits from an EV root causes verifications
|
|
// relying on that root to fail (and then test that adding back the trust bits
|
|
// causes the verifications to succeed again).
|
|
add_task(function* evRootTrustTests() {
|
|
clearOCSPCache();
|
|
let evroot = certdb.findCertByNickname("evroot");
|
|
do_print("untrusting evroot");
|
|
certdb.setCertTrust(evroot, Ci.nsIX509Cert.CA_CERT,
|
|
Ci.nsIX509CertDB.UNTRUSTED);
|
|
yield ensureVerificationFails("test-oid-path", SEC_ERROR_UNKNOWN_ISSUER);
|
|
do_print("re-trusting evroot");
|
|
certdb.setCertTrust(evroot, Ci.nsIX509Cert.CA_CERT,
|
|
Ci.nsIX509CertDB.TRUSTED_SSL);
|
|
yield ensureVerifiesAsEV("test-oid-path");
|
|
});
|
|
|
|
// Test that if FLAG_LOCAL_ONLY and FLAG_MUST_BE_EV are specified, that no OCSP
|
|
// requests are made (this also means that nothing will verify as EV).
|
|
add_task(function* localOnlyMustBeEVTests() {
|
|
clearOCSPCache();
|
|
yield ensureNoOCSPMeansNoEV("anyPolicy-ee-path");
|
|
yield ensureNoOCSPMeansNoEV("anyPolicy-int-path");
|
|
yield ensureNoOCSPMeansNoEV("non-ev-root-path");
|
|
yield ensureNoOCSPMeansNoEV("no-ocsp-ee-path");
|
|
yield ensureNoOCSPMeansNoEV("no-ocsp-int-path");
|
|
yield ensureNoOCSPMeansNoEV("test-oid-path");
|
|
});
|
|
|
|
|
|
// Under certain conditions, OneCRL allows us to skip OCSP requests for
|
|
// intermediates.
|
|
add_task(function* oneCRLTests() {
|
|
clearOCSPCache();
|
|
|
|
// enable OneCRL OCSP skipping - allow staleness of up to 30 hours
|
|
Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds",
|
|
108000);
|
|
// set the blocklist-background-update-timer value to the recent past
|
|
Services.prefs.setIntPref("services.blocklist.onecrl.checked",
|
|
Math.floor(Date.now() / 1000) - 1);
|
|
Services.prefs.setIntPref(
|
|
"app.update.lastUpdateTime.blocklist-background-update-timer",
|
|
Math.floor(Date.now() / 1000) - 1);
|
|
|
|
yield ensureOneCRLSkipsOCSPForIntermediates("anyPolicy-int-path");
|
|
yield ensureOneCRLSkipsOCSPForIntermediates("no-ocsp-int-path");
|
|
yield ensureOneCRLSkipsOCSPForIntermediates("test-oid-path");
|
|
|
|
clearOCSPCache();
|
|
// disable OneCRL OCSP Skipping (no staleness allowed)
|
|
Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds", 0);
|
|
yield ensureVerifiesAsEV("anyPolicy-int-path");
|
|
// Because the intermediate in this case is missing an OCSP URI, it will not
|
|
// validate as EV, but it should fall back to DV.
|
|
yield ensureVerifiesAsDV("no-ocsp-int-path");
|
|
yield ensureVerifiesAsEV("test-oid-path");
|
|
|
|
clearOCSPCache();
|
|
// enable OneCRL OCSP skipping - allow staleness of up to 30 hours
|
|
Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds",
|
|
108000);
|
|
// set the blocklist-background-update-timer value to the more distant past
|
|
Services.prefs.setIntPref("services.blocklist.onecrl.checked",
|
|
Math.floor(Date.now() / 1000) - 108080);
|
|
Services.prefs.setIntPref(
|
|
"app.update.lastUpdateTime.blocklist-background-update-timer",
|
|
Math.floor(Date.now() / 1000) - 108080);
|
|
yield ensureVerifiesAsEV("anyPolicy-int-path");
|
|
yield ensureVerifiesAsDV("no-ocsp-int-path");
|
|
yield ensureVerifiesAsEV("test-oid-path");
|
|
|
|
clearOCSPCache();
|
|
// test that setting "security.onecrl.via.amo" results in the correct
|
|
// OCSP behavior when services.blocklist.onecrl.checked is in the distant past
|
|
// and blacklist-background-update-timer is recent
|
|
Services.prefs.setBoolPref("security.onecrl.via.amo", false);
|
|
// enable OneCRL OCSP skipping - allow staleness of up to 30 hours
|
|
Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds",
|
|
108000);
|
|
// set the blocklist-background-update-timer value to the recent past
|
|
// (services.blocklist.onecrl.checked defaults to 0)
|
|
Services.prefs.setIntPref(
|
|
"app.update.lastUpdateTime.blocklist-background-update-timer",
|
|
Math.floor(Date.now() / 1000) - 1);
|
|
|
|
yield ensureVerifiesAsEV("anyPolicy-int-path");
|
|
yield ensureVerifiesAsDV("no-ocsp-int-path");
|
|
yield ensureVerifiesAsEV("test-oid-path");
|
|
|
|
clearOCSPCache();
|
|
// test that setting "security.onecrl.via.amo" results in the correct
|
|
// OCSP behavior when services.blocklist.onecrl.checked is recent
|
|
Services.prefs.setBoolPref("security.onecrl.via.amo", false);
|
|
// enable OneCRL OCSP skipping - allow staleness of up to 30 hours
|
|
Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds",
|
|
108000);
|
|
// now set services.blocklist.onecrl.checked to a recent value
|
|
Services.prefs.setIntPref("services.blocklist.onecrl.checked",
|
|
Math.floor(Date.now() / 1000) - 1);
|
|
yield ensureOneCRLSkipsOCSPForIntermediates("anyPolicy-int-path");
|
|
yield ensureOneCRLSkipsOCSPForIntermediates("no-ocsp-int-path");
|
|
yield ensureOneCRLSkipsOCSPForIntermediates("test-oid-path");
|
|
|
|
Services.prefs.clearUserPref("security.onecrl.via.amo");
|
|
Services.prefs.clearUserPref("security.onecrl.maximum_staleness_in_seconds");
|
|
Services.prefs.clearUserPref("services.blocklist.onecrl.checked");
|
|
Services.prefs.clearUserPref(
|
|
"app.update.lastUpdateTime.blocklist-background-update-timer");
|
|
});
|
|
|
|
// Prime the OCSP cache and then ensure that we can validate certificates as EV
|
|
// without hitting the network. There's two cases here: one where we simply
|
|
// validate like normal and then check that the network was never accessed and
|
|
// another where we use flags to mandate that the network not be used.
|
|
add_task(function* ocspCachingTests() {
|
|
clearOCSPCache();
|
|
|
|
yield ensureVerifiesAsEV("anyPolicy-int-path");
|
|
yield ensureVerifiesAsEV("test-oid-path");
|
|
|
|
yield ensureVerifiesAsEVWithNoOCSPRequests("anyPolicy-int-path");
|
|
yield ensureVerifiesAsEVWithNoOCSPRequests("test-oid-path");
|
|
|
|
yield ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("anyPolicy-int-path");
|
|
yield ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("test-oid-path");
|
|
});
|
|
|
|
// Old-but-still-valid OCSP responses are accepted for intermediates but not
|
|
// end-entity certificates (because of OCSP soft-fail this results in DV
|
|
// fallback).
|
|
add_task(function* oldOCSPResponseTests() {
|
|
clearOCSPCache();
|
|
|
|
yield ensureVerifiesAsEVWithOldIntermediateOCSPResponse("anyPolicy-int-path");
|
|
yield ensureVerifiesAsEVWithOldIntermediateOCSPResponse("test-oid-path");
|
|
|
|
clearOCSPCache();
|
|
yield ensureVerifiesAsDVWithOldEndEntityOCSPResponse("anyPolicy-int-path");
|
|
yield ensureVerifiesAsDVWithOldEndEntityOCSPResponse("test-oid-path");
|
|
|
|
clearOCSPCache();
|
|
yield ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse(
|
|
"anyPolicy-int-path");
|
|
yield ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse("test-oid-path");
|
|
});
|