1
+ #! /usr/bin/env python3
2
+ #
3
+ # Copyright 2017 Linaro Limited
4
+ # Copyright (c) 2018, Arm Limited.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ from __future__ import print_function
19
+ import os
20
+ import re
21
+ import argparse
22
+ from imgtool_lib import keys
23
+ from imgtool_lib import image
24
+ from imgtool_lib import version
25
+ import sys
26
+
27
+ def find_load_address (args ):
28
+ load_address_re = re .compile (r"^#define\sIMAGE_LOAD_ADDRESS\s+(0x[0-9a-fA-F]+)" )
29
+
30
+ if os .path .isabs (args .layout ):
31
+ configFile = args .layout
32
+ else :
33
+ scriptsDir = os .path .dirname (os .path .abspath (__file__ ))
34
+ configFile = os .path .join (scriptsDir , args .layout )
35
+
36
+ ramLoadAddress = None
37
+ with open (configFile , 'r' ) as flash_layout_file :
38
+ for line in flash_layout_file :
39
+ m = load_address_re .match (line )
40
+ if m is not None :
41
+ ramLoadAddress = int (m .group (1 ), 0 )
42
+ print ("**[INFO]** Writing load address from the macro in "
43
+ "flash_layout.h to the image header.. "
44
+ + hex (ramLoadAddress )
45
+ + " (dec. " + str (ramLoadAddress ) + ")" )
46
+ break
47
+ return ramLoadAddress
48
+
49
+ # Returns the last version number if present, or None if not
50
+ def get_last_version (path ):
51
+ if (os .path .isfile (path ) == False ): # Version file not present
52
+ return None
53
+ else : # Version file is present, check it has a valid number inside it
54
+ with open (path , "r" ) as oldFile :
55
+ fileContents = oldFile .read ()
56
+ if version .version_re .match (fileContents ): # number is valid
57
+ return version .decode_version (fileContents )
58
+ else :
59
+ return None
60
+
61
+ def next_version_number (args , defaultVersion , path ):
62
+ newVersion = None
63
+ if (version .compare (args .version , defaultVersion ) == 0 ): # Default version
64
+ lastVersion = get_last_version (path )
65
+ if (lastVersion is not None ):
66
+ newVersion = version .increment_build_num (lastVersion )
67
+ else :
68
+ newVersion = version .increment_build_num (defaultVersion )
69
+ else : # Version number has been explicitly provided (not using the default)
70
+ newVersion = args .version
71
+ versionString = "{a}.{b}.{c}+{d}" .format (
72
+ a = str (newVersion .major ),
73
+ b = str (newVersion .minor ),
74
+ c = str (newVersion .revision ),
75
+ d = str (newVersion .build )
76
+ )
77
+ with open (path , "w" ) as newFile :
78
+ newFile .write (versionString )
79
+ print ("**[INFO]** Image version number set to " + versionString )
80
+ return newVersion
81
+
82
+ def gen_rsa2048 (args ):
83
+ keys .RSA2048 .generate ().export_private (args .key )
84
+
85
+ keygens = {
86
+ 'rsa-2048' : gen_rsa2048 , }
87
+
88
+ def do_keygen (args ):
89
+ if args .type not in keygens :
90
+ msg = "Unexpected key type: {}" .format (args .type )
91
+ raise argparse .ArgumentTypeError (msg )
92
+ keygens [args .type ](args )
93
+
94
+ def do_getpub (args ):
95
+ key = keys .load (args .key )
96
+ if args .lang == 'c' :
97
+ key .emit_c ()
98
+ else :
99
+ msg = "Unsupported language, valid are: c"
100
+ raise argparse .ArgumentTypeError (msg )
101
+
102
+ def do_sign (args ):
103
+ if args .rsa_pkcs1_15 :
104
+ keys .sign_rsa_pss = False
105
+ img = image .Image .load (args .infile ,
106
+ version = next_version_number (args ,
107
+ version .decode_version ("0" ),
108
+ "lastVerNum.txt" ),
109
+ header_size = args .header_size ,
110
+ included_header = args .included_header ,
111
+ pad = args .pad )
112
+ key = keys .load (args .key ) if args .key else None
113
+ img .sign (key , find_load_address (args ))
114
+
115
+ if args .pad :
116
+ img .pad_to (args .pad , args .align )
117
+
118
+ img .save (args .outfile )
119
+
120
+ subcmds = {
121
+ 'keygen' : do_keygen ,
122
+ 'getpub' : do_getpub ,
123
+ 'sign' : do_sign , }
124
+
125
+ def alignment_value (text ):
126
+ value = int (text )
127
+ if value not in [1 , 2 , 4 , 8 ]:
128
+ msg = "{} must be one of 1, 2, 4 or 8" .format (value )
129
+ raise argparse .ArgumentTypeError (msg )
130
+ return value
131
+
132
+ def intparse (text ):
133
+ """Parse a command line argument as an integer.
134
+
135
+ Accepts 0x and other prefixes to allow other bases to be used."""
136
+ return int (text , 0 )
137
+
138
+ def args ():
139
+ parser = argparse .ArgumentParser ()
140
+ subs = parser .add_subparsers (help = 'subcommand help' , dest = 'subcmd' )
141
+
142
+ keygenp = subs .add_parser ('keygen' , help = 'Generate pub/private keypair' )
143
+ keygenp .add_argument ('-k' , '--key' , metavar = 'filename' , required = True )
144
+ keygenp .add_argument ('-t' , '--type' , metavar = 'type' ,
145
+ choices = keygens .keys (), required = True )
146
+
147
+ getpub = subs .add_parser ('getpub' , help = 'Get public key from keypair' )
148
+ getpub .add_argument ('-k' , '--key' , metavar = 'filename' , required = True )
149
+ getpub .add_argument ('-l' , '--lang' , metavar = 'lang' , default = 'c' )
150
+
151
+ sign = subs .add_parser ('sign' , help = 'Sign an image with a private key' )
152
+ sign .add_argument ('--layout' , required = True ,
153
+ help = 'Location of the memory layout file' )
154
+ sign .add_argument ('-k' , '--key' , metavar = 'filename' )
155
+ sign .add_argument ("--align" , type = alignment_value , required = True )
156
+ sign .add_argument ("-v" , "--version" , type = version .decode_version ,
157
+ default = "0.0.0+0" )
158
+ sign .add_argument ("-H" , "--header-size" , type = intparse , required = True )
159
+ sign .add_argument ("--included-header" , default = False , action = 'store_true' ,
160
+ help = 'Image has gap for header' )
161
+ sign .add_argument ("--pad" , type = intparse ,
162
+ help = 'Pad image to this many bytes, adding trailer magic' )
163
+ sign .add_argument ("--rsa-pkcs1-15" ,
164
+ help = 'Use old PKCS#1 v1.5 signature algorithm' ,
165
+ default = False , action = 'store_true' )
166
+ sign .add_argument ("infile" )
167
+ sign .add_argument ("outfile" )
168
+
169
+ args = parser .parse_args ()
170
+ if args .subcmd is None :
171
+ print ('Must specify a subcommand' , file = sys .stderr )
172
+ sys .exit (1 )
173
+
174
+ subcmds [args .subcmd ](args )
175
+
176
+ if __name__ == '__main__' :
177
+ args ()
0 commit comments