Skip to content

Commit d118866

Browse files
committed
fwtpm: peer-review batch 2 (medium hardening)
Security/bounds: - FlushContext: reject persistent handles (use EvictControl) - PCR_Extend: reject unknown hash alg (dSize=0 desync fix) - HierarchyChangeAuth: ForceZero old auth buffer before overwrite - FwDecryptSeed ECC: bounds check before ySz FwLoadU16BE (OOB read) - FwImportReconstructKey: verify q divides n (mp_div remainder check) - FwWrapPrivate: validate outPriv buffer size before packing - TIS FIFO: snapshot shared-mem positions to locals (TOCTOU) - NV writePos: validate against hal->maxSize on journal load - NV compaction: only reset writePos after successful erase Zeroization: - FwWrapPrivate: zero sensBuf, hmacDigest before free - FwDeriveEccPrimaryKey: constant-time zero check (OR accumulator) - FwCredentialDeriveKeys: zero symKey on second KDFa failure - FwCredentialUnwrap: zero computedHmac, integrityHmac - FwEncryptSeed: zero seedBuf on RSA/ECC error paths - FwImportVerifyAndDecrypt: zero plainSens on decrypt failure - NV save functions: ForceZero marshal buffers before XFREE - KDFa/KDFe: zero output key on mid-loop hash error
1 parent de11f20 commit d118866

5 files changed

Lines changed: 107 additions & 18 deletions

File tree

