Bug 787133 - (hpkp) testing of internal storage and idl r=keeler.

--HG--
extra : rebase_source : c4f83f38a3b8f293a1ca61f2f0a6f90df6ff7840
This commit is contained in:
Camilo Viecco 2014-09-12 14:59:37 -07:00
parent 025a95d373
commit 2cb42272c4
18 changed files with 413 additions and 4 deletions

View File

@ -0,0 +1,204 @@
/* 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/. */
// The purpose of this test is to create a site security service state file
// and see that the site security service reads it properly.
function writeLine(aLine, aOutputStream) {
aOutputStream.write(aLine, aLine.length);
}
let gSSService = null;
let profileDir = do_get_profile();
let certdb;
function certFromFile(filename) {
let der = readFile(do_get_file("test_pinning_dynamic/" + filename, false));
return certdb.constructX509(der, der.length);
}
function loadCert(cert_name, trust_string) {
let cert_filename = cert_name + ".der";
addCertFromFile(certdb, "test_pinning_dynamic/" + cert_filename, trust_string);
return certFromFile(cert_filename);
}
function checkOK(cert) {
return checkCertErrorGeneric(certdb, cert, 0, certificateUsageSSLServer);
}
function checkFail(cert) {
return checkCertErrorGeneric(certdb, cert, MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE,
certificateUsageSSLServer);
}
const NON_ISSUED_KEY_HASH = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
const PINNING_ROOT_KEY_HASH ="kXoHD1ZGyMuowchJwy+xgHlzh0kJFoI9KX0o0IrzTps=";
function run_test() {
Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
let stateFile = profileDir.clone();
stateFile.append(SSS_STATE_FILE_NAME);
// Assuming we're working with a clean slate, the file shouldn't exist
// until we create it.
do_check_false(stateFile.exists());
let outputStream = FileUtils.openFileOutputStream(stateFile);
let now = (new Date()).getTime();
writeLine("a.pinning2.example.com:HPKP\t0\t0\t" + (now + 100000) + ",1,0,kXoHD1ZGyMuowchJwy+xgHlzh0kJFoI9KX0o0IrzTps=\n", outputStream);
writeLine("b.pinning2.example.com:HPKP\t0\t0\t" + (now + 100000) + ",1,1,kXoHD1ZGyMuowchJwy+xgHlzh0kJFoI9KX0o0IrzTps=\n", outputStream);
outputStream.close();
Services.obs.addObserver(checkStateRead, "data-storage-ready", false);
do_test_pending();
gSSService = Cc["@mozilla.org/ssservice;1"]
.getService(Ci.nsISiteSecurityService);
do_check_true(gSSService != null);
}
function checkStateRead(aSubject, aTopic, aData) {
do_check_eq(aData, SSS_STATE_FILE_NAME);
do_check_neq(gSSService, null);
// Initializing the certificate DB will cause NSS-initialization, which in
// turn initializes the site security service. Since we're in part testing
// that the site security service correctly reads its state file, we have to
// make sure it doesn't start up before we've populated the file
certdb = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
loadCert("pinningroot", "CTu,CTu,CTu");
loadCert("badca", "CTu,CTu,CTu");
// the written entry is for a.pinning2.example.com without subdomains
// and b.pinning2.example.com with subdomains
checkFail(certFromFile('cn-a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-a.pinning2.example.com-pinningroot.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-www.example.com-alt-a.pinning2.example-badca.der'));
checkOK(certFromFile('cn-www.example.com-alt-a.pinning2.example-pinningroot.der'));
checkFail(certFromFile('cn-b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-b.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-x.b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.b.pinning2.example.com-pinningroot.der'));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"a.pinning2.example.com", 0));
do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"x.a.pinning2.example.com", 0));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"b.pinning2.example.com", 0));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"x.b.pinning2.example.com", 0));
// add withSubdomains to a.pinning2.example.com
gSSService.setKeyPins("a.pinning2.example.com", true, 1000, 2,
[NON_ISSUED_KEY_HASH, PINNING_ROOT_KEY_HASH]);
checkFail(certFromFile('cn-a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-a.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-x.a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-www.example.com-alt-a.pinning2.example-badca.der'));
checkOK(certFromFile('cn-www.example.com-alt-a.pinning2.example-pinningroot.der'));
checkFail(certFromFile('cn-b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-b.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-x.b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.b.pinning2.example.com-pinningroot.der'));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"a.pinning2.example.com", 0));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"x.a.pinning2.example.com", 0));
// Now setpins without subdomains
gSSService.setKeyPins("a.pinning2.example.com", false, 1000, 2,
[NON_ISSUED_KEY_HASH, PINNING_ROOT_KEY_HASH]);
checkFail(certFromFile('cn-a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-a.pinning2.example.com-pinningroot.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-www.example.com-alt-a.pinning2.example-badca.der'));
checkOK(certFromFile('cn-www.example.com-alt-a.pinning2.example-pinningroot.der'));
checkFail(certFromFile('cn-b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-b.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-x.b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.b.pinning2.example.com-pinningroot.der'));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"a.pinning2.example.com", 0));
do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"x.a.pinning2.example.com", 0));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"b.pinning2.example.com", 0));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"x.b.pinning2.example.com", 0));
// failure to insert new pin entry leaves previous pin behavior
try {
gSSService.setKeyPins("a.pinning2.example.com", true, 1000, 1,
["not a hash"]);
do_check_true(false); // this shouldn't run
} catch(e) {
}
checkFail(certFromFile('cn-a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-a.pinning2.example.com-pinningroot.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-www.example.com-alt-a.pinning2.example-badca.der'));
checkOK(certFromFile('cn-www.example.com-alt-a.pinning2.example-pinningroot.der'));
checkFail(certFromFile('cn-b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-b.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-x.b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.b.pinning2.example.com-pinningroot.der'));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"a.pinning2.example.com", 0));
do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"x.a.pinning2.example.com", 0));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"b.pinning2.example.com", 0));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"x.b.pinning2.example.com", 0));
// Incorrect size results in failure
try {
gSSService.setKeyPins("a.pinning2.example.com", true, 1000, 2,
["not a hash"]);
do_check_true(false); // this shouldn't run
} catch(e) {
}
// Ensure built-in pins work as expected
do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"nonexistent.example.com", 0));
do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
"include-subdomains.pinning.example.com", 0));
gSSService.setKeyPins("a.pinning2.example.com", false, 0, 1,
[NON_ISSUED_KEY_HASH]);
do_timeout(1250, checkExpiredState);
}
function checkExpiredState() {
checkOK(certFromFile('cn-a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-a.pinning2.example.com-pinningroot.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.a.pinning2.example.com-pinningroot.der'));
checkOK(certFromFile('cn-www.example.com-alt-a.pinning2.example-badca.der'));
checkOK(certFromFile('cn-www.example.com-alt-a.pinning2.example-pinningroot.der'));
checkFail(certFromFile('cn-b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-b.pinning2.example.com-pinningroot.der'));
checkFail(certFromFile('cn-x.b.pinning2.example.com-badca.der'));
checkOK(certFromFile('cn-x.b.pinning2.example.com-pinningroot.der'));
do_test_finished();
}

