mirror of https://github.com/wolfSSL/wolfTPM.git
fwtpm: scope context replay to sessions and accept unsalted param-enc
parent
61f7fec929
commit
5699061b53
|
|
@ -3042,19 +3042,22 @@ static TPM_RC FwCmd_ContextSave(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
}
|
||||
}
|
||||
|
||||
if (rc == 0 &&
|
||||
/* Only session contexts carry single-use replay tracking (the policy
|
||||
* replay concern); object contexts are freely reloadable, so tracking
|
||||
* them would exhaust the small live set under normal multi-load use. */
|
||||
if (rc == 0 && isSession &&
|
||||
ctx->contextLiveCount >= (int)(sizeof(ctx->contextLive) /
|
||||
sizeof(ctx->contextLive[0]))) {
|
||||
/* No room to record another live context; emitting a blob we cannot
|
||||
* track would have it rejected as non-live at load time. */
|
||||
rc = TPM_RC_OBJECT_MEMORY;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/* TPMS_CONTEXT: sequence(8) | savedHandle(4) | hierarchy(4) | blob */
|
||||
ctx->contextSeqCounter++;
|
||||
/* Record this context as live so it loads at most once, in any order */
|
||||
ctx->contextLive[ctx->contextLiveCount++] = ctx->contextSeqCounter;
|
||||
if (isSession) {
|
||||
/* Record so this session context loads at most once, any order */
|
||||
ctx->contextLive[ctx->contextLiveCount++] = ctx->contextSeqCounter;
|
||||
}
|
||||
seqHi = (UINT32)(ctx->contextSeqCounter >> 32);
|
||||
seqLo = (UINT32)(ctx->contextSeqCounter & 0xFFFFFFFFu);
|
||||
TPM2_Packet_AppendU32(rsp, seqHi);
|
||||
|
|
@ -3137,10 +3140,13 @@ static TPM_RC FwCmd_ContextLoad(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
TPM2_Packet_ParseU16(cmd, &blobSz);
|
||||
}
|
||||
|
||||
/* Replay protection: the context must be a live (saved, not-yet-loaded)
|
||||
* sequence. A load consumes it; independent saved contexts may load in
|
||||
* any order. */
|
||||
if (rc == 0) {
|
||||
/* Replay protection applies to session contexts only: a saved session
|
||||
* must be a live (not-yet-loaded) sequence and a load consumes it, so a
|
||||
* satisfied policy session cannot be replayed. Object contexts are
|
||||
* reloadable any number of times and are not tracked. */
|
||||
if (rc == 0 &&
|
||||
((savedHandle & 0xFF000000) == HMAC_SESSION_FIRST ||
|
||||
(savedHandle & 0xFF000000) == POLICY_SESSION_FIRST)) {
|
||||
loadSeq = ((UINT64)seqHi << 32) | (UINT64)seqLo;
|
||||
for (liveScan = 0; liveScan < ctx->contextLiveCount; liveScan++) {
|
||||
if (ctx->contextLive[liveScan] == loadSeq) {
|
||||
|
|
@ -15793,14 +15799,13 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx,
|
|||
}
|
||||
|
||||
#ifndef FWTPM_NO_PARAM_ENC
|
||||
/* Detect encryption session (first non-PW with
|
||||
* symmetric alg). A session with an empty
|
||||
* sessionKey (unsalted and unbound) derives a
|
||||
* wire-observable key, so it must not be used
|
||||
* for parameter encryption. */
|
||||
/* Detect encryption session (first non-PW with a
|
||||
* symmetric alg). Unsalted/unbound sessions are
|
||||
* accepted for client compatibility; over the
|
||||
* loopback transport their key is only observable
|
||||
* to a local peer. */
|
||||
if (encSess == NULL &&
|
||||
sess->symmetric.algorithm != TPM_ALG_NULL &&
|
||||
sess->sessionKey.size > 0) {
|
||||
sess->symmetric.algorithm != TPM_ALG_NULL) {
|
||||
encSess = sess;
|
||||
/* decrypt attr = client encrypted cmd param */
|
||||
if ((attribs & TPMA_SESSION_decrypt)
|
||||
|
|
|
|||
|
|
@ -8672,10 +8672,10 @@ static void test_fwtpm_context_save(void)
|
|||
static void test_fwtpm_context_load_replay_rejected(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
int rspSize = 0, ctxSzA, ctxSzB, pos;
|
||||
UINT32 keyH;
|
||||
byte savedCtxA[512];
|
||||
byte savedCtxB[512];
|
||||
int rspSize = 0, ctxSz, pos;
|
||||
UINT32 keyH, sessH;
|
||||
byte savedObj[MAX_CONTEXT_SIZE];
|
||||
byte savedSess[MAX_CONTEXT_SIZE];
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
|
@ -8686,50 +8686,60 @@ static void test_fwtpm_context_load_replay_rejected(void)
|
|||
#endif
|
||||
AssertIntNE(keyH, 0);
|
||||
|
||||
/* Save context A, then save context B while A is still outstanding */
|
||||
/* Object contexts are freely reloadable (real TPM behavior, relied on by
|
||||
* tpm2-tools): save once, load the same blob twice. */
|
||||
BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 14, TPM_CC_ContextSave);
|
||||
PutU32BE(gCmd + 10, keyH);
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, 14, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
ctxSzA = rspSize - TPM2_HEADER_SIZE;
|
||||
AssertIntGT(ctxSzA, 0);
|
||||
memcpy(savedCtxA, gRsp + TPM2_HEADER_SIZE, ctxSzA);
|
||||
ctxSz = rspSize - TPM2_HEADER_SIZE;
|
||||
AssertIntGT(ctxSz, 0);
|
||||
memcpy(savedObj, gRsp + TPM2_HEADER_SIZE, ctxSz);
|
||||
|
||||
BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 14, TPM_CC_ContextSave);
|
||||
PutU32BE(gCmd + 10, keyH);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, 14, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
ctxSzB = rspSize - TPM2_HEADER_SIZE;
|
||||
memcpy(savedCtxB, gRsp + TPM2_HEADER_SIZE, ctxSzB);
|
||||
|
||||
/* Load A first (older blob, B still outstanding) — must succeed */
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_ContextLoad);
|
||||
memcpy(gCmd + pos, savedCtxA, ctxSzA); pos += ctxSzA;
|
||||
memcpy(gCmd + pos, savedObj, ctxSz); pos += ctxSz;
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
/* Replaying A is rejected (single-use) */
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_ContextLoad);
|
||||
memcpy(gCmd + pos, savedCtxA, ctxSzA); pos += ctxSzA;
|
||||
memcpy(gCmd + pos, savedObj, ctxSz); pos += ctxSz;
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
/* Session contexts are single-use: a satisfied policy/HMAC session must
|
||||
* not be replayable. Save a session, load once, then reject the replay. */
|
||||
sessH = StartSessionHelper(&ctx, TPM_SE_HMAC);
|
||||
AssertIntNE(sessH, 0);
|
||||
BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 14, TPM_CC_ContextSave);
|
||||
PutU32BE(gCmd + 10, sessH);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, 14, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
ctxSz = rspSize - TPM2_HEADER_SIZE;
|
||||
AssertIntGT(ctxSz, 0);
|
||||
memcpy(savedSess, gRsp + TPM2_HEADER_SIZE, ctxSz);
|
||||
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_ContextLoad);
|
||||
memcpy(gCmd + pos, savedSess, ctxSz); pos += ctxSz;
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_ContextLoad);
|
||||
memcpy(gCmd + pos, savedSess, ctxSz); pos += ctxSz;
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntNE(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
/* B is still loadable after A was consumed (any-order multi-context) */
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_ContextLoad);
|
||||
memcpy(gCmd + pos, savedCtxB, ctxSzB); pos += ctxSzB;
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
FlushHandle(&ctx, keyH);
|
||||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tContextLoad replay + multi-context:\tPassed\n");
|
||||
printf("Test fwTPM:\tContextLoad object reload + session replay:\tPassed\n");
|
||||
}
|
||||
|
||||
/* When the command client changes, transient objects must be flushed so a
|
||||
|
|
|
|||
Loading…
Reference in New Issue