TCG TIS 1.3 compliant TPM driver to use the TPM as hardware crypto library.

/media/uploads/LordOfDorks/wp_20150409_16_47_21_pro.jpg The TPM 2.0 architecture, commands and structures are defined in the set of 4 Trusted Platform Module Library Specification, Family "2.0" specifications that that can be found at http://www.trustedcomputinggroup.org/resources/tpm_library_specification

The "PC Client Specific TPM Interface Specification (TIS), Version 1.3" that was used for this implementation can be found at http://www.trustedcomputinggroup.org/resources/pc_client_work_group_pc_client_specific_tpm_interface_specification_tis

All the information to get going is in SPITIS_TPM20.h!

TPMCommands.cpp

Committer:
LordOfDorks
Date:
2015-04-11
Revision:
3:4b9ad18eae02
Parent:
1:fd0a59e55a85

File content as of revision 3:4b9ad18eae02:

/* mbed TCG SPI TPM 2.0 TIS 1.3 driver,
 * Copyright (c) 2015, Microsoft Coprporation Inc.
 * by Stefan Thom (LordOfDorks) StefanTh@Microsoft.com, Stefan@ThomsR.Us
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */
 
#include "SPITIS_TPM20.h"
#include "TPM20Tables.h"

TPM_RC
TIS_TPM20::ParseResponseHeader(TPM_ST* tag, UINT32* rspSize, uint8_t** buffer, int32_t* size)
{
    TPM_RC tpmRC = TPM_RC_SUCCESS;

    if((TPM_ST_Unmarshal(tag, buffer, size) != TPM_RC_SUCCESS) ||
       (UINT32_Unmarshal(rspSize, buffer, size) != TPM_RC_SUCCESS) ||
       (TPM_RC_Unmarshal(&tpmRC, buffer, size) != TPM_RC_SUCCESS) ||
       ((*tag != TPM_ST_SESSIONS) && (*tag != TPM_ST_NO_SESSIONS)))
    {
        tpmRC = TPM_RC_FAILURE;
    }

    return tpmRC;
}

#ifndef TPM_TPM_TIS_NO_COMMAND_FILTERING
TIS_TPM20::TIS_RESULT
TIS_TPM20::ApplyFilter(TIS_TPM20::TIS_LOCALITY locality, uint8_t* pbCmd, uint32_t cbCmd)
{
    uint8_t* buffer = pbCmd;
    int32_t size = (int32_t)cbCmd;
    TPM_ST tag = 0;
    UINT32 cmdSize = 0;
    TPM_CC ordinal = 0;
    int32_t handleCount = -1;
    
    // Read the header
    if((TPM_ST_Unmarshal(&tag, &buffer, &size) != TPM_RC_SUCCESS) ||
       (UINT32_Unmarshal(&cmdSize, &buffer, &size) != TPM_RC_SUCCESS) ||
       (TPM_CC_Unmarshal(&ordinal, &buffer, &size) != TPM_RC_SUCCESS) ||
       ((tag != TPM_ST_SESSIONS) && (tag != TPM_ST_NO_SESSIONS)))
    {
        return TIS_SESSION_RESULT_FILTERED;
    }

    // First filter ordinals based on locality
    if(locality == TIS_LOCALITY_0)
    {
        switch(ordinal)
        {
            // We will not allow certain commands from locality 0 - aka externally exposed TPM access
            case TPM_CC_ChangeEPS:
            case TPM_CC_ChangePPS:
            case TPM_CC_Clear:
            case TPM_CC_ClearControl:
            case TPM_CC_ClockSet:
            case TPM_CC_PCR_Allocate:
            case TPM_CC_PCR_SetAuthPolicy:
            case TPM_CC_Shutdown:
                return TIS_SESSION_RESULT_FILTERED;
            default:
                break;
        }
    }
    
    // Lookup the number of handles in the command
    for(uint32_t n = 0; s_ccAttr[n].commandIndex != 0x0000 ;n++)
    {
        if(s_ccAttr[n].commandIndex == ordinal)
        {
            handleCount = s_ccAttr[n].cHandles;
            break;
        }
    }
    
    // Easy elimination of invalid cases
    if((handleCount < 0) || (handleCount > 3))
    {
        return TIS_SESSION_RESULT_FILTERED;
    }
    
    // Read the handles from the command and see if they are allowed
    for(uint32_t n = 0 ;n < handleCount; n++)
    {
        TPM_HANDLE handle = TPM_RH_NULL;
        if(TPM_HANDLE_Unmarshal(&handle, &buffer, &size) != TPM_RC_SUCCESS)
        {
            return TIS_SESSION_RESULT_FILTERED;
        }
        
        if(locality == TIS_LOCALITY_0)
        {
            switch(handle)
            {
                // We will not allow the platform entity to be used from locality 0
                case TPM_RH_PLATFORM:
                    return TIS_SESSION_RESULT_FILTERED;
                default:
                    break;
            }
        }
    }
    
    return TIS_SESSION_RESULT_COMPLETE;
}
#endif

