TBB: Add an IO abstraction layer to load encrypted firmwares

TBBR spec advocates for optional encryption of firmwares (see optional
requirement: R060_TBBR_FUNCTION). So add an IO abstaction layer to
support firmware decryption that can be stacked above any underlying IO/
packaging layer like FIP etc. It aims to provide a framework to load any
encrypted IO payload.

Also, add plat_get_enc_key_info() to be implemented in a platform
specific manner as handling of encryption key may vary from one platform
to another.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
Change-Id: I9892e0ddf00ebecb8981301dbfa41ea23e078b03
diff --git a/drivers/io/io_encrypted.c b/drivers/io/io_encrypted.c
new file mode 100644
index 0000000..744ca83
--- /dev/null
+++ b/drivers/io/io_encrypted.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2020, Linaro Limited. All rights reserved.
+ * Author: Sumit Garg <sumit.garg@linaro.org>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <platform_def.h>
+
+#include <common/bl_common.h>
+#include <common/debug.h>
+#include <drivers/auth/crypto_mod.h>
+#include <drivers/io/io_driver.h>
+#include <drivers/io/io_encrypted.h>
+#include <drivers/io/io_storage.h>
+#include <lib/utils.h>
+#include <plat/common/platform.h>
+#include <tools_share/firmware_encrypted.h>
+#include <tools_share/uuid.h>
+
+static uintptr_t backend_dev_handle;
+static uintptr_t backend_dev_spec;
+static uintptr_t backend_handle;
+static uintptr_t backend_image_spec;
+
+static io_dev_info_t enc_dev_info;
+
+/* Encrypted firmware driver functions */
+static int enc_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
+static int enc_file_open(io_dev_info_t *dev_info, const uintptr_t spec,
+			  io_entity_t *entity);
+static int enc_file_len(io_entity_t *entity, size_t *length);
+static int enc_file_read(io_entity_t *entity, uintptr_t buffer, size_t length,
+			  size_t *length_read);
+static int enc_file_close(io_entity_t *entity);
+static int enc_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params);
+static int enc_dev_close(io_dev_info_t *dev_info);
+
+static inline int is_valid_header(struct fw_enc_hdr *header)
+{
+	if (header->magic == ENC_HEADER_MAGIC)
+		return 1;
+	else
+		return 0;
+}
+
+static io_type_t device_type_enc(void)
+{
+	return IO_TYPE_ENCRYPTED;
+}
+
+static const io_dev_connector_t enc_dev_connector = {
+	.dev_open = enc_dev_open
+};
+
+static const io_dev_funcs_t enc_dev_funcs = {
+	.type = device_type_enc,
+	.open = enc_file_open,
+	.seek = NULL,
+	.size = enc_file_len,
+	.read = enc_file_read,
+	.write = NULL,
+	.close = enc_file_close,
+	.dev_init = enc_dev_init,
+	.dev_close = enc_dev_close,
+};
+
+static int enc_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
+{
+	assert(dev_info != NULL);
+
+	enc_dev_info.funcs = &enc_dev_funcs;
+	*dev_info = &enc_dev_info;
+
+	return 0;
+}
+
+static int enc_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params)
+{
+	int result;
+	unsigned int image_id = (unsigned int)init_params;
+
+	/* Obtain a reference to the image by querying the platform layer */
+	result = plat_get_image_source(image_id, &backend_dev_handle,
+				       &backend_dev_spec);
+	if (result != 0) {
+		WARN("Failed to obtain reference to image id=%u (%i)\n",
+			image_id, result);
+		return -ENOENT;
+	}
+
+	return result;
+}
+
+static int enc_dev_close(io_dev_info_t *dev_info)
+{
+	backend_dev_handle = (uintptr_t)NULL;
+	backend_dev_spec = (uintptr_t)NULL;
+
+	return 0;
+}
+
+static int enc_file_open(io_dev_info_t *dev_info, const uintptr_t spec,
+			 io_entity_t *entity)
+{
+	int result;
+
+	assert(spec != 0);
+	assert(entity != NULL);
+
+	backend_image_spec = spec;
+
+	result = io_open(backend_dev_handle, backend_image_spec,
+			 &backend_handle);
+	if (result != 0) {
+		WARN("Failed to open backend device (%i)\n", result);
+		result = -ENOENT;
+	}
+
+	return result;
+}
+
+static int enc_file_len(io_entity_t *entity, size_t *length)
+{
+	int result;
+
+	assert(entity != NULL);
+	assert(length != NULL);
+
+	result = io_size(backend_handle, length);
+	if (result != 0) {
+		WARN("Failed to read blob length (%i)\n", result);
+		return -ENOENT;
+	}
+
+	/*
+	 * Encryption header is attached at the beginning of the encrypted file
+	 * and is not considered a part of the payload.
+	 */
+	if (*length < sizeof(struct fw_enc_hdr))
+		return -EIO;
+
+	*length -= sizeof(struct fw_enc_hdr);
+
+	return result;
+}
+
+static int enc_file_read(io_entity_t *entity, uintptr_t buffer, size_t length,
+			 size_t *length_read)
+{
+	int result;
+	struct fw_enc_hdr header;
+	enum fw_enc_status_t fw_enc_status;
+	size_t bytes_read;
+	uint8_t key[ENC_MAX_KEY_SIZE];
+	size_t key_len = sizeof(key);
+	unsigned int key_flags = 0;
+	const io_uuid_spec_t *uuid_spec = (io_uuid_spec_t *)backend_image_spec;
+
+	assert(entity != NULL);
+	assert(length_read != NULL);
+
+	result = io_read(backend_handle, (uintptr_t)&header, sizeof(header),
+			 &bytes_read);
+	if (result != 0) {
+		WARN("Failed to read encryption header (%i)\n", result);
+		return -ENOENT;
+	}
+
+	if (!is_valid_header(&header)) {
+		WARN("Encryption header check failed.\n");
+		return -ENOENT;
+	}
+
+	VERBOSE("Encryption header looks OK.\n");
+	fw_enc_status = header.flags & FW_ENC_STATUS_FLAG_MASK;
+
+	if ((header.iv_len > ENC_MAX_IV_SIZE) ||
+	    (header.tag_len > ENC_MAX_TAG_SIZE)) {
+		WARN("Incorrect IV or tag length\n");
+		return -ENOENT;
+	}
+
+	result = io_read(backend_handle, buffer, length, &bytes_read);
+	if (result != 0) {
+		WARN("Failed to read encrypted payload (%i)\n", result);
+		return -ENOENT;
+	}
+
+	*length_read = bytes_read;
+
+	result = plat_get_enc_key_info(fw_enc_status, key, &key_len, &key_flags,
+				       (uint8_t *)&uuid_spec->uuid,
+				       sizeof(uuid_t));
+	if (result != 0) {
+		WARN("Failed to obtain encryption key (%i)\n", result);
+		return -ENOENT;
+	}
+
+	result = crypto_mod_auth_decrypt(header.dec_algo,
+					 (void *)buffer, *length_read, key,
+					 key_len, key_flags, header.iv,
+					 header.iv_len, header.tag,
+					 header.tag_len);
+	memset(key, 0, key_len);
+
+	if (result != 0) {
+		ERROR("File decryption failed (%i)\n", result);
+		return -ENOENT;
+	}
+
+	return result;
+}
+
+static int enc_file_close(io_entity_t *entity)
+{
+	io_close(backend_handle);
+
+	backend_image_spec = (uintptr_t)NULL;
+	entity->info = 0;
+
+	return 0;
+}
+
+/* Exported functions */
+
+/* Register the Encrypted Firmware driver with the IO abstraction */
+int register_io_dev_enc(const io_dev_connector_t **dev_con)
+{
+	int result;
+
+	assert(dev_con != NULL);
+
+	result = io_register_device(&enc_dev_info);
+	if (result == 0)
+		*dev_con = &enc_dev_connector;
+
+	return result;
+}
diff --git a/include/drivers/io/io_encrypted.h b/include/drivers/io/io_encrypted.h
new file mode 100644
index 0000000..9dcf061
--- /dev/null
+++ b/include/drivers/io/io_encrypted.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2020, Linaro Limited. All rights reserved.
+ * Author: Sumit Garg <sumit.garg@linaro.org>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef IO_ENCRYPTED_H
+#define IO_ENCRYPTED_H
+
+struct io_dev_connector;
+
+int register_io_dev_enc(const struct io_dev_connector **dev_con);
+
+#endif /* IO_ENCRYPTED_H */
diff --git a/include/drivers/io/io_storage.h b/include/drivers/io/io_storage.h
index a301ad5..f2d641c 100644
--- a/include/drivers/io/io_storage.h
+++ b/include/drivers/io/io_storage.h
@@ -25,6 +25,7 @@
 	IO_TYPE_MTD,
 	IO_TYPE_MMC,
 	IO_TYPE_STM32IMAGE,
