blob: 1b4646502712d3ce9c45252cb57a8a3dd5ee24cd [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')
66
67 return parser.parse_args()
68
69def trailer_size(word_size):
70 """
71 Compute the size of the image trailer.
72
73 The image trailer size depends on the writable unit size of the
74 flash in question. Given a word size of 1, 2, 4 or 8, return the
75 size, in bytes, of the image trailer. The magic number should be
76 placed at this particular offset from the end of the segment
77 """
78 sizes = { 1: 402, 2: 788, 4: 1560, 8: 3104 }
79 return sizes[word_size]
80
81class Signature(object):
82 """
83 Sign an image appropriately.
84 """
85
86 def compute(self, payload, key_file):
87 # Base computes sha256.
88 ctx = SHA256.new()
89 ctx.update(payload)
90 self.hash = ctx.digest()
91 self.ctx = ctx
92
93 def get_trailer(self):
94 return struct.pack('bxh32s', newtimg.IMAGE_TLV_SHA256,
95 len(self.hash),
96 self.hash)
97
98 def trailer_len(self):
99 return 32 + 4
100
101 def get_flags(self):
102 return newtimg.IMAGE_F_SHA256
103
104class RSASignature(Signature):
105
106 def compute(self, payload, key_file):
107 super(RSASignature, self).compute(payload, key_file)
108 with open(key_file, 'rb') as f:
109 rsa_key = RSA.importKey(f.read())
110 rsa = PKCS1_v1_5.new(rsa_key)
111 self.signature = rsa.sign(self.ctx)
112
113 def trailer_len(self):
114 return super(RSASignature, self).trailer_len() + newtimg.RSA_SIZE
115
116 def get_trailer(self):
117 buf = bytearray(super(RSASignature, self).get_trailer())
118 buf.extend(struct.pack('bxh', newtimg.IMAGE_TLV_RSA2048,
119 newtimg.RSA_SIZE))
120 buf.extend(self.signature)
121 return buf
122
123 def get_flags(self):
124 return newtimg.IMAGE_F_PKCS15_RSA2048_SHA256 | newtimg.IMAGE_F_SHA256
125
126sigs = {
127 'SHA256': Signature,
128 'RSA': RSASignature,
129 }
130
131class Convert():
132 def __init__(self, args):
133 self.args = args
134 if args.verbose:
135 for a in vars(args):
136 print("Arg -> {}: {}".format(a, getattr(args, a)))
137 self.debug = True
138 else:
139 self.debug = False
140
141 self.vtable_offs = int(args.vtable_offs, 16)
142 self.word_size = int(args.word_size)
143
144 self.load_image(args.binary_file)
145 self.validate_header()
146
147 sig = sigs[args.sig_type]()
148 header = self.make_header(sig)
149 assert len(header) == 32
150 self.image[:len(header)] = header
151
152 sig.compute(self.image, args.key_file)
153 self.trailer = sig.get_trailer()
154
155 self.image.extend(self.trailer)
156
157 if args.bit:
158 self.add_trailer(args.pad)
159
160 self.save_image(args.image_file)
161
162 def load_image(self, name):
163 with open(name, 'rb') as f:
164 image = f.read()
165 self.image = bytearray(image)
166
167 def save_image(self, name):
168 with open(name, 'wb') as f:
169 f.write(self.image)
170
171 def validate_header(self):
172 """Ensure that the image has space for a header
173
174 If the image is build with CONFIG_BOOT_HEADER off, the vector
175 table will be at the beginning, rather than the zero padding.
176 Verify that the padding is present.
177 """
178 if self.image[:self.vtable_offs] != bytearray(self.vtable_offs):
179 raise Exception("Image does not have space for header")
180
181 def make_header(self, sig):
182 image_size = len(self.image) - self.vtable_offs
183 tlv_size = sig.trailer_len()
184 key_id = 0
185 hd = struct.pack('IHBxHxxIIBBHI4x',
186 newtimg.IMAGE_MAGIC,
187 tlv_size,
188 key_id,
189 self.vtable_offs,
190 image_size,
191 sig.get_flags(),
192 1, 0, 0, 0)
193 return hd
194
195 def add_trailer(self, pad):
196 """
197 Add the image trailer, to indicate to the bootloader that this
198 image should be flashed
199 """
200 if not pad:
201 raise Exception("Must specify image length with --pad to use --bit")
202 pad = int(pad, 16)
203
204 if len(self.image) > pad:
205 raise Exception("Image is too large for padding")
206
207 self.image.extend(b'\xFF' * (pad - len(self.image)))
208
209 magic = struct.pack('4I', *newtimg.BOOT_IMG_MAGIC)
210 pos = pad - trailer_size(self.word_size)
211 self.image[pos:pos + len(magic)] = magic
212
213def main(argv):
214 args = get_args()
215 erase = False
216
217 conv = Convert(args)
218
219if __name__ == "__main__":
220 main(sys.argv)