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)