/********************************************************************************/
/*                                                                              */
/*                              TPM TIS I/O					*/
/*                           Written by Ken Goldman                             */
/*                     IBM Thomas J. Watson Research Center                     */
/*            $Id: tpm_tis.c 4505 2011-03-20 17:43:27Z kgoldman $		*/
/*                                                                              */
/* (c) Copyright IBM Corporation 2006, 2011.					*/
/*										*/
/* All rights reserved.								*/
/* 										*/
/* Redistribution and use in source and binary forms, with or without		*/
/* modification, are permitted provided that the following conditions are	*/
/* met:										*/
/* 										*/
/* Redistributions of source code must retain the above copyright notice,	*/
/* this list of conditions and the following disclaimer.			*/
/* 										*/
/* 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.		*/
/* 										*/
/* Neither the names of the IBM Corporation nor the names of its		*/
/* contributors may be used to endorse or promote products derived from		*/
/* this software without specific prior written permission.			*/
/* 										*/
/* 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.		*/
/********************************************************************************/

/*
  This file implements the TPM TIS interface out-of-band commands.
*/

#include <stdio.h>
#include <string.h>

#include "tpm12/tpm_crypto.h"
#include "tpm12/tpm_cryptoh.h"
#include "tpm12/tpm_debug.h"
#include "tpm_error.h"
#include "tpm12/tpm_digest.h"
#include "tpm12/tpm_global.h"
#include "tpm12/tpm_pcr.h"
#include "tpm12/tpm_permanent.h"
#include "tpm12/tpm_platform.h"
#include "tpm12/tpm_process.h"
#include "tpm12/tpm_transport.h"

#include "tpm_tis.h"

/* These commands do not test for TPM_ContinueSelfTest:

   The following operations MUST be available after TPM_Init and before a call to
   TPM_ContinueSelfTest 1.9. TPM_HASH_START / TPM_HASH_DATA / TPM_HASH_END */

/* TPM_IO_Hash_Start() implements the LPC bus TPM_HASH_START command
 */
