wolfTPM/tests/spdm_config_tests.c

259 lines
10 KiB
C

/* spdm_config_tests.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfTPM.
*
* wolfTPM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfTPM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wolftpm/tpm2_wrap.h>
#include <wolftpm/tpm2_packet.h>
static byte captured[MAX_COMMAND_SIZE];
static int capturedSz, failCommand, hmacCalls;
static UINT32 configCount;
static int configTruncate;
/* Compile the command builders with a mock transport, so both request
* encoding and response decoding run without a TPM, socket, or SPI bus. */
#if defined(WOLFTPM_LINUX_DEV)
#define TPM2_LINUX_SendCommand test_config_send
#elif defined(WOLFTPM_LINUX_DEV_AUTODETECT)
#define TPM2_LINUX_AUTODETECT_SendCommand test_config_send
#elif defined(WOLFTPM_SWTPM)
#define TPM2_SWTPM_SendCommand test_config_send
#elif defined(WOLFTPM_WINAPI)
#define TPM2_WinApi_SendCommand test_config_send
#else
#define TPM2_TIS_SendCommand test_config_send
#endif
#include "../src/tpm2.c"
int test_config_send(TPM2_CTX* ctx, TPM2_Packet* packet)
{
TPM2_Packet rsp;
UINT32 i;
int isConfig = packet->buf[9] == (byte)TPM_CC_GetCapability;
capturedSz = packet->pos;
XMEMCPY(captured, packet->buf, capturedSz);
TPM2_Packet_Init(ctx, &rsp);
if (failCommand) {
TPM2_Packet_Finalize(&rsp, TPM_ST_NO_SESSIONS, TPM_RC_FAILURE);
}
else if (isConfig) {
TPM2_Packet_AppendU8(&rsp, 0);
TPM2_Packet_AppendU32(&rsp, TPM_CAP_CONFIGS);
TPM2_Packet_AppendU32(&rsp, configCount);
for (i = 0; i < configCount; i++) {
/* configTruncate stops partway through the last entry, which is
* the shape a report cut short on the wire has. */
if (configTruncate && i + 1 == configCount) {
TPM2_Packet_AppendU32(&rsp, i + 1);
if (configTruncate > 1) {
TPM2_Packet_AppendU8(&rsp, (byte)(i & 1));
}
break;
}
TPM2_Packet_AppendU32(&rsp, i + 1);
TPM2_Packet_AppendU8(&rsp, (byte)(i & 1));
TPM2_Packet_AppendU8(&rsp, (byte)((i + 1) & 1));
}
TPM2_Packet_Finalize(&rsp, TPM_ST_NO_SESSIONS, 0);
}
else if (TPM2_IS_HMAC_SESSION(ctx->session[0].sessionHandle)) {
TPM2_AUTH_SESSION* session = &ctx->session[0];
TPM2B_DIGEST hash;
TPMS_AUTH_RESPONSE authRsp;
int rc;
XMEMSET(&hash, 0, sizeof(hash));
XMEMSET(&authRsp, 0, sizeof(authRsp));
hmacCalls++;
authRsp.nonce.size = TPM_SHA256_DIGEST_SIZE;
XMEMSET(authRsp.nonce.buffer, 0x33, authRsp.nonce.size);
authRsp.sessionAttributes = TPMA_SESSION_continueSession;
rc = TPM2_CalcRpHash(session->authHash, TPM_CC_SetCapability,
NULL, 0, &hash);
if (rc == 0) rc = TPM2_CalcHmac(session->authHash, &session->auth,
&hash, &authRsp.nonce, &session->nonceCaller,
authRsp.sessionAttributes, &authRsp.hmac);
if (rc != 0) return rc;
TPM2_Packet_AppendU32(&rsp, 0);
TPM2_Packet_AppendU16(&rsp, authRsp.nonce.size);
TPM2_Packet_AppendBytes(&rsp, authRsp.nonce.buffer, authRsp.nonce.size);
TPM2_Packet_AppendU8(&rsp, authRsp.sessionAttributes);
TPM2_Packet_AppendU16(&rsp, authRsp.hmac.size);
TPM2_Packet_AppendBytes(&rsp, authRsp.hmac.buffer, authRsp.hmac.size);
TPM2_Packet_Finalize(&rsp, TPM_ST_SESSIONS, 0);
}
else {
TPM2_Packet_AppendU32(&rsp, 0); /* parameterSize */
TPM2_Packet_AppendU16(&rsp, 0); /* nonce */
TPM2_Packet_AppendU8(&rsp, 0); /* attributes */
TPM2_Packet_AppendU16(&rsp, 0); /* authorization */
TPM2_Packet_Finalize(&rsp, TPM_ST_SESSIONS, 0);
}
packet->size = rsp.pos;
return 0;
}
#if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \
!defined(WOLFTPM_WINAPI)
#ifdef WOLFTPM_ADV_IO
static int test_config_io(TPM2_CTX* ctx, INT32 read, UINT32 addr, BYTE* buf,
UINT16 sz, void* user)
{ (void)ctx; (void)read; (void)addr; (void)buf; (void)sz; (void)user;
#else
static int test_config_io(TPM2_CTX* ctx, const BYTE* tx, BYTE* rx,
UINT16 sz, void* user)
{ (void)ctx; (void)tx; (void)rx; (void)sz; (void)user;
#endif
return TPM_RC_FAILURE;
}
#endif /* callback transports */
int main(void)
{
static const byte expected[] = {
0x80,0x02,0,0,0,43,0,0,1,0x9f,
0x40,0,0,0x0c, 0,0,0,13,
0x40,0,0,9, 0,0,1,0,4,'t','e','s','t',
0,10, 0x81,0xc9,0,1, 0,0,0,3, 1,0
};
WOLFTPM2_DEV dev;
TPM2B_AUTH auth;
GetCapability_In in;
GetCapability_Out out;
UINT32 counts[] = { 0, 1, MAX_TAGGED_CONFIGS };
UINT32 i, j;
CmdInfo_t info;
int rc;
XMEMSET(&dev, 0, sizeof(dev));
XMEMSET(&auth, 0, sizeof(auth));
XMEMSET(&in, 0, sizeof(in));
XMEMSET(&out, 0, sizeof(out));
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \
defined(WOLFTPM_WINAPI)
rc = TPM2_Init_minimal(&dev.ctx);
#else
rc = TPM2_Init_ex(&dev.ctx, test_config_io, NULL, 0);
#endif
if (rc != 0) return 1;
auth.size = 4; XMEMCPY(auth.buffer, "test", 4);
rc = wolfTPM2_SetAuthPassword(&dev, 0, &auth);
if (rc != 0) return 2;
for (i = 0; i < 2; i++) {
failCommand = (int)i;
rc = wolfTPM2_SpdmInfineonEnable(&dev, 1, 0);
if (rc != (i == 0 ? TPM_RC_SUCCESS : TPM_RC_FAILURE) ||
capturedSz != (int)sizeof(expected) ||
XMEMCMP(captured, expected, sizeof(expected)) != 0 ||
dev.session[0].auth.size != 4 ||
XMEMCMP(dev.session[0].auth.buffer, "test", 4) != 0) {
printf("FAIL: SetCapability rc=0x%x, captured size=%d\n", rc, capturedSz);
return 3;
}
}
puts("PASS: SetCapability wire encoding and platform auth on success/failure");
XMEMSET(&dev.session[0], 0, sizeof(dev.session[0]));
for (i = 0; i < 2; i++) {
failCommand = (int)i;
rc = wolfTPM2_SpdmInfineonEnable(&dev, 1, 0);
if (rc != (i == 0 ? TPM_RC_SUCCESS : TPM_RC_FAILURE) ||
capturedSz != 39 || dev.session[0].sessionHandle != 0 ||
dev.session[0].auth.size != 0) return 6;
}
puts("PASS: unused auth slot restored after success/failure");
failCommand = 0;
in.capability = TPM_CAP_CONFIGS;
in.propertyCount = MAX_TAGGED_CONFIGS;
for (i = 0; i < sizeof(counts) / sizeof(counts[0]); i++) {
configCount = counts[i];
XMEMSET(&out, 0, sizeof(out));
rc = TPM2_GetCapability(&in, &out);
if (rc != 0 || out.capabilityData.data.tpmConfigs.count != configCount)
return 4;
for (j = 0; j < configCount; j++) {
TPMS_TAGGED_CONFIG* cfg = &out.capabilityData.data.tpmConfigs.configs[j];
if (cfg->configuration != j + 1 || cfg->enable != (j & 1) ||
cfg->lock != ((j + 1) & 1)) return 5;
}
}
puts("PASS: TPM_CAP_CONFIGS counts 0, 1 and the maximum");
/* A count the local array cannot hold, and an entry cut short after its
* configuration and after its enable byte. Each must be refused whole:
* a shortened list would read as a complete security report. */
configCount = MAX_TAGGED_CONFIGS + 1;
configTruncate = 0;
XMEMSET(&out, 0, sizeof(out));
rc = TPM2_GetCapability(&in, &out);
if (rc != TPM_RC_SIZE ||
out.capabilityData.data.tpmConfigs.count != 0) {
printf("FAIL: oversize count rc=0x%x count=%u\n", rc,
out.capabilityData.data.tpmConfigs.count);
return 9;
}
for (i = 1; i <= 2; i++) {
configCount = 2;
configTruncate = (int)i;
XMEMSET(&out, 0, sizeof(out));
rc = TPM2_GetCapability(&in, &out);
if (rc != TPM_RC_SIZE ||
out.capabilityData.data.tpmConfigs.count != 0) {
printf("FAIL: truncated entry %u rc=0x%x count=%u\n", i, rc,
out.capabilityData.data.tpmConfigs.count);
return 9;
}
}
configTruncate = 0;
puts("PASS: oversize count and truncated entries refused with TPM_RC_SIZE");
XMEMSET(&info, 0, sizeof(info));
XMEMSET(dev.session, 0, sizeof(dev.session));
info.flags = CMD_FLAG_AUTH_USER1;
dev.session[0].sessionHandle = HMAC_SESSION_FIRST;
if (TPM2_GetCmdAuthCount(&dev.ctx, &info) != 1) return 8;
info.flags = 0;
if (TPM2_GetCmdAuthCount(&dev.ctx, &info) != 0) return 8;
info.flags = CMD_FLAG_AUTH_USER1 | CMD_FLAG_ENC2;
dev.session[0].sessionHandle = TPM_RS_PW;
dev.session[1].sessionHandle = HMAC_SESSION_FIRST;
dev.session[1].sessionAttributes = TPMA_SESSION_decrypt;
if (TPM2_GetCmdAuthCount(&dev.ctx, &info) != 2) return 8;
XMEMSET(dev.session, 0, sizeof(dev.session));
dev.session[0].sessionHandle = HMAC_SESSION_FIRST;
dev.session[0].authHash = TPM_ALG_SHA256;
dev.session[0].auth.size = 4;
XMEMCPY(dev.session[0].auth.buffer, "test", 4);
dev.session[0].nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
dev.session[0].nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
XMEMSET(dev.session[0].nonceTPM.buffer, 0x11, TPM_SHA256_DIGEST_SIZE);
dev.session[0].sessionAttributes = TPMA_SESSION_continueSession;
rc = wolfTPM2_SpdmInfineonEnable(&dev, 1, 0);
if (rc != 0 || hmacCalls != 1 || capturedSz != 103 ||
dev.session[0].nonceTPM.buffer[0] != 0x33) {
printf("FAIL: primary HMAC authorization rc=0x%x, calls=%d\n", rc, hmacCalls);
return 7;
}
puts("PASS: caller HMAC authorization and authenticated response processing");
TPM2_Cleanup(&dev.ctx);
puts("PASS: empty, single and maximum-count TPM_CAP_CONFIGS fixtures");
return 0;
}