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;
+}