// 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 SESSION_PROCESS_C #include "InternalRoutines.h" #include "SessionProcess_fp.h" #include "Platform.h" // // // Authorization Support Functions // // IsDAExempted() // // This function indicates if a handle is exempted from DA logic. A handle is exempted if it is // a) a primary seed handle, // b) an object with noDA bit SET, // c) an NV Index with TPMA_NV_NO_DA bit SET, or // d) a PCR handle. // // Return Value Meaning // // TRUE handle is exempted from DA logic // FALSE handle is not exempted from DA logic // BOOL IsDAExempted( TPM_HANDLE handle // IN: entity handle ) { BOOL result = FALSE; switch(HandleGetType(handle)) { case TPM_HT_PERMANENT: // All permanent handles, other than TPM_RH_LOCKOUT, are exempt from // DA protection. result = (handle != TPM_RH_LOCKOUT); break; // When this function is called, a persistent object will have been loaded // into an object slot and assigned a transient handle. case TPM_HT_TRANSIENT: { OBJECT *object; object = ObjectGet(handle); result = (object->publicArea.objectAttributes.noDA == SET); break; } case TPM_HT_NV_INDEX: { NV_INDEX nvIndex; NvGetIndexInfo(handle, &nvIndex); result = (nvIndex.publicArea.attributes.TPMA_NV_NO_DA == SET); break; } case TPM_HT_PCR: // PCRs are always exempted from DA. result = TRUE; break; default: break; } return result; } // // // IncrementLockout() // // This function is called after an authorization failure that involves use of an authValue. If the entity // referenced by the handle is not exempt from DA protection, then the failedTries counter will be // incremented. // // Error Returns Meaning // // TPM_RC_AUTH_FAIL authorization failure that caused DA lockout to increment // TPM_RC_BAD_AUTH authorization failure did not cause DA lockout to increment // static TPM_RC IncrementLockout( UINT32 sessionIndex ) { TPM_HANDLE handle = s_associatedHandles[sessionIndex]; TPM_HANDLE sessionHandle = s_sessionHandles[sessionIndex]; TPM_RC result; SESSION *session = NULL; // Don't increment lockout unless the handle associated with the session // is DA protected or the session is bound to a DA protected entity. if(sessionHandle == TPM_RS_PW) { if(IsDAExempted(handle)) return TPM_RC_BAD_AUTH; } else { session = SessionGet(sessionHandle); // If the session is bound to lockout, then use that as the relevant // handle. This means that an auth failure with a bound session // bound to lockoutAuth will take precedence over any other // lockout check if(session->attributes.isLockoutBound == SET) handle = TPM_RH_LOCKOUT; if( session->attributes.isDaBound == CLEAR && IsDAExempted(handle) ) // If the handle was changed to TPM_RH_LOCKOUT, this will not return // TPM_RC_BAD_AUTH return TPM_RC_BAD_AUTH; } if(handle == TPM_RH_LOCKOUT) { pAssert(gp.lockOutAuthEnabled); gp.lockOutAuthEnabled = FALSE; // For TPM_RH_LOCKOUT, if lockoutRecovery is 0, no need to update NV since // the lockout auth will be reset at startup. if(gp.lockoutRecovery != 0) { result = NvIsAvailable(); if(result != TPM_RC_SUCCESS) { // No NV access for now. Put the TPM in pending mode. s_DAPendingOnNV = TRUE; } else { // Update NV. NvWriteReserved(NV_LOCKOUT_AUTH_ENABLED, &gp.lockOutAuthEnabled); g_updateNV = TRUE; } } } else { if(gp.recoveryTime != 0) { gp.failedTries++; result = NvIsAvailable(); if(result != TPM_RC_SUCCESS) { // No NV access for now. Put the TPM in pending mode. s_DAPendingOnNV = TRUE; } else { // Record changes to NV. NvWriteReserved(NV_FAILED_TRIES, &gp.failedTries); g_updateNV = TRUE; } } } // Register a DA failure and reset the timers. DARegisterFailure(handle); return TPM_RC_AUTH_FAIL; } // // // IsSessionBindEntity() // // This function indicates if the entity associated with the handle is the entity, to which this session is bound. // The binding would occur by making the bind parameter in TPM2_StartAuthSession() not equal to // TPM_RH_NULL. The binding only occurs if the session is an HMAC session. The bind value is a // combination of the Name and the authValue of the entity. // // Return Value Meaning // // TRUE handle points to the session start entity // FALSE handle does not point to the session start entity // static BOOL IsSessionBindEntity( TPM_HANDLE associatedHandle, // IN: handle to be authorized SESSION *session // IN: associated session ) { TPM2B_NAME entity; // The bind value for the entity // If the session is not bound, return FALSE. if(!session->attributes.isBound) return FALSE; // Compute the bind value for the entity. SessionComputeBoundEntity(associatedHandle, &entity); // Compare to the bind value in the session. session->attributes.requestWasBound = Memory2BEqual(&entity.b, &session->u1.boundEntity.b); return session->attributes.requestWasBound; } // // // IsPolicySessionRequired() // // Checks if a policy session is required for a command. If a command requires DUP or ADMIN role // authorization, then the handle that requires that role is the first handle in the command. This simplifies // this checking. If a new command is created that requires multiple ADMIN role authorizations, then it will // have to be special-cased in this function. A policy session is required if: // a) the command requires the DUP role, // b) the command requires the ADMIN role and the authorized entity is an object and its adminWithPolicy // bit is SET, or // c) the command requires the ADMIN role and the authorized entity is a permanent handle or an NV // Index. // d) The authorized entity is a PCR belonging to a policy group, and has its policy initialized // // Return Value Meaning // // TRUE policy session is required // FALSE policy session is not required // static BOOL IsPolicySessionRequired( TPM_CC commandCode, // IN: command code UINT32 sessionIndex // IN: session index ) { AUTH_ROLE role = CommandAuthRole(commandCode, sessionIndex); TPM_HT type = HandleGetType(s_associatedHandles[sessionIndex]); if(role == AUTH_DUP) return TRUE; if(role == AUTH_ADMIN) { if(type == TPM_HT_TRANSIENT) { OBJECT *object = ObjectGet(s_associatedHandles[sessionIndex]); if(object->publicArea.objectAttributes.adminWithPolicy == CLEAR) return FALSE; } return TRUE; } // if(type == TPM_HT_PCR) { if(PCRPolicyIsAvailable(s_associatedHandles[sessionIndex])) { TPM2B_DIGEST policy; TPMI_ALG_HASH policyAlg; policyAlg = PCRGetAuthPolicy(s_associatedHandles[sessionIndex], &policy); if(policyAlg != TPM_ALG_NULL) return TRUE; } } return FALSE; } // // // IsAuthValueAvailable() // // This function indicates if authValue is available and allowed for USER role authorization of an entity. // This function is similar to IsAuthPolicyAvailable() except that it does not check the size of the authValue // as IsAuthPolicyAvailable() does (a null authValue is a valid auth, but a null policy is not a valid policy). // This function does not check that the handle reference is valid or if the entity is in an enabled hierarchy. // Those checks are assumed to have been performed during the handle unmarshaling. // // Return Value Meaning // // TRUE authValue is available // FALSE authValue is not available // static BOOL IsAuthValueAvailable( TPM_HANDLE handle, // IN: handle of entity TPM_CC commandCode, // IN: commandCode UINT32 sessionIndex // IN: session index ) { BOOL result = FALSE; // If a policy session is required, the entity can not be authorized by // authValue. However, at this point, the policy session requirement should // already have been checked. pAssert(!IsPolicySessionRequired(commandCode, sessionIndex)); switch(HandleGetType(handle)) { case TPM_HT_PERMANENT: switch(handle) { // At this point hierarchy availability has already been // checked so primary seed handles are always available here case TPM_RH_OWNER: case TPM_RH_ENDORSEMENT: case TPM_RH_PLATFORM: #ifdef VENDOR_PERMANENT // This vendor defined handle associated with the // manufacturer's shared secret case VENDOR_PERMANENT: #endif // NullAuth is always available. case TPM_RH_NULL: // At the point when authValue availability is checked, control // path has already passed the DA check so LockOut auth is // always available here case TPM_RH_LOCKOUT: result = TRUE; break; default: // Otherwise authValue is not available. break; } break; case TPM_HT_TRANSIENT: // A persistent object has already been loaded and the internal // handle changed. { OBJECT *object; object = ObjectGet(handle); // authValue is always available for a sequence object. if(ObjectIsSequence(object)) { result = TRUE; break; } // authValue is available for an object if it has its sensitive // portion loaded and // 1. userWithAuth bit is SET, or // 2. ADMIN role is required if( object->attributes.publicOnly == CLEAR && (object->publicArea.objectAttributes.userWithAuth == SET || (CommandAuthRole(commandCode, sessionIndex) == AUTH_ADMIN && object->publicArea.objectAttributes.adminWithPolicy == CLEAR))) result = TRUE; } break; case TPM_HT_NV_INDEX: // NV Index. { NV_INDEX nvIndex; NvGetIndexInfo(handle, &nvIndex); if(IsWriteOperation(commandCode)) { if (nvIndex.publicArea.attributes.TPMA_NV_AUTHWRITE == SET) result = TRUE; } else { if (nvIndex.publicArea.attributes.TPMA_NV_AUTHREAD == SET) result = TRUE; } } break; case TPM_HT_PCR: // PCR handle. // authValue is always allowed for PCR result = TRUE; break; default: // Otherwise, authValue is not available break; } return result; } // // // // IsAuthPolicyAvailable() // // This function indicates if an authPolicy is available and allowed. // This function does not check that the handle reference is valid or if the entity is in an enabled hierarchy. // Those checks are assumed to have been performed during the handle unmarshaling. // // Return Value Meaning // // TRUE authPolicy is available // FALSE authPolicy is not available // static BOOL IsAuthPolicyAvailable( TPM_HANDLE handle, // IN: handle of entity TPM_CC commandCode, // IN: commandCode UINT32 sessionIndex // IN: session index ) { BOOL result = FALSE; switch(HandleGetType(handle)) { case TPM_HT_PERMANENT: switch(handle) { // At this point hierarchy availability has already been checked. case TPM_RH_OWNER: if (gp.ownerPolicy.t.size != 0) result = TRUE; break; case TPM_RH_ENDORSEMENT: if (gp.endorsementPolicy.t.size != 0) result = TRUE; break; case TPM_RH_PLATFORM: if (gc.platformPolicy.t.size != 0) result = TRUE; break; case TPM_RH_LOCKOUT: if(gp.lockoutPolicy.t.size != 0) result = TRUE; break; default: break; } break; case TPM_HT_TRANSIENT: { // Object handle. // An evict object would already have been loaded and given a // transient object handle by this point. OBJECT *object = ObjectGet(handle); // Policy authorization is not available for an object with only // public portion loaded. if(object->attributes.publicOnly == CLEAR) { // Policy authorization is always available for an object but // is never available for a sequence. if(!ObjectIsSequence(object)) result = TRUE; } break; } case TPM_HT_NV_INDEX: // An NV Index. { NV_INDEX nvIndex; NvGetIndexInfo(handle, &nvIndex); // If the policy size is not zero, check if policy can be used. if(nvIndex.publicArea.authPolicy.t.size != 0) { // If policy session is required for this handle, always // uses policy regardless of the attributes bit setting if(IsPolicySessionRequired(commandCode, sessionIndex)) result = TRUE; // Otherwise, the presence of the policy depends on the NV // attributes. else if(IsWriteOperation(commandCode)) { if ( nvIndex.publicArea.attributes.TPMA_NV_POLICYWRITE == SET) result = TRUE; } else { if ( nvIndex.publicArea.attributes.TPMA_NV_POLICYREAD == SET) result = TRUE; } } } break; case TPM_HT_PCR: // PCR handle. if(PCRPolicyIsAvailable(handle)) result = TRUE; break; default: break; } return result; } // // // Session Parsing Functions // // ComputeCpHash() // // This function computes the cpHash as defined in Part 2 and described in Part 1. // static void ComputeCpHash( TPMI_ALG_HASH hashAlg, // IN: hash algorithm TPM_CC commandCode, // IN: command code UINT32 handleNum, // IN: number of handle TPM_HANDLE handles[], // IN: array of handle UINT32 parmBufferSize, // IN: size of input parameter area BYTE *parmBuffer, // IN: input parameter area TPM2B_DIGEST *cpHash, // OUT: cpHash TPM2B_DIGEST *nameHash // OUT: name hash of command ) { UINT32 i; HASH_STATE hashState; TPM2B_NAME name; // // cpHash = hash(commandCode [ || authName1 // [ || authName2 // [ || authName 3 ]]] // [ || parameters]) // A cpHash can contain just a commandCode only if the lone session is // an audit session. // Start cpHash. cpHash->t.size = CryptStartHash(hashAlg, &hashState); // Add commandCode. CryptUpdateDigestInt(&hashState, sizeof(TPM_CC), &commandCode); // Add authNames for each of the handles. for(i = 0; i < handleNum; i++) { name.t.size = EntityGetName(handles[i], &name.t.name); CryptUpdateDigest2B(&hashState, &name.b); } // Add the parameters. CryptUpdateDigest(&hashState, parmBufferSize, parmBuffer); // Complete the hash. CryptCompleteHash2B(&hashState, &cpHash->b); // If the nameHash is needed, compute it here. if(nameHash != NULL) { // Start name hash. hashState may be reused. nameHash->t.size = CryptStartHash(hashAlg, &hashState); // Adding names. for(i = 0; i < handleNum; i++) { name.t.size = EntityGetName(handles[i], &name.t.name); CryptUpdateDigest2B(&hashState, &name.b); } // Complete hash. CryptCompleteHash2B(&hashState, &nameHash->b); } return; } // // // CheckPWAuthSession() // // This function validates the authorization provided in a PWAP session. It compares the input value to // authValue of the authorized entity. Argument sessionIndex is used to get handles handle of the // referenced entities from s_inputAuthValues[] and s_associatedHandles[]. // // Error Returns Meaning // // TPM_RC_AUTH_FAIL auth fails and increments DA failure count // TPM_RC_BAD_AUTH auth fails but DA does not apply // static TPM_RC CheckPWAuthSession( UINT32 sessionIndex // IN: index of session to be processed ) { TPM2B_AUTH authValue; TPM_HANDLE associatedHandle = s_associatedHandles[sessionIndex]; // Strip trailing zeros from the password. MemoryRemoveTrailingZeros(&s_inputAuthValues[sessionIndex]); // Get the auth value and size. authValue.t.size = EntityGetAuthValue(associatedHandle, &authValue.t.buffer); // Success if the digests are identical. if(Memory2BEqual(&s_inputAuthValues[sessionIndex].b, &authValue.b)) { return TPM_RC_SUCCESS; } else // if the digests are not identical { // Invoke DA protection if applicable. return IncrementLockout(sessionIndex); } } // // // ComputeCommandHMAC() // // This function computes the HMAC for an authorization session in a command. // static void ComputeCommandHMAC( UINT32 sessionIndex, // IN: index of session to be processed TPM2B_DIGEST *cpHash, // IN: cpHash TPM2B_DIGEST *hmac // OUT: authorization HMAC ) { TPM2B_TYPE(KEY, (sizeof(AUTH_VALUE) * 2)); TPM2B_KEY key; BYTE marshalBuffer[sizeof(TPMA_SESSION)]; BYTE *buffer; INT32 bufferSize; UINT32 marshalSize; HMAC_STATE hmacState; TPM2B_NONCE *nonceDecrypt; TPM2B_NONCE *nonceEncrypt; SESSION *session; TPM_HT sessionHandleType = HandleGetType(s_sessionHandles[sessionIndex]); nonceDecrypt = NULL; nonceEncrypt = NULL; // Determine if extra nonceTPM values are going to be required. // If this is the first session (sessionIndex = 0) and it is an authorization // session that uses an HMAC, then check if additional session nonces are to be // included. if( sessionIndex == 0 && s_associatedHandles[sessionIndex] != TPM_RH_UNASSIGNED) { // If there is a decrypt session and if this is not the decrypt session, // then an extra nonce may be needed. if( s_decryptSessionIndex != UNDEFINED_INDEX && s_decryptSessionIndex != sessionIndex) { // Will add the nonce for the decrypt session. SESSION *decryptSession = SessionGet(s_sessionHandles[s_decryptSessionIndex]); nonceDecrypt = &decryptSession->nonceTPM; } // Now repeat for the encrypt session. if( s_encryptSessionIndex != UNDEFINED_INDEX && s_encryptSessionIndex != sessionIndex // && s_encryptSessionIndex != s_decryptSessionIndex) { // Have to have the nonce for the encrypt session. SESSION *encryptSession = SessionGet(s_sessionHandles[s_encryptSessionIndex]); nonceEncrypt = &encryptSession->nonceTPM; } } // Continue with the HMAC processing. session = SessionGet(s_sessionHandles[sessionIndex]); // Generate HMAC key. MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer)); // Check if the session has an associated handle and if the associated entity // is the one to which the session is bound. If not, add the authValue of // this entity to the HMAC key. // If the session is bound to the object or the session is a policy session // with no authValue required, do not include the authValue in the HMAC key. // Note: For a policy session, its isBound attribute is CLEARED. // If the session isn't used for authorization, then there is no auth value // to add if(s_associatedHandles[sessionIndex] != TPM_RH_UNASSIGNED) { // used for auth so see if this is a policy session with authValue needed // or an hmac session that is not bound if (((sessionHandleType == TPM_HT_POLICY_SESSION) && (session->attributes.isAuthValueNeeded == SET)) || ((sessionHandleType == TPM_HT_HMAC_SESSION) && !IsSessionBindEntity(s_associatedHandles[sessionIndex], session)) ) { // add the authValue to the HMAC key pAssert((sizeof(AUTH_VALUE) + key.t.size) <= sizeof(key.t.buffer)); key.t.size = key.t.size + EntityGetAuthValue(s_associatedHandles[sessionIndex], (AUTH_VALUE *)&(key.t.buffer[key.t.size])); } } // if the HMAC key size is 0, a NULL string HMAC is allowed if( key.t.size == 0 && s_inputAuthValues[sessionIndex].t.size == 0) { hmac->t.size = 0; return; } // Start HMAC hmac->t.size = CryptStartHMAC2B(session->authHashAlg, &key.b, &hmacState); // Add cpHash CryptUpdateDigest2B(&hmacState, &cpHash->b); // Add nonceCaller CryptUpdateDigest2B(&hmacState, &s_nonceCaller[sessionIndex].b); // Add nonceTPM CryptUpdateDigest2B(&hmacState, &session->nonceTPM.b); // If needed, add nonceTPM for decrypt session if(nonceDecrypt != NULL) CryptUpdateDigest2B(&hmacState, &nonceDecrypt->b); // If needed, add nonceTPM for encrypt session if(nonceEncrypt != NULL) CryptUpdateDigest2B(&hmacState, &nonceEncrypt->b); // Add sessionAttributes buffer = marshalBuffer; bufferSize = sizeof(TPMA_SESSION); marshalSize = TPMA_SESSION_Marshal(&(s_attributes[sessionIndex]), &buffer, &bufferSize); CryptUpdateDigest(&hmacState, marshalSize, marshalBuffer); // Complete the HMAC computation CryptCompleteHMAC2B(&hmacState, &hmac->b); return; } // // // CheckSessionHMAC() // // This function checks the HMAC of in a session. It uses ComputeCommandHMAC() to compute the // expected HMAC value and then compares the result with the HMAC in the authorization session. The // authorization is successful if they are the same. // If the authorizations are not the same, IncrementLockout() is called. It will return TPM_RC_AUTH_FAIL if // the failure caused the failureCount to increment. Otherwise, it will return TPM_RC_BAD_AUTH. // // Error Returns Meaning // // TPM_RC_AUTH_FAIL auth failure caused failureCount increment // TPM_RC_BAD_AUTH auth failure did not cause failureCount increment // static TPM_RC CheckSessionHMAC( UINT32 sessionIndex, // IN: index of session to be processed TPM2B_DIGEST *cpHash // IN: cpHash of the command ) { TPM2B_DIGEST hmac; // authHMAC for comparing // Compute authHMAC ComputeCommandHMAC(sessionIndex, cpHash, &hmac); // Compare the input HMAC with the authHMAC computed above. if(!Memory2BEqual(&s_inputAuthValues[sessionIndex].b, &hmac.b)) { // If an HMAC session has a failure, invoke the anti-hammering // if it applies to the authorized entity or the session. // Otherwise, just indicate that the authorization is bad. return IncrementLockout(sessionIndex); } return TPM_RC_SUCCESS; } // // // CheckPolicyAuthSession() // // This function is used to validate the authorization in a policy session. This function performs the following // comparisons to see if a policy authorization is properly provided. The check are: // a) compare policyDigest in session with authPolicy associated with the entity to be authorized; // b) compare timeout if applicable; // c) compare commandCode if applicable; // // d) compare cpHash if applicable; and // e) see if PCR values have changed since computed. // If all the above checks succeed, the handle is authorized. The order of these comparisons is not // important because any failure will result in the same error code. // // Error Returns Meaning // // TPM_RC_PCR_CHANGED PCR value is not current // TPM_RC_POLICY_FAIL policy session fails // TPM_RC_LOCALITY command locality is not allowed // TPM_RC_POLICY_CC CC doesn't match // TPM_RC_EXPIRED policy session has expired // TPM_RC_PP PP is required but not asserted // TPM_RC_NV_UNAVAILABLE NV is not available for write // TPM_RC_NV_RATE NV is rate limiting // static TPM_RC CheckPolicyAuthSession( UINT32 sessionIndex, // IN: index of session to be processed TPM_CC commandCode, // IN: command code TPM2B_DIGEST *cpHash, // IN: cpHash using the algorithm of this // session TPM2B_DIGEST *nameHash // IN: nameHash using the session algorithm ) { TPM_RC result = TPM_RC_SUCCESS; SESSION *session; TPM2B_DIGEST authPolicy; TPMI_ALG_HASH policyAlg; UINT8 locality; // Initialize pointer to the auth session. session = SessionGet(s_sessionHandles[sessionIndex]); // If the command is TPM_RC_PolicySecret(), make sure that // either password or authValue is required if( commandCode == TPM_CC_PolicySecret && session->attributes.isPasswordNeeded == CLEAR && session->attributes.isAuthValueNeeded == CLEAR) return TPM_RC_MODE; // See if the PCR counter for the session is still valid. if( !SessionPCRValueIsCurrent(s_sessionHandles[sessionIndex]) ) return TPM_RC_PCR_CHANGED; // Get authPolicy. policyAlg = EntityGetAuthPolicy(s_associatedHandles[sessionIndex], &authPolicy); // Compare authPolicy. if(!Memory2BEqual(&session->u2.policyDigest.b, &authPolicy.b)) return TPM_RC_POLICY_FAIL; // Policy is OK so check if the other factors are correct // Compare policy hash algorithm. if(policyAlg != session->authHashAlg) return TPM_RC_POLICY_FAIL; // Compare timeout. if(session->timeOut != 0) { // Cannot compare time if clock stop advancing. An TPM_RC_NV_UNAVAILABLE // or TPM_RC_NV_RATE error may be returned here. result = NvIsAvailable(); if(result != TPM_RC_SUCCESS) return result; if(session->timeOut < go.clock) return TPM_RC_EXPIRED; } // If command code is provided it must match if(session->commandCode != 0) { if(session->commandCode != commandCode) return TPM_RC_POLICY_CC; } else { // If command requires a DUP or ADMIN authorization, the session must have // command code set. AUTH_ROLE role = CommandAuthRole(commandCode, sessionIndex); if(role == AUTH_ADMIN || role == AUTH_DUP) return TPM_RC_POLICY_FAIL; } // Check command locality. { BYTE sessionLocality[sizeof(TPMA_LOCALITY)]; BYTE *buffer = sessionLocality; INT32 bufferSize = sizeof(TPMA_LOCALITY); // Get existing locality setting in canonical form TPMA_LOCALITY_Marshal(&session->commandLocality, &buffer, &bufferSize); // See if the locality has been set if(sessionLocality[0] != 0) { // If so, get the current locality locality = _plat__LocalityGet(); if (locality < 5) { if( ((sessionLocality[0] & (1 << locality)) == 0) || sessionLocality[0] > 31) return TPM_RC_LOCALITY; } else if (locality > 31) { if(sessionLocality[0] != locality) return TPM_RC_LOCALITY; } else { // Could throw an assert here but a locality error is just // as good. It just means that, whatever the locality is, it isn't // the locality requested so... return TPM_RC_LOCALITY; } } } // end of locality check // Check physical presence. if( session->attributes.isPPRequired == SET && !_plat__PhysicalPresenceAsserted()) return TPM_RC_PP; // Compare cpHash/nameHash if defined, or if the command requires an ADMIN or // DUP role for this handle. if(session->u1.cpHash.b.size != 0) { if(session->attributes.iscpHashDefined) { // Compare cpHash. if(!Memory2BEqual(&session->u1.cpHash.b, &cpHash->b)) return TPM_RC_POLICY_FAIL; } else { // Compare nameHash. // When cpHash is not defined, nameHash is placed in its space. if(!Memory2BEqual(&session->u1.cpHash.b, &nameHash->b)) return TPM_RC_POLICY_FAIL; } } if(session->attributes.checkNvWritten) { NV_INDEX nvIndex; // If this is not an NV index, the policy makes no sense so fail it. if(HandleGetType(s_associatedHandles[sessionIndex])!= TPM_HT_NV_INDEX) return TPM_RC_POLICY_FAIL; // Get the index data NvGetIndexInfo(s_associatedHandles[sessionIndex], &nvIndex); // Make sure that the TPMA_WRITTEN_ATTRIBUTE has the desired state if( (nvIndex.publicArea.attributes.TPMA_NV_WRITTEN == SET) != (session->attributes.nvWrittenState == SET)) return TPM_RC_POLICY_FAIL; } return TPM_RC_SUCCESS; } // // // RetrieveSessionData() // // This function will unmarshal the sessions in the session area of a command. The values are placed in the // arrays that are defined at the beginning of this file. The normal unmarshaling errors are possible. // // Error Returns Meaning // // TPM_RC_SUCCSS unmarshaled without error // TPM_RC_SIZE the number of bytes unmarshaled is not the same as the value for // authorizationSize in the command // static TPM_RC RetrieveSessionData ( TPM_CC commandCode, // IN: command code UINT32 *sessionCount, // OUT: number of sessions found BYTE *sessionBuffer, // IN: pointer to the session buffer INT32 bufferSize // IN: size of the session buffer ) { int sessionIndex; int i; TPM_RC result; SESSION *session; TPM_HT sessionType; s_decryptSessionIndex = UNDEFINED_INDEX; s_encryptSessionIndex = UNDEFINED_INDEX; s_auditSessionIndex = UNDEFINED_INDEX; for(sessionIndex = 0; bufferSize > 0; sessionIndex++) { // If maximum allowed number of sessions has been parsed, return a size // error with a session number that is larger than the number of allowed // sessions if(sessionIndex == MAX_SESSION_NUM) return TPM_RC_SIZE + TPM_RC_S + g_rcIndex[sessionIndex+1]; // make sure that the associated handle for each session starts out // unassigned s_associatedHandles[sessionIndex] = TPM_RH_UNASSIGNED; // First parameter: Session handle. result = TPMI_SH_AUTH_SESSION_Unmarshal(&s_sessionHandles[sessionIndex], &sessionBuffer, &bufferSize, TRUE); if(result != TPM_RC_SUCCESS) return result + TPM_RC_S + g_rcIndex[sessionIndex]; // Second parameter: Nonce. result = TPM2B_NONCE_Unmarshal(&s_nonceCaller[sessionIndex], &sessionBuffer, &bufferSize); if(result != TPM_RC_SUCCESS) return result + TPM_RC_S + g_rcIndex[sessionIndex]; // Third parameter: sessionAttributes. result = TPMA_SESSION_Unmarshal(&s_attributes[sessionIndex], &sessionBuffer, &bufferSize); if(result != TPM_RC_SUCCESS) return result + TPM_RC_S + g_rcIndex[sessionIndex]; // Fourth parameter: authValue (PW or HMAC). result = TPM2B_AUTH_Unmarshal(&s_inputAuthValues[sessionIndex], &sessionBuffer, &bufferSize); if(result != TPM_RC_SUCCESS) return result + TPM_RC_S + g_rcIndex[sessionIndex]; if(s_sessionHandles[sessionIndex] == TPM_RS_PW) { // A PWAP session needs additional processing. // Can't have any attributes set other than continueSession bit if( s_attributes[sessionIndex].encrypt || s_attributes[sessionIndex].decrypt || s_attributes[sessionIndex].audit || s_attributes[sessionIndex].auditExclusive || s_attributes[sessionIndex].auditReset ) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // The nonce size must be zero. if(s_nonceCaller[sessionIndex].t.size != 0) return TPM_RC_NONCE + TPM_RC_S + g_rcIndex[sessionIndex]; continue; } // For not password sessions... // Find out if the session is loaded. if(!SessionIsLoaded(s_sessionHandles[sessionIndex])) return TPM_RC_REFERENCE_S0 + sessionIndex; sessionType = HandleGetType(s_sessionHandles[sessionIndex]); session = SessionGet(s_sessionHandles[sessionIndex]); // Check if the session is an HMAC/policy session. if( ( session->attributes.isPolicy == SET && sessionType == TPM_HT_HMAC_SESSION ) || ( session->attributes.isPolicy == CLEAR && sessionType == TPM_HT_POLICY_SESSION ) ) return TPM_RC_HANDLE + TPM_RC_S + g_rcIndex[sessionIndex]; // Check that this handle has not previously been used. for(i = 0; i < sessionIndex; i++) { if(s_sessionHandles[i] == s_sessionHandles[sessionIndex]) return TPM_RC_HANDLE + TPM_RC_S + g_rcIndex[sessionIndex]; } // If the session is used for parameter encryption or audit as well, set // the corresponding indices. // First process decrypt. if(s_attributes[sessionIndex].decrypt) { // Check if the commandCode allows command parameter encryption. if(DecryptSize(commandCode) == 0) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // Encrypt attribute can only appear in one session if(s_decryptSessionIndex != UNDEFINED_INDEX) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // Can't decrypt if the session's symmetric algorithm is TPM_ALG_NULL if(session->symmetric.algorithm == TPM_ALG_NULL) return TPM_RC_SYMMETRIC + TPM_RC_S + g_rcIndex[sessionIndex]; // All checks passed, so set the index for the session used to decrypt // a command parameter. s_decryptSessionIndex = sessionIndex; } // Now process encrypt. if(s_attributes[sessionIndex].encrypt) { // Check if the commandCode allows response parameter encryption. if(EncryptSize(commandCode) == 0) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // Encrypt attribute can only appear in one session. if(s_encryptSessionIndex != UNDEFINED_INDEX) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // Can't encrypt if the session's symmetric algorithm is TPM_ALG_NULL if(session->symmetric.algorithm == TPM_ALG_NULL) return TPM_RC_SYMMETRIC + TPM_RC_S + g_rcIndex[sessionIndex]; // All checks passed, so set the index for the session used to encrypt // a response parameter. s_encryptSessionIndex = sessionIndex; } // At last process audit. if(s_attributes[sessionIndex].audit) { // Audit attribute can only appear in one session. if(s_auditSessionIndex != UNDEFINED_INDEX) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // An audit session can not be policy session. if( HandleGetType(s_sessionHandles[sessionIndex]) == TPM_HT_POLICY_SESSION) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // If this is a reset of the audit session, or the first use // of the session as an audit session, it doesn't matter what // the exclusive state is. The session will become exclusive. if( s_attributes[sessionIndex].auditReset == CLEAR && session->attributes.isAudit == SET) { // Not first use or reset. If auditExlusive is SET, then this // session must be the current exclusive session. if( s_attributes[sessionIndex].auditExclusive == SET && g_exclusiveAuditSession != s_sessionHandles[sessionIndex]) return TPM_RC_EXCLUSIVE; } s_auditSessionIndex = sessionIndex; } // Initialize associated handle as undefined. This will be changed when // the handles are processed. s_associatedHandles[sessionIndex] = TPM_RH_UNASSIGNED; } // Set the number of sessions found. *sessionCount = sessionIndex; return TPM_RC_SUCCESS; } // // // CheckLockedOut() // // This function checks to see if the TPM is in lockout. This function should only be called if the entity being // checked is subject to DA protection. The TPM is in lockout if the NV is not available and a DA write is // pending. Otherwise the TPM is locked out if checking for lockoutAuth (lockoutAuthCheck == TRUE) and // use of lockoutAuth is disabled, or failedTries >= maxTries // // Error Returns Meaning // // TPM_RC_NV_RATE NV is rate limiting // TPM_RC_NV_UNAVAILABLE NV is not available at this time // TPM_RC_LOCKOUT TPM is in lockout // static TPM_RC CheckLockedOut( BOOL lockoutAuthCheck // IN: TRUE if checking is for lockoutAuth ) { TPM_RC result; // If NV is unavailable, and current cycle state recorded in NV is not // SHUTDOWN_NONE, refuse to check any authorization because we would // not be able to handle a DA failure. result = NvIsAvailable(); if(result != TPM_RC_SUCCESS && gp.orderlyState != SHUTDOWN_NONE) return result; // Check if DA info needs to be updated in NV. if(s_DAPendingOnNV) { // If NV is accessible, ... if(result == TPM_RC_SUCCESS) { // ... write the pending DA data and proceed. NvWriteReserved(NV_LOCKOUT_AUTH_ENABLED, &gp.lockOutAuthEnabled); NvWriteReserved(NV_FAILED_TRIES, &gp.failedTries); g_updateNV = TRUE; s_DAPendingOnNV = FALSE; } else { // Otherwise no authorization can be checked. return result; } } // Lockout is in effect if checking for lockoutAuth and use of lockoutAuth // is disabled... if(lockoutAuthCheck) { if(gp.lockOutAuthEnabled == FALSE) return TPM_RC_LOCKOUT; } else { // ... or if the number of failed tries has been maxed out. if(gp.failedTries >= gp.maxTries) return TPM_RC_LOCKOUT; } return TPM_RC_SUCCESS; } // // // CheckAuthSession() // // This function checks that the authorization session properly authorizes the use of the associated handle. // // Error Returns Meaning // // TPM_RC_LOCKOUT entity is protected by DA and TPM is in lockout, or TPM is locked out // on NV update pending on DA parameters // TPM_RC_PP Physical Presence is required but not provided // TPM_RC_AUTH_FAIL HMAC or PW authorization failed with DA side-effects (can be a // policy session) // TPM_RC_BAD_AUTH HMAC or PW authorization failed without DA side-effects (can be a // policy session) // TPM_RC_POLICY_FAIL if policy session fails // TPM_RC_POLICY_CC command code of policy was wrong // TPM_RC_EXPIRED the policy session has expired // TPM_RC_PCR ??? // TPM_RC_AUTH_UNAVAILABLE authValue or authPolicy unavailable // static TPM_RC CheckAuthSession( TPM_CC commandCode, // IN: commandCode UINT32 sessionIndex, // IN: index of session to be processed TPM2B_DIGEST *cpHash, // IN: cpHash TPM2B_DIGEST *nameHash // IN: nameHash // ) { TPM_RC result; SESSION *session = NULL; TPM_HANDLE sessionHandle = s_sessionHandles[sessionIndex]; TPM_HANDLE associatedHandle = s_associatedHandles[sessionIndex]; TPM_HT sessionHandleType = HandleGetType(sessionHandle); pAssert(sessionHandle != TPM_RH_UNASSIGNED); if(sessionHandle != TPM_RS_PW) session = SessionGet(sessionHandle); pAssert(sessionHandleType != TPM_HT_POLICY_SESSION || session != NULL); // If the authorization session is not a policy session, or if the policy // session requires authorization, then check lockout. if( sessionHandleType != TPM_HT_POLICY_SESSION || session->attributes.isAuthValueNeeded || session->attributes.isPasswordNeeded) { // See if entity is subject to lockout. if(!IsDAExempted(associatedHandle)) { // If NV is unavailable, and current cycle state recorded in NV is not // SHUTDOWN_NONE, refuse to check any authorization because we would // not be able to handle a DA failure. result = CheckLockedOut(associatedHandle == TPM_RH_LOCKOUT); if(result != TPM_RC_SUCCESS) return result; } } if(associatedHandle == TPM_RH_PLATFORM) { // If the physical presence is required for this command, check for PP // assertion. If it isn't asserted, no point going any further. if( PhysicalPresenceIsRequired(commandCode) && !_plat__PhysicalPresenceAsserted() ) return TPM_RC_PP; } // If a policy session is required, make sure that it is being used. if( IsPolicySessionRequired(commandCode, sessionIndex) && sessionHandleType != TPM_HT_POLICY_SESSION) return TPM_RC_AUTH_TYPE; // If this is a PW authorization, check it and return. if(sessionHandle == TPM_RS_PW) { if(IsAuthValueAvailable(associatedHandle, commandCode, sessionIndex)) return CheckPWAuthSession(sessionIndex); else return TPM_RC_AUTH_UNAVAILABLE; } // If this is a policy session, ... if(sessionHandleType == TPM_HT_POLICY_SESSION) { // ... see if the entity has a policy, ... if( !IsAuthPolicyAvailable(associatedHandle, commandCode, sessionIndex)) return TPM_RC_AUTH_UNAVAILABLE; // ... and check the policy session. result = CheckPolicyAuthSession(sessionIndex, commandCode, cpHash, nameHash); if (result != TPM_RC_SUCCESS) return result; } else { // For non policy, the entity being accessed must allow authorization // with an auth value. This is required even if the auth value is not // going to be used in an HMAC because it is bound. if(!IsAuthValueAvailable(associatedHandle, commandCode, sessionIndex)) return TPM_RC_AUTH_UNAVAILABLE; } // At this point, the session must be either a policy or an HMAC session. session = SessionGet(s_sessionHandles[sessionIndex]); if( sessionHandleType == TPM_HT_POLICY_SESSION && session->attributes.isPasswordNeeded == SET) { // For policy session that requires a password, check it as PWAP session. return CheckPWAuthSession(sessionIndex); } else { // For other policy or HMAC sessions, have its HMAC checked. return CheckSessionHMAC(sessionIndex, cpHash); } } #ifdef TPM_CC_GetCommandAuditDigest // // // CheckCommandAudit() // // This function checks if the current command may trigger command audit, and if it is safe to perform the // action. // // Error Returns Meaning // // TPM_RC_NV_UNAVAILABLE NV is not available for write // TPM_RC_NV_RATE NV is rate limiting // static TPM_RC CheckCommandAudit( TPM_CC commandCode, // IN: Command code UINT32 handleNum, // IN: number of element in handle array TPM_HANDLE handles[], // IN: array of handle BYTE *parmBufferStart, // IN: start of parameter buffer UINT32 parmBufferSize // IN: size of parameter buffer ) { TPM_RC result = TPM_RC_SUCCESS; // If audit is implemented, need to check to see if auditing is being done // for this command. if(CommandAuditIsRequired(commandCode)) { // If the audit digest is clear and command audit is required, NV must be // available so that TPM2_GetCommandAuditDigest() is able to increment // audit counter. If NV is not available, the function bails out to prevent // the TPM from attempting an operation that would fail anyway. if( gr.commandAuditDigest.t.size == 0 || commandCode == TPM_CC_GetCommandAuditDigest) { result = NvIsAvailable(); if(result != TPM_RC_SUCCESS) return result; } ComputeCpHash(gp.auditHashAlg, commandCode, handleNum, handles, parmBufferSize, parmBufferStart, &s_cpHashForCommandAudit, NULL); } return TPM_RC_SUCCESS; } #endif // // // ParseSessionBuffer() // // This function is the entry function for command session processing. It iterates sessions in session area // and reports if the required authorization has been properly provided. It also processes audit session and // passes the information of encryption sessions to parameter encryption module. // // Error Returns Meaning // // various parsing failure or authorization failure // TPM_RC ParseSessionBuffer( TPM_CC commandCode, // IN: Command code UINT32 handleNum, // IN: number of element in handle array TPM_HANDLE handles[], // IN: array of handle BYTE *sessionBufferStart, // IN: start of session buffer UINT32 sessionBufferSize, // IN: size of session buffer BYTE *parmBufferStart, // IN: start of parameter buffer UINT32 parmBufferSize // IN: size of parameter buffer ) { TPM_RC result; UINT32 i; INT32 size = 0; TPM2B_AUTH extraKey; UINT32 sessionIndex; SESSION *session; TPM2B_DIGEST cpHash; TPM2B_DIGEST nameHash; TPM_ALG_ID cpHashAlg = TPM_ALG_NULL; // algID for the last computed // cpHash // Check if a command allows any session in its session area. if(!IsSessionAllowed(commandCode)) return TPM_RC_AUTH_CONTEXT; // Default-initialization. s_sessionNum = 0; cpHash.t.size = 0; result = RetrieveSessionData(commandCode, &s_sessionNum, sessionBufferStart, sessionBufferSize); if(result != TPM_RC_SUCCESS) return result; // There is no command in the TPM spec that has more handles than // MAX_SESSION_NUM. pAssert(handleNum <= MAX_SESSION_NUM); // Associate the session with an authorization handle. for(i = 0; i < handleNum; i++) { if(CommandAuthRole(commandCode, i) != AUTH_NONE) { // If the received session number is less than the number of handle // that requires authorization, an error should be returned. // Note: for all the TPM 2.0 commands, handles requiring // authorization come first in a command input. if(i > (s_sessionNum - 1)) return TPM_RC_AUTH_MISSING; // Record the handle associated with the authorization session s_associatedHandles[i] = handles[i]; } } // Consistency checks are done first to avoid auth failure when the command // will not be executed anyway. for(sessionIndex = 0; sessionIndex < s_sessionNum; sessionIndex++) { // PW session must be an authorization session if(s_sessionHandles[sessionIndex] == TPM_RS_PW ) { if(s_associatedHandles[sessionIndex] == TPM_RH_UNASSIGNED) return TPM_RC_HANDLE + g_rcIndex[sessionIndex]; } else { session = SessionGet(s_sessionHandles[sessionIndex]); // A trial session can not appear in session area, because it cannot // be used for authorization, audit or encrypt/decrypt. if(session->attributes.isTrialPolicy == SET) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // See if the session is bound to a DA protected entity // NOTE: Since a policy session is never bound, a policy is still // usable even if the object is DA protected and the TPM is in // lockout. if(session->attributes.isDaBound == SET) { result = CheckLockedOut(session->attributes.isLockoutBound == SET); if(result != TPM_RC_SUCCESS) return result; } // If the current cpHash is the right one, don't re-compute. if(cpHashAlg != session->authHashAlg) // different so compute { cpHashAlg = session->authHashAlg; // save this new algID ComputeCpHash(session->authHashAlg, commandCode, handleNum, handles, parmBufferSize, parmBufferStart, &cpHash, &nameHash); } // If this session is for auditing, save the cpHash. if(s_attributes[sessionIndex].audit) s_cpHashForAudit = cpHash; } // if the session has an associated handle, check the auth if(s_associatedHandles[sessionIndex] != TPM_RH_UNASSIGNED) { result = CheckAuthSession(commandCode, sessionIndex, &cpHash, &nameHash); if(result != TPM_RC_SUCCESS) return RcSafeAddToResult(result, TPM_RC_S + g_rcIndex[sessionIndex]); } else { // a session that is not for authorization must either be encrypt, // decrypt, or audit if( s_attributes[sessionIndex].audit == CLEAR && s_attributes[sessionIndex].encrypt == CLEAR && s_attributes[sessionIndex].decrypt == CLEAR) return TPM_RC_ATTRIBUTES + TPM_RC_S + g_rcIndex[sessionIndex]; // check HMAC for encrypt/decrypt/audit only sessions result = CheckSessionHMAC(sessionIndex, &cpHash); if(result != TPM_RC_SUCCESS) return RcSafeAddToResult(result, TPM_RC_S + g_rcIndex[sessionIndex]); } } #ifdef TPM_CC_GetCommandAuditDigest // Check if the command should be audited. result = CheckCommandAudit(commandCode, handleNum, handles, parmBufferStart, parmBufferSize); if(result != TPM_RC_SUCCESS) return result; // No session number to reference #endif // Decrypt the first parameter if applicable. This should be the last operation // in session processing. // If the encrypt session is associated with a handle and the handle's // authValue is available, then authValue is concatenated with sessionAuth to // generate encryption key, no matter if the handle is the session bound entity // or not. if(s_decryptSessionIndex != UNDEFINED_INDEX) { // Get size of the leading size field in decrypt parameter if( s_associatedHandles[s_decryptSessionIndex] != TPM_RH_UNASSIGNED && IsAuthValueAvailable(s_associatedHandles[s_decryptSessionIndex], commandCode, s_decryptSessionIndex) ) { extraKey.b.size= EntityGetAuthValue(s_associatedHandles[s_decryptSessionIndex], &extraKey.t.buffer); } else { extraKey.b.size = 0; } size = DecryptSize(commandCode); result = CryptParameterDecryption( s_sessionHandles[s_decryptSessionIndex], &s_nonceCaller[s_decryptSessionIndex].b, parmBufferSize, (UINT16)size, &extraKey, parmBufferStart); if(result != TPM_RC_SUCCESS) return RcSafeAddToResult(result, TPM_RC_S + g_rcIndex[s_decryptSessionIndex]); } return TPM_RC_SUCCESS; } // // // CheckAuthNoSession() // // Function to process a command with no session associated. The function makes sure all the handles in // the command require no authorization. // // // // Error Returns Meaning // // TPM_RC_AUTH_MISSING failure - one or more handles require auth // TPM_RC CheckAuthNoSession( TPM_CC commandCode, // IN: Command Code UINT32 handleNum, // IN: number of handles in command TPM_HANDLE handles[], // IN: array of handle BYTE *parmBufferStart, // IN: start of parameter buffer UINT32 parmBufferSize // IN: size of parameter buffer ) { UINT32 i; TPM_RC result = TPM_RC_SUCCESS; // Check if the commandCode requires authorization for(i = 0; i < handleNum; i++) { if(CommandAuthRole(commandCode, i) != AUTH_NONE) return TPM_RC_AUTH_MISSING; } #ifdef TPM_CC_GetCommandAuditDigest // Check if the command should be audited. result = CheckCommandAudit(commandCode, handleNum, handles, parmBufferStart, parmBufferSize); if(result != TPM_RC_SUCCESS) return result; #endif // Initialize number of sessions to be 0 s_sessionNum = 0; return TPM_RC_SUCCESS; } // // // Response Session Processing // // Introduction // // The following functions build the session area in a response, and handle the audit sessions (if present). // // ComputeRpHash() // // Function to compute rpHash (Response Parameter Hash). The rpHash is only computed if there is an // HMAC authorization session and the return code is TPM_RC_SUCCESS. // static void ComputeRpHash( TPM_ALG_ID hashAlg, // IN: hash algorithm to compute rpHash TPM_CC commandCode, // IN: commandCode UINT32 resParmBufferSize, // IN: size of response parameter buffer BYTE *resParmBuffer, // IN: response parameter buffer TPM2B_DIGEST *rpHash // OUT: rpHash ) { // The command result in rpHash is always TPM_RC_SUCCESS. TPM_RC responseCode = TPM_RC_SUCCESS; HASH_STATE hashState; // rpHash := hash(responseCode || commandCode || parameters) // Initiate hash creation. rpHash->t.size = CryptStartHash(hashAlg, &hashState); // Add hash constituents. CryptUpdateDigestInt(&hashState, sizeof(TPM_RC), &responseCode); CryptUpdateDigestInt(&hashState, sizeof(TPM_CC), &commandCode); CryptUpdateDigest(&hashState, resParmBufferSize, resParmBuffer); // Complete hash computation. CryptCompleteHash2B(&hashState, &rpHash->b); return; } // // // InitAuditSession() // // This function initializes the audit data in an audit session. // static void InitAuditSession( SESSION *session // session to be initialized ) { // Mark session as an audit session. session->attributes.isAudit = SET; // Audit session can not be bound. session->attributes.isBound = CLEAR; // Size of the audit log is the size of session hash algorithm digest. session->u2.auditDigest.t.size = CryptGetHashDigestSize(session->authHashAlg); // Set the original digest value to be 0. MemorySet(&session->u2.auditDigest.t.buffer, 0, session->u2.auditDigest.t.size); return; } // // // Audit() // // This function updates the audit digest in an audit session. // static void Audit( SESSION *auditSession, // IN: loaded audit session TPM_CC commandCode, // IN: commandCode UINT32 resParmBufferSize, // IN: size of response parameter buffer BYTE *resParmBuffer // IN: response parameter buffer ) { TPM2B_DIGEST rpHash; // rpHash for response HASH_STATE hashState; // Compute rpHash ComputeRpHash(auditSession->authHashAlg, commandCode, resParmBufferSize, resParmBuffer, &rpHash); // auditDigestnew := hash (auditDigestold || cpHash || rpHash) // Start hash computation. CryptStartHash(auditSession->authHashAlg, &hashState); // Add old digest. CryptUpdateDigest2B(&hashState, &auditSession->u2.auditDigest.b); // Add cpHash and rpHash. CryptUpdateDigest2B(&hashState, &s_cpHashForAudit.b); CryptUpdateDigest2B(&hashState, &rpHash.b); // Finalize the hash. CryptCompleteHash2B(&hashState, &auditSession->u2.auditDigest.b); return; } #ifdef TPM_CC_GetCommandAuditDigest // // // CommandAudit() // // This function updates the command audit digest. // static void CommandAudit( TPM_CC commandCode, // IN: commandCode UINT32 resParmBufferSize, // IN: size of response parameter buffer BYTE *resParmBuffer // IN: response parameter buffer ) { if(CommandAuditIsRequired(commandCode)) { TPM2B_DIGEST rpHash; // rpHash for response HASH_STATE hashState; // Compute rpHash. ComputeRpHash(gp.auditHashAlg, commandCode, resParmBufferSize, resParmBuffer, &rpHash); // If the digest.size is one, it indicates the special case of changing // the audit hash algorithm. For this case, no audit is done on exit. // NOTE: When the hash algorithm is changed, g_updateNV is set in order to // force an update to the NV on exit so that the change in digest will // be recorded. So, it is safe to exit here without setting any flags // because the digest change will be written to NV when this code exits. if(gr.commandAuditDigest.t.size == 1) { gr.commandAuditDigest.t.size = 0; return; } // If the digest size is zero, need to start a new digest and increment // the audit counter. if(gr.commandAuditDigest.t.size == 0) { gr.commandAuditDigest.t.size = CryptGetHashDigestSize(gp.auditHashAlg); MemorySet(gr.commandAuditDigest.t.buffer, 0, gr.commandAuditDigest.t.size); // Bump the counter and save its value to NV. gp.auditCounter++; NvWriteReserved(NV_AUDIT_COUNTER, &gp.auditCounter); g_updateNV = TRUE; // } // auditDigestnew := hash (auditDigestold || cpHash || rpHash) // Start hash computation. CryptStartHash(gp.auditHashAlg, &hashState); // Add old digest. CryptUpdateDigest2B(&hashState, &gr.commandAuditDigest.b); // Add cpHash CryptUpdateDigest2B(&hashState, &s_cpHashForCommandAudit.b); // Add rpHash CryptUpdateDigest2B(&hashState, &rpHash.b); // Finalize the hash. CryptCompleteHash2B(&hashState, &gr.commandAuditDigest.b); } return; } #endif // // // UpdateAuditSessionStatus() // // Function to update the internal audit related states of a session. It // a) initializes the session as audit session and sets it to be exclusive if this is the first time it is used for // audit or audit reset was requested; // b) reports exclusive audit session; // c) extends audit log; and // d) clears exclusive audit session if no audit session found in the command. // static void UpdateAuditSessionStatus( TPM_CC commandCode, // IN: commandCode UINT32 resParmBufferSize, // IN: size of response parameter buffer BYTE *resParmBuffer // IN: response parameter buffer ) { UINT32 i; TPM_HANDLE auditSession = TPM_RH_UNASSIGNED; // Iterate through sessions for (i = 0; i < s_sessionNum; i++) { SESSION *session; // PW session do not have a loaded session and can not be an audit // session either. Skip it. if(s_sessionHandles[i] == TPM_RS_PW) continue; session = SessionGet(s_sessionHandles[i]); // If a session is used for audit if(s_attributes[i].audit == SET) { // An audit session has been found auditSession = s_sessionHandles[i]; // If the session has not been an audit session yet, or // the auditSetting bits indicate a reset, initialize it and set // it to be the exclusive session if( session->attributes.isAudit == CLEAR || s_attributes[i].auditReset == SET ) { InitAuditSession(session); g_exclusiveAuditSession = auditSession; } else { // Check if the audit session is the current exclusive audit // session and, if not, clear previous exclusive audit session. if(g_exclusiveAuditSession != auditSession) g_exclusiveAuditSession = TPM_RH_UNASSIGNED; } // Report audit session exclusivity. if(g_exclusiveAuditSession == auditSession) { s_attributes[i].auditExclusive = SET; } else { s_attributes[i].auditExclusive = CLEAR; } // Extend audit log. Audit(session, commandCode, resParmBufferSize, resParmBuffer); } } // If no audit session is found in the command, and the command allows // a session then, clear the current exclusive // audit session. if(auditSession == TPM_RH_UNASSIGNED && IsSessionAllowed(commandCode)) { g_exclusiveAuditSession = TPM_RH_UNASSIGNED; } return; } // // // ComputeResponseHMAC() // // Function to compute HMAC for authorization session in a response. // static void ComputeResponseHMAC( UINT32 sessionIndex, // IN: session index to be processed SESSION *session, // IN: loaded session TPM_CC commandCode, // IN: commandCode TPM2B_NONCE *nonceTPM, // IN: nonceTPM UINT32 resParmBufferSize, // IN: size of response parameter buffer BYTE *resParmBuffer, // IN: response parameter buffer TPM2B_DIGEST *hmac // OUT: authHMAC ) { TPM2B_TYPE(KEY, (sizeof(AUTH_VALUE) * 2)); TPM2B_KEY key; // HMAC key BYTE marshalBuffer[sizeof(TPMA_SESSION)]; BYTE *buffer; INT32 bufferSize; UINT32 marshalSize; HMAC_STATE hmacState; TPM2B_DIGEST rp_hash; // // Compute rpHash. ComputeRpHash(session->authHashAlg, commandCode, resParmBufferSize, resParmBuffer, &rp_hash); // Generate HMAC key MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer)); // Check if the session has an associated handle and the associated entity is // the one that the session is bound to. // If not bound, add the authValue of this entity to the HMAC key. if( s_associatedHandles[sessionIndex] != TPM_RH_UNASSIGNED && !( HandleGetType(s_sessionHandles[sessionIndex]) == TPM_HT_POLICY_SESSION && session->attributes.isAuthValueNeeded == CLEAR) && !session->attributes.requestWasBound) { pAssert((sizeof(AUTH_VALUE) + key.t.size) <= sizeof(key.t.buffer)); key.t.size = key.t.size + EntityGetAuthValue(s_associatedHandles[sessionIndex], (AUTH_VALUE *)&key.t.buffer[key.t.size]); } // if the HMAC key size for a policy session is 0, the response HMAC is // computed according to the input HMAC if(HandleGetType(s_sessionHandles[sessionIndex]) == TPM_HT_POLICY_SESSION && key.t.size == 0 && s_inputAuthValues[sessionIndex].t.size == 0) { hmac->t.size = 0; return; } // Start HMAC computation. hmac->t.size = CryptStartHMAC2B(session->authHashAlg, &key.b, &hmacState); // Add hash components. CryptUpdateDigest2B(&hmacState, &rp_hash.b); CryptUpdateDigest2B(&hmacState, &nonceTPM->b); CryptUpdateDigest2B(&hmacState, &s_nonceCaller[sessionIndex].b); // Add session attributes. buffer = marshalBuffer; bufferSize = sizeof(TPMA_SESSION); marshalSize = TPMA_SESSION_Marshal(&s_attributes[sessionIndex], &buffer, &bufferSize); CryptUpdateDigest(&hmacState, marshalSize, marshalBuffer); // Finalize HMAC. CryptCompleteHMAC2B(&hmacState, &hmac->b); return; } // // // BuildSingleResponseAuth() // // Function to compute response for an authorization session. // static void BuildSingleResponseAuth( UINT32 sessionIndex, // IN: session index to be processed TPM_CC commandCode, // IN: commandCode UINT32 resParmBufferSize, // IN: size of response parameter buffer BYTE *resParmBuffer, // IN: response parameter buffer TPM2B_AUTH *auth // OUT: authHMAC ) // { // For password authorization, field is empty. if(s_sessionHandles[sessionIndex] == TPM_RS_PW) { auth->t.size = 0; } else { // Fill in policy/HMAC based session response. SESSION *session = SessionGet(s_sessionHandles[sessionIndex]); // If the session is a policy session with isPasswordNeeded SET, the auth // field is empty. if(HandleGetType(s_sessionHandles[sessionIndex]) == TPM_HT_POLICY_SESSION && session->attributes.isPasswordNeeded == SET) auth->t.size = 0; else // Compute response HMAC. ComputeResponseHMAC(sessionIndex, session, commandCode, &session->nonceTPM, resParmBufferSize, resParmBuffer, auth); } return; } // // // UpdateTPMNonce() // // Updates TPM nonce in both internal session or response if applicable. // static void UpdateTPMNonce( UINT16 noncesSize, // IN: number of elements in 'nonces' array TPM2B_NONCE nonces[] // OUT: nonceTPM ) { UINT32 i; pAssert(noncesSize >= s_sessionNum); for(i = 0; i < s_sessionNum; i++) { SESSION *session; // For PW session, nonce is 0. if(s_sessionHandles[i] == TPM_RS_PW) { nonces[i].t.size = 0; continue; } session = SessionGet(s_sessionHandles[i]); // Update nonceTPM in both internal session and response. CryptGenerateRandom(session->nonceTPM.t.size, session->nonceTPM.t.buffer); nonces[i] = session->nonceTPM; } return; } // // // UpdateInternalSession() // // Updates internal sessions: // // // a) Restarts session time, and // b) Clears a policy session since nonce is rolling. // static void UpdateInternalSession( void ) { UINT32 i; for(i = 0; i < s_sessionNum; i++) { // For PW session, no update. if(s_sessionHandles[i] == TPM_RS_PW) continue; if(s_attributes[i].continueSession == CLEAR) { // Close internal session. SessionFlush(s_sessionHandles[i]); } else { // If nonce is rolling in a policy session, the policy related data // will be re-initialized. if(HandleGetType(s_sessionHandles[i]) == TPM_HT_POLICY_SESSION) { SESSION *session = SessionGet(s_sessionHandles[i]); // When the nonce rolls it starts a new timing interval for the // policy session. SessionResetPolicyData(session); session->startTime = go.clock; } } } return; } // // // BuildResponseSession() // // Function to build Session buffer in a response. // void BuildResponseSession( TPM_ST tag, // IN: tag TPM_CC commandCode, // IN: commandCode UINT32 resHandleSize, // IN: size of response handle buffer UINT32 resParmSize, // IN: size of response parameter buffer UINT32 *resSessionSize // OUT: response session area ) { BYTE *resParmBuffer; INT32 bufferSize; TPM2B_NONCE responseNonces[MAX_SESSION_NUM]; // Compute response parameter buffer start. resParmBuffer = MemoryGetResponseBuffer(commandCode) + sizeof(TPM_ST) + sizeof(UINT32) + sizeof(TPM_RC) + resHandleSize; bufferSize = MAX_RESPONSE_SIZE - sizeof(TPM_ST) - sizeof(UINT32) - sizeof(TPM_RC) - resHandleSize; // For TPM_ST_SESSIONS, there is parameterSize field. if(tag == TPM_ST_SESSIONS) { resParmBuffer += sizeof(UINT32); bufferSize -= sizeof(UINT32); } // Session nonce should be updated before parameter encryption if(tag == TPM_ST_SESSIONS) { UpdateTPMNonce(MAX_SESSION_NUM, responseNonces); // Encrypt first parameter if applicable. Parameter encryption should // happen after nonce update and before any rpHash is computed. // If the encrypt session is associated with a handle, the authValue of // this handle will be concatenated with sessionAuth to generate // encryption key, no matter if the handle is the session bound entity // or not. The authValue is added to sessionAuth only when the authValue // is available. if(s_encryptSessionIndex != UNDEFINED_INDEX) { UINT32 size; TPM2B_AUTH extraKey; // Get size of the leading size field if( s_associatedHandles[s_encryptSessionIndex] != TPM_RH_UNASSIGNED && IsAuthValueAvailable(s_associatedHandles[s_encryptSessionIndex], commandCode, s_encryptSessionIndex) ) { extraKey.b.size = EntityGetAuthValue(s_associatedHandles[s_encryptSessionIndex], &extraKey.t.buffer); } else { extraKey.b.size = 0; } size = EncryptSize(commandCode); CryptParameterEncryption(s_sessionHandles[s_encryptSessionIndex], &s_nonceCaller[s_encryptSessionIndex].b, (UINT16)size, &extraKey, resParmBuffer); } } // Audit session should be updated first regardless of the tag. // A command with no session may trigger a change of the exclusivity state. UpdateAuditSessionStatus(commandCode, resParmSize, resParmBuffer); // Audit command. CommandAudit(commandCode, resParmSize, resParmBuffer); // Process command with sessions. if(tag == TPM_ST_SESSIONS) { UINT32 i; BYTE *buffer; TPM2B_DIGEST responseAuths[MAX_SESSION_NUM]; pAssert(s_sessionNum > 0); // Iterate over each session in the command session area, and create // corresponding sessions for response. for(i = 0; i < s_sessionNum; i++) { BuildSingleResponseAuth( i, commandCode, resParmSize, resParmBuffer, &responseAuths[i]); // Make sure that continueSession is SET on any Password session. // This makes it marginally easier for the management software // to keep track of the closed sessions. if( s_attributes[i].continueSession == CLEAR && s_sessionHandles[i] == TPM_RS_PW) { s_attributes[i].continueSession = SET; } } // Assemble Response Sessions. *resSessionSize = 0; buffer = resParmBuffer + resParmSize; bufferSize -= resParmSize; for(i = 0; i < s_sessionNum; i++) { *resSessionSize += TPM2B_NONCE_Marshal(&responseNonces[i], &buffer, &bufferSize); *resSessionSize += TPMA_SESSION_Marshal(&s_attributes[i], &buffer, &bufferSize); *resSessionSize += TPM2B_DIGEST_Marshal(&responseAuths[i], &buffer, &bufferSize); } // Update internal sessions after completing response buffer computation. UpdateInternalSession(); } else { // Process command with no session. *resSessionSize = 0; } return; }