diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:41:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:41:43 +0000 |
commit | 92cccad89d1c12b39165d5f0ed7ccd2d44965a1a (patch) | |
tree | f59a2764cd8c50959050a428bd8fc935138df750 /src/tpm2/SessionProcess.c | |
parent | Initial commit. (diff) | |
download | libtpms-92cccad89d1c12b39165d5f0ed7ccd2d44965a1a.tar.xz libtpms-92cccad89d1c12b39165d5f0ed7ccd2d44965a1a.zip |
Adding upstream version 0.9.2.upstream/0.9.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tpm2/SessionProcess.c')
-rw-r--r-- | src/tpm2/SessionProcess.c | 1990 |
1 files changed, 1990 insertions, 0 deletions
diff --git a/src/tpm2/SessionProcess.c b/src/tpm2/SessionProcess.c new file mode 100644 index 0000000..f8efc68 --- /dev/null +++ b/src/tpm2/SessionProcess.c @@ -0,0 +1,1990 @@ +/********************************************************************************/ +/* */ +/* Process the Authorization Sessions */ +/* Written by Ken Goldman */ +/* IBM Thomas J. Watson Research Center */ +/* $Id: SessionProcess.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 */ +/* */ +/********************************************************************************/ + +/* 6.4 SessionProcess.c */ +/* 6.4.1 Introduction */ +/* This file contains the subsystem that process the authorization sessions including implementation + of the Dictionary Attack logic. ExecCommand() uses ParseSessionBuffer() to process the + authorization session area of a command and BuildResponseSession() to create the authorization + session area of a response */ +#define SESSION_PROCESS_C +#include "Tpm.h" +#include "ACT.h" +/* 6.4.3.1 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. */ +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: + { + TPMA_OBJECT attributes = ObjectGetPublicAttributes(handle); + result = IS_ATTRIBUTE(attributes, TPMA_OBJECT, noDA); + break; + } + case TPM_HT_NV_INDEX: + { + NV_INDEX *nvIndex = NvGetIndexInfo(handle, NULL); + result = IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, NO_DA); + break; + } + case TPM_HT_PCR: + // PCRs are always exempted from DA. + result = TRUE; + break; + default: + break; + } + return result; +} +/* 6.4.3.2 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. */ +static TPM_RC +IncrementLockout( + UINT32 sessionIndex + ) +{ + TPM_HANDLE handle = s_associatedHandles[sessionIndex]; + TPM_HANDLE sessionHandle = s_sessionHandles[sessionIndex]; + 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 authorization 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) || session->attributes.includeAuth == CLEAR)) + // 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 == TRUE); + // lockout is no longer enabled + gp.lockOutAuthEnabled = FALSE; + // For TPM_RH_LOCKOUT, if lockoutRecovery is 0, no need to update NV since + // the lockout authorization will be reset at startup. + if(gp.lockoutRecovery != 0) + { + if(NV_IS_AVAILABLE) + // Update NV. + NV_SYNC_PERSISTENT(lockOutAuthEnabled); + else + // No NV access for now. Put the TPM in pending mode. + s_DAPendingOnNV = TRUE; + } + } + else + { + if(gp.recoveryTime != 0) + { + gp.failedTries++; + if(NV_IS_AVAILABLE) + // Record changes to NV. NvWrite will SET g_updateNV + NV_SYNC_PERSISTENT(failedTries); + else + // No NV access for now. Put the TPM in pending mode. + s_DAPendingOnNV = TRUE; + } + } + // Register a DA failure and reset the timers. + DARegisterFailure(handle); + return TPM_RC_AUTH_FAIL; +} +/* 6.4.3.3 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. */ +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) + { + // Compute the bind value for the entity. + SessionComputeBoundEntity(associatedHandle, &entity); + // Compare to the bind value in the session. + return MemoryEqual2B(&entity.b, &session->u1.boundEntity.b); + } + return FALSE; +} +/* 6.4.3.4 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 Values Meaning */ +/* TRUE policy session is required */ +/* FALSE policy session is not required */ +static BOOL +IsPolicySessionRequired( + COMMAND_INDEX commandIndex, // IN: command index + UINT32 sessionIndex // IN: session index + ) +{ + AUTH_ROLE role = CommandAuthRole(commandIndex, sessionIndex); + TPM_HT type = HandleGetType(s_associatedHandles[sessionIndex]); + if(role == AUTH_DUP) + return TRUE; + if(role == AUTH_ADMIN) + { + // We allow an exception for ADMIN role in a transient object. If the object + // allows ADMIN role actions with authorization, then policy is not + // required. For all other cases, there is no way to override the command + // requirement that a policy be used + if(type == TPM_HT_TRANSIENT) + { + OBJECT *object = HandleToObject(s_associatedHandles[sessionIndex]); + if(!IS_ATTRIBUTE(object->publicArea.objectAttributes, TPMA_OBJECT, + adminWithPolicy)) + 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; +} +/* 6.4.3.5 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 authorization, 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. */ +static BOOL +IsAuthValueAvailable( + TPM_HANDLE handle, // IN: handle of entity + COMMAND_INDEX commandIndex, // IN: command index + 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 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 + // The DA checking has been performed on LockoutAuth but we + // bypass the DA logic if we are using lockout policy. The + // policy would allow execution to continue an lockoutAuth + // could be used, even if direct use of lockoutAuth is disabled + case TPM_RH_LOCKOUT: + // NullAuth is always available. + case TPM_RH_NULL: + result = TRUE; + break; +#ifndef __ACT_DISABLED // libtpms added begin + FOR_EACH_ACT(CASE_ACT_HANDLE) + { + // The ACT auth value is not available if the platform is disabled + result = g_phEnable == SET; + break; + } +#endif // libtpms added end + 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; + TPMA_OBJECT attributes; + // + object = HandleToObject(handle); + attributes = object->publicArea.objectAttributes; + // authValue is always available for a sequence object. + // An alternative for this is to + // SET_ATTRIBUTE(object->publicArea, TPMA_OBJECT, userWithAuth) when the + // sequence is started. + 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 + && (IS_ATTRIBUTE(attributes, TPMA_OBJECT, userWithAuth) + || (CommandAuthRole(commandIndex, sessionIndex) == AUTH_ADMIN + && !IS_ATTRIBUTE(attributes, TPMA_OBJECT, adminWithPolicy)))) + result = TRUE; + } + break; + case TPM_HT_NV_INDEX: + // NV Index. + { + NV_REF locator; + NV_INDEX *nvIndex = NvGetIndexInfo(handle, &locator); + TPMA_NV nvAttributes; + // + pAssert(nvIndex != 0); + nvAttributes = nvIndex->publicArea.attributes; + if(IsWriteOperation(commandIndex)) + { + // AuthWrite can't be set for a PIN index + if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, AUTHWRITE)) + result = TRUE; + } + else + { + // A "read" operation + // For a PIN Index, the authValue is available as long as the + // Index has been written and the pinCount is less than pinLimit + if(IsNvPinFailIndex(nvAttributes) + || IsNvPinPassIndex(nvAttributes)) + { + NV_PIN pin; + if(!IS_ATTRIBUTE(nvAttributes, TPMA_NV, WRITTEN)) + break; // return false + // get the index values + pin.intVal = NvGetUINT64Data(nvIndex, locator); + if(pin.pin.pinCount < pin.pin.pinLimit) + result = TRUE; + } + // For non-PIN Indexes, need to allow use of the authValue + else if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, AUTHREAD)) + 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; +} + +/* 6.4.3.6 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 Values Meaning */ +/* TRUE authPolicy is available */ +/* FALSE authPolicy is not available */ +static BOOL +IsAuthPolicyAvailable( + TPM_HANDLE handle, // IN: handle of entity + COMMAND_INDEX commandIndex, // IN: command index + 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; +#define ACT_GET_POLICY(N) \ + case TPM_RH_ACT_##N: \ + if(go.ACT_##N.authPolicy.t.size != 0) \ + result = TRUE; \ + break; + + FOR_EACH_ACT(ACT_GET_POLICY) + + 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 = HandleToObject(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, NULL); + TPMA_NV nvAttributes = nvIndex->publicArea.attributes; + // + // 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(commandIndex, sessionIndex)) + result = TRUE; + // Otherwise, the presence of the policy depends on the NV + // attributes. + else if(IsWriteOperation(commandIndex)) + { + if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, POLICYWRITE)) + result = TRUE; + } + else + { + if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, POLICYREAD)) + result = TRUE; + } + } + } + break; + case TPM_HT_PCR: + // PCR handle. + if(PCRPolicyIsAvailable(handle)) + result = TRUE; + break; + default: + break; + } + return result; +} +/* 6.4.4 Session Parsing Functions */ +/* 6.4.4.1 ClearCpRpHashes() */ + +void +ClearCpRpHashes( + COMMAND *command + ) +{ + // The macros expand according to the implemented hash algorithms. An IDE may + // complain that COMMAND does not contain SHA1CpHash or SHA1RpHash because of the + // complexity of the macro expansion where the data space is defined; but, if SHA1 + // is implemented, it actually does and the compiler is happy. +#define CLEAR_CP_HASH(HASH, Hash) command->Hash##CpHash.b.size = 0; + FOR_EACH_HASH(CLEAR_CP_HASH) +#define CLEAR_RP_HASH(HASH, Hash) command->Hash##RpHash.b.size = 0; + FOR_EACH_HASH(CLEAR_RP_HASH) +} + +/* 6.4.4.2 GetCpHashPointer() */ +/* Function to get a pointer to the cpHash of the command */ +static TPM2B_DIGEST * +GetCpHashPointer( + COMMAND *command, + TPMI_ALG_HASH hashAlg + ) +{ + TPM2B_DIGEST *retVal; + // + // Define the macro that will expand for each implemented algorithm in the switch + // statement below. +#define GET_CP_HASH_POINTER(HASH, Hash) \ + case ALG_##HASH##_VALUE: \ + retVal = (TPM2B_DIGEST *)&command->Hash##CpHash; \ + break; + + switch(hashAlg) + { + // For each implemented hash, this will expand as defined above + // by GET_CP_HASH_POINTER. Your IDE may complain that + // 'struct "COMMAND" has no field "SHA1CpHash"' but the compiler says + // it does, so... + FOR_EACH_HASH(GET_CP_HASH_POINTER) + default: + retVal = NULL; + break; + } + return retVal; +} + +/* 6.4.4.3 GetRpHashPointer() */ +/* Function to get a pointer to the RpHash() of the command */ +static TPM2B_DIGEST * +GetRpHashPointer( + COMMAND *command, + TPMI_ALG_HASH hashAlg + ) +{ + TPM2B_DIGEST *retVal; + // + // Define the macro that will expand for each implemented algorithm in the switch + // statement below. +#define GET_RP_HASH_POINTER(HASH, Hash) \ + case ALG_##HASH##_VALUE: \ + retVal = (TPM2B_DIGEST *)&command->Hash##RpHash; \ + break; + + switch(hashAlg) + { + // For each implemented hash, this will expand as defined above + // by GET_RP_HASH_POINTER. Your IDE may complain that + // 'struct "COMMAND" has no field 'SHA1RpHash'" but the compiler says + // it does, so... + FOR_EACH_HASH(GET_RP_HASH_POINTER) + default: + retVal = NULL; + break; + } + return retVal; +} + +/* 6.4.4.4 ComputeCpHash() */ +/* This function computes the cpHash as defined in Part 2 and described in Part 1. */ +static TPM2B_DIGEST * +ComputeCpHash( + COMMAND *command, // IN: command parsing structure + TPMI_ALG_HASH hashAlg // IN: hash algorithm + ) +{ + UINT32 i; + HASH_STATE hashState; + TPM2B_NAME name; + TPM2B_DIGEST *cpHash; + // cpHash = hash(commandCode [ || authName1 + // [ || authName2 + // [ || authName 3 ]]] + // [ || parameters]) + // A cpHash can contain just a commandCode only if the lone session is + // an audit session. + // Get pointer to the hash value + cpHash = GetCpHashPointer(command, hashAlg); + if(cpHash->t.size == 0) + { + cpHash->t.size = CryptHashStart(&hashState, hashAlg); + // Add commandCode. + CryptDigestUpdateInt(&hashState, sizeof(TPM_CC), command->code); + // Add authNames for each of the handles. + for(i = 0; i < command->handleNum; i++) + CryptDigestUpdate2B(&hashState, &EntityGetName(command->handles[i], + &name)->b); + // Add the parameters. + CryptDigestUpdate(&hashState, command->parameterSize, + command->parameterBuffer); + // Complete the hash. + CryptHashEnd2B(&hashState, &cpHash->b); + } + return cpHash; +} +/* 6.4.4.5 GetCpHash() */ +/* This function is used to access a precomputed cpHash. */ +static TPM2B_DIGEST * +GetCpHash( + COMMAND *command, + TPMI_ALG_HASH hashAlg + ) +{ + TPM2B_DIGEST *cpHash = GetCpHashPointer(command, hashAlg); + // + pAssert(cpHash->t.size != 0); + return cpHash; +} +/* 6.4.4.6 CompareTemplateHash() */ +/* This function computes the template hash and compares it to the session templateHash. It is the + hash of the second parameter assuming that the command is TPM2_Create(), TPM2_CreatePrimary(), or + TPM2_CreateLoaded() */ +static BOOL +CompareTemplateHash( + COMMAND *command, // IN: parsing structure + SESSION *session // IN: session data + ) +{ + BYTE *pBuffer = command->parameterBuffer; + INT32 pSize = command->parameterSize; + TPM2B_DIGEST tHash; + UINT16 size; + // + // Only try this for the three commands for which it is intended + if(command->code != TPM_CC_Create + && command->code != TPM_CC_CreatePrimary +#if CC_CreateLoaded + && command->code != TPM_CC_CreateLoaded +#endif + ) + return FALSE; + // Assume that the first parameter is a TPM2B and unmarshal the size field + // Note: this will not affect the parameter buffer and size in the calling + // function. + if(UINT16_Unmarshal(&size, &pBuffer, &pSize) != TPM_RC_SUCCESS) + return FALSE; + // reduce the space in the buffer. + // NOTE: this could make pSize go negative if the parameters are not correct but + // the unmarshaling code does not try to unmarshal if the remaining size is + // negative. + pSize -= size; + // Advance the pointer + pBuffer += size; + // Get the size of what should be the template + if(UINT16_Unmarshal(&size, &pBuffer, &pSize) != TPM_RC_SUCCESS) + return FALSE; + // See if this is reasonable + if(size > pSize) + return FALSE; + // Hash the template data + tHash.t.size = CryptHashBlock(session->authHashAlg, size, pBuffer, + sizeof(tHash.t.buffer), tHash.t.buffer); + return(MemoryEqual2B(&session->u1.templateHash.b, &tHash.b)); +} +/* 6.4.4.7 CompareNameHash() */ +/* This function computes the name hash and compares it to the nameHash in the session data. */ +BOOL +CompareNameHash( + COMMAND *command, // IN: main parsing structure + SESSION *session // IN: session structure with nameHash + ) +{ + HASH_STATE hashState; + TPM2B_DIGEST nameHash; + UINT32 i; + TPM2B_NAME name; + // + nameHash.t.size = CryptHashStart(&hashState, session->authHashAlg); + // Add names. + for(i = 0; i < command->handleNum; i++) + CryptDigestUpdate2B(&hashState, &EntityGetName(command->handles[i], + &name)->b); + // Complete hash. + CryptHashEnd2B(&hashState, &nameHash.b); + // and compare + return MemoryEqual(session->u1.nameHash.t.buffer, nameHash.t.buffer, + nameHash.t.size); +} +/* 6.4.4.8 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 authorization fails and increments DA failure count */ +/* TPM_RC_BAD_AUTH authorization 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 authValue with trailing zeros removed + EntityGetAuthValue(associatedHandle, &authValue); + // Success if the values are identical. + if(MemoryEqual2B(&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); + } +} +/* 6.4.4.9 ComputeCommandHMAC() */ +/* This function computes the HMAC for an authorization session in a command. */ +static TPM2B_DIGEST * +ComputeCommandHMAC( + COMMAND *command, // IN: primary control structure + UINT32 sessionIndex, // IN: index of session to be processed + TPM2B_DIGEST *hmac // OUT: authorization HMAC + ) +{ + TPM2B_TYPE(KEY, (sizeof(AUTH_VALUE) * 2)); + TPM2B_KEY key; + BYTE marshalBuffer[sizeof(TPMA_SESSION)]; + BYTE *buffer; + UINT32 marshalSize; + HMAC_STATE hmacState; + TPM2B_NONCE *nonceDecrypt; + TPM2B_NONCE *nonceEncrypt; + SESSION *session; + 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. + // Include the entity authValue if it is needed + if(session->attributes.includeAuth == SET) + { + TPM2B_AUTH authValue; + // Get the entity authValue with trailing zeros removed + EntityGetAuthValue(s_associatedHandles[sessionIndex], &authValue); + // add the authValue to the HMAC key + MemoryConcat2B(&key.b, &authValue.b, sizeof(key.t.buffer)); + } + // 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 hmac; + } + // Start HMAC + hmac->t.size = CryptHmacStart2B(&hmacState, session->authHashAlg, &key.b); + // Add cpHash + CryptDigestUpdate2B(&hmacState.hashState, + &ComputeCpHash(command, session->authHashAlg)->b); + // Add nonces as required + CryptDigestUpdate2B(&hmacState.hashState, &s_nonceCaller[sessionIndex].b); + CryptDigestUpdate2B(&hmacState.hashState, &session->nonceTPM.b); + if(nonceDecrypt != NULL) + CryptDigestUpdate2B(&hmacState.hashState, &nonceDecrypt->b); + if(nonceEncrypt != NULL) + CryptDigestUpdate2B(&hmacState.hashState, &nonceEncrypt->b); + // Add sessionAttributes + buffer = marshalBuffer; + marshalSize = TPMA_SESSION_Marshal(&(s_attributes[sessionIndex]), + &buffer, NULL); + CryptDigestUpdate(&hmacState.hashState, marshalSize, marshalBuffer); + // Complete the HMAC computation + CryptHmacEnd2B(&hmacState, &hmac->b); + return hmac; +} +/* 6.4.4.10 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 authorization failure caused failureCount increment */ +/* TPM_RC_BAD_AUTH authorization failure did not cause failureCount increment */ +static TPM_RC +CheckSessionHMAC( + COMMAND *command, // IN: primary control structure + UINT32 sessionIndex // IN: index of session to be processed + ) +{ + TPM2B_DIGEST hmac; // authHMAC for comparing + // Compute authHMAC + ComputeCommandHMAC(command, sessionIndex, &hmac); + // Compare the input HMAC with the authHMAC computed above. + if(!MemoryEqual2B(&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; +} +/* 6.4.4.11 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( + COMMAND *command, // IN: primary parsing structure + UINT32 sessionIndex // IN: index of session to be processed + ) +{ + SESSION *session; + TPM2B_DIGEST authPolicy; + TPMI_ALG_HASH policyAlg; + UINT8 locality; + // Initialize pointer to the authorization session. + session = SessionGet(s_sessionHandles[sessionIndex]); + // If the command is TPM2_PolicySecret(), make sure that + // either password or authValue is required + if(command->code == 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(session)) + return TPM_RC_PCR_CHANGED; + // Get authPolicy. + policyAlg = EntityGetAuthPolicy(s_associatedHandles[sessionIndex], + &authPolicy); + // Compare authPolicy. + if(!MemoryEqual2B(&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. This doesn't mean that + // a new nonce will be created just that, because TPM time can't advance + // we can't do time-based operations. + RETURN_IF_NV_IS_NOT_AVAILABLE; + if((session->timeout < g_time) + || (session->epoch != g_timeEpoch)) + return TPM_RC_EXPIRED; + } + // If command code is provided it must match + if(session->commandCode != 0) + { + if(session->commandCode != command->code) + 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(command->index, sessionIndex); + if(role == AUTH_ADMIN || role == AUTH_DUP) + return TPM_RC_POLICY_FAIL; + } + // Check command locality. + { + BYTE sessionLocality[sizeof(TPMA_LOCALITY)]; + BYTE *buffer = sessionLocality; + // Get existing locality setting in canonical form + sessionLocality[0] = 0; + TPMA_LOCALITY_Marshal(&session->commandLocality, &buffer, NULL); + // 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) + { + BOOL OK; + if(session->attributes.isCpHashDefined) + // Compare cpHash. + OK = MemoryEqual2B(&session->u1.cpHash.b, + &ComputeCpHash(command, session->authHashAlg)->b); + else if(session->attributes.isTemplateSet) + OK = CompareTemplateHash(command, session); + else + OK = CompareNameHash(command, session); + if(!OK) + return TPM_RCS_POLICY_FAIL; + } + if(session->attributes.checkNvWritten) + { + NV_REF locator; + 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 + nvIndex = NvGetIndexInfo(s_associatedHandles[sessionIndex], &locator); + // Make sure that the TPMA_WRITTEN_ATTRIBUTE has the desired state + if((IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, WRITTEN)) + != (session->attributes.nvWrittenState == SET)) + return TPM_RC_POLICY_FAIL; + } + return TPM_RC_SUCCESS; +} +/* 6.4.4.12 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( + COMMAND *command // IN: main parsing structure for command + ) +{ + int i; + TPM_RC result; + SESSION *session; + TPMA_SESSION sessionAttributes; + TPM_HT sessionType; + INT32 sessionIndex; + TPM_RC errorIndex; + s_decryptSessionIndex = UNDEFINED_INDEX; + s_encryptSessionIndex = UNDEFINED_INDEX; + s_auditSessionIndex = UNDEFINED_INDEX; + for(sessionIndex = 0; command->authSize > 0; sessionIndex++) + { + errorIndex = TPM_RC_S + g_rcIndex[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_RCS_SIZE + errorIndex; + // 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], + &command->parameterBuffer, + &command->authSize, TRUE); + if(result != TPM_RC_SUCCESS) + return result + TPM_RC_S + g_rcIndex[sessionIndex]; + // Second parameter: Nonce. + result = TPM2B_NONCE_Unmarshal(&s_nonceCaller[sessionIndex], + &command->parameterBuffer, + &command->authSize); + if(result != TPM_RC_SUCCESS) + return result + TPM_RC_S + g_rcIndex[sessionIndex]; + // Third parameter: sessionAttributes. + result = TPMA_SESSION_Unmarshal(&s_attributes[sessionIndex], + &command->parameterBuffer, + &command->authSize); + 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], + &command->parameterBuffer, + &command->authSize); + if(result != TPM_RC_SUCCESS) + return result + errorIndex; + sessionAttributes = s_attributes[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(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, encrypt) + || IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, decrypt) + || IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, audit) + || IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, auditExclusive) + || IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, auditReset)) + return TPM_RCS_ATTRIBUTES + errorIndex; + // The nonce size must be zero. + if(s_nonceCaller[sessionIndex].t.size != 0) + return TPM_RCS_NONCE + errorIndex; + 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_RCS_HANDLE + errorIndex; + // Check that this handle has not previously been used. + for(i = 0; i < sessionIndex; i++) + { + if(s_sessionHandles[i] == s_sessionHandles[sessionIndex]) + return TPM_RCS_HANDLE + errorIndex; + } + // If the session is used for parameter encryption or audit as well, set + // the corresponding Indexes. + // First process decrypt. + if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, decrypt)) + { + // Check if the commandCode allows command parameter encryption. + if(DecryptSize(command->index) == 0) + return TPM_RCS_ATTRIBUTES + errorIndex; + // Encrypt attribute can only appear in one session + if(s_decryptSessionIndex != UNDEFINED_INDEX) + return TPM_RCS_ATTRIBUTES + errorIndex; + // Can't decrypt if the session's symmetric algorithm is TPM_ALG_NULL + if(session->symmetric.algorithm == TPM_ALG_NULL) + return TPM_RCS_SYMMETRIC + errorIndex; + // All checks passed, so set the index for the session used to decrypt + // a command parameter. + s_decryptSessionIndex = sessionIndex; + } + // Now process encrypt. + if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, encrypt)) + { + // Check if the commandCode allows response parameter encryption. + if(EncryptSize(command->index) == 0) + return TPM_RCS_ATTRIBUTES + errorIndex; + // Encrypt attribute can only appear in one session. + if(s_encryptSessionIndex != UNDEFINED_INDEX) + return TPM_RCS_ATTRIBUTES + errorIndex; + // Can't encrypt if the session's symmetric algorithm is TPM_ALG_NULL + if(session->symmetric.algorithm == TPM_ALG_NULL) + return TPM_RCS_SYMMETRIC + errorIndex; + // All checks passed, so set the index for the session used to encrypt + // a response parameter. + s_encryptSessionIndex = sessionIndex; + } + // At last process audit. + if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, audit)) + { + // Audit attribute can only appear in one session. + if(s_auditSessionIndex != UNDEFINED_INDEX) + return TPM_RCS_ATTRIBUTES + errorIndex; + // An audit session can not be policy session. + if(HandleGetType(s_sessionHandles[sessionIndex]) + == TPM_HT_POLICY_SESSION) + return TPM_RCS_ATTRIBUTES + errorIndex; + // 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(!IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, auditReset) + && session->attributes.isAudit == SET) + { + // Not first use or reset. If auditExlusive is SET, then this + // session must be the current exclusive session. + if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, auditExclusive) + && 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; + } + command->sessionNum = sessionIndex; + return TPM_RC_SUCCESS; +} +/* 6.4.4.13 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 + ) +{ + // If NV is unavailable, and current cycle state recorded in NV is not + // SU_NONE_VALUE, refuse to check any authorization because we would + // not be able to handle a DA failure. + if(!NV_IS_AVAILABLE && NV_IS_ORDERLY) + return g_NvStatus; + // Check if DA info needs to be updated in NV. + if(s_DAPendingOnNV) + { + // If NV is accessible, + RETURN_IF_NV_IS_NOT_AVAILABLE; + // ... write the pending DA data and proceed. + NV_SYNC_PERSISTENT(lockOutAuthEnabled); + NV_SYNC_PERSISTENT(failedTries); + s_DAPendingOnNV = FALSE; + } + // 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; +#if USE_DA_USED + // If the daUsed flag is not SET, then no DA validation until the + // daUsed state is written to NV + if(!g_daUsed) + { + RETURN_IF_NV_IS_NOT_AVAILABLE; + g_daUsed = TRUE; + gp.orderlyState = SU_DA_USED_VALUE; + NV_SYNC_PERSISTENT(orderlyState); + return TPM_RC_RETRY; + } +#endif + } + return TPM_RC_SUCCESS; +} +/* 6.4.4.14 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( + COMMAND *command, // IN: primary parsing structure + UINT32 sessionIndex // IN: index of session to be processed + ) +{ + TPM_RC result = TPM_RC_SUCCESS; + SESSION *session = NULL; + TPM_HANDLE sessionHandle = s_sessionHandles[sessionIndex]; + TPM_HANDLE associatedHandle = s_associatedHandles[sessionIndex]; + TPM_HT sessionHandleType = HandleGetType(sessionHandle); + BOOL authUsed; + + pAssert(sessionHandle != TPM_RH_UNASSIGNED); + + // Take care of physical presence + 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(command->index) + && !_plat__PhysicalPresenceAsserted()) + return TPM_RC_PP; + } + if(sessionHandle != TPM_RS_PW) + { + session = SessionGet(sessionHandle); + // Set includeAuth to indicate if DA checking will be required and if the + // authValue will be included in any HMAC. + if(sessionHandleType == TPM_HT_POLICY_SESSION) + { + // For a policy session, will check the DA status of the entity if either + // isAuthValueNeeded or isPasswordNeeded is SET. + session->attributes.includeAuth = + session->attributes.isAuthValueNeeded + || session->attributes.isPasswordNeeded; + } + else + { + // For an HMAC session, need to check unless the session + // is bound. + session->attributes.includeAuth = + !IsSessionBindEntity(s_associatedHandles[sessionIndex], session); + } + authUsed = session->attributes.includeAuth; + } + else + // Password session + authUsed = TRUE; + // If the authorization session is going to use an authValue, then make sure + // that access to that authValue isn't locked out. + if(authUsed) + { + // See if entity is subject to lockout. + if(!IsDAExempted(associatedHandle)) + { + // See if in lockout + result = CheckLockedOut(associatedHandle == TPM_RH_LOCKOUT); + if(result != TPM_RC_SUCCESS) + return result; + } + } + // Policy or HMAC+PW? + if(sessionHandleType != TPM_HT_POLICY_SESSION) + { + // for non-policy session make sure that a policy session is not required + if(IsPolicySessionRequired(command->index, sessionIndex)) + return TPM_RC_AUTH_TYPE; + // The authValue must be available. + // Note: The authValue is going to be "used" even if it is an EmptyAuth. + // and the session is bound. + if(!IsAuthValueAvailable(associatedHandle, command->index, sessionIndex)) + return TPM_RC_AUTH_UNAVAILABLE; + } + else + { + // ... see if the entity has a policy, ... + // Note: IsAuthPolicyAvailable will return FALSE if the sensitive area of the + // object is not loaded + if(!IsAuthPolicyAvailable(associatedHandle, command->index, sessionIndex)) + return TPM_RC_AUTH_UNAVAILABLE; + // ... and check the policy session. + result = CheckPolicyAuthSession(command, sessionIndex); + if(result != TPM_RC_SUCCESS) + return result; + } + // Check authorization according to the type + if((TPM_RS_PW == sessionHandle) || (session->attributes.isPasswordNeeded == SET)) + result = CheckPWAuthSession(sessionIndex); + else + result = CheckSessionHMAC(command, sessionIndex); + // Do processing for PIN Indexes are only three possibilities for 'result' at + // this point: TPM_RC_SUCCESS, TPM_RC_AUTH_FAIL, TPM_RC_BAD_AUTH + // For all these cases, we would have to process a PIN index if the + // authValue of the index was used for authorization. + if((TPM_HT_NV_INDEX == HandleGetType(associatedHandle)) && authUsed) + { + NV_REF locator; + NV_INDEX *nvIndex = NvGetIndexInfo(associatedHandle, &locator); + NV_PIN pinData; + TPMA_NV nvAttributes; + + pAssert(nvIndex != NULL); + nvAttributes = nvIndex->publicArea.attributes; + // If this is a PIN FAIL index and the value has been written + // then we can update the counter (increment or clear) + if(IsNvPinFailIndex(nvAttributes) + && IS_ATTRIBUTE(nvAttributes, TPMA_NV, WRITTEN)) + { + pinData.intVal = NvGetUINT64Data(nvIndex, locator); + if(result != TPM_RC_SUCCESS) + pinData.pin.pinCount++; + else + pinData.pin.pinCount = 0; + NvWriteUINT64Data(nvIndex, pinData.intVal); + } + // If this is a PIN PASS Index, increment if we have used the + // authorization value. + // NOTE: If the counter has already hit the limit, then we + // would not get here because the authorization value would not + // be available and the TPM would have returned before it gets here + else if(IsNvPinPassIndex(nvAttributes) + && IS_ATTRIBUTE(nvAttributes, TPMA_NV, WRITTEN) + && result == TPM_RC_SUCCESS) + { + // If the access is valid, then increment the use counter + pinData.intVal = NvGetUINT64Data(nvIndex, locator); + pinData.pin.pinCount++; + NvWriteUINT64Data(nvIndex, pinData.intVal); + } + } + return result; +} +#if CC_GetCommandAuditDigest +/* 6.4.4.15 CheckCommandAudit() */ +/* This function is called before the command is processed if audit is enabled for the command. It + will check to see if the audit can be performed and will ensure that the cpHash is available for + the audit. */ +/* 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( + COMMAND *command + ) +{ + // 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 + || GetCommandCode(command->index) == TPM_CC_GetCommandAuditDigest) + { + RETURN_IF_NV_IS_NOT_AVAILABLE; + } + // Make sure that the cpHash is computed for the algorithm + ComputeCpHash(command, gp.auditHashAlg); + return TPM_RC_SUCCESS; +} +#endif +/* 6.4.4.16 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( + COMMAND *command // IN: the structure that contains + ) +{ + TPM_RC result; + UINT32 i; + INT32 size = 0; + TPM2B_AUTH extraKey; + UINT32 sessionIndex; + TPM_RC errorIndex; + SESSION *session = NULL; + // Check if a command allows any session in its session area. + if(!IsSessionAllowed(command->index)) + return TPM_RC_AUTH_CONTEXT; + // Default-initialization. + command->sessionNum = 0; + result = RetrieveSessionData(command); + if(result != TPM_RC_SUCCESS) + return result; + // There is no command in the TPM spec that has more handles than + // MAX_SESSION_NUM. + pAssert(command->handleNum <= MAX_SESSION_NUM); + // Associate the session with an authorization handle. + for(i = 0; i < command->handleNum; i++) + { + if(CommandAuthRole(command->index, i) != AUTH_NONE) + { + // If the received session number is less than the number of handles + // 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 and there are only ever + // two values requiring authorization + if(i > (command->sessionNum - 1)) + return TPM_RC_AUTH_MISSING; + // Record the handle associated with the authorization session + s_associatedHandles[i] = command->handles[i]; + } + } + // Consistency checks are done first to avoid authorization failure when the + // command will not be executed anyway. + for(sessionIndex = 0; sessionIndex < command->sessionNum; sessionIndex++) + { + errorIndex = TPM_RC_S + g_rcIndex[sessionIndex]; + // PW session must be an authorization session + if(s_sessionHandles[sessionIndex] == TPM_RS_PW) + { + if(s_associatedHandles[sessionIndex] == TPM_RH_UNASSIGNED) + return TPM_RCS_HANDLE + errorIndex; + // a password session can't be audit, encrypt or decrypt + if(IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, audit) + || IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, encrypt) + || IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, decrypt)) + return TPM_RCS_ATTRIBUTES + errorIndex; + session = NULL; + } + 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_RCS_ATTRIBUTES + errorIndex; + // 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 this session is for auditing, make sure the cpHash is computed. + if(IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, audit)) + ComputeCpHash(command, session->authHashAlg); + } + // if the session has an associated handle, check the authorization + if(s_associatedHandles[sessionIndex] != TPM_RH_UNASSIGNED) + { + result = CheckAuthSession(command, sessionIndex); + if(result != TPM_RC_SUCCESS) + return RcSafeAddToResult(result, errorIndex); + } + else + { + // a session that is not for authorization must either be encrypt, + // decrypt, or audit + if(!IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, audit) + && !IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, encrypt) + && !IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, decrypt)) + return TPM_RCS_ATTRIBUTES + errorIndex; + // no authValue included in any of the HMAC computations + pAssert(session != NULL); + session->attributes.includeAuth = CLEAR; + // check HMAC for encrypt/decrypt/audit only sessions + result = CheckSessionHMAC(command, sessionIndex); + if(result != TPM_RC_SUCCESS) + return RcSafeAddToResult(result, errorIndex); + } + } +#if CC_GetCommandAuditDigest + // Check if the command should be audited. Need to do this before any parameter + // encryption so that the cpHash for the audit is correct + if(CommandAuditIsRequired(command->index)) + { + result = CheckCommandAudit(command); + 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 sessionKey to + // generate encryption key, no matter if the handle is the session bound entity + // or not. + if(s_decryptSessionIndex != UNDEFINED_INDEX) + { + // If this is an authorization session, include the authValue in the + // generation of the decryption key + if(s_associatedHandles[s_decryptSessionIndex] != TPM_RH_UNASSIGNED) + { + EntityGetAuthValue(s_associatedHandles[s_decryptSessionIndex], + &extraKey); + } + else + { + extraKey.b.size = 0; + } + size = DecryptSize(command->index); + result = CryptParameterDecryption(s_sessionHandles[s_decryptSessionIndex], + &s_nonceCaller[s_decryptSessionIndex].b, + command->parameterSize, (UINT16)size, + &extraKey, + command->parameterBuffer); + if(result != TPM_RC_SUCCESS) + return RcSafeAddToResult(result, + TPM_RC_S + g_rcIndex[s_decryptSessionIndex]); + } + return TPM_RC_SUCCESS; +} +/* 6.4.4.17 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 authorization */ +TPM_RC +CheckAuthNoSession( + COMMAND *command // IN: command parsing structure + ) +{ + UINT32 i; + TPM_RC result = TPM_RC_SUCCESS; + // Check if the command requires authorization + for(i = 0; i < command->handleNum; i++) + { + if(CommandAuthRole(command->index, i) != AUTH_NONE) + return TPM_RC_AUTH_MISSING; + } +#if CC_GetCommandAuditDigest + // Check if the command should be audited. + if(CommandAuditIsRequired(command->index)) + { + result = CheckCommandAudit(command); + if(result != TPM_RC_SUCCESS) + return result; + } +#endif + // Initialize number of sessions to be 0 + command->sessionNum = 0; + return TPM_RC_SUCCESS; +} +/* 6.4.5 Response Session Processing */ +/* 6.4.5.1 Introduction */ +/* The following functions build the session area in a response and handle the audit sessions (if + present). */ +/* 6.4.5.2 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 TPM2B_DIGEST * +ComputeRpHash( + COMMAND *command, // IN: command structure + TPM_ALG_ID hashAlg // IN: hash algorithm to compute rpHash + ) +{ + TPM2B_DIGEST *rpHash = GetRpHashPointer(command, hashAlg); + HASH_STATE hashState; + if(rpHash->t.size == 0) + { + // rpHash := hash(responseCode || commandCode || parameters) + // Initiate hash creation. + rpHash->t.size = CryptHashStart(&hashState, hashAlg); + // Add hash constituents. + CryptDigestUpdateInt(&hashState, sizeof(TPM_RC), TPM_RC_SUCCESS); + CryptDigestUpdateInt(&hashState, sizeof(TPM_CC), command->code); + CryptDigestUpdate(&hashState, command->parameterSize, + command->parameterBuffer); + // Complete hash computation. + CryptHashEnd2B(&hashState, &rpHash->b); + } + return rpHash; +} +/* 6.4.5.3 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 = CryptHashGetDigestSize(session->authHashAlg); + // Set the original digest value to be 0. + MemorySet(&session->u2.auditDigest.t.buffer, + 0, + session->u2.auditDigest.t.size); + return; +} +/* 6.4.5.4 UpdateAuditDigest */ +/* Function to update an audit digest */ +static void +UpdateAuditDigest( + COMMAND *command, + TPMI_ALG_HASH hashAlg, + TPM2B_DIGEST *digest + ) +{ + HASH_STATE hashState; + TPM2B_DIGEST *cpHash = GetCpHash(command, hashAlg); + TPM2B_DIGEST *rpHash = ComputeRpHash(command, hashAlg); + // + pAssert(cpHash != NULL); + // digestNew := hash (digestOld || cpHash || rpHash) + // Start hash computation. + digest->t.size = CryptHashStart(&hashState, hashAlg); + // Add old digest. + CryptDigestUpdate2B(&hashState, &digest->b); + // Add cpHash + CryptDigestUpdate2B(&hashState, &cpHash->b); + // Add rpHash + CryptDigestUpdate2B(&hashState, &rpHash->b); + // Finalize the hash. + CryptHashEnd2B(&hashState, &digest->b); +} +/* 6.4.5.5 Audit() */ +/* This function updates the audit digest in an audit session. */ +static void +Audit( + COMMAND *command, // IN: primary control structure + SESSION *auditSession // IN: loaded audit session + ) +{ + UpdateAuditDigest(command, auditSession->authHashAlg, + &auditSession->u2.auditDigest); + return; +} +#if CC_GetCommandAuditDigest +/* 6.4.5.6 CommandAudit() */ +/* This function updates the command audit digest. */ +static void +CommandAudit( + COMMAND *command // IN: + ) +{ + // 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 = CryptHashGetDigestSize(gp.auditHashAlg); + MemorySet(gr.commandAuditDigest.t.buffer, + 0, + gr.commandAuditDigest.t.size); + // Bump the counter and save its value to NV. + gp.auditCounter++; + NV_SYNC_PERSISTENT(auditCounter); + } + UpdateAuditDigest(command, gp.auditHashAlg, &gr.commandAuditDigest); + return; +} +#endif +/* 6.4.5.7 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( + COMMAND *command // IN: primary control structure + ) +{ + UINT32 i; + TPM_HANDLE auditSession = TPM_RH_UNASSIGNED; + // Iterate through sessions + for(i = 0; i < command->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(IS_ATTRIBUTE(s_attributes[i], TPMA_SESSION, audit)) + { + // 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 + || IS_ATTRIBUTE(s_attributes[i], TPMA_SESSION, auditReset)) + { + 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) + { + SET_ATTRIBUTE(s_attributes[i], TPMA_SESSION, auditExclusive); + } + else + { + CLEAR_ATTRIBUTE(s_attributes[i], TPMA_SESSION, auditExclusive); + } + // Extend audit log. + Audit(command, session); + } + } + // 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(command->index)) + { + g_exclusiveAuditSession = TPM_RH_UNASSIGNED; + } + return; +} +/* 6.4.5.8 ComputeResponseHMAC() */ +/* Function to compute HMAC for authorization session in a response. */ +static void +ComputeResponseHMAC( + COMMAND *command, // IN: command structure + UINT32 sessionIndex, // IN: session index to be processed + SESSION *session, // IN: loaded session + TPM2B_DIGEST *hmac // OUT: authHMAC + ) +{ + TPM2B_TYPE(KEY, (sizeof(AUTH_VALUE) * 2)); + TPM2B_KEY key; // HMAC key + BYTE marshalBuffer[sizeof(TPMA_SESSION)]; + BYTE *buffer; + UINT32 marshalSize; + HMAC_STATE hmacState; + TPM2B_DIGEST *rpHash = ComputeRpHash(command, session->authHashAlg); + // Generate HMAC key + MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer)); + // Add the object authValue if required + if(session->attributes.includeAuth == SET) + { + // Note: includeAuth may be SET for a policy that is used in + // UndefineSpaceSpecial(). At this point, the Index has been deleted + // so the includeAuth will have no meaning. However, the + // s_associatedHandles[] value for the session is now set to TPM_RH_NULL so + // this will return the authValue associated with TPM_RH_NULL and that is + // and empty buffer. + TPM2B_AUTH authValue; + // Get the authValue with trailing zeros removed + EntityGetAuthValue(s_associatedHandles[sessionIndex], &authValue); + // Add it to the key + MemoryConcat2B(&key.b, &authValue.b, sizeof(key.t.buffer)); + } + // if the HMAC key size is 0, the response HMAC is computed according to the + // input HMAC + if(key.t.size == 0 + && s_inputAuthValues[sessionIndex].t.size == 0) + { + hmac->t.size = 0; + return; + } + // Start HMAC computation. + hmac->t.size = CryptHmacStart2B(&hmacState, session->authHashAlg, &key.b); + // Add hash components. + CryptDigestUpdate2B(&hmacState.hashState, &rpHash->b); + CryptDigestUpdate2B(&hmacState.hashState, &session->nonceTPM.b); + CryptDigestUpdate2B(&hmacState.hashState, &s_nonceCaller[sessionIndex].b); + // Add session attributes. + buffer = marshalBuffer; + marshalSize = TPMA_SESSION_Marshal(&s_attributes[sessionIndex], &buffer, NULL); + CryptDigestUpdate(&hmacState.hashState, marshalSize, marshalBuffer); + // Finalize HMAC. + CryptHmacEnd2B(&hmacState, &hmac->b); + return; +} +/* 6.4.5.9 UpdateInternalSession() */ +/* Updates internal sessions: */ +/* a) Restarts session time. */ +/* b) Clears a policy session since nonce is rolling. */ +static void +UpdateInternalSession( + SESSION *session, // IN: the session structure + UINT32 i // IN: session number + ) +{ + // 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 + && IS_ATTRIBUTE(s_attributes[i], TPMA_SESSION, continueSession)) + { + // When the nonce rolls it starts a new timing interval for the + // policy session. + SessionResetPolicyData(session); + SessionSetStartTime(session); + } + return; +} +/* 6.4.5.10 BuildSingleResponseAuth() */ +/* Function to compute response HMAC value for a policy or HMAC session. */ +static TPM2B_NONCE * +BuildSingleResponseAuth( + COMMAND *command, // IN: command structure + UINT32 sessionIndex, // IN: session index to be processed + TPM2B_AUTH *auth // OUT: authHMAC + ) +{ + // Fill in policy/HMAC based session response. + SESSION *session = SessionGet(s_sessionHandles[sessionIndex]); + // If the session is a policy session with isPasswordNeeded SET, the + // authorization 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(command, sessionIndex, session, auth); + UpdateInternalSession(session, sessionIndex); + return &session->nonceTPM; +} +/* 6.4.5.11 UpdateAllNonceTPM() */ +/* Updates TPM nonce for all sessions in command. */ +static void +UpdateAllNonceTPM( + COMMAND *command // IN: controlling structure + ) +{ + UINT32 i; + SESSION *session; + for(i = 0; i < command->sessionNum; i++) + { + // If not a PW session, compute the new nonceTPM. + if(s_sessionHandles[i] != TPM_RS_PW) + { + session = SessionGet(s_sessionHandles[i]); + // Update nonceTPM in both internal session and response. + CryptRandomGenerate(session->nonceTPM.t.size, + session->nonceTPM.t.buffer); + } + } + return; +} +/* 6.4.5.12 BuildResponseSession() */ +/* Function to build Session buffer in a response. The authorization data is added to the end of + command->responseBuffer. The size of the authorization area is accumulated in + command->authSize. When this is called, command->responseBuffer is pointing at the next location + in the response buffer to be filled. This is where the authorization sessions will go, if + any. command->parameterSize is the number of bytes that have been marshaled as parameters in the + output buffer. */ +void +BuildResponseSession( + COMMAND *command // IN: structure that has relevant command + // information + ) +{ + pAssert(command->authSize == 0); + // Reset the parameter buffer to point to the start of the parameters so that + // there is a starting point for any rpHash that might be generated and so there + // is a place where parameter encryption would start + command->parameterBuffer = command->responseBuffer - command->parameterSize; + // Session nonces should be updated before parameter encryption + if(command->tag == TPM_ST_SESSIONS) + { + UpdateAllNonceTPM(command); + // 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 sessionKey to generate + // encryption key, no matter if the handle is the session bound entity + // or not. The authValue is added to sessionKey only when the authValue + // is available. + if(s_encryptSessionIndex != UNDEFINED_INDEX) + { + UINT32 size; + TPM2B_AUTH extraKey; + extraKey.b.size = 0; + // If this is an authorization session, include the authValue in the + // generation of the encryption key + if(s_associatedHandles[s_encryptSessionIndex] != TPM_RH_UNASSIGNED) + { + EntityGetAuthValue(s_associatedHandles[s_encryptSessionIndex], + &extraKey); + } + size = EncryptSize(command->index); + CryptParameterEncryption(s_sessionHandles[s_encryptSessionIndex], + &s_nonceCaller[s_encryptSessionIndex].b, + (UINT16)size, + &extraKey, + command->parameterBuffer); + } + } + // Audit sessions should be processed regardless of the tag because + // a command with no session may cause a change of the exclusivity state. + UpdateAuditSessionStatus(command); +#if CC_GetCommandAuditDigest + // Command Audit + if(CommandAuditIsRequired(command->index)) + CommandAudit(command); +#endif + // Process command with sessions. + if(command->tag == TPM_ST_SESSIONS) + { + UINT32 i; + pAssert(command->sessionNum > 0); + // Iterate over each session in the command session area, and create + // corresponding sessions for response. + for(i = 0; i < command->sessionNum; i++) + { + TPM2B_NONCE *nonceTPM; + TPM2B_DIGEST responseAuth; + // 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_sessionHandles[i] == TPM_RS_PW) + { + SET_ATTRIBUTE(s_attributes[i], TPMA_SESSION, continueSession); + responseAuth.t.size = 0; + nonceTPM = (TPM2B_NONCE *)&responseAuth; + } + else + { + // Compute the response HMAC and get a pointer to the nonce used. + // This function will also update the values if needed. Note, the + nonceTPM = BuildSingleResponseAuth(command, i, &responseAuth); + } + command->authSize += TPM2B_NONCE_Marshal(nonceTPM, + &command->responseBuffer, + NULL); + command->authSize += TPMA_SESSION_Marshal(&s_attributes[i], + &command->responseBuffer, + NULL); + command->authSize += TPM2B_DIGEST_Marshal(&responseAuth, + &command->responseBuffer, + NULL); + if(!IS_ATTRIBUTE(s_attributes[i], TPMA_SESSION, continueSession)) + SessionFlush(s_sessionHandles[i]); + } + } + return; +} +/* 6.4.5.13 SessionRemoveAssociationToHandle() */ +/* This function deals with the case where an entity associated with an authorization is deleted + during command processing. The primary use of this is to support UndefineSpaceSpecial(). */ +void +SessionRemoveAssociationToHandle( + TPM_HANDLE handle + ) +{ + UINT32 i; + for(i = 0; i < MAX_SESSION_NUM; i++) + { + if(s_associatedHandles[i] == handle) + { + s_associatedHandles[i] = TPM_RH_NULL; + } + } +} |