security/manager/ssl/tests/unit/test_cert_eku/generate.py

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rwxr-xr-x

Revert simplistic fix pending revisit of Mozilla integration attempt.

michael@0 1 #!/usr/bin/python
michael@0 2
michael@0 3 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 6
michael@0 7 # requires openssl >= 1.0.0
michael@0 8
michael@0 9 import tempfile, os, sys, random
michael@0 10 libpath = os.path.abspath('../psm_common_py')
michael@0 11
michael@0 12 sys.path.append(libpath)
michael@0 13
michael@0 14 import CertUtils
michael@0 15
michael@0 16 srcdir = os.getcwd()
michael@0 17 db = tempfile.mkdtemp()
michael@0 18
michael@0 19 CA_basic_constraints = "basicConstraints = critical, CA:TRUE\n"
michael@0 20 EE_basic_constraints = "basicConstraints = CA:FALSE\n"
michael@0 21
michael@0 22 CA_full_ku = "keyUsage = keyCertSign, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, cRLSign\n"
michael@0 23 EE_full_ku = "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement\n"
michael@0 24
michael@0 25 key_type = 'rsa'
michael@0 26
michael@0 27 # codesigning differs significantly between mozilla::pkix and
michael@0 28 # classic NSS that actual testing on it is not very useful
michael@0 29 eku_values = { 'SA': "serverAuth",
michael@0 30 'CA': "clientAuth",
michael@0 31 #'CS': "codeSigning",
michael@0 32 'EP': "emailProtection",
michael@0 33 'TS': "timeStamping",
michael@0 34 'NS': "nsSGC", # Netscape Server Gated Crypto.
michael@0 35 'OS': "1.3.6.1.5.5.7.3.9"
michael@0 36 }
michael@0 37
michael@0 38 cert_usages = [ "certificateUsageSSLClient",
michael@0 39 "certificateUsageSSLServer",
michael@0 40 "certificateUsageSSLCA",
michael@0 41 "certificateUsageEmailSigner",
michael@0 42 "certificateUsageEmailRecipient",
michael@0 43 #"certificateUsageObjectSigner",
michael@0 44 "certificateUsageStatusResponder"
michael@0 45 ]
michael@0 46
michael@0 47 js_file_header = """//// AUTOGENERATED FILE, DO NOT EDIT
michael@0 48 // -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 49 // This Source Code Form is subject to the terms of the Mozilla Public
michael@0 50 // License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 51 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 52
michael@0 53 "use strict";
michael@0 54
michael@0 55 do_get_profile(); // must be called before getting nsIX509CertDB
michael@0 56 const certdb = Cc["@mozilla.org/security/x509certdb;1"]
michael@0 57 .getService(Ci.nsIX509CertDB);
michael@0 58
michael@0 59 function cert_from_file(filename) {
michael@0 60 let der = readFile(do_get_file("test_cert_eku/" + filename, false));
michael@0 61 return certdb.constructX509(der, der.length);
michael@0 62 }
michael@0 63
michael@0 64 function load_cert(cert_name, trust_string) {
michael@0 65 var cert_filename = cert_name + ".der";
michael@0 66 addCertFromFile(certdb, "test_cert_eku/" + cert_filename, trust_string);
michael@0 67 return cert_from_file(cert_filename);
michael@0 68 }
michael@0 69
michael@0 70 function run_test() {
michael@0 71 load_cert("ca", "CT,CT,CT");
michael@0 72 Services.prefs.setBoolPref("security.use_mozillapkix_verification", true);
michael@0 73 """
michael@0 74
michael@0 75 js_file_footer = """}
michael@0 76 """
michael@0 77
michael@0 78 def gen_int_js_output(int_string):
michael@0 79 expectedResult = "SEC_ERROR_INADEQUATE_CERT_TYPE"
michael@0 80 # For a certificate to verify successfully as a SSL CA, it must either
michael@0 81 # have no EKU or have the Server Auth or Netscape Server Gated Crypto
michael@0 82 # usage (the second of which is deprecated but currently supported for
michael@0 83 # compatibility purposes).
michael@0 84 if ("NONE" in int_string or "SA" in int_string or "NS" in int_string):
michael@0 85 expectedResult = "0"
michael@0 86 return (" checkCertErrorGeneric(certdb, load_cert('" + int_string +
michael@0 87 "', ',,'), " + expectedResult + ", certificateUsageSSLCA);\n")
michael@0 88
michael@0 89 def single_test_output(ee_name, cert_usage, error):
michael@0 90 return (" checkCertErrorGeneric(certdb, cert_from_file('" + ee_name +
michael@0 91 ".der'), " + error + ", " + cert_usage + ");\n")
michael@0 92
michael@0 93 def usage_to_abbreviation(usage):
michael@0 94 if usage is "certificateUsageStatusResponder":
michael@0 95 return "OS"
michael@0 96 if usage is "certificateUsageSSLServer":
michael@0 97 return "SA"
michael@0 98 if usage is "certificateUsageSSLClient":
michael@0 99 return "CA"
michael@0 100 if (usage is "certificateUsageEmailSigner" or
michael@0 101 usage is "certificateUsageEmailRecipient"):
michael@0 102 return "EP"
michael@0 103 raise Exception("unsupported usage: " + usage)
michael@0 104
michael@0 105 # In general, for a certificate to be compatible with a usage, it must either
michael@0 106 # have no EKU at all or that usage must be present in its EKU extension.
michael@0 107 def has_compatible_eku(name_string, usage_abbreviation):
michael@0 108 return ("NONE" in name_string or usage_abbreviation in name_string)
michael@0 109
michael@0 110 def gen_ee_js_output(int_string, ee_string, cert_usage, ee_name):
michael@0 111 if cert_usage is "certificateUsageSSLCA":
michael@0 112 # since none of these are CA certs (BC) these should all fail
michael@0 113 return single_test_output(ee_name, cert_usage,
michael@0 114 "SEC_ERROR_INADEQUATE_KEY_USAGE")
michael@0 115
michael@0 116 usage_abbreviation = usage_to_abbreviation(cert_usage)
michael@0 117 if cert_usage is "certificateUsageStatusResponder":
michael@0 118 # For the Status Responder usage, the OSCP Signing usage must be
michael@0 119 # present in the end-entity's EKU extension (i.e. if the extension
michael@0 120 # is not present, the cert is not compatible with this usage).
michael@0 121 if "OS" not in ee_string:
michael@0 122 return single_test_output(ee_name, cert_usage,
michael@0 123 "SEC_ERROR_INADEQUATE_CERT_TYPE")
michael@0 124 if not has_compatible_eku(int_string, usage_abbreviation):
michael@0 125 return single_test_output(ee_name, cert_usage,
michael@0 126 "SEC_ERROR_INADEQUATE_CERT_TYPE")
michael@0 127 return single_test_output(ee_name, cert_usage, "0")
michael@0 128
michael@0 129 # If the usage isn't Status Responder, if the end-entity certificate has
michael@0 130 # the OCSP Signing usage in its EKU, it is not valid for any other usage.
michael@0 131 if "OS" in ee_string:
michael@0 132 return single_test_output(ee_name, cert_usage,
michael@0 133 "SEC_ERROR_INADEQUATE_CERT_TYPE")
michael@0 134
michael@0 135 if cert_usage is "certificateUsageSSLServer":
michael@0 136 if not has_compatible_eku(ee_string, usage_abbreviation):
michael@0 137 return single_test_output(ee_name, cert_usage,
michael@0 138 "SEC_ERROR_INADEQUATE_CERT_TYPE")
michael@0 139 # If the usage is SSL Server, the intermediate certificate must either
michael@0 140 # have no EKU extension or it must have the Server Auth or Netscape
michael@0 141 # Server Gated Crypto (deprecated but allowed for compatibility).
michael@0 142 if ("SA" not in int_string and "NONE" not in int_string and
michael@0 143 "NS" not in int_string):
michael@0 144 return single_test_output(ee_name, cert_usage,
michael@0 145 "SEC_ERROR_INADEQUATE_CERT_TYPE")
michael@0 146 return single_test_output(ee_name, cert_usage, "0")
michael@0 147
michael@0 148 if not has_compatible_eku(ee_string, usage_abbreviation):
michael@0 149 return single_test_output(ee_name, cert_usage,
michael@0 150 "SEC_ERROR_INADEQUATE_CERT_TYPE")
michael@0 151 if not has_compatible_eku(int_string, usage_abbreviation):
michael@0 152 return single_test_output(ee_name, cert_usage,
michael@0 153 "SEC_ERROR_INADEQUATE_CERT_TYPE")
michael@0 154
michael@0 155 return single_test_output(ee_name, cert_usage, "0")
michael@0 156
michael@0 157 def generate_test_eku():
michael@0 158 outmap = { "NONE" : ""}
michael@0 159 # add each one by itself
michael@0 160 for eku_name in (eku_values.keys()):
michael@0 161 outmap[eku_name] = eku_values[eku_name]
michael@0 162 # now combo of duples
michael@0 163 eku_names = sorted(eku_values.keys())
michael@0 164 for i in range(len(eku_names)):
michael@0 165 for j in range(i + 1, len(eku_names)):
michael@0 166 name = eku_names[i] + "_" + eku_names[j]
michael@0 167 outmap[name] = (eku_values[eku_names[i]] + "," +
michael@0 168 eku_values[eku_names[j]])
michael@0 169 all_names = eku_names[0]
michael@0 170 all_values = eku_values[eku_names[0]]
michael@0 171 for i in range (1, len(eku_names)):
michael@0 172 all_names = all_names + "_" + eku_names[i]
michael@0 173 all_values = all_values + ", " + eku_values[eku_names[i]]
michael@0 174 outmap[all_names] = all_values
michael@0 175 return outmap
michael@0 176
michael@0 177 def generate_certs(do_cert_generation):
michael@0 178 js_outfile = open("../test_cert_eku.js", 'w')
michael@0 179 ca_name = "ca"
michael@0 180 if do_cert_generation:
michael@0 181 [ca_key, ca_cert] = CertUtils.generate_cert_generic(
michael@0 182 db, srcdir, 1, key_type, ca_name,
michael@0 183 CA_basic_constraints)
michael@0 184 ee_ext_text = EE_basic_constraints + EE_full_ku
michael@0 185
michael@0 186 js_outfile.write(js_file_header)
michael@0 187
michael@0 188 # now we do it again for valid basic constraints but strange eku/ku at the
michael@0 189 # intermediate layer
michael@0 190 eku_dict = generate_test_eku()
michael@0 191 print eku_dict
michael@0 192 for eku_name in (sorted(eku_dict.keys())):
michael@0 193 # generate int
michael@0 194 int_name = "int-EKU-" + eku_name
michael@0 195 int_serial = random.randint(100, 40000000)
michael@0 196 eku_text = "extendedKeyUsage = " + eku_dict[eku_name]
michael@0 197 if (eku_name == "NONE"):
michael@0 198 eku_text = ""
michael@0 199 int_ext_text = CA_basic_constraints + CA_full_ku + eku_text
michael@0 200 if do_cert_generation:
michael@0 201 [int_key, int_cert] = CertUtils.generate_cert_generic(
michael@0 202 db, srcdir, int_serial, key_type, int_name,
michael@0 203 int_ext_text, ca_key, ca_cert)
michael@0 204 js_outfile.write("\n")
michael@0 205 js_outfile.write(gen_int_js_output(int_name))
michael@0 206
michael@0 207 for ee_eku_name in (sorted(eku_dict.keys())):
michael@0 208 ee_base_name = "ee-EKU-" + ee_eku_name
michael@0 209 ee_name = ee_base_name + "-" + int_name
michael@0 210 ee_serial = random.randint(100, 40000000)
michael@0 211 ee_eku = "extendedKeyUsage = critical," + eku_dict[ee_eku_name]
michael@0 212 if (ee_eku_name == "NONE"):
michael@0 213 ee_eku = ""
michael@0 214 ee_ext_text = EE_basic_constraints + EE_full_ku + ee_eku
michael@0 215 if do_cert_generation:
michael@0 216 [ee_key, ee_cert] = CertUtils.generate_cert_generic(
michael@0 217 db, srcdir, ee_serial, key_type, ee_name,
michael@0 218 ee_ext_text, int_key, int_cert)
michael@0 219 for cert_usage in (cert_usages):
michael@0 220 js_outfile.write(gen_ee_js_output(int_name, ee_base_name,
michael@0 221 cert_usage, ee_name))
michael@0 222
michael@0 223 js_outfile.write(js_file_footer)
michael@0 224 js_outfile.close()
michael@0 225
michael@0 226 # By default, re-generate the certificates. Anything can be passed as a
michael@0 227 # command-line option to prevent this.
michael@0 228 do_cert_generation = len(sys.argv) < 2
michael@0 229 generate_certs(do_cert_generation)

mercurial