From b22c85ed275b9f6a2ca9ed329649f990c884b40d Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 21 Apr 2026 20:53:48 +0200 Subject: [PATCH] Added support for fTPM in TrustZone + STM32H5 test app --- .../workflows/trustzone-emulator-tests.yml | 13 + Makefile | 2 +- config/examples/stm32h5-tz-ftpm.config | 35 +++ include/user_settings.h | 9 +- include/wolfboot/wcs_ftpm.h | 27 ++ lib/wolfTPM | 2 +- options.mk | 53 ++++ src/ftpm_callable.c | 146 +++++++++++ src/loader.c | 2 +- src/wc_callable.c | 7 + test-app/Makefile | 40 ++- test-app/app_stm32h5.c | 244 +++++++++++++++++- test-app/wcs/ftpm_stub.c | 208 +++++++++++++++ test-app/wcs/user_settings.h | 2 +- tools/config.mk | 2 + 15 files changed, 780 insertions(+), 12 deletions(-) create mode 100644 config/examples/stm32h5-tz-ftpm.config create mode 100644 include/wolfboot/wcs_ftpm.h create mode 100644 src/ftpm_callable.c create mode 100644 test-app/wcs/ftpm_stub.c diff --git a/.github/workflows/trustzone-emulator-tests.yml b/.github/workflows/trustzone-emulator-tests.yml index 757e779e..95214785 100644 --- a/.github/workflows/trustzone-emulator-tests.yml +++ b/.github/workflows/trustzone-emulator-tests.yml @@ -106,6 +106,19 @@ jobs: make m33mu wolfboot.bin test-app/image_v1_signed.bin:0x60000 --uart-stdout --expect-bkpt 0x7f --timeout 600 + - name: Clean and build test with fTPM (stm32h5) + run: | + make clean distclean + cp config/examples/stm32h5-tz-ftpm.config .config + make + m33mu wolfboot.bin test-app/image_v1_signed.bin:0x60000 --uart-stdout --expect-bkpt 0x7f --timeout 600 \ + | tee /tmp/m33mu-ftpm.log + grep -q "fTPM PCR0 extended:" /tmp/m33mu-ftpm.log + grep -q "fTPM unsealed secret: wolfBoot fTPM PCR secret" /tmp/m33mu-ftpm.log + grep -q "fTPM NSC tests passed" /tmp/m33mu-ftpm.log + grep -q "\\[BKPT\\] imm=0x7f" /tmp/m33mu-ftpm.log + grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-ftpm.log + - name: Clean and build test with DICE attestation + OTP (stm32h5) run: | make clean distclean diff --git a/Makefile b/Makefile index 775822c7..f85d53f7 100644 --- a/Makefile +++ b/Makefile @@ -575,7 +575,7 @@ keys: $(PRIVATE_KEY) clean: $(Q)rm -f src/*.o hal/*.o hal/spi/*.o test-app/*.o src/x86/*.o $(Q)rm -f src/wolfboot_tz_nsc.o - $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/hal/*.o $(WOLFBOOT_LIB_WOLFTPM)/examples/pcr/*.o + $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/src/fwtpm/*.o $(WOLFBOOT_LIB_WOLFTPM)/hal/*.o $(WOLFBOOT_LIB_WOLFTPM)/examples/pcr/*.o $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/Renesas/*.o $(Q)rm -f wolfboot.bin wolfboot.elf wolfboot.map test-update.rom wolfboot.hex wolfboot.srec factory.srec $(Q)rm -f $(MACHINE_OBJ) $(MAIN_TARGET) $(LSCRIPT) diff --git a/config/examples/stm32h5-tz-ftpm.config b/config/examples/stm32h5-tz-ftpm.config new file mode 100644 index 00000000..21d10f95 --- /dev/null +++ b/config/examples/stm32h5-tz-ftpm.config @@ -0,0 +1,35 @@ +ARCH?=ARM +TZEN?=1 +TARGET?=stm32h5 +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +CORTEX_M0?=0 +CORTEX_M33?=1 +NO_ASM?=0 +NO_MPU=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=1 +WOLFBOOT_VERSION?=1 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 +WOLFBOOT_PARTITION_SIZE?=0xA0000 +WOLFBOOT_SECTOR_SIZE?=0x2000 +WOLFBOOT_KEYVAULT_ADDRESS?=0x0C040000 +WOLFBOOT_KEYVAULT_SIZE?=0x1C000 +WOLFBOOT_NSC_ADDRESS?=0x0C05C000 +WOLFBOOT_NSC_SIZE?=0x4000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08060000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x0C100000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x0C1A0000 +FLAGS_HOME=0 +DISABLE_BACKUP=0 +WOLFCRYPT_TZ=1 +WOLFCRYPT_TZ_FTPM=1 +IMAGE_HEADER_SIZE?=1024 +ARMORED=1 diff --git a/include/user_settings.h b/include/user_settings.h index ca93d72a..a8735216 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -434,7 +434,7 @@ extern int tolower(int c); # define NO_CODING #endif -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && !defined(WOLFBOOT_TZ_FTPM) /* Do not use heap */ #define WOLFTPM2_NO_HEAP /* small stack options */ @@ -632,6 +632,13 @@ extern int tolower(int c); #undef NO_KDF #endif +#if defined(WOLFBOOT_TZ_FTPM) +#undef NO_CMAC +#undef NO_KDF +#define WOLFSSL_AES_CFB +#define WOLFSSL_SHA384 +#endif + #ifdef __QNX__ # define WOLFSSL_HAVE_MIN # define WOLFSSL_HAVE_MAX diff --git a/include/wolfboot/wcs_ftpm.h b/include/wolfboot/wcs_ftpm.h new file mode 100644 index 00000000..1cd28f65 --- /dev/null +++ b/include/wolfboot/wcs_ftpm.h @@ -0,0 +1,27 @@ +/* wcs_ftpm.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + */ + +#ifndef WOLFBOOT_WCS_FTPM_H +#define WOLFBOOT_WCS_FTPM_H + +#include +#include "wolfboot/wc_secure.h" + +#ifdef WOLFBOOT_TZ_FTPM + +#ifndef WCS_FTPM_MAX_COMMAND_SIZE +#define WCS_FTPM_MAX_COMMAND_SIZE 4096U +#endif + +int CSME_NSE_API wcs_ftpm_transmit(const uint8_t *cmd, uint32_t cmdSz, + uint8_t *rsp, uint32_t *rspSz); + +void wcs_ftpm_init(void); + +#endif /* WOLFBOOT_TZ_FTPM */ + +#endif /* WOLFBOOT_WCS_FTPM_H */ diff --git a/lib/wolfTPM b/lib/wolfTPM index efaab4af..c8d51e96 160000 --- a/lib/wolfTPM +++ b/lib/wolfTPM @@ -1 +1 @@ -Subproject commit efaab4afe67fbe8616a08d46b721cb0cbd375894 +Subproject commit c8d51e969301859b16fd62fdaf2011a23546d8f2 diff --git a/options.mk b/options.mk index e75f43c6..2c4b22b0 100644 --- a/options.mk +++ b/options.mk @@ -810,6 +810,15 @@ ifeq ($(WOLFCRYPT_TZ_PKCS11),1) ifeq ($(WOLFCRYPT_TZ_PSA),1) $(error WOLFCRYPT_TZ_PKCS11 and WOLFCRYPT_TZ_PSA are mutually exclusive) endif + ifeq ($(WOLFCRYPT_TZ_FTPM),1) + $(error WOLFCRYPT_TZ_PKCS11 and WOLFCRYPT_TZ_FTPM are mutually exclusive) + endif +endif + +ifeq ($(WOLFCRYPT_TZ_PSA),1) + ifeq ($(WOLFCRYPT_TZ_FTPM),1) + $(error WOLFCRYPT_TZ_PSA and WOLFCRYPT_TZ_FTPM are mutually exclusive) + endif endif ifeq ($(WOLFCRYPT_TZ_PKCS11),1) @@ -919,6 +928,50 @@ ifeq ($(WOLFCRYPT_TZ_PSA),1) endif endif +ifeq ($(WOLFCRYPT_TZ_FTPM),1) + CFLAGS+=-DWOLFBOOT_TZ_FTPM + CFLAGS+=-DWOLFCRYPT_SECURE_MODE + CFLAGS+=-DWOLFTPM_FWTPM + CFLAGS+=-DFWTPM_NO_NV + CFLAGS+=-DWC_RSA_PSS + CFLAGS+=-DWOLFSSL_PSS_SALT_LEN_DISCOVER + CFLAGS+=-DFWTPM_MAX_COMMAND_SIZE=4096 + CFLAGS+=-I$(WOLFBOOT_LIB_WOLFTPM) + ifeq ($(USE_CLANG),1) + CLANG_MULTILIB_FLAGS:=$(filter -mthumb -mlittle-endian,$(LDFLAGS)) $(filter -mcpu=%,$(CFLAGS)) + LIBS+=$(shell $(CLANG_GCC_NAME) $(CLANG_MULTILIB_FLAGS) -print-file-name=libc.a) + LIBS+=$(shell $(CLANG_GCC_NAME) $(CLANG_MULTILIB_FLAGS) -print-libgcc-file-name) + else + LDFLAGS+=--specs=nano.specs + endif + WOLFCRYPT_OBJS+=src/store_sbrk.o + WOLFCRYPT_OBJS+=src/ftpm_callable.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFTPM)/src/fwtpm/fwtpm.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFTPM)/src/fwtpm/fwtpm_command.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFTPM)/src/fwtpm/fwtpm_crypto.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFTPM)/src/fwtpm/fwtpm_nv.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_util.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_packet.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_crypto.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_param_enc.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hmac.o + ifneq ($(SIGN),ED25519) + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o + endif + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wc_encrypt.o + ifeq ($(ENCRYPT_WITH_AES128)$(ENCRYPT_WITH_AES256),) + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/aes.o + endif + WOLFCRYPT_OBJS+=$(RSA_OBJS) + ifeq ($(findstring ECC,$(SIGN)),) + ifeq ($(findstring ECC,$(SIGN_SECONDARY)),) + WOLFCRYPT_OBJS+=$(ECC_OBJS) + WOLFCRYPT_OBJS+=$(MATH_OBJS) + endif + endif + STACK_USAGE=20000 +endif + OBJS+=$(PUBLIC_KEY_OBJS) ifneq ($(STAGE1),1) OBJS+=$(UPDATE_OBJS) diff --git a/src/ftpm_callable.c b/src/ftpm_callable.c new file mode 100644 index 00000000..8a4c39ab --- /dev/null +++ b/src/ftpm_callable.c @@ -0,0 +1,146 @@ +/* ftpm_callable.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + */ + +#ifdef WOLFBOOT_TZ_FTPM + +#include +#include + +#include "store_sbrk.h" +#include "wolfboot/wcs_ftpm.h" +#include "wolftpm/fwtpm/fwtpm.h" +#include "wolftpm/fwtpm/fwtpm_command.h" +#include "wolftpm/fwtpm/fwtpm_nv.h" +#include "wolftpm/tpm2_types.h" + +static FWTPM_CTX ftpm_ctx; +static int ftpm_ready; + +extern unsigned int _start_heap; +extern unsigned int _heap_size; + +void *_sbrk(unsigned int incr) +{ + static uint8_t *heap; + return wolfboot_store_sbrk(incr, &heap, (uint8_t *)&_start_heap, + (uint32_t)(&_heap_size)); +} + +#ifndef FWTPM_NO_NV +#define WCS_FTPM_NV_SIZE (16U * 1024U) +static uint8_t ftpm_nv[WCS_FTPM_NV_SIZE]; + +static int ftpm_nv_read(void *ctx, word32 offset, byte *buf, word32 size) +{ + uint8_t *nv = (uint8_t *)ctx; + + if (nv == NULL || buf == NULL || offset > WCS_FTPM_NV_SIZE || + size > (WCS_FTPM_NV_SIZE - offset)) { + return BAD_FUNC_ARG; + } + + XMEMCPY(buf, nv + offset, size); + return TPM_RC_SUCCESS; +} + +static int ftpm_nv_write(void *ctx, word32 offset, const byte *buf, + word32 size) +{ + uint8_t *nv = (uint8_t *)ctx; + + if (nv == NULL || buf == NULL || offset > WCS_FTPM_NV_SIZE || + size > (WCS_FTPM_NV_SIZE - offset)) { + return BAD_FUNC_ARG; + } + + XMEMCPY(nv + offset, buf, size); + return TPM_RC_SUCCESS; +} + +static int ftpm_nv_erase(void *ctx, word32 offset, word32 size) +{ + uint8_t *nv = (uint8_t *)ctx; + + if (nv == NULL || offset > WCS_FTPM_NV_SIZE || + size > (WCS_FTPM_NV_SIZE - offset)) { + return BAD_FUNC_ARG; + } + + XMEMSET(nv + offset, 0xFF, size); + return TPM_RC_SUCCESS; +} + +static FWTPM_NV_HAL ftpm_nv_hal = { + ftpm_nv_read, + ftpm_nv_write, + ftpm_nv_erase, + ftpm_nv, + WCS_FTPM_NV_SIZE +}; +#endif /* !FWTPM_NO_NV */ + +static uint32_t ftpm_rsp_size(const uint8_t *rsp, int rspLen) +{ + if (rsp == NULL || rspLen < TPM2_HEADER_SIZE) { + return 0; + } + + return ((uint32_t)rsp[2] << 24) | + ((uint32_t)rsp[3] << 16) | + ((uint32_t)rsp[4] << 8) | + (uint32_t)rsp[5]; +} + +void wcs_ftpm_init(void) +{ + int rc; + +#ifndef FWTPM_NO_NV + XMEMSET(ftpm_nv, 0xFF, sizeof(ftpm_nv)); +#endif + XMEMSET(&ftpm_ctx, 0, sizeof(ftpm_ctx)); +#ifndef FWTPM_NO_NV + (void)FWTPM_NV_SetHAL(&ftpm_ctx, &ftpm_nv_hal); +#endif + + rc = FWTPM_Init(&ftpm_ctx); + if (rc == 0) { + ftpm_ctx.wasStarted = 1; + ftpm_ready = 1; + } +} + +int CSME_NSE_API wcs_ftpm_transmit(const uint8_t *cmd, uint32_t cmdSz, + uint8_t *rsp, uint32_t *rspSz) +{ + int rspLen; + + if (!ftpm_ready) { + return TPM_RC_INITIALIZE; + } + if (cmd == NULL || rsp == NULL || rspSz == NULL || cmdSz == 0U || + cmdSz > WCS_FTPM_MAX_COMMAND_SIZE || *rspSz == 0U || + *rspSz > WCS_FTPM_MAX_COMMAND_SIZE) { + return BAD_FUNC_ARG; + } + + rspLen = (int)*rspSz; + int rc = FWTPM_ProcessCommand(&ftpm_ctx, cmd, (int)cmdSz, rsp, &rspLen, 0); + if (rc >= 0) { + uint32_t wireSz = ftpm_rsp_size(rsp, rspLen); + if (wireSz > 0U && wireSz <= *rspSz) { + *rspSz = wireSz; + rc = TPM_RC_SUCCESS; + } + else if (rspLen >= 0) { + *rspSz = (uint32_t)rspLen; + } + } + return rc; +} + +#endif /* WOLFBOOT_TZ_FTPM */ diff --git a/src/loader.c b/src/loader.c index 929b8188..59527031 100644 --- a/src/loader.c +++ b/src/loader.c @@ -123,7 +123,7 @@ int main(void) uart_init(UART_FLASH_BITRATE, 8, 'N', 1); uart_send_current_version(); #endif -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && !defined(WOLFBOOT_TZ_FTPM) if (wolfBoot_tpm2_init() != 0) { wolfBoot_panic(); } diff --git a/src/wc_callable.c b/src/wc_callable.c index 656f9746..f3159e8c 100644 --- a/src/wc_callable.c +++ b/src/wc_callable.c @@ -30,6 +30,10 @@ #include "hal.h" #include +#ifdef WOLFBOOT_TZ_FTPM +#include "wolfboot/wcs_ftpm.h" +#endif + static WC_RNG wcs_rng; int CSME_NSE_API wcs_get_random(uint8_t *rand, uint32_t size) @@ -41,6 +45,9 @@ void wcs_Init(void) { hal_trng_init(); wc_InitRng(&wcs_rng); +#ifdef WOLFBOOT_TZ_FTPM + wcs_ftpm_init(); +#endif } #endif /* WOLFCRYPT_SECURE_MODE */ diff --git a/test-app/Makefile b/test-app/Makefile index bb50facb..531bdd7f 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -4,9 +4,13 @@ -include ./wcs/pkcs11.mk WOLFBOOT_LIB_WOLFSSL?=../lib/wolfssl +WOLFBOOT_LIB_WOLFTPM?=../lib/wolfTPM WOLFSSL_LOCAL_OBJDIR?=wolfssl_obj +WOLFTPM_LOCAL_OBJDIR?=wolftpm_obj vpath %.c $(WOLFBOOT_LIB_WOLFSSL) vpath %.S $(WOLFBOOT_LIB_WOLFSSL) +vpath %.c $(WOLFBOOT_LIB_WOLFTPM) +vpath %.S $(WOLFBOOT_LIB_WOLFTPM) TARGET?=none ARCH?=ARM @@ -100,6 +104,11 @@ ifeq ($(WOLFCRYPT_TZ_PSA),1) WOLFCRYPT_TZ_PSA=1 endif +ifeq ($(WOLFCRYPT_TZ_FTPM),1) + WOLFCRYPT_TZ=1 + WOLFCRYPT_TZ_FTPM=1 +endif + # Setup default linker flags LDFLAGS+=-T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map -nostartfiles @@ -325,6 +334,24 @@ ifeq ($(TZEN),1) $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/psa/psa_hash.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/psa/psa_pkcbs.o endif + ifeq ($(WOLFCRYPT_TZ_FTPM),1) + CFLAGS:=$(filter-out -DWOLFTPM_FWTPM,$(CFLAGS)) + CFLAGS+=-DWOLFBOOT_TZ_FTPM -DWOLFBOOT_TPM -DWOLFTPM_ADV_IO + CFLAGS+=-DWOLFTPM_EXAMPLE_HAL + CFLAGS+=-I"$(WOLFBOOT_LIB_WOLFTPM)" + APP_OBJS+=./wcs/ftpm_stub.o + WOLFTPM_APP_OBJS+=\ + $(WOLFBOOT_LIB_WOLFTPM)/src/tpm2.o \ + $(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_packet.o \ + $(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_tis.o \ + $(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_wrap.o \ + $(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_param_enc.o \ + $(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_util.o \ + $(WOLFBOOT_LIB_WOLFTPM)/src/tpm2_crypto.o + WOLFTPM_APP_OBJS := $(patsubst $(WOLFBOOT_LIB_WOLFTPM)/%, \ + $(WOLFTPM_LOCAL_OBJDIR)/%, $(WOLFTPM_APP_OBJS)) + APP_OBJS+=$(sort $(WOLFTPM_APP_OBJS)) + endif WOLFCRYPT_APP_OBJS := $(patsubst $(WOLFBOOT_LIB_WOLFSSL)/%, \ $(WOLFSSL_LOCAL_OBJDIR)/%, $(WOLFCRYPT_APP_OBJS)) ifneq ($(WOLFCRYPT_TZ_PKCS11),1) @@ -908,6 +935,7 @@ endif # Capture final flags for locally built wolfSSL objects. WOLFSSL_CFLAGS:=$(CFLAGS) +WOLFTPM_CFLAGS:=$(CFLAGS) ifeq ($(WOLFHSM_CLIENT),1) CFLAGS += -DSTRING_USER -I"$(WOLFBOOT_LIB_WOLFSSL)" @@ -995,9 +1023,19 @@ $(WOLFSSL_LOCAL_OBJDIR)/%.o: %.S $(Q)mkdir -p $(dir $@) $(Q)$(CC) $(WOLFSSL_CFLAGS) -c $(OUTPUT_FLAG) $@ $< +$(WOLFTPM_LOCAL_OBJDIR)/%.o: %.c + @echo "\t[CC-$(ARCH)] $@" + $(Q)mkdir -p $(dir $@) + $(Q)$(CC) $(WOLFTPM_CFLAGS) -c $(OUTPUT_FLAG) $@ $< + +$(WOLFTPM_LOCAL_OBJDIR)/%.o: %.S + @echo "\t[AS-$(ARCH)] $@" + $(Q)mkdir -p $(dir $@) + $(Q)$(CC) $(WOLFTPM_CFLAGS) -c $(OUTPUT_FLAG) $@ $< + clean: $(Q)rm -f *.bin *.elf tags *.o $(LSCRIPT) $(APP_OBJS) wcs/*.o - $(Q)rm -rf $(WOLFSSL_LOCAL_OBJDIR) + $(Q)rm -rf $(WOLFSSL_LOCAL_OBJDIR) $(WOLFTPM_LOCAL_OBJDIR) $(LSCRIPT): $(LSCRIPT_TEMPLATE) FORCE $(Q)printf "%d" $(WOLFBOOT_PARTITION_BOOT_ADDRESS) > .wolfboot-offset diff --git a/test-app/app_stm32h5.c b/test-app/app_stm32h5.c index 2c68e258..f8f13511 100644 --- a/test-app/app_stm32h5.c +++ b/test-app/app_stm32h5.c @@ -36,10 +36,15 @@ #endif #include "target.h" -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && !defined(WOLFBOOT_TZ_FTPM) #include "tpm.h" #endif +#ifdef WOLFBOOT_TZ_FTPM +#include "wolftpm/tpm2_wrap.h" +#include "wolfssl/wolfcrypt/sha256.h" +#endif + #ifdef WOLFBOOT_TZ_PKCS11 #include "test_pkcs11.h" #endif @@ -201,7 +206,7 @@ static int cmd_timestamp(const char *args); static int cmd_update(const char *args); static int cmd_update_xmodem(const char *args); static int cmd_reboot(const char *args); -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && !defined(WOLFBOOT_TZ_FTPM) static int cmd_tpm_info(const char *args); #ifdef WOLFTPM_MFG_IDENTITY static int cmd_tpm_idevid(const char *args); @@ -210,6 +215,9 @@ static int cmd_tpm_signed_timestamp(const char *args); static int cmd_tpm_quote(const char *args); #endif #endif +#ifdef WOLFBOOT_TZ_FTPM +static int cmd_ftpm_test(const char *args); +#endif #define CMD_BUFFER_SIZE 256 @@ -238,7 +246,7 @@ struct console_command COMMANDS[] = {cmd_test, "test", "run the wolfCrypt test"}, {cmd_update_xmodem, "update", "update the firmware via XMODEM"}, {cmd_reboot, "reboot", "reboot the system"}, -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && !defined(WOLFBOOT_TZ_FTPM) {cmd_tpm_info, "tpm", "get TPM capabilities"}, #ifdef WOLFTPM_MFG_IDENTITY {cmd_tpm_idevid, "idevid", "show Initial Device Identification (IDevID) certificate"}, @@ -246,6 +254,9 @@ struct console_command COMMANDS[] = {cmd_tpm_signed_timestamp, "signed_time", "TPM IAK signed timestamp attestation report"}, {cmd_tpm_quote, "quote", "TPM IAK signed PCR(s) attestation report"}, #endif +#endif +#ifdef WOLFBOOT_TZ_FTPM + {cmd_ftpm_test, "ftpm", "run fTPM tests through the secure NSC interface"}, #endif {NULL, "", ""} }; @@ -852,7 +863,7 @@ static int cmd_test(const char *args) return 0; } -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && !defined(WOLFBOOT_TZ_FTPM) #include #include @@ -1119,7 +1130,221 @@ static int cmd_tpm_quote(const char *args) return rc; } #endif /* WOLFTPM_MFG_IDENTITY */ -#endif /* WOLFBOOT_TPM */ +#endif /* WOLFBOOT_TPM && !WOLFBOOT_TZ_FTPM */ + +#ifdef WOLFBOOT_TZ_FTPM +extern int TPM2_IoCb_FtpmNsc(TPM2_CTX *ctx, INT32 isRead, UINT32 addr, + BYTE *buf, UINT16 size, void *userCtx); + +static int ftpm_expected_pcr_after_extend(const uint8_t *before, + const uint8_t *event, uint8_t *expected) +{ + wc_Sha256 sha; + int rc; + + rc = wc_InitSha256(&sha); + if (rc == 0) { + rc = wc_Sha256Update(&sha, before, WC_SHA256_DIGEST_SIZE); + } + if (rc == 0) { + rc = wc_Sha256Update(&sha, event, WC_SHA256_DIGEST_SIZE); + } + if (rc == 0) { + rc = wc_Sha256Final(&sha, expected); + } + + wc_Sha256Free(&sha); + return rc; +} + +static int ftpm_seal_unseal_pcr_secret(WOLFTPM2_DEV *dev) +{ + static const uint8_t secret[] = "wolfBoot fTPM PCR secret"; + uint8_t pcrArray[1] = { 0 }; + uint8_t policyDigest[TPM_MAX_DIGEST_SIZE]; + word32 policyDigestSz = sizeof(policyDigest); + WOLFTPM2_KEY storage; + WOLFTPM2_KEYBLOB sealBlob; + WOLFTPM2_SESSION trialSession; + WOLFTPM2_SESSION policySession; + TPMT_PUBLIC sealTemplate; + Unseal_In unsealIn; + Unseal_Out unsealOut; + int rc; + + XMEMSET(&storage, 0, sizeof(storage)); + XMEMSET(&sealBlob, 0, sizeof(sealBlob)); + XMEMSET(&trialSession, 0, sizeof(trialSession)); + XMEMSET(&policySession, 0, sizeof(policySession)); + XMEMSET(&sealTemplate, 0, sizeof(sealTemplate)); + XMEMSET(&unsealIn, 0, sizeof(unsealIn)); + XMEMSET(&unsealOut, 0, sizeof(unsealOut)); + + rc = wolfTPM2_CreateSRK(dev, &storage, TPM_ALG_ECC, NULL, 0); + if (rc != TPM_RC_SUCCESS) { + printf("fTPM seal SRK failed: 0x%x\r\n", rc); + return rc; + } + + rc = wolfTPM2_StartSession(dev, &trialSession, NULL, NULL, + TPM_SE_TRIAL, TPM_ALG_NULL); + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_PolicyPCR(dev, trialSession.handle.hndl, + TPM_ALG_SHA256, pcrArray, 1); + } + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_GetPolicyDigest(dev, trialSession.handle.hndl, + policyDigest, &policyDigestSz); + } + wolfTPM2_UnloadHandle(dev, &trialSession.handle); + if (rc != TPM_RC_SUCCESS) { + printf("fTPM seal policy failed: 0x%x\r\n", rc); + wolfTPM2_UnloadHandle(dev, &storage.handle); + return rc; + } + + wolfTPM2_GetKeyTemplate_KeySeal(&sealTemplate, TPM_ALG_SHA256); + sealTemplate.objectAttributes &= ~TPMA_OBJECT_userWithAuth; + sealTemplate.authPolicy.size = (UINT16)policyDigestSz; + XMEMCPY(sealTemplate.authPolicy.buffer, policyDigest, policyDigestSz); + + rc = wolfTPM2_CreateKeySeal_ex(dev, &sealBlob, &storage.handle, + &sealTemplate, NULL, 0, TPM_ALG_SHA256, pcrArray, 1, + secret, (int)sizeof(secret)); + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_LoadKey(dev, &sealBlob, &storage.handle); + } + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_StartSession(dev, &policySession, NULL, NULL, + TPM_SE_POLICY, TPM_ALG_NULL); + } + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_PolicyPCR(dev, policySession.handle.hndl, + TPM_ALG_SHA256, pcrArray, 1); + } + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_SetAuthSession(dev, 0, &policySession, + TPMA_SESSION_continueSession); + } + if (rc == TPM_RC_SUCCESS) { + wolfTPM2_SetAuthHandleName(dev, 0, &sealBlob.handle); + unsealIn.itemHandle = sealBlob.handle.hndl; + rc = TPM2_Unseal(&unsealIn, &unsealOut); + } + + wolfTPM2_UnloadHandle(dev, &policySession.handle); + wolfTPM2_UnloadHandle(dev, &sealBlob.handle); + wolfTPM2_UnloadHandle(dev, &storage.handle); + + if (rc != TPM_RC_SUCCESS) { + printf("fTPM unseal failed: 0x%x\r\n", rc); + return rc; + } + + if (unsealOut.outData.size != sizeof(secret) || + XMEMCMP(unsealOut.outData.buffer, secret, sizeof(secret)) != 0) { + printf("fTPM unsealed secret mismatch\r\n"); + return TPM_RC_FAILURE; + } + + printf("fTPM unsealed secret: %.*s\r\n", + unsealOut.outData.size, unsealOut.outData.buffer); + return TPM_RC_SUCCESS; +} + +static int cmd_ftpm_test(const char *args) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_CAPS caps; + uint8_t rnd[16]; + uint8_t pcr[TPM_MAX_DIGEST_SIZE]; + uint8_t pcrBefore[TPM_SHA256_DIGEST_SIZE]; + uint8_t pcrEvent[TPM_SHA256_DIGEST_SIZE]; + uint8_t pcrExpected[TPM_SHA256_DIGEST_SIZE]; + int pcrSz = 0; + + (void)args; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&caps, 0, sizeof(caps)); + XMEMSET(rnd, 0, sizeof(rnd)); + XMEMSET(pcr, 0, sizeof(pcr)); + XMEMSET(pcrBefore, 0, sizeof(pcrBefore)); + XMEMSET(pcrEvent, 0, sizeof(pcrEvent)); + XMEMSET(pcrExpected, 0, sizeof(pcrExpected)); + + rc = wolfTPM2_Init(&dev, TPM2_IoCb_FtpmNsc, NULL); + if (rc != TPM_RC_SUCCESS) { + printf("fTPM init failed: 0x%x\r\n", rc); + return rc; + } + + rc = wolfTPM2_GetCapabilities(&dev, &caps); + if (rc == TPM_RC_SUCCESS) { + printf("fTPM caps: mfg %s vendor %s fw %u.%u\r\n", + caps.mfgStr, caps.vendorStr, caps.fwVerMajor, caps.fwVerMinor); + } + + if (rc == TPM_RC_SUCCESS) { + int randRc = wolfTPM2_GetRandom(&dev, rnd, sizeof(rnd)); + if (randRc == TPM_RC_SUCCESS) { + printf("fTPM random:"); + print_hex(rnd, sizeof(rnd), 0); + } + else { + printf("fTPM random unavailable: 0x%x\r\n", randRc); + } + } + + if (rc == TPM_RC_SUCCESS) { + pcrSz = sizeof(pcr); + rc = wolfTPM2_ReadPCR(&dev, 0, TPM_ALG_SHA256, pcr, &pcrSz); + if (rc == TPM_RC_SUCCESS) { + XMEMCPY(pcrBefore, pcr, sizeof(pcrBefore)); + } + } + + if (rc == TPM_RC_SUCCESS) { + rc = wc_Sha256Hash((const uint8_t *)"wolfBoot fTPM PCR event", + 23, pcrEvent); + } + if (rc == TPM_RC_SUCCESS) { + rc = ftpm_expected_pcr_after_extend(pcrBefore, pcrEvent, pcrExpected); + } + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_ExtendPCR(&dev, 0, TPM_ALG_SHA256, pcrEvent, + sizeof(pcrEvent)); + } + if (rc == TPM_RC_SUCCESS) { + pcrSz = sizeof(pcr); + rc = wolfTPM2_ReadPCR(&dev, 0, TPM_ALG_SHA256, pcr, &pcrSz); + if (rc == TPM_RC_SUCCESS) { + printf("fTPM PCR0 extended:"); + print_hex(pcr, (uint32_t)pcrSz, 0); + } + } + if (rc == TPM_RC_SUCCESS && (pcrSz != (int)sizeof(pcrExpected) || + XMEMCMP(pcr, pcrExpected, sizeof(pcrExpected)) != 0)) { + printf("fTPM PCR0 extend mismatch\r\n"); + rc = TPM_RC_FAILURE; + } + if (rc == TPM_RC_SUCCESS) { + rc = ftpm_seal_unseal_pcr_secret(&dev); + } + + wolfTPM2_Cleanup(&dev); + + if (rc == TPM_RC_SUCCESS) { + printf("fTPM NSC tests passed\r\n"); + } + else { + printf("fTPM NSC tests failed: 0x%x\r\n", rc); + } + + return rc; +} +#endif /* WOLFBOOT_TZ_FTPM */ static int parse_cmd(const char *cmd) @@ -1268,9 +1493,16 @@ void main(void) #endif cmd_info(NULL); -#ifdef WOLFBOOT_TPM +#if defined(WOLFBOOT_TPM) && !defined(WOLFBOOT_TZ_FTPM) cmd_tpm_info(NULL); #endif +#ifdef WOLFBOOT_TZ_FTPM + ret = cmd_ftpm_test(NULL); + if (ret == 0) + asm volatile ("bkpt #0x7f"); + else + asm volatile ("bkpt #0x7e"); +#endif #if defined(WOLFBOOT_ATTESTATION_TEST) && defined(WOLFCRYPT_TZ_PSA) (void)run_attestation_test(); diff --git a/test-app/wcs/ftpm_stub.c b/test-app/wcs/ftpm_stub.c new file mode 100644 index 00000000..c698c02f --- /dev/null +++ b/test-app/wcs/ftpm_stub.c @@ -0,0 +1,208 @@ +/* ftpm_stub.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + */ + +#ifdef WOLFBOOT_TZ_FTPM + +#include +#include + +#include "wolfboot/wcs_ftpm.h" +#include "wolftpm/tpm2.h" +#include "wolftpm/tpm2_tis.h" + +#define FTPM_TIS_BASE 0xD40000U +#define FTPM_TIS_ACCESS 0x0000U +#define FTPM_TIS_INTF_CAPS 0x0014U +#define FTPM_TIS_STS 0x0018U +#define FTPM_TIS_BURST_COUNT 0x0019U +#define FTPM_TIS_DATA_FIFO 0x0024U +#define FTPM_TIS_DID_VID 0x0F00U +#define FTPM_TIS_RID 0x0F04U + +#define FTPM_ACCESS_VALID 0x80U +#define FTPM_ACCESS_ACTIVE_LOCALITY 0x20U +#define FTPM_ACCESS_REQUEST_USE 0x02U + +#define FTPM_STS_VALID 0x80U +#define FTPM_STS_COMMAND_READY 0x40U +#define FTPM_STS_GO 0x20U +#define FTPM_STS_DATA_AVAIL 0x10U +#define FTPM_STS_DATA_EXPECT 0x08U + +#define FTPM_BURST_COUNT 64U + +struct ftpm_tis_state { + uint8_t cmd[WCS_FTPM_MAX_COMMAND_SIZE]; + uint8_t rsp[WCS_FTPM_MAX_COMMAND_SIZE]; + uint32_t cmdSz; + uint32_t rspSz; + uint32_t rspPos; + uint8_t locality; + uint8_t status; +}; + +static struct ftpm_tis_state ftpm_tis = { + {0}, {0}, 0, 0, 0, 0, + FTPM_STS_VALID | FTPM_STS_COMMAND_READY +}; + +static uint32_t ftpm_reg_offset(uint32_t addr) +{ + return (addr - FTPM_TIS_BASE) & 0x0FFFU; +} + +static void ftpm_store_le(uint8_t *buf, uint16_t size, uint32_t val) +{ + uint16_t i; + + for (i = 0; i < size; i++) { + buf[i] = (uint8_t)(val >> (8U * i)); + } +} + +static uint32_t ftpm_cmd_size(void) +{ + if (ftpm_tis.cmdSz < TPM2_HEADER_SIZE) { + return TPM2_HEADER_SIZE; + } + + return ((uint32_t)ftpm_tis.cmd[2] << 24) | + ((uint32_t)ftpm_tis.cmd[3] << 16) | + ((uint32_t)ftpm_tis.cmd[4] << 8) | + (uint32_t)ftpm_tis.cmd[5]; +} + +static int ftpm_execute(void) +{ + int rc; + + ftpm_tis.rspSz = sizeof(ftpm_tis.rsp); + ftpm_tis.rspPos = 0; + + rc = wcs_ftpm_transmit(ftpm_tis.cmd, ftpm_tis.cmdSz, ftpm_tis.rsp, + &ftpm_tis.rspSz); + ftpm_tis.cmdSz = 0; + + if (rc == TPM_RC_SUCCESS) { + ftpm_tis.status = FTPM_STS_VALID | FTPM_STS_DATA_AVAIL; + } + else { + ftpm_tis.rspSz = 0; + ftpm_tis.status = FTPM_STS_VALID | FTPM_STS_COMMAND_READY; + } + + return rc; +} + +int TPM2_IoCb_FtpmNsc(TPM2_CTX *ctx, INT32 isRead, UINT32 addr, + BYTE *buf, UINT16 size, void *userCtx) +{ + uint32_t off; + uint16_t burst; + + (void)ctx; + (void)userCtx; + + if (buf == NULL || size == 0U || addr < FTPM_TIS_BASE) { + return BAD_FUNC_ARG; + } + + off = ftpm_reg_offset(addr); + burst = FTPM_BURST_COUNT; + + if (isRead) { + switch (off) { + case FTPM_TIS_ACCESS: + buf[0] = FTPM_ACCESS_VALID | FTPM_ACCESS_ACTIVE_LOCALITY; + break; + case FTPM_TIS_STS: + XMEMSET(buf, 0, size); + buf[0] = ftpm_tis.status; + if (size > 1U) { + buf[1] = (uint8_t)burst; + } + if (size > 2U) { + buf[2] = (uint8_t)(burst >> 8); + } + break; + case FTPM_TIS_BURST_COUNT: + ftpm_store_le(buf, size, burst); + break; + case FTPM_TIS_BURST_COUNT + 1U: + ftpm_store_le(buf, size, burst >> 8); + break; + case FTPM_TIS_DATA_FIFO: + if (ftpm_tis.rspPos + size > ftpm_tis.rspSz) { + return TPM_RC_FAILURE; + } + XMEMCPY(buf, ftpm_tis.rsp + ftpm_tis.rspPos, size); + ftpm_tis.rspPos += size; + if (ftpm_tis.rspPos >= ftpm_tis.rspSz) { + ftpm_tis.status = FTPM_STS_VALID | FTPM_STS_COMMAND_READY; + } + break; + case FTPM_TIS_INTF_CAPS: + ftpm_store_le(buf, size, 0x00000183U); + break; + case FTPM_TIS_DID_VID: + ftpm_store_le(buf, size, 0x00011B4EU); + break; + case FTPM_TIS_RID: + buf[0] = 0x01U; + break; + default: + XMEMSET(buf, 0, size); + break; + } + } + else { + switch (off) { + case FTPM_TIS_ACCESS: + if ((buf[0] & FTPM_ACCESS_REQUEST_USE) != 0U) { + ftpm_tis.locality = 0; + } + break; + case FTPM_TIS_STS: + if ((buf[0] & FTPM_STS_COMMAND_READY) != 0U) { + ftpm_tis.cmdSz = 0; + ftpm_tis.rspSz = 0; + ftpm_tis.rspPos = 0; + ftpm_tis.status = FTPM_STS_VALID | + FTPM_STS_COMMAND_READY; + } + else if ((buf[0] & FTPM_STS_GO) != 0U) { + return ftpm_execute(); + } + break; + case FTPM_TIS_DATA_FIFO: + if (ftpm_tis.cmdSz + size > sizeof(ftpm_tis.cmd)) { + return TPM_RC_FAILURE; + } + XMEMCPY(ftpm_tis.cmd + ftpm_tis.cmdSz, buf, size); + ftpm_tis.cmdSz += size; + if (ftpm_tis.cmdSz < ftpm_cmd_size()) { + ftpm_tis.status = FTPM_STS_VALID | FTPM_STS_DATA_EXPECT; + } + else { + ftpm_tis.status = FTPM_STS_VALID; + } + break; + default: + break; + } + } + + return TPM_RC_SUCCESS; +} + +int TPM2_IoCb(TPM2_CTX *ctx, INT32 isRead, UINT32 addr, BYTE *buf, + UINT16 size, void *userCtx) +{ + return TPM2_IoCb_FtpmNsc(ctx, isRead, addr, buf, size, userCtx); +} + +#endif /* WOLFBOOT_TZ_FTPM */ diff --git a/test-app/wcs/user_settings.h b/test-app/wcs/user_settings.h index 2f497f77..20e43150 100644 --- a/test-app/wcs/user_settings.h +++ b/test-app/wcs/user_settings.h @@ -156,7 +156,7 @@ extern int tolower(int c); #define HAVE_PKCS8 #define HAVE_PKCS12 -#ifdef SECURE_PKCS11 +#if defined(SECURE_PKCS11) || defined(WOLFBOOT_TZ_FTPM) static inline int wcs_cmse_get_random(unsigned char* output, int sz) { diff --git a/tools/config.mk b/tools/config.mk index 2d158ee3..3aae0835 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -66,6 +66,7 @@ ifeq ($(ARCH),) WOLFCRYPT_TZ?=0 WOLFCRYPT_TZ_PKCS11?=0 WOLFCRYPT_TZ_PSA?=0 + WOLFCRYPT_TZ_FTPM?=0 WOLFBOOT_PARTITION_SIZE?=0x20000 WOLFBOOT_SECTOR_SIZE?=0x20000 WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08020000 @@ -104,6 +105,7 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO WOLFBOOT_UDS_OBKEYS \ WOLFCRYPT_TZ WOLFCRYPT_TZ_PKCS11 \ WOLFCRYPT_TZ_PSA \ + WOLFCRYPT_TZ_FTPM \ WOLFBOOT_PARTITION_SIZE WOLFBOOT_SECTOR_SIZE \ WOLFBOOT_PARTITION_BOOT_ADDRESS WOLFBOOT_PARTITION_UPDATE_ADDRESS \ WOLFBOOT_PARTITION_SWAP_ADDRESS WOLFBOOT_LOAD_ADDRESS \