TPM_RESULT TPM12_IO_Hash_Start(void)
{
    TPM_RESULT		rc = 0;
    tpm_state_t		*tpm_state = tpm_instances[0];	/* TPM global state */
    TPM_PCRVALUE	zeroPCR;
    TPM_BOOL		altered = FALSE;	/* TRUE if the structure has been changed */

    printf("\nTPM_IO_Hash_Start: Ordinal Entry\n");
    TPM_Digest_Init(zeroPCR);

    /* Prior to receiving the TPM_HASH_START command the TPM must have received a TPM_Startup
       command. If the TPM receives a TPM_HASH_START after a TPM_Init but before a startup command,
       the TPM treats this as an error */
    if (rc == 0) {
	if (tpm_state->tpm_stany_flags.postInitialise) {
	    printf("TPM_IO_Hash_Start: Error, postInitialise is TRUE\n");
	    rc = TPM_INVALID_POSTINIT;
	}
    }
    /* NOTE: Done by caller
       (1) If no TPM_ACCESS_x.activeLocality field is set, the TPM MUST set the
       TPM_ACCESS_x.activeLocality field to indicate Locality 4. Any currently executing command
       MUST be aborted per and subject to Section 11.2.3. */
    /* NOTE: Done by caller
       (2) If TPM_ACCESS_x.activeLocality is set, and if the TPM_ACCESS_x.activeLocality field is
       not 4, the TPM MUST ignore this command. */
    /* NOTE: Done by caller
       (3) The TPM MUST clear the write FIFO. */
    if (rc == 0) {
	/* (4) If there is an exclusive transport session, it MUST be invalidated. */
	if (tpm_state->tpm_stany_flags.transportExclusive != 0)	{ /* active exclusive */
	    rc = TPM_TransportSessions_TerminateHandle
		 (tpm_state->tpm_stclear_data.transSessions,
		  tpm_state->tpm_stany_flags.transportExclusive,
		  &(tpm_state->tpm_stany_flags.transportExclusive));
	}
    }
    if (rc == 0) {
	/* (5) Set the TPM_PERMANENT_FLAGS->tpmEstablished flag to TRUE (1). Note: see description of
	   Bit Field: tpmEstablishment in 11.2.11 Access Register. */
	TPM_SetCapability_Flag(&altered,
			       &(tpm_state->tpm_permanent_flags.tpmEstablished),
			       TRUE);
    }
    if (rc == 0) {
	/* (6) Set the TPM_STANY_FLAGS->TOSPresent flag to TRUE (1). */
	tpm_state->tpm_stany_flags.TOSPresent = TRUE;
	/* (7) Set PCRs per column labeled TPM_HASH_START in Table 5: PCR Initial and Reset Values.
	   (PCR 17-22 to zero, others unchanged */
	TPM_PCR_Store(tpm_state->tpm_stclear_data.PCRS, 17, zeroPCR);
	TPM_PCR_Store(tpm_state->tpm_stclear_data.PCRS, 18, zeroPCR);
	TPM_PCR_Store(tpm_state->tpm_stclear_data.PCRS, 19, zeroPCR);
	TPM_PCR_Store(tpm_state->tpm_stclear_data.PCRS, 20, zeroPCR);
	TPM_PCR_Store(tpm_state->tpm_stclear_data.PCRS, 21, zeroPCR);
	TPM_PCR_Store(tpm_state->tpm_stclear_data.PCRS, 22, zeroPCR);
	/* (8) Ignore any data component of the TPM_HASH_START LPC command. */
	/* (9) Allocate tempLocation of a size required to perform the SHA-1 operation. */
	/* (10) Initialize tempLocation per SHA-1. */
	rc = TPM_SHA1InitCmd(&(tpm_state->sha1_context_tis));
    }
    rc = TPM_PermanentAll_NVStore(tpm_state,
				  altered,
				  rc);
    /*
       1) Upon any error in the above steps the TPM:
       a) MUST enter Failure Mode.
       NOTE: Done by caller
       b) MUST release locality.
    */
    if (rc != 0) {
	printf("TPM_IO_Hash_Start: Error, (fatal)\n");
	printf("  TPM_IO_Hash_Start: Set testState to %u \n", TPM_TEST_STATE_FAILURE);
	tpm_state->testState = TPM_TEST_STATE_FAILURE;
    }
    return rc;
}

/* TPM_IO_Hash_Data() implements the LPC bus TPM_HASH_DATA command
 */
TPM_RESULT TPM12_IO_Hash_Data(const unsigned char *data,
			      uint32_t data_length)
{
    TPM_RESULT 		rc = 0;
    tpm_state_t		*tpm_state = tpm_instances[0];	/* TPM global state */

    printf("\nTPM_IO_Hash_Data: Ordinal Entry\n");
    /* (1) Transform tempLocation per SHA-1 with data received from this command. */
    /* (2) Repeat for each TPM_HASH_DATA LPC command received. */
    if (rc == 0) {
	if (tpm_state->sha1_context_tis == NULL) {
	    printf("TPM_IO_Hash_Data: Error, no existing SHA1 thread\n");
	    rc = TPM_SHA_THREAD;
	}
    }
    if (rc == 0) {
	rc = TPM_SHA1UpdateCmd(tpm_state->sha1_context_tis, data, data_length);
    }
    /*
       1) Upon any error in the above steps the TPM:
       a) MUST enter Failure Mode.
       NOTE: Done by caller
       b) MUST release locality.
    */
    if (rc != 0) {
	printf("TPM_IO_Hash_Data: Error, (fatal)\n");
	printf("  TPM_IO_Hash_Data: Set testState to %u \n", TPM_TEST_STATE_FAILURE);
	tpm_state->testState = TPM_TEST_STATE_FAILURE;
    }
    return rc;
}

/* TPM_IO_Hash_End() implements the LPC bus TPM_HASH_END command
 */
