Skip to content

Commit 59d2f7a

Browse files
tomchyde-nordic
authored andcommitted
imgtool: Add support for VID and CID
Allow to specify VID and CID for an image. Signed-off-by: Tomasz Chyrowicz <[email protected]>
1 parent 94ad4d4 commit 59d2f7a

File tree

2 files changed

+65
-6
lines changed

2 files changed

+65
-6
lines changed

scripts/imgtool/image.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
import hashlib
3131
import array
3232
import os.path
33+
import re
3334
import struct
35+
import uuid
3436
from enum import Enum
3537

3638
import click
@@ -96,6 +98,8 @@
9698
'DECOMP_SHA': 0x71,
9799
'DECOMP_SIGNATURE': 0x72,
98100
'COMP_DEC_SIZE' : 0x73,
101+
'UUID_VID': 0x74,
102+
'UUID_CID': 0x75,
99103
}
100104

101105
TLV_SIZE = 4
@@ -254,6 +258,27 @@ def tlv_matches_key_type(tlv_type, key):
254258

255259
return False
256260

261+
def parse_uuid(namespace, value):
262+
# Check if UUID is in the RAW format (12345678-1234-5678-1234-567812345678)
263+
uuid_re = r'[0-9A-f]{8}-[0-9A-f]{4}-[0-9A-f]{4}-[0-9A-f]{4}-[0-9A-f]{12}'
264+
if re.match(uuid_re, value):
265+
uuid_bytes = bytes.fromhex(value.replace('-', ''))
266+
267+
# Check if UUID is in the RAW HEX format (12345678123456781234567812345678)
268+
elif re.match(r'[0-9A-f]{32}', value):
269+
uuid_bytes = bytes.fromhex(value)
270+
271+
# Check if UUID is in the string format
272+
elif value.isprintable():
273+
if namespace is not None:
274+
uuid_bytes = uuid.uuid5(namespace, value).bytes
275+
else:
276+
raise ValueError(f"Unknown namespace for UUID: {value}")
277+
else:
278+
raise ValueError(f"Unknown UUID format: {value}")
279+
280+
return uuid_bytes
281+
257282

258283
class Image:
259284

@@ -263,7 +288,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE,
263288
overwrite_only=False, endian="little", load_addr=0,
264289
rom_fixed=None, erased_val=None, save_enctlv=False,
265290
security_counter=None, max_align=None,
266-
non_bootable=False):
291+
non_bootable=False, vid=None, cid=None):
267292

268293
if load_addr and rom_fixed:
269294
raise click.UsageError("Can not set rom_fixed and load_addr at the same time")
@@ -292,6 +317,8 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE,
292317
self.enctlv_len = 0
293318
self.max_align = max(DEFAULT_MAX_ALIGN, align) if max_align is None else int(max_align)
294319
self.non_bootable = non_bootable
320+
self.vid = vid
321+
self.cid = cid
295322

296323
if self.max_align == DEFAULT_MAX_ALIGN:
297324
self.boot_magic = bytes([
@@ -321,7 +348,7 @@ def __repr__(self):
321348
return "<Image version={}, header_size={}, security_counter={}, \
322349
base_addr={}, load_addr={}, align={}, slot_size={}, \
323350
max_sectors={}, overwrite_only={}, endian={} format={}, \
324-
payloadlen=0x{:x}>".format(
351+
payloadlen=0x{:x}, vid={}, cid={}>".format(
325352
self.version,
326353
self.header_size,
327354
self.security_counter,
@@ -333,7 +360,9 @@ def __repr__(self):
333360
self.overwrite_only,
334361
self.endian,
335362
self.__class__.__name__,
336-
len(self.payload))
363+
len(self.payload),
364+
self.vid,
365+
self.cid)
337366

