1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/ssl/tests/unit/test_cert_eku/generate.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,229 @@ 1.4 +#!/usr/bin/python 1.5 + 1.6 +# This Source Code Form is subject to the terms of the Mozilla Public 1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.9 + 1.10 +# requires openssl >= 1.0.0 1.11 + 1.12 +import tempfile, os, sys, random 1.13 +libpath = os.path.abspath('../psm_common_py') 1.14 + 1.15 +sys.path.append(libpath) 1.16 + 1.17 +import CertUtils 1.18 + 1.19 +srcdir = os.getcwd() 1.20 +db = tempfile.mkdtemp() 1.21 + 1.22 +CA_basic_constraints = "basicConstraints = critical, CA:TRUE\n" 1.23 +EE_basic_constraints = "basicConstraints = CA:FALSE\n" 1.24 + 1.25 +CA_full_ku = "keyUsage = keyCertSign, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, cRLSign\n" 1.26 +EE_full_ku = "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement\n" 1.27 + 1.28 +key_type = 'rsa' 1.29 + 1.30 +# codesigning differs significantly between mozilla::pkix and 1.31 +# classic NSS that actual testing on it is not very useful 1.32 +eku_values = { 'SA': "serverAuth", 1.33 + 'CA': "clientAuth", 1.34 + #'CS': "codeSigning", 1.35 + 'EP': "emailProtection", 1.36 + 'TS': "timeStamping", 1.37 + 'NS': "nsSGC", # Netscape Server Gated Crypto. 1.38 + 'OS': "1.3.6.1.5.5.7.3.9" 1.39 + } 1.40 + 1.41 +cert_usages = [ "certificateUsageSSLClient", 1.42 + "certificateUsageSSLServer", 1.43 + "certificateUsageSSLCA", 1.44 + "certificateUsageEmailSigner", 1.45 + "certificateUsageEmailRecipient", 1.46 + #"certificateUsageObjectSigner", 1.47 + "certificateUsageStatusResponder" 1.48 + ] 1.49 + 1.50 +js_file_header = """//// AUTOGENERATED FILE, DO NOT EDIT 1.51 +// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.52 +// This Source Code Form is subject to the terms of the Mozilla Public 1.53 +// License, v. 2.0. If a copy of the MPL was not distributed with this 1.54 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.55 + 1.56 +"use strict"; 1.57 + 1.58 +do_get_profile(); // must be called before getting nsIX509CertDB 1.59 +const certdb = Cc["@mozilla.org/security/x509certdb;1"] 1.60 + .getService(Ci.nsIX509CertDB); 1.61 + 1.62 +function cert_from_file(filename) { 1.63 + let der = readFile(do_get_file("test_cert_eku/" + filename, false)); 1.64 + return certdb.constructX509(der, der.length); 1.65 +} 1.66 + 1.67 +function load_cert(cert_name, trust_string) { 1.68 + var cert_filename = cert_name + ".der"; 1.69 + addCertFromFile(certdb, "test_cert_eku/" + cert_filename, trust_string); 1.70 + return cert_from_file(cert_filename); 1.71 +} 1.72 + 1.73 +function run_test() { 1.74 + load_cert("ca", "CT,CT,CT"); 1.75 + Services.prefs.setBoolPref("security.use_mozillapkix_verification", true); 1.76 +""" 1.77 + 1.78 +js_file_footer = """} 1.79 +""" 1.80 + 1.81 +def gen_int_js_output(int_string): 1.82 + expectedResult = "SEC_ERROR_INADEQUATE_CERT_TYPE" 1.83 + # For a certificate to verify successfully as a SSL CA, it must either 1.84 + # have no EKU or have the Server Auth or Netscape Server Gated Crypto 1.85 + # usage (the second of which is deprecated but currently supported for 1.86 + # compatibility purposes). 1.87 + if ("NONE" in int_string or "SA" in int_string or "NS" in int_string): 1.88 + expectedResult = "0" 1.89 + return (" checkCertErrorGeneric(certdb, load_cert('" + int_string + 1.90 + "', ',,'), " + expectedResult + ", certificateUsageSSLCA);\n") 1.91 + 1.92 +def single_test_output(ee_name, cert_usage, error): 1.93 + return (" checkCertErrorGeneric(certdb, cert_from_file('" + ee_name + 1.94 + ".der'), " + error + ", " + cert_usage + ");\n") 1.95 + 1.96 +def usage_to_abbreviation(usage): 1.97 + if usage is "certificateUsageStatusResponder": 1.98 + return "OS" 1.99 + if usage is "certificateUsageSSLServer": 1.100 + return "SA" 1.101 + if usage is "certificateUsageSSLClient": 1.102 + return "CA" 1.103 + if (usage is "certificateUsageEmailSigner" or 1.104 + usage is "certificateUsageEmailRecipient"): 1.105 + return "EP" 1.106 + raise Exception("unsupported usage: " + usage) 1.107 + 1.108 +# In general, for a certificate to be compatible with a usage, it must either 1.109 +# have no EKU at all or that usage must be present in its EKU extension. 1.110 +def has_compatible_eku(name_string, usage_abbreviation): 1.111 + return ("NONE" in name_string or usage_abbreviation in name_string) 1.112 + 1.113 +def gen_ee_js_output(int_string, ee_string, cert_usage, ee_name): 1.114 + if cert_usage is "certificateUsageSSLCA": 1.115 + # since none of these are CA certs (BC) these should all fail 1.116 + return single_test_output(ee_name, cert_usage, 1.117 + "SEC_ERROR_INADEQUATE_KEY_USAGE") 1.118 + 1.119 + usage_abbreviation = usage_to_abbreviation(cert_usage) 1.120 + if cert_usage is "certificateUsageStatusResponder": 1.121 + # For the Status Responder usage, the OSCP Signing usage must be 1.122 + # present in the end-entity's EKU extension (i.e. if the extension 1.123 + # is not present, the cert is not compatible with this usage). 1.124 + if "OS" not in ee_string: 1.125 + return single_test_output(ee_name, cert_usage, 1.126 + "SEC_ERROR_INADEQUATE_CERT_TYPE") 1.127 + if not has_compatible_eku(int_string, usage_abbreviation): 1.128 + return single_test_output(ee_name, cert_usage, 1.129 + "SEC_ERROR_INADEQUATE_CERT_TYPE") 1.130 + return single_test_output(ee_name, cert_usage, "0") 1.131 + 1.132 + # If the usage isn't Status Responder, if the end-entity certificate has 1.133 + # the OCSP Signing usage in its EKU, it is not valid for any other usage. 1.134 + if "OS" in ee_string: 1.135 + return single_test_output(ee_name, cert_usage, 1.136 + "SEC_ERROR_INADEQUATE_CERT_TYPE") 1.137 + 1.138 + if cert_usage is "certificateUsageSSLServer": 1.139 + if not has_compatible_eku(ee_string, usage_abbreviation): 1.140 + return single_test_output(ee_name, cert_usage, 1.141 + "SEC_ERROR_INADEQUATE_CERT_TYPE") 1.142 + # If the usage is SSL Server, the intermediate certificate must either 1.143 + # have no EKU extension or it must have the Server Auth or Netscape 1.144 + # Server Gated Crypto (deprecated but allowed for compatibility). 1.145 + if ("SA" not in int_string and "NONE" not in int_string and 1.146 + "NS" not in int_string): 1.147 + return single_test_output(ee_name, cert_usage, 1.148 + "SEC_ERROR_INADEQUATE_CERT_TYPE") 1.149 + return single_test_output(ee_name, cert_usage, "0") 1.150 + 1.151 + if not has_compatible_eku(ee_string, usage_abbreviation): 1.152 + return single_test_output(ee_name, cert_usage, 1.153 + "SEC_ERROR_INADEQUATE_CERT_TYPE") 1.154 + if not has_compatible_eku(int_string, usage_abbreviation): 1.155 + return single_test_output(ee_name, cert_usage, 1.156 + "SEC_ERROR_INADEQUATE_CERT_TYPE") 1.157 + 1.158 + return single_test_output(ee_name, cert_usage, "0") 1.159 + 1.160 +def generate_test_eku(): 1.161 + outmap = { "NONE" : ""} 1.162 + # add each one by itself 1.163 + for eku_name in (eku_values.keys()): 1.164 + outmap[eku_name] = eku_values[eku_name] 1.165 + # now combo of duples 1.166 + eku_names = sorted(eku_values.keys()) 1.167 + for i in range(len(eku_names)): 1.168 + for j in range(i + 1, len(eku_names)): 1.169 + name = eku_names[i] + "_" + eku_names[j] 1.170 + outmap[name] = (eku_values[eku_names[i]] + "," + 1.171 + eku_values[eku_names[j]]) 1.172 + all_names = eku_names[0] 1.173 + all_values = eku_values[eku_names[0]] 1.174 + for i in range (1, len(eku_names)): 1.175 + all_names = all_names + "_" + eku_names[i] 1.176 + all_values = all_values + ", " + eku_values[eku_names[i]] 1.177 + outmap[all_names] = all_values 1.178 + return outmap 1.179 + 1.180 +def generate_certs(do_cert_generation): 1.181 + js_outfile = open("../test_cert_eku.js", 'w') 1.182 + ca_name = "ca" 1.183 + if do_cert_generation: 1.184 + [ca_key, ca_cert] = CertUtils.generate_cert_generic( 1.185 + db, srcdir, 1, key_type, ca_name, 1.186 + CA_basic_constraints) 1.187 + ee_ext_text = EE_basic_constraints + EE_full_ku 1.188 + 1.189 + js_outfile.write(js_file_header) 1.190 + 1.191 + # now we do it again for valid basic constraints but strange eku/ku at the 1.192 + # intermediate layer 1.193 + eku_dict = generate_test_eku() 1.194 + print eku_dict 1.195 + for eku_name in (sorted(eku_dict.keys())): 1.196 + # generate int 1.197 + int_name = "int-EKU-" + eku_name 1.198 + int_serial = random.randint(100, 40000000) 1.199 + eku_text = "extendedKeyUsage = " + eku_dict[eku_name] 1.200 + if (eku_name == "NONE"): 1.201 + eku_text = "" 1.202 + int_ext_text = CA_basic_constraints + CA_full_ku + eku_text 1.203 + if do_cert_generation: 1.204 + [int_key, int_cert] = CertUtils.generate_cert_generic( 1.205 + db, srcdir, int_serial, key_type, int_name, 1.206 + int_ext_text, ca_key, ca_cert) 1.207 + js_outfile.write("\n") 1.208 + js_outfile.write(gen_int_js_output(int_name)) 1.209 + 1.210 + for ee_eku_name in (sorted(eku_dict.keys())): 1.211 + ee_base_name = "ee-EKU-" + ee_eku_name 1.212 + ee_name = ee_base_name + "-" + int_name 1.213 + ee_serial = random.randint(100, 40000000) 1.214 + ee_eku = "extendedKeyUsage = critical," + eku_dict[ee_eku_name] 1.215 + if (ee_eku_name == "NONE"): 1.216 + ee_eku = "" 1.217 + ee_ext_text = EE_basic_constraints + EE_full_ku + ee_eku 1.218 + if do_cert_generation: 1.219 + [ee_key, ee_cert] = CertUtils.generate_cert_generic( 1.220 + db, srcdir, ee_serial, key_type, ee_name, 1.221 + ee_ext_text, int_key, int_cert) 1.222 + for cert_usage in (cert_usages): 1.223 + js_outfile.write(gen_ee_js_output(int_name, ee_base_name, 1.224 + cert_usage, ee_name)) 1.225 + 1.226 + js_outfile.write(js_file_footer) 1.227 + js_outfile.close() 1.228 + 1.229 +# By default, re-generate the certificates. Anything can be passed as a 1.230 +# command-line option to prevent this. 1.231 +do_cert_generation = len(sys.argv) < 2 1.232 +generate_certs(do_cert_generation)