Skip to content

Commit 1697117

Browse files
committed
Fix ST33 generation 1 manifest size and refuse oversized commands (ZD 22193)
1 parent e87efa6 commit 1697117

7 files changed

Lines changed: 235 additions & 102 deletions

File tree

examples/firmware/README.md

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Currently wolfTPM supports firmware update capability for:
44
- Infineon SLB9672 (SPI) and SLB9673 (I2C) TPM 2.0 modules. Infineon has open sourced their firmware update.
5-
- 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).
5+
- STMicroelectronics ST33KTPM TPM 2.0 modules. Support covers generation 1 firmware (RSA signed manifest), generation 9 firmware below 512 (ECDSA signed manifest) and generation 9 firmware at 512 and above (LMS signature requirement).
66

77
## Infineon Firmware
88

@@ -117,15 +117,19 @@ KeyGroupId 0x7, FwCounter 1253 (254 same)
117117

118118
ST33KTPM firmware update automatically detects the required format based on TPM firmware version:
119119

120-
- **Legacy firmware (< 512, e.g., 9.257)**: Non-LMS format
121-
- Manifest size: 177 bytes
122-
- Generation 1 firmware (ECC-only)
120+
The manifest (blob0) is a 33 byte fixed header followed by the firmware digest and the signature over it, so its size follows the algorithms that generation signs with:
123121

124-
- **Modern firmware (>= 512, e.g., 9.512)**: LMS format
122+
- **Generation 1 (major version 1, e.g., 1.257 or 1.771)**: Non-LMS format
123+
- Manifest size: 321 bytes (SHA-256 digest, RSAPSS-2048 signature)
124+
- Always non-LMS, no matter how high the minor version goes
125+
126+
- **Generation 9 below 512 (e.g., 9.257)**: Non-LMS format
127+
- Manifest size: 177 bytes (SHA-384 digest, ECDSA P-384 signature)
128+
129+
- **Generation 9 at 512 and above (e.g., 9.512)**: LMS format
125130
- Manifest size: 2697 bytes (includes embedded LMS signature)
126-
- Generation 2 firmware (LMS mandatory)
127131

128-
The firmware version is automatically detected from `fwVerMinor` in TPM capabilities. The correct manifest size is determined automatically - no manual format selection is needed.
132+
The LMS requirement is a generation 9 rule, so both `fwVerMajor` and `fwVerMinor` from TPM capabilities are consulted. The example confirms its choice against the file itself: everything after blob0 is a chain of `[type][length]` records that ends exactly at end of file, and only the correct manifest size lands on the final byte. No manual format selection is needed.
129133

130134
### Updating the firmware
131135

@@ -145,9 +149,10 @@ Policy options (caller-supplied authorization):
145149
--policyor provision+satisfy a PolicyOR (multi-branch)
146150
--sha256|--sha384|--sha512 policy hash (default SHA-256)
147151

148-
Firmware format is auto-detected from TPM firmware version:
149-
- Firmware < 512: Non-LMS format (177 byte manifest)
150-
- Firmware >= 512: LMS format (2697 byte manifest with embedded signature)
152+
Firmware format is auto-detected from TPM firmware version and the file:
153+
- Generation 1 (e.g. 1.771): Non-LMS format (321 byte manifest)
154+
- Generation 9 below 512: Non-LMS format (177 byte manifest)
155+
- Generation 9 at 512 and above: LMS format (2697 byte manifest)
151156

152157
# Run without arguments to display the current firmware information
153158
./st33_fw_update

examples/firmware/st33_fw_update.c

Lines changed: 93 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,67 @@
5151
/******************************************************************************/
5252

