core: add framework to load REE-FS encrypted TAs

Add framework to support loading of encrypted TAs from REE-FS using
symmetric authenticated encryption scheme supported by OP-TEE.

The default encryption key is derived from hardware unique key which
can be overridden via platform specific encryption key.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
diff --git a/core/arch/arm/kernel/otp_stubs.c b/core/arch/arm/kernel/otp_stubs.c
index 2f2cba8..bd510d9 100644
--- a/core/arch/arm/kernel/otp_stubs.c
+++ b/core/arch/arm/kernel/otp_stubs.c
@@ -1,11 +1,14 @@
 // SPDX-License-Identifier: BSD-2-Clause
 /*
- * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2015, 2019, Linaro Limited
  */
 
+#include <assert.h>
 #include <inttypes.h>
 #include <kernel/tee_common_otp.h>
 #include <kernel/huk_subkey.h>
+#include <signed_hdr.h>
+#include <ta_pub_key.h>
 
 /*
  * Override these in your platform code to really fetch device-unique
@@ -27,3 +30,24 @@
 
 	return 0;
 }
+
+/*
+ * Override this API on your platform to provide TA encryption key as
+ * per your security requirements. There can be two options for this key:
+ *
+ * 1) Unique per device encryption key.
+ * 2) Class wide encryption key.
+ *
+ * The default implementation chooses option (1).
+ */
+__weak TEE_Result tee_otp_get_ta_enc_key(uint32_t key_type __maybe_unused,
+					 uint8_t *buffer, size_t len)
+{
+	assert(key_type == SHDR_ENC_KEY_DEV_SPECIFIC);
+
+	if (huk_subkey_derive(HUK_SUBKEY_TA_ENC, ta_pub_key_modulus,
+			      ta_pub_key_modulus_size, buffer, len))
+		return TEE_ERROR_SECURITY;
+
+	return TEE_SUCCESS;
+}
diff --git a/core/arch/arm/kernel/ree_fs_ta.c b/core/arch/arm/kernel/ree_fs_ta.c
index 12406b6..f8e5b23 100644
--- a/core/arch/arm/kernel/ree_fs_ta.c
+++ b/core/arch/arm/kernel/ree_fs_ta.c
@@ -1,7 +1,39 @@
 // SPDX-License-Identifier: BSD-2-Clause
 /*
- * Copyright (c) 2017, Linaro Limited
+ * Copyright (c) 2017, 2019, Linaro Limited
  */
+
+/*
+ * Security properties of REE-FS TAs
+ * =================================
+ *
+ * Authentication only
+ * -------------------
+ *
+ * Required security properties:
+ * 1. Authentication and non-repudiation of a TA to Service Provider (SP).
+ * 2. Integrity of a TA.
+ *
+ * To satisfy (1) and (2), SP needs to sign TA and OP-TEE core needs to verify
+ * the signature using SP public key with computed hash of the TA.
+ *
+ * Authentication along with Confidentiality
+ * -----------------------------------------
+ *
+ * Required security properties:
+ * 1. Authentication and non-repudiation of a TA to Service Provider (SP).
+ * 2. Confidentiality of a TA.
+ * 3. Integrity of an encrypted TA blob.
+ *
+ * To satisfy (1), SP needs to sign plain TA and OP-TEE core needs to verify the
+ * signature using SP public key with computed hash of the TA.
+ *
+ * To satisfy (2) and (3), SP needs to do authenticated encryption of TA and
+ * OP-TEE core needs to do authenticated decryption of TA to retrieve its
+ * contents. Here encryption provides the confidentiality of TA and MAC tag
+ * provides the integrity of encrypted TA blob.
+ */
+
 #include <assert.h>
 #include <crypto/crypto.h>
 #include <initcall.h>
@@ -15,6 +47,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <tee_api_types.h>
+#include <tee/tee_ta_enc_manager.h>
 #include <tee/uuid.h>
 #include <utee_defines.h>
 
@@ -25,6 +58,8 @@
 	size_t offs;
 	struct shdr *shdr; /* Verified secure copy of @nw_ta's signed header */
 	void *hash_ctx;
