Boot: Add measured boot record TLV to shared area

This patch provides an alternative implementation of the
boot_save_boot_status() function which can read the already CBOR encoded
measured boot record from the image manifest and writes it to the shared
data area (between the bootloader and runtime SW) instead of copying the
individual (not encoded) claims separately.

Add new ATTEST_BOOT_INTERFACE variable to the build system to be able to
switch between the two behaviours.

Change-Id: I9ee66a5174cb5b851a79262decd35192cae7cc27
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/CommonConfig.cmake b/CommonConfig.cmake
index 7557293..3e03aec 100644
--- a/CommonConfig.cmake
+++ b/CommonConfig.cmake
@@ -370,6 +370,10 @@
 	set(ATTEST_INCLUDE_TEST_CODE_AND_KEY_ID OFF)
 endif()
 
+set(ATTEST_BOOT_INTERFACE "INDIVIDUAL_CLAIMS" CACHE STRING "Set the format in which to pass the claims to the initial-attestation service.")
+set_property(CACHE ATTEST_BOOT_INTERFACE PROPERTY STRINGS "INDIVIDUAL_CLAIMS;CBOR_ENCODED_CLAIMS")
+validate_cache_value(ATTEST_BOOT_INTERFACE)
+
 ##Set mbedTLS compiler flags for BL2 bootloader
 set(MBEDTLS_C_FLAGS_BL2 "-D__ARM_FEATURE_CMSE=${ARM_FEATURE_CMSE} -D__thumb2__ ${COMMON_COMPILE_FLAGS_STR} -DMBEDTLS_CONFIG_FILE=\\\\\\\"config-boot.h\\\\\\\" -I${CMAKE_CURRENT_LIST_DIR}/bl2/ext/mcuboot/include")
 if (MCUBOOT_SIGNATURE_TYPE STREQUAL "RSA-3072")
diff --git a/bl2/ext/mcuboot/CMakeLists.txt b/bl2/ext/mcuboot/CMakeLists.txt
index 684de64..bf29f18 100644
--- a/bl2/ext/mcuboot/CMakeLists.txt
+++ b/bl2/ext/mcuboot/CMakeLists.txt
@@ -180,6 +180,12 @@
 	target_compile_definitions(${PROJECT_NAME} PRIVATE MCUBOOT_HW_KEY)
 endif()
 
+if (ATTEST_BOOT_INTERFACE STREQUAL "INDIVIDUAL_CLAIMS")
+	target_compile_definitions(${PROJECT_NAME} PRIVATE MCUBOOT_INDIVIDUAL_CLAIMS)
+	message(WARNING "ATTEST_BOOT_INTERFACE was set to ${ATTEST_BOOT_INTERFACE}. This configuration is "
+			"deprecated and this feature will probably be removed from MCUBoot in the future.")
+endif()
+
 #Set install location. Keep original value to avoid overriding command line settings.
 if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
 	set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Default install location for MCUBoot." FORCE)
diff --git a/bl2/ext/mcuboot/bootutil/include/bootutil/image.h b/bl2/ext/mcuboot/bootutil/include/bootutil/image.h
index 596f0f8..bb2e9b8 100644
--- a/bl2/ext/mcuboot/bootutil/include/bootutil/image.h
+++ b/bl2/ext/mcuboot/bootutil/include/bootutil/image.h
@@ -71,6 +71,7 @@
 #define IMAGE_TLV_RSA3072_PSS       0x23   /* RSA3072 of hash output */
 #define IMAGE_TLV_DEPENDENCY        0x40   /* Image depends on other image */
 #define IMAGE_TLV_SEC_CNT           0x50   /* security counter */
+#define IMAGE_TLV_BOOT_RECORD       0x60   /* measured boot record */
 
 #define IMAGE_VER_MAJOR_LENGTH      8
 #define IMAGE_VER_MINOR_LENGTH      8
diff --git a/bl2/ext/mcuboot/bootutil/src/image_validate.c b/bl2/ext/mcuboot/bootutil/src/image_validate.c
index 94a25d0..13892d8 100644
--- a/bl2/ext/mcuboot/bootutil/src/image_validate.c
+++ b/bl2/ext/mcuboot/bootutil/src/image_validate.c
@@ -73,9 +73,8 @@
     /* Hash is computed over image header and image itself. */
     size = hdr->ih_img_size + hdr->ih_hdr_size;
 