TPM_RESULT TPM12_IO_Hash_End(void)
{
    TPM_RESULT 		rc = 0;
    TPM_PCRVALUE	zeroPCR;
    TPM_DIGEST 		extendDigest;
    tpm_state_t		*tpm_state = tpm_instances[0];	/* TPM global state */

    printf("\nTPM_IO_Hash_End: Ordinal Entry\n");
    if (rc == 0) {
	if (tpm_state->sha1_context_tis == NULL) {
	    printf("TPM_IO_Hash_End: Error, no existing SHA1 thread\n");
	    rc = TPM_SHA_THREAD;
	}
    }
    /* (1) Ignore any data sent with the command. */
    /* (2) Perform finalize operation on tempLocation per SHA-1. */
    if (rc == 0) {
	rc = TPM_SHA1FinalCmd(extendDigest, tpm_state->sha1_context_tis);
    }
    /* (3) Perform an “extend” operation, as defined in the TPM_Extend command, of the value within
       tempLocation into PCR[Locality 4]. */
    if (rc == 0) {
	/* In the previous line above, “PCR[Locality 4]” within and before the SHA-1 function is
	   TPM_PCRVALUE = 0 (i.e., 20 bytes of all zeros). */
	TPM_Digest_Init(zeroPCR);	/* initial PCR value */
	/* PCR[Locality 4] = SHA-1( PCR[Locality 4] || tempLoc) */
	rc = TPM_SHA1(tpm_state->tpm_stclear_data.PCRS[TPM_LOCALITY_4_PCR],
		      TPM_DIGEST_SIZE, zeroPCR,
		      TPM_DIGEST_SIZE, extendDigest,
		      0, NULL);
    }
    /* NOTE: Done by caller
       (4) Clear TPM_ACCESS_x.activeLocality for Locality 4. */
    /*
       1) Upon any error in the above steps the TPM:
       a) MUST enter Failure Mode.
       NOTE: Done by caller
       b) MUST release locality.
    */
    if (rc != 0) {
	printf("TPM_IO_Hash_End: Error, (fatal)\n");
	printf("  TPM_IO_Hash_End: Set testState to %u \n", TPM_TEST_STATE_FAILURE);
	tpm_state->testState = TPM_TEST_STATE_FAILURE;
    }
    TPM_SHA1Delete(&(tpm_state->sha1_context_tis));
    return rc;
}

TPM_RESULT TPM12_IO_TpmEstablished_Get(TPM_BOOL *tpmEstablished)
{
    TPM_RESULT 		rc = 0;
    tpm_state_t		*tpm_state = tpm_instances[0];	/* TPM global state */

    if (rc == 0) {
	*tpmEstablished = tpm_state->tpm_permanent_flags.tpmEstablished;
    }
    /*
       1) Upon any error in the above steps the TPM:
       a) MUST enter Failure Mode.
       NOTE: Done by caller
       b) MUST release locality.
    */
    if (rc != 0) {
	printf("TPM_IO_TpmEstablished_Get: Error, (fatal)\n");
	printf("  TPM_IO_TpmEstablished_Get: Set testState to %u \n", TPM_TEST_STATE_FAILURE);
	tpm_state->testState = TPM_TEST_STATE_FAILURE;
    }
    return 0;
}

TPM_RESULT TPM12_IO_TpmEstablished_Reset(void)
{
    TPM_RESULT          returnCode = 0;
    tpm_state_t		*tpm_state = tpm_instances[0];	/* TPM global state */
    TPM_BOOL		writeAllNV = FALSE;	/* flag to write back flags */

    if (returnCode == TPM_SUCCESS) {
        returnCode = TPM_IO_GetLocality(&(tpm_state->tpm_stany_flags.localityModifier),
                                        tpm_state->tpm_number);
    }

    /* 1. Validate the assertion of locality 3 or locality 4 */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Locality_Check(TPM_LOC_THREE | TPM_LOC_FOUR,  /* BYTE bitmap */
					tpm_state->tpm_stany_flags.localityModifier);
    }
    /* 2. Set TPM_PERMANENT_FLAGS -> tpmEstablished to FALSE */
    if (returnCode == TPM_SUCCESS) {
	TPM_SetCapability_Flag(&writeAllNV,					/* altered */
			       &(tpm_state->tpm_permanent_flags.tpmEstablished),	/* flag */
			       FALSE);							/* value */

    }
    /* Store the permanent flags back to NVRAM */
    returnCode = TPM_PermanentAll_NVStore(tpm_state,
					  writeAllNV,
					  returnCode);

    return returnCode;
}