338367
def load(self, path):
339368
"""Load an image from a given file"""
@@ -509,6 +538,16 @@ def create(self, key, public_key_format, enckey, dependencies=None,
509538
# = 4 + 4 = 8 Bytes
510539
protected_tlv_size += TLV_SIZE + 4
511540

541+
if self.vid is not None:
542+
# Size of the VID TLV: header ('HH') + payload ('16s')
543+
# = 4 + 16 = 20 Bytes
544+
protected_tlv_size += TLV_SIZE + 16
545+
546+
if self.cid is not None:
547+
# Size of the CID TLV: header ('HH') + payload ('16s')
548+
# = 4 + 16 = 20 Bytes
549+
protected_tlv_size += TLV_SIZE + 16
550+
512551
if sw_type is not None:
513552
if len(sw_type) > MAX_SW_TYPE_LENGTH:
514553
msg = "'{}' is too long ({} characters) for sw_type. Its " \
@@ -612,6 +651,21 @@ def create(self, key, public_key_format, enckey, dependencies=None,
612651
if compression_tlvs is not None:
613652
for tag, value in compression_tlvs.items():
614653
prot_tlv.add(tag, value)
654+
655+
if self.vid is not None:
656+
vid = parse_uuid(uuid.NAMESPACE_DNS, self.vid)
657+
payload = struct.pack(e + '16s', vid)
658+
prot_tlv.add('UUID_VID', payload)
659+
660+
if self.cid is not None:
661+
if self.vid is not None:
662+
namespace = uuid.UUID(bytes=vid)
663+
else:
664+
namespace = None
665+
cid = parse_uuid(namespace, self.cid)
666+
payload = struct.pack(e + '16s', cid)
667+
prot_tlv.add('UUID_CID', payload)
668+
615669
if custom_tlvs is not None:
616670
for tag, value in custom_tlvs.items():
617671
prot_tlv.add(tag, value)

scripts/imgtool/main.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -473,13 +473,17 @@ def convert(self, value, param, ctx):
473473
@click.command(help='''Create a signed or unsigned image\n
474474
INFILE and OUTFILE are parsed as Intel HEX if the params have
475475
.hex extension, otherwise binary format is used''')
476+
@click.option('--vid', default=None, required=False,
477+
help='Unique vendor identifier, format: (<raw_uuid>|<domain_name)>')
478+
@click.option('--cid', default=None, required=False,
479+
help='Unique image class identifier, format: (<raw_uuid>|<image_class_name>)')
476480
def sign(key, public_key_format, align, version, pad_sig, header_size,
477481
pad_header, slot_size, pad, confirm, max_sectors, overwrite_only,
478482
endian, encrypt_keylen, encrypt, compression, infile, outfile,
479483
dependencies, load_addr, hex_addr, erased_val, save_enctlv,
480484
security_counter, boot_record, custom_tlv, rom_fixed, max_align,
481485
clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, hmac_sha, is_pure,
482-
vector_to_sign, non_bootable):
486+
vector_to_sign, non_bootable, vid, cid):
483487

484488
if confirm:
485489
# Confirmed but non-padded images don't make much sense, because
@@ -492,7 +496,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
492496
endian=endian, load_addr=load_addr, rom_fixed=rom_fixed,
493497
erased_val=erased_val, save_enctlv=save_enctlv,
494498
security_counter=security_counter, max_align=max_align,
495-
non_bootable=non_bootable)
499+
non_bootable=non_bootable, vid=vid, cid=cid)
496500
compression_tlvs = {}
497501
img.load(infile)
498502
key = load_key(key) if key else None
@@ -563,7 +567,8 @@ def sign(key, public_key_format, align, version, pad_sig, header_size,
563567
overwrite_only=overwrite_only, endian=endian,
564568
load_addr=load_addr, rom_fixed=rom_fixed,
565569
erased_val=erased_val, save_enctlv=save_enctlv,
566-
security_counter=security_counter, max_align=max_align)
570+
security_counter=security_counter, max_align=max_align,
571+
vid=vid, cid=cid)
567572
compression_filters = [
568573
{"id": lzma.FILTER_LZMA2, "preset": comp_default_preset,
569574
"dict_size": comp_default_dictsize, "lp": comp_default_lp,

0 commit comments

Comments
 (0)