Infineon: Add cyw20829 platform, shared slot feature, json memory map, psoc6 xip
Based in 1.8.0 release of MCUBoot library
This commit adds CYW20829 Infineon platform support with following capabilities:
1. Overwrite and swap upgrade mode support
2. Multi-image with up to 4 images
3. Hardware security counter is supported for CYW20829 platform
Add XIP support for PSOC6 platform - place BOOT slot in external memory and execute it in place using SMIF in XIP mode
and some new features for Infineon devices.
1. Shared upgrade slot feature - use one shared area for upgrade slots of multiple images
2. Memory map defined using JSON file - define memory regions for bootloader and user app in conventional way using JSON file
diff --git a/scripts/assemble.py b/scripts/assemble.py
index 5ef403f..0f39fcc 100755
--- a/scripts/assemble.py
+++ b/scripts/assemble.py
@@ -127,14 +127,14 @@
print('Need to either have ZEPHYR_BASE in environment or pass in -z')
sys.exit(1)
- sys.path.insert(0, os.path.join(zephyr_base, "scripts", "dts"))
- import edtlib
+ sys.path.insert(0, os.path.join(zephyr_base, "scripts", "dts", "python-devicetree", "src"))
+ import devicetree.edtlib
board = find_board_name(args.bootdir)
dts_path = os.path.join(args.bootdir, "zephyr", board + ".dts.pre.tmp")
- edt = edtlib.EDT(dts_path, [os.path.join(zephyr_base, "dts", "bindings")],
+ edt = devicetree.edtlib.EDT(dts_path, [os.path.join(zephyr_base, "dts", "bindings")],
warn_reg_unit_address_mismatch=False)
output = Assembly(args.output, args.bootdir, edt)
diff --git a/scripts/imgtool/__init__.py b/scripts/imgtool/__init__.py
index 4f19399..ca43b8d 100644
--- a/scripts/imgtool/__init__.py
+++ b/scripts/imgtool/__init__.py
@@ -14,4 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-imgtool_version = "1.7.0"
+imgtool_version = "1.8.0"
diff --git a/scripts/imgtool/encrypt_mxs40sv2.py b/scripts/imgtool/encrypt_mxs40sv2.py
new file mode 100644
index 0000000..f95a3a5
--- /dev/null
+++ b/scripts/imgtool/encrypt_mxs40sv2.py
@@ -0,0 +1,110 @@
+"""
+Copyright (c) 2021 Cypress Semiconductor Corporation
+
+Licensed 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.
+"""
+import os
+import struct
+from cryptography.hazmat.primitives.ciphers import (
+ Cipher, algorithms, modes
+)
+
+NONCE_SIZE = 12
+
+class EncryptorMXS40Sv2:
+ def __init__(self, key, nonce, initial_counter=0):
+ # with open(key_path, 'rb') as f:
+ # self.key = f.read()
+ self.key = key
+ self.nonce = nonce
+ self.counter = 0
+ self.initial_counter = initial_counter
+ cipher = Cipher(algorithms.AES(key), modes.ECB())
+ self.encryptor = cipher.encryptor()
+
+ def _load(self, image_path):
+ with open(image_path, 'rb') as f:
+ image = f.read()
+ return image
+
+ def _save(self, data, output_path):
+ with open(output_path, 'wb') as f:
+ f.write(data)
+
+ def update(self, data):
+ """
+ Encrypts a byte array using a customized AES-CTR mode
+ where a counter is incremented by 16 per block.
+ A nonce format is (128 bit):
+ bits 0...31 - counter + initial values
+ bits 32...127 - random nonce
+ """
+ chunk_size = 16
+ counter = self.counter
+ ciphertext = bytes()
+ for i in range(0, len(image), chunk_size):
+ indata = struct.pack('<I', initial_counter + counter) + nonce
+ counter += chunk_size
+ cipher_block = self.encryptor.update(indata)
+ chunk = image[i:i + chunk_size]
+ ciphertext += bytes(a ^ b for a, b in zip(chunk, cipher_block))
+ self.counter = counter
+ return ciphertext
+
+ def encrypt(self, image, initial_counter=0):
+ """
+ Encrypts a byte array using a customized AES-CTR mode
+ where a counter is incremented by 16 per block.
+ A nonce format is (128 bit):
+ bits 0...31 - counter + initial values
+ bits 32...127 - random nonce
+ """
+ chunk_size = 16
+ counter = 0
+ # self.initial_counter = initial_counter
+ # counter = 0 if initial_counter is None else int(initial_counter, 0)
+ # counter = initial_counter
+ ciphertext = bytes()
+ for i in range(0, len(image), chunk_size):
+ indata = struct.pack('<I', initial_counter + counter) + self.nonce[:12]
+ counter += chunk_size
+ cipher_block = self.encryptor.update(indata)
+ chunk = image[i:i + chunk_size]
+ ciphertext += bytes(a ^ b for a, b in zip(chunk, cipher_block))
+ self.encryptor.finalize()
+ # return ciphertext, nonce
+ return ciphertext
+
+ def encrypt_image(self, input_path, initial_counter=None, output_path=None, nonce_path=None):
+ """
+ Encrypts an image each time using new random nonce.
+ Saves the nonce and the encrypted image to specified locations.
+ If the output locations are not given the output files are saved
+ in the same location as the input image with predefined names.
+ """
+ image = self._load(input_path)
+
+ nonce = os.urandom(NONCE_SIZE)
+ init = 0 if initial_counter is None else int(initial_counter, 0)
+ ciphertext, nonce = self._encrypt(image, nonce, init)
+
+ if output_path is None:
+ output_path = '{0}_{2}{1}'.format(*os.path.splitext(input_path) + ('encrypted',))
+
+ if nonce_path is None:
+ nonce_path = '{0}_{2}{1}'.format(*os.path.splitext(input_path) + ('nonce',))
+
+ self._save(ciphertext, output_path)
+ self._save(nonce, nonce_path)
+
+ return output_path, nonce_path
diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py
index ec18fa5..2a5eb59 100644
--- a/scripts/imgtool/image.py
+++ b/scripts/imgtool/image.py
@@ -1,6 +1,6 @@
# Copyright 2018 Nordic Semiconductor ASA
# Copyright 2017-2020 Linaro Limited
-# Copyright 2019-2020 Arm Limited
+# Copyright 2019-2021 Arm Limited
#
# SPDX-License-Identifier: Apache-2.0
#
@@ -51,7 +51,8 @@
# Image header flags.
IMAGE_F = {
'PIC': 0x0000001,
- 'ENCRYPTED': 0x0000004,
+ 'ENCRYPTED_AES128': 0x0000004,
+ 'ENCRYPTED_AES256': 0x0000008,
'NON_BOOTABLE': 0x0000010,
'RAM_LOAD': 0x0000020,
'ROM_FIXED': 0x0000100,
@@ -67,7 +68,7 @@
'RSA3072': 0x23,
'ED25519': 0x24,
'ENCRSA2048': 0x30,
- 'ENCKW128': 0x31,
+ 'ENCKW': 0x31,
'ENCEC256': 0x32,
'ENCX25519': 0x33,
'DEPENDENCY': 0x40,
@@ -296,7 +297,7 @@
return cipherkey, ciphermac, pubk
def create(self, key, public_key_format, enckey, dependencies=None,
- sw_type=None, custom_tlvs=None, use_random_iv=False):
+ sw_type=None, custom_tlvs=None, encrypt_keylen=128, use_random_iv=False):
self.enckey = enckey
if use_random_iv:
@@ -373,7 +374,10 @@
self.payload.extend(pad)
# This adds the header to the payload as well
- self.add_header(enckey, protected_tlv_size)
+ if encrypt_keylen == 256:
+ self.add_header(enckey, protected_tlv_size, 256)
+ else:
+ self.add_header(enckey, protected_tlv_size)
prot_tlv = TLV(self.endian, TLV_PROT_INFO_MAGIC)
@@ -441,7 +445,10 @@
self.payload = self.payload[:protected_tlv_off]
if enckey is not None:
- plainkey = os.urandom(16)
+ if encrypt_keylen == 256:
+ plainkey = os.urandom(32)
+ else:
+ plainkey = os.urandom(16)
if isinstance(enckey, rsa.RSAPublic):
cipherkey = enckey._get_public().encrypt(
@@ -476,12 +483,15 @@
self.check_trailer()
- def add_header(self, enckey, protected_tlv_size):
+ def add_header(self, enckey, protected_tlv_size, aes_length=128):
"""Install the image header."""
flags = 0
if enckey is not None:
- flags |= IMAGE_F['ENCRYPTED']
+ if aes_length == 128:
+ flags |= IMAGE_F['ENCRYPTED_AES128']
+ else:
+ flags |= IMAGE_F['ENCRYPTED_AES256']
if self.load_addr != 0:
# Indicates that this image should be loaded into RAM
# instead of run directly from flash.
diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py
index 53f4d98..941c096 100755
--- a/scripts/imgtool/main.py
+++ b/scripts/imgtool/main.py
@@ -1,7 +1,7 @@
#! /usr/bin/env python3
#
# Copyright 2017-2020 Linaro Limited
-# Copyright 2019-2020 Arm Limited
+# Copyright 2019-2021 Arm Limited
#
# SPDX-License-Identifier: Apache-2.0
#
@@ -250,6 +250,10 @@
@click.option('-E', '--encrypt', metavar='filename',
help='Encrypt image using the provided public key. '
'(Not supported in direct-xip or ram-load mode.)')
+@click.option('--encrypt-keylen', default='128',
+ type=click.Choice(['128','256']),
+ help='When encrypting the image using AES, select a 128 bit or '
+ '256 bit key len.')
@click.option('-e', '--endian', type=click.Choice(['little', 'big']),
default='little', help="Select little or big endian")
@click.option('--overwrite-only', default=False, is_flag=True,
@@ -298,9 +302,9 @@
.hex extension, otherwise binary format is used''')
def sign(key, public_key_format, align, version, pad_sig, header_size,
pad_header, slot_size, pad, confirm, max_sectors, overwrite_only,
- endian, encrypt, infile, outfile, dependencies, load_addr, hex_addr,
- erased_val, save_enctlv, security_counter, boot_record, custom_tlv,
- rom_fixed, use_random_iv):
+ endian, encrypt_keylen, encrypt, infile, outfile, dependencies,
+ load_addr, hex_addr, erased_val, save_enctlv, security_counter,
+ boot_record, custom_tlv, rom_fixed, use_random_iv):
if confirm:
# Confirmed but non-padded images don't make much sense, because
@@ -347,7 +351,7 @@
custom_tlvs[tag] = value.encode('utf-8')
img.create(key, public_key_format, enckey, dependencies, boot_record,
- custom_tlvs, use_random_iv=use_random_iv)
+ custom_tlvs, int(encrypt_keylen), use_random_iv=use_random_iv)
img.save(outfile, hex_addr)