mirror of https://github.com/wolfSSL/wolfTPM.git
Enforce SPDM transport policies in the firmware TPM
Implement PolicyTransportSPDM and the SPDM session-info capability in the fwTPM. Track authenticated responder key names, fail closed on malformed state, and add regression coverage for policy enforcement.pull/594/head
parent
b064b409ae
commit
ecf89639a8
|
|
@ -38,6 +38,16 @@
|
|||
#include <wolftpm/fwtpm/fwtpm_command.h>
|
||||
#include <wolftpm/fwtpm/fwtpm_nv.h>
|
||||
#include <wolftpm/fwtpm/fwtpm_crypto.h>
|
||||
/* Responder objects are linked into fwtpm_server (FWTPM_SPDM_HAVE_RESPONDER)
|
||||
* and into libwolftpm. The fwTPM unit test and fuzz harness compile this file
|
||||
* standalone against wolfSSL only, so they drive the SPDM flag directly. */
|
||||
#if defined(WOLFTPM_SPDM_RESPONDER) && \
|
||||
(defined(FWTPM_SPDM_HAVE_RESPONDER) || defined(BUILDING_WOLFTPM))
|
||||
#define FWTPM_SPDM_USE_RESPONDER
|
||||
#endif
|
||||
#ifdef FWTPM_SPDM_USE_RESPONDER
|
||||
#include <wolftpm/spdm/spdm_responder.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -200,6 +210,180 @@ static TPM_RC FwSkipAuthArea(TPM2_Packet* cmd, int cmdSize)
|
|||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef WOLFTPM_SPDM
|
||||
#ifdef FWTPM_SPDM_USE_RESPONDER
|
||||
#define FWTPM_SPDM_P384_SZ 48
|
||||
/* Name of a marshaled TPMT_PUBLIC: nameAlg || H_nameAlg(publicArea). The
|
||||
* publicArea is marshaled type(2) || nameAlg(2) || ..., so nameAlg is at
|
||||
* offset 2, not 0. */
|
||||
static TPM_RC FwNameFromPublicArea(const byte* pub, word32 pubSz,
|
||||
TPM2B_NAME* name)
|
||||
{
|
||||
TPM_RC rc = TPM_RC_SUCCESS;
|
||||
UINT16 nameAlg;
|
||||
int digestSz;
|
||||
enum wc_HashType wcHash;
|
||||
|
||||
name->size = 0;
|
||||
if (pub == NULL || pubSz < 4) {
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
nameAlg = (UINT16)(((UINT16)pub[2] << 8) | pub[3]);
|
||||
digestSz = TPM2_GetHashDigestSize(nameAlg);
|
||||
wcHash = FwGetWcHashType(nameAlg);
|
||||
if (digestSz <= 0 || wcHash == WC_HASH_TYPE_NONE ||
|
||||
digestSz + 2 > (int)sizeof(name->name)) {
|
||||
return TPM_RC_HASH;
|
||||
}
|
||||
name->name[0] = pub[2];
|
||||
name->name[1] = pub[3];
|
||||
if (wc_Hash(wcHash, pub, pubSz, name->name + 2, digestSz) != 0) {
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
if (rc == 0) {
|
||||
name->size = (UINT16)(2 + digestSz);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Compute the Name of the responder's own SPDM identity key (raw P-384
|
||||
* X||Y) using the TCG SPDM key template. */
|
||||
static TPM_RC FwSpdmTpmKeyName(const byte* rawPub, word32 rawPubSz,
|
||||
TPM2B_NAME* name)
|
||||
{
|
||||
TPM_RC rc = TPM_RC_SUCCESS;
|
||||
TPM2_Packet pkt;
|
||||
FWTPM_DECLARE_VAR(pub, TPMT_PUBLIC);
|
||||
FWTPM_DECLARE_BUF(buf, FWTPM_MAX_PUB_BUF);
|
||||
|
||||
name->size = 0;
|
||||
if (rawPub == NULL || rawPubSz != 2 * FWTPM_SPDM_P384_SZ) {
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
FWTPM_ALLOC_VAR(pub, TPMT_PUBLIC);
|
||||
FWTPM_ALLOC_BUF(buf, FWTPM_MAX_PUB_BUF);
|
||||
if (rc == 0) {
|
||||
XMEMSET(pub, 0, sizeof(TPMT_PUBLIC));
|
||||
pub->type = TPM_ALG_ECC;
|
||||
pub->nameAlg = TPM_ALG_SHA384;
|
||||
pub->objectAttributes = TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
||||
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_restricted |
|
||||
TPMA_OBJECT_sign;
|
||||
pub->parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL;
|
||||
pub->parameters.eccDetail.scheme.scheme = TPM_ALG_ECDSA;
|
||||
pub->parameters.eccDetail.scheme.details.ecdsa.hashAlg = TPM_ALG_SHA384;
|
||||
pub->parameters.eccDetail.curveID = TPM_ECC_NIST_P384;
|
||||
pub->parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
|
||||
pub->unique.ecc.x.size = FWTPM_SPDM_P384_SZ;
|
||||
XMEMCPY(pub->unique.ecc.x.buffer, rawPub, FWTPM_SPDM_P384_SZ);
|
||||
pub->unique.ecc.y.size = FWTPM_SPDM_P384_SZ;
|
||||
XMEMCPY(pub->unique.ecc.y.buffer, rawPub + FWTPM_SPDM_P384_SZ,
|
||||
FWTPM_SPDM_P384_SZ);
|
||||
|
||||
XMEMSET(&pkt, 0, sizeof(pkt));
|
||||
pkt.buf = buf;
|
||||
pkt.size = (int)FWTPM_MAX_PUB_BUF;
|
||||
TPM2_Packet_AppendPublicArea(&pkt, pub);
|
||||
rc = FwNameFromPublicArea(buf, (word32)pkt.pos, name);
|
||||
}
|
||||
FWTPM_FREE_BUF(buf);
|
||||
FWTPM_FREE_VAR(pub);
|
||||
return rc;
|
||||
}
|
||||
#endif /* FWTPM_SPDM_USE_RESPONDER */
|
||||
|
||||
/* Returns 1 when the command being processed arrived inside an established
|
||||
* SPDM secure session. Any SPDM secure session qualifies, asymmetric (TCG
|
||||
* binding) or vendor PSK, since wolfTPM supports both SPDM session types.
|
||||
* reqKeyName is always empty: the responder performs no requester
|
||||
* mutual-authentication, so a requester key is never trusted. tpmKeyName is
|
||||
* the responder's own identity-key Name when one exists. */
|
||||
static int FwSpdmSessionNames(FWTPM_CTX* ctx, TPM2B_NAME* reqKeyName,
|
||||
TPM2B_NAME* tpmKeyName)
|
||||
{
|
||||
#ifdef FWTPM_SPDM_USE_RESPONDER
|
||||
const byte* idPub = NULL;
|
||||
word32 idPubSz = 0;
|
||||
#endif
|
||||
|
||||
reqKeyName->size = 0;
|
||||
tpmKeyName->size = 0;
|
||||
if (!ctx->activeCmdOverSpdm) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef FWTPM_SPDM_USE_RESPONDER
|
||||
if (ctx->spdmRespCtx != NULL) {
|
||||
if (!wolfSPDM_RespIsSessionActive(ctx->spdmRespCtx)) {
|
||||
return 0;
|
||||
}
|
||||
idPubSz = wolfSPDM_RespGetIdentityKey(ctx->spdmRespCtx, &idPub);
|
||||
if (FwSpdmTpmKeyName(idPub, idPubSz, tpmKeyName) != 0) {
|
||||
tpmKeyName->size = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef FWTPM_NO_POLICY
|
||||
/* scKeyNameHash = H(req.size || req.name || tpm.size || tpm.name). A name
|
||||
* that is not checked contributes a zero size (Part 3 CompareScKeyNameHash). */
|
||||
static TPM_RC FwScKeyNameHash(TPMI_ALG_HASH hashAlg,
|
||||
const TPM2B_NAME* reqKeyName, int inclReq,
|
||||
const TPM2B_NAME* tpmKeyName, int inclTpm,
|
||||
byte* out, int* outSz)
|
||||
{
|
||||
TPM_RC rc = TPM_RC_SUCCESS;
|
||||
FWTPM_DECLARE_VAR(hashCtx, wc_HashAlg);
|
||||
enum wc_HashType wcHash = FwGetWcHashType(hashAlg);
|
||||
int digestSz = TPM2_GetHashDigestSize(hashAlg);
|
||||
byte szBuf[2];
|
||||
UINT16 sz;
|
||||
|
||||
FWTPM_ALLOC_VAR(hashCtx, wc_HashAlg);
|
||||
if (rc == 0 && (digestSz <= 0 || wcHash == WC_HASH_TYPE_NONE)) {
|
||||
rc = TPM_RC_HASH;
|
||||
}
|
||||
if (rc == 0 && wc_HashInit_ex(hashCtx, wcHash, NULL, INVALID_DEVID) != 0) {
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
if (rc == 0) {
|
||||
sz = inclReq ? reqKeyName->size : 0;
|
||||
szBuf[0] = (byte)(sz >> 8);
|
||||
szBuf[1] = (byte)sz;
|
||||
if (wc_HashUpdate(hashCtx, wcHash, szBuf, 2) != 0) {
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
if (rc == 0 && sz > 0 &&
|
||||
wc_HashUpdate(hashCtx, wcHash, reqKeyName->name, sz) != 0) {
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
if (rc == 0) {
|
||||
sz = inclTpm ? tpmKeyName->size : 0;
|
||||
szBuf[0] = (byte)(sz >> 8);
|
||||
szBuf[1] = (byte)sz;
|
||||
if (wc_HashUpdate(hashCtx, wcHash, szBuf, 2) != 0) {
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
}
|
||||
if (rc == 0 && sz > 0 &&
|
||||
wc_HashUpdate(hashCtx, wcHash, tpmKeyName->name, sz) != 0) {
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
if (rc == 0 && wc_HashFinal(hashCtx, wcHash, out) != 0) {
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
wc_HashFree(hashCtx, wcHash);
|
||||
if (rc == 0) {
|
||||
*outSz = digestSz;
|
||||
}
|
||||
}
|
||||
FWTPM_FREE_VAR(hashCtx);
|
||||
return rc;
|
||||
}
|
||||
#endif /* !FWTPM_NO_POLICY */
|
||||
#endif /* WOLFTPM_SPDM */
|
||||
|
||||
/* Map hash alg to fwTPM PCR bank index */
|
||||
static int FwGetPcrBankIndex(UINT16 hashAlg)
|
||||
{
|
||||
|
|
@ -1355,6 +1539,11 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
printf("fwTPM: GetCapability(cap=0x%x, prop=0x%x, count=%d)\n",
|
||||
capability, property, propertyCount);
|
||||
#endif
|
||||
#ifdef WOLFTPM_SPDM
|
||||
if (capability == TPM_CAP_SPDM_SESSION_INFO && property != 0) {
|
||||
return TPM_RC_VALUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
paramStart = FwRspParamsBegin(rsp, cmdTag, ¶mSzPos);
|
||||
|
||||
|
|
@ -1893,6 +2082,30 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
TPM2_Packet_AppendU32(rsp, 0);
|
||||
break;
|
||||
|
||||
#ifdef WOLFTPM_SPDM
|
||||
case TPM_CAP_SPDM_SESSION_INFO: {
|
||||
TPM2B_NAME reqKeyName;
|
||||
TPM2B_NAME tpmKeyName;
|
||||
/* A session entry exists only when this command came over SPDM. */
|
||||
int haveSession = FwSpdmSessionNames(ctx, &reqKeyName, &tpmKeyName);
|
||||
if (haveSession && propertyCount > 0) {
|
||||
TPM2_Packet_AppendU32(rsp, 1);
|
||||
TPM2_Packet_AppendU16(rsp, reqKeyName.size);
|
||||
TPM2_Packet_AppendBytes(rsp, reqKeyName.name, reqKeyName.size);
|
||||
TPM2_Packet_AppendU16(rsp, tpmKeyName.size);
|
||||
TPM2_Packet_AppendBytes(rsp, tpmKeyName.name, tpmKeyName.size);
|
||||
}
|
||||
else {
|
||||
TPM2_Packet_AppendU32(rsp, 0);
|
||||
/* Entry exists but was not returned (propertyCount 0): the
|
||||
* TPM must report moreData=YES per Part 3 GetCapability. */
|
||||
if (haveSession)
|
||||
FwPatchMoreData(rsp, moreDataPos, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFTPM_SPDM */
|
||||
|
||||
default:
|
||||
TPM2_Packet_AppendU32(rsp, 0);
|
||||
break;
|
||||
|
|
@ -10725,6 +10938,12 @@ static TPM_RC FwCmd_PolicyRestart(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
sess->nvWrittenState = 0;
|
||||
sess->pcrUpdateCounter = 0;
|
||||
sess->hasPcrUpdateCounter = 0;
|
||||
#ifdef WOLFTPM_SPDM
|
||||
sess->checkSecureChannel = 0;
|
||||
sess->checkReqKey = 0;
|
||||
sess->checkTpmKey = 0;
|
||||
sess->scKeyNameHash.size = 0;
|
||||
#endif
|
||||
|
||||
FwRspFinalize(rsp, TPM_ST_NO_SESSIONS, TPM_RC_SUCCESS);
|
||||
}
|
||||
|
|
@ -11833,6 +12052,111 @@ static TPM_RC FwCmd_PolicyLocality(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifdef WOLFTPM_SPDM
|
||||
/* A non-empty name is nameAlg || digest and must be exactly that long. */
|
||||
static TPM_RC FwSpdmCheckName(const TPM2B_NAME* name)
|
||||
{
|
||||
UINT16 alg;
|
||||
int digestSz;
|
||||
|
||||
if (name->size == 0) {
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
if (name->size < 2) {
|
||||
return TPM_RC_SIZE;
|
||||
}
|
||||
alg = (UINT16)(((UINT16)name->name[0] << 8) | name->name[1]);
|
||||
digestSz = TPM2_GetHashDigestSize(alg);
|
||||
if (digestSz <= 0 || FwGetWcHashType(alg) == WC_HASH_TYPE_NONE) {
|
||||
return TPM_RC_HASH;
|
||||
}
|
||||
if ((int)name->size - 2 != digestSz) {
|
||||
return TPM_RC_SIZE;
|
||||
}
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
|
||||
static TPM_RC FwSpdmParseName(TPM2_Packet* cmd, int cmdSize, TPM2B_NAME* name)
|
||||
{
|
||||
/* A missing size prefix must be rejected, not read as an empty name. */
|
||||
if (cmd->pos + (int)sizeof(UINT16) > cmdSize) {
|
||||
return TPM_RC_COMMAND_SIZE;
|
||||
}
|
||||
TPM2_Packet_ParseU16(cmd, &name->size);
|
||||
if (name->size > sizeof(name->name)) {
|
||||
return TPM_RC_SIZE;
|
||||
}
|
||||
if (cmd->pos + (int)name->size > cmdSize) {
|
||||
return TPM_RC_COMMAND_SIZE;
|
||||
}
|
||||
TPM2_Packet_ParseBytes(cmd, name->name, name->size);
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
|
||||
/* --- TPM2_PolicyTransportSPDM (CC 0x01A1) --- */
|
||||
/* policyDigest = H(policyDigest || TPM_CC_PolicyTransportSPDM || scKeyNameHash)
|
||||
* Wire: policySession (U32) -> reqKeyName (TPM2B_NAME) -> tpmKeyName. */
|
||||
static TPM_RC FwCmd_PolicyTransportSPDM(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
||||
int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag)
|
||||
{
|
||||
TPM_RC rc = TPM_RC_SUCCESS;
|
||||
UINT32 sessHandle;
|
||||
FWTPM_Session* sess;
|
||||
TPM2B_NAME reqKeyName;
|
||||
TPM2B_NAME tpmKeyName;
|
||||
byte scHash[TPM_MAX_DIGEST_SIZE];
|
||||
int scHashSz = 0;
|
||||
|
||||
XMEMSET(&reqKeyName, 0, sizeof(reqKeyName));
|
||||
XMEMSET(&tpmKeyName, 0, sizeof(tpmKeyName));
|
||||
|
||||
TPM2_Packet_ParseU32(cmd, &sessHandle);
|
||||
if (cmdTag == TPM_ST_SESSIONS) rc = FwSkipAuthArea(cmd, cmdSize);
|
||||
if (rc == 0) rc = FwSpdmParseName(cmd, cmdSize, &reqKeyName);
|
||||
if (rc == 0) rc = FwSpdmParseName(cmd, cmdSize, &tpmKeyName);
|
||||
|
||||
sess = FwFindSession(ctx, sessHandle);
|
||||
if (rc == 0 && sess == NULL) {
|
||||
rc = TPM_RC_VALUE;
|
||||
}
|
||||
if (rc == 0 && sess->sessionType != TPM_SE_POLICY &&
|
||||
sess->sessionType != TPM_SE_TRIAL) {
|
||||
rc = TPM_RC_AUTH_TYPE;
|
||||
}
|
||||
/* Part 3: may only be applied once per session */
|
||||
if (rc == 0 && sess->checkSecureChannel) {
|
||||
rc = TPM_RC_VALUE;
|
||||
}
|
||||
if (rc == 0) rc = FwSpdmCheckName(&reqKeyName);
|
||||
if (rc == 0) rc = FwSpdmCheckName(&tpmKeyName);
|
||||
|
||||
if (rc == 0 && (reqKeyName.size > 0 || tpmKeyName.size > 0)) {
|
||||
rc = FwScKeyNameHash(sess->authHash, &reqKeyName, 1, &tpmKeyName, 1,
|
||||
scHash, &scHashSz);
|
||||
}
|
||||
if (rc == 0) {
|
||||
#ifdef DEBUG_WOLFTPM
|
||||
printf("fwTPM: PolicyTransportSPDM(session=0x%x, req=%d, tpm=%d)\n",
|
||||
sessHandle, reqKeyName.size, tpmKeyName.size);
|
||||
#endif
|
||||
if (FwPolicyExtend(sess, TPM_CC_PolicyTransportSPDM,
|
||||
(scHashSz > 0) ? scHash : NULL, scHashSz, NULL, 0, 0) != 0) {
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
sess->checkSecureChannel = 1;
|
||||
sess->checkReqKey = (reqKeyName.size > 0);
|
||||
sess->checkTpmKey = (tpmKeyName.size > 0);
|
||||
sess->scKeyNameHash.size = (UINT16)scHashSz;
|
||||
XMEMCPY(sess->scKeyNameHash.buffer, scHash, (size_t)scHashSz);
|
||||
FwRspNoParams(rsp, cmdTag);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* WOLFTPM_SPDM */
|
||||
|
||||
/* --- TPM2_PolicySigned (CC 0x0160) --- */
|
||||
/* Simplified: verify signature and extend policyDigest with
|
||||
* H(policyDigest || TPM_CC_PolicySigned || authObject.name).
|
||||
|
|
@ -18563,6 +18887,9 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = {
|
|||
{ TPM_CC_PolicySecret, FwCmd_PolicySecret, 2, 1, 0, 0 },
|
||||
{ TPM_CC_PolicyAuthorize, FwCmd_PolicyAuthorize, 1, 0, 0, 0 },
|
||||
{ TPM_CC_PolicyLocality, FwCmd_PolicyLocality, 1, 0, 0, 0 },
|
||||
#ifdef WOLFTPM_SPDM
|
||||
{ TPM_CC_PolicyTransportSPDM, FwCmd_PolicyTransportSPDM, 1, 0, 0, FW_CMD_FLAG_ENC },
|
||||
#endif
|
||||
{ TPM_CC_PolicySigned, FwCmd_PolicySigned, 2, 0, 0, 0 },
|
||||
#ifndef FWTPM_NO_NV
|
||||
{ TPM_CC_PolicyNV, FwCmd_PolicyNV, 3, 1, 0, 0 },
|
||||
|
|
@ -19519,6 +19846,33 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx,
|
|||
TPM2_ForceZero(cmdAuths, sizeof(cmdAuths));
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
#if defined(WOLFTPM_SPDM) && !defined(FWTPM_NO_POLICY)
|
||||
/* Enforce PolicyTransportSPDM: the command must have arrived
|
||||
* over an SPDM session, optionally under specific keys. */
|
||||
if (pSess->checkSecureChannel) {
|
||||
TPM2B_NAME scReq;
|
||||
TPM2B_NAME scTpm;
|
||||
byte scHash[TPM_MAX_DIGEST_SIZE];
|
||||
int scHashSz = 0;
|
||||
if (!FwSpdmSessionNames(ctx, &scReq, &scTpm)) {
|
||||
*rspSize = FwBuildErrorResponse(rspBuf, rspCap,
|
||||
TPM_ST_NO_SESSIONS, TPM_RC_CHANNEL);
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
if ((pSess->checkReqKey || pSess->checkTpmKey) &&
|
||||
(FwScKeyNameHash(pSess->authHash,
|
||||
&scReq, pSess->checkReqKey,
|
||||
&scTpm, pSess->checkTpmKey,
|
||||
scHash, &scHashSz) != 0 ||
|
||||
scHashSz != (int)pSess->scKeyNameHash.size ||
|
||||
TPM2_ConstantCompare(pSess->scKeyNameHash.buffer,
|
||||
scHash, (word32)scHashSz) != 0)) {
|
||||
*rspSize = FwBuildErrorResponse(rspBuf, rspCap,
|
||||
TPM_ST_NO_SESSIONS, TPM_RC_CHANNEL_KEY);
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
}
|
||||
#endif /* WOLFTPM_SPDM && !FWTPM_NO_POLICY */
|
||||
#ifndef FWTPM_NO_PP
|
||||
/* Enforce PolicyPhysicalPresence: the platform PP signal must
|
||||
* be asserted now (Part 1 Sec.23.2). */
|
||||
|
|
|
|||
|
|
@ -111,7 +111,9 @@ static int fwtpmSpdmTpmDispatch(void* userCtx,
|
|||
return BAD_FUNC_ARG;
|
||||
}
|
||||
rspSize = (int)sizeof(stageBuf);
|
||||
ctx->activeCmdOverSpdm = 1;
|
||||
rc = FWTPM_ProcessCommand(ctx, cmd, (int)cmdSz, stageBuf, &rspSize, 0);
|
||||
ctx->activeCmdOverSpdm = 0;
|
||||
if (rc == TPM_RC_SUCCESS && rspSize >= TPM2_HEADER_SIZE) {
|
||||
if ((word32)rspSize <= respBufSz) {
|
||||
XMEMCPY(resp, stageBuf, (size_t)rspSize);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ src_fwtpm_fwtpm_server_SOURCES += \
|
|||
src/spdm/spdm_secured.c \
|
||||
src/spdm/spdm_session.c \
|
||||
src/spdm/spdm_transcript.c
|
||||
src_fwtpm_fwtpm_server_CFLAGS += -DFWTPM_SPDM_HAVE_RESPONDER
|
||||
src_fwtpm_fwtpm_server_CPPFLAGS += -DFWTPM_SPDM_HAVE_RESPONDER
|
||||
if BUILD_SPDM_TCG
|
||||
src_fwtpm_fwtpm_server_SOURCES += src/spdm/spdm_tcg.c
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ struct WOLFSPDM_RESP_CTX {
|
|||
* rejected with TPM_RC_DISABLED */
|
||||
unsigned int pskProvisioned : 1; /* PSK_SET / PSK_CLR vendor state */
|
||||
unsigned int clearAuthSet : 1; /* a ClearAuth digest is stored */
|
||||
unsigned int sessionAsym : 1; /* session came from KEY_EXCHANGE */
|
||||
unsigned int pendingAsym : 1; /* KEY_EX reached via KEY_EXCHANGE */
|
||||
} flags;
|
||||
|
||||
/* SHA-384(ClearAuth) stored on PSK_SET, verified on PSK_CLR. */
|
||||
|
|
@ -191,6 +193,11 @@ int wolfSPDM_RespSetIdentityKey(WOLFSPDM_RESP_CTX* ctx,
|
|||
pubSz != WOLFSPDM_ECC_POINT_SIZE) {
|
||||
return WOLFSPDM_E_INVALID_ARG;
|
||||
}
|
||||
/* Rotating the key mid-session would attribute that session to a key it
|
||||
* never negotiated with. */
|
||||
if (ctx->ctx.state != WOLFSPDM_STATE_INIT) {
|
||||
return WOLFSPDM_E_BAD_STATE;
|
||||
}
|
||||
XMEMCPY(ctx->idPrivKey, privKey, privSz);
|
||||
ctx->idPrivKeyLen = privSz;
|
||||
XMEMCPY(ctx->idPubKey, pubKey, pubSz);
|
||||
|
|
@ -223,6 +230,26 @@ int wolfSPDM_RespIsLocked(const WOLFSPDM_RESP_CTX* ctx)
|
|||
return (ctx != NULL && ctx->flags.spdmOnlyLock) ? 1 : 0;
|
||||
}
|
||||
|
||||
int wolfSPDM_RespIsSessionActive(const WOLFSPDM_RESP_CTX* ctx)
|
||||
{
|
||||
if (ctx == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return (ctx->ctx.state == WOLFSPDM_STATE_CONNECTED &&
|
||||
ctx->ctx.sessionId != 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
word32 wolfSPDM_RespGetIdentityKey(const WOLFSPDM_RESP_CTX* ctx,
|
||||
const byte** idPub)
|
||||
{
|
||||
if (ctx == NULL || idPub == NULL || !ctx->flags.hasIdKey ||
|
||||
!ctx->flags.sessionAsym) {
|
||||
return 0;
|
||||
}
|
||||
*idPub = ctx->idPubKey;
|
||||
return ctx->idPubKeyLen;
|
||||
}
|
||||
|
||||
void wolfSPDM_RespReset(WOLFSPDM_RESP_CTX* ctx)
|
||||
{
|
||||
if (ctx == NULL) {
|
||||
|
|
@ -248,6 +275,8 @@ void wolfSPDM_RespReset(WOLFSPDM_RESP_CTX* ctx)
|
|||
ctx->ctx.rspSeqNum = 0;
|
||||
ctx->ctx.sessionId = 0;
|
||||
ctx->ctx.state = WOLFSPDM_STATE_INIT;
|
||||
ctx->flags.sessionAsym = 0;
|
||||
ctx->flags.pendingAsym = 0;
|
||||
}
|
||||
|
||||
#ifdef WOLFTPM_SPDM_TCG
|
||||
|
|
@ -537,6 +566,7 @@ static int RespBuildPskExchangeRsp(WOLFSPDM_RESP_CTX* rctx,
|
|||
if (rc == WOLFSPDM_SUCCESS) {
|
||||
*outSz = off;
|
||||
ctx->state = WOLFSPDM_STATE_KEY_EX;
|
||||
rctx->flags.pendingAsym = 0;
|
||||
}
|
||||
|
||||
wc_ForceZero(verifyData, sizeof(verifyData));
|
||||
|
|
@ -768,6 +798,7 @@ static int RespBuildKeyExchangeRsp(WOLFSPDM_RESP_CTX* rctx,
|
|||
if (rc == WOLFSPDM_SUCCESS) {
|
||||
*outSz = off;
|
||||
ctx->state = WOLFSPDM_STATE_KEY_EX;
|
||||
rctx->flags.pendingAsym = 1;
|
||||
}
|
||||
|
||||
wc_ForceZero(savedReqPriv, sizeof(savedReqPriv));
|
||||
|
|
@ -827,6 +858,7 @@ static int RespHandleFinish(WOLFSPDM_RESP_CTX* rctx,
|
|||
}
|
||||
if (rc == WOLFSPDM_SUCCESS) {
|
||||
ctx->state = WOLFSPDM_STATE_CONNECTED;
|
||||
rctx->flags.sessionAsym = 1;
|
||||
}
|
||||
|
||||
wc_ForceZero(expectedHmac, sizeof(expectedHmac));
|
||||
|
|
@ -885,6 +917,7 @@ static int RespHandlePskFinish(WOLFSPDM_RESP_CTX* rctx,
|
|||
* requester decrypts with handshake keys but we wrote with app keys). */
|
||||
if (rc == WOLFSPDM_SUCCESS) {
|
||||
ctx->state = WOLFSPDM_STATE_CONNECTED;
|
||||
rctx->flags.sessionAsym = 0;
|
||||
}
|
||||
|
||||
wc_ForceZero(expectedHmac, sizeof(expectedHmac));
|
||||
|
|
@ -1124,14 +1157,25 @@ static int RespDispatchSecured(WOLFSPDM_RESP_CTX* rctx,
|
|||
|
||||
respPlainSz = WOLFSPDM_MAX_MSG_SIZE;
|
||||
switch (code) {
|
||||
/* A finish must match the exchange that opened KEY_EX and cannot run
|
||||
* again once connected, or a PSK peer could relabel its session as
|
||||
* identity-key authenticated with a plain FINISH. */
|
||||
#ifdef WOLFTPM_SPDM_PSK
|
||||
case SPDM_PSK_FINISH:
|
||||
if (ctx->state != WOLFSPDM_STATE_KEY_EX ||
|
||||
rctx->flags.pendingAsym) {
|
||||
return WOLFSPDM_E_BAD_STATE;
|
||||
}
|
||||
rc = RespHandlePskFinish(rctx, plain, plainSz,
|
||||
respPlain, &respPlainSz);
|
||||
derivedAppKeys = (rc == WOLFSPDM_SUCCESS) ? 1 : 0;
|
||||
break;
|
||||
#endif
|
||||
case SPDM_FINISH:
|
||||
if (ctx->state != WOLFSPDM_STATE_KEY_EX ||
|
||||
!rctx->flags.pendingAsym) {
|
||||
return WOLFSPDM_E_BAD_STATE;
|
||||
}
|
||||
rc = RespHandleFinish(rctx, plain, plainSz,
|
||||
respPlain, &respPlainSz);
|
||||
derivedAppKeys = (rc == WOLFSPDM_SUCCESS) ? 1 : 0;
|
||||
|
|
|
|||
|
|
@ -10460,6 +10460,294 @@ static void test_fwtpm_policy_locality_enforced(void)
|
|||
printf("Test fwTPM:\tPolicyLocality enforced:\tPassed\n");
|
||||
}
|
||||
|
||||
#ifdef WOLFTPM_SPDM
|
||||
/* PolicyTransportSPDM(sessHandle, reqKeyName, tpmKeyName) */
|
||||
static TPM_RC SendPolicyTransportSPDM(FWTPM_CTX* ctx, UINT32 sessH,
|
||||
const byte* req, UINT16 reqSz, const byte* tpm, UINT16 tpmSz)
|
||||
{
|
||||
int pos = 0, rspSize = 0;
|
||||
PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2;
|
||||
PutU32BE(gCmd + pos, 0); pos += 4;
|
||||
PutU32BE(gCmd + pos, TPM_CC_PolicyTransportSPDM); pos += 4;
|
||||
PutU32BE(gCmd + pos, sessH); pos += 4;
|
||||
pos = AppendPwAuth(gCmd, pos, NULL, 0);
|
||||
PutU16BE(gCmd + pos, reqSz); pos += 2;
|
||||
if (reqSz > 0) { memcpy(gCmd + pos, req, reqSz); pos += reqSz; }
|
||||
PutU16BE(gCmd + pos, tpmSz); pos += 2;
|
||||
if (tpmSz > 0) { memcpy(gCmd + pos, tpm, tpmSz); pos += tpmSz; }
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
FWTPM_ProcessCommand(ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
return GetRspRC(gRsp);
|
||||
}
|
||||
|
||||
static void ReadPolicyDigest(FWTPM_CTX* ctx, UINT32 sessH, byte* digest,
|
||||
UINT16* dSz)
|
||||
{
|
||||
AssertIntEQ(SendPolicyCmd(ctx, TPM_CC_PolicyGetDigest, sessH),
|
||||
TPM_RC_SUCCESS);
|
||||
*dSz = GetU16BE(gRsp + TPM2_HEADER_SIZE + 4);
|
||||
AssertIntEQ(*dSz, 32);
|
||||
memcpy(digest, gRsp + TPM2_HEADER_SIZE + 6, *dSz);
|
||||
}
|
||||
|
||||
/* Vectors: SHA-256 of (zeros[32] || 0x000001A1 || scKeyNameHash) where
|
||||
* scKeyNameHash = SHA-256(reqSz || req || tpmSz || tpm) or absent. */
|
||||
static const byte kSpdmDigestNoNames[32] = {
|
||||
0xf9,0x63,0xdc,0x07,0x41,0x29,0x97,0x27,0x0c,0xb4,0x3f,0xf9,0x3f,0x56,0xd3,0x58,
|
||||
0x61,0xe1,0xc9,0x5c,0x3c,0x5d,0x07,0xc7,0x33,0x9b,0x5c,0xf5,0xbb,0xa1,0x58,0x2d
|
||||
};
|
||||
static const byte kSpdmDigestBothNames[32] = {
|
||||
0x95,0x2f,0x41,0x84,0xb8,0x29,0x2a,0x66,0xa4,0x5e,0xb6,0x61,0xb9,0xfd,0xad,0x4c,
|
||||
0x6d,0x7e,0x49,0x0a,0xe7,0x4b,0x0b,0x7c,0x0b,0x7f,0x12,0x54,0x1c,0x9d,0x4d,0x95
|
||||
};
|
||||
static const byte kSpdmDigestReqOnly[32] = {
|
||||
0x1b,0x94,0xc1,0xb4,0x82,0x5a,0x35,0xd5,0x08,0x7e,0x75,0xba,0x0e,0xee,0x72,0xf8,
|
||||
0xef,0xff,0x32,0xf1,0xc9,0x86,0x0c,0xbf,0xec,0x51,0x84,0x28,0xbd,0xc0,0x56,0x3c
|
||||
};
|
||||
|
||||
static void FillSpdmTestName(byte* name, byte fill)
|
||||
{
|
||||
PutU16BE(name, TPM_ALG_SHA256);
|
||||
memset(name + 2, fill, 32);
|
||||
}
|
||||
|
||||
static void test_fwtpm_policy_transport_spdm(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
UINT32 sessH;
|
||||
UINT16 dSz;
|
||||
int tpos, trspSize;
|
||||
byte digest[64];
|
||||
byte reqName[34];
|
||||
byte tpmName[34];
|
||||
byte badName[34];
|
||||
|
||||
FillSpdmTestName(reqName, 0x11);
|
||||
FillSpdmTestName(tpmName, 0x22);
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
||||
/* No names: policyDigest = H(0 || CC) */
|
||||
sessH = StartSessionHelper(&ctx, TPM_SE_POLICY);
|
||||
AssertIntNE(sessH, 0);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, NULL, 0, NULL, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
ReadPolicyDigest(&ctx, sessH, digest, &dSz);
|
||||
AssertIntEQ(memcmp(digest, kSpdmDigestNoNames, 32), 0);
|
||||
/* Only once per session */
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, NULL, 0, NULL, 0),
|
||||
TPM_RC_VALUE);
|
||||
/* PolicyRestart clears the binding */
|
||||
AssertIntEQ(SendPolicyCmd(&ctx, TPM_CC_PolicyRestart, sessH),
|
||||
TPM_RC_SUCCESS);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, reqName, 34, tpmName, 34),
|
||||
TPM_RC_SUCCESS);
|
||||
ReadPolicyDigest(&ctx, sessH, digest, &dSz);
|
||||
AssertIntEQ(memcmp(digest, kSpdmDigestBothNames, 32), 0);
|
||||
FlushHandle(&ctx, sessH);
|
||||
|
||||
/* Requester name only */
|
||||
sessH = StartSessionHelper(&ctx, TPM_SE_POLICY);
|
||||
AssertIntNE(sessH, 0);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, reqName, 34, NULL, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
ReadPolicyDigest(&ctx, sessH, digest, &dSz);
|
||||
AssertIntEQ(memcmp(digest, kSpdmDigestReqOnly, 32), 0);
|
||||
FlushHandle(&ctx, sessH);
|
||||
|
||||
/* Malformed names are rejected and leave the session untouched */
|
||||
sessH = StartSessionHelper(&ctx, TPM_SE_POLICY);
|
||||
AssertIntNE(sessH, 0);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, reqName, 1, NULL, 0),
|
||||
TPM_RC_SIZE);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, reqName, 33, NULL, 0),
|
||||
TPM_RC_SIZE);
|
||||
memcpy(badName, reqName, sizeof(badName));
|
||||
PutU16BE(badName, 0x1234);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, NULL, 0, badName, 34),
|
||||
TPM_RC_HASH);
|
||||
/* Truncated command with no name fields is rejected, not read as empty */
|
||||
tpos = 0;
|
||||
trspSize = 0;
|
||||
PutU16BE(gCmd + tpos, TPM_ST_SESSIONS); tpos += 2;
|
||||
PutU32BE(gCmd + tpos, 0); tpos += 4;
|
||||
PutU32BE(gCmd + tpos, TPM_CC_PolicyTransportSPDM); tpos += 4;
|
||||
PutU32BE(gCmd + tpos, sessH); tpos += 4;
|
||||
tpos = AppendPwAuth(gCmd, tpos, NULL, 0);
|
||||
PutU32BE(gCmd + 2, (UINT32)tpos);
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, tpos, gRsp, &trspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_COMMAND_SIZE);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, NULL, 0, NULL, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
FlushHandle(&ctx, sessH);
|
||||
|
||||
/* Trial sessions accept it too */
|
||||
sessH = StartSessionHelper(&ctx, TPM_SE_TRIAL);
|
||||
AssertIntNE(sessH, 0);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, NULL, 0, NULL, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
FlushHandle(&ctx, sessH);
|
||||
|
||||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("PolicyTransportSPDM:", 0);
|
||||
}
|
||||
|
||||
/* A policy carrying PolicyTransportSPDM must only authorize commands that
|
||||
* arrived over an SPDM session; a bound key name must match the session. */
|
||||
static void test_fwtpm_policy_transport_spdm_enforced(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
int pos, cmdSz, rspSize = 0;
|
||||
UINT32 sessH;
|
||||
UINT16 dSz;
|
||||
byte digest[64];
|
||||
byte reqName[34];
|
||||
UINT32 nvIdx = 0x01500081;
|
||||
UINT32 nvAttrs = TPMA_NV_OWNERWRITE | TPMA_NV_OWNERREAD | TPMA_NV_NO_DA;
|
||||
|
||||
FillSpdmTestName(reqName, 0x11);
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
||||
sessH = StartSessionHelper(&ctx, TPM_SE_POLICY);
|
||||
AssertIntNE(sessH, 0);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, NULL, 0, NULL, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
ReadPolicyDigest(&ctx, sessH, digest, &dSz);
|
||||
|
||||
/* Bind that policy to the owner hierarchy */
|
||||
pos = 0;
|
||||
PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2;
|
||||
PutU32BE(gCmd + pos, 0); pos += 4;
|
||||
PutU32BE(gCmd + pos, TPM_CC_SetPrimaryPolicy); pos += 4;
|
||||
PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4;
|
||||
pos = AppendPwAuth(gCmd, pos, NULL, 0);
|
||||
PutU16BE(gCmd + pos, dSz); pos += 2;
|
||||
memcpy(gCmd + pos, digest, dSz); pos += dSz;
|
||||
PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2;
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
/* Plaintext command: digest matches but no SPDM session */
|
||||
cmdSz = BuildNvDefineCmd(gCmd, nvIdx, 8, nvAttrs);
|
||||
PutU32BE(gCmd + 18, sessH); /* replace TPM_RS_PW with the policy session */
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_CHANNEL);
|
||||
|
||||
/* Same command marked as arriving inside an SPDM session */
|
||||
ctx.activeCmdOverSpdm = 1;
|
||||
cmdSz = BuildNvDefineCmd(gCmd, nvIdx, 8, nvAttrs);
|
||||
PutU32BE(gCmd + 18, sessH);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
ctx.activeCmdOverSpdm = 0;
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
FlushHandle(&ctx, sessH);
|
||||
|
||||
/* A requester-key binding cannot be satisfied without a responder
|
||||
* reporting that key, even inside an SPDM session */
|
||||
sessH = StartSessionHelper(&ctx, TPM_SE_POLICY);
|
||||
AssertIntNE(sessH, 0);
|
||||
AssertIntEQ(SendPolicyTransportSPDM(&ctx, sessH, reqName, 34, NULL, 0),
|
||||
TPM_RC_SUCCESS);
|
||||
ReadPolicyDigest(&ctx, sessH, digest, &dSz);
|
||||
pos = 0;
|
||||
PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2;
|
||||
PutU32BE(gCmd + pos, 0); pos += 4;
|
||||
PutU32BE(gCmd + pos, TPM_CC_SetPrimaryPolicy); pos += 4;
|
||||
PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4;
|
||||
pos = AppendPwAuth(gCmd, pos, NULL, 0);
|
||||
PutU16BE(gCmd + pos, dSz); pos += 2;
|
||||
memcpy(gCmd + pos, digest, dSz); pos += dSz;
|
||||
PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2;
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
ctx.activeCmdOverSpdm = 1;
|
||||
cmdSz = BuildNvDefineCmd(gCmd, nvIdx + 1, 8, nvAttrs);
|
||||
PutU32BE(gCmd + 18, sessH);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
ctx.activeCmdOverSpdm = 0;
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_CHANNEL_KEY);
|
||||
FlushHandle(&ctx, sessH);
|
||||
|
||||
/* Undefine the NV index created over the SPDM session */
|
||||
pos = 0;
|
||||
PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2;
|
||||
PutU32BE(gCmd + pos, 0); pos += 4;
|
||||
PutU32BE(gCmd + pos, TPM_CC_NV_UndefineSpace); pos += 4;
|
||||
PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4;
|
||||
PutU32BE(gCmd + pos, nvIdx); pos += 4;
|
||||
pos = AppendPwAuth(gCmd, pos, NULL, 0);
|
||||
PutU32BE(gCmd + 2, (UINT32)pos);
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
|
||||
FWTPM_Cleanup(&ctx);
|
||||
printf("Test fwTPM:\tPolicyTransportSPDM enforced:\tPassed\n");
|
||||
}
|
||||
|
||||
/* TPM_CAP_SPDM_SESSION_INFO is empty outside an SPDM session and lists one
|
||||
* entry inside; property must be zero. */
|
||||
static void test_fwtpm_spdm_session_info_cap(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
int pos, rspSize = 0;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
||||
pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 22, TPM_CC_GetCapability);
|
||||
PutU32BE(gCmd + pos, TPM_CAP_SPDM_SESSION_INFO); pos += 4;
|
||||
PutU32BE(gCmd + pos, 0); pos += 4;
|
||||
PutU32BE(gCmd + pos, 1); pos += 4;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 1),
|
||||
TPM_CAP_SPDM_SESSION_INFO);
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 5), 0);
|
||||
|
||||
ctx.activeCmdOverSpdm = 1;
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
ctx.activeCmdOverSpdm = 0;
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 5), 1);
|
||||
/* No responder context: names are reported empty */
|
||||
AssertIntEQ(GetU16BE(gRsp + TPM2_HEADER_SIZE + 9), 0);
|
||||
AssertIntEQ(GetU16BE(gRsp + TPM2_HEADER_SIZE + 11), 0);
|
||||
|
||||
/* propertyCount 0 yields an empty list but moreData=YES when a session
|
||||
* entry exists and could not be returned */
|
||||
PutU32BE(gCmd + TPM2_HEADER_SIZE + 8, 0);
|
||||
ctx.activeCmdOverSpdm = 1;
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
ctx.activeCmdOverSpdm = 0;
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
AssertIntEQ(gRsp[TPM2_HEADER_SIZE], 1); /* moreData = YES */
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 5), 0);
|
||||
PutU32BE(gCmd + TPM2_HEADER_SIZE + 8, 1); /* restore propertyCount */
|
||||
|
||||
PutU32BE(gCmd + TPM2_HEADER_SIZE + 4, 1); /* property must be 0 */
|
||||
rspSize = 0;
|
||||
FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_VALUE);
|
||||
|
||||
FWTPM_Cleanup(&ctx);
|
||||
fwtpm_pass("SPDM session info capability:", 0);
|
||||
}
|
||||
#endif /* WOLFTPM_SPDM */
|
||||
|
||||
/* PolicyCpHash binds a session to a specific command; a command whose real
|
||||
* cpHash differs must be rejected even when the policyDigest matches. */
|
||||
static void test_fwtpm_policy_cphash_enforced(void)
|
||||
|
|
@ -15837,6 +16125,11 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_policyauthorizenv_owner_read_denied();
|
||||
test_fwtpm_policy_locality_enforced();
|
||||
test_fwtpm_policy_cphash_enforced();
|
||||
#ifdef WOLFTPM_SPDM
|
||||
test_fwtpm_policy_transport_spdm();
|
||||
test_fwtpm_policy_transport_spdm_enforced();
|
||||
test_fwtpm_spdm_session_info_cap();
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)
|
||||
test_fwtpm_admin_authorization_requires_policy();
|
||||
|
|
|
|||
|
|
@ -620,6 +620,12 @@ typedef struct FWTPM_Session {
|
|||
int nvWrittenState; /* PolicyNvWritten writtenSet */
|
||||
UINT32 pcrUpdateCounter; /* PCR update counter seen by PolicyPCR */
|
||||
int hasPcrUpdateCounter; /* 1 once PolicyPCR has been evaluated */
|
||||
#ifdef WOLFTPM_SPDM
|
||||
int checkSecureChannel; /* 1 once PolicyTransportSPDM has been called */
|
||||
int checkReqKey; /* PolicyTransportSPDM bound reqKeyName */
|
||||
int checkTpmKey; /* PolicyTransportSPDM bound tpmKeyName */
|
||||
TPM2B_DIGEST scKeyNameHash; /* PolicyTransportSPDM key name hash */
|
||||
#endif
|
||||
} FWTPM_Session;
|
||||
|
||||
/* NV index slot (user NV RAM) */
|
||||
|
|
@ -766,6 +772,10 @@ typedef struct FWTPM_CTX {
|
|||
* only when clockless or lockoutRecovery==0) */
|
||||
#endif
|
||||
int activeLocality; /* locality of the command being processed */
|
||||
#ifdef WOLFTPM_SPDM
|
||||
int activeCmdOverSpdm; /* command being processed arrived inside an
|
||||
* SPDM secured session */
|
||||
#endif
|
||||
#ifndef FWTPM_NO_PP
|
||||
int physicalPresence; /* Platform-channel PP latch (volatile). Only
|
||||
* consulted when no PP HAL is registered. */
|
||||
|
|
|
|||
|
|
@ -69,7 +69,9 @@ WOLFTPM_API int wolfSPDM_RespSetPSK(WOLFSPDM_RESP_CTX* ctx,
|
|||
const byte* psk, word32 pskSz,
|
||||
const byte* hint, word32 hintSz);
|
||||
|
||||
/* privKey: 48 bytes (P-384 scalar). pubKey: 96 bytes (X||Y, big-endian). */
|
||||
/* privKey: 48 bytes (P-384 scalar). pubKey: 96 bytes (X||Y, big-endian).
|
||||
* Rejected with WOLFSPDM_E_BAD_STATE while a session is negotiating or
|
||||
* connected; reset the responder first. */
|
||||
WOLFTPM_API int wolfSPDM_RespSetIdentityKey(WOLFSPDM_RESP_CTX* ctx,
|
||||
const byte* privKey, word32 privSz,
|
||||
const byte* pubKey, word32 pubSz);
|
||||
|
|
@ -91,6 +93,16 @@ WOLFTPM_API void wolfSPDM_RespReset(WOLFSPDM_RESP_CTX* ctx);
|
|||
* Toggled by the requester via SPDMONLY vendor command. */
|
||||
WOLFTPM_API int wolfSPDM_RespIsLocked(const WOLFSPDM_RESP_CTX* ctx);
|
||||
|
||||
/* Returns 1 when a secured SPDM session is established. */
|
||||
WOLFTPM_API int wolfSPDM_RespIsSessionActive(const WOLFSPDM_RESP_CTX* ctx);
|
||||
|
||||
/* On success, points idPub at the responder's own SPDM identity key (raw
|
||||
* P-384 X||Y) and returns its length. Returns 0 when there is no identity
|
||||
* key or the active session did not authenticate with it (PSK sessions).
|
||||
* The requester's key is never exposed: no requester mutual-auth is done. */
|
||||
WOLFTPM_API word32 wolfSPDM_RespGetIdentityKey(const WOLFSPDM_RESP_CTX* ctx,
|
||||
const byte** idPub);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue