Skip to content

Commit 1a7a690

Browse files
davidvinczed3zd3z
authored andcommitted
imgtool: Add security counter to image manifest
Optionally add new security counter TLV to the protected image manifest and also introduce a new command line option for the imgtool to specify the value of this counter. The security counter can be used in rollback protection to compare the new image's security counter against the active counter value. Its value can be independent from the image version, but if the 'auto' keyword is passed in the argument list of the script then it will be generated from the version number (not including the build number). The value of the security counter is security critical data. Therefore, it must be part of the protected TLV area. Change-Id: I45926d22364d0528164f50fa379abf050bdf65ff Signed-off-by: David Vincze <david.vincze@arm.com>
1 parent 19df5c4 commit 1a7a690

File tree

3 files changed

+78
-34
lines changed

3 files changed

+78
-34
lines changed

NOTICE

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ Portions of this software were developed at
88
Runtime Inc, copyright 2015.
99

1010
Portions of this software were developed at
11-
Arm Limited, copyright 2019.
11+
Arm Limited, copyright 2019-2020.

scripts/imgtool/image.py

+51-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Copyright 2018 Nordic Semiconductor ASA
22
# Copyright 2017 Linaro Limited
3-
# Copyright 2019 Arm Limited
3+
# Copyright 2019-2020 Arm Limited
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
@@ -61,7 +61,8 @@
6161
'ENCRSA2048': 0x30,
6262
'ENCKW128': 0x31,
6363
'ENCEC256': 0x32,
64-
'DEPENDENCY': 0x40
64+
'DEPENDENCY': 0x40,
65+
'SEC_CNT': 0x50,
6566
}
6667

6768
TLV_SIZE = 4
@@ -119,7 +120,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE,
119120
pad_header=False, pad=False, align=1, slot_size=0,
120121
max_sectors=DEFAULT_MAX_SECTORS, overwrite_only=False,
121122
endian="little", load_addr=0, erased_val=None,
122-
save_enctlv=False):
123+
save_enctlv=False, security_counter=None):
123124
self.version = version or versmod.decode_version("0")
124125
self.header_size = header_size
125126
self.pad_header = pad_header
@@ -137,12 +138,23 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE,
137138
self.save_enctlv = save_enctlv
138139
self.enctlv_len = 0
139140

141+
if security_counter == 'auto':
142+
# Security counter has not been explicitly provided,
143+
# generate it from the version number
144+
self.security_counter = ((self.version.major << 24)
145+
+ (self.version.minor << 16)
146+
+ self.version.revision)
147+
else:
148+
self.security_counter = security_counter
149+
140150
def __repr__(self):
141-
return "<Image version={}, header_size={}, base_addr={}, load_addr={}, \
142-
align={}, slot_size={}, max_sectors={}, overwrite_only={}, \
143-
endian={} format={}, payloadlen=0x{:x}>".format(
151+
return "<Image version={}, header_size={}, security_counter={}, \
152+
base_addr={}, load_addr={}, align={}, slot_size={}, \
153+
max_sectors={}, overwrite_only={}, endian={} format={}, \
154+
payloadlen=0x{:x}>".format(
144155
self.version,
145156
self.header_size,
157+
self.security_counter,
146158
self.base_addr if self.base_addr is not None else "N/A",
147159
self.load_addr,
148160
self.align,
@@ -246,14 +258,22 @@ def ecies_p256_hkdf(self, enckey, plainkey):
246258
def create(self, key, enckey, dependencies=None):
247259
self.enckey = enckey
248260

249-
if dependencies is None:
250-
dependencies_num = 0
251-
protected_tlv_size = 0
252-
else:
253-
# Size of a Dependency TLV = Header ('BBH') + Payload('IBBHI')
254-
# = 16 Bytes
261+
protected_tlv_size = 0
262+
263+
if self.security_counter is not None:
264+
# Size of the security counter TLV: header ('HH') + payload ('I')
265+
# = 4 + 4 = 8 Bytes
266+
protected_tlv_size += TLV_SIZE + 4
267+
268+
if dependencies is not None:
269+
# Size of a Dependency TLV = Header ('HH') + Payload('IBBHI')
270+
# = 4 + 12 = 16 Bytes
255271
dependencies_num = len(dependencies[DEP_IMAGES_KEY])
256-
protected_tlv_size = (dependencies_num * 16) + TLV_INFO_SIZE
272+
protected_tlv_size += (dependencies_num * 16)
273+
274+
if protected_tlv_size != 0:
275+
# Add the size of the TLV info header
276+
protected_tlv_size += TLV_INFO_SIZE
257277