View File

@ -0,0 +1,99 @@
#!/usr/bin/python
# 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/.
import tempfile, os, sys
import random
import pexpect
import subprocess
import shutil
libpath = os.path.abspath('../psm_common_py')
sys.path.append(libpath)
import CertUtils
srcdir = os.getcwd()
db = tempfile.mkdtemp()
CA_basic_constraints = "basicConstraints = critical, CA:TRUE\n"
EE_basic_constraints = "basicConstraints = CA:FALSE\n"
CA_full_ku = ("keyUsage = keyCertSign, cRLSign\n")
authority_key_ident = "authorityKeyIdentifier = keyid, issuer\n"
subject_key_ident = "subjectKeyIdentifier = hash\n"
def generate_family(db_dir, dst_dir, ca_key, ca_cert, base_name):
key_type = 'rsa'
ee_ext_base = EE_basic_constraints + authority_key_ident;
CertUtils.generate_cert_generic(db,
srcdir,
10,
key_type,
'cn-a.pinning2.example.com-'+ base_name,
ee_ext_base,
ca_key,
ca_cert,
'/CN=a.pinning2.example.com')
CertUtils.generate_cert_generic(db,
srcdir,
11,
key_type,
'cn-x.a.pinning2.example.com-'+ base_name,
ee_ext_base,
ca_key,
ca_cert,
'/CN=x.a.pinning2.example.com')
alt_name_ext = 'subjectAltName =DNS:a.pinning2.example.com'
CertUtils.generate_cert_generic(db,
srcdir,
12,
key_type,
'cn-www.example.com-alt-a.pinning2.example-'+ base_name,
ee_ext_base + alt_name_ext,
ca_key,
ca_cert,
'/CN=www.example.com')
CertUtils.generate_cert_generic(db,
srcdir,
13,
key_type,
'cn-b.pinning2.example.com-'+ base_name,
ee_ext_base,
ca_key,
ca_cert,
'/CN=b.pinning2.example.com')
CertUtils.generate_cert_generic(db,
srcdir,
14,
key_type,
'cn-x.b.pinning2.example.com-'+ base_name,
ee_ext_base,
ca_key,
ca_cert,
'/CN=x.b.pinning2.example.com')
def generate_certs():
key_type = 'rsa'
ca_ext = CA_basic_constraints + CA_full_ku + subject_key_ident
ee_ext_text = (EE_basic_constraints + authority_key_ident)
[ca_key, ca_cert] = CertUtils.generate_cert_generic(db,
srcdir,
1,
key_type,
'badca',
ca_ext)
generate_family(db, srcdir, ca_key, ca_cert, 'badca')
ca_cert = 'pinningroot.der'
ca_key = 'pinningroot.key'
generate_family(db, srcdir, ca_key, ca_cert, 'pinningroot')
generate_certs()

