tpm2: Switch to UINT16 for CONTEXT_SLOT and 64k context gap

This patch addresses issue #209.

The context gap for libtpms is currently only 0xff due to the CONTEXT_SLOT
being a UINT8. To extend this to 0xffff, we need to define the CONTEXT_SLOT
as UINT16 and introduce a global variable s_ContextArrayMask that takes on
two valid values, 0xff for simulating the CONTEXT_SLOT when it was UINT8
and 0xffff for usage with the new CONTEXT_SLOT of type UINT16. All
occurrences of casts to CONTEXT_SLOT are replaced with a macro
CONTEXT_SLOT_MASKED that applies this mask to a value instead of using the
cast. We also use it for some calculations to avoid spilling over from
1 byte into 2 bytes for example. The cast with the new code is the same as
applying the mask 0xffff, and using the 0xff mask we can simulate the old
CONTEXT_SLOT (1 byte), which we need for seamlessly resuming old state. We
switch from the 0xff mask to the 0xffff mask when the TPM is reset.

There's one place where the s_ContextArrayMask is initialized to 0xff, and
this is when we resume 'old' STATE_RESET_DATA. The places where it is
intialized to 0xffff are in TPM_Manufacture() and
TPM_SessionStartup(SU_CLEAR), both of which are not called after resuming
state.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
This commit is contained in:
Stefan Berger 2021-04-30 16:44:03 -04:00 committed by Stefan Berger
parent 31925c8e33
commit db1fd5948b
7 changed files with 89 additions and 22 deletions

View File

@ -1,5 +1,11 @@
CHANGES - changes for libtpms
version 0.9.0:
- NOTE: Downgrade to previous versions is not possible. See below.
- The size of the context gap has been adjusted to 0xffff from 0xff.
As a consequence of this the volatile state's format (STATE_RESET_DATA)
has changed and cannot be downgraded.
version 0.8.0
- NOTE: Downgrade to previous versions is not possible. See below.
- Update to TPM 2 code release 159

View File

@ -996,6 +996,28 @@ typedef struct state_reset_data
} STATE_RESET_DATA;
EXTERN STATE_RESET_DATA gr;
// libtpms added begin
/* The s_ContextSlotMask masks CONTEXT_SLOT values; this variable can have
* only two valid values, 0xff or 0xffff. The former is used to simulate
* a CONTEXT_SLOT defined as UINT8, the latter is used for the CONTEXT_SLOT
* when it is a UINT16. The original TPM 2 code uses a cast to CONTEXT_SLOT
* to truncate larger values and has been modified to use CONTEXT_SLOT_MASKED
* to achieve the same effect with the above two values.
*
* Using CONTEXT_SLOT_MASKED we make sure that when we write values into
* gr.contextArray that these values are properly masked/truncated so that
* when we read values from gr.contextArray that we don't have to mask
* them again.
*
* s_ContextSlotMask may only be initialized to 0xff when resuming an older
* state from the time when CONTEXT_SLOT was UINT8, otherwise it must be set
* to 0xffff. We set it to 0xffff in SessionStartup(SU_CLEAR) and to be
* able to save the TPM state really early (and restore it) also in
* TPM_Manufacture().
*/
EXTERN CONTEXT_SLOT s_ContextSlotMask;
#define CONTEXT_SLOT_MASKED(val) ((CONTEXT_SLOT)(val) & s_ContextSlotMask) // libtpms added end
/* 5.9.12 NV Layout */
/* The NV data organization is */
/* a) a PERSISTENT_DATA structure */

View File