+	void *enc_ctx;
+	struct shdr_encrypted_ta *ehdr;
 };
 
 /*
@@ -88,6 +123,7 @@
 	size_t ta_size = 0;
 	TEE_Result res;
 	size_t offs;
+	struct shdr_encrypted_ta *ehdr = NULL;
 
 	handle = calloc(1, sizeof(*handle));
 	if (!handle)
@@ -109,7 +145,8 @@
 	res = shdr_verify_signature(shdr);
 	if (res != TEE_SUCCESS)
 		goto error_free_payload;
-	if (shdr->img_type != SHDR_TA && shdr->img_type != SHDR_BOOTSTRAP_TA) {
+	if (shdr->img_type != SHDR_TA && shdr->img_type != SHDR_BOOTSTRAP_TA &&
+	    shdr->img_type != SHDR_ENCRYPTED_TA) {
 		res = TEE_ERROR_SECURITY;
 		goto error_free_payload;
 	}
@@ -130,7 +167,8 @@
 		goto error_free_hash;
 	offs = SHDR_GET_SIZE(shdr);
 
-	if (shdr->img_type == SHDR_BOOTSTRAP_TA) {
+	if (shdr->img_type == SHDR_BOOTSTRAP_TA ||
+	    shdr->img_type == SHDR_ENCRYPTED_TA) {
 		TEE_UUID bs_uuid;
 		struct shdr_bootstrap_ta bs_hdr;
 
@@ -160,6 +198,38 @@
 		offs += sizeof(bs_hdr);
 	}
 
+	if (shdr->img_type == SHDR_ENCRYPTED_TA) {
+		struct shdr_encrypted_ta img_ehdr;
+
+		if (ta_size < SHDR_GET_SIZE(shdr) +
+		    sizeof(struct shdr_bootstrap_ta) + sizeof(img_ehdr)) {
+			res = TEE_ERROR_SECURITY;
+			goto error_free_hash;
+		}
+
+		memcpy(&img_ehdr, ((uint8_t *)ta + offs), sizeof(img_ehdr));
+
+		ehdr = malloc(SHDR_ENC_GET_SIZE(&img_ehdr));
+		if (!ehdr)
+			return TEE_ERROR_OUT_OF_MEMORY;
+
+		memcpy(ehdr, ((uint8_t *)ta + offs),
+		       SHDR_ENC_GET_SIZE(&img_ehdr));
+
+		res = crypto_hash_update(hash_ctx, (uint8_t *)ehdr,
+					 SHDR_ENC_GET_SIZE(ehdr));
+		if (res != TEE_SUCCESS)
+			goto error_free_hash;
+
+		res = tee_ta_decrypt_init(&handle->enc_ctx, ehdr,
+					  shdr->img_size);
+		if (res != TEE_SUCCESS)
+			goto error_free_hash;
+
+		offs += SHDR_ENC_GET_SIZE(ehdr);
+		handle->ehdr = ehdr;
+	}
+
 	if (ta_size != offs + shdr->img_size) {
 		res = TEE_ERROR_SECURITY;
 		goto error_free_hash;
@@ -179,6 +249,7 @@
 error_free_payload:
 	thread_rpc_free_payload(mobj);
 error:
+	free(ehdr);
 	shdr_free(shdr);
 	free(handle);
 	return res;
@@ -240,15 +311,65 @@
 
 	if (handle->offs + len > handle->nw_ta_size)
 		return TEE_ERROR_BAD_PARAMETERS;
-	if (data) {
+
+	if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) {
+		if (data) {
+			dst = data; /* Hash secure buffer */
+			res = tee_ta_decrypt_update(handle->enc_ctx, dst, src,
+						    len);
+			if (res != TEE_SUCCESS)
+				return TEE_ERROR_SECURITY;
+		} else {
+			size_t num_bytes = 0;
+			size_t b_size = MIN(1024U, len);
+			uint8_t *b = malloc(b_size);
+
+			if (!b)
+				return TEE_ERROR_OUT_OF_MEMORY;
+
+			dst = NULL;
+			while (num_bytes < len) {
+				size_t n = MIN(b_size, len - num_bytes);
+
+				res = tee_ta_decrypt_update(handle->enc_ctx, b,
+							    src + num_bytes, n);
+				if (res)
+					break;
+				num_bytes += n;
+
+				res = crypto_hash_update(handle->hash_ctx, b,
+							 n);
+				if (res)
+					break;
+			}
+
+			free(b);
+			if (res != TEE_SUCCESS)
+				return TEE_ERROR_SECURITY;
+		}
+	} else if (data) {
 		dst = data; /* Hash secure buffer (shm might be modified) */
 		memcpy(dst, src, len);
 	}
