// This file was extracted from the TCG Published // Trusted Platform Module Library // Part 4: Supporting Routines // Family "2.0" // Level 00 Revision 01.16 // October 30, 2014 #define OBJECT_C #include "InternalRoutines.h" #include "Platform.h" // // // Functions // // ObjectStartup() // // This function is called at TPM2_Startup() to initialize the object subsystem. // void ObjectStartup( void ) { UINT32 i; // object slots initialization for(i = 0; i < MAX_LOADED_OBJECTS; i++) { //Set the slot to not occupied s_objects[i].occupied = FALSE; } return; } // // // ObjectCleanupEvict() // // In this implementation, a persistent object is moved from NV into an object slot for processing. It is // flushed after command execution. This function is called from ExecuteCommand(). // void ObjectCleanupEvict( void ) { UINT32 i; // This has to be iterated because a command may have two handles // and they may both be persistent. // This could be made to be more efficient so that a search is not needed. for(i = 0; i < MAX_LOADED_OBJECTS; i++) { // If an object is a temporary evict object, flush it from slot if(s_objects[i].object.entity.attributes.evict == SET) s_objects[i].occupied = FALSE; } return; } // // // ObjectIsPresent() // // This function checks to see if a transient handle references a loaded object. This routine should not be // called if the handle is not a transient handle. The function validates that the handle is in the // implementation-dependent allowed in range for loaded transient objects. // // Return Value Meaning // // TRUE if the handle references a loaded object // FALSE if the handle is not an object handle, or it does not reference to a // loaded object // BOOL ObjectIsPresent( TPMI_DH_OBJECT handle // IN: handle to be checked ) { UINT32 slotIndex; // index of object slot pAssert(HandleGetType(handle) == TPM_HT_TRANSIENT); // The index in the loaded object array is found by subtracting the first // object handle number from the input handle number. If the indicated // slot is occupied, then indicate that there is already is a loaded // object associated with the handle. slotIndex = handle - TRANSIENT_FIRST; if(slotIndex >= MAX_LOADED_OBJECTS) return FALSE; return s_objects[slotIndex].occupied; } // // // ObjectIsSequence() // // This function is used to check if the object is a sequence object. This function should not be called if the // handle does not reference a loaded object. // // Return Value Meaning // // TRUE object is an HMAC, hash, or event sequence object // FALSE object is not an HMAC, hash, or event sequence object // BOOL ObjectIsSequence( OBJECT *object // IN: handle to be checked ) { pAssert (object != NULL); if( object->attributes.hmacSeq == SET || object->attributes.hashSeq == SET || object->attributes.eventSeq == SET) return TRUE; else return FALSE; } // // // ObjectGet() // // This function is used to find the object structure associated with a handle. // This function requires that handle references a loaded object. // OBJECT* ObjectGet( TPMI_DH_OBJECT handle // IN: handle of the object ) { pAssert( handle >= TRANSIENT_FIRST && handle - TRANSIENT_FIRST < MAX_LOADED_OBJECTS); pAssert(s_objects[handle - TRANSIENT_FIRST].occupied == TRUE); // In this implementation, the handle is determined by the slot occupied by the // object. return &s_objects[handle - TRANSIENT_FIRST].object.entity; } // // // ObjectGetName() // // This function is used to access the Name of the object. In this implementation, the Name is computed // when the object is loaded and is saved in the internal representation of the object. This function copies // the Name data from the object into the buffer at name and returns the number of octets copied. // This function requires that handle references a loaded object. // UINT16 ObjectGetName( TPMI_DH_OBJECT handle, // IN: handle of the object NAME *name // OUT: name of the object ) { OBJECT *object = ObjectGet(handle); if(object->publicArea.nameAlg == TPM_ALG_NULL) return 0; // Copy the Name data to the output MemoryCopy(name, object->name.t.name, object->name.t.size, sizeof(NAME)); return object->name.t.size; } // // // ObjectGetNameAlg() // // This function is used to get the Name algorithm of a object. // This function requires that handle references a loaded object. // TPMI_ALG_HASH ObjectGetNameAlg( TPMI_DH_OBJECT handle // IN: handle of the object ) { OBJECT *object = ObjectGet(handle); return object->publicArea.nameAlg; } // // // // ObjectGetQualifiedName() // // This function returns the Qualified Name of the object. In this implementation, the Qualified Name is // computed when the object is loaded and is saved in the internal representation of the object. The // alternative would be to retain the Name of the parent and compute the QN when needed. This would take // the same amount of space so it is not recommended that the alternate be used. // This function requires that handle references a loaded object. // void ObjectGetQualifiedName( TPMI_DH_OBJECT handle, // IN: handle of the object TPM2B_NAME *qualifiedName // OUT: qualified name of the object ) { OBJECT *object = ObjectGet(handle); if(object->publicArea.nameAlg == TPM_ALG_NULL) qualifiedName->t.size = 0; else // Copy the name *qualifiedName = object->qualifiedName; return; } // // // ObjectDataGetHierarchy() // // This function returns the handle for the hierarchy of an object. // TPMI_RH_HIERARCHY ObjectDataGetHierarchy( OBJECT *object // IN :object ) { if(object->attributes.spsHierarchy) { return TPM_RH_OWNER; } else if(object->attributes.epsHierarchy) { return TPM_RH_ENDORSEMENT; } else if(object->attributes.ppsHierarchy) { return TPM_RH_PLATFORM; } else { return TPM_RH_NULL; } } // // // ObjectGetHierarchy() // // This function returns the handle of the hierarchy to which a handle belongs. This function is similar to // ObjectDataGetHierarchy() but this routine takes a handle but ObjectDataGetHierarchy() takes an pointer // to an object. // This function requires that handle references a loaded object. // TPMI_RH_HIERARCHY ObjectGetHierarchy( TPMI_DH_OBJECT handle // IN :object handle ) { OBJECT *object = ObjectGet(handle); return ObjectDataGetHierarchy(object); } // // // ObjectAllocateSlot() // // This function is used to allocate a slot in internal object array. // // Return Value Meaning // // TRUE allocate success // FALSE do not have free slot // static BOOL ObjectAllocateSlot( TPMI_DH_OBJECT *handle, // OUT: handle of allocated object OBJECT **object // OUT: points to the allocated object ) { UINT32 i; // find an unoccupied handle slot for(i = 0; i < MAX_LOADED_OBJECTS; i++) { if(!s_objects[i].occupied) // If found a free slot { // Mark the slot as occupied s_objects[i].occupied = TRUE; break; } } // If we reach the end of object slot without finding a free one, return // error. if(i == MAX_LOADED_OBJECTS) return FALSE; *handle = i + TRANSIENT_FIRST; *object = &s_objects[i].object.entity; // Initialize the container. MemorySet(*object, 0, sizeof(**object)); return TRUE; } // // // ObjectLoad() // // This function loads an object into an internal object structure. If an error is returned, the internal state is // unchanged. // // // // // Error Returns Meaning // // TPM_RC_BINDING if the public and sensitive parts of the object are not matched // TPM_RC_KEY if the parameters in the public area of the object are not consistent // TPM_RC_OBJECT_MEMORY if there is no free slot for an object // TPM_RC_TYPE the public and private parts are not the same type // TPM_RC ObjectLoad( TPMI_RH_HIERARCHY hierarchy, // IN: hierarchy to which the object belongs TPMT_PUBLIC *publicArea, // IN: public area TPMT_SENSITIVE *sensitive, // IN: sensitive area (may be null) TPM2B_NAME *name, // IN: object's name (may be null) TPM_HANDLE parentHandle, // IN: handle of parent BOOL skipChecks, // IN: flag to indicate if it is OK to skip // consistency checks. TPMI_DH_OBJECT *handle // OUT: object handle ) { OBJECT *object = NULL; OBJECT *parent = NULL; TPM_RC result = TPM_RC_SUCCESS; TPM2B_NAME parentQN; // Parent qualified name // Try to allocate a slot for new object if(!ObjectAllocateSlot(handle, &object)) return TPM_RC_OBJECT_MEMORY; // Initialize public object->publicArea = *publicArea; if(sensitive != NULL) object->sensitive = *sensitive; // Are the consistency checks needed if(!skipChecks) { // Check if key size matches if(!CryptObjectIsPublicConsistent(&object->publicArea)) { result = TPM_RC_KEY; goto ErrorExit; } if(sensitive != NULL) { // Check if public type matches sensitive type result = CryptObjectPublicPrivateMatch(object); if(result != TPM_RC_SUCCESS) goto ErrorExit; } } object->attributes.publicOnly = (sensitive == NULL); // If 'name' is NULL, then there is nothing left to do for this // object as it has no qualified name and it is not a member of any // hierarchy and it is temporary if(name == NULL || name->t.size == 0) { object->qualifiedName.t.size = 0; object->name.t.size = 0; object->attributes.temporary = SET; return TPM_RC_SUCCESS; } // If parent handle is a permanent handle, it is a primary or temporary // object if(HandleGetType(parentHandle) == TPM_HT_PERMANENT) { // initialize QN parentQN.t.size = 4; // for a primary key, parent qualified name is the handle of hierarchy UINT32_TO_BYTE_ARRAY(parentHandle, parentQN.t.name); } else { // Get hierarchy and qualified name of parent ObjectGetQualifiedName(parentHandle, &parentQN); // Check for stClear object parent = ObjectGet(parentHandle); if( publicArea->objectAttributes.stClear == SET || parent->attributes.stClear == SET) object->attributes.stClear = SET; } object->name = *name; // Compute object qualified name ObjectComputeQualifiedName(&parentQN, publicArea->nameAlg, name, &object->qualifiedName); // Any object in TPM_RH_NULL hierarchy is temporary if(hierarchy == TPM_RH_NULL) { object->attributes.temporary = SET; } else if(parentQN.t.size == sizeof(TPM_HANDLE)) { // Otherwise, if the size of parent's qualified name is the size of a // handle, this object is a primary object object->attributes.primary = SET; } switch(hierarchy) { case TPM_RH_PLATFORM: object->attributes.ppsHierarchy = SET; break; case TPM_RH_OWNER: object->attributes.spsHierarchy = SET; break; case TPM_RH_ENDORSEMENT: object->attributes.epsHierarchy = SET; break; case TPM_RH_NULL: break; default: pAssert(FALSE); break; } return TPM_RC_SUCCESS; ErrorExit: ObjectFlush(*handle); return result; } // // // // AllocateSequenceSlot() // // This function allocates a sequence slot and initializes the parts that are used by the normal objects so // that a sequence object is not inadvertently used for an operation that is not appropriate for a sequence. // static BOOL AllocateSequenceSlot( TPM_HANDLE *newHandle, // OUT: receives the allocated handle HASH_OBJECT **object, // OUT: receives pointer to allocated object TPM2B_AUTH *auth // IN: the authValue for the slot ) { OBJECT *objectHash; // the hash as an object if(!ObjectAllocateSlot(newHandle, &objectHash)) return FALSE; *object = (HASH_OBJECT *)objectHash; // Validate that the proper location of the hash state data relative to the // object state data. pAssert(&((*object)->auth) == &objectHash->publicArea.authPolicy); // Set the common values that a sequence object shares with an ordinary object // The type is TPM_ALG_NULL (*object)->type = TPM_ALG_NULL; // This has no name algorithm and the name is the Empty Buffer (*object)->nameAlg = TPM_ALG_NULL; // Clear the attributes MemorySet(&((*object)->objectAttributes), 0, sizeof(TPMA_OBJECT)); // A sequence object is considered to be in the NULL hierarchy so it should // be marked as temporary so that it can't be persisted (*object)->attributes.temporary = SET; // A sequence object is DA exempt. (*object)->objectAttributes.noDA = SET; if(auth != NULL) { MemoryRemoveTrailingZeros(auth); (*object)->auth = *auth; } else (*object)->auth.t.size = 0; return TRUE; } // // // ObjectCreateHMACSequence() // // This function creates an internal HMAC sequence object. // // Error Returns Meaning // // TPM_RC_OBJECT_MEMORY if there is no free slot for an object // TPM_RC ObjectCreateHMACSequence( TPMI_ALG_HASH hashAlg, // IN: hash algorithm TPM_HANDLE handle, // IN: the handle associated with sequence // object TPM2B_AUTH *auth, // IN: authValue TPMI_DH_OBJECT *newHandle // OUT: HMAC sequence object handle ) { HASH_OBJECT *hmacObject; OBJECT *keyObject; // Try to allocate a slot for new object if(!AllocateSequenceSlot(newHandle, &hmacObject, auth)) return TPM_RC_OBJECT_MEMORY; // Set HMAC sequence bit hmacObject->attributes.hmacSeq = SET; // Get pointer to the HMAC key object keyObject = ObjectGet(handle); CryptStartHMACSequence2B(hashAlg, &keyObject->sensitive.sensitive.bits.b, &hmacObject->state.hmacState); return TPM_RC_SUCCESS; } // // // ObjectCreateHashSequence() // // This function creates a hash sequence object. // // Error Returns Meaning // // TPM_RC_OBJECT_MEMORY if there is no free slot for an object // TPM_RC ObjectCreateHashSequence( TPMI_ALG_HASH hashAlg, // IN: hash algorithm TPM2B_AUTH *auth, // IN: authValue TPMI_DH_OBJECT *newHandle // OUT: sequence object handle ) { HASH_OBJECT *hashObject; // Try to allocate a slot for new object if(!AllocateSequenceSlot(newHandle, &hashObject, auth)) return TPM_RC_OBJECT_MEMORY; // Set hash sequence bit hashObject->attributes.hashSeq = SET; // Start hash for hash sequence CryptStartHashSequence(hashAlg, &hashObject->state.hashState[0]); return TPM_RC_SUCCESS; } // // // ObjectCreateEventSequence() // // This function creates an event sequence object. // // Error Returns Meaning // // TPM_RC_OBJECT_MEMORY if there is no free slot for an object // TPM_RC ObjectCreateEventSequence( TPM2B_AUTH *auth, // IN: authValue TPMI_DH_OBJECT *newHandle // OUT: sequence object handle ) { HASH_OBJECT *hashObject; UINT32 count; TPM_ALG_ID hash; // Try to allocate a slot for new object if(!AllocateSequenceSlot(newHandle, &hashObject, auth)) return TPM_RC_OBJECT_MEMORY; // Set the event sequence attribute hashObject->attributes.eventSeq = SET; // Initialize hash states for each implemented PCR algorithms for(count = 0; (hash = CryptGetHashAlgByIndex(count)) != TPM_ALG_NULL; count++) { // If this is a _TPM_Init or _TPM_HashStart, the sequence object will // not leave the TPM so it doesn't need the sequence handling if(auth == NULL) CryptStartHash(hash, &hashObject->state.hashState[count]); else CryptStartHashSequence(hash, &hashObject->state.hashState[count]); } return TPM_RC_SUCCESS; } // // // ObjectTerminateEvent() // // This function is called to close out the event sequence and clean up the hash context states. // void ObjectTerminateEvent( void ) { HASH_OBJECT *hashObject; int count; BYTE buffer[MAX_DIGEST_SIZE]; hashObject = (HASH_OBJECT *)ObjectGet(g_DRTMHandle); // Don't assume that this is a proper sequence object if(hashObject->attributes.eventSeq) { // If it is, close any open hash contexts. This is done in case // the crypto implementation has some context values that need to be // cleaned up (hygiene). // for(count = 0; CryptGetHashAlgByIndex(count) != TPM_ALG_NULL; count++) { CryptCompleteHash(&hashObject->state.hashState[count], 0, buffer); } // Flush sequence object ObjectFlush(g_DRTMHandle); } g_DRTMHandle = TPM_RH_UNASSIGNED; } // // // // ObjectContextLoad() // // This function loads an object from a saved object context. // // Error Returns Meaning // // TPM_RC_OBJECT_MEMORY if there is no free slot for an object // TPM_RC ObjectContextLoad( OBJECT *object, // IN: object structure from saved context TPMI_DH_OBJECT *handle // OUT: object handle ) { OBJECT *newObject; // Try to allocate a slot for new object if(!ObjectAllocateSlot(handle, &newObject)) return TPM_RC_OBJECT_MEMORY; // Copy input object data to internal structure *newObject = *object; return TPM_RC_SUCCESS; } // // // ObjectFlush() // // This function frees an object slot. // This function requires that the object is loaded. // void ObjectFlush( TPMI_DH_OBJECT handle // IN: handle to be freed ) { UINT32 index = handle - TRANSIENT_FIRST; pAssert(ObjectIsPresent(handle)); // Mark the handle slot as unoccupied s_objects[index].occupied = FALSE; // With no attributes MemorySet((BYTE*)&(s_objects[index].object.entity.attributes), 0, sizeof(OBJECT_ATTRIBUTES)); return; } // // // ObjectFlushHierarchy() // // This function is called to flush all the loaded transient objects associated with a hierarchy when the // hierarchy is disabled. // void ObjectFlushHierarchy( TPMI_RH_HIERARCHY hierarchy // IN: hierarchy to be flush ) { UINT16 i; // iterate object slots for(i = 0; i < MAX_LOADED_OBJECTS; i++) { if(s_objects[i].occupied) // If found an occupied slot { switch(hierarchy) { case TPM_RH_PLATFORM: if(s_objects[i].object.entity.attributes.ppsHierarchy == SET) s_objects[i].occupied = FALSE; break; case TPM_RH_OWNER: if(s_objects[i].object.entity.attributes.spsHierarchy == SET) s_objects[i].occupied = FALSE; break; case TPM_RH_ENDORSEMENT: if(s_objects[i].object.entity.attributes.epsHierarchy == SET) s_objects[i].occupied = FALSE; break; default: pAssert(FALSE); break; } } } return; } // // // ObjectLoadEvict() // // This function loads a persistent object into a transient object slot. // This function requires that handle is associated with a persistent object. // // Error Returns Meaning // // TPM_RC_HANDLE the persistent object does not exist or the associated hierarchy is // disabled. // TPM_RC_OBJECT_MEMORY no object slot // TPM_RC ObjectLoadEvict( TPM_HANDLE *handle, // IN:OUT: evict object handle. If success, it // will be replace by the loaded object handle TPM_CC commandCode // IN: the command being processed ) { TPM_RC result; TPM_HANDLE evictHandle = *handle; // Save the evict handle OBJECT *object; // If this is an index that references a persistent object created by // the platform, then return TPM_RH_HANDLE if the phEnable is FALSE if(*handle >= PLATFORM_PERSISTENT) { // belongs to platform if(g_phEnable == CLEAR) return TPM_RC_HANDLE; } // belongs to owner else if(gc.shEnable == CLEAR) return TPM_RC_HANDLE; // Try to allocate a slot for an object if(!ObjectAllocateSlot(handle, &object)) return TPM_RC_OBJECT_MEMORY; // Copy persistent object to transient object slot. A TPM_RC_HANDLE // may be returned at this point. This will mark the slot as containing // a transient object so that it will be flushed at the end of the // command result = NvGetEvictObject(evictHandle, object); // Bail out if this failed if(result != TPM_RC_SUCCESS) return result; // check the object to see if it is in the endorsement hierarchy // if it is and this is not a TPM2_EvictControl() command, indicate // that the hierarchy is disabled. // If the associated hierarchy is disabled, make it look like the // handle is not defined if( ObjectDataGetHierarchy(object) == TPM_RH_ENDORSEMENT && gc.ehEnable == CLEAR && commandCode != TPM_CC_EvictControl ) return TPM_RC_HANDLE; return result; } // // // ObjectComputeName() // // This function computes the Name of an object from its public area. // void ObjectComputeName( TPMT_PUBLIC *publicArea, // IN: public area of an object TPM2B_NAME *name // OUT: name of the object ) { TPM2B_PUBLIC marshalBuffer; BYTE *buffer; // auxiliary marshal buffer pointer INT32 bufferSize; HASH_STATE hashState; // hash state // if the nameAlg is NULL then there is no name. if(publicArea->nameAlg == TPM_ALG_NULL) { name->t.size = 0; return; } // Start hash stack name->t.size = CryptStartHash(publicArea->nameAlg, &hashState); // Marshal the public area into its canonical form buffer = marshalBuffer.b.buffer; bufferSize = sizeof(TPMT_PUBLIC); marshalBuffer.t.size = TPMT_PUBLIC_Marshal(publicArea, &buffer, &bufferSize); // Adding public area CryptUpdateDigest2B(&hashState, &marshalBuffer.b); // Complete hash leaving room for the name algorithm CryptCompleteHash(&hashState, name->t.size, &name->t.name[2]); // set the nameAlg UINT16_TO_BYTE_ARRAY(publicArea->nameAlg, name->t.name); // name->t.size += 2; return; } // // // ObjectComputeQualifiedName() // // This function computes the qualified name of an object. // void ObjectComputeQualifiedName( TPM2B_NAME *parentQN, // IN: parent's qualified name TPM_ALG_ID nameAlg, // IN: name hash TPM2B_NAME *name, // IN: name of the object TPM2B_NAME *qualifiedName // OUT: qualified name of the object ) { HASH_STATE hashState; // hash state // QN_A = hash_A (QN of parent || NAME_A) // Start hash qualifiedName->t.size = CryptStartHash(nameAlg, &hashState); // Add parent's qualified name CryptUpdateDigest2B(&hashState, &parentQN->b); // Add self name CryptUpdateDigest2B(&hashState, &name->b); // Complete hash leaving room for the name algorithm CryptCompleteHash(&hashState, qualifiedName->t.size, &qualifiedName->t.name[2]); UINT16_TO_BYTE_ARRAY(nameAlg, qualifiedName->t.name); qualifiedName->t.size += 2; return; } // // // ObjectDataIsStorage() // // This function determines if a public area has the attributes associated with a storage key. A storage key is // an asymmetric object that has its restricted and decrypt attributes SET, and sign CLEAR. // // Return Value Meaning // // TRUE if the object is a storage key // FALSE if the object is not a storage key // BOOL ObjectDataIsStorage( TPMT_PUBLIC *publicArea // IN: public area of the object ) { if( CryptIsAsymAlgorithm(publicArea->type) // must be asymmetric, && publicArea->objectAttributes.restricted == SET // restricted, && publicArea->objectAttributes.decrypt == SET // decryption key && publicArea->objectAttributes.sign == CLEAR // can not be sign key ) return TRUE; else return FALSE; } // // ObjectIsStorage() // // This function determines if an object has the attributes associated with a storage key. A storage key is an // asymmetric object that has its restricted and decrypt attributes SET, and sign CLEAR. // // Return Value Meaning // // TRUE if the object is a storage key // FALSE if the object is not a storage key // BOOL ObjectIsStorage( TPMI_DH_OBJECT handle // IN: object handle ) { OBJECT *object = ObjectGet(handle); return ObjectDataIsStorage(&object->publicArea); } // // // ObjectCapGetLoaded() // // This function returns a a list of handles of loaded object, starting from handle. Handle must be in the // range of valid transient object handles, but does not have to be the handle of a loaded transient object. // // Return Value Meaning // // YES if there are more handles available // NO all the available handles has been returned // TPMI_YES_NO ObjectCapGetLoaded( TPMI_DH_OBJECT handle, // IN: start handle UINT32 count, // IN: count of returned handles TPML_HANDLE *handleList // OUT: list of handle ) { TPMI_YES_NO more = NO; UINT32 i; pAssert(HandleGetType(handle) == TPM_HT_TRANSIENT); // Initialize output handle list handleList->count = 0; // The maximum count of handles we may return is MAX_CAP_HANDLES if(count > MAX_CAP_HANDLES) count = MAX_CAP_HANDLES; // Iterate object slots to get loaded object handles for(i = handle - TRANSIENT_FIRST; i < MAX_LOADED_OBJECTS; i++) { if(s_objects[i].occupied == TRUE) { // A valid transient object can not be the copy of a persistent object pAssert(s_objects[i].object.entity.attributes.evict == CLEAR); if(handleList->count < count) { // If we have not filled up the return list, add this object // handle to it handleList->handle[handleList->count] = i + TRANSIENT_FIRST; handleList->count++; // } else { // If the return list is full but we still have loaded object // available, report this and stop iterating more = YES; break; } } } return more; } // // // ObjectCapGetTransientAvail() // // This function returns an estimate of the number of additional transient objects that could be loaded into // the TPM. // UINT32 ObjectCapGetTransientAvail( void ) { UINT32 i; UINT32 num = 0; // Iterate object slot to get the number of unoccupied slots for(i = 0; i < MAX_LOADED_OBJECTS; i++) { if(s_objects[i].occupied == FALSE) num++; } return num; }