+	IO_TYPE_ENCRYPTED,
 	IO_TYPE_MAX
 } io_type_t;
 
diff --git a/include/export/common/tbbr/tbbr_img_def_exp.h b/include/export/common/tbbr/tbbr_img_def_exp.h
index 3602554..89dbc58 100644
--- a/include/export/common/tbbr/tbbr_img_def_exp.h
+++ b/include/export/common/tbbr/tbbr_img_def_exp.h
@@ -85,12 +85,15 @@
 /* Binary with STM32 header */
 #define STM32_IMAGE_ID			U(29)
 
+/* Encrypted image identifier */
+#define ENC_IMAGE_ID			U(30)
+
 /* Define size of the array */
 #if defined(SPD_spmd)
 #define MAX_SP_IDS			U(8)
-#define MAX_NUMBER_IDS			MAX_SP_IDS + U(30)
+#define MAX_NUMBER_IDS			MAX_SP_IDS + U(31)
 #else
-#define MAX_NUMBER_IDS			U(30)
+#define MAX_NUMBER_IDS			U(31)
 #endif
 
 #endif /* ARM_TRUSTED_FIRMWARE_EXPORT_COMMON_TBBR_TBBR_IMG_DEF_EXP_H */
diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h
index 06b334d..5b5ebb9 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -27,6 +27,7 @@
 struct mmap_region;
 struct spm_mm_boot_info;
 struct sp_res_desc;
