1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/ssl/tests/unit/test_pinning.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,182 @@ 1.4 +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- 1.5 +// This Source Code Form is subject to the terms of the Mozilla Public 1.6 +// License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.8 +// 1.9 +// For all cases, the acceptable pinset includes only certificates pinned to 1.10 +// Test End Entity Cert (signed by issuer testCA). Other certificates 1.11 +// are issued by otherCA, which is never in the pinset but is a user-specified 1.12 +// trust anchor. This test covers multiple cases: 1.13 +// 1.14 +// Pinned domain include-subdomains.pinning.example.com includes subdomains 1.15 +// - PASS: include-subdomains.pinning.example.com serves a correct cert 1.16 +// - PASS: good.include-subdomains.pinning.example.com serves a correct cert 1.17 +// - FAIL (strict): bad.include-subdomains.pinning.example.com serves a cert 1.18 +// not in the pinset 1.19 +// - PASS (mitm): bad.include-subdomains.pinning.example.com serves a cert not 1.20 +// in the pinset, but issued by a user-specified trust domain 1.21 +// 1.22 +// Pinned domain exclude-subdomains.pinning.example.com excludes subdomains 1.23 +// - PASS: exclude-subdomains.pinning.example.com serves a correct cert 1.24 +// - FAIL: exclude-subdomains.pinning.example.com services an incorrect cert 1.25 +// (TODO: test using verifyCertnow) 1.26 +// - PASS: sub.exclude-subdomains.pinning.example.com serves an incorrect cert 1.27 + 1.28 +"use strict"; 1.29 + 1.30 +do_get_profile(); // must be called before getting nsIX509CertDB 1.31 +const certdb = Cc["@mozilla.org/security/x509certdb;1"] 1.32 + .getService(Ci.nsIX509CertDB); 1.33 + 1.34 +function test_strict() { 1.35 + // In strict mode, we always evaluate pinning data, regardless of whether the 1.36 + // issuer is a built-in trust anchor. We only enforce pins that are not in 1.37 + // test mode. 1.38 + add_test(function() { 1.39 + Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2); 1.40 + run_next_test(); 1.41 + }); 1.42 + 1.43 + // If a host should be pinned but other errors (particularly overridable 1.44 + // errors) like 'unknown issuer' are encountered, the pinning error takes 1.45 + // precedence. This prevents overrides for such hosts. 1.46 + add_connection_test("unknownissuer.include-subdomains.pinning.example.com", 1.47 + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); 1.48 + 1.49 + // Issued by otherCA, which is not in the pinset for pinning.example.com. 1.50 + add_connection_test("bad.include-subdomains.pinning.example.com", 1.51 + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); 1.52 + 1.53 + // These domains serve certs that match the pinset. 1.54 + add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); 1.55 + add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); 1.56 + add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); 1.57 + 1.58 + // This domain serves a cert that doesn't match the pinset, but subdomains 1.59 + // are excluded. 1.60 + add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); 1.61 + 1.62 + // This domain's pinset is exactly the same as 1.63 + // include-subdomains.pinning.example.com, serves the same cert as 1.64 + // bad.include-subdomains.pinning.example.com, but it should pass because 1.65 + // it's in test_mode. 1.66 + add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); 1.67 +} 1.68 + 1.69 +function test_mitm() { 1.70 + // In MITM mode, we allow pinning to pass if the chain resolves to any 1.71 + // user-specified trust anchor, even if it is not in the pinset. 1.72 + add_test(function() { 1.73 + Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1); 1.74 + run_next_test(); 1.75 + }); 1.76 + 1.77 + add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); 1.78 + add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); 1.79 + 1.80 + add_connection_test("unknownissuer.include-subdomains.pinning.example.com", 1.81 + getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); 1.82 + 1.83 + // In this case, even though otherCA is not in the pinset, it is a 1.84 + // user-specified trust anchor and the pinning check succeeds. 1.85 + add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK); 1.86 + 1.87 + add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); 1.88 + add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); 1.89 + add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); 1.90 +}; 1.91 + 1.92 +function test_disabled() { 1.93 + // Disable pinning. 1.94 + add_test(function() { 1.95 + Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0); 1.96 + run_next_test(); 1.97 + }); 1.98 + 1.99 + add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); 1.100 + add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); 1.101 + add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK); 1.102 + add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); 1.103 + add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); 1.104 + add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); 1.105 + 1.106 + add_connection_test("unknownissuer.include-subdomains.pinning.example.com", 1.107 + getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); 1.108 +} 1.109 + 1.110 +function test_enforce_test_mode() { 1.111 + // In enforce test mode, we always enforce all pins, even test pins. 1.112 + add_test(function() { 1.113 + Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3); 1.114 + run_next_test(); 1.115 + }); 1.116 + 1.117 + add_connection_test("unknownissuer.include-subdomains.pinning.example.com", 1.118 + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); 1.119 + 1.120 + // Issued by otherCA, which is not in the pinset for pinning.example.com. 1.121 + add_connection_test("bad.include-subdomains.pinning.example.com", 1.122 + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); 1.123 + 1.124 + // These domains serve certs that match the pinset. 1.125 + add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); 1.126 + add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); 1.127 + add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); 1.128 + 1.129 + // This domain serves a cert that doesn't match the pinset, but subdomains 1.130 + // are excluded. 1.131 + add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); 1.132 + 1.133 + // This domain's pinset is exactly the same as 1.134 + // include-subdomains.pinning.example.com, serves the same cert as 1.135 + // bad.include-subdomains.pinning.example.com, is in test-mode, but we are 1.136 + // enforcing test mode pins. 1.137 + add_connection_test("test-mode.pinning.example.com", 1.138 + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); 1.139 +} 1.140 + 1.141 +function check_pinning_telemetry() { 1.142 + let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); 1.143 + let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS") 1.144 + .snapshot(); 1.145 + let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS") 1.146 + .snapshot(); 1.147 + // Because all of our test domains are pinned to user-specified trust 1.148 + // anchors, effectively only strict mode and enforce test-mode get evaluated 1.149 + do_check_eq(prod_histogram.counts[0], 4); // Failure count 1.150 + do_check_eq(prod_histogram.counts[1], 4); // Success count 1.151 + do_check_eq(test_histogram.counts[0], 2); // Failure count 1.152 + do_check_eq(test_histogram.counts[1], 0); // Success count 1.153 + 1.154 + let moz_prod_histogram = service.getHistogramById("CERT_PINNING_MOZ_RESULTS") 1.155 + .snapshot(); 1.156 + let moz_test_histogram = 1.157 + service.getHistogramById("CERT_PINNING_MOZ_TEST_RESULTS").snapshot(); 1.158 + do_check_eq(moz_prod_histogram.counts[0], 0); // Failure count 1.159 + do_check_eq(moz_prod_histogram.counts[1], 0); // Success count 1.160 + do_check_eq(moz_test_histogram.counts[0], 0); // Failure count 1.161 + do_check_eq(moz_test_histogram.counts[1], 0); // Success count 1.162 + 1.163 + let per_host_histogram = 1.164 + service.getHistogramById("CERT_PINNING_MOZ_RESULTS_BY_HOST").snapshot(); 1.165 + do_check_eq(per_host_histogram.counts[0], 0); // Failure count 1.166 + do_check_eq(per_host_histogram.counts[1], 2); // Success count 1.167 + run_next_test(); 1.168 +} 1.169 + 1.170 +function run_test() { 1.171 + add_tls_server_setup("BadCertServer"); 1.172 + 1.173 + // Add a user-specified trust anchor. 1.174 + addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u"); 1.175 + 1.176 + test_strict(); 1.177 + test_mitm(); 1.178 + test_disabled(); 1.179 + test_enforce_test_mode(); 1.180 + 1.181 + add_test(function () { 1.182 + check_pinning_telemetry(); 1.183 + }); 1.184 + run_next_test(); 1.185 +}