Boot: Add measured boot record TLV to image manifest

Generate CBOR encoded boot record structure at build time and add as a
new type of TLV to the image manifest for device attestation. This new
TLV can represent the image to which it belongs as one item of the
SW_COMPONENTS array in an IAT token.

Change-Id: I84adf0faa57c40428c3e48cce4398e346d9192b7
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/bl2/ext/mcuboot/scripts/imgtool_lib/image.py b/bl2/ext/mcuboot/scripts/imgtool_lib/image.py
index cb97a35..d89ec99 100644
--- a/bl2/ext/mcuboot/scripts/imgtool_lib/image.py
+++ b/bl2/ext/mcuboot/scripts/imgtool_lib/image.py
@@ -18,6 +18,7 @@
 """
 
 from . import version as versmod
+from . import boot_record as br
 import hashlib
 import struct
 
@@ -41,7 +42,8 @@
         'RSA2048': 0x20,
         'RSA3072': 0x23,
         'DEPENDENCY': 0x40,
-        'SEC_CNT': 0x50, }
+        'SEC_CNT': 0x50,
+        'BOOT_RECORD': 0x60, }
 
 TLV_INFO_SIZE = 4
 TLV_INFO_MAGIC = 0x6907
@@ -116,10 +118,39 @@
             if any(v != 0 and v != b'\000' for v in self.payload[0:self.header_size]):
                 raise Exception("Padding requested, but image does not start with zeros")
 
-    def sign(self, key, ramLoadAddress, dependencies=None):
-        # Size of the security counter TLV:
-        # header ('BBH') + payload ('I') = 8 Bytes
-        protected_tlv_size = TLV_INFO_SIZE + 8
+    def sign(self, sw_type, key, ramLoadAddress, dependencies=None):
+        image_version = (str(self.version.major) + '.'
+                      + str(self.version.minor) + '.'
+                      + str(self.version.revision))
+
+        # 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(KEYHASH_SIZE)
+
+        # 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.
+        image_hash = bytes(PAYLOAD_DIGEST_SIZE)
+
+        # Create CBOR encoded boot record
+        boot_record = br.create_sw_component_data(sw_type, image_version,
+                                                  "SHA256", image_hash,
+                                                  pubbytes)
+
+        # Mandatory protected TLV area: TLV info header
+        #                               + security counter TLV
+        #                               + boot record TLV
+        # Size of the security counter TLV: header ('BBH') + payload ('I')
+        #                                   = 8 Bytes
+        protected_tlv_size = TLV_INFO_SIZE + 8 + TLV_HEADER_SIZE \
+                           + len(boot_record)
 
         if dependencies is None:
             dependencies_num = 0
@@ -135,6 +166,7 @@
 
         payload = struct.pack('I', self.security_cnt)
         tlv.add('SEC_CNT', payload)
+        tlv.add('BOOT_RECORD', boot_record)
 
         if dependencies_num != 0:
             for i in range(dependencies_num):
@@ -153,7 +185,6 @@
         full_size = (TLV_INFO_SIZE + len(tlv.buf) + TLV_HEADER_SIZE
                      + PAYLOAD_DIGEST_SIZE)
         if key is not None:
-            pub = key.get_public_bytes()
             if key.get_public_key_format() == 'hash':
                 tlv_key_data_size = KEYHASH_SIZE
             else:
@@ -166,15 +197,12 @@
 
         sha = hashlib.sha256()
         sha.update(self.payload)
-        digest = sha.digest()
+        image_hash = sha.digest()
 
-        tlv.add('SHA256', digest)
+        tlv.add('SHA256', image_hash)
 
         if key is not None:
             if key.get_public_key_format() == 'hash':
-                sha = hashlib.sha256()
-                sha.update(pub)
-                pubbytes = sha.digest()
                 tlv.add('KEYHASH', pubbytes)
             else:
                 tlv.add('KEY', pub)