mirror of https://github.com/wolfSSL/wolfTPM.git
F-4673 - Bind context sequence counter to blob and reject replay
parent
c03db2d23d
commit
60aef67a05
|
|
@ -3077,7 +3077,7 @@ static TPM_RC FwCmd_ContextLoad(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
UINT32 magic = 0, version = 0;
|
||||
|
||||
(void)cmdTag;
|
||||
(void)seqHi; (void)seqLo; (void)hierarchy;
|
||||
(void)hierarchy;
|
||||
|
||||
if (cmdSize < TPM2_HEADER_SIZE + 18) {
|
||||
rc = TPM_RC_COMMAND_SIZE;
|
||||
|
|
@ -3091,6 +3091,13 @@ static TPM_RC FwCmd_ContextLoad(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
TPM2_Packet_ParseU16(cmd, &blobSz);
|
||||
}
|
||||
|
||||
/* Replay protection: a saved context is bound to the sequence counter
|
||||
* value it was created with, and each successful load consumes it. */
|
||||
if (rc == 0 &&
|
||||
(((UINT64)seqHi << 32) | (UINT64)seqLo) != ctx->contextSeqCounter) {
|
||||
rc = TPM_RC_INTEGRITY;
|
||||
}
|
||||
|
||||
/* Validate minimum blob size (magic + version = 8 bytes) */
|
||||
if (rc == 0 && blobSz < 8) {
|
||||
rc = TPM_RC_SIZE;
|
||||
|
|
@ -3189,6 +3196,8 @@ static TPM_RC FwCmd_ContextLoad(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
#ifdef DEBUG_WOLFTPM
|
||||
printf("fwTPM: ContextLoad(handle=0x%x)\n", savedHandle);
|
||||
#endif
|
||||
/* Consume this sequence value so the same blob cannot be replayed */
|
||||
ctx->contextSeqCounter++;
|
||||
TPM2_Packet_AppendU32(rsp, savedHandle);
|
||||
FwRspFinalize(rsp, TPM_ST_NO_SESSIONS, TPM_RC_SUCCESS);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2219,6 +2219,18 @@ int FwUnwrapPrivate(FWTPM_Object* parent,
|
|||
/* Context blob wrap/unwrap (ContextSave/ContextLoad) */
|
||||
/* ================================================================== */
|
||||
|
||||
/* Fold a 64-bit value into an HMAC as big-endian, used to bind the context
|
||||
* sequence counter into the blob MAC for replay protection. */
|
||||
static int FwHmacUpdateU64(Hmac* hmac, UINT64 v)
|
||||
{
|
||||
byte b[8];
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
b[i] = (byte)(v >> (56 - 8 * i));
|
||||
}
|
||||
return wc_HmacUpdate(hmac, b, (word32)sizeof(b));
|
||||
}
|
||||
|
||||
/* Encrypt-then-MAC context blob protection using the per-boot key.
|
||||
* Layout: iv(16) | ciphertext(plainSz) | hmac(32)
|
||||
* Returns 0 on success, sets *outSz. */
|
||||
|
|
@ -2271,6 +2283,9 @@ int FwWrapContextBlob(FWTPM_CTX* ctx,
|
|||
if (rc == 0) {
|
||||
rc = wc_HmacUpdate(hmac, out, AES_BLOCK_SIZE + plainSz);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = FwHmacUpdateU64(hmac, ctx->contextSeqCounter);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = wc_HmacFinal(hmac, out + AES_BLOCK_SIZE + plainSz);
|
||||
}
|
||||
|
|
@ -2328,6 +2343,9 @@ int FwUnwrapContextBlob(FWTPM_CTX* ctx,
|
|||
if (rc == 0) {
|
||||
rc = wc_HmacUpdate(hmac, in, AES_BLOCK_SIZE + cipherSz);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = FwHmacUpdateU64(hmac, ctx->contextSeqCounter);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = wc_HmacFinal(hmac, computedHmac);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8482,6 +8482,53 @@ static void test_fwtpm_context_save(void)
|
|||
fwtpm_pass("ContextSave:", 0);
|
||||
}
|
||||
|
||||
/* A saved context must load at most once; replaying the same blob is
|
||||
* rejected to prevent resurrecting a satisfied policy session. */
|
||||
static void test_fwtpm_context_load_replay_rejected(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
int rspSize = 0, ctxSz, pos;
|
||||
UINT32 keyH;
|
||||
byte savedCtx[512];
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
#ifdef HAVE_ECC
|
||||
keyH = CreatePrimaryHelper(&ctx, TPM_ALG_ECC);
|
||||
#else
|
||||
keyH = CreatePrimaryHelper(&ctx, TPM_ALG_RSA);
|
||||
#endif
|
||||
AssertIntNE(keyH, 0);
|
||||
|
||||
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);
|
||||
ctxSz = rspSize - TPM2_HEADER_SIZE; /* TPMS_CONTEXT follows the header */
|
||||
AssertIntGT(ctxSz, 0);
|
||||
memcpy(savedCtx, gRsp + TPM2_HEADER_SIZE, ctxSz);
|
||||
|
||||
/* First load succeeds */
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_ContextLoad);
|
||||
memcpy(gCmd + pos, savedCtx, 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 the identical blob is rejected */
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_ContextLoad);
|
||||
memcpy(gCmd + pos, savedCtx, ctxSz); pos += ctxSz;
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntNE(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
FlushHandle(&ctx, keyH);
|
||||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tContextLoad replay rejected:\tPassed\n");
|
||||
}
|
||||
|
||||
static void test_fwtpm_evict_control(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
|
|
@ -9017,6 +9064,7 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_evict_control_bad_persistent_handle_rejected();
|
||||
test_fwtpm_evict_control_persistent_object_rejected();
|
||||
test_fwtpm_context_save();
|
||||
test_fwtpm_context_load_replay_rejected();
|
||||
|
||||
/* Crypto */
|
||||
test_fwtpm_hash();
|
||||
|
|
|
|||
Loading…
Reference in New Issue