Bug 895601: Add tests to cert verification in psm. r=keeler.

This commit is contained in:
Camilo Viecco 2013-07-30 11:18:40 -07:00
parent dc9c3466c4
commit 6a58b9a90d
23 changed files with 336 additions and 0 deletions

View File

@ -0,0 +1,139 @@
# 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/.
# This file requires openssl 1.0.0 at least
import os
import random
def init_dsa(db_dir):
"""
Initialize dsa parameters
Sets up a set of params to be reused for DSA key generation
Arguments:
db_dir -- location of the temporary params for the certificate
"""
dsa_key_params = db_dir + "/dsa_param.pem"
os.system ("openssl dsaparam -out "+ dsa_key_params + " 2048")
def generate_cert_generic(db_dir, dest_dir, serial_num, key_type, name,
ext_text, signer_key_filename = "",
signer_cert_filename = ""):
"""
Generate an x509 certificate with a sha256 signature
Preconditions:
if dsa keys are to be generated init_dsa must have been called before.
Arguments:
db_dir -- location of the temporary params for the certificate
dest_dir -- location of the x509 cert
serial_num -- serial number for the cert (must be unique for each signer
key)
key_type -- the type of key generated: potential values: 'rsa', 'dsa',
or any of the curves found by 'openssl ecparam -list_curves'
name -- the common name for the cert, will match the prefix of the
output cert
ext_text -- the text for the x509 extensions to be added to the
certificate
signer_key_filename -- the filename of the key from which the cert will
be signed if null the cert will be self signed (think CA
roots).
signer_cert_filename -- the certificate that will sign the certificate
(used to extract signer info) it must be in DER format.
output:
key_name -- the filename of the key file (PEM format)
cert_name -- the filename of the output certificate (DER format)
"""
key_name = db_dir + "/"+ name + ".key"
if key_type == 'rsa':
os.system ("openssl genpkey -algorithm RSA -out " + key_name +
" -pkeyopt rsa_keygen_bits:2048")
elif key_type == 'dsa':
dsa_key_params = db_dir + "/dsa_param.pem"
os.system("openssl gendsa -out " + key_name + " " + dsa_key_params)
else:
#assume is ec
os.system("openssl ecparam -out " + key_name + " -name "+ key_type +
" -genkey");
csr_name = db_dir + "/"+ name + ".csr"
os.system ("openssl req -new -key " + key_name + " -days 3650" +
" -extensions v3_ca -batch -out " + csr_name +
" -utf8 -subj '/CN=" + name + "'")
extensions_filename = db_dir + "/openssl-exts"
f = open(extensions_filename,'w')
f.write(ext_text)
f.close()
cert_name = dest_dir + "/"+ name + ".der"
if not signer_key_filename:
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)
else:
os.system ("openssl x509 -req -sha256 -days 3650 -in " + csr_name +
" -CAkey " + signer_key_filename +
" -CA " + signer_cert_filename + " -CAform DER " +
" -set_serial " + str(serial_num) + " -out " + cert_name +
" -outform DER -extfile " + extensions_filename)
return key_name, cert_name
def generate_int_and_ee(db_dir, dest_dir, ca_key, ca_cert, name, int_ext_text,
ee_ext_text, key_type = 'rsa'):
"""
Generate an intermediate and ee signed by the generated intermediate. The
name of the intermediate files will be the name '.der' or '.key'. The name
of the end entity files with be "ee-"+ name plus the appropiate prefixes.
The serial number will be generated radomly so it is potentially possible
to have problem (but very unlikely).
Arguments:
db_dir -- location of the temporary params for the certificate
dest_dir -- location of the x509 cert
ca_key -- The filename of the key that will be used to sign the
intermediate (PEM FORMAT)
ca_cert -- The filename of the cert that will be used to sign the
intermediate, it MUST be the private key for the ca_key.
The file must be in DER format.
name -- the common name for the intermediate, will match the prefix
of the output intermediate. The ee will have the name
prefixed with "ee-"
int_ext_text -- the text for the x509 extensions to be added to the
intermediate certificate
ee_ext_text -- the text for the x509 extensions to be added to the
end entity certificate
key_type -- the type of key generated: potential values: 'rsa', 'dsa',
or any of the curves found by 'openssl ecparam -list_curves'
output:
int_key -- the filename of the intermeidate key file (PEM format)
int_cert -- the filename of the intermediate certificate (DER format)
ee_key -- the filename of the end entity key file (PEM format)
ee_cert -- the filename of the end entity certficate (DER format)
"""
[int_key, int_cert] = generate_cert_generic(db_dir, dest_dir,
random.randint(100,40000000),
key_type, "int-" + name,
int_ext_text,
ca_key, ca_cert)
[ee_key, ee_cert] = generate_cert_generic(db_dir, dest_dir,
random.randint(100,40000000),
key_type, name,
ee_ext_text, int_key, int_cert)
return int_key, int_cert, ee_key, ee_cert

View File

