bug 993186 - improve test_cert_eku generator r=cviecco

This commit is contained in:
David Keeler 2014-04-09 11:04:00 -07:00
parent 97f8165ef6
commit 13f4429374
2 changed files with 3345 additions and 3751 deletions

File diff suppressed because it is too large Load Diff

View File

@ -20,11 +20,11 @@ CA_basic_constraints = "basicConstraints = critical, CA:TRUE\n"
EE_basic_constraints = "basicConstraints = CA:FALSE\n"
CA_full_ku = "keyUsage = keyCertSign, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, cRLSign\n"
EE_full_ku ="keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement\n"
EE_full_ku = "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement\n"
key_type = 'rsa'
#codesigning differs significantly between mozilla::pkix and
# codesigning differs significantly between mozilla::pkix and
# classic NSS that actual testing on it is not very useful
eku_values = { 'SA': "serverAuth",
'CA': "clientAuth",
@ -33,17 +33,16 @@ eku_values = { 'SA': "serverAuth",
'TS': "timeStamping",
'NS': "nsSGC", # Netscape Server Gated Crypto.
'OS': "1.3.6.1.5.5.7.3.9"
};
}
cert_usages = [
"certificateUsageSSLClient",
"certificateUsageSSLServer",
"certificateUsageSSLCA",
"certificateUsageEmailSigner",
"certificateUsageEmailRecipient",
#"certificateUsageObjectSigner",
"certificateUsageStatusResponder",
];
cert_usages = [ "certificateUsageSSLClient",
"certificateUsageSSLServer",
"certificateUsageSSLCA",
"certificateUsageEmailSigner",
"certificateUsageEmailRecipient",
#"certificateUsageObjectSigner",
"certificateUsageStatusResponder"
]
js_file_header = """//// AUTOGENERATED FILE, DO NOT EDIT
// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
@ -68,301 +67,167 @@ function load_cert(cert_name, trust_string) {
return cert_from_file(cert_filename);
}
function check_cert_err_generic(cert, expected_error, usage) {
do_print("cert cn=" + cert.commonName);
do_print("cert issuer cn=" + cert.issuerCommonName);
let hasEVPolicy = {};
let verifiedChain = {};
let error = certdb.verifyCertNow(cert, usage,
NO_FLAGS, verifiedChain, hasEVPolicy);
// expected error == 1 is a special marker for any error is OK
// use to simplify tests for the classic NSS code
if (expected_error != -1 ) {
do_check_eq(error, expected_error);
} else {
do_check_neq (error, 0);
}
}
function check_ok_ca(cert) {
return check_cert_err_generic(cert, 0, certificateUsageSSLCA);
}
function check_ok_ee(cert, usage) {
return check_cert_err_generic(cert, 0, usage);
}
function run_test_in_mode(useMozillaPKIX) {
Services.prefs.setBoolPref("security.use_mozillapkix_verification",
useMozillaPKIX);
function run_test() {
load_cert("ca", "CT,CT,CT");
Services.prefs.setBoolPref("security.use_mozillapkix_verification", true);
"""
js_file_footer = """}
function run_test() {
load_cert("ca", "CT,CT,CT");
run_test_in_mode(true);
run_test_in_mode(false);
}
"""
def get_classic_error(name_string, cert_usage):
if ("NONE" in name_string):
return 0;
if (cert_usage == "certificateUsageSSLServer"):
if ("SA" in name_string) or ("NS" in name_string):
return 0;
else:
return -1;
return get_mozpkix_ee_error(name_string, cert_usage);
def gen_int_js_output(int_string):
expectedResult = "SEC_ERROR_INADEQUATE_CERT_TYPE"
# For a certificate to verify successfully as a SSL CA, it must either
# have no EKU or have the Server Auth or Netscape Server Gated Crypto
# usage (the second of which is deprecated but currently supported for
# compatibility purposes).
# TODO(bug 991209) Additionally, if it has the OCSP Signing usage, it is
# considered not valid as a SSL CA.
if (("NONE" in int_string or "SA" in int_string or "NS" in int_string) and
"OS" not in int_string):
expectedResult = "0"
return (" checkCertErrorGeneric(certdb, load_cert('" + int_string +
"', ',,'), " + expectedResult + ", certificateUsageSSLCA);\n")
def get_mozpkix_ee_error(name_string, cert_usage):
if ("NONE" in name_string):
return 0;
if (cert_usage == "certificateUsageSSLClient"):
if ("CA" in name_string):
return 0;
else:
return -1;
elif (cert_usage == "certificateUsageSSLServer"):
if ("SA" in name_string):
return 0;
else:
return -1;
elif (cert_usage == "certificateUsageEmailSigner"):
if ("EP" in name_string):
return 0;
else:
return -1;
elif (cert_usage == "certificateUsageEmailRecipient"):
if ("EP" in name_string):
return 0;
else:
return -1;
elif (cert_usage == "certificateUsageStatusResponder"):
if ("OS" in name_string):
return 0;
else:
return -1;
else:
return -1
raise Exception("Impossible state")
def single_test_output(ee_name, cert_usage, error):
return (" checkCertErrorGeneric(certdb, cert_from_file('" + ee_name +
".der'), " + error + ", " + cert_usage + ");\n")
def generate_simple_overlay_output(int_string, ee_name, int_error, classic_ee_error, cert_usage):
if (int_error == 0):
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), SEC_ERROR_INADEQUATE_CERT_TYPE, " +
cert_usage + ");\n");
if (classic_ee_error != 0) :
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), SEC_ERROR_INADEQUATE_CERT_TYPE, " +
cert_usage + ");\n");
def usage_to_abbreviation(usage):
if usage is "certificateUsageStatusResponder":
return "OS"
if usage is "certificateUsageSSLServer":
return "SA"
if usage is "certificateUsageSSLClient":
return "CA"
if (usage is "certificateUsageEmailSigner" or
usage is "certificateUsageEmailRecipient"):
return "EP"
raise Exception("unsupported usage: " + usage)
##classic mode considers some values of EKU equivalent to SSL server
if (((cert_usage == "certificateUsageSSLServer") and ("CA" in int_string)) or
((cert_usage == "certificateUsageSSLClient") and ("NS" in int_string)) or
((cert_usage == "certificateUsageSSLClient") and ("SA" in int_string)) or
((cert_usage == "certificateUsageSSLClient") and ("int-EKU-TS" == int_string)) or
((cert_usage == "certificateUsageSSLServer") and ("int-EKU-TS" == int_string)) or
((cert_usage == "certificateUsageEmailSigner") and ("int-EKU-TS" == int_string)) or
((cert_usage == "certificateUsageEmailRecipient") and ("int-EKU-TS" == int_string))):
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), useMozillaPKIX ? SEC_ERROR_INADEQUATE_CERT_TYPE : 0, "
+ cert_usage +");\n")
# In general, for a certificate to be compatible with a usage, it must either
# have no EKU at all or that usage must be present in its EKU extension.
def has_compatible_eku(name_string, usage_abbreviation):
return ("NONE" in name_string or usage_abbreviation in name_string)
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), useMozillaPKIX ? " +
"SEC_ERROR_INADEQUATE_CERT_TYPE : -1, " + cert_usage + ");\n");
def gen_ee_js_output(int_string, ee_string, cert_usage, ee_name):
if cert_usage is "certificateUsageSSLCA":
# since none of these are CA certs (BC) these should all fail
return single_test_output(ee_name, cert_usage,
"SEC_ERROR_INADEQUATE_KEY_USAGE")
def gen_os_js_output(int_string, ee_string, cert_usage, ee_name):
int_has_OS_eku = False
if ( "OS" in int_string):
int_has_OS_eku = True
ee_has_os_eku = False
if ( "OS" in ee_string):
ee_has_os_eku = True
int_has_SA_eku = False
usage_abbreviation = usage_to_abbreviation(cert_usage)
if cert_usage is "certificateUsageStatusResponder":
# For the Status Responder usage, the OSCP Signing usage must be
# present in the end-entity's EKU extension (i.e. if the extension
# is not present, the cert is not compatible with this usage).
if "OS" not in ee_string:
return single_test_output(ee_name, cert_usage,
"SEC_ERROR_INADEQUATE_CERT_TYPE")
if not has_compatible_eku(int_string, usage_abbreviation):
return single_test_output(ee_name, cert_usage,
"SEC_ERROR_INADEQUATE_CERT_TYPE")
return single_test_output(ee_name, cert_usage, "0")
int_error = get_classic_error(int_string, cert_usage);
classic_ee_error = get_classic_error(ee_string, cert_usage)
mozpkix_ee_error = get_mozpkix_ee_error(ee_string, cert_usage);
if (ee_has_os_eku):
if (cert_usage == "certificateUsageStatusResponder"):
if (not int_has_OS_eku):
##classic is OK from here on
if ("NONE" in int_string):
return (" check_ok_ee(cert_from_file('" + ee_name + ".der'), " +
cert_usage + ");\n")
else:
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), useMozillaPKIX ? " +
"SEC_ERROR_INADEQUATE_CERT_TYPE : 0, "+
cert_usage + ");\n");
else :
if (int_string == "int-EKU-OS") or (int_string == "int-EKU-OS_TS"):
#divergent
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), useMozillaPKIX? 0 : -1, "+cert_usage+");\n");
else:
return (" check_ok_ee(cert_from_file('" + ee_name + ".der'), " + cert_usage + ");\n")
else:
## mozpkix is fail here with err = SEC_ERROR_INADEQUATE_CERT_TYPE
if (int_error == 0) and (classic_ee_error == 0):
## classic considers these certs OK
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), useMozillaPKIX? SEC_ERROR_INADEQUATE_CERT_TYPE : 0, "+cert_usage+");\n");
if (classic_ee_error != 0) :
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), SEC_ERROR_INADEQUATE_CERT_TYPE, "+cert_usage+");\n");
## classic is really messy, sometimes OK, sometimes INVALID_CA,
## so limiting testing to mozpkix
return ( " if (useMozillaPKIX) {\n check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), SEC_ERROR_INADEQUATE_CERT_TYPE, " + cert_usage + ");\n }\n")
# If the usage isn't Status Responder, if either the end-entity or
# intermediate certificate has the OCSP Signing usage in its EKU,
# it is not valid for any other usage.
if ("OS" in ee_string or "OS" in int_string):
return single_test_output(ee_name, cert_usage,
"SEC_ERROR_INADEQUATE_CERT_TYPE")
#unreachable
raise Exception("Impossible condition")
##eehasOSeku == 0
if (cert_usage == "certificateUsageStatusResponder"):
#this is fail
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), SEC_ERROR_INADEQUATE_CERT_TYPE, "+cert_usage+");\n");
if cert_usage is "certificateUsageSSLServer":
if not has_compatible_eku(ee_string, usage_abbreviation):
return single_test_output(ee_name, cert_usage,
"SEC_ERROR_INADEQUATE_CERT_TYPE")
# If the usage is SSL Server, the intermediate certificate must either
# have no EKU extension or it must have the Server Auth or Netscape
# Server Gated Crypto (deprecated but allowed for compatibility).
if ("SA" not in int_string and "NONE" not in int_string and
"NS" not in int_string):
return single_test_output(ee_name, cert_usage,
"SEC_ERROR_INADEQUATE_CERT_TYPE")
return single_test_output(ee_name, cert_usage, "0")
if (int_error == 0) and (classic_ee_error == 0):
if (mozpkix_ee_error == 0) :
if (int_has_OS_eku == 0):
return (" check_ok_ee(cert_from_file('" + ee_name + ".der'), " + cert_usage + ");\n")
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), useMozillaPKIX ? SEC_ERROR_INADEQUATE_CERT_TYPE : 0, "
+ cert_usage +");\n")
if not has_compatible_eku(ee_string, usage_abbreviation):
return single_test_output(ee_name, cert_usage,
"SEC_ERROR_INADEQUATE_CERT_TYPE")
if not has_compatible_eku(int_string, usage_abbreviation):
return single_test_output(ee_name, cert_usage,
"SEC_ERROR_INADEQUATE_CERT_TYPE")
return generate_simple_overlay_output(int_string, ee_name, int_error,
classic_ee_error, cert_usage);
def gen_js_output(int_string, ee_string, cert_usage, ee_name):
int_error = get_classic_error(int_string, cert_usage)
classic_ee_error = get_classic_error(ee_string, cert_usage)
mozpkix_ee_error = get_mozpkix_ee_error(ee_string, cert_usage);
if (cert_usage == "certificateUsageSSLCA"):
# since none of these are CA certs (BC) these should all fail.
# I am not strict about The error returned here
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), -1, " + cert_usage + ");\n");
if ((cert_usage == "certificateUsageStatusResponder") or
("OS" in int_string) or ("OS" in ee_string)):
# when the OCSPSigning EKU is set or evalutating against
# usageStatusResponder the calculations are not trivial and thus they
# go into their own routine
return gen_os_js_output(int_string, ee_string, cert_usage, ee_name)
if (int_error == 0) and (classic_ee_error == 0):
if (mozpkix_ee_error == 0) :
return (" check_ok_ee(cert_from_file('" + ee_name + ".der'), " +
cert_usage + ");\n")
else:
return (" check_cert_err_generic(cert_from_file('" +
ee_name + ".der'), useMozillaPKIX ? SEC_ERROR_INADEQUATE_CERT_TYPE : 0, "
+ cert_usage +");\n")
return generate_simple_overlay_output(int_string, ee_name, int_error,
classic_ee_error, cert_usage);
return single_test_output(ee_name, cert_usage, "0")
def generate_test_eku():
outmap = { "NONE" : ""};
#add each one by itself
outmap = { "NONE" : ""}
# add each one by itself
for eku_name in (eku_values.keys()):
outmap[eku_name] = eku_values[eku_name]
#now combo of duples
eku_names = sorted(eku_values.keys());
# now combo of duples
eku_names = sorted(eku_values.keys())
for i in range(len(eku_names)):
for j in range(i + 1, len(eku_names)):
name = eku_names[i] + "_" + eku_names[j];
outmap[name] = ( eku_values[eku_names[i]] + "," +
eku_values[eku_names[j]])
name = eku_names[i] + "_" + eku_names[j]
outmap[name] = (eku_values[eku_names[i]] + "," +
eku_values[eku_names[j]])
all_names = eku_names[0]
all_values = eku_values[eku_names[0]]
for i in range (1, len(eku_names)):
all_names = all_names + "_" + eku_names[i];
all_names = all_names + "_" + eku_names[i]
all_values = all_values + ", " + eku_values[eku_names[i]]
outmap[all_names] = all_values
return outmap
def generate_certs():
js_outfile = open("../test_cert_eku.js", 'w');
def generate_certs(do_cert_generation):
js_outfile = open("../test_cert_eku.js", 'w')
ca_name = "ca"
[ca_key, ca_cert ] = CertUtils.generate_cert_generic(db,
srcdir,
1,
key_type,
ca_name,
CA_basic_constraints)
if do_cert_generation:
[ca_key, ca_cert] = CertUtils.generate_cert_generic(
db, srcdir, 1, key_type, ca_name,
CA_basic_constraints)
ee_ext_text = EE_basic_constraints + EE_full_ku
js_outfile.write(js_file_header);
js_outfile.write(js_file_header)
# now we do it again for valid basic constraints but strange eku/ku at the
# intermediate layer
eku_dict = generate_test_eku();
eku_dict = generate_test_eku()
print eku_dict
for eku_name in (sorted(eku_dict.keys())):
#generate int
int_name = "int-EKU-" + eku_name;
int_serial = random.randint(100, 40000000);
# generate int
int_name = "int-EKU-" + eku_name
int_serial = random.randint(100, 40000000)
eku_text = "extendedKeyUsage = " + eku_dict[eku_name]
if (eku_name == "NONE"):
eku_text = ""
int_ext_text = CA_basic_constraints + CA_full_ku + eku_text
[int_key, int_cert] = CertUtils.generate_cert_generic(db,
srcdir,
int_serial,
key_type,
int_name,
int_ext_text,
ca_key,
ca_cert);
if ("NS" in int_name) or ("SA" in int_name) or ("NONE" in int_name):
if ("OS" not in int_name):
js_outfile.write("\n check_ok_ca(load_cert('"+ int_name + "', ',,'));\n")
else:
js_outfile.write("\n load_cert('"+ int_name +"', ',,');\n")
else:
if ( "CA" in int_name):
# classic allows a cert to be considered CA even if it only
# asserts the ClientAuth EKU, here we have a failure of
# expression. Insanity is more explicit and requires
# the ServerAuth EKU
js_outfile.write("\n check_cert_err_generic(load_cert('"+
int_name +"', ',,'), useMozillaPKIX ? -1 : " +
"0, certificateUsageSSLCA) ;\n")
else:
js_outfile.write("\n check_cert_err_generic(load_cert('"+
int_name +"', ',,'), useMozillaPKIX ? -1 : " +
"-1, certificateUsageSSLCA) ;\n")
if do_cert_generation:
[int_key, int_cert] = CertUtils.generate_cert_generic(
db, srcdir, int_serial, key_type, int_name,
int_ext_text, ca_key, ca_cert)
js_outfile.write("\n")
js_outfile.write(gen_int_js_output(int_name))
for ee_eku_name in (sorted(eku_dict.keys())):
ee_base_name = "ee-EKU-" + ee_eku_name;
for ee_eku_name in (sorted(eku_dict.keys())):
ee_base_name = "ee-EKU-" + ee_eku_name
ee_name = ee_base_name + "-" + int_name
ee_serial = random.randint(100, 40000000);
ee_serial = random.randint(100, 40000000)
ee_eku = "extendedKeyUsage = critical," + eku_dict[ee_eku_name]
if (ee_eku_name == "NONE"):
ee_eku = ""
ee_ext_text = EE_basic_constraints + EE_full_ku + ee_eku
[ee_key, ee_cert] = CertUtils.generate_cert_generic(db,
srcdir,
ee_serial,
key_type,
ee_name,
ee_ext_text,
int_key,
int_cert)
ee_ext_text = EE_basic_constraints + EE_full_ku + ee_eku
if do_cert_generation:
[ee_key, ee_cert] = CertUtils.generate_cert_generic(
db, srcdir, ee_serial, key_type, ee_name,
ee_ext_text, int_key, int_cert)
for cert_usage in (cert_usages):
js_outfile.write(gen_js_output(int_name, ee_base_name,
js_outfile.write(gen_ee_js_output(int_name, ee_base_name,
cert_usage, ee_name))
js_outfile.write(js_file_footer)
js_outfile.close()
generate_certs()
# By default, re-generate the certificates. Anything can be passed as a
# command-line option to prevent this.
do_cert_generation = len(sys.argv) < 2
generate_certs(do_cert_generation)