blob: 2cd0c899504cddaa25e87f3730009d7e76baa257 [file] [log] [blame]
Christoffer Jansson4e8a7732022-02-08 09:01:12 +01001#!/usr/bin/env vpython3
2
Guo-wei Shiehee408212015-12-09 11:25:38 -08003# -*- coding:utf-8 -*-
4# Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
5#
6# Use of this source code is governed by a BSD-style license
7# that can be found in the LICENSE file in the root of the source
8# tree. An additional intellectual property rights grant can be found
9# in the file PATENTS. All contributing project authors may
10# be found in the AUTHORS file in the root of the source tree.
Guo-wei Shiehee408212015-12-09 11:25:38 -080011"""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
Christoffer Jansson4e8a7732022-02-08 09:01:12 +010022import subprocess
Guo-wei Shiehee408212015-12-09 11:25:38 -080023from optparse import OptionParser
24import os
25import re
26import string
27
Mirko Bonadei86bd33a2020-04-24 21:53:49 +020028_GENERATED_FILE = 'ssl_roots.h'
Guo-wei Shiehee408212015-12-09 11:25:38 -080029_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'
Taylor Brandstetterbee59832020-10-28 14:06:21 -070039_CHAR_TYPE = 'unsigned char* const'
Guo-wei Shiehee408212015-12-09 11:25:38 -080040_VERBOSE = 'verbose'
41
42
43def main():
Christoffer Jansson4e8a7732022-02-08 09:01:12 +010044 """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)
Guo-wei Shiehee408212015-12-09 11:25:38 -080055
56
57def _SplitCrt(source_file, options):
Christoffer Jansson4e8a7732022-02-08 09:01:12 +010058 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
Guo-wei Shiehee408212015-12-09 11:25:38 -080083
84
85def _GenCFiles(root_dir, options):
Christoffer Jansson4e8a7732022-02-08 09:01:12 +010086 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)
Guo-wei Shiehee408212015-12-09 11:25:38 -080097
Christoffer Jansson4e8a7732022-02-08 09:01:12 +010098 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)
Guo-wei Shiehee408212015-12-09 11:25:38 -0800112
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100113 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.write(_CreateOutputFooter())
123 output_header_file.close()
Guo-wei Shiehee408212015-12-09 11:25:38 -0800124
125
126def _Cleanup(root_dir):
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100127 for f in os.listdir(root_dir):
128 if f.startswith(_PREFIX):
129 os.remove(root_dir + f)
Guo-wei Shiehee408212015-12-09 11:25:38 -0800130
131
132def _CreateCertSection(root_dir, source_file, label, options):
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100133 command = 'openssl x509 -in %s%s -noout -C' % (root_dir, source_file)
134 _PrintOutput(command, options)
135 output = subprocess.getstatusoutput(command)[1]
136 renamed_output = output.replace('unsigned char XXX_',
137 'const unsigned char ' + label + '_')
138 filtered_output = ''
139 cert_block = '^const unsigned char.*?};$'
140 prog = re.compile(cert_block, re.IGNORECASE | re.MULTILINE | re.DOTALL)
141 if not options.full_cert:
142 filtered_output = prog.sub('', renamed_output, count=2)
143 else:
144 filtered_output = renamed_output
Guo-wei Shiehee408212015-12-09 11:25:38 -0800145
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100146 cert_size_block = r'\d\d\d+'
147 prog2 = re.compile(cert_size_block, re.MULTILINE | re.VERBOSE)
148 result = prog2.findall(renamed_output)
149 cert_size = result[len(result) - 1]
Guo-wei Shiehee408212015-12-09 11:25:38 -0800150
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100151 return filtered_output, cert_size
Guo-wei Shiehee408212015-12-09 11:25:38 -0800152
153
154def _CreateOutputHeader():
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100155 output = ('/*\n'
156 ' * Copyright 2004 The WebRTC Project Authors. All rights '
157 'reserved.\n'
158 ' *\n'
159 ' * Use of this source code is governed by a BSD-style license\n'
160 ' * that can be found in the LICENSE file in the root of the '
161 'source\n'
162 ' * tree. An additional intellectual property rights grant can be '
163 'found\n'
164 ' * in the file PATENTS. All contributing project authors may\n'
165 ' * be found in the AUTHORS file in the root of the source tree.\n'
166 ' */\n\n'
167 '#ifndef RTC_BASE_SSL_ROOTS_H_\n'
168 '#define RTC_BASE_SSL_ROOTS_H_\n\n'
169 '// This file is the root certificates in C form that are needed to'
170 ' connect to\n// Google.\n\n'
171 '// It was generated with the following command line:\n'
172 '// > vpython3 tools_webrtc/sslroots/generate_sslroots.py'
173 '\n// https://pki.goog/roots.pem\n\n'
174 '// clang-format off\n'
175 '// Don\'t bother formatting generated code,\n'
176 '// also it would breaks subject/issuer lines.\n\n')
177 return output
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100178
Guo-wei Shiehee408212015-12-09 11:25:38 -0800179
Taylor Brandstetterbee59832020-10-28 14:06:21 -0700180def _CreateOutputFooter():
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100181 output = ('// clang-format on\n\n#endif // RTC_BASE_SSL_ROOTS_H_\n')
182 return output
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100183
Guo-wei Shiehee408212015-12-09 11:25:38 -0800184
185def _CreateArraySectionHeader(type_name, type_type, options):
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100186 output = ('const %s kSSLCert%sList[] = {\n') % (type_type, type_name)
187 _PrintOutput(output, options)
188 return output
Guo-wei Shiehee408212015-12-09 11:25:38 -0800189
190
191def _AddLabelToArray(label, type_name):
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100192 return ' %s_%s,\n' % (label, type_name)
Guo-wei Shiehee408212015-12-09 11:25:38 -0800193
194
195def _CreateArraySectionFooter():
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100196 return '};\n\n'
Guo-wei Shiehee408212015-12-09 11:25:38 -0800197
198
199def _SafeName(original_file_name):
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100200 bad_chars = ' -./\\()áéíőú'
201 replacement_chars = ''
202 for _ in bad_chars:
203 replacement_chars += '_'
204 translation_table = string.maketrans(bad_chars, replacement_chars)
205 return original_file_name.translate(translation_table)
Guo-wei Shiehee408212015-12-09 11:25:38 -0800206
207
208def _PrintOutput(output, options):
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100209 if options.verbose:
210 print(output)
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100211
Guo-wei Shiehee408212015-12-09 11:25:38 -0800212
213if __name__ == '__main__':
Christoffer Jansson4e8a7732022-02-08 09:01:12 +0100214 main()