/********************************************************************************/ /* */ /* Context Management */ /* Written by Ken Goldman */ /* IBM Thomas J. Watson Research Center */ /* $Id: ContextCommands.c 1658 2021-01-22 23:14:01Z kgoldman $ */ /* */ /* Licenses and Notices */ /* */ /* 1. Copyright Licenses: */ /* */ /* - Trusted Computing Group (TCG) grants to the user of the source code in */ /* this specification (the "Source Code") a worldwide, irrevocable, */ /* nonexclusive, royalty free, copyright license to reproduce, create */ /* derivative works, distribute, display and perform the Source Code and */ /* derivative works thereof, and to grant others the rights granted herein. */ /* */ /* - The TCG grants to the user of the other parts of the specification */ /* (other than the Source Code) the rights to reproduce, distribute, */ /* display, and perform the specification solely for the purpose of */ /* developing products based on such documents. */ /* */ /* 2. Source Code Distribution Conditions: */ /* */ /* - Redistributions of Source Code must retain the above copyright licenses, */ /* this list of conditions and the following disclaimers. */ /* */ /* - Redistributions in binary form must reproduce the above copyright */ /* licenses, this list of conditions and the following disclaimers in the */ /* documentation and/or other materials provided with the distribution. */ /* */ /* 3. Disclaimers: */ /* */ /* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */ /* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */ /* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */ /* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */ /* Contact TCG Administration (admin@trustedcomputinggroup.org) for */ /* information on specification licensing rights available through TCG */ /* membership agreements. */ /* */ /* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */ /* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */ /* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */ /* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */ /* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */ /* */ /* - Without limitation, TCG and its members and licensors disclaim all */ /* liability, including liability for infringement of any proprietary */ /* rights, relating to use of information in this specification and to the */ /* implementation of this specification, and TCG disclaims all liability for */ /* cost of procurement of substitute goods or services, lost profits, loss */ /* of use, loss of data or any incidental, consequential, direct, indirect, */ /* or special damages, whether under contract, tort, warranty or otherwise, */ /* arising in any way out of use or reliance upon this specification or any */ /* information herein. */ /* */ /* (c) Copyright IBM Corp. and others, 2016 - 2021 */ /* */ /********************************************************************************/ #include "Tpm.h" #include "ContextSave_fp.h" #include "NVMarshal.h" // libtpms added #if CC_ContextSave // Conditional expansion of this file #include "Context_spt_fp.h" /* Error Returns Meaning */ /* TPM_RC_CONTEXT_GAP a contextID could not be assigned for a session context save */ /* TPM_RC_TOO_MANY_CONTEXTS no more contexts can be saved as the counter has maxed out */ TPM_RC TPM2_ContextSave( ContextSave_In *in, // IN: input parameter list ContextSave_Out *out // OUT: output parameter list ) { TPM_RC result = TPM_RC_SUCCESS; UINT16 fingerprintSize; // The size of fingerprint in context // blob. UINT64 contextID = 0; // session context ID TPM2B_SYM_KEY symKey; TPM2B_IV iv; TPM2B_DIGEST integrity; UINT16 integritySize; BYTE *buffer; // This command may cause the orderlyState to be cleared due to // the update of state reset data. If the state is orderly and // cannot be changed, exit early. RETURN_IF_ORDERLY; // Internal Data Update // This implementation does not do things in quite the same way as described in // Part 2 of the specification. In Part 2, it indicates that the // TPMS_CONTEXT_DATA contains two TPM2B values. That is not how this is // implemented. Rather, the size field of the TPM2B_CONTEXT_DATA is used to // determine the amount of data in the encrypted data. That part is not // independently sized. This makes the actual size 2 bytes smaller than // calculated using Part 2. Since this is opaque to the caller, it is not // necessary to fix. The actual size is returned by TPM2_GetCapabilties(). // Initialize output handle. At the end of command action, the output // handle of an object will be replaced, while the output handle // for a session will be the same as input out->context.savedHandle = in->saveHandle; // Get the size of fingerprint in context blob. The sequence value in // TPMS_CONTEXT structure is used as the fingerprint fingerprintSize = sizeof(out->context.sequence); // Compute the integrity size at the beginning of context blob integritySize = sizeof(integrity.t.size) + CryptHashGetDigestSize(CONTEXT_INTEGRITY_HASH_ALG); // Perform object or session specific context save switch(HandleGetType(in->saveHandle)) { case TPM_HT_TRANSIENT: { OBJECT *object = HandleToObject(in->saveHandle); ANY_OBJECT_BUFFER *outObject; unsigned char buffer[sizeof(OBJECT) * 2]; // libtpms changed begin BYTE *bufptr = &buffer[0]; INT32 size = sizeof(buffer); UINT16 written = ANY_OBJECT_Marshal(object, &bufptr, &size); UINT16 objectSize = written; // libtpms changed end outObject = (ANY_OBJECT_BUFFER *)(out->context.contextBlob.t.buffer + integritySize + fingerprintSize); // Set size of the context data. The contents of context blob is vendor // defined. In this implementation, the size is size of integrity // plus fingerprint plus the whole internal OBJECT structure out->context.contextBlob.t.size = integritySize + fingerprintSize + objectSize; // Make sure things fit pAssert(out->context.contextBlob.t.size <= sizeof(out->context.contextBlob.t.buffer)); // Copy the whole internal OBJECT structure to context blob MemoryCopy(outObject, buffer, written); // libtpms changed // Increment object context ID gr.objectContextID++; // If object context ID overflows, TPM should be put in failure mode if(gr.objectContextID == 0) FAIL(FATAL_ERROR_INTERNAL); // Fill in other return values for an object. out->context.sequence = gr.objectContextID; // For regular object, savedHandle is 0x80000000. For sequence object, // savedHandle is 0x80000001. For object with stClear, savedHandle // is 0x80000002 if(ObjectIsSequence(object)) { out->context.savedHandle = 0x80000001; /* ANY_OBJECT_Marshal already wrote it // libtpms changed begin SequenceDataExport((HASH_OBJECT *)object, (HASH_OBJECT_BUFFER *)outObject); */ // libtpms changed end } else out->context.savedHandle = (object->attributes.stClear == SET) ? 0x80000002 : 0x80000000; // Get object hierarchy out->context.hierarchy = ObjectGetHierarchy(object); break; } case TPM_HT_HMAC_SESSION: case TPM_HT_POLICY_SESSION: { SESSION *session = SessionGet(in->saveHandle); // Set size of the context data. The contents of context blob is vendor // defined. In this implementation, the size of context blob is the // size of a internal session structure plus the size of // fingerprint plus the size of integrity out->context.contextBlob.t.size = integritySize + fingerprintSize + sizeof(*session); // Make sure things fit pAssert(out->context.contextBlob.t.size < sizeof(out->context.contextBlob.t.buffer)); // Copy the whole internal SESSION structure to context blob. // Save space for fingerprint at the beginning of the buffer // This is done before anything else so that the actual context // can be reclaimed after this call pAssert(sizeof(*session) <= sizeof(out->context.contextBlob.t.buffer) - integritySize - fingerprintSize); MemoryCopy(out->context.contextBlob.t.buffer + integritySize + fingerprintSize, session, sizeof(*session)); // Fill in the other return parameters for a session // Get a context ID and set the session tracking values appropriately // TPM_RC_CONTEXT_GAP is a possible error. // SessionContextSave() will flush the in-memory context // so no additional errors may occur after this call. result = SessionContextSave(out->context.savedHandle, &contextID); if(result != TPM_RC_SUCCESS) return result; // sequence number is the current session contextID out->context.sequence = contextID; // use TPM_RH_NULL as hierarchy for session context out->context.hierarchy = TPM_RH_NULL; break; } default: // SaveContext may only take an object handle or a session handle. // All the other handle type should be filtered out at unmarshal FAIL(FATAL_ERROR_INTERNAL); break; } // Save fingerprint at the beginning of encrypted area of context blob. // Reserve the integrity space pAssert(sizeof(out->context.sequence) <= sizeof(out->context.contextBlob.t.buffer) - integritySize); MemoryCopy(out->context.contextBlob.t.buffer + integritySize, &out->context.sequence, sizeof(out->context.sequence)); // Compute context encryption key ComputeContextProtectionKey(&out->context, &symKey, &iv); // Encrypt context blob CryptSymmetricEncrypt(out->context.contextBlob.t.buffer + integritySize, CONTEXT_ENCRYPT_ALG, CONTEXT_ENCRYPT_KEY_BITS, symKey.t.buffer, &iv, TPM_ALG_CFB, out->context.contextBlob.t.size - integritySize, out->context.contextBlob.t.buffer + integritySize); // Compute integrity hash for the object // In this implementation, the same routine is used for both sessions // and objects. ComputeContextIntegrity(&out->context, &integrity); // add integrity at the beginning of context blob buffer = out->context.contextBlob.t.buffer; TPM2B_DIGEST_Marshal(&integrity, &buffer, NULL); // orderly state should be cleared because of the update of state reset and // state clear data g_clearOrderly = TRUE; return result; } #endif // CC_ContextSave #include "Tpm.h" #include "ContextLoad_fp.h" #if CC_ContextLoad // Conditional expansion of this file #include "Context_spt_fp.h" TPM_RC TPM2_ContextLoad( ContextLoad_In *in, // IN: input parameter list ContextLoad_Out *out // OUT: output parameter list ) { TPM_RC result; TPM2B_DIGEST integrityToCompare; TPM2B_DIGEST integrity; BYTE *buffer; // defined to save some typing INT32 size; // defined to save some typing TPM_HT handleType; TPM2B_SYM_KEY symKey; TPM2B_IV iv; // Input Validation // See discussion about the context format in TPM2_ContextSave Detailed Actions // IF this is a session context, make sure that the sequence number is // consistent with the version in the slot // Check context blob size handleType = HandleGetType(in->context.savedHandle); // Get integrity from context blob buffer = in->context.contextBlob.t.buffer; size = (INT32)in->context.contextBlob.t.size; result = TPM2B_DIGEST_Unmarshal(&integrity, &buffer, &size); if(result != TPM_RC_SUCCESS) return result; // the size of the integrity value has to match the size of digest produced // by the integrity hash if(integrity.t.size != CryptHashGetDigestSize(CONTEXT_INTEGRITY_HASH_ALG)) return TPM_RCS_SIZE + RC_ContextLoad_context; // Make sure that the context blob has enough space for the fingerprint. This // is elastic pants to go with the belt and suspenders we already have to make // sure that the context is complete and untampered. if((unsigned)size < sizeof(in->context.sequence)) return TPM_RCS_SIZE + RC_ContextLoad_context; // After unmarshaling the integrity value, 'buffer' is pointing at the first // byte of the integrity protected and encrypted buffer and 'size' is the number // of integrity protected and encrypted bytes. // Compute context integrity ComputeContextIntegrity(&in->context, &integrityToCompare); // Compare integrity if(!MemoryEqual2B(&integrity.b, &integrityToCompare.b)) return TPM_RCS_INTEGRITY + RC_ContextLoad_context; // Compute context encryption key ComputeContextProtectionKey(&in->context, &symKey, &iv); // Decrypt context data in place CryptSymmetricDecrypt(buffer, CONTEXT_ENCRYPT_ALG, CONTEXT_ENCRYPT_KEY_BITS, symKey.t.buffer, &iv, TPM_ALG_CFB, size, buffer); // See if the fingerprint value matches. If not, it is symptomatic of either // a broken TPM or that the TPM is under attack so go into failure mode. if(!MemoryEqual(buffer, &in->context.sequence, sizeof(in->context.sequence))) FAIL(FATAL_ERROR_INTERNAL); // step over fingerprint buffer += sizeof(in->context.sequence); // set the remaining size of the context size -= sizeof(in->context.sequence); // Perform object or session specific input check switch(handleType) { case TPM_HT_TRANSIENT: { OBJECT *outObject; if(size > (INT32)sizeof(OBJECT)) FAIL(FATAL_ERROR_INTERNAL); // Discard any changes to the handle that the TRM might have made in->context.savedHandle = TRANSIENT_FIRST; // If hierarchy is disabled, no object context can be loaded in this // hierarchy if(!HierarchyIsEnabled(in->context.hierarchy)) return TPM_RCS_HIERARCHY + RC_ContextLoad_context; // Restore object. If there is no empty space, indicate as much outObject = ObjectContextLoadLibtpms(buffer, size, // libtpms changed &out->loadedHandle); if(outObject == NULL) return TPM_RC_OBJECT_MEMORY; break; } case TPM_HT_POLICY_SESSION: case TPM_HT_HMAC_SESSION: { if(size != sizeof(SESSION)) FAIL(FATAL_ERROR_INTERNAL); // This command may cause the orderlyState to be cleared due to // the update of state reset data. If this is the case, check if NV is // available first RETURN_IF_ORDERLY; // Check if input handle points to a valid saved session and that the // sequence number makes sense if(!SequenceNumberForSavedContextIsValid(&in->context)) return TPM_RCS_HANDLE + RC_ContextLoad_context; // Restore session. A TPM_RC_SESSION_MEMORY, TPM_RC_CONTEXT_GAP error // may be returned at this point result = SessionContextLoad((SESSION_BUF *)buffer, &in->context.savedHandle); if(result != TPM_RC_SUCCESS) return result; out->loadedHandle = in->context.savedHandle; // orderly state should be cleared because of the update of state // reset and state clear data g_clearOrderly = TRUE; break; } default: // Context blob may only have an object handle or a session handle. // All the other handle type should be filtered out at unmarshal FAIL(FATAL_ERROR_INTERNAL); break; } return TPM_RC_SUCCESS; } #endif // CC_ContextLoad #include "Tpm.h" #include "FlushContext_fp.h" #if CC_FlushContext // Conditional expansion of this file TPM_RC TPM2_FlushContext( FlushContext_In *in // IN: input parameter list ) { // Internal Data Update // Call object or session specific routine to flush switch(HandleGetType(in->flushHandle)) { case TPM_HT_TRANSIENT: if(!IsObjectPresent(in->flushHandle)) return TPM_RCS_HANDLE + RC_FlushContext_flushHandle; // Flush object FlushObject(in->flushHandle); break; case TPM_HT_HMAC_SESSION: case TPM_HT_POLICY_SESSION: if(!SessionIsLoaded(in->flushHandle) && !SessionIsSaved(in->flushHandle) ) return TPM_RCS_HANDLE + RC_FlushContext_flushHandle; // If the session to be flushed is the exclusive audit session, then // indicate that there is no exclusive audit session any longer. if(in->flushHandle == g_exclusiveAuditSession) g_exclusiveAuditSession = TPM_RH_UNASSIGNED; // Flush session SessionFlush(in->flushHandle); break; default: // This command only takes object or session handle. Other handles // should be filtered out at handle unmarshal FAIL(FATAL_ERROR_INTERNAL); break; } return TPM_RC_SUCCESS; } #endif // CC_FlushContext #include "Tpm.h" #include "EvictControl_fp.h" #if CC_EvictControl // Conditional expansion of this file TPM_RC TPM2_EvictControl( EvictControl_In *in // IN: input parameter list ) { TPM_RC result; OBJECT *evictObject; // Input Validation // Get internal object pointer evictObject = HandleToObject(in->objectHandle); // Temporary, stClear or public only objects can not be made persistent if(evictObject->attributes.temporary == SET || evictObject->attributes.stClear == SET || evictObject->attributes.publicOnly == SET) return TPM_RCS_ATTRIBUTES + RC_EvictControl_objectHandle; // If objectHandle refers to a persistent object, it should be the same as // input persistentHandle if(evictObject->attributes.evict == SET && evictObject->evictHandle != in->persistentHandle) return TPM_RCS_HANDLE + RC_EvictControl_objectHandle; // Additional authorization validation if(in->auth == TPM_RH_PLATFORM) { // To make persistent if(evictObject->attributes.evict == CLEAR) { // PlatformAuth can not set evict object in storage or endorsement // hierarchy if(evictObject->attributes.ppsHierarchy == CLEAR) return TPM_RCS_HIERARCHY + RC_EvictControl_objectHandle; // Platform cannot use a handle outside of platform persistent range. if(!NvIsPlatformPersistentHandle(in->persistentHandle)) return TPM_RCS_RANGE + RC_EvictControl_persistentHandle; } // PlatformAuth can delete any persistent object } else if(in->auth == TPM_RH_OWNER) { // OwnerAuth can not set or clear evict object in platform hierarchy if(evictObject->attributes.ppsHierarchy == SET) return TPM_RCS_HIERARCHY + RC_EvictControl_objectHandle; // Owner cannot use a handle outside of owner persistent range. if(evictObject->attributes.evict == CLEAR && !NvIsOwnerPersistentHandle(in->persistentHandle)) return TPM_RCS_RANGE + RC_EvictControl_persistentHandle; } else { // Other authorization is not allowed in this command and should have been // filtered out in unmarshal process FAIL(FATAL_ERROR_INTERNAL); } // Internal Data Update // Change evict state if(evictObject->attributes.evict == CLEAR) { // Make object persistent if(NvFindHandle(in->persistentHandle) != 0) return TPM_RC_NV_DEFINED; // A TPM_RC_NV_HANDLE or TPM_RC_NV_SPACE error may be returned at this // point result = NvAddEvictObject(in->persistentHandle, evictObject); } else { // Delete the persistent object in NV result = NvDeleteEvict(evictObject->evictHandle); } return result; } #endif // CC_EvictControl