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

changeset 0
6474c204b198
     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)

mercurial