//**********************************************************************;
// Copyright (c) 2015, Intel Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//**********************************************************************;

#include <sapi/tpm20.h>
#include "changeEndian.h"
#include "sample.h"
#include <stdio.h>
#include <stdlib.h>

//
// This function is a helper function used to calculate cpHash and rpHash.
//
// NOTE:  for calculating cpHash, set responseCode to TPM_RC_NO_RESPONSE; this
// tells the function to leave it out of the calculation.
//
TPM_RC TpmCalcPHash( TSS2_SYS_CONTEXT *sysContext, TPM_HANDLE handle1, TPM_HANDLE handle2,
    TPMI_ALG_HASH authHash, TPM_RC responseCode, TPM2B_DIGEST *pHash )
{
    TPM_RC rval = TPM_RC_SUCCESS;
    UINT32 i;
    TPM2B_NAME name1;
    TPM2B_NAME name2;
    TPM2B_MAX_BUFFER hashInput; // Byte stream to be hashed to create pHash
    UINT8 *hashInputPtr;
    size_t parametersSize;
    const uint8_t *startParams;
    UINT8 cmdCode[4] = {0,0,0,0};
    UINT8 *cmdCodePtr = &cmdCode[0];
    
    name1.b.size = name2.b.size = 0;
    
    // Calculate pHash
    //

    // Only get names for commands
    if( responseCode == TPM_RC_NO_RESPONSE )
    {
        // Get names for the handles
        rval = TpmHandleToName( handle1, &name1 );
        if( rval != TPM_RC_SUCCESS )
            return rval;
    }

#ifdef DEBUG
    OpenOutFile( &outFp );
    TpmClientPrintf( 0, "\n\nNAME1 = \n" );
    PrintSizedBuffer( &(name1.b) );
    CloseOutFile( &outFp );
#endif
    
    // Only get names for commands
    if( responseCode == TPM_RC_NO_RESPONSE )
    {
        rval = Tss2_Sys_GetCpBuffer( sysContext, &parametersSize, &startParams);
        if( rval != TPM_RC_SUCCESS )
            return rval;

        rval = TpmHandleToName( handle2, &name2 );
        if( rval != TPM_RC_SUCCESS )
            return rval;
    }
    else
    {
        rval = Tss2_Sys_GetRpBuffer( sysContext, &parametersSize, &startParams);
        if( rval != TPM_RC_SUCCESS )
            return rval;
    }
    
#ifdef DEBUG
    OpenOutFile( &outFp );
    TpmClientPrintf( 0, "\n\nNAME2 = \n" );
    PrintSizedBuffer( &(name2.b) );
    CloseOutFile( &outFp );
#endif
    
    // Create pHash input byte stream:  first add response code, if any.
    hashInput.b.size = 0;
    if( responseCode != TPM_RC_NO_RESPONSE )
    {
        hashInputPtr = &( hashInput.t.buffer[hashInput.b.size] );
        *(UINT32 *)hashInputPtr = CHANGE_ENDIAN_DWORD( responseCode );
        hashInput.b.size += 4;
        hashInputPtr += 4;
    }

    // Create pHash input byte stream:  now add command code.
    rval = Tss2_Sys_GetCommandCode( sysContext, &cmdCode );
    if( rval != TPM_RC_SUCCESS )
        return rval;

    hashInputPtr = &( hashInput.t.buffer[hashInput.b.size] );    
    *(UINT32 *)hashInputPtr = CHANGE_ENDIAN_DWORD( *(UINT32 *)cmdCodePtr );
    hashInput.t.size += 4;

    // Create pHash input byte stream:  now add in names for the handles.
    rval = ConcatSizedByteBuffer( &hashInput, &( name1.b ) );
    if( rval != TPM_RC_SUCCESS )
        return rval;
    
    rval = ConcatSizedByteBuffer( &hashInput, &( name2.b ) );
    if( rval != TPM_RC_SUCCESS )
        return rval;

    if( ( hashInput.t.size + parametersSize ) <= sizeof( hashInput.t.buffer ) )
    {
        // Create pHash input byte stream:  now add in parameters byte stream
        for( i = 0; i < parametersSize; i++ )
            hashInput.t.buffer[hashInput.t.size + i ] = startParams[i];
        hashInput.t.size += (UINT16)parametersSize;
    }
    else
    {
        return( APPLICATION_ERROR( TSS2_BASE_RC_INSUFFICIENT_BUFFER ) );

    }
#ifdef DEBUG
    OpenOutFile( &outFp );
    TpmClientPrintf( 0, "\n\nPHASH input bytes= \n" );
    PrintSizedBuffer( &(hashInput.b) );
    CloseOutFile( &outFp );
#endif
    
    // Now hash the whole mess.
    if( hashInput.t.size > sizeof( hashInput.t.buffer ) )
    {
        rval = APPLICATION_ERROR( TSS2_BASE_RC_INSUFFICIENT_BUFFER );
    }
    else
    {
        rval = TpmHash( authHash, hashInput.t.size, &( hashInput.t.buffer[0] ), pHash );
        if( rval != TPM_RC_SUCCESS )
            return rval;
#ifdef DEBUG
        OpenOutFile( &outFp );
        TpmClientPrintf( 0, "\n\nPHASH = " );
        PrintSizedBuffer( &(pHash->b) );
        CloseOutFile( &outFp );
#endif
    }
    
    return rval;
 }
