mirror of https://github.com/wolfSSL/wolfTPM.git
F-12709 - Order handle capability pages numerically
parent
657cfa499f
commit
17c6926f80
|
|
@ -1207,6 +1207,76 @@ static void FwPatchMoreData(TPM2_Packet* rsp, int pos, byte v)
|
|||
rsp->buf[pos] = v;
|
||||
}
|
||||
|
||||
/* Select the numerically lowest handle in handleClass that is at or above
|
||||
* property and, after the first selection, greater than previous. The slot
|
||||
* tables are intentionally rescanned per result: their configured defaults
|
||||
* are small, and this avoids scratch storage proportional to the table sizes. */
|
||||
static int FwSelectCapabilityHandle(const FWTPM_CTX* ctx, UINT32 handleClass,
|
||||
UINT32 property, UINT32 previous, int havePrevious, UINT32* selected)
|
||||
{
|
||||
int idx;
|
||||
int slotCount = 0;
|
||||
int found = 0;
|
||||
int used;
|
||||
UINT32 candidate;
|
||||
|
||||
if (handleClass == HR_TRANSIENT) {
|
||||
slotCount = FWTPM_MAX_OBJECTS;
|
||||
}
|
||||
else if (handleClass == HR_PERSISTENT) {
|
||||
slotCount = FWTPM_MAX_PERSISTENT;
|
||||
}
|
||||
#ifndef FWTPM_NO_NV
|
||||
else if (handleClass == HR_NV_INDEX) {
|
||||
slotCount = FWTPM_MAX_NV_INDICES;
|
||||
}
|
||||
#endif
|
||||
else if (handleClass == HR_HMAC_SESSION ||
|
||||
handleClass == HR_POLICY_SESSION) {
|
||||
slotCount = FWTPM_MAX_SESSIONS;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < slotCount; idx++) {
|
||||
used = 0;
|
||||
candidate = 0;
|
||||
if (handleClass == HR_TRANSIENT) {
|
||||
used = ctx->objects[idx].used;
|
||||
candidate = ctx->objects[idx].handle;
|
||||
}
|
||||
else if (handleClass == HR_PERSISTENT) {
|
||||
used = ctx->persistent[idx].used;
|
||||
candidate = ctx->persistent[idx].handle;
|
||||
}
|
||||
#ifndef FWTPM_NO_NV
|
||||
else if (handleClass == HR_NV_INDEX) {
|
||||
used = ctx->nvIndices[idx].inUse;
|
||||
candidate = ctx->nvIndices[idx].nvPublic.nvIndex;
|
||||
}
|
||||
#endif
|
||||
else if (handleClass == HR_HMAC_SESSION ||
|
||||
handleClass == HR_POLICY_SESSION) {
|
||||
used = ctx->sessions[idx].used;
|
||||
candidate = ctx->sessions[idx].handle;
|
||||
}
|
||||
|
||||
/* TPM_HT_LOADED_SESSION (0x02) covers both HMAC and policy sessions.
|
||||
* Preserve each real 0x02/0x03 handle prefix so the reported handle
|
||||
* remains directly usable instead of normalizing it to 0x02. A 0x03
|
||||
* query is deliberately limited to loaded policy sessions because
|
||||
* fwTPM has no saved-session list. */
|
||||
if (used && (handleClass == HR_HMAC_SESSION ||
|
||||
(candidate & HR_RANGE_MASK) == handleClass) &&
|
||||
candidate >= property &&
|
||||
(!havePrevious || candidate > previous) &&
|
||||
(!found || candidate < *selected)) {
|
||||
*selected = candidate;
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* --- TPM2_GetCapability (CC 0x017A) --- */
|
||||
static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
||||
int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag)
|
||||
|
|
@ -1219,8 +1289,6 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
int paramSzPos, paramStart;
|
||||
int moreDataPos;
|
||||
|
||||
(void)ctx;
|
||||
|
||||
if (cmdSize < TPM2_HEADER_SIZE + 12) {
|
||||
rc = TPM_RC_COMMAND_SIZE;
|
||||
}
|
||||
|
|
@ -1694,107 +1762,30 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd,
|
|||
}
|
||||
|
||||
case TPM_CAP_HANDLES: {
|
||||
int count = 0;
|
||||
int idx;
|
||||
UINT32 handleClass = property & 0xFF000000;
|
||||
UINT32 handleClass = property & HR_RANGE_MASK;
|
||||
UINT32 selected = 0;
|
||||
UINT32 previous = 0;
|
||||
int countPos = rsp->pos;
|
||||
int emitted = 0;
|
||||
int havePrevious = 0;
|
||||
|
||||
/* Filter by handle class per TPM 2.0 spec Part 2 Section 8.4:
|
||||
* only return handles whose upper byte matches property */
|
||||
if (handleClass == 0x80000000) {
|
||||
/* Transient objects */
|
||||
for (idx = 0; idx < FWTPM_MAX_OBJECTS; idx++) {
|
||||
if (ctx->objects[idx].used &&
|
||||
ctx->objects[idx].handle >= property) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
TPM2_Packet_AppendU32(rsp, 0); /* back-patched below */
|
||||
while ((UINT32)emitted < propertyCount &&
|
||||
FwSelectCapabilityHandle(ctx, handleClass, property,
|
||||
previous, havePrevious, &selected)) {
|
||||
TPM2_Packet_AppendU32(rsp, selected);
|
||||
previous = selected;
|
||||
havePrevious = 1;
|
||||
emitted++;
|
||||
}
|
||||
else if (handleClass == 0x81000000) {
|
||||
/* Persistent objects */
|
||||
for (idx = 0; idx < FWTPM_MAX_PERSISTENT; idx++) {
|
||||
if (ctx->persistent[idx].used &&
|
||||
ctx->persistent[idx].handle >= property) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef FWTPM_NO_NV
|
||||
else if (handleClass == 0x01000000) {
|
||||
/* NV indices */
|
||||
for (idx = 0; idx < FWTPM_MAX_NV_INDICES; idx++) {
|
||||
if (ctx->nvIndices[idx].inUse &&
|
||||
ctx->nvIndices[idx].nvPublic.nvIndex >= property) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (handleClass == 0x02000000 ||
|
||||
handleClass == 0x03000000) {
|
||||
/* HMAC / policy sessions */
|
||||
for (idx = 0; idx < FWTPM_MAX_SESSIONS; idx++) {
|
||||
if (ctx->sessions[idx].used &&
|
||||
ctx->sessions[idx].handle >= property) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Other classes (PCR, permanent): report 0 */
|
||||
FwPatchU32BE(rsp, countPos, (UINT32)emitted);
|
||||
|
||||
if ((UINT32)count > propertyCount) {
|
||||
count = (int)propertyCount;
|
||||
FwPatchMoreData(rsp, moreDataPos, 1); /* more handles available */
|
||||
}
|
||||
TPM2_Packet_AppendU32(rsp, (UINT32)count);
|
||||
if (count > 0) {
|
||||
int emitted = 0;
|
||||
if (handleClass == 0x81000000) {
|
||||
for (idx = 0; idx < FWTPM_MAX_PERSISTENT &&
|
||||
emitted < count; idx++) {
|
||||
if (ctx->persistent[idx].used &&
|
||||
ctx->persistent[idx].handle >= property) {
|
||||
TPM2_Packet_AppendU32(rsp,
|
||||
ctx->persistent[idx].handle);
|
||||
emitted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (handleClass == 0x80000000) {
|
||||
for (idx = 0; idx < FWTPM_MAX_OBJECTS &&
|
||||
emitted < count; idx++) {
|
||||
if (ctx->objects[idx].used &&
|
||||
ctx->objects[idx].handle >= property) {
|
||||
TPM2_Packet_AppendU32(rsp,
|
||||
ctx->objects[idx].handle);
|
||||
emitted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef FWTPM_NO_NV
|
||||
else if (handleClass == 0x01000000) {
|
||||
for (idx = 0; idx < FWTPM_MAX_NV_INDICES &&
|
||||
emitted < count; idx++) {
|
||||
if (ctx->nvIndices[idx].inUse &&
|
||||
ctx->nvIndices[idx].nvPublic.nvIndex >= property) {
|
||||
TPM2_Packet_AppendU32(rsp,
|
||||
ctx->nvIndices[idx].nvPublic.nvIndex);
|
||||
emitted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (handleClass == 0x02000000 ||
|
||||
handleClass == 0x03000000) {
|
||||
for (idx = 0; idx < FWTPM_MAX_SESSIONS &&
|
||||
emitted < count; idx++) {
|
||||
if (ctx->sessions[idx].used &&
|
||||
ctx->sessions[idx].handle >= property) {
|
||||
TPM2_Packet_AppendU32(rsp,
|
||||
ctx->sessions[idx].handle);
|
||||
emitted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If selection exhausted the table before reaching the requested
|
||||
* count, it already proved there is no next page. */
|
||||
if ((UINT32)emitted == propertyCount &&
|
||||
FwSelectCapabilityHandle(ctx, handleClass, property,
|
||||
previous, havePrevious, &selected)) {
|
||||
FwPatchMoreData(rsp, moreDataPos, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1161,6 +1161,165 @@ static void test_fwtpm_getcap_paging(void)
|
|||
fwtpm_pass("GetCapability paging convergence:", 0);
|
||||
}
|
||||
|
||||
#if FWTPM_MAX_OBJECTS >= 2 || FWTPM_MAX_PERSISTENT >= 2 || \
|
||||
FWTPM_MAX_SESSIONS >= 2 || \
|
||||
(!defined(FWTPM_NO_NV) && FWTPM_MAX_NV_INDICES >= 2)
|
||||
static UINT32 getcap_handle_page_ex(FWTPM_CTX* ctx, UINT32 property,
|
||||
UINT32 propertyCount, byte* moreData, UINT32* handleCount)
|
||||
{
|
||||
int rc, rspSize, cmdSz;
|
||||
|
||||
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0,
|
||||
TPM_CC_GetCapability);
|
||||
PutU32BE(gCmd + cmdSz, TPM_CAP_HANDLES); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, property); cmdSz += 4;
|
||||
PutU32BE(gCmd + cmdSz, propertyCount); cmdSz += 4;
|
||||
PutU32BE(gCmd + 2, (UINT32)cmdSz);
|
||||
rspSize = 0;
|
||||
rc = FWTPM_ProcessCommand(ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
|
||||
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
||||
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
|
||||
AssertTrue(rspSize >= TPM2_HEADER_SIZE + 9);
|
||||
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 1),
|
||||
(int)TPM_CAP_HANDLES);
|
||||
*moreData = gRsp[TPM2_HEADER_SIZE];
|
||||
*handleCount = GetU32BE(gRsp + TPM2_HEADER_SIZE + 5);
|
||||
AssertTrue(*handleCount <= propertyCount);
|
||||
if (*handleCount > 0U) {
|
||||
AssertTrue(rspSize >= TPM2_HEADER_SIZE + 13);
|
||||
return GetU32BE(gRsp + TPM2_HEADER_SIZE + 9);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UINT32 getcap_handle_page(FWTPM_CTX* ctx, UINT32 property,
|
||||
byte* moreData)
|
||||
{
|
||||
UINT32 handleCount;
|
||||
UINT32 handle;
|
||||
|
||||
handle = getcap_handle_page_ex(ctx, property, 1, moreData,
|
||||
&handleCount);
|
||||
AssertIntEQ(handleCount, 1);
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void check_handle_zero_count(FWTPM_CTX* ctx, UINT32 property)
|
||||
{
|
||||
UINT32 handleCount;
|
||||
byte moreData;
|
||||
|
||||
(void)getcap_handle_page_ex(ctx, property, 0, &moreData, &handleCount);
|
||||
AssertIntEQ(handleCount, 0);
|
||||
AssertIntEQ(moreData, 1);
|
||||
}
|
||||
|
||||
static void check_empty_handle_class(FWTPM_CTX* ctx, UINT32 property)
|
||||
{
|
||||
UINT32 handleCount;
|
||||
byte moreData;
|
||||
|
||||
(void)getcap_handle_page_ex(ctx, property, 1, &moreData, &handleCount);
|
||||
AssertIntEQ(handleCount, 0);
|
||||
AssertIntEQ(moreData, 0);
|
||||
}
|
||||
|
||||
static void check_handle_paging(FWTPM_CTX* ctx, UINT32 firstProperty,
|
||||
UINT32 lowHandle, UINT32 highHandle)
|
||||
{
|
||||
UINT32 handle;
|
||||
byte moreData;
|
||||
|
||||
handle = getcap_handle_page(ctx, firstProperty, &moreData);
|
||||
AssertIntEQ(handle, lowHandle);
|
||||
AssertIntEQ(moreData, 1);
|
||||
|
||||
handle = getcap_handle_page(ctx, handle + 1, &moreData);
|
||||
AssertIntEQ(handle, highHandle);
|
||||
AssertIntEQ(moreData, 0);
|
||||
}
|
||||
|
||||
/* Handle capability pages are numerically ordered independently of their
|
||||
* backing-slot order, so cursor-based enumeration cannot omit an entry. */
|
||||
static void test_fwtpm_getcap_handles_ordered(void)
|
||||
{
|
||||
FWTPM_CTX ctx;
|
||||
UINT32 lowHandle;
|
||||
UINT32 highHandle;
|
||||
#if FWTPM_MAX_SESSIONS >= 2
|
||||
byte moreData;
|
||||
#endif
|
||||
|
||||
(void)remove(FWTPM_NV_FILE);
|
||||
XMEMSET(&ctx, 0, sizeof(ctx));
|
||||
AssertIntEQ(fwtpm_test_startup(&ctx), 0);
|
||||
|
||||
/* PCR and permanent handles have no backing capability table. */
|
||||
check_empty_handle_class(&ctx, PCR_FIRST);
|
||||
check_empty_handle_class(&ctx, PERMANENT_FIRST);
|
||||
|
||||
#if FWTPM_MAX_OBJECTS >= 2
|
||||
lowHandle = TRANSIENT_FIRST + 0x10u;
|
||||
highHandle = TRANSIENT_FIRST + 0x20u;
|
||||
ctx.objects[0].used = 1;
|
||||
ctx.objects[0].handle = highHandle;
|
||||
ctx.objects[1].used = 1;
|
||||
ctx.objects[1].handle = lowHandle;
|
||||
check_handle_zero_count(&ctx, TRANSIENT_FIRST);
|
||||
check_handle_paging(&ctx, TRANSIENT_FIRST, lowHandle, highHandle);
|
||||
XMEMSET(ctx.objects, 0, sizeof(ctx.objects));
|
||||
#endif /* FWTPM_MAX_OBJECTS >= 2 */
|
||||
|
||||
#if FWTPM_MAX_PERSISTENT >= 2
|
||||
lowHandle = PERSISTENT_FIRST + 0x10u;
|
||||
highHandle = PERSISTENT_FIRST + 0x20u;
|
||||
ctx.persistent[0].used = 1;
|
||||
ctx.persistent[0].handle = highHandle;
|
||||
ctx.persistent[1].used = 1;
|
||||
ctx.persistent[1].handle = lowHandle;
|
||||
check_handle_zero_count(&ctx, PERSISTENT_FIRST);
|
||||
check_handle_paging(&ctx, PERSISTENT_FIRST, lowHandle, highHandle);
|
||||
XMEMSET(ctx.persistent, 0, sizeof(ctx.persistent));
|
||||
#endif /* FWTPM_MAX_PERSISTENT >= 2 */
|
||||
|
||||
#if !defined(FWTPM_NO_NV) && FWTPM_MAX_NV_INDICES >= 2
|
||||
lowHandle = NV_INDEX_FIRST + 0x10u;
|
||||
highHandle = NV_INDEX_FIRST + 0x20u;
|
||||
ctx.nvIndices[0].inUse = 1;
|
||||
ctx.nvIndices[0].nvPublic.nvIndex = highHandle;
|
||||
ctx.nvIndices[1].inUse = 1;
|
||||
ctx.nvIndices[1].nvPublic.nvIndex = lowHandle;
|
||||
check_handle_zero_count(&ctx, NV_INDEX_FIRST);
|
||||
check_handle_paging(&ctx, NV_INDEX_FIRST, lowHandle, highHandle);
|
||||
XMEMSET(ctx.nvIndices, 0, sizeof(ctx.nvIndices));
|
||||
#endif /* !FWTPM_NO_NV && FWTPM_MAX_NV_INDICES >= 2 */
|
||||
|
||||
#if FWTPM_MAX_SESSIONS >= 2
|
||||
/* A loaded-session query intentionally spans HMAC and policy sessions;
|
||||
* fwTPM reports each session's real, directly usable handle prefix. */
|
||||
lowHandle = HMAC_SESSION_FIRST + 0x10u;
|
||||
highHandle = POLICY_SESSION_FIRST + 0x20u;
|
||||
ctx.sessions[0].used = 1;
|
||||
ctx.sessions[0].handle = highHandle;
|
||||
ctx.sessions[1].used = 1;
|
||||
ctx.sessions[1].handle = lowHandle;
|
||||
check_handle_zero_count(&ctx, HMAC_SESSION_FIRST);
|
||||
check_handle_paging(&ctx, HMAC_SESSION_FIRST, lowHandle, highHandle);
|
||||
|
||||
/* A policy-session query must exclude the lower HMAC session and return
|
||||
* the policy handle without claiming another page. */
|
||||
highHandle = getcap_handle_page(&ctx, POLICY_SESSION_FIRST, &moreData);
|
||||
AssertIntEQ(highHandle, POLICY_SESSION_FIRST + 0x20u);
|
||||
AssertIntEQ(moreData, 0);
|
||||
XMEMSET(ctx.sessions, 0, sizeof(ctx.sessions));
|
||||
#endif /* FWTPM_MAX_SESSIONS >= 2 */
|
||||
|
||||
FWTPM_Cleanup(&ctx);
|
||||
(void)remove(FWTPM_NV_FILE);
|
||||
fwtpm_pass("GetCapability(HANDLES) ordered paging:", 0);
|
||||
}
|
||||
#endif /* handle capability test has at least two slots */
|
||||
|
||||
/* ================================================================== */
|
||||
/* Command-group gates (FWTPM_NO_* macros) */
|
||||
/* ================================================================== */
|
||||
|
|
@ -13328,6 +13487,11 @@ int fwtpm_unit_tests(int argc, char *argv[])
|
|||
test_fwtpm_getcap_properties();
|
||||
test_fwtpm_getcap_pcrs();
|
||||
test_fwtpm_getcap_paging();
|
||||
#if FWTPM_MAX_OBJECTS >= 2 || FWTPM_MAX_PERSISTENT >= 2 || \
|
||||
FWTPM_MAX_SESSIONS >= 2 || \
|
||||
(!defined(FWTPM_NO_NV) && FWTPM_MAX_NV_INDICES >= 2)
|
||||
test_fwtpm_getcap_handles_ordered();
|
||||
#endif /* handle capability test has at least two slots */
|
||||
test_fwtpm_total_commands();
|
||||
|
||||
/* Command-group gates (FWTPM_NO_* macros) */
|
||||
|
|
|
|||
Loading…
Reference in New Issue