TPM_RC
TIS_TPM20::TPM2_Startup(
    TPM_SU startupType
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_NO_SESSIONS;
    TPM_CC commandCode = TPM_CC_Startup;
    TPM_RC tpmRC = TPM_RC_SUCCESS;

    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    cursor += TPM_SU_Marshal(&startupType, &buffer, &size);
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    // Send the command
    if(SendCommand(pbBuffer, cursor) != TIS_SESSION_RESULT_COMPLETE)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Wait for response
    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }

Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_Startup.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}

uint32_t
TIS_TPM20::TPM2_Shutdown(
    uint16_t shutdownType
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_NO_SESSIONS;
    TPM_CC commandCode = TPM_CC_Shutdown;
    TPM_RC tpmRC = TPM_RC_SUCCESS;

    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    cursor += TPM_SU_Marshal(&shutdownType, &buffer, &size);
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    if(SendCommand(pbBuffer, cursor) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }

Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_Shutdown.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}

uint32_t
TIS_TPM20::TPM2_GetRandom(
    TPM2B_DIGEST* random
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_NO_SESSIONS;
    TPM_CC commandCode = TPM_CC_GetRandom;
    TPM_RC tpmRC = TPM_RC_SUCCESS;

    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    cursor += UINT16_Marshal(&random->t.size, &buffer, &size);
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    if((result = SendCommand(pbBuffer, cursor)) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    if(result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }

    // Copy the parameters out
    if((tpmRC = TPM2B_DIGEST_Unmarshal(random, &buffer, &size)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }
 
 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_GetRandom.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}

uint32_t
TIS_TPM20::TPM2_StirRandom(
    TPM2B_DIGEST* seed
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_NO_SESSIONS;
    TPM_CC commandCode = TPM_CC_StirRandom;
    TPM_RC tpmRC = TPM_RC_SUCCESS;

    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    cursor += TPM2B_DIGEST_Marshal(seed, &buffer, &size);
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    if((result = SendCommand(pbBuffer, cursor)) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    if(result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }

 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_StirRandom.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}

TPM_RC
TIS_TPM20::TPM2_HMAC_Start(
    TPMI_DH_OBJECT handle,
    TPM2B_AUTH* handleAuth,
    TPM2B_AUTH* auth,
    TPMI_ALG_HASH hashAlg,
    TPMI_DH_OBJECT* sequenceHandle
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_SESSIONS;
    TPM_CC commandCode = TPM_CC_HMAC_Start;
    TPM_RC tpmRC = TPM_RC_SUCCESS;
    TPM_HANDLE sessionHandle = TPM_RS_PW;
    TPMA_SESSION sessionAttributes = {0};
    UINT16 nonce = 0;
    UINT32 paramStart = sizeof(sessionHandle) + sizeof(nonce) + sizeof(sessionAttributes) + sizeof(handleAuth->t.size) + handleAuth->t.size;


    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    
    // Object handles
    cursor += TPM_HANDLE_Marshal(&handle, &buffer, &size);

    // Authorization session
    sessionAttributes.continueSession = YES;
    cursor += UINT32_Marshal(&paramStart, &buffer, &size);
    cursor += TPM_HANDLE_Marshal(&sessionHandle, &buffer, &size);
    cursor += UINT16_Marshal(&nonce, &buffer, &size);
    cursor += TPMA_SESSION_Marshal(&sessionAttributes, &buffer, &size);
    cursor += TPM2B_AUTH_Marshal(handleAuth, &buffer, &size);

    // Parameters
    cursor += TPM2B_AUTH_Marshal(auth, &buffer, &size);
    cursor += TPM_ALG_ID_Marshal(&hashAlg, &buffer, &size);
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    if((result = SendCommand(pbBuffer, cursor)) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    if(result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }

    // Copy the parameters out
    if((tpmRC = TPM_HANDLE_Unmarshal(sequenceHandle, &buffer, &size)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }
    
    // Ignore the return session

 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_HMAC_Start.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}

TPM_RC
TIS_TPM20::TPM2_HashSequenceStart(
    TPM2B_AUTH* auth,
    TPMI_ALG_HASH hashAlg,
    TPMI_DH_OBJECT* sequenceHandle
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_NO_SESSIONS;
    TPM_CC commandCode = TPM_CC_HashSequenceStart;
    TPM_RC tpmRC = TPM_RC_SUCCESS;

    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    cursor += TPM2B_AUTH_Marshal(auth, &buffer, &size);
    cursor += TPM_ALG_ID_Marshal(&hashAlg, &buffer, &size);
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    if((result = SendCommand(pbBuffer, cursor)) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    if(result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }

    // Copy the parameters out
    if((tpmRC = TPM_HANDLE_Unmarshal(sequenceHandle, &buffer, &size)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }

 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_HashSequenceStart.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}

TPM_RC
TIS_TPM20::TPM2_SequenceUpdate(
    TPMI_DH_OBJECT sequenceHandle,
    TPM2B_AUTH* sequenceHandleAuth,
    uint8_t* pbData,
    uint16_t cbData
    
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_SESSIONS;
    TPM_CC commandCode = TPM_CC_SequenceUpdate;
    TPM_RC tpmRC = TPM_RC_SUCCESS;
    TPM_HANDLE sessionHandle = TPM_RS_PW;
    TPMA_SESSION sessionAttributes = {0};
    UINT16 nonce = 0;
    UINT32 paramStart = sizeof(TPM_HANDLE) + sizeof(UINT16) + sizeof(UINT8) + sizeof(sequenceHandleAuth->t.size) + sequenceHandleAuth->t.size;
    
    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    
    // Object handles
    cursor += TPM_HANDLE_Marshal(&sequenceHandle, &buffer, &size);

    // Authorization session
    sessionAttributes.continueSession = YES;
    cursor += UINT32_Marshal(&paramStart, &buffer, &size);
    cursor += TPM_HANDLE_Marshal(&sessionHandle, &buffer, &size);
    cursor += UINT16_Marshal(&nonce, &buffer, &size);
    cursor += TPMA_SESSION_Marshal(&sessionAttributes, &buffer, &size);
    cursor += TPM2B_AUTH_Marshal(sequenceHandleAuth, &buffer, &size);
    
    // Parameter
    cursor += UINT16_Marshal(&cbData, &buffer, &size);

    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    cursor += cbData; // Add the trailing data!
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    if((result = SendCommand(pbBuffer, cursor - cbData, pbData, cbData)) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    if(result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }
    
    // Ignore the return session

 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_SequenceUpdate.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}

TPM_RC
TIS_TPM20::TPM2_SequenceComplete(
    TPMI_DH_OBJECT sequenceHandle,
    TPM2B_AUTH* sequenceHandleAuth,
    TPMI_RH_HIERARCHY hierarchy,
    TPM2B_DIGEST* digest,
    TPMT_TK_HASHCHECK* validation
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_SESSIONS;
    TPM_CC commandCode = TPM_CC_SequenceComplete;
    TPM_RC tpmRC = TPM_RC_SUCCESS;
    TPM_HANDLE sessionHandle = TPM_RS_PW;
    TPMA_SESSION sessionAttributes = {0};
    UINT16 nonce = 0;
    UINT32 paramStart = sizeof(sessionHandle) + sizeof(nonce) + sizeof(UINT8) + sizeof(sequenceHandleAuth->t.size) + sequenceHandleAuth->t.size;

    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    
    // Object handles
    cursor += TPM_HANDLE_Marshal(&sequenceHandle, &buffer, &size);

    // Authorization session
    sessionAttributes.continueSession = YES;
    cursor += UINT32_Marshal(&paramStart, &buffer, &size);
    cursor += TPM_HANDLE_Marshal(&sessionHandle, &buffer, &size);
    cursor += UINT16_Marshal(&nonce, &buffer, &size);
    cursor += TPMA_SESSION_Marshal(&sessionAttributes, &buffer, &size);
    cursor += TPM2B_AUTH_Marshal(sequenceHandleAuth, &buffer, &size);

    // Parameters
    cursor += UINT16_Marshal(&nonce, &buffer, &size);
    cursor += TPM_HANDLE_Marshal(&hierarchy, &buffer, &size);
    
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    if((result = SendCommand(pbBuffer, cursor)) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    if(result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    
    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }
    
    // Read the parameter Size
    if((tpmRC = UINT32_Unmarshal(&paramStart, &buffer, &size)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }

    // Copy the parameters out
    if(((tpmRC = TPM2B_DIGEST_Unmarshal(digest, &buffer, &size)) != TPM_RC_SUCCESS) ||
       ((tpmRC = TPMT_TK_HASHCHECK_Unmarshal(validation, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }
    
    // Ignore the return session

 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_SequenceComplete.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}

TPM_RC
TIS_TPM20::TPM2_EventSequenceComplete(
    TPMI_DH_PCR pcrHandle,
    TPM2B_AUTH* pcrAuth,
    TPMI_DH_OBJECT sequenceHandle,
    TPM2B_AUTH* sequenceHandleAuth,
    TPML_DIGEST_VALUES* results
    )
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_SESSIONS;
    TPM_CC commandCode = TPM_CC_EventSequenceComplete;
    TPM_RC tpmRC = TPM_RC_SUCCESS;
    TPM_HANDLE sessionHandle = TPM_RS_PW;
    TPMA_SESSION sessionAttributes = {0};
    UINT16 nonce = 0;
    UINT32 paramStart = sizeof(sessionHandle) + sizeof(nonce) + sizeof(UINT8) + sizeof(pcrAuth->t.size) + pcrAuth->t.size + sizeof(sessionHandle) + sizeof(nonce) + sizeof(UINT8) + sizeof(sequenceHandleAuth->t.size) + sequenceHandleAuth->t.size;

    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    
    // Object handles
    cursor += TPM_HANDLE_Marshal(&pcrHandle, &buffer, &size);
    cursor += TPM_HANDLE_Marshal(&sequenceHandle, &buffer, &size);

    // Authorization sessions
    sessionAttributes.continueSession = YES;
    cursor += UINT32_Marshal(&paramStart, &buffer, &size);
    cursor += TPM_HANDLE_Marshal(&sessionHandle, &buffer, &size);
    cursor += UINT16_Marshal(&nonce, &buffer, &size);
    cursor += TPMA_SESSION_Marshal(&sessionAttributes, &buffer, &size);
    cursor += TPM2B_AUTH_Marshal(pcrAuth, &buffer, &size);

    cursor += TPM_HANDLE_Marshal(&sessionHandle, &buffer, &size);
    cursor += UINT16_Marshal(&nonce, &buffer, &size);
    cursor += TPMA_SESSION_Marshal(&sessionAttributes, &buffer, &size);
    cursor += TPM2B_AUTH_Marshal(sequenceHandleAuth, &buffer, &size);

    // Parameters
    cursor += UINT16_Marshal(&nonce, &buffer, &size);
    
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);

    if((result = SendCommand(pbBuffer, cursor)) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    if(result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Look at the result
    buffer = pbBuffer;
    size = (int32_t)cbRsp;
    tpmRC = TPM_RC_FAILURE;
    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) ||
       ((tpmRC = ParseResponseHeader(&tag, &cursor, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }

    // Read the parameter Size
    if((tpmRC = UINT32_Unmarshal(&paramStart, &buffer, &size)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }

    // Copy the parameters out
    if((tpmRC = TPML_DIGEST_VALUES_Unmarshal(results, &buffer, &size)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }
    
    // Ignore return sessions

 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_EventSequenceComplete.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}


TPM_RC
TIS_TPM20::TPM2_PCR_Read(
    TPML_PCR_SELECTION* pcrSelectionIn,
    uint32_t* pcrUpdateCounter,
    TPML_PCR_SELECTION* pcrSelectionOut,
    TPML_DIGEST* pcrValues)
{
    TIS_TPM20::TIS_RESULT result = TIS_TPM20::TIS_SESSION_RESULT_COMPLETE;
    const uint32_t maxSize = 100; // Educated guess
    uint8_t* pbBuffer = NULL;
    uint32_t cbRsp = 0;
    uint8_t* buffer = NULL;
    int32_t size = 0;
    uint32_t cursor = 0;
    TPM_ST tag = TPM_ST_NO_SESSIONS;
    TPM_CC commandCode = TPM_CC_PCR_Read;
    TPM_RC tpmRC = TPM_RC_SUCCESS;

    if((pbBuffer = new uint8_t[maxSize]) == NULL)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    size = maxSize;
    buffer = pbBuffer;
    
    // Marshal the command
    cursor += TPM_ST_Marshal(&tag, &buffer, &size);
    cursor += UINT32_Marshal(&cursor, &buffer, &size);
    cursor += TPM_CC_Marshal(&commandCode, &buffer, &size);
    cursor += TPML_PCR_SELECTION_Marshal(pcrSelectionIn, &buffer, &size);
    if(size < 0)
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    // Fill in the command size
    buffer = &pbBuffer[sizeof(TPM_ST)];
    size = sizeof(UINT32);
    UINT32_Marshal(&cursor, &buffer, &size);
    
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_PCR_Read()\n\r");
#endif

    if((result = SendCommand(pbBuffer, cursor)) != TIS_SESSION_RESULT_COMPLETE)
    {
        return TPM_RC_FAILURE;
    }

    while((result = RetrieveResponse(pbBuffer, maxSize, &cbRsp)) == TIS_SESSION_RESULT_PENDING)
    {
        wait_us(1000);
    }

    if((result != TIS_TPM20::TIS_SESSION_RESULT_COMPLETE) || (cbRsp < 10))
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Look at the response header
    buffer = pbBuffer;
    size = (INT32)cbRsp;
    if((TPM_ST_Unmarshal(&tag, &buffer, &size) != TPM_RC_SUCCESS) ||
       (UINT32_Unmarshal(&cursor, &buffer, &size) != TPM_RC_SUCCESS) ||
       (TPM_RC_Unmarshal(&tpmRC, &buffer, &size) != TPM_RC_SUCCESS) ||
       ((tag != TPM_ST_SESSIONS) && (tag != TPM_ST_NO_SESSIONS)) ||
       (cursor != cbRsp))
    {
        tpmRC = TPM_RC_FAILURE;
        goto Cleanup;
    }
    if(tpmRC != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }
    
    // Unmarshal the rest
    if(((tpmRC = UINT32_Unmarshal(pcrUpdateCounter, &buffer, &size)) != TPM_RC_SUCCESS) ||
       ((tpmRC = TPML_PCR_SELECTION_Unmarshal(pcrSelectionOut, &buffer, &size)) != TPM_RC_SUCCESS) ||
       ((tpmRC = TPML_DIGEST_Unmarshal(pcrValues, &buffer, &size)) != TPM_RC_SUCCESS))
    {
        goto Cleanup;
    }
    
    // If there is trailing data
    if(size > 0)
    {
        tpmRC = TPM_RC_SIZE;
        goto Cleanup;
    }
    
 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_PCR_Read.ResponseCode = 0x%08x\n\r", tpmRC);
#endif
    if(pbBuffer != NULL)
    {
        delete[] pbBuffer;
        pbBuffer = NULL;
    }
    return tpmRC;
}