-    /* If a security counter TLV and/or a dependency TLV(s) are present then the
-     * TLV info header, the security counter TLV and/or the dependency TLV(s)
-     * are also protected and must be included in the hash calculation.
+    /* If protected TLVs are present (e.g. security counter TLV) then the
+     * TLV info header and these TLVs must be included in the hash calculation.
      */
     if (hdr->ih_protect_tlv_size != 0) {
         size += hdr->ih_protect_tlv_size;
diff --git a/bl2/include/boot_record.h b/bl2/include/boot_record.h
index 8ec2e6f..51bfc1d 100644
--- a/bl2/include/boot_record.h
+++ b/bl2/include/boot_record.h
@@ -36,7 +36,7 @@
 /*!
  * \enum boot_status_err_t
  *
- * \brief Return values for saving boot status information to shared memory are
+ * \brief Return values for saving boot status information to shared memory area
  */
 enum boot_status_err_t {
     BOOT_STATUS_OK,
diff --git a/bl2/include/tfm_boot_status.h b/bl2/include/tfm_boot_status.h
index 3ee807f..c321d97 100644
--- a/bl2/include/tfm_boot_status.h
+++ b/bl2/include/tfm_boot_status.h
@@ -83,6 +83,7 @@
 /* Bits: 3-5 */
 #define SW_MEASURE_VALUE 0x08
 #define SW_MEASURE_TYPE  0x09
+#define SW_BOOT_RECORD   0x3F
 
 /* Initial attestation: General claim does not belong any particular SW
  * component. But they might be part of the boot status.
diff --git a/bl2/src/boot_record.c b/bl2/src/boot_record.c
index 1826bf3..35a8c7c 100644
--- a/bl2/src/boot_record.c
+++ b/bl2/src/boot_record.c
@@ -17,12 +17,26 @@
 #include <string.h>
 #include <stdio.h>
 
-#define SHA256_HASH_SIZE (32u)
+#define SHA256_HASH_SIZE    (32u)
 #if defined(MCUBOOT_SIGN_RSA) && defined(MCUBOOT_HW_KEY)
-#define SIG_BUF_SIZE (MCUBOOT_SIGN_RSA_LEN / 8)
+#   define SIG_BUF_SIZE     (MCUBOOT_SIGN_RSA_LEN / 8)
 #endif
 
 /*!
+ * \def MAX_BOOT_RECORD_SZ
+ *
+ * \brief Maximum size of the measured boot record.
+ *
+ * Its size can be calculated based on the following aspects:
+ *   - There are 5 allowed software component claims,
+ *   - SHA256 is used as the measurement method for the other claims.
+ * Considering these aspects, the only claim which size can vary is the
+ * type of the software component. In case of single image boot it is
+ * "NSPE_SPE" which results the maximum boot record size of 96.
+ */
+#define MAX_BOOT_RECORD_SZ  (96u)
+
+/*!
  * \var shared_memory_init_done
  *
  * \brief Indicates whether shared memory area was already initialized.
@@ -54,6 +68,7 @@
 #error "Shared data area and non-secure data area is overlapping"
 #endif
 
+#ifdef MCUBOOT_INDIVIDUAL_CLAIMS
 /*!
  * \brief Add the measurement data of SW component to the shared memory area
  *
@@ -277,6 +292,7 @@
 
     return BOOT_STATUS_OK;
 }
+#endif /* MCUBOOT_INDIVIDUAL_CLAIMS */
 
 /* See in boot_record.h */
 enum shared_memory_err_t
@@ -353,6 +369,11 @@
                       const struct image_header *hdr,
                       const struct flash_area *fap)
 {
+#ifdef MCUBOOT_INDIVIDUAL_CLAIMS
+    /* This implementation is deprecated and will probably
+     * be removed in the future.
+     */
+
     enum boot_status_err_t res;
 
     res = boot_save_sw_type(sw_module);
@@ -371,4 +392,121 @@
     }
 
     return BOOT_STATUS_OK;
+
+#else /* MCUBOOT_INDIVIDUAL_CLAIMS */
+
+    struct image_tlv_info tlv_header;
+    struct image_tlv tlv_entry;
+    uintptr_t tlv_end, offset;
+    size_t record_len = 0;
+    uint8_t image_hash[32]; /* SHA256 - 32 Bytes */
+    uint8_t buf[MAX_BOOT_RECORD_SZ];
+    uint32_t boot_record_found = 0;
+    uint32_t hash_found = 0;
+    uint16_t ias_minor;
+    int32_t res;
+    enum shared_memory_err_t res2;
+
+    /* Manifest data is concatenated to the end of the image.
+     * It is encoded in TLV format.
+     */
+    offset = hdr->ih_hdr_size + hdr->ih_img_size;
+
+    /* The TLV area always starts with an image_tlv_info structure. */
+    res = LOAD_IMAGE_DATA(fap, offset, &tlv_header, sizeof(tlv_header));
+    if (res) {
+        return BOOT_STATUS_ERROR;
+    }
+    if (tlv_header.it_magic != IMAGE_TLV_INFO_MAGIC) {
+        return BOOT_STATUS_ERROR;
+    }
+    tlv_end = offset + (uintptr_t)tlv_header.it_tlv_tot;
+    offset += sizeof(tlv_header);
+
+    /* Traverse through the TLV area to find the boot record
+     * and image hash TLVs.
+     */
+    while (offset < tlv_end) {
+        res = LOAD_IMAGE_DATA(fap, offset, &tlv_entry, sizeof(tlv_entry));
+        if (res) {
+            return BOOT_STATUS_ERROR;
+        }
+
+        if (tlv_entry.it_type == IMAGE_TLV_BOOT_RECORD) {
+            if (tlv_entry.it_len > sizeof(buf)) {
+                return BOOT_STATUS_ERROR;
+            }
+            res = LOAD_IMAGE_DATA(fap, offset + sizeof(tlv_entry),
+                                  buf, tlv_entry.it_len);
+            if (res) {
+                return BOOT_STATUS_ERROR;
+            }
+
+            record_len = tlv_entry.it_len;
+            boot_record_found = 1;
+
+        } else if (tlv_entry.it_type == IMAGE_TLV_SHA256) {
+            /* Get the image's hash value from the manifest section. */
+            if (tlv_entry.it_len > sizeof(image_hash)) {
+                return BOOT_STATUS_ERROR;
+            }
+            res = LOAD_IMAGE_DATA(fap, offset + sizeof(tlv_entry),
+                                  image_hash, tlv_entry.it_len);
+            if (res) {
+                return BOOT_STATUS_ERROR;
+            }
+
+            hash_found = 1;
+
+            /* The boot record TLV is part of the protected TLV area which is
+             * located before the other parts of the TLV area (including the
+             * image hash) so at this point it is okay to break the loop
+             * as the boot record TLV should have already been found.
+             */
+            break;
+        }
+
+        /* Avoid integer overflow. */
+        if ((UINTPTR_MAX - offset) <
+            (sizeof(tlv_entry) + tlv_entry.it_len)) {
+            /* Potential overflow. */
+            break;
+        } else {
+            offset += sizeof(tlv_entry) + tlv_entry.it_len;
+        }
+    }
+
+
+    if (!boot_record_found || !hash_found) {
+        return BOOT_STATUS_ERROR;
+    }
+
+    /* Update the measurement value (hash of the image) data item in the
+     * boot record. It is always the last item in the structure to make
+     * it easy to calculate its position.
+     * The image hash is computed over the image header, the image itself and
+     * the protected TLV area (which should already include the image hash as
+     * part of the boot record TLV). For this reason this field has been
+     * filled with zeros during the image signing process.
+     */
+    offset = record_len - sizeof(image_hash);
+    /* Avoid buffer overflow. */
+    if ((offset + sizeof(image_hash)) > sizeof(buf)) {
+        return BOOT_STATUS_ERROR;
+    }
+    memcpy(buf + offset, image_hash, sizeof(image_hash));
+
+    /* Add the CBOR encoded boot record to the shared data area. */
+    ias_minor = SET_IAS_MINOR(sw_module, SW_BOOT_RECORD);
+    res2 = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
+                                        ias_minor,
+                                        record_len,
+                                        buf);
+    if (res2) {
+        return BOOT_STATUS_ERROR;
+    }
+
+    return BOOT_STATUS_OK;
+
+#endif /* MCUBOOT_INDIVIDUAL_CLAIMS */
 }