-	res = crypto_hash_update(handle->hash_ctx, dst, len);
-	if (res != TEE_SUCCESS)
-		return TEE_ERROR_SECURITY;
+
+	if (dst) {
+		res = crypto_hash_update(handle->hash_ctx, dst, len);
+		if (res != TEE_SUCCESS)
+			return TEE_ERROR_SECURITY;
+	}
+
 	handle->offs += len;
 	if (handle->offs == handle->nw_ta_size) {
+		if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) {
+			/*
+			 * Last read: time to finalize authenticated
+			 * decryption.
+			 */
+			res = tee_ta_decrypt_final(handle->enc_ctx,
+						   handle->ehdr, NULL, NULL, 0);
+			if (res != TEE_SUCCESS)
+				return TEE_ERROR_SECURITY;
+		}
 		/*
 		 * Last read: time to check if our digest matches the expected
 		 * one (from the signed header)
@@ -267,6 +388,7 @@
 	thread_rpc_free_payload(handle->mobj);
 	crypto_hash_free_ctx(handle->hash_ctx);
 	free(handle->shdr);
+	free(handle->ehdr);
 	free(handle);
 }
 
diff --git a/core/include/kernel/huk_subkey.h b/core/include/kernel/huk_subkey.h
index 364cf3c..429480e 100644
--- a/core/include/kernel/huk_subkey.h
+++ b/core/include/kernel/huk_subkey.h
@@ -16,6 +16,7 @@
  * @HUK_SUBKEY_SSK:	  Secure Storage key
  * @HUK_SUBKEY_DIE_ID:	  Representing the die ID
  * @HUK_SUBKEY_UNIQUE_TA: TA unique key
+ * @HUK_SUBKEY_TA_ENC:    TA encryption key
  *
  * Add more identifiers as needed, be careful to not change the already
  * assigned numbers as that will affect the derived subkey.
@@ -29,6 +30,7 @@
 	HUK_SUBKEY_SSK = 1,
 	HUK_SUBKEY_DIE_ID = 2,
 	HUK_SUBKEY_UNIQUE_TA = 3,
+	HUK_SUBKEY_TA_ENC = 4,
 };
 
 #define HUK_SUBKEY_MAX_LEN	TEE_SHA256_HASH_SIZE
diff --git a/core/include/kernel/tee_common_otp.h b/core/include/kernel/tee_common_otp.h
index 8013849..2b0d3a3 100644
--- a/core/include/kernel/tee_common_otp.h
+++ b/core/include/kernel/tee_common_otp.h
@@ -17,5 +17,7 @@
 
 TEE_Result tee_otp_get_hw_unique_key(struct tee_hw_unique_key *hwkey);
 int tee_otp_get_die_id(uint8_t *buffer, size_t len);
+TEE_Result tee_otp_get_ta_enc_key(uint32_t key_type, uint8_t *buffer,
+				  size_t len);
 
 #endif /* TEE_COMMON_OTP_H */
diff --git a/core/include/signed_hdr.h b/core/include/signed_hdr.h
index 88f537b..5294f43 100644
--- a/core/include/signed_hdr.h
+++ b/core/include/signed_hdr.h
@@ -12,6 +12,7 @@
 enum shdr_img_type {
 	SHDR_TA = 0,
 	SHDR_BOOTSTRAP_TA = 1,
+	SHDR_ENCRYPTED_TA = 2,
 };
 
 #define SHDR_MAGIC	0x4f545348
@@ -57,6 +58,49 @@
 	uint32_t ta_version;
 };
 
