Home | History | Annotate | Download | only in sslroots
      1 # -*- coding:utf-8 -*-
      2 # Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
      3 #
      4 # Use of this source code is governed by a BSD-style license
      5 # that can be found in the LICENSE file in the root of the source
      6 # tree. An additional intellectual property rights grant can be found
      7 # in the file PATENTS.  All contributing project authors may
      8 # be found in the AUTHORS file in the root of the source tree.
      9 
     10 
     11 """This is a tool to transform a crt file into a C/C++ header.
     12 
     13 Usage:
     14 generate_sslroots.py cert_file.crt [--verbose | -v] [--full_cert | -f]
     15 
     16 Arguments:
     17   -v  Print output while running.
     18   -f  Add public key and certificate name.  Default is to skip and reduce
     19       generated file size.
     20 """
     21 
     22 import commands
     23 from optparse import OptionParser
     24 import os
     25 import re
     26 import string
     27 
     28 _GENERATED_FILE = 'sslroots.h'
     29 _PREFIX = '__generated__'
     30 _EXTENSION = '.crt'
     31 _SUBJECT_NAME_ARRAY = 'subject_name'
     32 _SUBJECT_NAME_VARIABLE = 'SubjectName'
     33 _PUBLIC_KEY_ARRAY = 'public_key'
     34 _PUBLIC_KEY_VARIABLE = 'PublicKey'
     35 _CERTIFICATE_ARRAY = 'certificate'
     36 _CERTIFICATE_VARIABLE = 'Certificate'
     37 _CERTIFICATE_SIZE_VARIABLE = 'CertificateSize'
     38 _INT_TYPE = 'size_t'
     39 _CHAR_TYPE = 'const unsigned char*'
     40 _VERBOSE = 'verbose'
     41 
     42 
     43 def main():
     44   """The main entrypoint."""
     45   parser = OptionParser('usage %prog FILE')
     46   parser.add_option('-v', '--verbose', dest='verbose', action='store_true')
     47   parser.add_option('-f', '--full_cert', dest='full_cert', action='store_true')
     48   options, args = parser.parse_args()
     49   if len(args) < 1:
     50     parser.error('No crt file specified.')
     51     return
     52   root_dir = _SplitCrt(args[0], options)
     53   _GenCFiles(root_dir, options)
     54   _Cleanup(root_dir)
     55 
     56 
     57 def _SplitCrt(source_file, options):
     58   sub_file_blocks = []
     59   label_name = ''
     60   root_dir = os.path.dirname(os.path.abspath(source_file)) + '/'
     61   _PrintOutput(root_dir, options)
     62   f = open(source_file)
     63   for line in f:
     64     if line.startswith('# Label: '):
     65       sub_file_blocks.append(line)
     66       label = re.search(r'\".*\"', line)
     67       temp_label = label.group(0)
     68       end = len(temp_label)-1
     69       label_name = _SafeName(temp_label[1:end])
     70     elif line.startswith('-----END CERTIFICATE-----'):
     71       sub_file_blocks.append(line)
     72       new_file_name = root_dir + _PREFIX + label_name + _EXTENSION
     73       _PrintOutput('Generating: ' + new_file_name, options)
     74       new_file = open(new_file_name, 'w')
     75       for out_line in sub_file_blocks:
     76         new_file.write(out_line)
     77       new_file.close()
     78       sub_file_blocks = []
     79     else:
     80       sub_file_blocks.append(line)
     81   f.close()
     82   return root_dir
     83 
     84 
     85 def _GenCFiles(root_dir, options):
     86   output_header_file = open(root_dir + _GENERATED_FILE, 'w')
     87   output_header_file.write(_CreateOutputHeader())
     88   if options.full_cert:
     89     subject_name_list = _CreateArraySectionHeader(_SUBJECT_NAME_VARIABLE,
     90                                                   _CHAR_TYPE, options)
     91     public_key_list = _CreateArraySectionHeader(_PUBLIC_KEY_VARIABLE,
     92                                                 _CHAR_TYPE, options)
     93   certificate_list = _CreateArraySectionHeader(_CERTIFICATE_VARIABLE,
     94                                                _CHAR_TYPE, options)
     95   certificate_size_list = _CreateArraySectionHeader(_CERTIFICATE_SIZE_VARIABLE,
     96                                                     _INT_TYPE, options)
     97 
     98   for _, _, files in os.walk(root_dir):
     99     for current_file in files:
    100       if current_file.startswith(_PREFIX):
    101         prefix_length = len(_PREFIX)
    102         length = len(current_file) - len(_EXTENSION)
    103         label = current_file[prefix_length:length]
    104         filtered_output, cert_size = _CreateCertSection(root_dir, current_file,
    105                                                         label, options)
    106         output_header_file.write(filtered_output + '\n\n\n')
    107         if options.full_cert:
    108           subject_name_list += _AddLabelToArray(label, _SUBJECT_NAME_ARRAY)
    109           public_key_list += _AddLabelToArray(label, _PUBLIC_KEY_ARRAY)
    110         certificate_list += _AddLabelToArray(label, _CERTIFICATE_ARRAY)
    111         certificate_size_list += ('  %s,\n') %(cert_size)
    112 
    113   if options.full_cert:
    114     subject_name_list += _CreateArraySectionFooter()
    115     output_header_file.write(subject_name_list)
    116     public_key_list += _CreateArraySectionFooter()
    117     output_header_file.write(public_key_list)
    118   certificate_list += _CreateArraySectionFooter()
    119   output_header_file.write(certificate_list)
    120   certificate_size_list += _CreateArraySectionFooter()
    121   output_header_file.write(certificate_size_list)
    122   output_header_file.close()
    123 
    124 
    125 def _Cleanup(root_dir):
    126   for f in os.listdir(root_dir):
    127     if f.startswith(_PREFIX):
    128       os.remove(root_dir + f)
    129 
    130 
    131 def _CreateCertSection(root_dir, source_file, label, options):
    132   command = 'openssl x509 -in %s%s -noout -C' %(root_dir, source_file)
    133   _PrintOutput(command, options)
    134   output = commands.getstatusoutput(command)[1]
    135   renamed_output = output.replace('unsigned char XXX_',
    136                                   'const unsigned char ' + label + '_')
    137   filtered_output = ''
    138   cert_block = '^const unsigned char.*?};$'
    139   prog = re.compile(cert_block, re.IGNORECASE | re.MULTILINE | re.DOTALL)
    140   if not options.full_cert:
    141     filtered_output = prog.sub('', renamed_output, count=2)
    142   else:
    143     filtered_output = renamed_output
    144 
    145   cert_size_block = r'\d\d\d+'
    146   prog2 = re.compile(cert_size_block, re.MULTILINE | re.VERBOSE)
    147   result = prog2.findall(renamed_output)
    148   cert_size = result[len(result) - 1]
    149 
    150   return filtered_output, cert_size
    151 
    152 
    153 def _CreateOutputHeader():
    154   output = ('// This file is the root certificates in C form that are needed to'
    155             ' connect to\n// Google.\n\n'
    156             '// It was generated with the following command line:\n'
    157             '// > python tools/certs/generate_sslroots.py'
    158             '\n//    https://pki.google.com/roots.pem\n\n')
    159   return output
    160 
    161 
    162 def _CreateArraySectionHeader(type_name, type_type, options):
    163   output = ('const %s kSSLCert%sList[] = {\n') %(type_type, type_name)
    164   _PrintOutput(output, options)
    165   return output
    166 
    167 
    168 def _AddLabelToArray(label, type_name):
    169   return ' %s_%s,\n' %(label, type_name)
    170 
    171 
    172 def _CreateArraySectionFooter():
    173   return '};\n\n'
    174 
    175 
    176 def _SafeName(original_file_name):
    177   bad_chars = ' -./\\()'
    178   replacement_chars = ''
    179   for _ in bad_chars:
    180     replacement_chars += '_'
    181   translation_table = string.maketrans(bad_chars, replacement_chars)
    182   return original_file_name.translate(translation_table)
    183 
    184 
    185 def _PrintOutput(output, options):
    186   if options.verbose:
    187     print output
    188 
    189 if __name__ == '__main__':
    190   main()
    191