+enum fw_enc_status_t;
 
 /*******************************************************************************
  * plat_get_rotpk_info() flags
@@ -274,6 +275,9 @@
 int plat_set_nv_ctr2(void *cookie, const struct auth_img_desc_s *img_desc,
 		unsigned int nv_ctr);
 int get_mbedtls_heap_helper(void **heap_addr, size_t *heap_size);
+int plat_get_enc_key_info(enum fw_enc_status_t fw_enc_status, uint8_t *key,
+			  size_t *key_len, unsigned int *flags,
+			  const uint8_t *img_id, size_t img_id_len);
 
 /*******************************************************************************
  * Secure Partitions functions
diff --git a/include/tools_share/firmware_encrypted.h b/include/tools_share/firmware_encrypted.h
new file mode 100644
index 0000000..7ca634f
--- /dev/null
+++ b/include/tools_share/firmware_encrypted.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, Linaro Limited. All rights reserved.
+ * Author: Sumit Garg <sumit.garg@linaro.org>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FIRMWARE_ENCRYPTED_H
+#define FIRMWARE_ENCRYPTED_H
+
+#include <stdint.h>
+
+/* This is used as a signature to validate the encryption header */
+#define ENC_HEADER_MAGIC		0xAA640001U
+
+/* Firmware encryption status flag mask */
+#define FW_ENC_STATUS_FLAG_MASK		0x1
+
+/*
+ * SSK: Secret Symmetric Key
+ * BSSK: Binding Secret Symmetric Key
+ */
+enum fw_enc_status_t {
+	FW_ENC_WITH_SSK = 0,
+	FW_ENC_WITH_BSSK = 1,
+};
+
+#define ENC_MAX_IV_SIZE			16U
+#define ENC_MAX_TAG_SIZE		16U
+#define ENC_MAX_KEY_SIZE		32U
+
+struct fw_enc_hdr {
+	uint32_t magic;
+	uint16_t dec_algo;
+	uint16_t flags;
+	uint16_t iv_len;
+	uint16_t tag_len;
+	uint8_t iv[ENC_MAX_IV_SIZE];
+	uint8_t tag[ENC_MAX_TAG_SIZE];
+};
+
+#endif /* FIRMWARE_ENCRYPTED_H */
diff --git a/plat/common/plat_bl_common.c b/plat/common/plat_bl_common.c
index 6070db2..de6c1d1 100644
--- a/plat/common/plat_bl_common.c
+++ b/plat/common/plat_bl_common.c
@@ -11,6 +11,7 @@
 #include <common/debug.h>
 #include <lib/xlat_tables/xlat_tables_compat.h>
 #include <plat/common/platform.h>
+#include <tools_share/firmware_encrypted.h>
 
 /*
  * The following platform functions are weakly defined. The Platforms
@@ -22,6 +23,7 @@
 #pragma weak bl2_plat_handle_pre_image_load
 #pragma weak bl2_plat_handle_post_image_load
 #pragma weak plat_try_next_boot_source
+#pragma weak plat_get_enc_key_info
 
 void bl2_el3_plat_prepare_exit(void)
 {
@@ -53,6 +55,31 @@
 }
 
 /*
+ * Weak implementation to provide dummy decryption key only for test purposes,
+ * platforms must override this API for any real world firmware encryption
+ * use-case.
+ */
+int plat_get_enc_key_info(enum fw_enc_status_t fw_enc_status, uint8_t *key,
+			  size_t *key_len, unsigned int *flags,
+			  const uint8_t *img_id, size_t img_id_len)
+{
+#define DUMMY_FIP_ENC_KEY { 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, \
+			    0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, \
+			    0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, \
+			    0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef }
+
+	const uint8_t dummy_key[] = DUMMY_FIP_ENC_KEY;
+
+	assert(*key_len >= sizeof(dummy_key));
+
+	*key_len = sizeof(dummy_key);
+	memcpy(key, dummy_key, *key_len);
+	*flags = 0;
+
+	return 0;
+}
+
+/*
  * Set up the page tables for the generic and platform-specific memory regions.
  * The size of the Trusted SRAM seen by the BL image must be specified as well
  * as an array specifying the generic memory regions which can be;