@ -86,6 +86,9 @@ TPM_Manufacture(
)
{
TPM_SU orderlyShutdown;
// Initialize the context slot mask for UINT16
s_ContextSlotMask = 0xffff; // libtpms added
#if RUNTIME_SIZE_CHECKS
// Call the function to verify the sizes of values that result from different
// compile options.

View File

@ -1262,7 +1262,7 @@ skip_future_versions:
}
#define STATE_RESET_DATA_MAGIC 0x01102332
#define STATE_RESET_DATA_VERSION 3
#define STATE_RESET_DATA_VERSION 4
static TPM_RC
STATE_RESET_DATA_Unmarshal(STATE_RESET_DATA *data, BYTE **buffer, INT32 *size)
@ -1295,15 +1295,38 @@ STATE_RESET_DATA_Unmarshal(STATE_RESET_DATA *data, BYTE **buffer, INT32 *size)
rc = UINT16_Unmarshal(&array_size, buffer, size);
}
if (rc == TPM_RC_SUCCESS &&
array_size != sizeof(data->contextArray)) {
array_size != ARRAY_SIZE(data->contextArray)) {
TPMLIB_LogTPM2Error("STATE_RESET_DATA: Bad array size for contextArray; "
"expected %zu, got %u\n",
sizeof(data->contextArray), array_size);
ARRAY_SIZE(data->contextArray), array_size);
rc = TPM_RC_BAD_PARAMETER;
}
if (rc == TPM_RC_SUCCESS) {
rc = Array_Unmarshal((BYTE *)&data->contextArray, array_size,
buffer, size);
size_t i;
if (hdr.version <= 3) {
/* version <= 3 was writing an array of UINT8 */
UINT8 element;
for (i = 0; i < array_size && rc == TPM_RC_SUCCESS; i++) {
rc = UINT8_Unmarshal(&element, buffer, size);
data->contextArray[i] = element;
}
s_ContextSlotMask = 0xff;
} else {
/* version 4 and later an array of UINT16 */
for (i = 0; i < array_size && rc == TPM_RC_SUCCESS; i++) {
rc = UINT16_Unmarshal(&data->contextArray[i], buffer, size);
}
if (rc == TPM_RC_SUCCESS) {
rc = UINT16_Unmarshal(&s_ContextSlotMask, buffer, size);
}
if (rc == TPM_RC_SUCCESS) {
if (s_ContextSlotMask != 0xffff && s_ContextSlotMask != 0x00ff) {
TPMLIB_LogTPM2Error("STATE_RESET_DATA: s_ContextSlotMask has bad value: 0x%04x\n",
s_ContextSlotMask);
rc = TPM_RC_BAD_PARAMETER;
}
}
}
}
if (rc == TPM_RC_SUCCESS) {
rc = UINT64_Unmarshal(&data->contextCounter, buffer, size);
@ -1385,19 +1408,21 @@ STATE_RESET_DATA_Marshal(STATE_RESET_DATA *data, BYTE **buffer, INT32 *size)
BOOL has_block;
UINT16 array_size;
BLOCK_SKIP_INIT;
size_t i;
written = NV_HEADER_Marshal(buffer, size,
STATE_RESET_DATA_VERSION,
STATE_RESET_DATA_MAGIC, 3);
STATE_RESET_DATA_MAGIC, 4);
written += TPM2B_PROOF_Marshal(&data->nullProof, buffer, size);
written += TPM2B_Marshal(&data->nullSeed.b, buffer, size);
written += UINT32_Marshal(&data->clearCount, buffer, size);
written += UINT64_Marshal(&data->objectContextID, buffer, size);
array_size = sizeof(data->contextArray);
array_size = ARRAY_SIZE(data->contextArray);
written += UINT16_Marshal(&array_size, buffer, size);
written += Array_Marshal((BYTE *)&data->contextArray, array_size,
buffer, size);
for (i = 0; i < array_size; i++)
written += UINT16_Marshal(&data->contextArray[i], buffer, size);
written += UINT16_Marshal(&s_ContextSlotMask, buffer, size);
written += UINT64_Marshal(&data->contextCounter, buffer, size);
written += TPM2B_DIGEST_Marshal(&data->commandAuditDigest,

View File

@ -185,7 +185,10 @@ TPMPropertyIsDefined(
case TPM_PT_CONTEXT_GAP_MAX:
// maximum allowed difference (unsigned) between the contextID
// values of two saved session contexts
#if 0
*value = ((UINT32)1 << (sizeof(CONTEXT_SLOT) * 8)) - 1;
#endif
*value = s_ContextSlotMask; // libtpms added; the mask is either 0xff (old state) or 0xffff
break;
case TPM_PT_NV_COUNTERS_MAX:
// maximum number of NV indexes that are allowed to have the

View File

@ -73,11 +73,12 @@ ContextIdSetOldest(
{
CONTEXT_SLOT lowBits;
CONTEXT_SLOT entry;
CONTEXT_SLOT smallest = ((CONTEXT_SLOT)~0);
CONTEXT_SLOT smallest = CONTEXT_SLOT_MASKED(~0); // libtpms changed
UINT32 i;
pAssert(s_ContextSlotMask == 0xff || s_ContextSlotMask == 0xffff); // libtpms added
// Set oldestSaveContext to a value indicating none assigned
s_oldestSavedSession = MAX_ACTIVE_SESSIONS + 1;
lowBits = (CONTEXT_SLOT)gr.contextCounter;
lowBits = CONTEXT_SLOT_MASKED(gr.contextCounter); // libtpms changed
for(i = 0; i < MAX_ACTIVE_SESSIONS; i++)
{
entry = gr.contextArray[i];
@ -87,9 +88,9 @@ ContextIdSetOldest(
// Use a less than or equal in case the oldest
// is brand new (= lowBits-1) and equal to our initial
// value for smallest.
if(((CONTEXT_SLOT)(entry - lowBits)) <= smallest)
if(CONTEXT_SLOT_MASKED(entry - lowBits) <= smallest) // libtpms changed
{
smallest = (entry - lowBits);
smallest = CONTEXT_SLOT_MASKED(entry - lowBits); // libtpms changed
s_oldestSavedSession = i;
}
}
@ -140,6 +141,9 @@ SessionStartup(
gr.contextCounter = MAX_LOADED_SESSIONS + 1;
// Initialize oldest saved session
s_oldestSavedSession = MAX_ACTIVE_SESSIONS + 1;
// Initialize the context slot mask for UINT16
s_ContextSlotMask = 0xffff; // libtpms added
}
return TRUE;
}
@ -199,14 +203,16 @@ SequenceNumberForSavedContextIsValid(
// structure to be validated
)
{
#define MAX_CONTEXT_GAP ((UINT64)((CONTEXT_SLOT) ~0) + 1)
#define MAX_CONTEXT_GAP ((UINT64)(CONTEXT_SLOT_MASKED(~0) + 1)) /* libtpms changed */
pAssert(s_ContextSlotMask == 0xff || s_ContextSlotMask == 0xffff); // libtpms added
TPM_HANDLE handle = context->savedHandle & HR_HANDLE_MASK;
if(// Handle must be with the range of active sessions
handle >= MAX_ACTIVE_SESSIONS
// the array entry must be for a saved context
|| gr.contextArray[handle] <= MAX_LOADED_SESSIONS
// the array entry must agree with the sequence number
|| gr.contextArray[handle] != (CONTEXT_SLOT)context->sequence
|| gr.contextArray[handle] != CONTEXT_SLOT_MASKED(context->sequence) // libtpms changed
// the provided sequence number has to be less than the current counter
|| context->sequence > gr.contextCounter
// but not so much that it could not be a valid sequence number
@ -287,7 +293,7 @@ ContextIdSessionCreate(
// saved context. If the value to be assigned would make the same as an
// existing context, then we can't use it because of the ambiguity it would
// create.
if((CONTEXT_SLOT)gr.contextCounter
if(CONTEXT_SLOT_MASKED(gr.contextCounter) // libtpms changed
== gr.contextArray[s_oldestSavedSession])
return TPM_RC_CONTEXT_GAP;
}
@ -298,7 +304,7 @@ ContextIdSessionCreate(
{
// indicate that the session associated with this handle
// references a loaded session
gr.contextArray[*handle] = (CONTEXT_SLOT)(sessionIndex + 1);
gr.contextArray[*handle] = CONTEXT_SLOT_MASKED(sessionIndex + 1); // libtpms changed
return TPM_RC_SUCCESS;
}
}
@ -446,12 +452,13 @@ SessionContextSave(
UINT32 contextIndex;
CONTEXT_SLOT slotIndex;
pAssert(SessionIsLoaded(handle));
pAssert(s_ContextSlotMask == 0xff || s_ContextSlotMask == 0xffff); // libtpms added
// check to see if the gap is already maxed out
// Need to have a saved session
if(s_oldestSavedSession < MAX_ACTIVE_SESSIONS
// if the oldest saved session has the same value as the low bits
// of the contextCounter, then the GAP is maxed out.
&& gr.contextArray[s_oldestSavedSession] == (CONTEXT_SLOT)gr.contextCounter)
&& gr.contextArray[s_oldestSavedSession] == CONTEXT_SLOT_MASKED(gr.contextCounter)) // libtpms changed
return TPM_RC_CONTEXT_GAP;
// if the caller wants the context counter, set it
if(contextID != NULL)
@ -463,7 +470,7 @@ SessionContextSave(
// contextID value.
slotIndex = gr.contextArray[contextIndex] - 1;
// Set the contextID for the contextArray
gr.contextArray[contextIndex] = (CONTEXT_SLOT)gr.contextCounter;
gr.contextArray[contextIndex] = CONTEXT_SLOT_MASKED(gr.contextCounter); // libtpms changed
// Increment the counter
gr.contextCounter++;
// In the unlikely event that the 64-bit context counter rolls over...
@ -476,7 +483,7 @@ SessionContextSave(
}
// if the low-order bits wrapped, need to advance the value to skip over
// the values used to indicate that a session is loaded
if(((CONTEXT_SLOT)gr.contextCounter) == 0)
if(CONTEXT_SLOT_MASKED(gr.contextCounter) == 0) // libtpms changed
gr.contextCounter += MAX_LOADED_SESSIONS + 1;
// If no other sessions are saved, this is now the oldest.
if(s_oldestSavedSession >= MAX_ACTIVE_SESSIONS)
@ -504,6 +511,7 @@ SessionContextLoad(
{
UINT32 contextIndex;
CONTEXT_SLOT slotIndex;
pAssert(s_ContextSlotMask == 0xff || s_ContextSlotMask == 0xffff); // libtpms added
pAssert(HandleGetType(*handle) == TPM_HT_POLICY_SESSION
|| HandleGetType(*handle) == TPM_HT_HMAC_SESSION);
// Don't bother looking if no openings
@ -525,7 +533,7 @@ SessionContextLoad(
// context that we can safely load is the oldest one.
if(s_oldestSavedSession < MAX_ACTIVE_SESSIONS
&& s_freeSessionSlots == 1
&& (CONTEXT_SLOT)gr.contextCounter == gr.contextArray[s_oldestSavedSession]
&& CONTEXT_SLOT_MASKED(gr.contextCounter) == gr.contextArray[s_oldestSavedSession] // libtpms changed
&& contextIndex != s_oldestSavedSession)
return TPM_RC_CONTEXT_GAP;
pAssert(contextIndex < MAX_ACTIVE_SESSIONS);

View File

@ -219,7 +219,7 @@
#define MAX_ACTIVE_SESSIONS 64
#endif
#ifndef CONTEXT_SLOT
#define CONTEXT_SLOT UINT8 /* libtpms: use 'old' type */
#define CONTEXT_SLOT UINT16 /* libtpms: changed from UINT8 in v0.9.0 */
#endif
#ifndef MAX_LOADED_SESSIONS
#define MAX_LOADED_SESSIONS 3