+/**
+ * struct shdr_encrypted_ta - encrypted TA header
+ * @enc_algo:	authenticated encyption algorithm, defined by symmetric key
+ *		algorithms TEE_ALG_* from TEE Internal API
+ *		specification
+ * @flags:	authenticated encyption flags
+ * @iv_size:	size of the initialization vector
+ * @tag_size:	size of the authentication tag
+ * @iv:		initialization vector
+ * @tag:	authentication tag
+ */
+struct shdr_encrypted_ta {
+	uint32_t enc_algo;
+	uint32_t flags;
+	uint16_t iv_size;
+	uint16_t tag_size;
+	/*
+	 * Commented out element used to visualize the layout dynamic part
+	 * of the struct.
+	 *
+	 * iv is accessed through the macro SHDR_ENC_GET_IV and
+	 * tag is accessed through the macro SHDR_ENC_GET_TAG
+	 *
+	 * uint8_t iv[iv_size];
+	 * uint8_t tag[tag_size];
+	 */
+};
+
+#define SHDR_ENC_KEY_TYPE_MASK	0x1
+
+enum shdr_enc_key_type {
+	SHDR_ENC_KEY_DEV_SPECIFIC = 0,
+	SHDR_ENC_KEY_CLASS_WIDE = 1,
+};
+
+#define SHDR_ENC_GET_SIZE(x)	({ typeof(x) _x = (x); \
+				   (sizeof(struct shdr_encrypted_ta) + \
+				   _x->iv_size + _x->tag_size); })
+#define SHDR_ENC_GET_IV(x)	((uint8_t *) \
+				 (((struct shdr_encrypted_ta *)(x)) + 1))
+#define SHDR_ENC_GET_TAG(x)	({ typeof(x) _x = (x); \
+				   (SHDR_ENC_GET_IV(_x) + _x->iv_size); })
+
 /*
  * Allocates a struct shdr large enough to hold the entire header,
  * excluding a subheader like struct shdr_bootstrap_ta.
diff --git a/core/include/tee/tee_ta_enc_manager.h b/core/include/tee/tee_ta_enc_manager.h
new file mode 100644
index 0000000..5e1b2e5
--- /dev/null
+++ b/core/include/tee/tee_ta_enc_manager.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#ifndef TEE_TA_ENC_MANAGER_H
+#define TEE_TA_ENC_MANAGER_H
+
+#include <signed_hdr.h>
+#include <tee_api_types.h>
+#include <utee_defines.h>
+
+#define TEE_TA_ENC_KEY_SIZE		TEE_SHA256_HASH_SIZE
+
+TEE_Result tee_ta_decrypt_init(void **enc_ctx, struct shdr_encrypted_ta *ehdr,
+			       size_t len);
+TEE_Result tee_ta_decrypt_update(void *enc_ctx, uint8_t *dst, uint8_t *src,
+				 size_t len);
+TEE_Result tee_ta_decrypt_final(void *enc_ctx, struct shdr_encrypted_ta *ehdr,
+				uint8_t *dst, uint8_t *src, size_t len);
+
+#endif
diff --git a/core/tee/sub.mk b/core/tee/sub.mk
index 6483e40..554edfc 100644
--- a/core/tee/sub.mk
+++ b/core/tee/sub.mk
@@ -45,3 +45,4 @@
 endif #CFG_WITH_USER_TA,y
 
 srcs-y += uuid.c
+srcs-y += tee_ta_enc_manager.c
diff --git a/core/tee/tee_ta_enc_manager.c b/core/tee/tee_ta_enc_manager.c
new file mode 100644
index 0000000..2bab308
--- /dev/null
+++ b/core/tee/tee_ta_enc_manager.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#include <crypto/crypto.h>
+#include <kernel/tee_common_otp.h>
+#include <string_ext.h>
+#include <tee/tee_ta_enc_manager.h>
+#include <trace.h>
+
+TEE_Result tee_ta_decrypt_init(void **enc_ctx, struct shdr_encrypted_ta *ehdr,
+			       size_t len)
+{
+	TEE_Result res = TEE_SUCCESS;
+	uint8_t key[TEE_TA_ENC_KEY_SIZE] = {0};
+
+	res = crypto_authenc_alloc_ctx(enc_ctx, ehdr->enc_algo);
+	if (res != TEE_SUCCESS)
+		return res;
+
+	res = tee_otp_get_ta_enc_key(ehdr->flags & SHDR_ENC_KEY_TYPE_MASK,
+				     key, sizeof(key));
+	if (res != TEE_SUCCESS)
+		goto out_init;
+
+	res = crypto_authenc_init(*enc_ctx, TEE_MODE_DECRYPT, key, sizeof(key),
+				  SHDR_ENC_GET_IV(ehdr), ehdr->iv_size,
+				  ehdr->tag_size, 0, len);
+
+out_init:
+	if (res != TEE_SUCCESS)
+		crypto_authenc_free_ctx(*enc_ctx);
+
+	memzero_explicit(key, sizeof(key));
+	return res;
+}
+
+TEE_Result tee_ta_decrypt_update(void *enc_ctx, uint8_t *dst, uint8_t *src,
+				 size_t len)
+{
+	TEE_Result res = TEE_SUCCESS;
+	size_t dlen = len;
+
+	res = crypto_authenc_update_payload(enc_ctx, TEE_MODE_DECRYPT, src, len,
+					    dst, &dlen);
+	if (res != TEE_SUCCESS)
+		crypto_authenc_free_ctx(enc_ctx);
+
+	return res;
+}
+
+TEE_Result tee_ta_decrypt_final(void *enc_ctx, struct shdr_encrypted_ta *ehdr,
+				uint8_t *dst, uint8_t *src, size_t len)
+{
+	TEE_Result res = TEE_SUCCESS;
+	size_t dlen = len;
+
+	res = crypto_authenc_dec_final(enc_ctx, src, len, dst, &dlen,
+				       SHDR_ENC_GET_TAG(ehdr), ehdr->tag_size);
+
+	crypto_authenc_free_ctx(enc_ctx);
+
+	return res;
+}