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

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

mercurial