imgtool: Add support for dependency description

This commit aims to add the ability to specify and add dependency
TLVs to MCUBOOT. Due to the private nature of this feature, having
dependency TLVs mean that the TLV Info header and these TLVs become
part of the protected area (they are supposed to get signed as well).
Since the TLV Info header containing the whole TLV section's size
becomes protected, this size needs to be calculated in advance to get
proper hash values.

Change-Id: I13277a3b595acc2bb8c5084420f3d61c8d301dc2
Author: Bence Kaposzta <bence.kaposzta@arm.com>
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py
index 3ccd86f..c36f802 100644
--- a/scripts/imgtool/image.py
+++ b/scripts/imgtool/image.py
@@ -1,5 +1,6 @@
 # Copyright 2018 Nordic Semiconductor ASA
 # Copyright 2017 Linaro Limited
+# Copyright 2019 Arm Limited
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -32,6 +33,8 @@
 BIN_EXT = "bin"
 INTEL_HEX_EXT = "hex"
 DEFAULT_MAX_SECTORS = 128
+DEP_IMAGES_KEY = "images"
+DEP_VERSIONS_KEY = "versions"
 
 # Image header flags.
 IMAGE_F = {
@@ -48,6 +51,7 @@
         'ECDSA256': 0x22,
         'ENCRSA2048': 0x30,
         'ENCKW128': 0x31,
+        'DEPENDENCY': 0x40
 }
 
 TLV_INFO_SIZE = 4
@@ -168,11 +172,49 @@
                         len(self.payload), tsize, self.slot_size)
                 raise Exception(msg)
 
-    def create(self, key, enckey):
-        self.add_header(enckey)
+    def create(self, key, enckey, dependencies=None):
+        if dependencies is None:
+            dependencies_num = 0
+            protected_tlv_size = 0
+        else:
+            # Size of a Dependency TLV = Header ('BBH') + Payload('IBBHI')
+            # = 16 Bytes
+            dependencies_num = len(dependencies[DEP_IMAGES_KEY])
+            protected_tlv_size = (dependencies_num * 16) + TLV_INFO_SIZE
+
+        self.add_header(enckey, protected_tlv_size)
 
         tlv = TLV(self.endian)
 
+        if protected_tlv_size != 0:
+            for i in range(dependencies_num):
+                e = STRUCT_ENDIAN_DICT[self.endian]
+                payload = struct.pack(
+                                e + 'I'+'BBHI',
+                                int(dependencies[DEP_IMAGES_KEY][i]),
+                                dependencies[DEP_VERSIONS_KEY][i].major,
+                                dependencies[DEP_VERSIONS_KEY][i].minor,
+                                dependencies[DEP_VERSIONS_KEY][i].revision,
+                                dependencies[DEP_VERSIONS_KEY][i].build
+                                )
+                tlv.add('DEPENDENCY', payload)
+            # Full TLV size needs to be calculated in advance, because the
+            # header will be protected as well
+            tlv_header_size = 4
+            payload_digest_size = 32
+            keyhash_size = 32
+            cipherkey_size = 32
+
+            full_size = TLV_INFO_SIZE + len(tlv.buf) + tlv_header_size \
+                        + payload_digest_size
+            if key is not None:
+                full_size += tlv_header_size + keyhash_size \
+                             + tlv_header_size + key.sig_len()
+            if enckey is not None:
+                full_size += tlv_header_size + cipherkey_size
+            tlv_header = struct.pack(e + 'HH', TLV_INFO_MAGIC, full_size)
+            self.payload += tlv_header + bytes(tlv.buf)
+
         # Note that ecdsa wants to do the hashing itself, which means
         # we get to hash it twice.
         sha = hashlib.sha256()
@@ -208,9 +250,9 @@
             self.payload[self.header_size:] = encryptor.update(img) + \
                                               encryptor.finalize()
 
-        self.payload += tlv.get()
+        self.payload += tlv.get()[protected_tlv_size:]
 
-    def add_header(self, enckey):
+    def add_header(self, enckey, protected_tlv_size):
         """Install the image header."""
 
         flags = 0
@@ -219,29 +261,29 @@
 
         e = STRUCT_ENDIAN_DICT[self.endian]
         fmt = (e +
-            # type ImageHdr struct {
-            'I' +   # Magic uint32
-            'I' +   # LoadAddr uint32
-            'H' +   # HdrSz uint16
-            'H' +   # Pad1  uint16
-            'I' +   # ImgSz uint32
-            'I' +   # Flags uint32
-            'BBHI' + # Vers  ImageVersion
-            'I'     # Pad2  uint32
-            ) # }
+               # type ImageHdr struct {
+               'I' +     # Magic    uint32
+               'I' +     # LoadAddr uint32
+               'H' +     # HdrSz    uint16
+               'H' +     # PTLVSz   uint16
+               'I' +     # ImgSz    uint32
+               'I' +     # Flags    uint32
+               'BBHI' +  # Vers     ImageVersion
+               'I'       # Pad1     uint32
+               )  # }
         assert struct.calcsize(fmt) == IMAGE_HEADER_SIZE
         header = struct.pack(fmt,
                 IMAGE_MAGIC,
                 0, # LoadAddr
                 self.header_size,
-                0, # Pad1
+                protected_tlv_size,  # TLV Info header + Dependency TLVs
                 len(self.payload) - self.header_size, # ImageSz
                 flags, # Flags
                 self.version.major,
                 self.version.minor or 0,
                 self.version.revision or 0,
                 self.version.build or 0,
-                0) # Pad2
+                0)  # Pad1
         self.payload = bytearray(self.payload)
         self.payload[:len(header)] = header