Skip to content

Commit da8c919

Browse files
davidvinczeutzig
authored andcommitted
imgtool: Add support for dependency description
This commit aims to add the ability to specify and add dependency TLVs to MCUBOOT. Due to the private nature of this feature, having dependency TLVs mean that the TLV Info header and these TLVs become part of the protected area (they are supposed to get signed as well). Since the TLV Info header containing the whole TLV section's size becomes protected, this size needs to be calculated in advance to get proper hash values. Change-Id: I13277a3b595acc2bb8c5084420f3d61c8d301dc2 Author: Bence Kaposzta <[email protected]> Signed-off-by: David Vincze <[email protected]>
1 parent 15aa6ef commit da8c919

File tree

2 files changed

+89
-18
lines changed

2 files changed

+89
-18
lines changed

scripts/imgtool/image.py

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright 2018 Nordic Semiconductor ASA
22
# Copyright 2017 Linaro Limited
3+
# Copyright 2019 Arm Limited
34
#
45
# Licensed under the Apache License, Version 2.0 (the "License");
56
# you may not use this file except in compliance with the License.
@@ -32,6 +33,8 @@
3233
BIN_EXT = "bin"
3334
INTEL_HEX_EXT = "hex"
3435
DEFAULT_MAX_SECTORS = 128
36+
DEP_IMAGES_KEY = "images"
37+
DEP_VERSIONS_KEY = "versions"
3538

3639
# Image header flags.
3740
IMAGE_F = {
@@ -48,6 +51,7 @@
4851
'ECDSA256': 0x22,
4952
'ENCRSA2048': 0x30,
5053
'ENCKW128': 0x31,
54+
'DEPENDENCY': 0x40
5155
}
5256

5357
TLV_INFO_SIZE = 4
@@ -168,11 +172,49 @@ def check(self):
168172
len(self.payload), tsize, self.slot_size)
169173
raise Exception(msg)
170174

171-
def create(self, key, enckey):
172-
self.add_header(enckey)
175+
def create(self, key, enckey, dependencies=None):
176+
if dependencies is None:
177+
dependencies_num = 0
178+
protected_tlv_size = 0
179+
else:
180+
# Size of a Dependency TLV = Header ('BBH') + Payload('IBBHI')
181+
# = 16 Bytes
182+
dependencies_num = len(dependencies[DEP_IMAGES_KEY])
183+
protected_tlv_size = (dependencies_num * 16) + TLV_INFO_SIZE
184+
185+
self.add_header(enckey, protected_tlv_size)
173186

174187
tlv = TLV(self.endian)
175188

189+
if protected_tlv_size != 0:
190+
for i in range(dependencies_num):
191+
e = STRUCT_ENDIAN_DICT[self.endian]
192+
payload = struct.pack(
193+
e + 'I'+'BBHI',
194+
int(dependencies[DEP_IMAGES_KEY][i]),
195+
dependencies[DEP_VERSIONS_KEY][i].major,
196+
dependencies[DEP_VERSIONS_KEY][i].minor,
197+
dependencies[DEP_VERSIONS_KEY][i].revision,
198+
dependencies[DEP_VERSIONS_KEY][i].build
199+
)
200+
tlv.add('DEPENDENCY', payload)
201+
# Full TLV size needs to be calculated in advance, because the
202+
# header will be protected as well
203+
tlv_header_size = 4
204+
payload_digest_size = 32
205+
keyhash_size = 32
206+
cipherkey_size = 32
207+
208+
full_size = TLV_INFO_SIZE + len(tlv.buf) + tlv_header_size \
209+
+ payload_digest_size
210+
if key is not None:
211+
full_size += tlv_header_size + keyhash_size \
212+
+ tlv_header_size + key.sig_len()
213+
if enckey is not None:
214+
full_size += tlv_header_size + cipherkey_size
215+
tlv_header = struct.pack(e + 'HH', TLV_INFO_MAGIC, full_size)
216+
self.payload += tlv_header + bytes(tlv.buf)
217+
176218
# Note that ecdsa wants to do the hashing itself, which means
177219
# we get to hash it twice.
178220
sha = hashlib.sha256()
@@ -208,9 +250,9 @@ def create(self, key, enckey):
208250
self.payload[self.header_size:] = encryptor.update(img) + \
209251
encryptor.finalize()
210252

211-
self.payload += tlv.get()
253+
self.payload += tlv.get()[protected_tlv_size:]
212254

213-
def add_header(self, enckey):
255+
def add_header(self, enckey, protected_tlv_size):
214256
"""Install the image header."""
215257

216258
flags = 0
@@ -219,29 +261,29 @@ def add_header(self, enckey):
219261

