blob: e9904f8642976ce8d294bd060c75bd9fd348cabc [file] [log] [blame]
David Brown22a6fe32017-01-10 10:40:43 -07001#!/usr/bin/python2
2
3from __future__ import print_function
4
5import mmap
6import os
7import struct
8import sys
9from argparse import ArgumentParser
10import newtimg
11from ctypes import *
12from Crypto.Signature import PKCS1_v1_5
13from Crypto.Hash import SHA256
14from Crypto.PublicKey import RSA
15
16DEBUG = False
17
18def get_args():
19 parser = ArgumentParser(description='Script to create images on a format \
20 that Mynewts bootloader expects')
21
22 parser.add_argument('--bin', required=True, dest='binary_file', \
23 help='Name of *.bin file (input)')
24
25 parser.add_argument('--key', required=False, dest='key_file', \
26 help='Name of private key file (*.pem format)')
27
28 parser.add_argument('--out', required=False, dest='image_file', \
29 default='zephyr.img.bin', \
30 help='Name of *.img file (output)')
31
32 parser.add_argument('--sig', required=False, dest='sig_type', \
33 default='SHA256', \
34 help='Type of signature <SHA256|RSA|EC>')
35
36 parser.add_argument('--off', required=False, dest='flash_offs_addr', \
37 default='0x08020000', \
38 help='Offset for the binary in flash (at what address \
39 should it be flashed?)')
40
41 parser.add_argument('--word-size', required=False, dest='word_size',
42 default=1,
43 help='Writable size of flash device (1, 2, 4, or 8)')
44
45 parser.add_argument('--vtoff', required=False, dest='vtable_offs', \
46 default=str(hex(newtimg.OFFSET_VECTOR_TABLE)), \
47 help='Offset to vector table in HEX (default: 0x80)')
48
49 parser.add_argument('--pad', required=False, \
50 help='Pad file with 0xff up to this size (in hex)')
51
52 parser.add_argument('--bit', required=False, action="store_true", \
53 default=False, \
54 help='Whether to add the Boot Image Trailer to the \
55 padded image or not (default: False)')
56
57 parser.add_argument('--verbose', required=False, action="store_true", \
58 default=False, \
59 help='Enable verbose mode')
60
61 parser.add_argument('--version', action='version', version='%(prog)s 1.0')
62
63 parser.add_argument('-f', required=False, action="store_true", \
64 default=False, \
65 help='Flash using JLinkExe')
David Brown06446dd2017-01-23 10:19:13 -070066 parser.add_argument('--image-version', dest='image_version',
67 default=1,
68 help='Major part of version number')
David Brown22a6fe32017-01-10 10:40:43 -070069
70 return parser.parse_args()
71
72def trailer_size(word_size):
73 """
74 Compute the size of the image trailer.
75
76 The image trailer size depends on the writable unit size of the
77 flash in question. Given a word size of 1, 2, 4 or 8, return the
78 size, in bytes, of the image trailer. The magic number should be
79 placed at this particular offset from the end of the segment
80 """
81 sizes = { 1: 402, 2: 788, 4: 1560, 8: 3104 }
82 return sizes[word_size]
83
84class Signature(object):
85 """
86 Sign an image appropriately.
87 """
88
89 def compute(self, payload, key_file):
90 # Base computes sha256.
91 ctx = SHA256.new()
92 ctx.update(payload)
93 self.hash = ctx.digest()
94 self.ctx = ctx
95
96 def get_trailer(self):
97 return struct.pack('bxh32s', newtimg.IMAGE_TLV_SHA256,
98 len(self.hash),
99 self.hash)
100
101 def trailer_len(self):
102 return 32 + 4
103
104 def get_flags(self):
105 return newtimg.IMAGE_F_SHA256
106
107class RSASignature(Signature):
108
109 def compute(self, payload, key_file):
110 super(RSASignature, self).compute(payload, key_file)
111 with open(key_file, 'rb') as f:
112 rsa_key = RSA.importKey(f.read())
113 rsa = PKCS1_v1_5.new(rsa_key)
114 self.signature = rsa.sign(self.ctx)
115
116 def trailer_len(self):
117 return super(RSASignature, self).trailer_len() + newtimg.RSA_SIZE
118
119 def get_trailer(self):
120 buf = bytearray(super(RSASignature, self).get_trailer())
121 buf.extend(struct.pack('bxh', newtimg.IMAGE_TLV_RSA2048,
122 newtimg.RSA_SIZE))
123 buf.extend(self.signature)
124 return buf
125
126 def get_flags(self):
127 return newtimg.IMAGE_F_PKCS15_RSA2048_SHA256 | newtimg.IMAGE_F_SHA256
128
129sigs = {
130 'SHA256': Signature,
131 'RSA': RSASignature,
132 }
133
134class Convert():
135 def __init__(self, args):
136 self.args = args
137 if args.verbose:
138 for a in vars(args):
139 print("Arg -> {}: {}".format(a, getattr(args, a)))
140 self.debug = True
141 else:
142 self.debug = False
143
144 self.vtable_offs = int(args.vtable_offs, 16)
145 self.word_size = int(args.word_size)
146
147 self.load_image(args.binary_file)
148 self.validate_header()
149
150 sig = sigs[args.sig_type]()
151 header = self.make_header(sig)
152 assert len(header) == 32
153 self.image[:len(header)] = header
154
155 sig.compute(self.image, args.key_file)
156 self.trailer = sig.get_trailer()
157
158 self.image.extend(self.trailer)
159
160 if args.bit:
161 self.add_trailer(args.pad)
162
163 self.save_image(args.image_file)
164
165 def load_image(self, name):
166 with open(name, 'rb') as f:
167 image = f.read()
168 self.image = bytearray(image)
169
170 def save_image(self, name):
171 with open(name, 'wb') as f:
172 f.write(self.image)
173
174 def validate_header(self):
175 """Ensure that the image has space for a header
176
177 If the image is build with CONFIG_BOOT_HEADER off, the vector
178 table will be at the beginning, rather than the zero padding.
179 Verify that the padding is present.
180 """
181 if self.image[:self.vtable_offs] != bytearray(self.vtable_offs):
182 raise Exception("Image does not have space for header")
183
184 def make_header(self, sig):
185 image_size = len(self.image) - self.vtable_offs
186 tlv_size = sig.trailer_len()
187 key_id = 0
188 hd = struct.pack('IHBxHxxIIBBHI4x',
189 newtimg.IMAGE_MAGIC,
190 tlv_size,
191 key_id,
192 self.vtable_offs,
193 image_size,
194 sig.get_flags(),
David Brown06446dd2017-01-23 10:19:13 -0700195 int(self.args.image_version), 0, 0, 0)
David Brown22a6fe32017-01-10 10:40:43 -0700196 return hd
197
198 def add_trailer(self, pad):
199 """
200 Add the image trailer, to indicate to the bootloader that this
201 image should be flashed
202 """
203 if not pad:
204 raise Exception("Must specify image length with --pad to use --bit")
205 pad = int(pad, 16)
206
207 if len(self.image) > pad:
208 raise Exception("Image is too large for padding")
209
210 self.image.extend(b'\xFF' * (pad - len(self.image)))
211
212 magic = struct.pack('4I', *newtimg.BOOT_IMG_MAGIC)
213 pos = pad - trailer_size(self.word_size)
214 self.image[pos:pos + len(magic)] = magic
215
216def main(argv):
217 args = get_args()
218 erase = False
219
220 conv = Convert(args)
221
222if __name__ == "__main__":
223 main(sys.argv)