View File

@ -0,0 +1,59 @@
#!/usr/bin/python
# after runing this file you MUST modify test_pinning_dynamic to change the
# fingerprint of the pinningroot
import tempfile, os, sys
import random
import pexpect
import subprocess
import shutil
libpath = os.path.abspath('../psm_common_py')
sys.path.append(libpath)
import CertUtils
dest_dir = os.getcwd()
db = tempfile.mkdtemp()
CA_basic_constraints = "basicConstraints = critical, CA:TRUE\n"
CA_min_ku = "keyUsage = critical, digitalSignature, keyCertSign, cRLSign\n"
subject_key_ident = "subjectKeyIdentifier = hash\n"
def generate_root_cert(db_dir, dest_dir, prefix, ext_text):
serial_num = 12223299546
name = prefix
key_name = dest_dir + "/" + name + ".key"
os.system ("openssl genpkey -algorithm RSA -out " + key_name +
" -pkeyopt rsa_keygen_bits:2048")
csr_name = dest_dir + "/" + name + ".csr"
os.system ("openssl req -new -key " + key_name + " -days 3650" +
" -extensions v3_ca -batch -out " + csr_name +
" -utf8 -subj '/C=US/ST=CA/L=Mountain View" +
"/O=Mozilla - Pinning test CA/OU=Security Engineering" +
"/CN=XPCShell Pinning Testing (untrustworthy) CA'")
extensions_filename = db_dir + "/openssl-exts"
f = open(extensions_filename, 'w')
f.write(ext_text)
f.close()
cert_name = dest_dir + "/" + name + ".der"
signer_key_filename = key_name
os.system ("openssl x509 -req -sha256 -days 3650 -in " + csr_name +
" -signkey " + signer_key_filename +
" -set_serial " + str(serial_num) +
" -extfile " + extensions_filename +
" -outform DER -out " + cert_name)
return key_name, cert_name
prefix = "pinningroot"
[ca_key, ca_cert] = generate_root_cert(db, dest_dir, prefix,
CA_basic_constraints +
CA_min_ku + subject_key_ident)
CertUtils.generate_pkcs12(db, dest_dir, ca_cert, ca_key, prefix)
print ("You now MUST modify test_pinning_dynamic to ensure the xpchell debug " +
"certificate there matches this newly generated one\n")

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCqzcG6+9uf056Z
dyemAZbNDiKx4TKIe8YcnNHSaf7sKhtFTSx7DffjElclg8S8cZnJYpaiVjtwm1fe
YoxRv6nr4bJvnQZzESoBJWle+8wkKJiEV8keQ+MWyGHL7hi3ldANVz7IKkdMGi5j
PPI48CEQvYqYKQ/ap2fE9c4Iui7ghVQsM69zYOcc/OUx71XCkPm4O6O/iyHyOvTW
3UdPuevPJedqAybxPr0mLOUUxiAid7hlwMwYm/ZkwZUYK2uZHSnLES6bX9+e4SJm
GpCFuo0eR3ww3ymeUNUX2hIQ5U6QcweBuGYikcMSUyy2YQvgvIYFgfYp4HRm0K63
p+aQzhUnAgMBAAECggEAYnCBhg2fz2Z8ksAstq1Vx0qtTk2W/Tgwvs7W0ulKBbxi
yTcoOqebRWCNn05pNZ5XXsyWOmtuiIDPTJkJfBf1i8AhQdiDHhA3YD+6kgaW6gQH
msapX+sKV1gXnRvVXQFXzkiJI33SZ5bzQzid9ZjXxbknxnhr7b3rvYVZQIhlefoM
nZbUMS5iyZSeJ13Zj/GxGagK3OwoV4tSG1BbE6at+uNgC0MJuAwBiGvWdCTPPiWc
j+IgvE8V/7ZyZjVZMRj/Bwu6M9L6X3H5N6P7yZ8kyvuZBm2fzi/ZsDetjfylRGXd
H7qqrSJmU/uJC89JBtBuplppnfpx7z8VWzfzVkBmeQKBgQDXQPgad4+WSXAmHfCW
UZzwM5BImL/TffXtsZaA2/KL1FJ9qH+oIyM/8SoIipCDO5iw3hVyJoDcvgAbQLfw
5B0HhWnLmPvP5p5K6e/hfs6IeBM4TFqiixwNMbsAMaDcGeXm6ifP7QrRkNrFatd/
ZnX+noSI2avoKP5Ta01fu4CLewKBgQDLIsXa437RZHzlKzcm9wqSm693ctiwyyzo
ku31LzJxzR8ZmUKquVQv8gEkC2rejQHA47nZ9a+JRkP+O4TAwFvQlk2fE4RQqSNC
j8C9IJbqKv8pd12MLg4PjxYsGOdJPawLKDBAtAFIxcwPbv7z87AYUwJHsA4NGBxM
D1o0Uf5nRQKBgEo/oNUQVLkUT9j2Dtzq/eVlA+nmtpRvKHt6/F0qgTl2XZX2n0IS
DMCroUIc+cgqsHqhwGgFnAGIkGTWKByxssm7XUjbVlDaTdOtag8wPHGaaZbLz5+t
uFlcxBJHDCOf00uZjW4NZ+PtwWjDd938K8tORo0l+EQ2j7Oama/h728tAoGAMhLJ
SPLJQR6PNwKXHtDI12jvMPereLEsycvmQok22xygLPQ9nc3/da4nH9Z8qo2BwCWi
m5rXgXRDsHjFeaoxN9LFIZBzDlsTKQY8kaJl8SVWLseQ1MxJhQZVhdtBsCk2rq1i
b6CvrFutuKQ4zrPkVaqopjk8zXeDCjDCoKERoC0CgYAfZrMB70Za9/ha8S00HhHW
SZ7pvFFZuWrpGVNIK2DmhUFfl+T0w+qxyVNIJ/oHeMq7jy0HDbwYBTbf6+6Q6Kur
hKl2FHYU8kg+Qt5DT9yx0FLM4xQ8E4IlR7bF/3ZQl+qZGhuxfX8LqSHY7u3IYqpX
qt81/FWJVd3bd1g61cbBog==
-----END PRIVATE KEY-----

