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.

     1 #!/usr/bin/python
     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/.
     7 # requires openssl >= 1.0.0
     9 import tempfile, os, sys, random
    10 libpath = os.path.abspath('../psm_common_py')
    12 sys.path.append(libpath)
    14 import CertUtils
    16 srcdir = os.getcwd()
    17 db = tempfile.mkdtemp()
    19 CA_basic_constraints = "basicConstraints = critical, CA:TRUE\n"
    20 EE_basic_constraints = "basicConstraints = CA:FALSE\n"
    22 CA_full_ku = "keyUsage = keyCertSign, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, cRLSign\n"
    23 EE_full_ku = "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement\n"
    25 key_type = 'rsa'
    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              }
    38 cert_usages = [ "certificateUsageSSLClient",
    39                 "certificateUsageSSLServer",
    40                 "certificateUsageSSLCA",
    41                 "certificateUsageEmailSigner",
    42                 "certificateUsageEmailRecipient",
    43                 #"certificateUsageObjectSigner",
    44                 "certificateUsageStatusResponder"
    45               ]
    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/.
    53 "use strict";
    55 do_get_profile(); // must be called before getting nsIX509CertDB
    56 const certdb = Cc["@mozilla.org/security/x509certdb;1"]
    57                  .getService(Ci.nsIX509CertDB);
    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 }
    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 }
    70 function run_test() {
    71   load_cert("ca", "CT,CT,CT");
    72   Services.prefs.setBoolPref("security.use_mozillapkix_verification", true);
    73 """
    75 js_file_footer = """}
    76 """
    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")
    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")
    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)
   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)
   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")
   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")
   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")
   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")
   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")
   155     return single_test_output(ee_name, cert_usage, "0")
   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
   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
   186     js_outfile.write(js_file_header)
   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))
   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))
   223     js_outfile.write(js_file_footer)
   224     js_outfile.close()
   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