5353
/* Manifest sizes per ST33 firmware format */
54-
#define ST33_BLOB0_SIZE_NON_LMS 177 /* Non-LMS manifest size */
55-
#define ST33_BLOB0_SIZE_LMS 2697 /* LMS manifest size (includes embedded signature) */
54+
/* The manifest is a 33 byte fixed header plus the firmware digest and the
55+
* signature over it, so its size follows the algorithms of that generation. */
56+
/* gen 1: SHA-256 + RSAPSS-2048 */
57+
#define ST33_BLOB0_SIZE_NON_LMS_RSA 321
58+
/* gen 9 below 512: SHA-384 + ECDSA P-384 */
59+
#define ST33_BLOB0_SIZE_NON_LMS 177
60+
/* gen 9 at 512 and above: embedded LMS signature */
61+
#define ST33_BLOB0_SIZE_LMS 2697
62+
63+
static const size_t st33_blob0_sizes[] = {
64+
ST33_BLOB0_SIZE_NON_LMS_RSA,
65+
ST33_BLOB0_SIZE_NON_LMS,
66+
ST33_BLOB0_SIZE_LMS
67+
};
68+
#define ST33_BLOB0_SIZE_CNT \
69+
(sizeof(st33_blob0_sizes) / sizeof(st33_blob0_sizes[0]))
70+
71+
/* Confirm a candidate blob0 size by walking the block chain that follows it.
72+
* Every byte after blob0 is a [type:1][len:2 big-endian][payload] record and
73+
* the chain ends exactly at end of file, so only the correct size lands on
74+
* the final byte. Candidates are tried in the supplied order, so the size the
75+
* TPM firmware version implies wins when more than one could fit. Returns the
76+
* blob0 size, or 0 when the file does not parse with any known size. */
77+
static size_t st33_detect_blob0(const byte* buf, size_t bufSz,
78+
const size_t* cand, size_t candCnt)
79+
{
80+
size_t i, off, len;
81+
82+
for (i = 0; i < candCnt; i++) {
83+
if (bufSz <= cand[i]) {
84+
continue;
85+
}
86+
off = cand[i];
87+
while (off + 3 <= bufSz) {
88+
if (buf[off] == 0) {
89+
break; /* end marker, not a record */
90+
}
91+
len = ((size_t)buf[off + 1] << 8) | buf[off + 2];
92+
if (len == 0) {
93+
break;
94+
}
95+
off += 3 + len;
96+
}
97+
if (off == bufSz) {
98+
return cand[i];
99+
}
100+
}
101+
return 0;
102+
}
103+
104+
/* Manifest size the running firmware expects for its next update */
105+
static size_t st33_expected_blob0(WOLFTPM2_CAPS* caps)
106+
{
107+
if (caps->fwVerMajor < 9) {
108+
return ST33_BLOB0_SIZE_NON_LMS_RSA;
109+
}
110+
if (caps->fwVerMinor < 512) {
111+
return ST33_BLOB0_SIZE_NON_LMS;
112+
}
113+
return ST33_BLOB0_SIZE_LMS;
114+
}
56115

57116
static void usage(void)
58117
{
@@ -203,6 +262,9 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[])
203262
fw_info_t fwinfo;
204263
int abandon = 0;
205264
size_t blob0_size;
265+
size_t cand[ST33_BLOB0_SIZE_CNT + 1];
266+
size_t candCnt;
267+
size_t idx;
206268
int i;
207269
#ifdef WOLFTPM_HAVE_FW_POLICY
208270
int policytest = 0;
@@ -372,50 +434,39 @@ int TPM2_ST33_Firmware_Update(void* userCtx, int argc, char *argv[])
372434
}
373435

