-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathpem2arakeys
executable file
·281 lines (242 loc) · 10.9 KB
/
pem2arakeys
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#! /usr/bin/env python
#
# Copyright (c) 2015 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
from __future__ import print_function
import sys
import argparse
import subprocess
from util import error
from signature_block import get_signature_algorithm
from signature_common import format_key_name, get_key_id, get_key_filename, \
TFTF_SIGNATURE_ALGORITHM_RSA_2048_SHA_256
# Program return values
PROGRAM_SUCCESS = 0
PROGRAM_WARNINGS = 1
PROGRAM_ERROR = 2
MAX_FILES = 4
USAGE = """"%(prog)s --key public_key.pem... --domain <string> \\
--algorithm <string> {--id <string>} {--name <string>} {--singleton}
Where:
--key
One to four public key files
--id
The key ID for the key (forms the left half of the key name
(e.g. 'test-151210-02-20151212-01'). Normally the key ID is
derived from the key file name, and up to 4 keys may be specified.
If --id is specified, only 1 key file may be used.
--domain
The right half of the key name (e.g. 's2fvk.projectara.com')
--algorithm
Specifies the signing algorithm (e.g., 'rsa2048-sha256')
--name
Name of the 'C' variable containing the keys (default: 'public_keys')
--singleton
Declare the one key as a struct instead of an array
"""
COPYRIGHT = """/*
* Copyright (c) 2015 Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"""
INCLUDES = """
#include <stddef.h>
#include "crypto.h"
"""
KEY_ARRAY = "const crypto_public_key {0:s}[] = {1:s}\n"
# TFTF Signature Types and associated dictionary of types and names
# NOTE: When adding new types, both the "define" and the dictionary
# need to be updated.
tftf_signature_define_names = \
{TFTF_SIGNATURE_ALGORITHM_RSA_2048_SHA_256:
"ALGORITHM_TYPE_RSA2048_SHA256"}
def get_signature_algorithm_define(algorithm):
""" Convert a algorithm_type (TFTF_SIGNATURE_TYPE_xxx) into a string
Convert the algorithm type into a string used as part of a "#define" (as
distinct from the regular name returned by get_signature_algorithm_name)
returns a key name, or raises an exception if invalid
"""
try:
return tftf_signature_define_names[algorithm]
except:
raise ValueError("Unknown algorithm type: '{0:d}'".format(algorithm))
def validate_input_files(keyfiles):
# Validate the list of key file names
success = True
for i, f in enumerate(keyfiles):
pathname = get_key_filename(f, False)
if not pathname:
print("Can't find", f)
success = False
else:
if (pathname != f):
keyfiles[i] = pathname
return success
def convert_pem_to_array(key_filename, wf, key_id, domain, algorithm,
indent, include_braces):
# Convert a public key file into a C-style array appended to the ouput
# file
# Note: indent3 is 7 spaces and not 8 because each array element is
# printed with a leading blank.
indent2 = indent + " "
indent3 = indent + " "
# Generate the key_name from the file and verify that it will fit
# in the 96-byte field as a null-terminated string
try:
key_name = format_key_name(get_key_id(key_id, key_filename), domain)
except ValueError as e:
error(e)
sys.exit(PROGRAM_ERROR)
# Get the blob and strip off the cruft around it.
str = subprocess.check_output(["openssl", "rsa", "-pubin", "-text",
"-noout", "-in", key_filename])
array_start = str.index("Modulus:") + 13
array_end = str.index("Exponent:") - 1
str = str[array_start:array_end]
str = str.replace("\n", "")
str = str.replace(" ", "")
block = str.split(':')
block = block[1:]
column = 0
if include_braces:
wf.write("{0:s}{1:s}\n".format(indent, "{"))
wf.write("{0:s}.type = {1:s},\n".
format(indent2, get_signature_algorithm_define(algorithm)))
wf.write("{0:s}.key_name = \"{1:s}\",\n".format(indent2, key_name))
wf.write("{0:s}.key = {1:s}\n".format(indent2, "{"))
for index, byte in enumerate(block):
# Indent the beginning of the line
if (column == 0):
wf.write(indent3)
# Break the line if it will go over the 80-char limit
if index == 255:
wf.write(" 0x{0:s}\n".format(byte))
elif column < 11:
wf.write(" 0x{0:s},".format(byte))
column += 1
else:
wf.write(" 0x{0:s},\n".format(byte))
column = 0
wf.write("{0:s}{1:s}\n".format(indent2, "}"))
if include_braces:
wf.write("{0:s}{1:s},\n".format(indent, "}"))
def process_input_files(keyfiles, wf, array_name, algorithm, key_id,
domain, singleton):
""" Convert the public key files into a C array in a header file """
# Add the boilerplate copyright and #includes
wf.write(COPYRIGHT)
wf.write(INCLUDES)
# Add the constant for the number of keys in the array
if singleton:
# Add the "public_keys" declaration
wf.write("const crypto_public_key {0:s} = {1:s}\n".
format(array_name, "{"))
# Process each file as a component of the array
for keyfile in keyfiles:
convert_pem_to_array(keyfile, wf, key_id, domain,
algorithm, "", False)
# Complete the declaration
wf.write("};\n\n")
else:
# Add the start of the "public_keys" array
wf.write(KEY_ARRAY.format(array_name, "{"))
# Process each file as a component of the array
for keyfile in keyfiles:
convert_pem_to_array(keyfile, wf, key_id, domain,
algorithm, " ", True)
# Complete the 2D array declaration
wf.write("};\n\n")
wf.write("const uint32_t number_of_public_keys = "
"sizeof(public_keys)/sizeof(crypto_public_key);\n")
def main():
"""Application to generate a header file containing 4 public keys
This is covered in detail in "ES3 Bridge ASIC Boot ROM High Level Design".
"""
# Parse the command line args
parser = argparse.ArgumentParser(usage=USAGE)
# App-specific args:
parser.add_argument("--key", required=True,
help="The name of the signing key PEM file "
"(e.g. 'test-151210-02-20151212-01.private.pem')")
parser.add_argument("--id", required=False,
help="The key ID to be used as the left half of the "
"validaion key name instead of using the key "
"file name")
parser.add_argument("--domain", required=True,
help="The key domain - the right-hand part "
"of the validation key name")
parser.add_argument("--algorithm", required=True,
help="The cryptographic signature algorithm used "
"in the PEM file")
parser.add_argument("--name", required=False, default="public_keys",
help="Name of the 'C' array or single struct")
parser.add_argument("--singleton", action='store_true',
help="Declare the one key as a variable instead "
"of an array of variables")
parser.add_argument("keyfiles", metavar="N", nargs="*",
help="One or more public key files (e.g., foo.pem)")
args = parser.parse_args()
# Validate parameters
algorithm = get_signature_algorithm(args.algorithm)
if args.singleton and (len(args.keyfiles) > 1):
error("--singleton specified with multiple files")
sys.exit(1)
if args.id and (len(args.keyfiles) > 1):
error("--id specified with multiple files")
sys.exit(1)
# Create the file on stdout
if validate_input_files(args.keyfiles):
process_input_files(args.keyfiles, sys.stdout, args.name,
algorithm,
args.id, args.domain, args.singleton)
## Launch main
#
if __name__ == '__main__':
main()