From 8b263bf14143d3cbe4f9f2ac605212b448c4db2e Mon Sep 17 00:00:00 2001 From: Maulik Patel Date: Wed, 14 May 2025 10:28:07 +0100 Subject: [PATCH 1/4] bootutil: Parse key id for built in keys When MCUBOOT_BUILTIN_KEY is enabled, the key id TLV entry is added to the image. Parse this entry while validating the image to identify the key used to sign the image. This enables future support for scenarios such as multiple built-in keys or multi-signature. Signed-off-by: Maulik Patel Change-Id: Ibe26bc2b09e63350f4214719606a5aa4bc1be93c --- boot/bootutil/include/bootutil/crypto/ecdsa.h | 5 --- boot/bootutil/include/bootutil/image.h | 1 + boot/bootutil/include/bootutil/sign_key.h | 11 +++++ boot/bootutil/src/image_validate.c | 40 ++++++++++++++----- scripts/imgtool/image.py | 24 ++++++++++- 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/boot/bootutil/include/bootutil/crypto/ecdsa.h b/boot/bootutil/include/bootutil/crypto/ecdsa.h index 3b05410722..e0cf493ce1 100644 --- a/boot/bootutil/include/bootutil/crypto/ecdsa.h +++ b/boot/bootutil/include/bootutil/crypto/ecdsa.h @@ -392,11 +392,6 @@ static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) ctx->required_algorithm = 0; #else /* !MCUBOOT_BUILTIN_KEY */ - /* The incoming key ID is equal to the image index. The key ID value must be - * shifted (by one in this case) because zero is reserved (PSA_KEY_ID_NULL) - * and considered invalid. - */ - ctx->key_id++; /* Make sure it is not equal to 0. */ #if defined(MCUBOOT_SIGN_EC256) ctx->curve_byte_count = 32; ctx->required_algorithm = PSA_ALG_SHA_256; diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h index 15de3e01ac..cd3a8bf5b1 100644 --- a/boot/bootutil/include/bootutil/image.h +++ b/boot/bootutil/include/bootutil/image.h @@ -100,6 +100,7 @@ struct flash_area; */ #define IMAGE_TLV_KEYHASH 0x01 /* hash of the public key */ #define IMAGE_TLV_PUBKEY 0x02 /* public key */ +#define IMAGE_TLV_KEYID 0x03 /* Key ID */ #define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */ #define IMAGE_TLV_SHA384 0x11 /* SHA384 of image hdr and body */ #define IMAGE_TLV_SHA512 0x12 /* SHA512 of image hdr and body */ diff --git a/boot/bootutil/include/bootutil/sign_key.h b/boot/bootutil/include/bootutil/sign_key.h index a5e81d3506..ea7df3f016 100644 --- a/boot/bootutil/include/bootutil/sign_key.h +++ b/boot/bootutil/include/bootutil/sign_key.h @@ -39,6 +39,17 @@ struct bootutil_key { }; extern const struct bootutil_key bootutil_keys[]; +#ifdef MCUBOOT_BUILTIN_KEY +/** + * Verify that the specified key ID is valid for authenticating the given image. + * + * @param[in] image_index Index of the image to be verified. + * @param[in] key_id Identifier of the key to be verified against the image. + * + * @return 0 if the key ID is valid for the image; nonzero on failure. + */ +int boot_verify_key_id_for_image(uint8_t image_index, int32_t key_id); +#endif /* MCUBOOT_BUILTIN_KEY */ #else struct bootutil_key { uint8_t *key; diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c index 61cbf4de04..e308a968f5 100644 --- a/boot/bootutil/src/image_validate.c +++ b/boot/bootutil/src/image_validate.c @@ -273,12 +273,12 @@ bootutil_img_hash(struct boot_loader_state *state, #if !defined(MCUBOOT_HW_KEY) static int -bootutil_find_key(uint8_t *keyhash, uint8_t keyhash_len) +bootutil_find_key(uint8_t image_index, uint8_t *keyhash, uint8_t keyhash_len) { bootutil_sha_context sha_ctx; int i; const struct bootutil_key *key; - uint8_t hash[IMAGE_HASH_SIZE]; + (void)image_index; if (keyhash_len > IMAGE_HASH_SIZE) { return -1; @@ -335,6 +335,32 @@ bootutil_find_key(uint8_t image_index, uint8_t *key, uint16_t key_len) return -1; } #endif /* !MCUBOOT_HW_KEY */ + +#else +/* For MCUBOOT_BUILTIN_KEY, key id is passed */ +#define EXPECTED_KEY_TLV IMAGE_TLV_KEYID +#define KEY_BUF_SIZE sizeof(int32_t) + +static int bootutil_find_key(uint8_t image_index, uint8_t *key_id_buf, uint8_t key_id_buf_len) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + /* Key id is passed */ + assert(key_id_buf_len == sizeof(int32_t)); + int32_t key_id = (((int32_t)key_id_buf[0] << 24) | + ((int32_t)key_id_buf[1] << 16) | + ((int32_t)key_id_buf[2] << 8) | + ((int32_t)key_id_buf[3])); + + /* Check if key id is associated with the image */ + FIH_CALL(boot_verify_key_id_for_image, fih_rc, image_index, key_id); + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + return key_id; + } + + return -1; +} #endif /* !MCUBOOT_BUILTIN_KEY */ #endif /* EXPECTED_SIG_TLV */ @@ -450,6 +476,7 @@ static int bootutil_check_for_pure(const struct image_header *hdr, static const uint16_t allowed_unprot_tlvs[] = { IMAGE_TLV_KEYHASH, IMAGE_TLV_PUBKEY, + IMAGE_TLV_KEYID, IMAGE_TLV_SHA256, IMAGE_TLV_SHA384, IMAGE_TLV_SHA512, @@ -492,14 +519,7 @@ bootutil_img_validate(struct boot_loader_state *state, uint16_t type; #ifdef EXPECTED_SIG_TLV FIH_DECLARE(valid_signature, FIH_FAILURE); -#ifndef MCUBOOT_BUILTIN_KEY int key_id = -1; -#else - /* Pass a key ID equal to the image index, the underlying crypto library - * is responsible for mapping the image index to a builtin key ID. - */ - int key_id = image_index; -#endif /* !MCUBOOT_BUILTIN_KEY */ #ifdef MCUBOOT_HW_KEY uint8_t key_buf[KEY_BUF_SIZE]; #endif @@ -631,7 +651,7 @@ bootutil_img_validate(struct boot_loader_state *state, if (rc) { goto out; } - key_id = bootutil_find_key(buf, len); + key_id = bootutil_find_key(image_index, buf, len); #else rc = LOAD_IMAGE_DATA(hdr, fap, off, key_buf, len); if (rc) { diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index 3e2c11073f..c5981a3495 100644 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -76,6 +76,7 @@ TLV_VALUES = { 'KEYHASH': 0x01, 'PUBKEY': 0x02, + 'KEYID': 0x03, 'SHA256': 0x10, 'SHA384': 0x11, 'SHA512': 0x12, @@ -135,13 +136,19 @@ def add(self, kind, payload): """ e = STRUCT_ENDIAN_DICT[self.endian] if isinstance(kind, int): - if not TLV_VENDOR_RES_MIN <= kind <= TLV_VENDOR_RES_MAX: + if kind in TLV_VALUES.values(): + buf = struct.pack(e + 'BBH', kind, 0, len(payload)) + elif TLV_VENDOR_RES_MIN <= kind <= TLV_VENDOR_RES_MAX: + # Custom vendor-reserved tag + buf = struct.pack(e + 'HH', kind, len(payload)) + else: msg = "Invalid custom TLV type value '0x{:04x}', allowed " \ "value should be between 0x{:04x} and 0x{:04x}".format( kind, TLV_VENDOR_RES_MIN, TLV_VENDOR_RES_MAX) raise click.UsageError(msg) - buf = struct.pack(e + 'HH', kind, len(payload)) else: + if kind not in TLV_VALUES: + raise click.UsageError(f"Unknown TLV type string: {kind}") buf = struct.pack(e + 'BBH', TLV_VALUES[kind], 0, len(payload)) self.buf += buf self.buf += payload @@ -632,6 +639,9 @@ def create(self, key, public_key_format, enckey, dependencies=None, print(os.path.basename(__file__) + ': export digest') return + if self.key_ids is not None: + self._add_key_id_tlv_to_unprotected(tlv, self.key_ids[0]) + if key is not None or fixed_sig is not None: if public_key_format == 'hash': tlv.add('KEYHASH', pubbytes) @@ -883,3 +893,13 @@ def verify(imgfile, key): pass tlv_off += TLV_SIZE + tlv_len return VerifyResult.INVALID_SIGNATURE, None, None, None + + def set_key_ids(self, key_ids): + """Set list of key IDs (integers) to be inserted before each signature.""" + self.key_ids = key_ids + + def _add_key_id_tlv_to_unprotected(self, tlv, key_id: int): + """Add a key ID TLV into the *unprotected* TLV area.""" + tag = TLV_VALUES['KEYID'] + value = key_id.to_bytes(4, self.endian) + tlv.add(tag, value) \ No newline at end of file From 6f6a6f6c73939002ec81781258f5fca5241fb5d0 Mon Sep 17 00:00:00 2001 From: Maulik Patel Date: Tue, 15 Apr 2025 16:29:38 +0100 Subject: [PATCH 2/4] imgtool: Add support for multiple signatures This patch adds support for multiple signatures to single image. This is useful for scenarios where multiple keys are used to sign images, allowing for greater flexibility and security in the image verification process. The tool command line interface is extended to support multiple signatures. The imgtool test suite is updated to test the new functionality. Change-Id: I285b426671f6ad76472f0a2f8fb3a330f8882c3d Signed-off-by: Maulik Patel --- scripts/imgtool/image.py | 193 ++++++++++++++++++++++++--------------- scripts/imgtool/main.py | 52 +++++++---- 2 files changed, 151 insertions(+), 94 deletions(-) diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index c5981a3495..b2f7cff637 100644 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -297,6 +297,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, self.enctlv_len = 0 self.max_align = max(DEFAULT_MAX_ALIGN, align) if max_align is None else int(max_align) self.non_bootable = non_bootable + self.key_ids = None if self.max_align == DEFAULT_MAX_ALIGN: self.boot_magic = bytes([ @@ -464,32 +465,40 @@ def ecies_hkdf(self, enckey, plainkey): format=PublicFormat.Raw) return cipherkey, ciphermac, pubk - def create(self, key, public_key_format, enckey, dependencies=None, + def create(self, keys, public_key_format, enckey, dependencies=None, sw_type=None, custom_tlvs=None, compression_tlvs=None, compression_type=None, encrypt_keylen=128, clear=False, fixed_sig=None, pub_key=None, vector_to_sign=None, user_sha='auto', is_pure=False, keep_comp_size=False, dont_encrypt=False): self.enckey = enckey - # key decides on sha, then pub_key; of both are none default is used - check_key = key if key is not None else pub_key + # key decides on sha, then pub_key; if both are none default is used + check_key = keys[0] if keys[0] is not None else pub_key hash_algorithm, hash_tlv = key_and_user_sha_to_alg_and_tlv(check_key, user_sha, is_pure) # Calculate the hash of the public key - if key is not None: - pub = key.get_public_bytes() - sha = hash_algorithm() - sha.update(pub) - pubbytes = sha.digest() - elif pub_key is not None: - if hasattr(pub_key, 'sign'): - print(os.path.basename(__file__) + ": sign the payload") - pub = pub_key.get_public_bytes() - sha = hash_algorithm() - sha.update(pub) - pubbytes = sha.digest() + pub_digests = [] + pub_list = [] + + if keys is None: + if pub_key is not None: + if hasattr(pub_key, 'sign'): + print(os.path.basename(__file__) + ": sign the payload") + pub = pub_key.get_public_bytes() + sha = hash_algorithm() + sha.update(pub) + pubbytes = sha.digest() + else: + pubbytes = bytes(hashlib.sha256().digest_size) else: - pubbytes = bytes(hashlib.sha256().digest_size) + for key in keys or []: + pub = key.get_public_bytes() + sha = hash_algorithm() + sha.update(pub) + pubbytes = sha.digest() + pub_digests.append(pubbytes) + pub_list.append(pub) + protected_tlv_size = 0 @@ -517,10 +526,14 @@ def create(self, key, public_key_format, enckey, dependencies=None, # value later. digest = bytes(hash_algorithm().digest_size) + if pub_digests: + boot_pub_digest = pub_digests[0] + else: + boot_pub_digest = pubbytes # Create CBOR encoded boot record boot_record = create_sw_component_data(sw_type, image_version, hash_tlv, digest, - pubbytes) + boot_pub_digest) protected_tlv_size += TLV_SIZE + len(boot_record) @@ -639,20 +652,30 @@ def create(self, key, public_key_format, enckey, dependencies=None, print(os.path.basename(__file__) + ': export digest') return - if self.key_ids is not None: - self._add_key_id_tlv_to_unprotected(tlv, self.key_ids[0]) + if fixed_sig is not None and keys is not None: + raise click.UsageError("Can not sign using key and provide fixed-signature at the same time") - if key is not None or fixed_sig is not None: - if public_key_format == 'hash': - tlv.add('KEYHASH', pubbytes) - else: - tlv.add('PUBKEY', pub) + if fixed_sig is not None: + tlv.add(pub_key.sig_tlv(), fixed_sig['value']) + self.signatures[0] = fixed_sig['value'] + else: + # Multi-signature handling: iterate through each provided key and sign. + self.signatures = [] + for i, key in enumerate(keys): + # If key IDs are provided, and we have enough for this key, add it first. + if self.key_ids is not None and len(self.key_ids) > i: + # Convert key id (an integer) to 4-byte big-endian bytes. + kid_bytes = self.key_ids[i].to_bytes(4, 'big') + tlv.add('KEYID', kid_bytes) # Using the TLV tag that corresponds to key IDs. + + if public_key_format == 'hash': + tlv.add('KEYHASH', pub_digests[i]) + else: + tlv.add('PUBKEY', pub_list[i]) - if key is not None and fixed_sig is None: # `sign` expects the full image payload (hashing done # internally), while `sign_digest` expects only the digest # of the payload - if hasattr(key, 'sign'): print(os.path.basename(__file__) + ": sign the payload") sig = key.sign(bytes(self.payload)) @@ -660,12 +683,8 @@ def create(self, key, public_key_format, enckey, dependencies=None, print(os.path.basename(__file__) + ": sign the digest") sig = key.sign_digest(message) tlv.add(key.sig_tlv(), sig) - self.signature = sig - elif fixed_sig is not None and key is None: - tlv.add(pub_key.sig_tlv(), fixed_sig['value']) - self.signature = fixed_sig['value'] - else: - raise click.UsageError("Can not sign using key and provide fixed-signature at the same time") + self.signatures.append(sig) + # At this point the image was hashed + signed, we can remove the # protected TLVs from the payload (will be re-added later) @@ -714,7 +733,7 @@ def get_struct_endian(self): return STRUCT_ENDIAN_DICT[self.endian] def get_signature(self): - return self.signature + return self.signatures def get_infile_data(self): return self.infile_data @@ -824,75 +843,99 @@ def verify(imgfile, key): if magic != IMAGE_MAGIC: return VerifyResult.INVALID_MAGIC, None, None, None + # Locate the first TLV info header tlv_off = header_size + img_size tlv_info = b[tlv_off:tlv_off + TLV_INFO_SIZE] magic, tlv_tot = struct.unpack('HH', tlv_info) + + # If it's the protected-TLV block, skip it if magic == TLV_PROT_INFO_MAGIC: - tlv_off += tlv_tot + tlv_off += TLV_INFO_SIZE + tlv_tot tlv_info = b[tlv_off:tlv_off + TLV_INFO_SIZE] magic, tlv_tot = struct.unpack('HH', tlv_info) if magic != TLV_INFO_MAGIC: return VerifyResult.INVALID_TLV_INFO_MAGIC, None, None, None - # This is set by existence of TLV SIG_PURE - is_pure = False + # Define the unprotected-TLV window + unprot_off = tlv_off + TLV_INFO_SIZE + unprot_end = unprot_off + tlv_tot - prot_tlv_size = tlv_off - hash_region = b[:prot_tlv_size] - tlv_end = tlv_off + tlv_tot - tlv_off += TLV_INFO_SIZE # skip tlv info + # Region up to the start of unprotected TLVs is hashed + prot_tlv_end = unprot_off - TLV_INFO_SIZE + hash_region = b[:prot_tlv_end] - # First scan all TLVs in search of SIG_PURE - while tlv_off < tlv_end: - tlv = b[tlv_off:tlv_off + TLV_SIZE] + # This is set by existence of TLV SIG_PURE + is_pure = False + scan_off = unprot_off + while scan_off < unprot_end: + tlv = b[scan_off:scan_off + TLV_SIZE] tlv_type, _, tlv_len = struct.unpack('BBH', tlv) if tlv_type == TLV_VALUES['SIG_PURE']: is_pure = True break - tlv_off += TLV_SIZE + tlv_len + scan_off += TLV_SIZE + tlv_len + if key is not None and not isinstance(key, list): + key = [key] + + verify_results = [] + scan_off = unprot_off digest = None - tlv_off = header_size + img_size - tlv_end = tlv_off + tlv_tot - tlv_off += TLV_INFO_SIZE # skip tlv info - while tlv_off < tlv_end: - tlv = b[tlv_off:tlv_off + TLV_SIZE] + prot_tlv_size = unprot_off - TLV_INFO_SIZE + + # Verify hash and signatures + while scan_off < unprot_end: + tlv = b[scan_off:scan_off + TLV_SIZE] tlv_type, _, tlv_len = struct.unpack('BBH', tlv) if is_sha_tlv(tlv_type): - if not tlv_matches_key_type(tlv_type, key): + if not tlv_matches_key_type(tlv_type, key[0]): return VerifyResult.KEY_MISMATCH, None, None, None - off = tlv_off + TLV_SIZE + off = scan_off + TLV_SIZE digest = get_digest(tlv_type, hash_region) - if digest == b[off:off + tlv_len]: - if key is None: - return VerifyResult.OK, version, digest, None - else: - return VerifyResult.INVALID_HASH, None, None, None - elif not is_pure and key is not None and tlv_type == TLV_VALUES[key.sig_tlv()]: - off = tlv_off + TLV_SIZE - tlv_sig = b[off:off + tlv_len] - payload = b[:prot_tlv_size] - try: - if hasattr(key, 'verify'): - key.verify(tlv_sig, payload) - else: - key.verify_digest(tlv_sig, digest) - return VerifyResult.OK, version, digest, None - except InvalidSignature: - # continue to next TLV - pass + if digest != b[off:off + tlv_len]: + verify_results.append(("Digest", "INVALID_HASH")) + + elif not is_pure and key is not None and tlv_type == TLV_VALUES[key[0].sig_tlv()]: + for idx, k in enumerate(key): + if tlv_type == TLV_VALUES[k.sig_tlv()]: + off = scan_off + TLV_SIZE + tlv_sig = b[off:off + tlv_len] + payload = b[:prot_tlv_size] + try: + if hasattr(k, 'verify'): + k.verify(tlv_sig, payload) + else: + k.verify_digest(tlv_sig, digest) + verify_results.append((f"Key {idx}", "OK")) + break + except InvalidSignature: + # continue to next TLV + verify_results.append((f"Key {idx}", "INVALID_SIGNATURE")) + continue + elif is_pure and key is not None and tlv_type in ALLOWED_PURE_SIG_TLVS: - off = tlv_off + TLV_SIZE + # pure signature verification + off = scan_off + TLV_SIZE tlv_sig = b[off:off + tlv_len] + k = key[0] try: - key.verify_digest(tlv_sig, hash_region) + k.verify_digest(tlv_sig, hash_region) return VerifyResult.OK, version, None, tlv_sig except InvalidSignature: - # continue to next TLV - pass - tlv_off += TLV_SIZE + tlv_len - return VerifyResult.INVALID_SIGNATURE, None, None, None + return VerifyResult.INVALID_SIGNATURE, None, None, None + + scan_off += TLV_SIZE + tlv_len + # Now print out the verification results: + for k, result in verify_results: + print(f"{k}: {result}") + + # Decide on a final return (for example, OK only if at least one signature is valid) + if any(result == "OK" for _, result in verify_results): + return VerifyResult.OK, version, digest, None + else: + return VerifyResult.INVALID_SIGNATURE, None, None, None + def set_key_ids(self, key_ids): """Set list of key IDs (integers) to be inserted before each signature.""" diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index 1cdb792a54..3362c9c36f 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -91,12 +91,14 @@ def load_signature(sigfile): signature = base64.b64decode(f.read()) return signature - -def save_signature(sigfile, sig): +def save_signatures(sigfile, sig): with open(sigfile, 'wb') as f: - signature = base64.b64encode(sig) - f.write(signature) - + if isinstance(sig, list): + for s in sig: + encoded = base64.b64encode(s) + f.write(encoded + b'\n') + else: + f.write(base64.b64encode(sig)) def load_key(keyfile): # TODO: better handling of invalid pass-phrase @@ -222,11 +224,14 @@ def getpriv(key, minimal, format): @click.argument('imgfile') -@click.option('-k', '--key', metavar='filename') +@click.option('-k', '--key', multiple=True, metavar='filename') @click.command(help="Check that signed image can be verified by given key") def verify(key, imgfile): - key = load_key(key) if key else None - ret, version, digest, signature = image.Image.verify(imgfile, key) + if key: + keys = [load_key(k) for k in key] + else: + keys = None + ret, version, digest, signature = image.Image.verify(imgfile, keys) if ret == image.VerifyResult.OK: print("Image was correctly validated") print("Image version: {}.{}.{}+{}".format(*version)) @@ -421,7 +426,7 @@ def convert(self, value, param, ctx): @click.option('--public-key-format', type=click.Choice(['hash', 'full']), default='hash', help='In what format to add the public key to ' 'the image manifest: full key or hash of the key.') -@click.option('-k', '--key', metavar='filename') +@click.option('-k', '--key', multiple=True, metavar='filename') @click.option('--fix-sig', metavar='filename', help='fixed signature for the image. It will be used instead of ' 'the signature calculated using the public key') @@ -441,6 +446,8 @@ def convert(self, value, param, ctx): help='send to OUTFILE the payload or payload''s digest instead ' 'of complied image. These data can be used for external image ' 'signing') +@click.option('--key-ids', multiple=True, type=int, required=False, + help='List of integer key IDs for each signature.') @click.command(help='''Create a signed or unsigned image\n INFILE and OUTFILE are parsed as Intel HEX if the params have .hex extension, otherwise binary format is used''') @@ -450,7 +457,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, dependencies, load_addr, hex_addr, erased_val, save_enctlv, security_counter, boot_record, custom_tlv, rom_fixed, max_align, clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, is_pure, - vector_to_sign, non_bootable): + vector_to_sign, non_bootable, key_ids): if confirm: # Confirmed but non-padded images don't make much sense, because @@ -466,21 +473,28 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, non_bootable=non_bootable) compression_tlvs = {} img.load(infile) - key = load_key(key) if key else None + if key: + loaded_keys = [load_key(k) for k in key] + else: + loaded_keys = None + enckey = load_key(encrypt) if encrypt else None if enckey and key: - if ((isinstance(key, keys.ECDSA256P1) and + first_key = loaded_keys[0] + if ((isinstance(first_key, keys.ECDSA256P1) and not isinstance(enckey, keys.ECDSA256P1Public)) - or (isinstance(key, keys.ECDSA384P1) and + or (isinstance(first_key, keys.ECDSA384P1) and not isinstance(enckey, keys.ECDSA384P1Public)) - or (isinstance(key, keys.RSA) and + or (isinstance(first_key, keys.RSA) and not isinstance(enckey, keys.RSAPublic))): # FIXME raise click.UsageError("Signing and encryption must use the same " "type of key") - if pad_sig and hasattr(key, 'pad_sig'): - key.pad_sig = True + if pad_sig and loaded_keys: + for k in loaded_keys: + if hasattr(k, 'pad_sig'): + k.pad_sig = True # Get list of custom protected TLVs from the command-line custom_tlvs = {} @@ -523,7 +537,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, 'and forbids sha selection by user.') if compression in ["lzma2", "lzma2armthumb"]: - img.create(key, public_key_format, enckey, dependencies, boot_record, + img.create(loaded_keys, public_key_format, enckey, dependencies, boot_record, custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear, baked_signature, pub_key, vector_to_sign, user_sha=user_sha, is_pure=is_pure, keep_comp_size=False, dont_encrypt=True) @@ -572,14 +586,14 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, is_pure=is_pure, keep_comp_size=keep_comp_size) img = compressed_img else: - img.create(key, public_key_format, enckey, dependencies, boot_record, + img.create(loaded_keys, public_key_format, enckey, dependencies, boot_record, custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear, baked_signature, pub_key, vector_to_sign, user_sha=user_sha, is_pure=is_pure) img.save(outfile, hex_addr) if sig_out is not None: new_signature = img.get_signature() - save_signature(sig_out, new_signature) + save_signatures(sig_out, new_signature) class AliasesGroup(click.Group): From 591c3e5521d7069d72bab2c059358dde279b3d1f Mon Sep 17 00:00:00 2001 From: Maulik Patel Date: Mon, 12 May 2025 15:23:49 +0100 Subject: [PATCH 3/4] bootutil: Add support for multi-sign of same type This commit adds functionality to the bootutil library to support multiple sign verfication of same type when 'MCUBOOT_BUILTIN_KEY' or 'MCUBOOT_HW_KEY' is enabled. Signed-off-by: Maulik Patel Change-Id: I05c97ac385c5816c812c51feb010028df8412fe5 --- boot/bootutil/CMakeLists.txt | 48 +- boot/bootutil/include/bootutil/sign_key.h | 3 + boot/bootutil/src/image_multi_sig.c | 536 ++++++++++++++++++++++ boot/bootutil/src/image_validate.c | 2 +- 4 files changed, 566 insertions(+), 23 deletions(-) create mode 100644 boot/bootutil/src/image_multi_sig.c diff --git a/boot/bootutil/CMakeLists.txt b/boot/bootutil/CMakeLists.txt index 3d1b32717e..9fae2ba850 100644 --- a/boot/bootutil/CMakeLists.txt +++ b/boot/bootutil/CMakeLists.txt @@ -14,28 +14,32 @@ target_include_directories(bootutil src ) -target_sources(bootutil - PRIVATE - src/boot_record.c - src/bootutil_misc.c - src/bootutil_public.c - src/caps.c - src/encrypted.c - src/fault_injection_hardening.c - src/fault_injection_hardening_delay_rng_mbedtls.c - src/image_ecdsa.c - src/image_ed25519.c - src/image_rsa.c - src/image_validate.c - src/loader.c - src/swap_misc.c - src/swap_move.c - src/swap_scratch.c - src/tlv.c +set(BOOTUTIL_SOURCES + src/boot_record.c + src/bootutil_misc.c + src/bootutil_public.c + src/caps.c + src/encrypted.c + src/fault_injection_hardening.c + src/fault_injection_hardening_delay_rng_mbedtls.c + src/image_ecdsa.c + src/image_ed25519.c + src/image_rsa.c + src/loader.c + src/swap_misc.c + src/swap_move.c + src/swap_scratch.c + src/tlv.c ) + if(CONFIG_BOOT_RAM_LOAD) - target_sources(bootutil - PRIVATE - src/ram_load.c - ) + list(APPEND BOOTUTIL_SOURCES src/ram_load.c) endif() + +if(MCUBOOT_IMAGE_MULTI_SIG_SUPPORT) + list(APPEND BOOTUTIL_SOURCES src/image_multi_sig.c) +else() + list(APPEND BOOTUTIL_SOURCES src/image_validate.c) +endif() + +target_sources(bootutil PRIVATE ${BOOTUTIL_SOURCES}) diff --git a/boot/bootutil/include/bootutil/sign_key.h b/boot/bootutil/include/bootutil/sign_key.h index ea7df3f016..46bed10fab 100644 --- a/boot/bootutil/include/bootutil/sign_key.h +++ b/boot/bootutil/include/bootutil/sign_key.h @@ -62,6 +62,7 @@ extern struct bootutil_key bootutil_keys[]; * Retrieve the hash of the corresponding public key for image authentication. * * @param[in] image_index Index of the image to be authenticated. + * @param[in] key_index Index of the key to be used. * @param[out] public_key_hash Buffer to store the key-hash in. * @param[in,out] key_hash_size As input the size of the buffer. As output * the actual key-hash length. @@ -69,8 +70,10 @@ extern struct bootutil_key bootutil_keys[]; * @return 0 on success; nonzero on failure. */ int boot_retrieve_public_key_hash(uint8_t image_index, + uint8_t key_index, uint8_t *public_key_hash, size_t *key_hash_size); + #endif /* !MCUBOOT_HW_KEY */ extern const int bootutil_key_cnt; diff --git a/boot/bootutil/src/image_multi_sig.c b/boot/bootutil/src/image_multi_sig.c new file mode 100644 index 0000000000..d284ee8a60 --- /dev/null +++ b/boot/bootutil/src/image_multi_sig.c @@ -0,0 +1,536 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2024 Arm Limited + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include "bootutil/bootutil_log.h" +#include "bootutil/crypto/ecdsa.h" +#include "bootutil/image.h" +#include "bootutil/crypto/sha.h" +#include "bootutil/sign_key.h" +#include "bootutil/security_cnt.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil_priv.h" + +#include "mcuboot_config/mcuboot_config.h" + +#include "mbedtls/ecdsa.h" +#include "mbedtls/asn1.h" + +/* + * Compute SHA hash over the image. + * (SHA384 if ECDSA-P384 is being used, + * SHA256 otherwise). + */ +static int +bootutil_img_hash(struct boot_loader_state *state, + struct image_header *hdr, const struct flash_area *fap, + uint8_t *tmp_buf, uint32_t tmp_buf_sz, uint8_t *hash_result, + uint8_t *seed, int seed_len) +{ + bootutil_sha_context sha_ctx; + uint32_t size; + uint16_t hdr_size; + uint32_t blk_off; + uint32_t tlv_off; + int rc; + uint32_t off; + uint32_t blk_sz; + +#if (BOOT_IMAGE_NUMBER == 1) || defined(MCUBOOT_RAM_LOAD) + (void)state; + (void)hdr_size; + (void)blk_off; + (void)tlv_off; +#ifdef MCUBOOT_RAM_LOAD + (void)blk_sz; + (void)off; + (void)rc; + (void)fap; + (void)tmp_buf; + (void)tmp_buf_sz; +#endif +#endif + + bootutil_sha_init(&sha_ctx); + + /* in some cases (split image) the hash is seeded with data from + * the loader image */ + if (seed && (seed_len > 0)) { + bootutil_sha_update(&sha_ctx, seed, seed_len); + } + + /* Hash is computed over image header and image itself. */ + size = hdr_size = hdr->ih_hdr_size; + size += hdr->ih_img_size; + tlv_off = size; + + /* If protected TLVs are present they are also hashed. */ + size += hdr->ih_protect_tlv_size; + +#ifdef MCUBOOT_RAM_LOAD + bootutil_sha_update(&sha_ctx, + (void*)(IMAGE_RAM_BASE + hdr->ih_load_addr), + size); +#else + for (off = 0; off < size; off += blk_sz) { + blk_sz = size - off; + if (blk_sz > tmp_buf_sz) { + blk_sz = tmp_buf_sz; + } + rc = flash_area_read(fap, off, tmp_buf, blk_sz); + if (rc) { + bootutil_sha_drop(&sha_ctx); + return rc; + } + bootutil_sha_update(&sha_ctx, tmp_buf, blk_sz); + } +#endif /* MCUBOOT_RAM_LOAD */ + + bootutil_sha_finish(&sha_ctx, hash_result); + bootutil_sha_drop(&sha_ctx); + + return 0; +} + +#if defined(MCUBOOT_SIGN_EC256) || \ + defined(MCUBOOT_SIGN_EC384) || \ + defined(MCUBOOT_SIGN_EC) +# define EXPECTED_SIG_TLV IMAGE_TLV_ECDSA_SIG +# define SIG_BUF_SIZE 128 +# define EXPECTED_SIG_LEN(x) (1) /* always true, ASN.1 will validate */ +#else +#error "Unsupported signature algorithm for multi signature support" +#endif + +#if ((defined(MCUBOOT_HW_KEY) + defined(MCUBOOT_BUILTIN_KEY) > 1) || \ + (!defined(MCUBOOT_HW_KEY) && !defined(MCUBOOT_BUILTIN_KEY))) +#error "Please use either MCUBOOT_HW_KEY or the MCUBOOT_BUILTIN_KEY feature." +#endif + +#if defined(MCUBOOT_HW_KEY) +/* The key TLV contains the whole public key. + * Add a few extra bytes to the key buffer size for encoding and + * for public exponent. + */ +#define EXPECTED_KEY_TLV IMAGE_TLV_PUBKEY +#define KEY_BUF_SIZE (SIG_BUF_SIZE + 24) +extern unsigned int pub_key_len; +static int +bootutil_find_key(uint8_t image_index, uint8_t *key, uint16_t key_len) +{ + bootutil_sha_context sha_ctx; + uint8_t key_index = 0; + uint8_t hash[IMAGE_HASH_SIZE]; + uint8_t key_hash[IMAGE_HASH_SIZE]; + size_t key_hash_size = sizeof(key_hash); + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + bootutil_sha_init(&sha_ctx); + bootutil_sha_update(&sha_ctx, key, key_len); + bootutil_sha_finish(&sha_ctx, hash); + bootutil_sha_drop(&sha_ctx); + + for(key_index = 0; key_index < 2; key_index++) { + rc = boot_retrieve_public_key_hash(image_index, key_index, key_hash, &key_hash_size); + if (rc) { + return -1; + } + + /* Adding hardening to avoid this potential attack: + * - Image is signed with an arbitrary key and the corresponding public + * key is added as a TLV field. + * - During public key validation (comparing against key-hash read from + * HW) a fault is injected to accept the public key as valid one. + */ + FIH_CALL(boot_fih_memequal, fih_rc, hash, key_hash, key_hash_size); + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_LOG_INF("Key %d hash found for image %d", key_index, image_index); + bootutil_keys[0].key = key; + pub_key_len = key_len; + return 0; + } + } + + BOOT_LOG_ERR("Key hash not found for image %d", image_index); + return -1; +} + +#endif /* MCUBOOT_HW_KEY */ + + +#if defined(MCUBOOT_BUILTIN_KEY) +/* For MCUBOOT_BUILTIN_KEY, key id is passed */ +#define EXPECTED_KEY_TLV IMAGE_TLV_KEYID +#define KEY_BUF_SIZE sizeof(int) + +static int bootutil_find_key(uint8_t *key_id_buf, uint8_t key_id_buf_len) +{ + /* Key id is passed */ + assert(key_id_buf_len == sizeof(int32_t)); + return ((int32_t)key_id_buf[0] << 24) | + ((int32_t)key_id_buf[1] << 16) | + ((int32_t)key_id_buf[2] << 8) | + ((int32_t)key_id_buf[3]); +} +#endif /* !MCUBOOT_BUILTIN_KEY */ + +/** + * Reads the value of an image's security counter. + * + * @param state Pointer to the boot state object. + * @param slot Slot of the current image to get the security counter of. + * @param fap Pointer to a description structure of the image's + * flash area. + * @param security_cnt Pointer to store the security counter value. + * + * @return 0 on success; nonzero on failure. + */ +int32_t +bootutil_get_img_security_cnt(struct boot_loader_state *state, int slot, + const struct flash_area *fap, + uint32_t *img_security_cnt) +{ + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + if ((state == NULL) || + (boot_img_hdr(state, slot) == NULL) || + (fap == NULL) || + (img_security_cnt == NULL)) { + /* Invalid parameter. */ + return BOOT_EBADARGS; + } + + /* The security counter TLV is in the protected part of the TLV area. */ + if (boot_img_hdr(state, slot)->ih_protect_tlv_size == 0) { + return BOOT_EBADIMAGE; + } + + rc = bootutil_tlv_iter_begin(&it, boot_img_hdr(state, slot), fap, IMAGE_TLV_SEC_CNT, true); + if (rc) { + return rc; + } + + /* Traverse through the protected TLV area to find + * the security counter TLV. + */ + + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + if (rc != 0) { + /* Security counter TLV has not been found. */ + return -1; + } + + if (len != sizeof(*img_security_cnt)) { + /* Security counter is not valid. */ + return BOOT_EBADIMAGE; + } + + rc = LOAD_IMAGE_DATA(boot_img_hdr(state, slot), fap, off, img_security_cnt, len); + if (rc != 0) { + return BOOT_EFLASH; + } + + return 0; +} + +#ifndef ALLOW_ROGUE_TLVS +/* + * The following list of TLVs are the only entries allowed in the unprotected + * TLV section. All other TLV entries must be in the protected section. + */ +static const uint16_t allowed_unprot_tlvs[] = { + IMAGE_TLV_KEYHASH, + IMAGE_TLV_PUBKEY, + IMAGE_TLV_KEYID, + IMAGE_TLV_SHA256, + IMAGE_TLV_SHA384, + IMAGE_TLV_SHA512, + IMAGE_TLV_RSA2048_PSS, + IMAGE_TLV_ECDSA224, + IMAGE_TLV_ECDSA_SIG, + IMAGE_TLV_RSA3072_PSS, + IMAGE_TLV_ED25519, + IMAGE_TLV_ENC_RSA2048, + IMAGE_TLV_ENC_KW, + IMAGE_TLV_ENC_EC256, + IMAGE_TLV_ENC_X25519, + /* Mark end with ANY. */ + IMAGE_TLV_ANY, +}; +#endif + +/* + * Verify the integrity of the image. + * Return non-zero if image could not be validated/does not validate. + */ +fih_ret +bootutil_img_validate(struct boot_loader_state *state, + struct image_header *hdr, const struct flash_area *fap, + uint8_t *tmp_buf, uint32_t tmp_buf_sz, uint8_t *seed, + int seed_len, uint8_t *out_hash) +{ +#if (defined(EXPECTED_KEY_TLV) && defined(MCUBOOT_HW_KEY)) || defined(MCUBOOT_HW_ROLLBACK_PROT) + int image_index = (state == NULL ? 0 : BOOT_CURR_IMG(state)); +#endif + uint32_t off; + uint16_t len; + uint16_t type; + FIH_DECLARE(valid_signature, FIH_FAILURE); + int key_id = -1; +#ifdef MCUBOOT_HW_KEY + uint8_t key_buf[KEY_BUF_SIZE]; +#endif + struct image_tlv_iter it; + uint8_t buf[SIG_BUF_SIZE]; +#if defined(EXPECTED_HASH_TLV) + int image_hash_valid = 0; + uint8_t hash[IMAGE_HASH_SIZE]; +#endif + int rc = 0; + FIH_DECLARE(fih_rc, FIH_FAILURE); +#ifdef MCUBOOT_HW_ROLLBACK_PROT + fih_int security_cnt = fih_int_encode(INT_MAX); + uint32_t img_security_cnt = 0; + FIH_DECLARE(security_counter_valid, FIH_FAILURE); +#endif + bool key_must_sign = true; + bool key_might_sign = false; + uint8_t key_must_sign_count = 0; + +#if defined(EXPECTED_HASH_TLV) + rc = bootutil_img_hash(state, hdr, fap, tmp_buf, tmp_buf_sz, hash, seed, seed_len); + if (rc) { + goto out; + } + + if (out_hash) { + memcpy(out_hash, hash, IMAGE_HASH_SIZE); + } +#endif /* defined(EXPECTED_HASH_TLV) */ + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + /* + * Traverse through all of the TLVs, performing any checks we know + * and are able to do. + */ + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + +#ifndef ALLOW_ROGUE_TLVS + /* + * Ensure that the non-protected TLV only has entries necessary to hold + * the signature. We also allow encryption related keys to be in the + * unprotected area. + */ + if (!bootutil_tlv_iter_is_prot(&it, off)) { + bool found = false; + for (const uint16_t *p = allowed_unprot_tlvs; *p != IMAGE_TLV_ANY; p++) { + if (type == *p) { + found = true; + break; + } + } + if (!found) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } +#endif + switch(type) { +#if defined(EXPECTED_HASH_TLV) + case EXPECTED_HASH_TLV: + { + /* Verify the image hash. This must always be present. */ + if (len != sizeof(hash)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, sizeof(hash)); + if (rc) { + goto out; + } + + FIH_CALL(boot_fih_memequal, fih_rc, hash, buf, sizeof(hash)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + image_hash_valid = 1; + break; + } +#endif /* defined(EXPECTED_HASH_TLV) */ + +#ifdef EXPECTED_KEY_TLV + case EXPECTED_KEY_TLV: + { + /* + * Determine which key we should be checking. + */ + if (len > KEY_BUF_SIZE) { + rc = -1; + goto out; + } +#ifndef MCUBOOT_HW_KEY + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(buf, len); +#else + rc = LOAD_IMAGE_DATA(hdr, fap, off, key_buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(image_index, key_buf, len); +#endif /* !MCUBOOT_HW_KEY */ + /* + * The key may not be found, which is acceptable. There + * can be multiple signatures, each preceded by a key. + */ + break; + } +#endif /* EXPECTED_KEY_TLV */ + case EXPECTED_SIG_TLV: + { + if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + + FIH_CALL(bootutil_verify_sig, valid_signature, hash, sizeof(hash), + buf, len, key_id); + + rc = boot_plat_check_key_policy((valid_signature == 0), key_id, + &key_might_sign, &key_must_sign, + &key_must_sign_count); + if (rc) { + goto out; + } + key_id = -1; + break; + } +#ifdef MCUBOOT_HW_ROLLBACK_PROT + case IMAGE_TLV_SEC_CNT: + { + /* + * Verify the image's security counter. + * This must always be present. + */ + if (len != sizeof(img_security_cnt)) { + /* Security counter is not valid. */ + rc = -1; + goto out; + } + + rc = LOAD_IMAGE_DATA(hdr, fap, off, &img_security_cnt, len); + if (rc) { + goto out; + } + + FIH_CALL(boot_nv_security_counter_get, fih_rc, image_index, + &security_cnt); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + /* Compare the new image's security counter value against the + * stored security counter value. + */ + fih_rc = fih_ret_encode_zero_equality(img_security_cnt < + (uint32_t)fih_int_decode(security_cnt)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + /* The image's security counter has been successfully verified. */ + security_counter_valid = fih_rc; + break; + } +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + } + } + +#if defined(EXPECTED_HASH_TLV) + rc = !image_hash_valid; + if (rc) { + goto out; + } +#endif + if (FIH_NOT_EQ(key_must_sign, true) || FIH_NOT_EQ(key_might_sign, true) || + FIH_EQ(key_must_sign_count, 0)) { + FIH_RET(FIH_FAILURE); + FIH_SET(fih_rc, FIH_FAILURE); + } else { + FIH_RET(FIH_SUCCESS); + } +#ifdef MCUBOOT_HW_ROLLBACK_PROT + if (FIH_NOT_EQ(security_counter_valid, FIH_SUCCESS)) { + rc = -1; + goto out; + } +#endif + +out: + if (rc) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c index e308a968f5..5589912762 100644 --- a/boot/bootutil/src/image_validate.c +++ b/boot/bootutil/src/image_validate.c @@ -314,7 +314,7 @@ bootutil_find_key(uint8_t image_index, uint8_t *key, uint16_t key_len) bootutil_sha_finish(&sha_ctx, hash); bootutil_sha_drop(&sha_ctx); - rc = boot_retrieve_public_key_hash(image_index, key_hash, &key_hash_size); + rc = boot_retrieve_public_key_hash(image_index, 0, key_hash, &key_hash_size); if (rc) { return -1; } From e0027a2cbf0e0d74851c95d9d73fe1aa9c5e6361 Mon Sep 17 00:00:00 2001 From: Maulik Patel Date: Fri, 16 May 2025 06:56:56 +0100 Subject: [PATCH 4/4] imgtool: Rename key-ids to psa-key-ids Since the key id concept in the PSA specific, rename the variables accordingly. Signed-off-by: Maulik Patel Change-Id: I8a8a5ceba5554211f185cc4045a6081b6d407507 --- scripts/imgtool/image.py | 10 +++++----- scripts/imgtool/main.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index b2f7cff637..4cc4851b99 100644 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -297,7 +297,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, self.enctlv_len = 0 self.max_align = max(DEFAULT_MAX_ALIGN, align) if max_align is None else int(max_align) self.non_bootable = non_bootable - self.key_ids = None + self.psa_key_ids = None if self.max_align == DEFAULT_MAX_ALIGN: self.boot_magic = bytes([ @@ -663,9 +663,9 @@ def create(self, keys, public_key_format, enckey, dependencies=None, self.signatures = [] for i, key in enumerate(keys): # If key IDs are provided, and we have enough for this key, add it first. - if self.key_ids is not None and len(self.key_ids) > i: + if self.psa_key_ids is not None and len(self.psa_key_ids) > i: # Convert key id (an integer) to 4-byte big-endian bytes. - kid_bytes = self.key_ids[i].to_bytes(4, 'big') + kid_bytes = self.psa_key_ids[i].to_bytes(4, 'big') tlv.add('KEYID', kid_bytes) # Using the TLV tag that corresponds to key IDs. if public_key_format == 'hash': @@ -937,9 +937,9 @@ def verify(imgfile, key): return VerifyResult.INVALID_SIGNATURE, None, None, None - def set_key_ids(self, key_ids): + def set_key_ids(self, psa_key_ids): """Set list of key IDs (integers) to be inserted before each signature.""" - self.key_ids = key_ids + self.psa_key_ids = psa_key_ids def _add_key_id_tlv_to_unprotected(self, tlv, key_id: int): """Add a key ID TLV into the *unprotected* TLV area.""" diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index 3362c9c36f..6db456ce30 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -446,7 +446,7 @@ def convert(self, value, param, ctx): help='send to OUTFILE the payload or payload''s digest instead ' 'of complied image. These data can be used for external image ' 'signing') -@click.option('--key-ids', multiple=True, type=int, required=False, +@click.option('--psa-key-ids', multiple=True, type=int, required=False, help='List of integer key IDs for each signature.') @click.command(help='''Create a signed or unsigned image\n INFILE and OUTFILE are parsed as Intel HEX if the params have @@ -457,7 +457,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, dependencies, load_addr, hex_addr, erased_val, save_enctlv, security_counter, boot_record, custom_tlv, rom_fixed, max_align, clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, is_pure, - vector_to_sign, non_bootable, key_ids): + vector_to_sign, non_bootable, psa_key_ids): if confirm: # Confirmed but non-padded images don't make much sense, because