258278
# At this point the image is already on the payload, this adds
259279
# the header to the payload as well
@@ -265,17 +285,24 @@ def create(self, key, enckey, dependencies=None):
265285
# in the hash calculation
266286
protected_tlv_off = None
267287
if protected_tlv_size != 0:
268-
for i in range(dependencies_num):
269-
e = STRUCT_ENDIAN_DICT[self.endian]
270-
payload = struct.pack(
271-
e + 'B3x'+'BBHI',
272-
int(dependencies[DEP_IMAGES_KEY][i]),
273-
dependencies[DEP_VERSIONS_KEY][i].major,
274-
dependencies[DEP_VERSIONS_KEY][i].minor,
275-
dependencies[DEP_VERSIONS_KEY][i].revision,
276-
dependencies[DEP_VERSIONS_KEY][i].build
277-
)
278-
prot_tlv.add('DEPENDENCY', payload)
288+
289+
e = STRUCT_ENDIAN_DICT[self.endian]
290+
291+
if self.security_counter is not None:
292+
payload = struct.pack(e + 'I', self.security_counter)
293+
prot_tlv.add('SEC_CNT', payload)
294+
295+
if dependencies is not None:
296+
for i in range(dependencies_num):
297+
payload = struct.pack(
298+
e + 'B3x'+'BBHI',
299+
int(dependencies[DEP_IMAGES_KEY][i]),
300+
dependencies[DEP_VERSIONS_KEY][i].major,
301+
dependencies[DEP_VERSIONS_KEY][i].minor,
302+
dependencies[DEP_VERSIONS_KEY][i].revision,
303+
dependencies[DEP_VERSIONS_KEY][i].build
304+
)
305+
prot_tlv.add('DEPENDENCY', payload)
279306

280307
protected_tlv_off = len(self.payload)
281308
self.payload += prot_tlv.get()

scripts/imgtool/main.py

+26-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#! /usr/bin/env python3
22
#
33
# Copyright 2017 Linaro Limited
4-
# Copyright 2019 Arm Limited
4+
# Copyright 2019-2020 Arm Limited
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -154,6 +154,20 @@ def validate_version(ctx, param, value):
154154
raise click.BadParameter("{}".format(e))
155155

156156

157+
def validate_security_counter(ctx, param, value):
158+
if value is not None:
159+
if value.lower() == 'auto':
160+
return 'auto'
161+
else:
162+
try:
163+
return int(value, 0)
164+
except ValueError:
165+
raise click.BadParameter(
166+
"{} is not a valid integer. Please use code literals "
167+
"prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary."
168+
.format(value))
169+
170+
157171
def validate_header_size(ctx, param, value):
158172
min_hdr_size = image.IMAGE_HEADER_SIZE
159173
if value < min_hdr_size:
@@ -190,13 +204,11 @@ class BasedIntParamType(click.ParamType):
190204

191205
def convert(self, value, param, ctx):
192206
try:
193-
if value[:2].lower() == '0x':
194-
return int(value[2:], 16)
195-
elif value[:1] == '0':
196-
return int(value, 8)
197-
return int(value, 10)
207+
return int(value, 0)
198208
except ValueError:
199-
self.fail('%s is not a valid integer' % value, param, ctx)
209+
self.fail('%s is not a valid integer. Please use code literals '
210+
'prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary.'
211+
% value, param, ctx)
200212

201213

202214
@click.argument('outfile')
@@ -233,6 +245,9 @@ def convert(self, value, param, ctx):
233245
@click.option('-d', '--dependencies', callback=get_dependencies,
234246
required=False, help='''Add dependence on another image, format:
235247
"(<image_ID>,<image_version>), ... "''')
248+
@click.option('-s', '--security-counter', callback=validate_security_counter,
249+
help='Specify the value of security counter. Use the `auto` '
250+
'keyword to automatically generate it from the image version.')
236251
@click.option('-v', '--version', callback=validate_version, required=True)
237252
@click.option('--align', type=click.Choice(['1', '2', '4', '8']),
238253
required=True)
@@ -242,13 +257,15 @@ def convert(self, value, param, ctx):
242257
.hex extension, otherwise binary format is used''')
243258
def sign(key, align, version, header_size, pad_header, slot_size, pad,
244259
max_sectors, overwrite_only, endian, encrypt, infile, outfile,
245-
dependencies, load_addr, hex_addr, erased_val, save_enctlv):
260+
dependencies, load_addr, hex_addr, erased_val, save_enctlv,
261+
security_counter):
246262
img = image.Image(version=decode_version(version), header_size=header_size,
247263
pad_header=pad_header, pad=pad, align=int(align),
248264
slot_size=slot_size, max_sectors=max_sectors,
249265
overwrite_only=overwrite_only, endian=endian,
250266
load_addr=load_addr, erased_val=erased_val,
251-
save_enctlv=save_enctlv)
267+
save_enctlv=save_enctlv,
268+
security_counter=security_counter)
252269
img.load(infile)
253270
key = load_key(key) if key else None
254271
enckey = load_key(encrypt) if encrypt else None

0 commit comments

Comments
 (0)