From db1fd5948bcad810344ea1b2bda1eaefacba3547 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Fri, 30 Apr 2021 16:44:03 -0400 Subject: [PATCH] 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 --- CHANGES | 6 ++++++ src/tpm2/Global.h | 22 +++++++++++++++++++++ src/tpm2/Manufacture.c | 3 +++ src/tpm2/NVMarshal.c | 43 +++++++++++++++++++++++++++++++++--------- src/tpm2/PropertyCap.c | 3 +++ src/tpm2/Session.c | 32 +++++++++++++++++++------------ src/tpm2/TpmProfile.h | 2 +- 7 files changed, 89 insertions(+), 22 deletions(-) diff --git a/CHANGES b/CHANGES index 5403b425..3247f26b 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/src/tpm2/Global.h b/src/tpm2/Global.h index 007b0a02..84a0d13e 100644 --- a/src/tpm2/Global.h +++ b/src/tpm2/Global.h @@ -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 */ diff --git a/src/tpm2/Manufacture.c b/src/tpm2/Manufacture.c index 19b480d9..032bc763 100644 --- a/src/tpm2/Manufacture.c +++ b/src/tpm2/Manufacture.c @@ -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. diff --git a/src/tpm2/NVMarshal.c b/src/tpm2/NVMarshal.c index a0299de1..12be70a4 100644 --- a/src/tpm2/NVMarshal.c +++ b/src/tpm2/NVMarshal.c @@ -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, diff --git a/src/tpm2/PropertyCap.c b/src/tpm2/PropertyCap.c index 5c933187..db7d679c 100644 --- a/src/tpm2/PropertyCap.c +++ b/src/tpm2/PropertyCap.c @@ -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 diff --git a/src/tpm2/Session.c b/src/tpm2/Session.c index b36ac234..991a1460 100644 --- a/src/tpm2/Session.c +++ b/src/tpm2/Session.c @@ -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); diff --git a/src/tpm2/TpmProfile.h b/src/tpm2/TpmProfile.h index e406bceb..e5764c9d 100644 --- a/src/tpm2/TpmProfile.h +++ b/src/tpm2/TpmProfile.h @@ -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