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
13Usage:
14generate_sslroots.py cert_file.crt [--verbose | -v] [--full_cert | -f]
15
16Arguments:
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
22import commands
23from optparse import OptionParser
24import os
25import re
26import 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
43def 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
57def _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
85def _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
125def _Cleanup(root_dir):
126  for f in os.listdir(root_dir):
127    if f.startswith(_PREFIX):
128      os.remove(root_dir + f)
129
130
131def _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
153def _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
162def _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
168def _AddLabelToArray(label, type_name):
169  return ' %s_%s,\n' %(label, type_name)
170
171
172def _CreateArraySectionFooter():
173  return '};\n\n'
174
175
176def _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
185def _PrintOutput(output, options):
186  if options.verbose:
187    print output
188
189if __name__ == '__main__':
190  main()
191