From 0b50f632ae90be42cff6b9767377406fc984008d Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 27 Jan 2026 15:57:09 -0800 Subject: [PATCH] Add stm33ktpm support LMS and non-LMS --- .github/workflows/cmake-build.yml | 3 + .github/workflows/make-test-swtpm.yml | 3 + .gitignore | 13 + README.md | 33 +- configure.ac | 1 + examples/firmware/README.md | 99 +++++- examples/firmware/include.am | 18 +- examples/firmware/st33_fw_update.c | 405 ++++++++++++++++++++++++ src/tpm2.c | 53 ++++ src/tpm2_wrap.c | 428 ++++++++++++++++++++++++-- tests/unit_tests.c | 114 ++++++- wolftpm/tpm2.h | 18 ++ wolftpm/tpm2_types.h | 29 +- wolftpm/tpm2_wrap.h | 10 +- 14 files changed, 1180 insertions(+), 47 deletions(-) create mode 100644 examples/firmware/st33_fw_update.c diff --git a/.github/workflows/cmake-build.yml b/.github/workflows/cmake-build.yml index de5cbe2c..96b0955d 100644 --- a/.github/workflows/cmake-build.yml +++ b/.github/workflows/cmake-build.yml @@ -22,6 +22,9 @@ jobs: options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_MODULE=st33" - name: "Module ST33 I2C" options: "-DWOLFTPM_INTERFACE=I2C -DWOLFTPM_MODULE=st33" + # ST33 Firmware + - name: "Module ST33 Firmware" + options: "-DWOLFTPM_MODULE=st33 -DWOLFTPM_FIRMWARE=yes" # Other modules use SPI - name: "Module Microchip" options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_MODULE=microchip" diff --git a/.github/workflows/make-test-swtpm.yml b/.github/workflows/make-test-swtpm.yml index 604c29e7..4f7cc926 100644 --- a/.github/workflows/make-test-swtpm.yml +++ b/.github/workflows/make-test-swtpm.yml @@ -66,6 +66,9 @@ jobs: # STMicro ST33KTPM2 - name: st33ktpm2 wolftpm_config: --enable-st33 + # STMicro ST33KTPM2 + - name: st33ktpm2 firmware + wolftpm_config: --enable-st33 --enable-firmware # Microchip - name: microchip wolftpm_config: --enable-microchip diff --git a/.gitignore b/.gitignore index 5d98b803..d6a057fb 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,7 @@ examples/boot/secret_seal examples/boot/secret_unseal examples/firmware/ifx_fw_extract examples/firmware/ifx_fw_update +examples/firmware/st33_fw_update examples/endorsement/get_ek_certs examples/endorsement/verify_ek_cert @@ -151,6 +152,10 @@ UpgradeLog.htm *.VC.db *.filters +# macOS files +._.DS_Store +.DS_Store + # Backup and old files /**/*.bak /**/*.old @@ -170,3 +175,11 @@ UpgradeLog.htm # we won't track the actual working sdkconfig files /IDE/Espressif/**/sdkconfig /IDE/Espressif/**/sdkconfig.old + +# Firmware files +examples/firmware/*.fi +examples/firmware/*.BIN +examples/firmware/*.DATA +examples/firmware/*.MANIFEST +examples/firmware/*.MANIFESTHASH + diff --git a/README.md b/README.md index 39fc78bb..4d14263c 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,7 @@ make install --enable-checkwaitstate Enable TIS / SPI Check Wait State support (default: depends on chip) - WOLFTPM_CHECK_WAIT_STATE --enable-smallstack Enable options to reduce stack usage --enable-tislock Enable Linux Named Semaphore for locking access to SPI device for concurrent access between processes - WOLFTPM_TIS_LOCK +--enable-firmware Enable firmware upgrade support for Infineon SLB9672/SLB9673 and ST ST33 (default: disabled) - WOLFTPM_FIRMWARE_UPGRADE --enable-autodetect Enable Runtime Module Detection (default: enable - when no module specified) - WOLFTPM_AUTODETECT --enable-infineon Enable Infineon SLB9670/SLB9672/SLB9673 TPM Support (default: disabled) - WOLFTPM_SLB9670 / WOLFTPM_SLB9672 @@ -234,10 +235,12 @@ Build wolfTPM: ```bash ./autogen.sh -./configure --enable-st33 [--enable-i2c] +./configure --enable-st33 [--enable-i2c] [--enable-firmware] make ``` +Note: The `--enable-firmware` option enables firmware upgrade support for ST33 TPMs. This adds the `st33_fw_update` example tool for performing firmware updates. + ### Building Microchip ATTPM20 Build wolfTPM: @@ -903,6 +906,34 @@ Connection: close ``` +### ST33 Firmware Update Example + +The firmware update example allows updating firmware on STMicro ST33 TPMs. Build with `--enable-st33 --enable-firmware` to enable this example. + +LMS (Leighton-Micali Signature) support is based on firmware version: +- **Firmware < 512**: Legacy firmware - Non-LMS format required +- **Firmware >= 512**: Modern firmware - LMS format required + +```bash +# Display firmware information +./examples/firmware/st33_fw_update + +# Cancel any in-progress firmware update +./examples/firmware/st33_fw_update --abandon + +# Perform firmware update (format auto-detected from TPM firmware version) +./examples/firmware/st33_fw_update +``` + +Example output: +``` +ST33 Firmware Update Tool +Mfg STM (2), Vendor ST33KTPM2X, Fw 9.512 (0x0) +Firmware version details: Major=9, Minor=512, Vendor=0x0 +Hardware: ST33K (modern firmware, Generation 2) +Firmware update: LMS format required +``` + ## Device Identity and Attestation Keys The TCG published a specification for TPM manufacture guidance on setting up keys that can be used for device identiy and attestation. diff --git a/configure.ac b/configure.ac index 1391c2a0..c9df3658 100644 --- a/configure.ac +++ b/configure.ac @@ -482,6 +482,7 @@ AM_CONDITIONAL([HAVE_LIBWOLFSSL], [test "x$ENABLED_WOLFCRYPT" = "xyes"]) AM_CONDITIONAL([BUILD_I2C], [test "x$ENABLED_I2C" = "xyes"]) AM_CONDITIONAL([BUILD_ADVIO], [test "x$ENABLED_ADVIO" = "xyes"]) AM_CONDITIONAL([BUILD_ST], [test "x$ENABLED_ST" = "xyes"]) +AM_CONDITIONAL([BUILD_ST33], [test "x$ENABLED_ST33" = "xyes"]) AM_CONDITIONAL([BUILD_MICROCHIP], [test "x$ENABLED_MICROCHIP" = "xyes"]) AM_CONDITIONAL([BUILD_INFINEON], [test "x$ENABLED_INFINEON" != "xno"]) AM_CONDITIONAL([BUILD_DEVTPM], [test "x$ENABLED_DEVTPM" = "xyes"]) diff --git a/examples/firmware/README.md b/examples/firmware/README.md index 45e4a604..66d0c60d 100644 --- a/examples/firmware/README.md +++ b/examples/firmware/README.md @@ -1,6 +1,8 @@ # TPM Firmware Update Support -Currently wolfTPM supports firmware update capability for the Infineon SLB9672 (SPI) and SLB9673 (I2C) TPM 2.0 modules. Infineon has open sourced their firmware update. +Currently wolfTPM supports firmware update capability for: +- Infineon SLB9672 (SPI) and SLB9673 (I2C) TPM 2.0 modules. Infineon has open sourced their firmware update. +- STMicroelectronics ST33KTPM TPM 2.0 modules. Support includes both Generation 1 firmware versions (< 512, without LMS signature) and Generation 2 firmware versions (>= 512, with LMS signature requirement). ## Infineon Firmware @@ -102,3 +104,98 @@ Mfg IFX (1), Vendor SLB9673, Fw 26.13 (0x456a) Operational mode: Normal TPM operational mode (0x0) KeyGroupId 0x7, FwCounter 1253 (254 same) ``` + +## ST33 Firmware Update + +### Firmware Format Auto-Detection + +ST33KTPM firmware update automatically detects the required format based on TPM firmware version: + +- **Legacy firmware (< 512, e.g., 9.257)**: Non-LMS format + - Manifest size: 177 bytes + - Generation 1 firmware (ECC-only) + +- **Modern firmware (>= 512, e.g., 9.512)**: LMS format + - Manifest size: 2697 bytes (includes embedded LMS signature) + - Generation 2 firmware (LMS mandatory) + +The firmware version is automatically detected from `fwVerMinor` in TPM capabilities. The correct manifest size is determined automatically - no manual format selection is needed. + +### Updating the firmware + +The `st33_fw_update` tool automatically detects the firmware format. + +```sh +# Help +./st33_fw_update --help +ST33 Firmware Update Usage: + ./st33_fw_update (get info) + ./st33_fw_update --abandon (cancel) + ./st33_fw_update + +Firmware format is auto-detected from TPM firmware version: + - Firmware < 512: Non-LMS format (177 byte manifest) + - Firmware >= 512: LMS format (2697 byte manifest with embedded signature) + +# Run without arguments to display the current firmware information +./st33_fw_update +ST33 Firmware Update Tool +TPM2: Caps 0x30000415, Did 0x0003, Vid 0x104a, Rid 0x 1 +TPM2_Startup pass +Mfg STM (2), Vendor ST33KTPM2X, Fw 9.257 (0x0) +Firmware version details: Major=9, Minor=257, Vendor=0x0 +Hardware: ST33K (legacy firmware, Generation 1) +Firmware update: Non-LMS format required + +# Run with firmware file (format auto-detected from TPM version) +./st33_fw_update TPM_ST33KTPM2X_00090200_V1.fi +ST33 Firmware Update Tool + Firmware File: TPM_ST33KTPM2X_00090200_V1.fi +TPM2: Caps 0x30000415, Did 0x0003, Vid 0x104a, Rid 0x 1 +TPM2_Startup pass +Mfg STM (2), Vendor ST33KTPM2X, Fw 9.257 (0x0) +Firmware version details: Major=9, Minor=257, Vendor=0x0 +Hardware: ST33K (legacy firmware, Generation 1) +Firmware update: Non-LMS format required + Format: Non-LMS (from TPM firmware version) +Firmware Update: + Total file size: 364290 bytes + Manifest (blob0): 177 bytes + Firmware data: 364113 bytes +... +Firmware update completed successfully. +Please reset or power cycle the TPM. + +# Example with LMS firmware (Generation 2 TPM, firmware >= 512) +./st33_fw_update ST33KTPM2X_FAC_00090200_V2.fi +ST33 Firmware Update Tool + Firmware File: ST33KTPM2X_FAC_00090200_V2.fi +TPM2: Caps 0x30000415, Did 0x0003, Vid 0x104a, Rid 0x 3 +TPM2_Startup pass +Mfg STM (2), Vendor ST33KTPM2X, Fw 9.512 (0x0) +Firmware version details: Major=9, Minor=512, Vendor=0x0 +Hardware: ST33K (modern firmware, Generation 2) +Firmware update: LMS format required + Format: LMS (from TPM firmware version) +Firmware Update: + Total file size: 360092 bytes + Manifest (blob0): 2697 bytes + Firmware data: 357395 bytes +... +Firmware update completed successfully. +Please reset or power cycle the TPM. + +# Cancel an ongoing firmware update +./st33_fw_update --abandon +ST33 Firmware Update Tool +TPM2: Caps 0x30000415, Did 0x0003, Vid 0x104a, Rid 0x 1 +TPM2_Startup pass +Mfg STM (2), Vendor ST33KTPM2X, Fw 9.257 (0x0) +Firmware version details: Major=9, Minor=257, Vendor=0x0 +Hardware: ST33K (legacy firmware, Generation 1) +Firmware update: Non-LMS format required +Firmware Update Abandon: +Success: Please reset or power cycle TPM +``` + +**Note**: Firmware files cannot be made public and must be obtained separately from STMicroelectronics. diff --git a/examples/firmware/include.am b/examples/firmware/include.am index f233fe45..4c355b85 100644 --- a/examples/firmware/include.am +++ b/examples/firmware/include.am @@ -8,6 +8,8 @@ EXTRA_DIST += examples/firmware/Makefile EXTRA_DIST += examples/firmware/ifx_fw_extract.c if BUILD_EXAMPLES +if BUILD_FIRMWARE + if BUILD_INFINEON noinst_PROGRAMS += examples/firmware/ifx_fw_update noinst_HEADERS += examples/firmware/ifx_fw_update.h @@ -16,9 +18,21 @@ examples_firmware_ifx_fw_update_SOURCES = examples/firmware/ifx_fw_update.c examples_firmware_ifx_fw_update_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) examples_firmware_ifx_fw_update_DEPENDENCIES = src/libwolftpm.la endif + +if BUILD_ST33 +noinst_PROGRAMS += examples/firmware/st33_fw_update +examples_firmware_st33_fw_update_SOURCES = examples/firmware/st33_fw_update.c \ + examples/tpm_test_keys.c +examples_firmware_st33_fw_update_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD) +examples_firmware_st33_fw_update_DEPENDENCIES = src/libwolftpm.la + +endif +endif endif example_firmwaredir = $(exampledir)/firmware -dist_example_firmware_DATA = examples/firmware/ifx_fw_update.c +dist_example_firmware_DATA = examples/firmware/ifx_fw_update.c \ + examples/firmware/st33_fw_update.c -DISTCLEANFILES+= examples/firmware/.libs/ifx_fw_update +DISTCLEANFILES+= examples/firmware/.libs/ifx_fw_update \ + examples/firmware/.libs/st33_fw_update diff --git a/examples/firmware/st33_fw_update.c b/examples/firmware/st33_fw_update.c new file mode 100644 index 00000000..100301be --- /dev/null +++ b/examples/firmware/st33_fw_update.c @@ -0,0 +1,405 @@ +/* st33_fw_update.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* This tool will perform a firmware update on STMicroelectronics ST33KTPM + * TPM 2.0 module */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFTPM_FIRMWARE_UPGRADE) && \ + (defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT)) + +#include +#include + +/******************************************************************************/ +/* --- BEGIN ST33 TPM2.0 Firmware Update tool -- */ +/******************************************************************************/ + +/* Manifest sizes per ST33 firmware format */ +#define ST33_BLOB0_SIZE_NON_LMS 177 /* Non-LMS manifest size */ +#define ST33_BLOB0_SIZE_LMS 2697 /* LMS manifest size (includes embedded signature) */ + +static void usage(void) +{ + printf("ST33 Firmware Update Usage:\n"); + printf("\t./st33_fw_update (get info)\n"); + printf("\t./st33_fw_update --abandon (cancel)\n"); + printf("\t./st33_fw_update \n"); + printf("\nFirmware format is auto-detected from the TPM firmware version.\n"); + printf("Just provide the correct .fi file for your TPM and it will be handled automatically.\n"); +} + +typedef struct { + byte* fi_buf; /* Full .fi file buffer */ + byte* manifest_buf; /* Points into fi_buf */ + byte* firmware_buf; /* Points into fi_buf */ + size_t fi_bufSz; + size_t manifest_bufSz; + size_t firmware_bufSz; + int in_upgrade_mode; /* 1 = continuing from upgrade mode */ +} fw_info_t; + +/* Send firmware data blobs directly - used when continuing from upgrade mode */ +static int TPM2_ST33_SendFirmwareData(fw_info_t* fwinfo) +{ + int rc; + uint32_t offset = 0; + uint8_t blob_header[3]; + uint8_t* blob_buf = NULL; + uint32_t blob_len; + uint32_t blob_total; + int blob_count = 0; + + /* Allocate buffer for largest possible blob */ + blob_buf = (uint8_t*)XMALLOC(2048 + 3, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (blob_buf == NULL) { + return MEMORY_E; + } + + while (offset < fwinfo->firmware_bufSz) { + /* Read blob header: type (1 byte) + length (2 bytes big-endian) */ + if (offset + 3 > fwinfo->firmware_bufSz) { + rc = TPM_RC_SUCCESS; /* End of data */ + break; + } + XMEMCPY(blob_header, &fwinfo->firmware_buf[offset], 3); + + /* Check for end marker (type byte = 0) */ + if (blob_header[0] == 0) { + rc = TPM_RC_SUCCESS; + break; + } + + /* Parse blob length from bytes 1-2 (big-endian) */ + blob_len = ((uint32_t)blob_header[1] << 8) | blob_header[2]; + blob_total = blob_len + 3; + + if (offset + blob_total > fwinfo->firmware_bufSz) { + printf("Error: Incomplete blob at offset %u\n", offset); + rc = BUFFER_E; + break; + } + + /* Copy complete blob (header + data) */ + XMEMCPY(blob_buf, &fwinfo->firmware_buf[offset], blob_total); + + /* Send blob to TPM */ + rc = TPM2_ST33_FieldUpgradeCommand(TPM_CC_FieldUpgradeDataVendor_ST33, + blob_buf, blob_total); + if (rc != TPM_RC_SUCCESS) { + printf("FieldUpgradeData failed at blob %d, offset %u: 0x%x\n", + blob_count, offset, rc); + break; + } + + blob_count++; + offset += blob_total; + + /* Progress indication */ + if (blob_count % 100 == 0) { + printf(" Sent %d blobs, %u/%zu bytes...\n", blob_count, offset, + fwinfo->firmware_bufSz); + } + } + + printf("Sent %d firmware blobs, %u bytes total\n", blob_count, offset); + XFREE(blob_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return rc; +} + +/* Callback function for firmware data access + * Returns the actual number of bytes copied (may be less than requested at end of buffer) + * Returns BUFFER_E on error (offset out of bounds) */ +static int TPM2_ST33_FwData_Cb(uint8_t* data, uint32_t data_req_sz, + uint32_t offset, void* cb_ctx) +{ + fw_info_t* fwinfo = (fw_info_t*)cb_ctx; + if (offset > fwinfo->firmware_bufSz) { + return BUFFER_E; + } + if (offset + data_req_sz > (uint32_t)fwinfo->firmware_bufSz) { + data_req_sz = (uint32_t)fwinfo->firmware_bufSz - offset; + } + if (data_req_sz > 0) { + XMEMCPY(data, &fwinfo->firmware_buf[offset], data_req_sz); + } + return data_req_sz; +} + +static void TPM2_ST33_PrintInfo(WOLFTPM2_CAPS* caps) +{ + printf("Mfg %s (%d), Vendor %s, Fw %u.%u (0x%x)\n", + caps->mfgStr, caps->mfg, caps->vendorStr, caps->fwVerMajor, + caps->fwVerMinor, caps->fwVerVendor); + printf("Firmware version details: Major=%u, Minor=%u, Vendor=0x%x\n", + caps->fwVerMajor, caps->fwVerMinor, caps->fwVerVendor); + if (caps->fwVerMinor < 512) { + printf("Hardware: ST33K (legacy firmware, Generation 1)\n"); + printf("Firmware update: Non-LMS format required\n"); + } + else { + printf("Hardware: ST33K (modern firmware, Generation 2)\n"); + printf("Firmware update: LMS format required\n"); + } +} + +/* Forward declaration */ +int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]); + +int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_CAPS caps; + const char* fi_file = NULL; + fw_info_t fwinfo; + int abandon = 0; + size_t blob0_size; + + XMEMSET(&fwinfo, 0, sizeof(fwinfo)); + XMEMSET(&caps, 0, sizeof(caps)); + + if (argc >= 2) { + if (XSTRCMP(argv[1], "-?") == 0 || + XSTRCMP(argv[1], "-h") == 0 || + XSTRCMP(argv[1], "--help") == 0) { + usage(); + return 0; + } + if (XSTRCMP(argv[1], "--abandon") == 0) { + abandon = 1; + } + else { + fi_file = argv[1]; + } + } + + printf("ST33 Firmware Update Tool\n"); + if (fi_file != NULL) { + printf("\tFirmware File: %s\n", fi_file); + } + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, userCtx); + if (rc == TPM_RC_UPGRADE) { + /* TPM is in firmware upgrade mode */ + printf("TPM is in firmware upgrade mode\n"); + if (abandon) { + uint8_t cmd[2] = {0, 0}; /* data size = 0 */ + printf("Firmware Update Abandon:\n"); + /* Call cancel command directly - can't use wolfTPM2_FirmwareUpgradeCancel + * because GetCapabilities also fails in upgrade mode */ + rc = TPM2_ST33_FieldUpgradeCommand(TPM_CC_FieldUpgradeAbandonVendor_ST33, + cmd, sizeof(cmd)); + if (rc != 0) { + printf("Abandon failed 0x%x: %s\n", rc, TPM2_GetRCString(rc)); + printf("Power cycle TPM to reset\n"); + } + else { + printf("Success: Please reset or power cycle TPM\n"); + } + wolfTPM2_Cleanup(&dev); + return rc; + } + if (fi_file != NULL) { + /* Continue firmware update - TPM already in upgrade mode */ + printf("Continuing firmware update...\n"); + fwinfo.in_upgrade_mode = 1; + /* Skip to firmware data loading, the start was already done */ + goto load_firmware; + } + printf("Use --abandon to cancel firmware upgrade, or power cycle TPM\n"); + goto exit; + } + else if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_Init failed 0x%x: %s\n", rc, TPM2_GetRCString(rc)); + goto exit; + } + + rc = wolfTPM2_GetCapabilities(&dev, &caps); + if (rc != TPM_RC_SUCCESS) { + printf("wolfTPM2_GetCapabilities failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + goto exit; + } + + TPM2_ST33_PrintInfo(&caps); + + /* Verify this is an ST33 TPM */ + if (caps.mfg != TPM_MFG_STM) { + printf("Error: This tool is for STMicroelectronics ST33 TPMs only!\n"); + printf("Detected manufacturer: %s (%d)\n", caps.mfgStr, caps.mfg); + rc = TPM_RC_COMMAND_CODE; + goto exit; + } + + if (abandon) { + printf("Firmware Update Abandon:\n"); + rc = wolfTPM2_FirmwareUpgradeCancel(&dev); + if (rc != 0) { + printf("Abandon failed 0x%x: %s\n", rc, TPM2_GetRCString(rc)); + } + else { + printf("Success: Please reset or power cycle TPM\n"); + } + return rc; + } + + if (fi_file == NULL) { + if (argc > 1) { + printf("Firmware file argument missing!\n"); + } + goto exit; + } + +load_firmware: + /* Determine blob0 (manifest) size based on firmware version. + * In upgrade mode (caps not available), auto-detect from file size. */ + if (fwinfo.in_upgrade_mode) { + /* In upgrade mode, we don't have caps. Load file first to detect format. */ + rc = loadFile(fi_file, &fwinfo.fi_buf, &fwinfo.fi_bufSz); + if (rc != 0) { + printf("Failed to load firmware file: %s\n", fi_file); + goto exit; + } + /* Auto-detect format from file size: LMS files are larger due to + * 2697 byte manifest vs 177 byte manifest */ + if (fwinfo.fi_bufSz > ST33_BLOB0_SIZE_LMS + 1000) { + /* File large enough to potentially be LMS format. + * Check if blob header at LMS offset looks valid. */ + if (fwinfo.fi_buf[ST33_BLOB0_SIZE_LMS] != 0 && + fwinfo.fi_buf[ST33_BLOB0_SIZE_LMS] != 0xFF) { + blob0_size = ST33_BLOB0_SIZE_LMS; + printf("\tFormat: LMS (auto-detected from file)\n"); + } + else { + blob0_size = ST33_BLOB0_SIZE_NON_LMS; + printf("\tFormat: Non-LMS (auto-detected from file)\n"); + } + } + else { + blob0_size = ST33_BLOB0_SIZE_NON_LMS; + printf("\tFormat: Non-LMS (auto-detected from file)\n"); + } + } + else { + /* Normal mode: determine format from firmware version */ + blob0_size = (caps.fwVerMinor >= 512) ? + ST33_BLOB0_SIZE_LMS : ST33_BLOB0_SIZE_NON_LMS; + printf("\tFormat: %s (from TPM firmware version)\n", + (caps.fwVerMinor >= 512) ? "LMS" : "Non-LMS"); + + /* Load the complete .fi file */ + rc = loadFile(fi_file, &fwinfo.fi_buf, &fwinfo.fi_bufSz); + if (rc != 0) { + printf("Failed to load firmware file: %s\n", fi_file); + goto exit; + } + } + + /* Validate file size */ + if (fwinfo.fi_bufSz <= blob0_size) { + printf("Error: Firmware file too small. Expected > %zu bytes, got %zu bytes.\n", + blob0_size, fwinfo.fi_bufSz); + rc = BAD_FUNC_ARG; + goto exit; + } + + /* Split .fi file into manifest (blob0) and firmware data */ + fwinfo.manifest_buf = fwinfo.fi_buf; + fwinfo.manifest_bufSz = blob0_size; + fwinfo.firmware_buf = fwinfo.fi_buf + blob0_size; + fwinfo.firmware_bufSz = fwinfo.fi_bufSz - blob0_size; + + printf("Firmware Update:\n"); + printf("\tTotal file size: %zu bytes\n", fwinfo.fi_bufSz); + printf("\tManifest (blob0): %zu bytes\n", fwinfo.manifest_bufSz); + printf("\tFirmware data: %zu bytes\n", fwinfo.firmware_bufSz); + + if (fwinfo.in_upgrade_mode) { + /* Continuing from upgrade mode - just send firmware data */ + printf("Sending firmware data (TPM already in upgrade mode)...\n"); + rc = TPM2_ST33_SendFirmwareData(&fwinfo); + } + else { + /* Normal mode - use unified API which auto-detects format from manifest size */ + rc = wolfTPM2_FirmwareUpgrade(&dev, + fwinfo.manifest_buf, (uint32_t)fwinfo.manifest_bufSz, + TPM2_ST33_FwData_Cb, &fwinfo); + } + if (rc == 0) { + printf("\nFirmware update completed successfully.\n"); + printf("Please reset or power cycle the TPM.\n"); + /* Get updated capabilities - may fail if still in special mode */ + rc = wolfTPM2_GetCapabilities(&dev, &caps); + if (rc == 0) { + TPM2_ST33_PrintInfo(&caps); + } + else { + printf("Power cycle TPM to complete update.\n"); + rc = 0; /* Update was successful, just need power cycle */ + } + } + +exit: + + if (rc != 0) { + printf("ST33 firmware update failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + + /* Only free the main fi_buf - manifest_buf and firmware_buf point into it */ + XFREE(fwinfo.fi_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wolfTPM2_Cleanup(&dev); + + return rc; +} + +/******************************************************************************/ +/* --- END ST33 TPM2.0 Firmware Update tool -- */ +/******************************************************************************/ +#endif /* WOLFTPM_FIRMWARE_UPGRADE && (WOLFTPM_ST33 || WOLFTPM_AUTODETECT) */ + +#ifndef NO_MAIN_DRIVER +int main(int argc, char *argv[]) +{ + int rc = -1; + +#if defined(WOLFTPM_FIRMWARE_UPGRADE) && \ + (defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT)) + rc = TPM2_ST33_Firmware_Update(NULL, argc, argv); +#else + printf("Support for ST33 firmware upgrade not compiled in!\n" + "See --enable-firmware or WOLFTPM_FIRMWARE_UPGRADE\n"); + printf("This tool is for the STMicroelectronics ST33KTPM TPMs only\n" + "\t--enable-st33 (WOLFTPM_ST33)\n"); + (void)argc; + (void)argv; +#endif + + return rc; +} +#endif /* !NO_MAIN_DRIVER */ + diff --git a/src/tpm2.c b/src/tpm2.c index b86aa904..bfdfee54 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -5650,6 +5650,59 @@ int TPM2_IFX_FieldUpgradeCommand(TPM_CC cc, uint8_t* data, uint32_t size) return rc; } #endif /* WOLFTPM_SLB9672 || WOLFTPM_SLB9673 */ + +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) +/* ST33 Firmware Update Vendor Command Functions */ +int TPM2_ST33_FieldUpgradeStart(TPM_HANDLE sessionHandle, + uint8_t* data, uint32_t size) +{ + int rc; + TPM2_CTX* ctx = TPM2_GetActiveCtx(); + TPMS_AUTH_COMMAND session; + + rc = TPM2_AcquireLock(ctx); + if (rc == TPM_RC_SUCCESS) { + TPM2_Packet packet; + int tmpSz = 0; + TPM2_Packet_Init(ctx, &packet); + TPM2_Packet_AppendU32(&packet, TPM_RH_PLATFORM); + + XMEMSET(&session, 0, sizeof(session)); + session.sessionHandle = sessionHandle; + + TPM2_Packet_MarkU32(&packet, &tmpSz); + TPM2_Packet_AppendAuthCmd(&packet, &session); + TPM2_Packet_PlaceU32(&packet, tmpSz); + + TPM2_Packet_AppendBytes(&packet, data, size); + + TPM2_Packet_Finalize(&packet, TPM_ST_SESSIONS, + TPM_CC_FieldUpgradeStartVendor_ST33); + + rc = TPM2_SendCommand(ctx, &packet); + + TPM2_ReleaseLock(ctx); + } + return rc; +} + +int TPM2_ST33_FieldUpgradeCommand(TPM_CC cc, uint8_t* data, uint32_t size) +{ + int rc; + TPM2_CTX* ctx = TPM2_GetActiveCtx(); + + rc = TPM2_AcquireLock(ctx); + if (rc == TPM_RC_SUCCESS) { + TPM2_Packet packet; + TPM2_Packet_Init(ctx, &packet); + TPM2_Packet_AppendBytes(&packet, data, size); + TPM2_Packet_Finalize(&packet, TPM_ST_NO_SESSIONS, cc); + rc = TPM2_SendCommand(ctx, &packet); + TPM2_ReleaseLock(ctx); + } + return rc; +} +#endif /* WOLFTPM_ST33 || WOLFTPM_AUTODETECT */ #endif /* WOLFTPM_FIRMWARE_UPGRADE */ /******************************************************************************/ diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index ed0d7b51..24880a59 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -93,12 +93,20 @@ static int wolfTPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, startupIn.startupType = TPM_SU_CLEAR; rc = TPM2_Startup(&startupIn); if (rc != TPM_RC_SUCCESS && - rc != TPM_RC_INITIALIZE /* TPM_RC_INITIALIZE = Already started */ ) { + rc != TPM_RC_INITIALIZE /* TPM_RC_INITIALIZE = Already started */ && + rc != TPM_RC_UPGRADE /* TPM_RC_UPGRADE = In firmware upgrade mode */ ) { #ifdef DEBUG_WOLFTPM printf("TPM2_Startup failed %d: %s\n", rc, wolfTPM2_GetRCString(rc)); #endif return rc; } + /* Return upgrade status so caller can handle appropriately */ + if (rc == TPM_RC_UPGRADE) { + #ifdef DEBUG_WOLFTPM + printf("TPM2_Startup: TPM is in firmware upgrade mode\n"); + #endif + return rc; + } #ifdef DEBUG_WOLFTPM printf("TPM2_Startup pass\n"); #endif @@ -8155,14 +8163,12 @@ int wolfTPM2_SetIdentityAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle, /******************************************************************************/ - - - /******************************************************************************/ /* --- BEGIN Firmware Upgrade Support -- */ /******************************************************************************/ #ifdef WOLFTPM_FIRMWARE_UPGRADE + #if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) /* Maximum size of firmware chunks */ @@ -8390,6 +8396,22 @@ static int tpm2_ifx_firmware_final(WOLFTPM2_DEV* dev) return rc; } +#endif /* WOLFTPM_SLB9672 || WOLFTPM_SLB9673 */ + +/* Public Firmware Upgrade APIs - routes to appropriate vendor implementation + * Available when any firmware upgrade vendor support is enabled */ +#if (defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) || \ + defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT)) + +/* Forward declarations for vendor-specific firmware functions */ +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) +static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, + uint8_t* manifest_hash, uint32_t manifest_hash_sz, + uint8_t* manifest, uint32_t manifest_sz, + wolfTPM2FwDataCb cb, void* cb_ctx); +static int tpm2_st33_firmware_cancel(WOLFTPM2_DEV* dev); +#endif + int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, uint8_t* manifest_hash, uint32_t manifest_hash_sz, uint8_t* manifest, uint32_t manifest_sz, @@ -8398,9 +8420,27 @@ int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, int rc; WOLFTPM2_CAPS caps; - /* check the operational mode */ + /* Get capabilities to determine manufacturer */ rc = wolfTPM2_GetCapabilities(dev, &caps); - if (rc == TPM_RC_SUCCESS) { + if (rc != TPM_RC_SUCCESS) { + return rc; + } + +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) + if (caps.mfg == TPM_MFG_STM) { + /* Route to ST33 firmware update - LMS vs non-LMS is + * auto-detected from manifest size */ + return tpm2_st33_firmware_upgrade_hash(dev, hashAlg, + manifest_hash, manifest_hash_sz, + manifest, manifest_sz, + cb, cb_ctx); + } +#endif + +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) + if (caps.mfg == TPM_MFG_INFINEON) { + /* Route to Infineon firmware update implementation */ + /* check the operational mode */ if (caps.opMode == 0x03) { /* firmware update is done, just needs finalized and TPM reset */ #ifdef DEBUG_WOLFTPM @@ -8408,30 +8448,37 @@ int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, #endif return tpm2_ifx_firmware_final(dev); } - } - if (rc == TPM_RC_SUCCESS && caps.opMode == 0x00) { - rc = tpm2_ifx_firmware_enable_policy(dev); + if (caps.opMode == 0x00) { + rc = tpm2_ifx_firmware_enable_policy(dev); + if (rc == TPM_RC_SUCCESS) { + rc = tpm2_ifx_firmware_start(dev, hashAlg, + manifest_hash, manifest_hash_sz); + } + } if (rc == TPM_RC_SUCCESS) { - rc = tpm2_ifx_firmware_start(dev, hashAlg, - manifest_hash, manifest_hash_sz); + rc = tpm2_ifx_firmware_manifest(dev, manifest, manifest_sz); } + if (rc == TPM_RC_SUCCESS) { + rc = tpm2_ifx_firmware_data(dev, cb, cb_ctx); + } + if (rc == TPM_RC_SUCCESS) { + rc = tpm2_ifx_firmware_final(dev); + } + #ifdef DEBUG_WOLFTPM + if (rc != TPM_RC_SUCCESS) { + printf("Firmware update failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + #endif + return rc; } - if (rc == TPM_RC_SUCCESS) { - rc = tpm2_ifx_firmware_manifest(dev, manifest, manifest_sz); - } - if (rc == TPM_RC_SUCCESS) { - rc = tpm2_ifx_firmware_data(dev, cb, cb_ctx); - } - if (rc == TPM_RC_SUCCESS) { - rc = tpm2_ifx_firmware_final(dev); - } +#endif + + /* Unsupported manufacturer */ #ifdef DEBUG_WOLFTPM - if (rc != TPM_RC_SUCCESS) { - printf("Firmware update failed 0x%x: %s\n", - rc, TPM2_GetRCString(rc)); - } + printf("Firmware update not supported for manufacturer %d\n", caps.mfg); #endif - return rc; + return TPM_RC_COMMAND_CODE; } #ifndef WOLFTPM2_NO_WOLFCRYPT @@ -8469,6 +8516,331 @@ int wolfTPM2_FirmwareUpgradeRecover(WOLFTPM2_DEV* dev, /* terminate a firmware update */ int wolfTPM2_FirmwareUpgradeCancel(WOLFTPM2_DEV* dev) +{ + int rc; + WOLFTPM2_CAPS caps; + + /* Get capabilities to determine manufacturer */ + rc = wolfTPM2_GetCapabilities(dev, &caps); + if (rc != TPM_RC_SUCCESS) { + return rc; + } + +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) + if (caps.mfg == TPM_MFG_STM) { + /* Route to ST33 firmware cancel */ + return tpm2_st33_firmware_cancel(dev); + } +#endif + +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) + if (caps.mfg == TPM_MFG_INFINEON) { + /* Route to Infineon firmware cancel */ + uint8_t cmd[2]; + uint16_t val16; + + val16 = 0; /* data size */ + XMEMCPY(&cmd[0], &val16, sizeof(val16)); + + rc = TPM2_IFX_FieldUpgradeCommand(TPM_CC_FieldUpgradeAbandonVendor, + cmd, sizeof(cmd)); + #ifdef DEBUG_WOLFTPM + if (rc != TPM_RC_SUCCESS) { + printf("Firmware abandon failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + #endif + return rc; + } +#endif + + /* Unsupported manufacturer */ +#ifdef DEBUG_WOLFTPM + printf("Firmware cancel not supported for manufacturer %d\n", caps.mfg); +#endif + return TPM_RC_COMMAND_CODE; +} + +#endif /* WOLFTPM_SLB9672 || WOLFTPM_SLB9673 || WOLFTPM_ST33 || WOLFTPM_AUTODETECT */ + +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) + +/* Maximum size of firmware chunks for ST33 */ +#define ST33_FW_MAX_CHUNK_SZ 2048 /* Must be large enough for firmware blobs */ + +/* ST33 firmware version threshold for LMS requirement: + * < 512: Non-LMS format required (legacy, e.g., 9.257) + * >= 512: LMS format required (modern, e.g., 9.512) */ +#define ST33_FW_VERSION_LMS_REQUIRED 512 + +/* ST33 manifest (blob0) sizes determine firmware format. + * The manifest size is used for auto-detection of LMS vs non-LMS format. */ +#define ST33_MANIFEST_SIZE_NON_LMS 177 /* Non-LMS manifest size */ +#define ST33_MANIFEST_SIZE_LMS 2697 /* LMS manifest size (includes embedded signature) */ + +/* ST33 uses password auth (TPM_RS_PW) for firmware update, not policy */ + +/* Common firmware upgrade start function + * Sends manifest (blob0) via FieldUpgradeStart + * 300ms delay: ST reference implementation uses this delay to allow + * TPM to switch modes after FieldUpgradeStart command */ +static int tpm2_st33_firmware_start_common(WOLFTPM2_DEV* dev, + uint8_t* manifest, uint32_t manifest_sz, int is_lms) +{ + int rc; + + (void)dev; + + /* ST33 uses password auth (TPM_RS_PW) for FieldUpgradeStart. + * This matches the ST reference implementation behavior. + * For LMS format, the manifest (blob0) already contains the embedded + * LMS signature. Send the full manifest directly. */ + rc = TPM2_ST33_FieldUpgradeStart(TPM_RS_PW, manifest, manifest_sz); + + if (rc == TPM_RC_SUCCESS) { + /* 300ms delay: ST reference implementation uses this delay to allow + * TPM to switch modes after FieldUpgradeStart command */ + XSLEEP_MS(300); + + #if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ + !defined(WOLFTPM_WINAPI) + /* Do chip startup and request locality again */ + rc = TPM2_ChipStartup(&dev->ctx, 10); + #endif + } +#ifdef DEBUG_WOLFTPM + if (rc != TPM_RC_SUCCESS) { + printf("ST33 Firmware upgrade start%s failed 0x%x: %s\n", + is_lms ? " (LMS)" : "", rc, TPM2_GetRCString(rc)); + } +#else + (void)is_lms; /* Suppress unused parameter warning when DEBUG_WOLFTPM not defined */ +#endif + return rc; +} + +/* ST33 sends full manifest (blob0) directly in FieldUpgradeStart */ + +/* Send firmware data as blobs per ST reference implementation + * Firmware data format (after blob0/manifest): + * - Byte 0: blob type + * - Bytes 1-2: blob data length (big-endian) + * - Bytes 3+: blob data + * Each blob is sent complete to the TPM via FieldUpgradeData command */ +static int tpm2_st33_firmware_data(WOLFTPM2_DEV* dev, + wolfTPM2FwDataCb cb, void* cb_ctx) +{ + int rc; + uint32_t offset = 0; + uint8_t blob_header[3]; + uint8_t* blob_buf = NULL; + uint32_t blob_len; + uint32_t blob_total; + + (void)dev; + + /* Allocate buffer for largest possible blob */ + blob_buf = (uint8_t*)XMALLOC(ST33_FW_MAX_CHUNK_SZ + 3, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (blob_buf == NULL) { + return MEMORY_E; + } + + for (;;) { + /* Read blob header: type (1 byte) + length (2 bytes big-endian) */ + rc = cb(blob_header, 3, offset, cb_ctx); + if (rc < 3) { + /* End of data or error */ + if (rc == 0 || blob_header[0] == 0) { + #ifdef DEBUG_WOLFTPM + printf("ST33 Firmware data done\n"); + #endif + rc = TPM_RC_SUCCESS; + } + else { + #ifdef DEBUG_WOLFTPM + printf("ST33 Firmware data read error at offset %u\n", offset); + #endif + rc = BUFFER_E; + } + break; + } + + /* Check for end marker (type byte = 0) */ + if (blob_header[0] == 0) { + #ifdef DEBUG_WOLFTPM + printf("ST33 Firmware data end marker at offset %u\n", offset); + #endif + rc = TPM_RC_SUCCESS; + break; + } + + /* Parse blob length from bytes 1-2 (big-endian) */ + blob_len = ((uint32_t)blob_header[1] << 8) | blob_header[2]; + blob_total = blob_len + 3; /* total blob size including header */ + + if (blob_total > ST33_FW_MAX_CHUNK_SZ + 3) { + #ifdef DEBUG_WOLFTPM + printf("ST33 Blob too large: %u bytes at offset %u\n", + blob_total, offset); + #endif + rc = BUFFER_E; + break; + } + + #ifdef DEBUG_WOLFTPM + printf("ST33 Firmware blob offset %u, type 0x%02x, len %u\n", + offset, blob_header[0], blob_len); + #endif + + /* Read complete blob (header + data) */ + rc = cb(blob_buf, blob_total, offset, cb_ctx); + if (rc != (int)blob_total) { + #ifdef DEBUG_WOLFTPM + printf("ST33 Failed to read blob data at offset %u\n", offset); + #endif + rc = BUFFER_E; + break; + } + + /* Send blob to TPM - blob is sent as-is per ST reference */ + rc = TPM2_ST33_FieldUpgradeCommand(TPM_CC_FieldUpgradeDataVendor_ST33, + blob_buf, blob_total); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFTPM + printf("ST33 FieldUpgradeData failed at offset %u: 0x%x\n", + offset, rc); + #endif + break; + } + + offset += blob_total; + } + + XFREE(blob_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + if (rc == TPM_RC_SUCCESS) { + /* 300ms delay: ST reference implementation uses this delay to allow + * TPM to process firmware data blobs */ + XSLEEP_MS(300); + + #if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ + !defined(WOLFTPM_WINAPI) + /* Do chip startup and request locality again */ + rc = TPM2_ChipStartup(&dev->ctx, 10); + #endif + } +#ifdef DEBUG_WOLFTPM + else { + printf("ST33 Firmware upgrade data failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } +#endif + return rc; +} + + +/* Main ST33 firmware upgrade function with auto-detection from manifest size. + * The manifest size determines whether LMS format is used: + * - 177 bytes: Non-LMS format (legacy firmware < 512) + * - 2697 bytes: LMS format (modern firmware >= 512, LMS signature embedded) + */ +static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, + uint8_t* manifest_hash, uint32_t manifest_hash_sz, + uint8_t* manifest, uint32_t manifest_sz, + wolfTPM2FwDataCb cb, void* cb_ctx) +{ + int rc; + WOLFTPM2_CAPS caps; + int is_lms; + + /* ST33 sends full manifest directly, not hash */ + (void)hashAlg; + (void)manifest_hash; + (void)manifest_hash_sz; + + /* Auto-detect LMS format from manifest size */ + if (manifest_sz == ST33_MANIFEST_SIZE_LMS) { + is_lms = 1; + } + else if (manifest_sz == ST33_MANIFEST_SIZE_NON_LMS) { + is_lms = 0; + } + else { + #ifdef DEBUG_WOLFTPM + printf("ST33 Error: Invalid manifest size %u (expected %d for non-LMS or %d for LMS)\n", + manifest_sz, ST33_MANIFEST_SIZE_NON_LMS, ST33_MANIFEST_SIZE_LMS); + #endif + return BAD_FUNC_ARG; + } + + /* Get capabilities to check firmware version */ + rc = wolfTPM2_GetCapabilities(dev, &caps); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFTPM + printf("ST33 GetCapabilities failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + #endif + return rc; + } + +#ifdef DEBUG_WOLFTPM + printf("ST33 Firmware version: Major=%u, Minor=%u, Vendor=0x%x\n", + caps.fwVerMajor, caps.fwVerMinor, caps.fwVerVendor); + printf("ST33 Manifest size: %u bytes, format: %s\n", + manifest_sz, is_lms ? "LMS" : "non-LMS"); +#endif + + /* Validate manifest format matches firmware version requirement */ + if (caps.fwVerMinor < ST33_FW_VERSION_LMS_REQUIRED) { + /* Legacy firmware (< 512): non-LMS only */ + if (is_lms) { + #ifdef DEBUG_WOLFTPM + printf("ST33 Error: LMS manifest provided but firmware version %u < %d requires non-LMS\n", + caps.fwVerMinor, ST33_FW_VERSION_LMS_REQUIRED); + #endif + return BAD_FUNC_ARG; + } + #ifdef DEBUG_WOLFTPM + printf("ST33 Using non-LMS path (fwVerMinor < %d)\n", + ST33_FW_VERSION_LMS_REQUIRED); + #endif + } + else { + /* Modern firmware (>= 512): LMS required */ + if (!is_lms) { + #ifdef DEBUG_WOLFTPM + printf("ST33 Error: Non-LMS manifest provided but firmware version %u >= %d requires LMS\n", + caps.fwVerMinor, ST33_FW_VERSION_LMS_REQUIRED); + #endif + return BAD_FUNC_ARG; + } + #ifdef DEBUG_WOLFTPM + printf("ST33 Using LMS path (fwVerMinor >= %d, LMS required)\n", + ST33_FW_VERSION_LMS_REQUIRED); + #endif + } + + /* Send manifest - the common function handles both LMS and non-LMS */ + rc = tpm2_st33_firmware_start_common(dev, manifest, manifest_sz, is_lms); + + if (rc == TPM_RC_SUCCESS) { + rc = tpm2_st33_firmware_data(dev, cb, cb_ctx); + } + /* Note: ST33 doesn't require a finalize command - the firmware update + * is complete after all blobs are sent. The TPM will automatically + * apply the update on next reset/power cycle. */ +#ifdef DEBUG_WOLFTPM + if (rc != TPM_RC_SUCCESS) { + printf("ST33 Firmware update failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } +#endif + return rc; +} + +/* Cancel/abandon firmware upgrade */ +static int tpm2_st33_firmware_cancel(WOLFTPM2_DEV* dev) { int rc; uint8_t cmd[2]; @@ -8479,18 +8851,18 @@ int wolfTPM2_FirmwareUpgradeCancel(WOLFTPM2_DEV* dev) val16 = 0; /* data size */ XMEMCPY(&cmd[0], &val16, sizeof(val16)); - rc = TPM2_IFX_FieldUpgradeCommand(TPM_CC_FieldUpgradeAbandonVendor, + rc = TPM2_ST33_FieldUpgradeCommand(TPM_CC_FieldUpgradeAbandonVendor_ST33, cmd, sizeof(cmd)); #ifdef DEBUG_WOLFTPM if (rc != TPM_RC_SUCCESS) { - printf("Firmware abandon failed 0x%x: %s\n", + printf("ST33 Firmware abandon failed 0x%x: %s\n", rc, TPM2_GetRCString(rc)); } #endif return rc; } -#endif /* WOLFTPM_SLB9672 || WOLFTPM_SLB9673 */ +#endif /* WOLFTPM_ST33 || WOLFTPM_AUTODETECT */ #endif /* WOLFTPM_FIRMWARE_UPGRADE */ /******************************************************************************/ diff --git a/tests/unit_tests.c b/tests/unit_tests.c index afbfceb2..05799cfd 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -216,6 +216,111 @@ static void test_wolfTPM2_ReadPublicKey(void) rc == 0 ? "Passed" : "Failed"); } +#ifdef WOLFTPM_FIRMWARE_UPGRADE +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) +/* Test ST33 firmware upgrade APIs (function availability and + * parameter validation). LMS vs non-LMS format is auto-detected + * from manifest size (177 bytes = non-LMS, 2697 bytes = LMS). */ +static void test_wolfTPM2_ST33_FirmwareUpgrade(void) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_CAPS caps; +#ifndef WOLFTPM2_NO_WOLFCRYPT + /* Invalid manifest size (not 177 or 2697) for testing auto-detection */ + uint8_t dummy_manifest[10] = {0}; +#endif + + /* Initialize TPM */ + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc != 0) { + printf("Test ST33 Firmware Upgrade:\tInit:\tSkipped (TPM not available)\n"); + return; + } + + rc = wolfTPM2_GetCapabilities(&dev, &caps); + AssertIntEQ(rc, 0); + +#ifdef DEBUG_WOLFTPM + /* Display firmware version info */ + if (caps.mfg == TPM_MFG_STM) { + printf("ST33 TPM - Firmware: %u.%u (0x%x), Format: %s\n", + caps.fwVerMajor, caps.fwVerMinor, caps.fwVerVendor, + (caps.fwVerMinor >= 512) ? "LMS" : "non-LMS"); + } +#endif + + /* ===== Test NULL dev parameter handling ===== */ + + /* wolfTPM2_FirmwareUpgradeCancel - NULL dev */ + rc = wolfTPM2_FirmwareUpgradeCancel(NULL); + AssertIntNE(rc, 0); + + /* wolfTPM2_FirmwareUpgradeHash - NULL dev */ + rc = wolfTPM2_FirmwareUpgradeHash(NULL, TPM_ALG_SHA384, NULL, 0, NULL, + 0, NULL, NULL); + AssertIntNE(rc, 0); + + /* wolfTPM2_FirmwareUpgradeRecover - NULL dev */ + rc = wolfTPM2_FirmwareUpgradeRecover(NULL, NULL, 0, NULL, NULL); + AssertIntNE(rc, 0); + +#ifndef WOLFTPM2_NO_WOLFCRYPT + /* wolfTPM2_FirmwareUpgrade - NULL dev */ + rc = wolfTPM2_FirmwareUpgrade(NULL, NULL, 0, NULL, NULL); + AssertIntNE(rc, 0); +#endif /* !WOLFTPM2_NO_WOLFCRYPT */ + + /* ===== Test NULL/invalid parameter combinations ===== */ + + /* wolfTPM2_FirmwareUpgradeHash - valid dev, NULL manifest */ + rc = wolfTPM2_FirmwareUpgradeHash(&dev, TPM_ALG_SHA384, NULL, 0, NULL, + 0, NULL, NULL); + AssertIntNE(rc, 0); + + /* wolfTPM2_FirmwareUpgradeRecover - valid dev, NULL manifest */ + rc = wolfTPM2_FirmwareUpgradeRecover(&dev, NULL, 0, NULL, NULL); + AssertIntNE(rc, 0); + + /* wolfTPM2_FirmwareUpgradeCancel - valid dev (may succeed or fail + * depending on TPM state) */ + rc = wolfTPM2_FirmwareUpgradeCancel(&dev); + /* Note: This may return success or error depending on TPM state - + * just verify it doesn't crash */ + (void)rc; + +#ifndef WOLFTPM2_NO_WOLFCRYPT + /* wolfTPM2_FirmwareUpgrade - valid dev, NULL manifest */ + rc = wolfTPM2_FirmwareUpgrade(&dev, NULL, 0, NULL, NULL); + AssertIntNE(rc, 0); + + /* wolfTPM2_FirmwareUpgrade - valid dev, NULL callback */ + rc = wolfTPM2_FirmwareUpgrade(&dev, dummy_manifest, sizeof(dummy_manifest), + NULL, NULL); + AssertIntNE(rc, 0); + + /* Test ST33-specific manifest size validation if we have an ST33 TPM. + * Invalid manifest size (not 177 or 2697) should return BAD_FUNC_ARG. */ + if (caps.mfg == TPM_MFG_STM) { + /* wolfTPM2_FirmwareUpgradeHash - invalid manifest size (10 bytes). + * Should fail with BAD_FUNC_ARG because manifest_sz must be + * exactly 177 (non-LMS) or 2697 (LMS). */ + rc = wolfTPM2_FirmwareUpgradeHash(&dev, TPM_ALG_SHA384, NULL, 0, + dummy_manifest, sizeof(dummy_manifest), NULL, NULL); + AssertIntEQ(rc, BAD_FUNC_ARG); + } +#endif /* !WOLFTPM2_NO_WOLFCRYPT */ + + rc = 0; + + wolfTPM2_Cleanup(&dev); + + printf("Test ST33 Firmware Upgrade:\tAPI Availability:\t%s\n", + rc == 0 ? "Passed" : "Failed"); +} +#endif /* WOLFTPM_ST33 || WOLFTPM_AUTODETECT */ +#endif /* WOLFTPM_FIRMWARE_UPGRADE */ + static void test_wolfTPM2_GetRandom(void) { int rc; @@ -428,8 +533,8 @@ static void test_wolfTPM2_EccSignVerifyDig(WOLFTPM2_DEV* dev, word32 rLen, sLen; ecc_key wolfKey; int curveSize = TPM2_GetCurveSize(curve); -#ifdef WOLF_CRYPTO_CB int tpmDevId = INVALID_DEVID; +#ifdef WOLF_CRYPTO_CB TpmCryptoDevCtx tpmCtx; XMEMSET(&tpmCtx, 0, sizeof(tpmCtx)); @@ -553,9 +658,11 @@ static void test_wolfTPM2_EccSignVerifyDig(WOLFTPM2_DEV* dev, (flags & FLAGS_USE_CRYPTO_CB) ? "Crypto CB" : "", rc == 0 ? "Passed" : "Failed"); +#ifdef WOLF_CRYPTO_CB if (flags & FLAGS_USE_CRYPTO_CB) { wolfTPM2_ClearCryptoDevCb(dev, tpmDevId); } +#endif } static void test_wolfTPM2_EccSignVerify_All(WOLFTPM2_DEV* dev, @@ -915,6 +1022,11 @@ int unit_tests(int argc, char *argv[]) !defined(WOLFTPM2_NO_ASN) test_wolfTPM2_EccSignVerify(); #endif + #ifdef WOLFTPM_FIRMWARE_UPGRADE + #if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) + test_wolfTPM2_ST33_FirmwareUpgrade(); + #endif + #endif test_wolfTPM2_Cleanup(); test_wolfTPM2_thread_local_storage(); #endif /* !WOLFTPM2_NO_WRAPPER */ diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 74664821..dfd22115 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -273,6 +273,16 @@ typedef enum { TPM_CC_FieldUpgradeDataVendor = CC_VEND + 0x132, TPM_CC_FieldUpgradeFinalizeVendor = CC_VEND + 0x133, #endif +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) + /* ST33 Firmware Update Vendor Command Codes + * Verified from ST reference implementation (TPM_FU_STM_KTPM_LMS.c): + * - Start: 0x2000030C, Data: 0x2000030D + * Note: Abandon still uses placeholder - may need verification */ + TPM_CC_FieldUpgradeStartVendor_ST33 = CC_VEND + 0x030C, + TPM_CC_FieldUpgradeAbandonVendor_ST33 = CC_VEND + 0x030E, /* Abandon/cancel */ + TPM_CC_FieldUpgradeDataVendor_ST33 = CC_VEND + 0x030D, + TPM_CC_FieldUpgradeFinalizeVendor_ST33 = CC_VEND + 0x030F, /* Finalize if needed */ +#endif } TPM_CC_T; typedef UINT32 TPM_CC; @@ -2981,6 +2991,14 @@ WOLFTPM_API int TPM2_IFX_FieldUpgradeCommand(TPM_CC cc, uint8_t* data, uint32_t #endif /* Infineon SLB Vendor Specific */ +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) +#ifdef WOLFTPM_FIRMWARE_UPGRADE +WOLFTPM_API int TPM2_ST33_FieldUpgradeStart(TPM_HANDLE sessionHandle, + uint8_t* data, uint32_t size); +WOLFTPM_API int TPM2_ST33_FieldUpgradeCommand(TPM_CC cc, uint8_t* data, uint32_t size); +#endif /* WOLFTPM_FIRMWARE_UPGRADE */ +#endif /* WOLFTPM_ST33 || WOLFTPM_AUTODETECT */ + /* Vendor Specific GPIO */ #ifdef WOLFTPM_ST33 diff --git a/wolftpm/tpm2_types.h b/wolftpm/tpm2_types.h index 7aeb187e..e102013c 100644 --- a/wolftpm/tpm2_types.h +++ b/wolftpm/tpm2_types.h @@ -521,12 +521,12 @@ typedef int64_t INT64; #define XSLEEP_MS(ms) vTaskDelay(pdMS_TO_TICKS(ms)) #elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L #include - #define XSLEEP_MS(ms) ({ \ - struct timespec ts; \ - ts.tv_sec = ms / 1000; \ - ts.tv_nsec = (ms % 1000) * 1000000; \ - nanosleep(&ts, NULL); \ - }) + static inline void XSLEEP_MS(unsigned int ms) { + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&ts, NULL); + } #elif defined(WIN32) #include #define XSLEEP_MS(ms) Sleep(ms) @@ -534,11 +534,11 @@ typedef int64_t INT64; #define XSLEEP_MS(ms) vTaskDelay(ms) #else #include - #define XSLEEP_MS(ms) ({ \ - if (ms >= 1000) \ - sleep(ms / 1000); \ - usleep((ms % 1000) * 1000); \ - }) + static inline void XSLEEP_MS(unsigned int ms) { + if (ms >= 1000) + sleep(ms / 1000); + usleep((ms % 1000) * 1000); + } #endif #endif @@ -794,9 +794,12 @@ typedef int64_t INT64; #define WOLFTPM2_PEM_DECODE #endif -/* Firmware upgrade supported only for Infineon SLB9672/SLB9673 */ +/* Firmware upgrade supported for Infineon SLB9672/SLB9673 and STM ST33. */ +/* If firmware upgrade is requested but none of these vendors (or AUTODETECT) + * are enabled, turn it off to avoid building unusable code. */ #if defined(WOLFTPM_FIRMWARE_UPGRADE) && (!defined(WOLFTPM_AUTODETECT) && \ - (!defined(WOLFTPM_SLB9672) && !defined(WOLFTPM_SLB9673))) + (!defined(WOLFTPM_SLB9672) && !defined(WOLFTPM_SLB9673) && \ + !defined(WOLFTPM_ST33))) #undef WOLFTPM_FIRMWARE_UPGRADE #endif diff --git a/wolftpm/tpm2_wrap.h b/wolftpm/tpm2_wrap.h index 8d709baa..21c80968 100644 --- a/wolftpm/tpm2_wrap.h +++ b/wolftpm/tpm2_wrap.h @@ -4129,8 +4129,11 @@ typedef int (*wolfTPM2FwDataCb)( /*! \ingroup wolfTPM2_Wrappers - \brief Calculate hash of firmware manifest for upgrade + \brief Perform TPM firmware upgrade with pre-computed manifest hash \note Supports SHA2-384 or SHA2-512 for manifest hash + \note For ST33KTPM: LMS vs non-LMS format is auto-detected from manifest size: + - 177 bytes: Non-LMS format (firmware < 512, e.g., 9.257) + - 2697 bytes: LMS format (firmware >= 512, e.g., 9.512) \return TPM_RC_SUCCESS: successful \return TPM_RC_FAILURE: generic failure (check TPM IO and TPM return code) @@ -4154,10 +4157,14 @@ WOLFTPM_API int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, uint8_t* manifest, uint32_t manifest_sz, wolfTPM2FwDataCb cb, void* cb_ctx); +#ifndef WOLFTPM2_NO_WOLFCRYPT /*! \ingroup wolfTPM2_Wrappers \brief Perform TPM firmware upgrade \note Upgrades TPM firmware using provided manifest and data callback + \note For ST33KTPM: LMS vs non-LMS format is auto-detected from manifest size: + - 177 bytes: Non-LMS format (firmware < 512, e.g., 9.257) + - 2697 bytes: LMS format (firmware >= 512, e.g., 9.512) \return TPM_RC_SUCCESS: successful \return TPM_RC_FAILURE: generic failure (check TPM IO and TPM return code) @@ -4175,6 +4182,7 @@ WOLFTPM_API int wolfTPM2_FirmwareUpgradeHash(WOLFTPM2_DEV* dev, WOLFTPM_API int wolfTPM2_FirmwareUpgrade(WOLFTPM2_DEV* dev, uint8_t* manifest, uint32_t manifest_sz, wolfTPM2FwDataCb cb, void* cb_ctx); +#endif /* !WOLFTPM2_NO_WOLFCRYPT */ /*! \ingroup wolfTPM2_Wrappers