@ -0,0 +1,89 @@
// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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";
/*
* The purpose of this test is to verify that we correctly detect bad
* signatures on tampered certificates. Eventually, we should also be
* verifying that the error we return is the correct error.
*
* To regenerate the certificates for this test:
*
* cd security/manager/ssl/tests/unit/test_cert_signatures
* ./generate.py
* cd ../../../../../..
* make -C $OBJDIR/security/manager/ssl/tests
*
* Check in the generated files. These steps are not done as part of the build
* because we do not want to add a build-time dependency on the OpenSSL or NSS
* tools or libraries built for the host platform.
*/
do_get_profile(); // must be called before getting nsIX509CertDB
const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB);
const ca_usage = 'SSL CA';
const int_usage = 'Client,Server,Sign,Encrypt,SSL CA,Status Responder';
const ee_usage = 'Client,Server,Sign,Encrypt';
const cert2usage = {
// certs without the "int" prefix are end entity certs.
'int-rsa-valid': int_usage,
'rsa-valid': ee_usage,
'int-p384-valid': int_usage,
'p384-valid': ee_usage,
'int-dsa-valid': int_usage,
'dsa-valid': ee_usage,
'rsa-valid-int-tampered-ee': "",
'p384-valid-int-tampered-ee': "",
'dsa-valid-int-tampered-ee': "",
'int-rsa-tampered': "",
'rsa-tampered-int-valid-ee': "",
'int-p384-tampered': "",
'p384-tampered-int-valid-ee': "",
'int-dsa-tampered': "",
'dsa-tampered-int-valid-ee': "",
};
function load_ca(ca_name) {
let ca_filename = ca_name + ".der";
addCertFromFile(certdb, "test_cert_signatures/" + ca_filename, 'CTu,CTu,CTu');
do_print("ca_name=" + ca_name);
let cert = certdb.findCertByNickname(null, ca_name);
let verified = {};
let usages = {};
cert.getUsagesString(true, verified, usages);
do_check_eq(ca_usage, usages.value);
}
function run_test() {
// Load the ca into mem
load_ca("ca-rsa");
load_ca("ca-p384");
load_ca("ca-dsa");
// Load certs first
for (let cert_name in cert2usage) {
let cert_filename = cert_name + ".der";
addCertFromFile(certdb, "test_cert_signatures/" + cert_filename, ',,');
}
// Now do the checks
for (let cert_name in cert2usage) {
do_print("cert_name=" + cert_name);
let cert = certdb.findCertByNickname(null, cert_name);
let verified = {};
let usages = {};
cert.getUsagesString(true, verified, usages);
do_check_eq(cert2usage[cert_name], usages.value);
}
}

View File

@ -0,0 +1,104 @@
#!/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
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"
CA_limited_basic_constraints = "basicConstraints=critical,CA:TRUE, pathlen:0\n"
EE_basic_constraints = "basicConstraints=CA:FALSE\n"
CA_min_ku = "keyUsage=critical, keyCertSign\n"
CA_bad_ku = ("keyUsage=digitalSignature, nonRepudiation, keyEncipherment," +
" dataEncipherment, keyAgreement, cRLSign\n")
EE_full_ku = ("keyUsage=digitalSignature, nonRepudiation, keyEncipherment," +
" dataEncipherment, keyAgreement, keyCertSign, cRLSign\n")
Server_eku= "extendedKeyUsage=critical,serverAuth,clientAuth\n"
pk_name = {'rsa': 'rsa', 'dsa': 'dsa', 'p384': 'secp384r1'}
def tamper_cert(cert_name):
f = open(cert_name, 'r+b')
f.seek(-3, 2) # third byte from the end to ensure we only touch the
# signature value. The location for the perturbation ensures that we are
# modifying just the tbsCertificate without the need of parsing the
# certificate. Also this guarantees that if a failure occurs it is because
# of an invalid signature and not another field that might have become
# invalid.
b = bytearray(f.read(1))
for i in range(len(b)):
b[i] ^= 0x77
f.seek(-1, 1)
f.write(b)
f.close()
return 1
def generate_certs():
CertUtils.init_dsa(db)
ee_ext_text = EE_basic_constraints + EE_full_ku
for name, key_type in pk_name.iteritems():
ca_name = "ca-" + name
[ca_key, ca_cert] = CertUtils.generate_cert_generic(db,
srcdir,
random.randint(100,4000000),
key_type,
ca_name,
CA_basic_constraints + CA_min_ku)
[valid_int_key, valid_int_cert, ee_key, ee_cert] = (
CertUtils.generate_int_and_ee(db,
srcdir,
ca_key,
ca_cert,
name + "-valid",
CA_basic_constraints,
ee_ext_text,
key_type) )
[int_key, int_cert] = CertUtils.generate_cert_generic(db,
srcdir,
random.randint(100,4000000),
key_type,
"int-" + name + "-tampered",
ee_ext_text,
ca_key,
ca_cert)
[ee_key, ee_cert] = CertUtils.generate_cert_generic(db,
srcdir,
random.randint(100,4000000),
key_type,
name + "-tampered-int-valid-ee",
ee_ext_text,
int_key,
int_cert)
#only tamper after ee has been generated
tamper_cert(int_cert);
[ee_key, ee_cert] = CertUtils.generate_cert_generic(db,
srcdir,
random.randint(100,4000000),
key_type,
name + "-valid-int-tampered-ee",
ee_ext_text,
valid_int_key,
valid_int_cert)
tamper_cert(ee_cert);
generate_certs()

View File

@ -5,6 +5,7 @@ support-files =
test_certificate_usages/**
test_signed_apps/**
tlsserver/**
test_cert_signatures/**
[test_certificate_usages.js]
# Bug 676972: test fails consistently on Android
@ -30,3 +31,6 @@ skip-if = os == "android"
# Bug 676972: test fails consistently on Android
fail-if = os == "android"
[test_sts_ipv4_ipv6.js]
[test_cert_signatures.js]
# Bug 676972: test fails consistently on Android
fail-if = os == "android"