374436
load_firmware:
375-
/* Determine blob0 (manifest) size based on firmware version.
376-
* In upgrade mode (caps not available), auto-detect from file size. */
377-
if (fwinfo.in_upgrade_mode) {
378-
/* In upgrade mode, we don't have caps. Load file first to detect format. */
379-
rc = loadFile(fi_file, &fwinfo.fi_buf, &fwinfo.fi_bufSz);
380-
if (rc != 0) {
381-
printf("Failed to load firmware file: %s\n", fi_file);
382-
goto exit;
383-
}
384-
/* Auto-detect format from file size: LMS files are larger due to
385-
* 2697 byte manifest vs 177 byte manifest */
386-
if (fwinfo.fi_bufSz > ST33_BLOB0_SIZE_LMS + 1000) {
387-
/* File large enough to potentially be LMS format.
388-
* Check if blob header at LMS offset looks valid. */
389-
if (fwinfo.fi_buf[ST33_BLOB0_SIZE_LMS] != 0 &&
390-
fwinfo.fi_buf[ST33_BLOB0_SIZE_LMS] != 0xFF) {
391-
blob0_size = ST33_BLOB0_SIZE_LMS;
392-
printf("\tFormat: LMS (auto-detected from file)\n");
393-
}
394-
else {
395-
blob0_size = ST33_BLOB0_SIZE_NON_LMS;
396-
printf("\tFormat: Non-LMS (auto-detected from file)\n");
397-
}
398-
}
399-
else {
400-
blob0_size = ST33_BLOB0_SIZE_NON_LMS;
401-
printf("\tFormat: Non-LMS (auto-detected from file)\n");
402-
}
437+
/* Load the complete .fi file, then determine the blob0 (manifest) size.
438+
* In upgrade mode caps are unavailable, so no size is preferred and the
439+
* block chain alone decides. */
440+
rc = loadFile(fi_file, &fwinfo.fi_buf, &fwinfo.fi_bufSz);
441+
if (rc != 0) {
442+
printf("Failed to load firmware file: %s\n", fi_file);
443+
goto exit;
403444
}
404-
else {
405-
/* Normal mode: determine format from firmware version */
406-
blob0_size = (caps.fwVerMinor >= 512) ?
407-
ST33_BLOB0_SIZE_LMS : ST33_BLOB0_SIZE_NON_LMS;
408-
printf("\tFormat: %s (from TPM firmware version)\n",
409-
(caps.fwVerMinor >= 512) ? "LMS" : "Non-LMS");
410-
411-
/* Load the complete .fi file */
412-
rc = loadFile(fi_file, &fwinfo.fi_buf, &fwinfo.fi_bufSz);
413-
if (rc != 0) {
414-
printf("Failed to load firmware file: %s\n", fi_file);
415-
goto exit;
445+
446+
candCnt = 0;
447+
if (!fwinfo.in_upgrade_mode) {
448+
cand[candCnt++] = st33_expected_blob0(&caps);
449+
}
450+
for (idx = 0; idx < ST33_BLOB0_SIZE_CNT; idx++) {
451+
if (candCnt == 0 || cand[0] != st33_blob0_sizes[idx]) {
452+
cand[candCnt++] = st33_blob0_sizes[idx];
416453
}
417454
}
418455

456+
blob0_size = st33_detect_blob0(fwinfo.fi_buf, fwinfo.fi_bufSz, cand,
457+
candCnt);
458+
if (blob0_size == 0) {
459+
printf("Error: could not determine the manifest (blob0) size of %s\n",
460+
fi_file);
461+
printf(" The %zu byte file does not parse as an ST33 firmware image "
462+
"with a\n 321, 177 or 2697 byte manifest.\n", fwinfo.fi_bufSz);
463+
rc = BAD_FUNC_ARG;
464+
goto exit;
465+
}
466+
printf("\tFormat: %s (blob0 %zu bytes, verified against the block "
467+
"chain)\n",
468+
(blob0_size == ST33_BLOB0_SIZE_LMS) ? "LMS" : "Non-LMS", blob0_size);
469+
419470
/* Validate file size */
420471
if (fwinfo.fi_bufSz <= blob0_size) {
421472
printf("Error: Firmware file too small. Expected > %zu bytes, got %zu bytes.\n",

src/tpm2.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,19 @@ static TPM_RC TPM2_DispatchCommand(TPM2_CTX* ctx, TPM2_Packet* packet)
521521
{
522522
TPM_RC rc;
523523

524+
/* A command that did not fit the packet buffer would otherwise go out
525+
* silently truncated, since the marshalling helpers drop what does not
526+
* fit and TPM2_Packet_Finalize then stamps the short length. The buffer
527+
* is MAX_COMMAND_SIZE / MAX_RESPONSE_SIZE, which --enable-smallstack
528+
* lowers to 1024 / 1350, so this is reachable on a normal build. */
529+
if (packet->overflow) {
530+
#ifdef DEBUG_WOLFTPM
531+
printf("Command exceeds the %d byte packet buffer "
532+
"(MAX_COMMAND_SIZE)\n", packet->size);
533+
#endif
534+
return (TPM_RC)BUFFER_E;
535+
}
536+
524537
#ifdef WOLFTPM_SPDM
525538
rc = TPM2_SPDM_SendCommand(ctx, packet);
526539
if (rc >= 0)

src/tpm2_packet.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,9 @@ int TPM2_Packet_RetryRestore(TPM_RC rc, int* retries, TPM2_Packet* packet,
17381738
* restore the clobbered header and the buffer size for an identical resend */
17391739
XMEMCPY(packet->buf, cmdHdr, TPM2_HEADER_SIZE);
17401740
packet->size = origSize;
1741+
/* The command body is intact again, so any overflow flagged while parsing
1742+
* the truncated retry response does not belong to the resend */
1743+
packet->overflow = 0;
17411744
return 1;
17421745
}
17431746
#endif /* !WOLFTPM_NO_RETRY */

src/tpm2_wrap.c

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11949,15 +11949,34 @@ int wolfTPM2_FirmwareUpgradeCancel(WOLFTPM2_DEV* dev)
1194911949
/* Maximum size of firmware chunks for ST33 */
1195011950
#define ST33_FW_MAX_CHUNK_SZ 2048 /* Must be large enough for firmware blobs */
1195111951

11952-
/* ST33 firmware version threshold for LMS requirement:
11953-
* < 512: Non-LMS format required (legacy, e.g., 9.257)
11954-
* >= 512: LMS format required (modern, e.g., 9.512) */
11952+
/* ST33 firmware version threshold for LMS requirement. LMS is a generation 9
11953+
* feature: on those parts a minor version >= 512 requires the LMS format
11954+
* (e.g. 9.512), below that the ECDSA format (e.g. 9.257). Generation 1 parts
11955+
* are always non-LMS no matter how high the minor version goes (e.g. 1.771). */
11956+
#define ST33_FW_GENERATION_LMS_CAPABLE 9
1195511957
#define ST33_FW_VERSION_LMS_REQUIRED 512
1195611958

11957-
/* ST33 manifest (blob0) sizes determine firmware format.
11958-
* The manifest size is used for auto-detection of LMS vs non-LMS format. */
11959-
#define ST33_MANIFEST_SIZE_NON_LMS 177 /* Non-LMS manifest size */
11960-
#define ST33_MANIFEST_SIZE_LMS 2697 /* LMS manifest size (includes embedded signature) */
11959+
/* ST33 manifest (blob0) sizes determine firmware format. The manifest is a
11960+
* 33 byte fixed header followed by the firmware digest and the signature over
11961+
* it, so its size follows the algorithms that generation signs with. */
11962+
/* gen 1: SHA-256 + RSAPSS-2048 */
11963+
#define ST33_MANIFEST_SIZE_NON_LMS_RSA 321
11964+
/* gen 9 below 512: SHA-384 + ECDSA P-384 */
11965+
#define ST33_MANIFEST_SIZE_NON_LMS 177
11966+
/* gen 9 at 512 and above: embedded LMS signature */
11967+
#define ST33_MANIFEST_SIZE_LMS 2697
11968+
11969+
/* Manifest size the running firmware expects for its next update */
11970+
static uint32_t tpm2_st33_expected_manifest_sz(const WOLFTPM2_CAPS* caps)
11971+
{
11972+
if (caps->fwVerMajor < ST33_FW_GENERATION_LMS_CAPABLE) {
11973+
return ST33_MANIFEST_SIZE_NON_LMS_RSA;
11974+
}
11975+
if (caps->fwVerMinor < ST33_FW_VERSION_LMS_REQUIRED) {
11976+
return ST33_MANIFEST_SIZE_NON_LMS;
11977+
}
11978+
return ST33_MANIFEST_SIZE_LMS;
11979+
}
1196111980

1196211981
/* ST33 uses password auth (TPM_RS_PW) for firmware update, not policy */
1196311982

@@ -12139,10 +12158,11 @@ static int tpm2_st33_firmware_data(WOLFTPM2_DEV* dev,
1213912158
}
1214012159

1214112160

12142-
/* Main ST33 firmware upgrade function with auto-detection from manifest size.
12143-
* The manifest size determines whether LMS format is used:
12144-
* - 177 bytes: Non-LMS format (legacy firmware < 512)
12145-
* - 2697 bytes: LMS format (modern firmware >= 512, LMS signature embedded)
12161+
/* Main ST33 firmware upgrade function. The manifest size selects the format
12162+
* and must match what the running firmware generation expects:
12163+
* - 321 bytes: Non-LMS, generation 1 (e.g. 1.257, 1.771)
12164+
* - 177 bytes: Non-LMS, generation 9 below 512 (e.g. 9.257)
12165+
* - 2697 bytes: LMS, generation 9 at 512 and above
1214612166
*/
1214712167
static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg,
1214812168
uint8_t* manifest_hash, uint32_t manifest_hash_sz,
@@ -12152,6 +12172,7 @@ static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg
1215212172
int rc;
1215312173
WOLFTPM2_CAPS caps;
1215412174
int is_lms;
12175+
uint32_t expected_sz;
1215512176

1215612177
/* ST33 sends full manifest directly, not hash */
1215712178
(void)hashAlg;
@@ -12162,13 +12183,16 @@ static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg
1216212183
if (manifest_sz == ST33_MANIFEST_SIZE_LMS) {
1216312184
is_lms = 1;
1216412185
}
12165-
else if (manifest_sz == ST33_MANIFEST_SIZE_NON_LMS) {
12186+
else if (manifest_sz == ST33_MANIFEST_SIZE_NON_LMS ||
12187+
manifest_sz == ST33_MANIFEST_SIZE_NON_LMS_RSA) {
1216612188
is_lms = 0;
1216712189
}
1216812190
else {
1216912191
#ifdef DEBUG_WOLFTPM
12170-
printf("ST33 Error: Invalid manifest size %u (expected %d for non-LMS or %d for LMS)\n",
12171-
manifest_sz, ST33_MANIFEST_SIZE_NON_LMS, ST33_MANIFEST_SIZE_LMS);
12192+
printf("ST33 Error: Invalid manifest size %u (expected %d or %d "
12193+
"for non-LMS or %d for LMS)\n", manifest_sz,
12194+
ST33_MANIFEST_SIZE_NON_LMS_RSA, ST33_MANIFEST_SIZE_NON_LMS,
12195+
ST33_MANIFEST_SIZE_LMS);
1217212196
#endif
1217312197
return BAD_FUNC_ARG;
1217412198
}
@@ -12190,34 +12214,15 @@ static int tpm2_st33_firmware_upgrade_hash(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg
1219012214
manifest_sz, is_lms ? "LMS" : "non-LMS");
1219112215
#endif
1219212216

12193-
/* Validate manifest format matches firmware version requirement */
12194-
if (caps.fwVerMinor < ST33_FW_VERSION_LMS_REQUIRED) {
12195-
/* Legacy firmware (< 512): non-LMS only */
12196-
if (is_lms) {
12197-
#ifdef DEBUG_WOLFTPM
12198-
printf("ST33 Error: LMS manifest provided but firmware version %u < %d requires non-LMS\n",
12199-
caps.fwVerMinor, ST33_FW_VERSION_LMS_REQUIRED);
12200-
#endif
12201-
return BAD_FUNC_ARG;
12202-
}
12217+
/* Validate the manifest matches what this firmware generation expects */
12218+
expected_sz = tpm2_st33_expected_manifest_sz(&caps);
12219+
if (manifest_sz != expected_sz) {
1220312220
#ifdef DEBUG_WOLFTPM
12204-
printf("ST33 Using non-LMS path (fwVerMinor < %d)\n",
12205-
ST33_FW_VERSION_LMS_REQUIRED);
12206-
#endif
12207-
}
12208-
else {
12209-
/* Modern firmware (>= 512): LMS required */
12210-
if (!is_lms) {
12211-
#ifdef DEBUG_WOLFTPM
12212-
printf("ST33 Error: Non-LMS manifest provided but firmware version %u >= %d requires LMS\n",
12213-
caps.fwVerMinor, ST33_FW_VERSION_LMS_REQUIRED);
12214-
#endif
12215-
return BAD_FUNC_ARG;
12216-
}
12217-
#ifdef DEBUG_WOLFTPM
12218-
printf("ST33 Using LMS path (fwVerMinor >= %d, LMS required)\n",
12219-
ST33_FW_VERSION_LMS_REQUIRED);
12221+
printf("ST33 Error: manifest size %u does not match the %u bytes "
12222+
"firmware %u.%u expects\n", manifest_sz, expected_sz,
12223+
caps.fwVerMajor, caps.fwVerMinor);
1222012224
#endif
12225+
return BAD_FUNC_ARG;
1222112226
}
1222212227

1222312228
/* Send manifest - the common function handles both LMS and non-LMS */

0 commit comments

Comments
 (0)