View File

@ -5,10 +5,13 @@
// The purpose of this test is to see that the site security service properly
// writes its state file.
const EXPECTED_ENTRIES = 5;
const EXPECTED_COLUMNS = 3;
const EXPECTED_ENTRIES = 6;
const EXPECTED_HSTS_COLUMNS = 3;
const EXPECTED_HPKP_COLUMNS = 4;
let gProfileDir = null;
const NON_ISSUED_KEY_HASH = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
// For reference, the format of the state file is a list of:
// <domain name> <expiration time in milliseconds>,<sts status>,<includeSubdomains>
// separated by newlines ('\n')
@ -30,7 +33,11 @@ function checkStateWritten(aSubject, aTopic, aData) {
let score = parts[1];
let lastAccessed = parts[2];
let entry = parts[3].split(',');
do_check_eq(entry.length, EXPECTED_COLUMNS);
let expectedColumns = EXPECTED_HSTS_COLUMNS;
if (host.indexOf("HPKP") != -1) {
expectedColumns = EXPECTED_HPKP_COLUMNS;
}
do_check_eq(entry.length, expectedColumns);
sites[host] = entry;
}
@ -72,6 +79,13 @@ function checkStateWritten(aSubject, aTopic, aData) {
if (sites["d.example.com:HSTS"][2] != 0) {
return;
}
if (sites["dynamic-pin.example.com:HPKP"][1] != 1) {
return;
}
if (sites["dynamic-pin.example.com:HPKP"][2] != 1) {
return;
}
do_check_eq(sites["dynamic-pin.example.com:HPKP"][3], NON_ISSUED_KEY_HASH);
do_test_finished();
}
@ -81,6 +95,9 @@ function run_test() {
gProfileDir = do_get_profile();
let SSService = Cc["@mozilla.org/ssservice;1"]
.getService(Ci.nsISiteSecurityService);
// Put an HPKP entry
SSService.setKeyPins("dynamic-pin.example.com", true, 1000, 1,
[NON_ISSUED_KEY_HASH]);
let uris = [ Services.io.newURI("http://bugzilla.mozilla.org", null, null),
Services.io.newURI("http://a.example.com", null, null),
@ -98,7 +115,6 @@ function run_test() {
uris[uriIndex], maxAge + includeSubdomains, 0);
}
do_test_pending();
Services.obs.addObserver(checkStateWritten, "data-storage-written", false);
}

View File

@ -17,6 +17,7 @@ support-files =
test_ocsp_url/**
test_ocsp_fetch_method/**
test_keysize/**
test_pinning_dynamic/**
[test_datasignatureverifier.js]
[test_hash_algorithms.js]
@ -37,6 +38,8 @@ skip-if = buildapp == "b2g" && processor = "arm"
[test_sss_readstate_huge.js]
[test_sss_savestate.js]
[test_pinning_dynamic.js]
[test_certificate_usages.js]
[test_ocsp_stapling.js]
run-sequentially = hardcoded ports