michael@0: // -*- indent-tabs-mode: nil; js-indent-level: 2 -*- michael@0: // This Source Code Form is subject to the terms of the Mozilla Public michael@0: // License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: // file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: // michael@0: // For all cases, the acceptable pinset includes only certificates pinned to michael@0: // Test End Entity Cert (signed by issuer testCA). Other certificates michael@0: // are issued by otherCA, which is never in the pinset but is a user-specified michael@0: // trust anchor. This test covers multiple cases: michael@0: // michael@0: // Pinned domain include-subdomains.pinning.example.com includes subdomains michael@0: // - PASS: include-subdomains.pinning.example.com serves a correct cert michael@0: // - PASS: good.include-subdomains.pinning.example.com serves a correct cert michael@0: // - FAIL (strict): bad.include-subdomains.pinning.example.com serves a cert michael@0: // not in the pinset michael@0: // - PASS (mitm): bad.include-subdomains.pinning.example.com serves a cert not michael@0: // in the pinset, but issued by a user-specified trust domain michael@0: // michael@0: // Pinned domain exclude-subdomains.pinning.example.com excludes subdomains michael@0: // - PASS: exclude-subdomains.pinning.example.com serves a correct cert michael@0: // - FAIL: exclude-subdomains.pinning.example.com services an incorrect cert michael@0: // (TODO: test using verifyCertnow) michael@0: // - PASS: sub.exclude-subdomains.pinning.example.com serves an incorrect cert michael@0: michael@0: "use strict"; michael@0: michael@0: do_get_profile(); // must be called before getting nsIX509CertDB michael@0: const certdb = Cc["@mozilla.org/security/x509certdb;1"] michael@0: .getService(Ci.nsIX509CertDB); michael@0: michael@0: function test_strict() { michael@0: // In strict mode, we always evaluate pinning data, regardless of whether the michael@0: // issuer is a built-in trust anchor. We only enforce pins that are not in michael@0: // test mode. michael@0: add_test(function() { michael@0: Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: // If a host should be pinned but other errors (particularly overridable michael@0: // errors) like 'unknown issuer' are encountered, the pinning error takes michael@0: // precedence. This prevents overrides for such hosts. michael@0: add_connection_test("unknownissuer.include-subdomains.pinning.example.com", michael@0: getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); michael@0: michael@0: // Issued by otherCA, which is not in the pinset for pinning.example.com. michael@0: add_connection_test("bad.include-subdomains.pinning.example.com", michael@0: getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); michael@0: michael@0: // These domains serve certs that match the pinset. michael@0: add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); michael@0: michael@0: // This domain serves a cert that doesn't match the pinset, but subdomains michael@0: // are excluded. michael@0: add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); michael@0: michael@0: // This domain's pinset is exactly the same as michael@0: // include-subdomains.pinning.example.com, serves the same cert as michael@0: // bad.include-subdomains.pinning.example.com, but it should pass because michael@0: // it's in test_mode. michael@0: add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); michael@0: } michael@0: michael@0: function test_mitm() { michael@0: // In MITM mode, we allow pinning to pass if the chain resolves to any michael@0: // user-specified trust anchor, even if it is not in the pinset. michael@0: add_test(function() { michael@0: Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: michael@0: add_connection_test("unknownissuer.include-subdomains.pinning.example.com", michael@0: getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); michael@0: michael@0: // In this case, even though otherCA is not in the pinset, it is a michael@0: // user-specified trust anchor and the pinning check succeeds. michael@0: add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: michael@0: add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); michael@0: }; michael@0: michael@0: function test_disabled() { michael@0: // Disable pinning. michael@0: add_test(function() { michael@0: Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); michael@0: michael@0: add_connection_test("unknownissuer.include-subdomains.pinning.example.com", michael@0: getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); michael@0: } michael@0: michael@0: function test_enforce_test_mode() { michael@0: // In enforce test mode, we always enforce all pins, even test pins. michael@0: add_test(function() { michael@0: Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: add_connection_test("unknownissuer.include-subdomains.pinning.example.com", michael@0: getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); michael@0: michael@0: // Issued by otherCA, which is not in the pinset for pinning.example.com. michael@0: add_connection_test("bad.include-subdomains.pinning.example.com", michael@0: getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); michael@0: michael@0: // These domains serve certs that match the pinset. michael@0: add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); michael@0: add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); michael@0: michael@0: // This domain serves a cert that doesn't match the pinset, but subdomains michael@0: // are excluded. michael@0: add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); michael@0: michael@0: // This domain's pinset is exactly the same as michael@0: // include-subdomains.pinning.example.com, serves the same cert as michael@0: // bad.include-subdomains.pinning.example.com, is in test-mode, but we are michael@0: // enforcing test mode pins. michael@0: add_connection_test("test-mode.pinning.example.com", michael@0: getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); michael@0: } michael@0: michael@0: function check_pinning_telemetry() { michael@0: let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); michael@0: let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS") michael@0: .snapshot(); michael@0: let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS") michael@0: .snapshot(); michael@0: // Because all of our test domains are pinned to user-specified trust michael@0: // anchors, effectively only strict mode and enforce test-mode get evaluated michael@0: do_check_eq(prod_histogram.counts[0], 4); // Failure count michael@0: do_check_eq(prod_histogram.counts[1], 4); // Success count michael@0: do_check_eq(test_histogram.counts[0], 2); // Failure count michael@0: do_check_eq(test_histogram.counts[1], 0); // Success count michael@0: michael@0: let moz_prod_histogram = service.getHistogramById("CERT_PINNING_MOZ_RESULTS") michael@0: .snapshot(); michael@0: let moz_test_histogram = michael@0: service.getHistogramById("CERT_PINNING_MOZ_TEST_RESULTS").snapshot(); michael@0: do_check_eq(moz_prod_histogram.counts[0], 0); // Failure count michael@0: do_check_eq(moz_prod_histogram.counts[1], 0); // Success count michael@0: do_check_eq(moz_test_histogram.counts[0], 0); // Failure count michael@0: do_check_eq(moz_test_histogram.counts[1], 0); // Success count michael@0: michael@0: let per_host_histogram = michael@0: service.getHistogramById("CERT_PINNING_MOZ_RESULTS_BY_HOST").snapshot(); michael@0: do_check_eq(per_host_histogram.counts[0], 0); // Failure count michael@0: do_check_eq(per_host_histogram.counts[1], 2); // Success count michael@0: run_next_test(); michael@0: } michael@0: michael@0: function run_test() { michael@0: add_tls_server_setup("BadCertServer"); michael@0: michael@0: // Add a user-specified trust anchor. michael@0: addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u"); michael@0: michael@0: test_strict(); michael@0: test_mitm(); michael@0: test_disabled(); michael@0: test_enforce_test_mode(); michael@0: michael@0: add_test(function () { michael@0: check_pinning_telemetry(); michael@0: }); michael@0: run_next_test(); michael@0: }