imgtool: Add CBOR encoded boot record to TLV area

Add new '--boot-record' option for imgtool to add a new type of TLV to
the image manifest called BOOT_RECORD. This TLV contains CBOR encoded
data with some basic information about the image (SW component) it
belongs to, these are the following:
- SW type (role of the software component)
- SW version
- Signer ID (identifies the signing authority)
- Measurement value (hash of the image)
- Measurement type (algorithm used to calculate the measurement value)

The boot_record.py file and most of the modifications in image.py are
coming from the Trusted Firmware-M project
(https://www.trustedfirmware.org/about/).
Hash of the source commit: 08d5572b4bcee306d8cf709c2200359a22d5b72c.

This patch is based on the recommendations of Arm's Platform Security
Architecture (PSA) and its purpose is to support compliance with it.

Change-Id: I379ccc57b48ad2311837cb3fd90f5f9d1c9b5bac
Signed-off-by: David Vincze <david.vincze@linaro.org>
diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py
index 9701e21..644a028 100644
--- a/scripts/imgtool/image.py
+++ b/scripts/imgtool/image.py
@@ -19,6 +19,7 @@
 """
 
 from . import version as versmod
+from .boot_record import create_sw_component_data
 import click
 from enum import Enum
 from intelhex import IntelHex
@@ -42,6 +43,7 @@
 MAX_ALIGN = 8
 DEP_IMAGES_KEY = "images"
 DEP_VERSIONS_KEY = "versions"
+MAX_SW_TYPE_LENGTH = 12  # Bytes
 
 # Image header flags.
 IMAGE_F = {
@@ -63,6 +65,7 @@
         'ENCEC256': 0x32,
         'DEPENDENCY': 0x40,
         'SEC_CNT': 0x50,
+        'BOOT_RECORD': 0x60,
 }
 
 TLV_SIZE = 4
@@ -256,9 +259,18 @@
             format=PublicFormat.UncompressedPoint)
         return cipherkey, ciphermac, pubk
 
-    def create(self, key, enckey, dependencies=None):
+    def create(self, key, enckey, dependencies=None, sw_type=None):
         self.enckey = enckey
 
+        # Calculate the hash of the public key
+        if key is not None:
+            pub = key.get_public_bytes()
+            sha = hashlib.sha256()
+            sha.update(pub)
+            pubbytes = sha.digest()
+        else:
+            pubbytes = bytes(hashlib.sha256().digest_size)
+
         protected_tlv_size = 0
 
         if self.security_counter is not None:
@@ -266,6 +278,32 @@
             #                                   = 4 + 4 = 8 Bytes
             protected_tlv_size += TLV_SIZE + 4
 
+        if sw_type is not None:
+            if len(sw_type) > MAX_SW_TYPE_LENGTH:
+                msg = "'{}' is too long ({} characters) for sw_type. Its " \
+                      "maximum allowed length is 12 characters.".format(
+                       sw_type, len(sw_type))
+                raise click.UsageError(msg)
+
+            image_version = (str(self.version.major) + '.'
+                             + str(self.version.minor) + '.'
+                             + str(self.version.revision))
+
+            # The image hash is computed over the image header, the image
+            # itself and the protected TLV area. However, the boot record TLV
+            # (which is part of the protected area) should contain this hash
+            # before it is even calculated. For this reason the script fills
+            # this field with zeros and the bootloader will insert the right
+            # value later.
+            digest = bytes(hashlib.sha256().digest_size)
+
+            # Create CBOR encoded boot record
+            boot_record = create_sw_component_data(sw_type, image_version,
+                                                   "SHA256", digest,
+                                                   pubbytes)
+
+            protected_tlv_size += TLV_SIZE + len(boot_record)
+
         if dependencies is not None:
             # Size of a Dependency TLV = Header ('HH') + Payload('IBBHI')
             # = 4 + 12 = 16 Bytes
@@ -293,6 +331,9 @@
                 payload = struct.pack(e + 'I', self.security_counter)
                 prot_tlv.add('SEC_CNT', payload)
 
+            if sw_type is not None:
+                prot_tlv.add('BOOT_RECORD', boot_record)
+
             if dependencies is not None:
                 for i in range(dependencies_num):
                     payload = struct.pack(
@@ -319,10 +360,6 @@
         tlv.add('SHA256', digest)
 
         if key is not None:
-            pub = key.get_public_bytes()
-            sha = hashlib.sha256()
-            sha.update(pub)
-            pubbytes = sha.digest()
             tlv.add('KEYHASH', pubbytes)
 
             # `sign` expects the full image payload (sha256 done internally),