src/fwtpm/fwtpm_command.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1403,7 +1403,14 @@ static TPM_RC FwCmd_PCR_Extend(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize,
14031403
bank = FwGetPcrBankIndex(hashAlg);
14041404
dSize = TPM2_GetHashDigestSize(hashAlg);
14051405

1406-
if (bank < 0 || dSize == 0) {
1406+
if (dSize == 0) {
1407+
/* Unknown hash algorithm — cannot determine digest size to skip,
1408+
* so reject rather than desync the command buffer parser. */
1409+
rc = TPM_RC_HASH;
1410+
break;
1411+
}
1412+
if (bank < 0) {
1413+
/* Known algorithm but unsupported bank — skip the digest bytes */
14071414
cmd->pos += dSize;
14081415
continue;
14091416
}
@@ -2481,6 +2488,11 @@ static TPM_RC FwCmd_FlushContext(FWTPM_CTX* ctx, TPM2_Packet* cmd,
24812488
FwFreeSession(sess);
24822489
}
24832490
}
2491+
else if ((flushHandle & 0xFF000000) ==
2492+
(PERSISTENT_FIRST & 0xFF000000)) {
2493+
/* Persistent objects cannot be flushed — use EvictControl */
2494+
rc = TPM_RC_HANDLE;
2495+
}
24842496
else {
24852497
/* Transient object handle */
24862498
FWTPM_Object* obj = FwFindObject(ctx, flushHandle);
@@ -3064,25 +3076,33 @@ static TPM_RC FwCmd_HierarchyChangeAuth(FWTPM_CTX* ctx, TPM2_Packet* cmd,
30643076
if (rc == 0) {
30653077
switch (authHandle) {
30663078
case TPM_RH_OWNER:
3079+
TPM2_ForceZero(ctx->ownerAuth.buffer,
3080+
sizeof(ctx->ownerAuth.buffer));
30673081
ctx->ownerAuth.size = newAuthSize;
30683082
if (newAuthSize > 0) {
30693083
XMEMCPY(ctx->ownerAuth.buffer, newAuthBuf, newAuthSize);
30703084
}
30713085
break;
30723086
case TPM_RH_ENDORSEMENT:
3087+
TPM2_ForceZero(ctx->endorsementAuth.buffer,
3088+
sizeof(ctx->endorsementAuth.buffer));
30733089
ctx->endorsementAuth.size = newAuthSize;
30743090
if (newAuthSize > 0) {
30753091
XMEMCPY(ctx->endorsementAuth.buffer, newAuthBuf,
30763092
newAuthSize);
30773093
}
30783094
break;
30793095
case TPM_RH_PLATFORM:
3096+
TPM2_ForceZero(ctx->platformAuth.buffer,
3097+
sizeof(ctx->platformAuth.buffer));
30803098
ctx->platformAuth.size = newAuthSize;
30813099
if (newAuthSize > 0) {
30823100
XMEMCPY(ctx->platformAuth.buffer, newAuthBuf, newAuthSize);
30833101
}
30843102
break;
30853103
case TPM_RH_LOCKOUT:
3104+
TPM2_ForceZero(ctx->lockoutAuth.buffer,
3105+
sizeof(ctx->lockoutAuth.buffer));
30863106
ctx->lockoutAuth.size = newAuthSize;
30873107
if (newAuthSize > 0) {
30883108
XMEMCPY(ctx->lockoutAuth.buffer, newAuthBuf, newAuthSize);

src/fwtpm/fwtpm_crypto.c

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,13 @@ TPM_RC FwDeriveEccPrimaryKey(TPMI_ALG_HASH nameAlg,
573573
rc = TPM_RC_FAILURE;
574574
break;
575575
}
576-
/* Check d != 0 (all zeros) */
577-
allZero = 1;
578-
for (i = 0; i < keySz; i++) {
579-
if (dBuf[i] != 0) {
580-
allZero = 0;
581-
break;
576+
/* Constant-time check d != 0 (all zeros) */
577+
{
578+
volatile byte orAccum = 0;
579+
for (i = 0; i < keySz; i++) {
580+
orAccum |= dBuf[i];
582581
}
582+
allZero = (orAccum == 0);
583583
}
584584
if (!allZero) {
585585
valid = 1; /* Accept — range check done by import */
@@ -1060,6 +1060,12 @@ int FwWrapPrivate(FWTPM_Object* parent,
10601060
wc_HmacFree(hmac);
10611061

10621062
/* Pack into TPM2B_PRIVATE */
1063+
if (rc == 0) {
1064+
int totalSz = 2 + WC_SHA256_DIGEST_SIZE + 2 + sensSz;
1065+
if (totalSz > (int)sizeof(outPriv->buffer)) {
1066+
rc = TPM_RC_SIZE;
1067+
}
1068+
}
10631069
if (rc == 0) {
10641070
/* integritySize(2) + integrity(32) + encSensSize(2) + encSens(N) */
10651071
outPriv->buffer[pos++] = 0;
@@ -1079,6 +1085,8 @@ int FwWrapPrivate(FWTPM_Object* parent,
10791085

10801086
TPM2_ForceZero(aesKey, sizeof(aesKey));
10811087
TPM2_ForceZero(aesIV, sizeof(aesIV));
1088+
TPM2_ForceZero(hmacDigest, sizeof(hmacDigest));
1089+
TPM2_ForceZero(sensBuf, FWTPM_MAX_PRIVKEY_DER + 128);
10821090
FWTPM_FREE_BUF(sensBuf);
10831091
FWTPM_FREE_VAR(aes);
10841092
FWTPM_FREE_VAR(hmac);
@@ -1284,6 +1292,11 @@ TPM_RC FwDecryptSeed(FWTPM_CTX* ctx,
12841292
if (rc == 0) {
12851293
XMEMCPY(xBuf, encSeedBuf + p, xSz);
12861294
p += xSz;
1295+
if (p + 2 > encSeedSz) {
1296+
rc = TPM_RC_SIZE;
1297+
}
1298+
}
1299+
if (rc == 0) {
12871300
ySz = FwLoadU16BE(encSeedBuf + p);
12881301
p += 2;
12891302
if (ySz > MAX_ECC_BYTES || p + ySz > encSeedSz) {
@@ -1433,6 +1446,9 @@ TPM_RC FwEncryptSeed(FWTPM_CTX* ctx,
14331446
wc_FreeRsaKey(rsaKey);
14341447
}
14351448
FWTPM_FREE_VAR(rsaKey);
1449+
if (rc != 0) {
1450+
TPM2_ForceZero(seedBuf, seedBufSz);
1451+
}
14361452
}
14371453
else
14381454
#endif /* !NO_RSA */
@@ -1551,6 +1567,9 @@ TPM_RC FwEncryptSeed(FWTPM_CTX* ctx,
15511567
TPM2_ForceZero(sharedZ, sizeof(sharedZ));
15521568
FWTPM_FREE_VAR(parentPub);
15531569
FWTPM_FREE_VAR(ephemKey);
1570+
if (rc != 0) {
1571+
TPM2_ForceZero(seedBuf, seedBufSz);
1572+
}
15541573
}
15551574
else
15561575
#endif /* HAVE_ECC */
@@ -1677,6 +1696,11 @@ TPM_RC FwImportVerifyAndDecrypt(
16771696
if (rc == 0) {
16781697
*plainSensSzOut = encSensSz;
16791698
}
1699+
else {
1700+
/* Zero caller's output buffer on any failure — it may contain
1701+
* partially decrypted sensitive data. */
1702+
TPM2_ForceZero(plainSens, plainSensBufSz);
1703+
}
16801704

16811705
TPM2_ForceZero(hmacCalc, sizeof(hmacCalc));
16821706
FWTPM_FREE_VAR(aesObj);
@@ -1856,7 +1880,15 @@ TPM_RC FwImportReconstructKey(
18561880
primeBuf, (word32)primeSz);
18571881
}
18581882
if (rc == 0) {
1859-
rc = mp_div(&rsaKey->n, &rsaKey->q, &rsaKey->p, NULL);
1883+
mp_int rem;
1884+
rc = mp_init(&rem);
1885+
if (rc == 0) {
1886+
rc = mp_div(&rsaKey->n, &rsaKey->q, &rsaKey->p, &rem);
1887+
if (rc == 0 && !mp_iszero(&rem)) {
1888+
rc = TPM_RC_BINDING; /* q does not divide n evenly */
1889+
}
1890+
mp_forcezero(&rem);
1891+
}
18601892
}
18611893
if (rc == 0) {
18621894
rc = FwRsaComputeCRT(rsaKey);
@@ -2509,6 +2541,7 @@ TPM_RC FwCredentialDeriveKeys(
25092541
kdfRc = TPM2_KDFa_ex(TPM_ALG_SHA256, seed, seedSz,
25102542
"INTEGRITY", NULL, 0, NULL, 0, hmacKey, hmacKeySz);
25112543
if (kdfRc != hmacKeySz) {
2544+
TPM2_ForceZero(symKey, symKeySz);
25122545
return TPM_RC_FAILURE;
25132546
}
25142547
return TPM_RC_SUCCESS;
@@ -2676,6 +2709,8 @@ TPM_RC FwCredentialUnwrap(
26762709
}
26772710

26782711
TPM2_ForceZero(decBuf, FWTPM_MAX_NV_DATA + 2);
2712+
TPM2_ForceZero(computedHmac, sizeof(computedHmac));
2713+
TPM2_ForceZero(integrityHmac, sizeof(integrityHmac));
26792714
FWTPM_FREE_BUF(decBuf);
26802715
FWTPM_FREE_VAR(aes);
26812716
FWTPM_FREE_VAR(hmac);

src/fwtpm/fwtpm_nv.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,16 @@ int FWTPM_NV_Init(FWTPM_CTX* ctx)
10171017
hdr.maxSize = FwLoadU32LE(hdrBuf + 12);
10181018
}
10191019

1020+
if (rc == TPM_RC_SUCCESS &&
1021+
hdr.magic == FWTPM_NV_MAGIC &&
1022+
hdr.version == FWTPM_NV_VERSION) {
1023+
/* Validate writePos against HAL bounds before using it */
1024+
if (hdr.writePos < sizeof(FWTPM_NV_HEADER) ||
1025+
hdr.writePos > hal->maxSize) {
1026+
/* Corrupt — treat as uninitialized, will generate fresh state */
1027+
rc = TPM_RC_NV_UNINITIALIZED;
1028+
}
1029+
}
10201030
if (rc == TPM_RC_SUCCESS &&
10211031
hdr.magic == FWTPM_NV_MAGIC &&
10221032
hdr.version == FWTPM_NV_VERSION) {
@@ -1170,8 +1180,10 @@ int FWTPM_NV_Save(FWTPM_CTX* ctx)
11701180
rc = hal->erase(hal->ctx, 0, hal->maxSize);
11711181
}
11721182

1173-
/* Reset write position to after header */
1174-
ctx->nvWritePos = (word32)sizeof(FWTPM_NV_HEADER);
1183+
/* Reset write position to after header (only if erase succeeded) */
1184+
if (rc == 0) {
1185+
ctx->nvWritePos = (word32)sizeof(FWTPM_NV_HEADER);
1186+
}
11751187

11761188
/* Write header (will be updated at end with final writePos) */
11771189
if (rc == 0) {
@@ -1425,6 +1437,7 @@ int FWTPM_NV_Save(FWTPM_CTX* ctx)
14251437
#endif
14261438

14271439
ctx->nvCompacting = 0;
1440+
TPM2_ForceZero(buf, bufSz);
14281441
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
14291442
return rc;
14301443
}
@@ -1664,6 +1677,7 @@ int FWTPM_NV_SaveNvIndex(FWTPM_CTX* ctx, int slot)
16641677
buf, (UINT16)pos);
16651678
}
16661679

1680+
TPM2_ForceZero(buf, bufSz);
16671681
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
16681682
return rc;
16691683
}
@@ -1713,6 +1727,7 @@ int FWTPM_NV_SavePersistent(FWTPM_CTX* ctx, int slot)
17131727
buf, (UINT16)pos);
17141728
}
17151729

1730+
TPM2_ForceZero(buf, bufSz);
17161731
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
17171732
return rc;
17181733
}
@@ -1761,6 +1776,7 @@ int FWTPM_NV_SavePrimaryCache(FWTPM_CTX* ctx, int slot)
17611776
buf, (UINT16)pos);
17621777
}
17631778

1779+
TPM2_ForceZero(buf, bufSz);
17641780
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
17651781
return rc;
17661782
}

src/fwtpm/fwtpm_tis.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,18 +168,25 @@ static void TisHandleRegAccess(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs)
168168
/* Write command data to FIFO */
169169
UINT32 i;
170170
UINT32 space;
171+
/* Snapshot write position to local for TOCTOU safety */
172+
UINT32 wpos = regs->fifo_write_pos;
173+
if (wpos >= FWTPM_TIS_FIFO_SIZE) {
174+
regs->fifo_write_pos = 0;
175+
break;
176+
}
171177
/* Clamp len to reg_data buffer size */
172178
if (len > sizeof(regs->reg_data)) {
173179
len = (UINT32)sizeof(regs->reg_data);
174180
}
175-
space = FWTPM_TIS_FIFO_SIZE - regs->fifo_write_pos;
181+
space = FWTPM_TIS_FIFO_SIZE - wpos;
176182
if (len > space) {
177183
len = space;
178184
}
179185
for (i = 0; i < len; i++) {
180-
regs->cmd_buf[regs->fifo_write_pos++] = regs->reg_data[i];
186+
regs->cmd_buf[wpos++] = regs->reg_data[i];
181187
}
182-
regs->cmd_len = regs->fifo_write_pos;
188+
regs->fifo_write_pos = wpos;
189+
regs->cmd_len = wpos;
183190

184191
/* Set EXPECT while we still expect more data */
185192
regs->sts = TisBuildSts(
@@ -252,12 +259,14 @@ static void TisHandleRegAccess(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs)
252259
/* Read response data from FIFO */
253260
UINT32 i;
254261
UINT32 avail;
255-
if (regs->fifo_read_pos > regs->rsp_len ||
256-
regs->fifo_read_pos >= sizeof(regs->rsp_buf)) {
262+
/* Snapshot read state to locals for TOCTOU safety */
263+
UINT32 rpos = regs->fifo_read_pos;
264+
UINT32 rlen = regs->rsp_len;
265+
if (rpos > rlen || rpos >= sizeof(regs->rsp_buf)) {
257266
avail = 0;
258267
}
259268
else {
260-
avail = regs->rsp_len - regs->fifo_read_pos;
269+
avail = rlen - rpos;
261270
}
262271
if (len > avail) {
263272
len = avail;
@@ -266,10 +275,11 @@ static void TisHandleRegAccess(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs)
266275
len = (UINT32)sizeof(regs->reg_data);
267276
}
268277
for (i = 0; i < len; i++) {
269-
regs->reg_data[i] = regs->rsp_buf[regs->fifo_read_pos++];
278+
regs->reg_data[i] = regs->rsp_buf[rpos++];
270279
}
280+
regs->fifo_read_pos = rpos;
271281
/* Update data availability */
272-
if (regs->fifo_read_pos >= regs->rsp_len) {
282+
if (rpos >= rlen) {
273283
/* All response bytes read */
274284
regs->sts = TisBuildSts(
275285
FWTPM_STS_VALID | FWTPM_STS_COMMAND_READY,

src/tpm2_crypto.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ int TPM2_KDFa_ex(
146146
if (ret == 0) {
147147
ret = (int)keySz;
148148
}
149+
else {
150+
/* Zero partial key material on mid-loop hash error */
151+
TPM2_ForceZero(key, keySz);
152+
}
149153
return ret;
150154
#else
151155
(void)hashAlg; (void)keyIn; (void)keyInSz; (void)label;
@@ -271,6 +275,10 @@ int TPM2_KDFe_ex(
271275
if (ret == 0) {
272276
ret = (int)keySz;
273277
}
278+
else {
279+
/* Zero partial key material on mid-loop hash error */
280+
TPM2_ForceZero(key, keySz);
281+
}
274282
return ret;
275283
#else
276284
(void)hashAlg; (void)Z; (void)ZSz; (void)label;

0 commit comments

Comments
 (0)