220262
e = STRUCT_ENDIAN_DICT[self.endian]
221263
fmt = (e +
222-
# type ImageHdr struct {
223-
'I' + # Magic uint32
224-
'I' + # LoadAddr uint32
225-
'H' + # HdrSz uint16
226-
'H' + # Pad1 uint16
227-
'I' + # ImgSz uint32
228-
'I' + # Flags uint32
229-
'BBHI' + # Vers ImageVersion
230-
'I' # Pad2 uint32
231-
) # }
264+
# type ImageHdr struct {
265+
'I' + # Magic uint32
266+
'I' + # LoadAddr uint32
267+
'H' + # HdrSz uint16
268+
'H' + # PTLVSz uint16
269+
'I' + # ImgSz uint32
270+
'I' + # Flags uint32
271+
'BBHI' + # Vers ImageVersion
272+
'I' # Pad1 uint32
273+
) # }
232274
assert struct.calcsize(fmt) == IMAGE_HEADER_SIZE
233275
header = struct.pack(fmt,
234276
IMAGE_MAGIC,
235277
0, # LoadAddr
236278
self.header_size,
237-
0, # Pad1
279+
protected_tlv_size, # TLV Info header + Dependency TLVs
238280
len(self.payload) - self.header_size, # ImageSz
239281
flags, # Flags
240282
self.version.major,
241283
self.version.minor or 0,
242284
self.version.revision or 0,
243285
self.version.build or 0,
244-
0) # Pad2
286+
0) # Pad1
245287
self.payload = bytearray(self.payload)
246288
self.payload[:len(header)] = header
247289

scripts/imgtool/main.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#! /usr/bin/env python3
22
#
33
# Copyright 2017 Linaro Limited
4+
# Copyright 2019 Arm Limited
45
#
56
# Licensed under the Apache License, Version 2.0 (the "License");
67
# you may not use this file except in compliance with the License.
@@ -14,6 +15,7 @@
1415
# See the License for the specific language governing permissions and
1516
# limitations under the License.
1617

18+
import re
1719
import click
1820
import getpass
1921
import imgtool.keys as keys
@@ -106,6 +108,29 @@ def validate_header_size(ctx, param, value):
106108
return value
107109

108110

111+
def get_dependencies(ctx, param, value):
112+
if value is not None:
113+
versions = []
114+
images = re.findall(r"\((\d+)", value)
115+
if len(images) == 0:
116+
raise click.BadParameter(
117+
"Image dependency format is invalid: {}".format(value))
118+
raw_versions = re.findall(r",\s*([0-9.+]+)\)", value)
119+
if len(images) != len(raw_versions):
120+
raise click.BadParameter(
121+
'''There's a mismatch between the number of dependency images
122+
and versions in: {}'''.format(value))
123+
for raw_version in raw_versions:
124+
try:
125+
versions.append(decode_version(raw_version))
126+
except ValueError as e:
127+
raise click.BadParameter("{}".format(e))
128+
dependencies = dict()
129+
dependencies[image.DEP_IMAGES_KEY] = images
130+
dependencies[image.DEP_VERSIONS_KEY] = versions
131+
return dependencies
132+
133+
109134
class BasedIntParamType(click.ParamType):
110135
name = 'integer'
111136

@@ -138,6 +163,9 @@ def convert(self, value, param, ctx):
138163
help='Add --header-size zeroed bytes at the beginning of the image')
139164
@click.option('-H', '--header-size', callback=validate_header_size,
140165
type=BasedIntParamType(), required=True)
166+
@click.option('-d', '--dependencies', callback=get_dependencies,
167+
required=False, help='''Add dependence on another image, format:
168+
"(<image_ID>,<image_version>), ... "''')
141169
@click.option('-v', '--version', callback=validate_version, required=True)
142170
@click.option('--align', type=click.Choice(['1', '2', '4', '8']),
143171
required=True)
@@ -146,7 +174,8 @@ def convert(self, value, param, ctx):
146174
INFILE and OUTFILE are parsed as Intel HEX if the params have
147175
.hex extension, othewise binary format is used''')
148176
def sign(key, align, version, header_size, pad_header, slot_size, pad,
149-
max_sectors, overwrite_only, endian, encrypt, infile, outfile):
177+
max_sectors, overwrite_only, endian, encrypt, infile, outfile,
178+
dependencies):
150179
img = image.Image(version=decode_version(version), header_size=header_size,
151180
pad_header=pad_header, pad=pad, align=int(align),
152181
slot_size=slot_size, max_sectors=max_sectors,
@@ -159,7 +188,7 @@ def sign(key, align, version, header_size, pad_header, slot_size, pad,
159188
raise Exception("Encryption only available with RSA key")
160189
if key and not isinstance(key, keys.RSA2048):
161190
raise Exception("Signing only available with private RSA key")
162-
img.create(key, enckey)
191+
img.create(key, enckey, dependencies)
163192
img.save(outfile)
164193

165194

0 commit comments

Comments
 (0)