aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJorge Ramirez-Ortiz <jorge@foundries.io>2021-10-08 09:46:13 +0200
committerJérôme Forissier <jerome@forissier.org>2021-11-08 10:13:23 +0100
commit1d23b02e1ce5665e6552ebd9f8d67883a405cc4e (patch)
tree8cd9fc360c86b0fd88abb0052d98a36f611e7e30 /core
parent9b61a2bcfdba19f2492ae4f31d106affe2d0a255 (diff)
zynqmp: drivers: generate HUK from PUF KEK
If authenticated boot was disabled we allow generating the HUK using the SHA-256 of the DNA unique identifier. If authenticated boot was enabled, use the PUK KEK to generate the HUK instead. The PUF KEK must be registered while securing the board using the Xilinx tools. In this case, the HUK is generated by reading the DNA eFuses. This 96 bits value is used to generate a 16 byte digest which is then AES-GCM encrypted using the PUF KEK. The resulting 16 byte value is the HUK. To prevent the HUK from being leaked, the AES-GCM module must be reserved. The HUK generation was validated on Zynqmp zu3cg using the Xilinx Lightweight Provisioning Tool to enable authenticated boot and to provision the PUF (burning a number of eFuses in the process). Tested-by: Jorge Ramirez-Ortiz <jorge@foundries.io> Signed-off-by: Jorge Ramirez-Ortiz <jorge@foundries.io> Tested-by: Ricardo Salveti <ricardo@foundries.io> Acked-by: Etienne Carriere <etienne.carriere@linaro.org>
Diffstat (limited to 'core')
-rw-r--r--core/arch/arm/plat-zynqmp/conf.mk5
-rw-r--r--core/drivers/sub.mk1
-rw-r--r--core/drivers/zynqmp_csu_puf.c15
-rw-r--r--core/drivers/zynqmp_huk.c121
-rw-r--r--core/include/drivers/zynqmp_csu.h1
5 files changed, 143 insertions, 0 deletions
diff --git a/core/arch/arm/plat-zynqmp/conf.mk b/core/arch/arm/plat-zynqmp/conf.mk
index ed773445..5d4a73af 100644
--- a/core/arch/arm/plat-zynqmp/conf.mk
+++ b/core/arch/arm/plat-zynqmp/conf.mk
@@ -30,6 +30,11 @@ CFG_CRYPTO_WITH_CE ?= y
CFG_ZYNQMP_PM ?= $(CFG_ARM64_core)
+ifeq ($(CFG_ZYNQMP_HUK),y)
+$(call force,CFG_ZYNQMP_CSU_AES,y,Mandated by CFG_ZYNQMP_HUK)
+$(call force,CFG_ZYNQMP_CSU_PUF,y,Mandated by CFG_ZYNQMP_HUK)
+endif
+
ifeq ($(CFG_ZYNQMP_CSU_AES),y)
$(call force,CFG_ZYNQMP_CSUDMA,y,Mandated by CFG_ZYNQMP_CSU_AES)
$(call force,CFG_DT,y,Mandated by CFG_ZYNQMP_CSU_AES)
diff --git a/core/drivers/sub.mk b/core/drivers/sub.mk
index 9edec347..a715859a 100644
--- a/core/drivers/sub.mk
+++ b/core/drivers/sub.mk
@@ -42,6 +42,7 @@ srcs-$(CFG_ZYNQMP_CSU_PUF) += zynqmp_csu_puf.c
srcs-$(CFG_ZYNQMP_CSUDMA) += zynqmp_csudma.c
srcs-$(CFG_ZYNQMP_CSU_AES) += zynqmp_csu_aes.c
srcs-$(CFG_ZYNQMP_PM) += zynqmp_pm.c
+srcs-$(CFG_ZYNQMP_HUK) += zynqmp_huk.c
subdirs-y += crypto
subdirs-$(CFG_BNXT_FW) += bnxt
diff --git a/core/drivers/zynqmp_csu_puf.c b/core/drivers/zynqmp_csu_puf.c
index ab79efa8..f51443b9 100644
--- a/core/drivers/zynqmp_csu_puf.c
+++ b/core/drivers/zynqmp_csu_puf.c
@@ -57,3 +57,18 @@ void zynqmp_csu_puf_reset(void)
io_write32(puf + PUF_CMD_OFFSET, PUF_RESET);
}
+
+static TEE_Result zynqmp_csu_puf_init(void)
+{
+ vaddr_t csu = core_mmu_get_va(CSU_BASE, MEM_AREA_IO_SEC, CSU_SIZE);
+ uint32_t status = 0;
+
+ /* if the bootloader has been authenticated, reserve the PUF */
+ status = io_read32(csu + ZYNQMP_CSU_STATUS_OFFSET);
+ if (status & ZYNQMP_CSU_STATUS_AUTH)
+ return zynqmp_csu_aes_dt_enable_secure_status();
+
+ return TEE_SUCCESS;
+}
+
+driver_init(zynqmp_csu_puf_init);
diff --git a/core/drivers/zynqmp_huk.c b/core/drivers/zynqmp_huk.c
new file mode 100644
index 00000000..45c89d02
--- /dev/null
+++ b/core/drivers/zynqmp_huk.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright 2021 Foundries.io Ltd.
+ * Jorge Ramirez-Ortiz <jorge@foundries.io>
+ */
+
+#include <assert.h>
+#include <drivers/zynqmp_csu_aes.h>
+#include <drivers/zynqmp_csu_puf.h>
+#include <drivers/zynqmp_pm.h>
+#include <io.h>
+#include <kernel/tee_common_otp.h>
+#include <mm/core_memprot.h>
+#include <tee/tee_cryp_utl.h>
+#include <trace.h>
+#include <utee_defines.h>
+
+static struct {
+ uint8_t key[HW_UNIQUE_KEY_LENGTH];
+ bool ready;
+} huk;
+
+TEE_Result tee_otp_get_hw_unique_key(struct tee_hw_unique_key *hwkey)
+{
+ vaddr_t csu = core_mmu_get_va(CSU_BASE, MEM_AREA_IO_SEC, CSU_SIZE);
+ uint8_t src[HW_UNIQUE_KEY_LENGTH] __aligned_csuaes = { 0 };
+ uint8_t iv[ZYNQMP_EFUSE_MEM(DNA)] __aligned_efuse = { 0 };
+ uint8_t tag[ZYNQMP_GCM_TAG_SIZE] __aligned_csuaes = { 0 };
+ uint8_t sha[HW_UNIQUE_KEY_LENGTH] = { 0 };
+ uint8_t dst[ZYNQMP_CSU_AES_DST_LEN(sizeof(src))]
+ __aligned_csuaes = { 0 };
+ TEE_Result ret = TEE_ERROR_GENERIC;
+ uint32_t status = 0;
+
+ if (huk.ready)
+ goto out;
+
+ COMPILE_TIME_ASSERT(ZYNQMP_EFUSE_LEN(DNA) == ZYNQMP_GCM_IV_SIZE);
+
+ ret = zynqmp_efuse_read(iv, sizeof(iv), DNA, false);
+ if (ret) {
+ EMSG("Can't read the DNA eFuse");
+ return ret;
+ }
+
+ if (tee_hash_createdigest(TEE_ALG_SHA256, iv, ZYNQMP_EFUSE_LEN(DNA),
+ src, sizeof(src))) {
+ EMSG("Can't generate the SHA256 for the DNA eFuse");
+ return ret;
+ }
+
+ status = io_read32(csu + ZYNQMP_CSU_STATUS_OFFSET);
+ if (!(status & ZYNQMP_CSU_STATUS_AUTH)) {
+ /* The DNA is a unique identifier valid but not secure */
+ IMSG("CSU authentication disabled, using development HUK");
+ memcpy(huk.key, src, sizeof(huk.key));
+ huk.ready = true;
+ goto out;
+ }
+
+ /*
+ * Neither the PMUFW nor the PUF hardware provide an indication of the
+ * PUF KEK registration status. The verification algorithm that follows
+ * encrypts and then decrypts the resulting string regenerating the
+ * PUF KEK in between: if the outputs match, then the PUF KEK was
+ * registered properly and we can use it to generate the HUK.
+ */
+ zynqmp_csu_puf_reset();
+
+ ret = zynqmp_csu_puf_regenerate();
+ if (ret) {
+ EMSG("PUF regeneration error");
+ return ret;
+ }
+
+ memcpy(sha, src, sizeof(sha));
+ /* The dst buffer must be large enough to include the generated tag */
+ ret = zynqmp_csu_aes_encrypt_data(src, sizeof(src), dst, sizeof(dst),
+ tag, sizeof(tag), iv,
+ ZYNQMP_EFUSE_LEN(DNA),
+ ZYNQMP_CSU_AES_KEY_SRC_DEV);
+ if (ret) {
+ EMSG("Can't encrypt DNA, please make sure PUF was registered");
+ return ret;
+ }
+
+ /* regenerate the PUF KEK */
+ ret = zynqmp_csu_puf_regenerate();
+ if (ret) {
+ EMSG("PUF regeneration error");
+ return ret;
+ }
+
+ memset(src, 0, sizeof(src));
+ /* Ignore the tag data from the dst buffer - pass a smaller size */
+ ret = zynqmp_csu_aes_decrypt_data(dst, sizeof(src), src, sizeof(src),
+ tag, sizeof(tag), iv,
+ ZYNQMP_EFUSE_LEN(DNA),
+ ZYNQMP_CSU_AES_KEY_SRC_DEV);
+ if (ret) {
+ EMSG("Can't decrypt DNA, please make sure PUF was registered");
+ return ret;
+ }
+
+ if (memcmp(src, sha, sizeof(sha))) {
+ EMSG("PUF not ready, can't create HUK");
+ return TEE_ERROR_GENERIC;
+ }
+
+ IMSG("HUK ready");
+ /*
+ * The HUK is the SHA-256 DNA eFUSE string AES-GCM encrypted with the
+ * PUF KEK using the DNA eFUSE string as the IV.
+ */
+ memcpy(huk.key, dst, sizeof(huk.key));
+ huk.ready = true;
+out:
+ memcpy(hwkey->data, huk.key, HW_UNIQUE_KEY_LENGTH);
+
+ return TEE_SUCCESS;
+}
diff --git a/core/include/drivers/zynqmp_csu.h b/core/include/drivers/zynqmp_csu.h
index 667a4af4..985689b3 100644
--- a/core/include/drivers/zynqmp_csu.h
+++ b/core/include/drivers/zynqmp_csu.h
@@ -17,6 +17,7 @@
#define ZYNQMP_CSU_FT_STATUS_OFFSET 0x18
#define ZYNQMP_CSU_ISR_OFFSET 0x20
+#define ZYNQMP_CSU_STATUS_AUTH BIT(0)
#define ZYNQMP_CSU_SSS_DMA0_STREAM_TO_AES 0x5A0
#define ZYNQMP_CSU_DMA_RESET_SET 1
#define ZYNQMP_CSU_DMA_RESET_CLR 0