Files
wlan-ap/feeds/edgecore/eltt2/src/eltt2.c
John Crispin 267147f971 edgecore: add TPM support
Signed-off-by: John Crispin <john@phrozen.org>
2023-04-10 14:25:48 +02:00

1743 lines
61 KiB
C

/**
* @brief Embedded Linux TPM Toolbox 2 (ELTT2)
* @details eltt2.c implements some basic methods to communicate with the Infineon TPM 2.0 without the TDDL lib.
* @file eltt2.c
* @copyright Copyright (c) 2014 - 2017 Infineon Technologies AG ( www.infineon.com ).\n
* All rights reserved.\n
* \n
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:\n
* \n
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.\n
* 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.\n
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.\n
* \n
* 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 "eltt2.h"
/**
* @brief Main entry point of the application.
* @details Handles the command line input and starts the communication with the TPM.
* @param [in] argc Counter for input parameters.
* @param [in] **argv Input parameters.
* @return One of the listed return codes, the TPM return code or the error code stored in the global errno system variable.
* @retval EXIT_SUCCESS In case of success.
* @retval ERR_BAD_CMD In case an invalid command line option.
* @retval value of errno In case of memory allocation error.
* @retval tpmtool_transmit All error codes from tpmtool_transmit.
* @retval return_error_handling All error codes from return_error_handling.
* @retval response_print All error codes from response_print.
* @retval create_hash_sequence All error codes from create_hash_sequence.
* @retval hexstr_to_bytearray All error codes from hexstr_to_bytearray.
* @retval pcr_extend All error codes from pcr_extend.
* @retval get_random All error codes from get_random.
* @retval pcr_read All error codes from pcr_read.
* @retval create_hash All error codes from create_hash.
* @retval pcr_reset All error codes from pcr_reset.
*/
int main(int argc, char **argv)
{
// ---------- Local declarations ----------
int ret_val = EXIT_SUCCESS; // Return value.
uint8_t *tpm_response_buf = NULL; // Buffer for TPM response.
ssize_t tpm_response_buf_size = 0; // Size of tpm_response_buf.
int i = 0; // Command line parsing counter.
int option = 0; // Command line option.
uint8_t *input_bytes = NULL; // Custom command bytes for transmit in case of command line options -b and -E.
size_t input_bytes_size = 0; // Size of input_bytes.
int no_transmission = 0; // Flag to skip the transmission call, e.g. in case of command line option -h.
int tpm_error = 0; // Flag to indicate whether a TPM response has returned a TPM error code or not.
hash_algo_enum hash_algo = ALG_NULL; // Variable to indicate the selected hash algorithm.
// ---------- Program flow ----------
printf("\n");
do // Begin of DO WHILE(FALSE) for error handling.
{
// ---------- Allocate memory for buffer containing TPM response ----------
tpm_response_buf_size = TPM_RESP_MAX_SIZE;
tpm_response_buf = malloc(tpm_response_buf_size);
MALLOC_ERROR_CHECK(tpm_response_buf);
memset(tpm_response_buf, 0xFF, tpm_response_buf_size);
// ---------- Check for command line parameters ----------
if (1 == argc)
{
fprintf(stderr, "ELTT needs an option. Use '-h' for displaying help.\n");
ret_val = ERR_BAD_CMD;
break;
}
// ---------- Command line parsing with getopt ----------
opterr = 0; // Disable getopt error messages in case of unknown parameters; we want to use our own error messages.
// Loop through parameters with getopt.
while (-1 != (option = getopt(argc, argv, "cgvhTa:A:b:d:e:E:G:l:r:R:s:S:t:u:z:")))
{
switch (option)
{
case 'a': // TPM2_HashSequenceStart SHA-1/256/384
case 'A': // TPM2_HashSequenceStart SHA-256
HASH_ALG_PARSER('a', 3);
ret_val = create_hash_sequence(optarg, hash_algo, tpm_response_buf, &tpm_response_buf_size);
break;
case 'b': // Enter your own command bytes
// Allocate the input buffer for hexstr_to_bytearray and tpmtool_transmit.
input_bytes_size = strlen(optarg) / HEX_BYTE_STRING_LENGTH + strlen(optarg) % HEX_BYTE_STRING_LENGTH; // 2 characters == 1 byte => size of input command bytes: length of input string / 2.
input_bytes = malloc(input_bytes_size);
MALLOC_ERROR_CHECK(input_bytes);
memset(input_bytes, 0xFF, input_bytes_size);
// Convert the command line input to bytes.
ret_val = hexstr_to_bytearray(optarg, input_bytes, input_bytes_size);
RET_VAL_CHECK(ret_val);
// Send bytes to TPM.
ret_val = tpmtool_transmit(input_bytes, input_bytes_size, tpm_response_buf, &tpm_response_buf_size);
break;
case 'c': // TPM_CC_ReadClock
ret_val = tpmtool_transmit(tpm_cc_readclock, sizeof(tpm_cc_readclock), tpm_response_buf, &tpm_response_buf_size);
break;
case 'd': // TPM_CC_Shutdown
if (0 == strcasecmp(optarg, "clear"))
{
ret_val = tpmtool_transmit(tpm_cc_shutdown_clear, sizeof(tpm_cc_shutdown_clear), tpm_response_buf, &tpm_response_buf_size);
}
else if (0 == strcasecmp(optarg, "state"))
{
ret_val = tpmtool_transmit(tpm_cc_shutdown_state, sizeof(tpm_cc_shutdown_state), tpm_response_buf, &tpm_response_buf_size);
}
else
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Unknown option. Use '-h' for more information.\n");
}
break;
case 'e': // PCR_Extend SHA-1/256/384
case 'E': // PCR_Extend SHA-256
if (4 > argc)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "The command '-%c' needs minimum two arguments. Use '-h' for more information.\n", option);
// Set the argument count to the next option for error handling.
optind += 2;
break;
}
HASH_ALG_PARSER('e', 4);
// Allocate the input buffer for pcr_extend and tpmtool_transmit.
if (ALG_SHA1 == hash_algo)
{
input_bytes_size = sizeof(tpm2_pcr_extend) + TPM_SHA1_DIGEST_SIZE;
}
else if (ALG_SHA256 == hash_algo)
{
input_bytes_size = sizeof(tpm2_pcr_extend) + TPM_SHA256_DIGEST_SIZE;
}
else
{
input_bytes_size = sizeof(tpm2_pcr_extend) + TPM_SHA384_DIGEST_SIZE;
}
input_bytes = malloc(input_bytes_size);
MALLOC_ERROR_CHECK(input_bytes);
memset(input_bytes, 0, input_bytes_size);
// Create PCR_Extend TPM request.
ret_val = pcr_extend(optarg, argv[optind], input_bytes, input_bytes_size, hash_algo);
// Set the argument count to the next option for error handling.
optind++;
RET_VAL_CHECK(ret_val);
// Send bytes to TPM.
ret_val = tpmtool_transmit(input_bytes, input_bytes_size, tpm_response_buf, &tpm_response_buf_size);
break;
case 'g': // TPM_CC_GetCapability
ret_val = tpmtool_transmit(tpm2_getcapability_fixed, sizeof(tpm2_getcapability_fixed), tpm_response_buf, &tpm_response_buf_size);
break;
case 'v': // TPM_CC_GetCapability
ret_val = tpmtool_transmit(tpm2_getcapability_var, sizeof(tpm2_getcapability_var), tpm_response_buf, &tpm_response_buf_size);
break;
case 'G': // TPM_CC_GetRandom
// Allocate the input buffer for get_random and tpmtool_transmit.
input_bytes_size = (sizeof(tpm2_getrandom));
input_bytes = malloc(input_bytes_size);
MALLOC_ERROR_CHECK(input_bytes);
memset(input_bytes, 0, input_bytes_size);
// Create GetRandom TPM request.
ret_val = get_random(optarg, input_bytes);
RET_VAL_CHECK(ret_val);
// Send bytes to TPM.
ret_val = tpmtool_transmit(input_bytes, input_bytes_size, tpm_response_buf, &tpm_response_buf_size);
break;
case 'h': // Help
print_help();
// Set flag to skip any TPM transmission
no_transmission = 1;
break;
case 'l': // PCR_Allocate SHA-1/256/384
HASH_ALG_PARSER('l', -1);
// Allocate the input buffer for pcr_read and tpmtool_transmit.
input_bytes_size = sizeof(tpm2_pcr_allocate);
input_bytes = malloc(input_bytes_size);
MALLOC_ERROR_CHECK(input_bytes);
memset(input_bytes, 0, input_bytes_size);
// Create PCR_Allocate TPM request.
ret_val = pcr_allocate(input_bytes, hash_algo);
RET_VAL_CHECK(ret_val);
// Send bytes to TPM.
ret_val = tpmtool_transmit(input_bytes, input_bytes_size, tpm_response_buf, &tpm_response_buf_size);
break;
case 'r': // PCR_Read SHA-1/256/384
case 'R': // PCR_Read SHA-256
HASH_ALG_PARSER('r', 3);
// Allocate the input buffer for pcr_read and tpmtool_transmit.
input_bytes_size = sizeof(tpm2_pcr_read);
input_bytes = malloc(input_bytes_size);
MALLOC_ERROR_CHECK(input_bytes);
memset(input_bytes, 0, input_bytes_size);
// Create PCR_Read TPM request.
ret_val = pcr_read(optarg, input_bytes, hash_algo);
RET_VAL_CHECK(ret_val);
// Send bytes to TPM.
ret_val = tpmtool_transmit(input_bytes, input_bytes_size, tpm_response_buf, &tpm_response_buf_size);
break;
case 's': // Hash SHA-1/256/384
case 'S': // Hash SHA-256
HASH_ALG_PARSER('s', 3);
// Allocate the input buffer for create_hash and tpmtool_transmit.
input_bytes_size = strlen(optarg) / HEX_BYTE_STRING_LENGTH + strlen(optarg) % HEX_BYTE_STRING_LENGTH + sizeof(tpm2_hash);
input_bytes = malloc(input_bytes_size);
MALLOC_ERROR_CHECK(input_bytes);
memset(input_bytes, 0, input_bytes_size);
// Create Hash TPM request.
ret_val = create_hash(optarg, hash_algo, input_bytes, input_bytes_size);
RET_VAL_CHECK(ret_val);
// Send bytes to TPM.
ret_val = tpmtool_transmit(input_bytes, input_bytes_size, tpm_response_buf, &tpm_response_buf_size);
break;
case 't': // TPM2_SelfTest
if (0 == strcasecmp(optarg, "not_full"))
{
ret_val = tpmtool_transmit(tpm2_self_test, sizeof(tpm2_self_test), tpm_response_buf, &tpm_response_buf_size);
}
else if (0 == strcasecmp(optarg, "full"))
{
ret_val = tpmtool_transmit(tpm2_self_test_full, sizeof(tpm2_self_test_full), tpm_response_buf, &tpm_response_buf_size);
}
else if (0 == strcasecmp(optarg, "incremental"))
{
ret_val = tpmtool_transmit(tpm2_self_test_incremental, sizeof(tpm2_self_test_incremental), tpm_response_buf, &tpm_response_buf_size);
}
else
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Unknown option. Use '-h' for more information.\n");
}
break;
case 'T': // TPM_CC_GetTestResult
ret_val = tpmtool_transmit(tpm_cc_get_test_result, sizeof(tpm_cc_get_test_result), tpm_response_buf, &tpm_response_buf_size);
break;
case 'u': // TPM2_Startup
if (0 == strcasecmp(optarg, "clear"))
{
ret_val = tpmtool_transmit(tpm2_startup_clear, sizeof(tpm2_startup_clear), tpm_response_buf, &tpm_response_buf_size);
}
else if (0 == strcasecmp(optarg, "state"))
{
ret_val = tpmtool_transmit(tpm2_startup_state, sizeof(tpm2_startup_state), tpm_response_buf, &tpm_response_buf_size);
}
else
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Unknown option. Use '-h' for more information.\n");
}
break;
case 'z': // PCR_Reset
// Allocate the input buffer for pcr_reset and tpmtool_transmit
input_bytes_size = sizeof(tpm2_pcr_reset);
input_bytes = malloc(input_bytes_size);
MALLOC_ERROR_CHECK(input_bytes);
memset(input_bytes, 0, input_bytes_size);
// Create PCR_Reset TPM request.
ret_val = pcr_reset(optarg, input_bytes);
RET_VAL_CHECK(ret_val);
// Send bytes to TPM.
ret_val = tpmtool_transmit(input_bytes, input_bytes_size, tpm_response_buf, &tpm_response_buf_size);
break;
default:
if ('a' == optopt || 'A' == optopt || 'b' == optopt || 'e' == optopt || 'E' == optopt || 'G' == optopt ||
'l' == optopt || 'r' == optopt || 'R' == optopt || 's' == optopt || 'S' == optopt || 'z' == optopt)
{
// Error output if arguments are missing.
fprintf(stderr, "Option '-%c' requires additional arguments. Use '-h' for more information.\n", optopt);
ret_val = ERR_BAD_CMD;
}
else if ('d' == optopt)
{
// TPM shutdown default option without parameter (default is tpm_cc_shutdown_clear).
ret_val = tpmtool_transmit(tpm_cc_shutdown_clear, sizeof(tpm_cc_shutdown_clear), tpm_response_buf, &tpm_response_buf_size);
option='d'; // for response_print handler
}
else if ('t' == optopt)
{
// TPM shutdown default option without parameter (default is tpm2_self_test).
ret_val = tpmtool_transmit(tpm2_self_test, sizeof(tpm2_self_test), tpm_response_buf, &tpm_response_buf_size);
option='t'; // for response_print handler
}
else if ('u' == optopt)
{
// TPM startup default option without parameter (default is tpm2_startup_clear).
ret_val = tpmtool_transmit(tpm2_startup_clear, sizeof(tpm2_startup_clear), tpm_response_buf, &tpm_response_buf_size);
option='u'; // for response_print handler
}
else if (isprint(optopt))
{
// Unknown parameter.
fprintf(stderr, "Unknown option '-%c'. Use '-h' for more information.\n", optopt);
ret_val = ERR_BAD_CMD;
}
else
{
// Non-printable character.
fprintf(stderr, "Invalid command line character. Use '-h' for more information.\n");
ret_val = ERR_BAD_CMD;
}
break;
} // End of switch.
// ---------- Output and error handling ----------
// Check for transmission errors or skipped transmission.
if (EXIT_SUCCESS != ret_val || 1 == no_transmission)
{
// Exit command line parameter parsing loop.
break;
}
// Transmission has been successful, now get TPM return code from TPM response.
ret_val = return_error_handling(tpm_response_buf);
if (EXIT_SUCCESS != ret_val) // Check for errors
{
// Set flag to indicate a TPM error.
tpm_error = 1;
// Go out of command line parameter parsing loop.
break;
}
// Print TPM response
ret_val = response_print(tpm_response_buf, tpm_response_buf_size, option);
RET_VAL_CHECK(ret_val);
// Free memory for next command line option
MEMSET_FREE(input_bytes, input_bytes_size);
} // End of while (command line parameter parsing loop).
// If no error has occurred so far, handle remaining unknown parameters, if present.
RET_VAL_CHECK(ret_val); // If we do not check and break here in case of an error, we would override the previous error
for (i = optind; i < argc; i++)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Non-option argument '%s'. Use '-h' for more information.\n", argv[i]);
}
} while (0); // End of DO WHILE FALSE loop.
// Check for non-TPM error.
if (EXIT_SUCCESS != ret_val && 1 != tpm_error)
{
fprintf(stderr, "Unexpected error: 0x%08X\n", ret_val);
}
// Map TPM return value 0x100 (TPM_RC_INITIALIZE) to 0x101 (TPM_RC_FAILURE), since in case you
// run ELTT 2 in a python script only the lowest byte of the return code is actually being returned.
// But since the lowest byte of 0x100 is 0x00 (== TPM_RC_SUCCESS), python would not be able to
// distinguish between 0x000 and 0x100 as return code, therefore we need the mapping.
if (TPM_RC_INITIALIZE == ret_val)
{
ret_val = TPM_RC_FAILURE;
}
// ---------- Cleanup ----------
MEMSET_FREE(tpm_response_buf, tpm_response_buf_size);
MEMSET_FREE(input_bytes, input_bytes_size);
printf("\n");
return ret_val;
}
int tpmtool_transmit(const uint8_t *buf, ssize_t length, uint8_t *response, ssize_t *resp_length)
{
// ---------- Transmit command given in buf to device with handle given in dev_tpm ----------
int ret_val = EXIT_SUCCESS; // Return value.
int dev_tpm = -1; // TPM device handle.
ssize_t transmit_size = 0; // Amount of bytes sent to / received from the TPM.
do
{
// Check input parameters.
NULL_POINTER_CHECK(buf);
NULL_POINTER_CHECK(response);
NULL_POINTER_CHECK(resp_length);
if (0 >= length)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'length' must be larger than 0.");
break;
}
if (TPM_REQ_MAX_SIZE < length)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'length' must be smaller than or equal to %u.", TPM_REQ_MAX_SIZE);
break;
}
if (TPM_CMD_HEADER_SIZE >= *resp_length)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter '*resp_length' must be at least %u.", TPM_CMD_HEADER_SIZE);
break;
}
if (TPM_RESP_MAX_SIZE < *resp_length)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter '*resp_length' must be smaller than or equal to %u.", TPM_RESP_MAX_SIZE);
break;
}
memset(response, 0, *resp_length);
// ---------- Open TPM device ----------
dev_tpm = open("/dev/tpm0", O_RDWR);
if (-1 == dev_tpm)
{
ret_val = errno;
fprintf(stderr, "Error opening the device.\n");
break;
}
// Send request data to TPM.
transmit_size = write(dev_tpm, buf, length);
if (transmit_size == ERR_COMMUNICATION || length != transmit_size)
{
ret_val = errno;
fprintf(stderr, "Error sending request to TPM.\n");
break;
}
// Read the TPM response header.
transmit_size = read(dev_tpm, response, TPM_RESP_MAX_SIZE);
if (transmit_size == ERR_COMMUNICATION)
{
ret_val = errno;
fprintf(stderr, "Error reading response from TPM.\n");
break;
}
// Update response buffer length with value of data length returned by TPM.
*resp_length = transmit_size;
} while (0);
// ---------- Close TPM device ----------
if (-1 != dev_tpm)
{
// Close file handle.
close(dev_tpm);
// Invalidate file handle.
dev_tpm = -1;
}
return ret_val;
}
static int return_error_handling(uint8_t *response_buf)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint64_t tpm_rc = 0; // Return code from TPM header.
do
{
NULL_POINTER_CHECK(response_buf);
ret_val = buf_to_uint64(response_buf, 6, sizeof(uint32_t), &tpm_rc, TPM_RESP_MAX_SIZE); // TPM Return code type is 32 bit unsigned int!
RET_VAL_CHECK(ret_val);
// Assign TPM return code to ret_val.
ret_val = tpm_rc;
if (TPM_RC_SUCCESS != tpm_rc)
{
fprintf(stderr, "TPM Error 0x%.8" PRIx64 ": ", tpm_rc);
}
// Handle some known TPM return codes.
switch (tpm_rc)
{
case TPM_RC_SUCCESS: // 0x00
break;
case TPM_RC_BAD_TAG: // 0x1E
fprintf(stderr, "The tag value sent to for a command is invalid.\n");
break;
case TPM_RC_SIZE: // 0x95
fprintf(stderr, "Structure is the wrong size.\n");
break;
case TPM_RC_INITIALIZE: // 0x100
fprintf(stderr, "TPM not initialized.\n");
break;
case TPM_RC_LOCALITY: // 0x907
fprintf(stderr, "Bad locality.\n");
break;
default:
fprintf(stderr, "See TPM Library Specification for more information.\n");
break;
}
} while (0);
return ret_val;
}
static int response_print(uint8_t *response_buf, size_t resp_size, int option)
{
int ret_val = EXIT_SUCCESS; // Return value.
do
{
NULL_POINTER_CHECK(response_buf);
if (0 >= resp_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'resp_size' must be larger than 0.");
break;
}
if (TPM_RESP_MAX_SIZE < resp_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'resp_size' must be smaller than or equal to %u.\n", TPM_RESP_MAX_SIZE);
break;
}
switch (option)
{
case 'a':
case 'A': // Print the returned hash number.
ret_val = print_response_buf(response_buf, resp_size, PRINT_RESPONSE_HASH_WITHOUT_HEADER, PRINT_RESPONSE_HASH);
break;
case 'b': // Print the response value in hex.
case 'e':
case 'E': // Print the PCR extend response.
ret_val = print_response_buf(response_buf, resp_size, PRINT_RESPONSE_WITH_HEADER, PRINT_RESPONSE_HEADERBLOCKS);
break;
case 'c': // Print the TPM clock values.
ret_val = print_clock_info(response_buf);
break;
case 'd': // Print "Shutdown works as expected."
printf("Shutdown works as expected.\n");
break;
case 'g': // Print the fixed capability flags.
ret_val = print_capability_flags(response_buf, PT_FIXED_SELECTOR);
break;
case 'v': // Print the variable capability flags.
ret_val = print_capability_flags(response_buf, PT_VAR_SELECTOR);
break;
case 'G': // Print the returned random value in hex.
printf("Random value:\n");
ret_val = print_response_buf(response_buf, resp_size, PRINT_RESPONSE_WITHOUT_HEADER, PRINT_RESPONSE_HEX_BLOCK);
break;
case 'r':
case 'R': // Print the response value in hex.
ret_val = print_response_buf(response_buf, resp_size, PRINT_RESPONSE_PCR_WITHOUT_HEADER, PRINT_RESPONSE_CLEAR);
break;
case 's':
case 'S': // Print the returned hash number.
ret_val = print_response_buf(response_buf, resp_size, PRINT_RESPONSE_WITHOUT_HEADER, PRINT_RESPONSE_HASH);
break;
case 't': // Print "Successfully tested. Works as expected."
printf("Successfully tested. Works as expected.\n");
break;
case 'T': // Print response value hex without the header.
printf("Test Result: ");
ret_val = print_response_buf(response_buf, resp_size, PRINT_RESPONSE_WITHOUT_HEADER, PRINT_RESPONSE_CLEAR);
break;
case 'u': // Print "Startup works as expected."
printf("Startup works as expected.\n");
break;
default: // Print "Done."
printf("Done.\n");
break;
}
printf("\n");
} while (0);
return ret_val;
}
static int print_response_buf(uint8_t *response_buf, size_t resp_size, uint32_t offset, int format)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint32_t i = 0; // Loop variable.
uint64_t data_size = 0; // Size of response data.
do
{
NULL_POINTER_CHECK(response_buf);
if (0 >= resp_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'resp_size' must be larger than 0.");
break;
}
if (TPM_RESP_MAX_SIZE < resp_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'resp_size' must be smaller than or equal to %u.\n", TPM_RESP_MAX_SIZE);
break;
}
if (resp_size <= offset)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Offset %u cannot be equal or larger than input buffer size %zu.\n", offset, resp_size);
break;
}
switch (format)
{
case PRINT_RESPONSE_CLEAR:
for (i = 0; i < resp_size - offset; i++)
{
printf("%02X ", response_buf[i + offset]);
}
break;
case PRINT_RESPONSE_HEADERBLOCKS:
if (TPM_CMD_HEADER_SIZE > resp_size)
{
ret_val = EINVAL;
fprintf(stderr, "Response size is too small.\n");
break;
}
printf("TPM Response:\n");
for (i = 0; i < resp_size - offset; i++)
{
printf("%02X ", response_buf[i + offset]);
if (i == 1) // Bytes 0 and 1 are TPM TAG.
{
printf(" TPM TAG\n");
}
else if (i == 5) // Bytes 2 to 5 are the response length.
{
printf(" RESPONSE SIZE\n");
}
else if (i == 9) // Last 4 bytes in header are the TPM return code.
{
printf(" RETURN CODE\n");
if (i + 1 < resp_size - offset)
{
printf(" Command-specific response Data:\n");
}
}
else if (i >= TPM_CMD_HEADER_SIZE && (i+1) % 10 == 0) // After all header bytes have been printed, start new line after every 10 bytes of data.
{
printf("\n");
}
}
break;
case PRINT_RESPONSE_HEX_BLOCK:
for (i = 0; i < resp_size - offset; i++)
{
if (i % 16 == 0)
{
printf("\n0x%08X: ", i);
}
printf("0x%02X ", response_buf[i + offset]);
}
break;
case PRINT_RESPONSE_HASH:
ret_val = buf_to_uint64(response_buf, offset - sizeof(uint16_t), sizeof(uint16_t), &data_size, resp_size); // Data size actually is only an uint16_t and should always be stored right before the actual data
if (data_size > resp_size - offset)
{
ret_val = EINVAL;
fprintf(stderr, "Invalid response data size.\n");
break;
}
for (i = 0; i < data_size; i++)
{
if (i % 8 == 0)
{
printf("\n0x%08X: ", i);
}
printf("%02X ", response_buf[i + offset]);
}
break;
default:
ret_val = EINVAL;
fprintf(stderr, "Unknown output format.\n");
break;
}
} while (0);
return ret_val;
}
static void print_help()
{
printf("'-a [hash algorithm] <data bytes>': Hash Sequence SHA-1/256/384 [default: SHA-1]\n");
printf(" -> Hash algorithm: Enter hash algorithm like 'sha1', 'sha256', 'sha384'\n");
printf(" Data bytes: Enter a byte sequence like '0F56...' for {0x0f, 0x56, ...}\n");
printf("'-A <data bytes>': Hash Sequence SHA-256\n");
printf(" -> Data bytes: Enter a byte sequence like '0F56...' for {0x0f, 0x56, ...}\n");
printf("'-b <command bytes>': Enter your own TPM command\n");
printf(" -> Command bytes: Enter your command bytes in hex like '0f56...' for {0x0f, 0x56, ...}\n");
printf("'-c': Read Clock\n");
printf("'-d <shutdown type>': Shutdown\n");
printf(" -> Shutdown types: clear [default], state\n");
printf("'-e [hash algorithm] <PCR index> <PCR digest>': PCR Extend SHA-1/256/384 [default: SHA-1]\n");
printf(" -> Hash algorithm: Enter hash algorithm like 'sha1', 'sha256', 'sha384'\n");
printf(" PCR index: Enter the PCR index in hex like '17' for 0x17\n");
printf(" PCR digest: Enter the value to extend the PCR with in hex like '0f56...' for {0x0f, 0x56, ...}\n");
printf("'-E <PCR index> <PCR digest>': PCR Extend SHA-256\n");
printf(" -> PCR index: Enter the PCR index in hex like '17' for 0x17\n");
printf(" PCR digest: Enter the value to extend the PCR with in hex like '0f56...' for {0x0f, 0x56, ...}\n");
printf("'-g': Get fixed capability values\n");
printf("'-v': Get variable capability values\n");
printf("'-G <byte count>': Get Random\n");
printf(" -> Enter desired number of random bytes in hex like '20' for 0x20 (=32 bytes, maximum)\n");
printf("'-h': Help\n");
printf("'-l <hash algorithm>': PCR allocate SHA-1/256/384\n");
printf(" -> Hash algorithm: Enter hash algorithm like 'sha1', 'sha256', 'sha384'\n");
printf("'-r [hash algorithm] <PCR index>': PCR Read SHA-1/256/384 [default: SHA-1]\n");
printf(" -> Hash algorithm: Enter hash algorithm like 'sha1', 'sha256', 'sha384'\n");
printf(" PCR index: Enter PCR number in hex like '17' for 0x17\n");
printf("'-R <PCR index>': PCR Read SHA-256\n");
printf(" -> PCR index: Enter PCR number in hex like '17' for 0x17\n");
printf("'-s [hash algorithm] <data bytes>': Hash SHA-1/256/384 [default: SHA-1]\n");
printf(" -> Hash algorithm: Enter hash algorithm like 'sha1', 'sha256', 'sha384'\n");
printf(" Data bytes: Enter a byte sequence like '0F56...' for {0x0f, 0x56, ...}\n");
printf("'-S <data bytes>': Hash SHA-256\n");
printf(" -> Data bytes: Enter a byte sequence like '0F56...' for {0x0f, 0x56, ...}\n");
printf("'-t <selftest type>': SelfTest\n");
printf(" -> Selftest type: not_full [default], full, incremental\n");
printf("'-T': Get Test Result\n");
printf("'-u <startup type>': Startup\n");
printf(" -> Startup types: clear [default], state\n");
printf("'-z <PCR index>': PCR Reset SHA-1, SHA-256, and SHA-384\n");
printf(" -> PCR index: Enter PCR number in hex like '17' for 0x17\n");
}
static int print_capability_flags(uint8_t *response_buf, uint8_t cap_selector)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint64_t propertyValue = 0; // Value of the read property.
uint64_t propertyKey = 0; // Key of the property.
int tmp = 0; // Temporary buffer.
do
{
NULL_POINTER_CHECK(response_buf);
if(cap_selector == PT_FIXED_SELECTOR)
{
printf("\nTPM capability information of fixed properties:\n");
printf("=========================================================\n");
for(int x = 0x13; x<(TPM_RESP_MAX_SIZE-8); x+=8)
{ //Iterate over each property key/value pair
ret_val = buf_to_uint64(response_buf, x, 4, &propertyKey, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
ret_val = buf_to_uint64(response_buf, x+4, 4, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
switch(propertyKey)
{
case 0x100:
printf("TPM_PT_FAMILY_INDICATOR: %c%c%c%c\n", response_buf[x+4], response_buf[x+5], response_buf[x+6], response_buf[x+7]);
break;
case 0x100+1:
printf("TPM_PT_LEVEL: %" PRIu64 "\n", propertyValue);
break;
case 0x100+2:
printf("TPM_PT_REVISION: %" PRIu64 "\n", propertyValue);
break;
case 0x100+3:
printf("TPM_PT_DAY_OF_YEAR: %" PRIu64 "\n", propertyValue);
break;
case 0x100+4:
printf("TPM_PT_YEAR: %" PRIu64 "\n", propertyValue);
break;
case 0x100+5:
printf("TPM_PT_MANUFACTURER: %c%c%c%c\n", response_buf[x+4], response_buf[x+5], response_buf[x+6], response_buf[x+7]);
break;
case 0x100+6:
printf("TPM_PT_VENDOR_STRING: ");
printf("%c%c%c%c", response_buf[x+4], response_buf[x+5], response_buf[x+6], response_buf[x+7]);
break;
case 0x100+7: // it is assumed that TPM_PT_VENDOR_STRING_2 follows _1
printf("%c%c%c%c", response_buf[x+4], response_buf[x+5], response_buf[x+6], response_buf[x+7]);
break;
case 0x100+8:
printf("%c%c%c%c", response_buf[x+4], response_buf[x+5], response_buf[x+6], response_buf[x+7]);
break;
case 0x100+9:
printf("%c%c%c%c\n", response_buf[x+4], response_buf[x+5], response_buf[x+6], response_buf[x+7]);
break;
case 0x100+10:
printf("TPM_PT_VENDOR_TPM_TYPE: %" PRIu64 "\n", propertyValue);
break;
case 0x100+11:
// special handling for firmware version XX.xx.xxxx.x
ret_val = buf_to_uint64(response_buf, x+4, 2, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf("TPM_PT_FIRMWARE_VERSION: %" PRIu64 "", propertyValue);
ret_val = buf_to_uint64(response_buf, x+6, 2, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf(".%" PRIu64 "", propertyValue);
break;
case 0x100+12:
// special handling for firmware version XX.xx.xxxx.x
ret_val = buf_to_uint64(response_buf, x+4, 2, &propertyValue, TPM_RESP_MAX_SIZE); // Check for output version.
RET_VAL_CHECK(ret_val);
if (2 <= propertyValue) // Infineon custom format
{
ret_val = buf_to_uint64(response_buf, x+5, 2, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf(".%" PRIu64 "", propertyValue);
ret_val = buf_to_uint64(response_buf, x+7, 1, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf(".%" PRIu64 "\n", propertyValue);
}
else
{
ret_val = buf_to_uint64(response_buf, x+4, 4, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf(".%" PRIu64 "\n", propertyValue);
}
break;
case 0x100+24:
printf("\nTPM_PT_MEMORY:\n");
printf("=========================================================\n");
tmp = ((propertyValue & (1<<0)) == 0? 0:1); // Check bit 0 value.
printf("Shared RAM: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
tmp = ((propertyValue & (1<<1)) == 0? 0:1); // Check bit 1 value.
printf("Shared NV: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
tmp = ((propertyValue & (1<<2)) == 0? 0:1); // Check bit 2 value.
printf("Object Copied To Ram: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
//bit 31:3 = reserved
break;
case 0x200:
printf("\nTPM_PT_PERMANENT:\n");
printf("=========================================================\n");
tmp = ((propertyValue & (1<<0)) == 0? 0:1); // Check bit 0 value.
printf("Owner Auth Set: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
tmp = ((propertyValue & (1<<1)) == 0? 0:1); // Check bit 1 value.
printf("Sendorsement Auth Set: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
tmp = ((propertyValue & (1<<2)) == 0? 0:1); // Check bit 2 value.
printf("Lockout Auth Set: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
//bit 7:3 = reserved
tmp = ((propertyValue & (1<<8)) == 0? 0:1); // Check bit 8 value.
printf("Disable Clear: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
tmp = ((propertyValue & (1<<9)) == 0? 0:1); // Check bit 9 value.
printf("In Lockout: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
tmp = ((propertyValue & (1<<10)) == 0? 0:1); // Check bit 10 value.
printf("TPM Generated EPS: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
//bit 31:11 = reserved
break;
default:
// Unknown attribute - ignore
break;
}
}
}
else if (cap_selector == PT_VAR_SELECTOR)
{
NULL_POINTER_CHECK(response_buf);
printf("\nTPM capability information of variable properties:\n");
for(int x = 0x13; x<TPM_RESP_MAX_SIZE-8; x+=8)
{ //Iterate over each property key/value pair
ret_val = buf_to_uint64(response_buf, x, 4, &propertyKey, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
ret_val = buf_to_uint64(response_buf, x+4, 4, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
switch(propertyKey)
{
case 0x201:
printf("\nTPM_PT_STARTUP_CLEAR:\n");
printf("=========================================================\n");
tmp = ((propertyValue & (1<<0)) == 0? 0:1); // Check bit 0 value.
printf("Ph Enable: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
tmp = ((propertyValue & (1<<1)) == 0? 0:1); // Check bit 1 value.
printf("Sh Enable: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
tmp = ((propertyValue & (1<<2)) == 0? 0:1); // Check bit 2 value.
printf("Eh Enable: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
//bit 30:3 = reserved
tmp = ((propertyValue & (1U<<31)) == 0? 0:1); // Check bit 31 value.
printf("Orderly: %i %s", (tmp), ((tmp)? "SET\n" : "CLEAR\n"));
break;
default:
// Unknown attribute - ignore
break;
}
}
}
} while (0);
return ret_val;
}
static int print_clock_info(uint8_t *response_buf)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint64_t propertyValue = 0; // Value of the read property.
uint64_t tmp_value = 0; // Helper variable for calculating actual values.
uint64_t sec = 0; // Value for seconds.
uint64_t tmp = 0; // buf_to_uint64 return value.
do
{
NULL_POINTER_CHECK(response_buf);
printf("\nClock info:\n");
printf("=========================================================\n");
ret_val = buf_to_uint64(response_buf, TPM_CMD_HEADER_SIZE, 8, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf("Time since the last TPM_Init:\n%" PRIu64 " ms = ", propertyValue);
sec = propertyValue / MILISECOND_TO_SECOND;
tmp_value = sec / YEAR_SECONDS;
printf("%" PRIu64 " y, ", tmp_value);
tmp_value = (sec % YEAR_SECONDS) / DAY_SECONDS;
printf("%" PRIu64 " d, ", tmp_value);
tmp_value = ((sec % YEAR_SECONDS) % DAY_SECONDS) / HOUR_SECONDS;
printf("%" PRIu64 " h, ", tmp_value);
tmp_value = (((sec % YEAR_SECONDS) % DAY_SECONDS) % HOUR_SECONDS) / MINUTE_SECONDS;
printf("%" PRIu64 " min, ", tmp_value);
tmp_value = (((sec % YEAR_SECONDS) % DAY_SECONDS) % HOUR_SECONDS) % MINUTE_SECONDS;
printf("%" PRIu64 " s, ", tmp_value);
tmp_value = propertyValue % MILISECOND_TO_SECOND;
printf("%" PRIu64 " ms\n\n", tmp_value);
ret_val = buf_to_uint64(response_buf, 18, 8, &propertyValue, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf("Time during which the TPM has been powered:\n%" PRIu64 " ms = ", propertyValue);
sec = propertyValue / MILISECOND_TO_SECOND;
tmp_value = sec / YEAR_SECONDS;
printf("%" PRIu64 " y, ", tmp_value);
tmp_value = (sec % YEAR_SECONDS) / DAY_SECONDS;
printf("%" PRIu64 " d, ", tmp_value);
tmp_value = ((sec % YEAR_SECONDS) % DAY_SECONDS) / HOUR_SECONDS;
printf("%" PRIu64 " h, ", tmp_value);
tmp_value = (((sec % YEAR_SECONDS) % DAY_SECONDS) % HOUR_SECONDS) / MINUTE_SECONDS;
printf("%" PRIu64 " min, ", tmp_value);
tmp_value = (((sec % YEAR_SECONDS) % DAY_SECONDS) % HOUR_SECONDS) % MINUTE_SECONDS;
printf("%" PRIu64 " s, ", tmp_value);
tmp_value = propertyValue % MILISECOND_TO_SECOND;
printf("%" PRIu64 " ms\n\n", tmp_value);
ret_val = buf_to_uint64(response_buf, 26, 4, &tmp, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf("TPM Reset since the last TPM2_Clear: %" PRIu64 "\n", tmp);
ret_val = buf_to_uint64(response_buf, 30, 4, &tmp, TPM_RESP_MAX_SIZE);
RET_VAL_CHECK(ret_val);
printf("Number of times that TPM2_Shutdown: %" PRIu64 "\n", tmp);
printf("Safe: %i = %s", response_buf[34], (response_buf[34]? "Yes\n" : "No\n"));
} while (0);
return ret_val;
}
static int buf_to_uint64(uint8_t *input_buffer, uint32_t offset, uint32_t length, uint64_t *output_value, uint32_t input_buffer_size)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint32_t i = 0; // Loop variable.
uint64_t tmp = 0; // Temporary variable for value calculation.
do
{
NULL_POINTER_CHECK(input_buffer);
NULL_POINTER_CHECK(output_value);
*output_value = 0;
if (offset >= input_buffer_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'input_buffer_size' must be larger than %u.\n", offset);
break;
}
if (INT_MAX < input_buffer_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'input_buffer_size' must be smaller or equal to %u.\n", INT_MAX);
break;
}
if (0 >= length)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'length' must be larger than 0.\n");
break;
}
if (length > input_buffer_size - offset)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'length' must be smaller or equal to %i.\n", input_buffer_size - offset);
break;
}
if (sizeof(uint64_t) < length)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Input buffer length remaining from offset must be smaller than %zu.\n", sizeof(uint64_t));
break;
}
for (i = 0; i < length; i++)
{
tmp = (tmp << 8) + input_buffer[offset + i];
}
*output_value = tmp;
} while (0);
return ret_val;
}
static int hexstr_to_bytearray(char *byte_string, uint8_t *byte_values, size_t byte_values_size)
{
int ret_val = EXIT_SUCCESS; // Return value.
char hex_byte[3] = {0}; // Temporary buffer for input bytes.
char* invalidChars = NULL; // Pointer to target buffer where method stores invalid characters.
uint32_t i = 0; // Loop variable.
uint32_t unStrLen = 0; // Temporary store for byte string length.
do
{
NULL_POINTER_CHECK(byte_string);
NULL_POINTER_CHECK(byte_values);
if (0 >= byte_values_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'byte_values_size' must be larger than 0.\n");
break;
}
if (INT_MAX < byte_values_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'byte_values_size' must be smaller or equal to %u.\n", INT_MAX);
break;
}
memset(byte_values, 0, byte_values_size);
unStrLen = strlen(byte_string);
if ((unStrLen / HEX_BYTE_STRING_LENGTH + unStrLen % HEX_BYTE_STRING_LENGTH) > (uint32_t)byte_values_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Input character string is too long for output buffer.\n");
break;
}
// Loop "byte-wise" through string
for (i = 0; i < unStrLen; i += HEX_BYTE_STRING_LENGTH)
{
// Split input string into "bytes"
if (1 == strlen(byte_string + i))
{
// Assemble a single digit in the hex byte string.
hex_byte[0] = byte_string[i];
hex_byte[1] = '\0';
hex_byte[2] = '\0';
}
else
{
// Assemble a digit pair in the hex byte string.
hex_byte[0] = byte_string[i];
hex_byte[1] = byte_string[i + 1];
hex_byte[2] = '\0';
}
// Convert the hex string to an integer.
errno = 0;
byte_values[i / HEX_BYTE_STRING_LENGTH] = (uint8_t)strtoul(hex_byte, &invalidChars, 16);
if (0 != errno)
{
ret_val = errno;
fprintf(stderr, "Error parsing string.\n");
break;
}
if ('\0' != *invalidChars)
{
ret_val = EINVAL;
fprintf(stderr, "Invalid character(s) '%s' while trying to parse '%s' to a byte array.\n", invalidChars, byte_string);
break;
}
}
} while (0);
return ret_val;
}
static int int_to_bytearray(uint64_t input, uint32_t input_size, uint8_t *output_byte)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint32_t i; // For-while-loop counter.
do
{
NULL_POINTER_CHECK(output_byte);
if (0 >= input_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'input_size' must be larger than 0.\n");
break;
}
if (sizeof(uint64_t) < input_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'input_size' must be smaller or equal to %zu.\n", sizeof(uint64_t));
break;
}
for (i = 0; i < input_size; i++)
{
output_byte[i] = input >> ((input_size - 1 - i) * 8);
}
} while (0);
return ret_val;
}
static int get_random(char *data_length_string, uint8_t *response_buf)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint8_t bytes_requested = 0; // Amount of random bytes requested by the user.
size_t byte_string_size = 0; // Size of user input.
do
{
NULL_POINTER_CHECK(data_length_string);
NULL_POINTER_CHECK(response_buf);
// Get length of command line input.
byte_string_size = strlen(data_length_string) / HEX_BYTE_STRING_LENGTH + strlen(data_length_string) % HEX_BYTE_STRING_LENGTH;
if (1 != byte_string_size)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Enter a single hex value (2 characters) without leading '0x'.\n");
break;
}
// Convert the command line input string for requested random data length to byte.
ret_val = hexstr_to_bytearray(data_length_string, &bytes_requested, 1);
RET_VAL_CHECK(ret_val);
if (32 < bytes_requested || 0 == bytes_requested)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Enter a hex value between 0x01 and 0x20.\n");
break;
}
// Copy command bytes.
memcpy(response_buf, tpm2_getrandom, sizeof(tpm2_getrandom));
// Store amount of requested bytes at the correct byte index in the command byte stream.
response_buf[sizeof(tpm2_getrandom) - 1] = bytes_requested;
} while (0);
return ret_val;
}
static int create_hash(char *data_string, hash_algo_enum hash_algo, uint8_t *hash_cmd_buf, uint32_t hash_cmd_buf_size)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint32_t offset = 0; // Helper offset for generating command request.
uint16_t data_string_size = 0; // Size of user input data.
const uint8_t *tpm_hash_alg = NULL; // Pointer to hash algorithm identifier.
do
{
NULL_POINTER_CHECK(data_string);
NULL_POINTER_CHECK(hash_cmd_buf);
if (TPM_REQ_MAX_SIZE < hash_cmd_buf_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'hash_cmd_buf_size' must be smaller or equal to %u.\n", TPM_REQ_MAX_SIZE);
break;
}
if (sizeof(tpm2_hash) > hash_cmd_buf_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'hash_cmd_buf_size' must be at least %zu.\n", sizeof(tpm2_hash));
break;
}
data_string_size = strlen(data_string) / HEX_BYTE_STRING_LENGTH + strlen(data_string) % HEX_BYTE_STRING_LENGTH;
if (0 == data_string_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. data_string is empty.\n");
break;
}
if (hash_cmd_buf_size - sizeof(tpm2_hash) < data_string_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Input data size must be smaller or equal to %zu.\n", hash_cmd_buf_size - sizeof(tpm2_hash));
break;
}
memset(hash_cmd_buf, 0, hash_cmd_buf_size);
// Copy basic command bytes.
memcpy(hash_cmd_buf, tpm2_hash, sizeof(tpm2_hash));
// Set hash algorithm, command and data sizes depending on user input option at the correct byte index in the command byte stream.
if (ALG_SHA1 == hash_algo)
{
tpm_hash_alg = sha1_alg;
printf("\nTPM2_Hash of '%s' with SHA-1:\n", data_string);
}
else if (ALG_SHA256 == hash_algo)
{
tpm_hash_alg = sha256_alg;
printf("\nTPM2_Hash of '%s' with SHA-256:\n", data_string);
}
else
{
tpm_hash_alg = sha384_alg;
printf("\nTPM2_Hash of '%s' with SHA-384:\n", data_string);
}
offset = TPM_CMD_SIZE_OFFSET;
ret_val = int_to_bytearray(sizeof(tpm2_hash) + data_string_size, sizeof(uint32_t), hash_cmd_buf + offset);
RET_VAL_CHECK(ret_val);
offset = TPM_CMD_HEADER_SIZE;
ret_val = int_to_bytearray(data_string_size, sizeof(data_string_size), hash_cmd_buf + offset);
RET_VAL_CHECK(ret_val);
offset += sizeof(data_string_size);
// Copy hash data to TPM request.
ret_val = hexstr_to_bytearray(data_string, hash_cmd_buf + offset, hash_cmd_buf_size - offset);
RET_VAL_CHECK(ret_val);
offset += data_string_size;
// Set hash algorithm and hierarchy.
memcpy(hash_cmd_buf + offset, tpm_hash_alg, sizeof(sha1_alg));
offset += sizeof(sha1_alg);
memcpy(hash_cmd_buf + offset, tpm_cc_hash_hierarchy, sizeof(tpm_cc_hash_hierarchy));
} while (0);
return ret_val;
}
static int create_hash_sequence(char *data_string, hash_algo_enum hash_algo, uint8_t *tpm_response_buf, ssize_t *tpm_response_buf_size)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint16_t data_string_bytes_size = 0; // Size of user input data string in bytes.
uint8_t *data_string_bytes = NULL; // Buffer for user input data string as bytes.
uint32_t update_request_size = 0; // Size of user input string.
uint16_t transfer_bytes = 0; // Amount of bytes to be transmitted to the TPM.
uint16_t remaining_bytes = 0; // Amount of bytes not yet transmitted to the TPM.
uint32_t offset = 0; // Helper offset for generating command request.
uint64_t tpm_rc = TPM_RC_SUCCESS; // TPM return code.
uint8_t *update_request = NULL; // Buffer for update sequence command.
uint8_t sequence_handle[4]; // Buffer for sequence handle.
ssize_t original_response_buf_size = 0; // Backup of the original response buffer size.
// Minimum success response buffer size (TPM command header + sequence handle)
ssize_t minimum_response_buf_size = TPM_CMD_HEADER_SIZE + sizeof(sequence_handle);
do
{
NULL_POINTER_CHECK(tpm_response_buf);
NULL_POINTER_CHECK(data_string);
NULL_POINTER_CHECK(tpm_response_buf_size);
memset(tpm_response_buf, 0, *tpm_response_buf_size);
memset(sequence_handle, 0, 4);
if (TPM_RESP_MAX_SIZE < *tpm_response_buf_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter '*tpm_response_buf_size' must be smaller or equal to %u.\n", TPM_RESP_MAX_SIZE);
break;
}
if (minimum_response_buf_size > *tpm_response_buf_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter '*tpm_response_buf_size' must be at least %zu.\n", minimum_response_buf_size);
break;
}
original_response_buf_size = *tpm_response_buf_size;
// Set hash algorithm depending on user input option at the correct byte index in the command byte stream.
if (ALG_SHA1 == hash_algo)
{
printf("\nTPM2_HashSequenceStart of '%s' with SHA-1:\n", data_string);
memcpy(tpm2_hash_sequence_start + 12, sha1_alg, sizeof(sha1_alg));
}
else if (ALG_SHA256 == hash_algo)
{
printf("\nTPM2_HashSequenceStart of '%s' with SHA-256:\n", data_string);
memcpy(tpm2_hash_sequence_start + 12, sha256_alg, sizeof(sha256_alg));
}
else
{
printf("\nTPM2_HashSequenceStart of '%s' with SHA-384:\n", data_string);
memcpy(tpm2_hash_sequence_start + 12, sha384_alg, sizeof(sha384_alg));
}
// Send the TPM2_HashSequenceStart command to the TPM.
ret_val = tpmtool_transmit(tpm2_hash_sequence_start, sizeof(tpm2_hash_sequence_start), tpm_response_buf, tpm_response_buf_size);
RET_VAL_CHECK(ret_val);
// Print the TPM response.
NULL_POINTER_CHECK(tpm_response_buf_size);
ret_val = print_response_buf(tpm_response_buf, (size_t)*tpm_response_buf_size, PRINT_RESPONSE_WITH_HEADER, PRINT_RESPONSE_HEADERBLOCKS);
RET_VAL_CHECK(ret_val);
// Abort in case of TPM error.
ret_val = buf_to_uint64(tpm_response_buf, 6, 4, &tpm_rc, *tpm_response_buf_size);
RET_VAL_CHECK(ret_val);
if (TPM_RC_SUCCESS != tpm_rc)
{
break;
}
// Get sequence handle from TPM response.
memcpy(sequence_handle, tpm_response_buf + TPM_CMD_HEADER_SIZE, sizeof(sequence_handle));
// Calculate byte sizes of hash input string.
data_string_bytes_size = strlen(data_string) / HEX_BYTE_STRING_LENGTH + strlen(data_string) % HEX_BYTE_STRING_LENGTH;
// Allocate memory for conversion of hash input string.
data_string_bytes = malloc(data_string_bytes_size);
MALLOC_ERROR_CHECK(data_string_bytes);
memset(data_string_bytes, 0, data_string_bytes_size);
// Convert hash input string to bytes.
ret_val = hexstr_to_bytearray(data_string, data_string_bytes, data_string_bytes_size);
RET_VAL_CHECK(ret_val);
// If necessary split input bytes into multiple TPM2_SequenceUpdate commands
remaining_bytes = data_string_bytes_size;
while (remaining_bytes > 0)
{
// Calculate amount of bytes to be transmitted next
if (remaining_bytes <= TPM_REQ_MAX_SIZE - sizeof(tpm2_sequence_update))
{
transfer_bytes = remaining_bytes;
}
else
{
transfer_bytes = TPM_REQ_MAX_SIZE - sizeof(tpm2_sequence_update);
}
update_request_size = transfer_bytes + sizeof(tpm2_sequence_update);
// Compose the TPM2_SequenceUpdate command.
update_request = malloc(update_request_size);
MALLOC_ERROR_CHECK(update_request);
memset(update_request, 0, update_request_size);
memcpy(update_request, tpm2_sequence_update, sizeof(tpm2_sequence_update));
offset = TPM_CMD_SIZE_OFFSET;
ret_val = int_to_bytearray(update_request_size, sizeof(update_request_size), update_request + offset);
RET_VAL_CHECK(ret_val);
offset = TPM_CMD_HEADER_SIZE;
memcpy(update_request + offset, sequence_handle, sizeof(sequence_handle));
offset = sizeof(tpm2_sequence_update) - sizeof(transfer_bytes);
ret_val = int_to_bytearray(transfer_bytes, sizeof(transfer_bytes), update_request + offset);
RET_VAL_CHECK(ret_val);
memcpy(update_request + sizeof(tpm2_sequence_update), data_string_bytes + data_string_bytes_size - remaining_bytes, transfer_bytes);
// Send the TPM2_SequenceUpdate command to the TPM.
printf("\n\nTPM2_SequenceUpdate:\n");
*tpm_response_buf_size = original_response_buf_size;
ret_val = tpmtool_transmit(update_request, update_request_size, tpm_response_buf, tpm_response_buf_size);
RET_VAL_CHECK(ret_val);
// Free allocated memory
MEMSET_FREE(update_request, update_request_size);
// Print the TPM response.
NULL_POINTER_CHECK(tpm_response_buf_size);
ret_val = print_response_buf(tpm_response_buf, (size_t)*tpm_response_buf_size, PRINT_RESPONSE_WITH_HEADER, PRINT_RESPONSE_HEADERBLOCKS);
RET_VAL_CHECK(ret_val);
// Abort in case of TPM error.
ret_val = buf_to_uint64(tpm_response_buf, 6, 4, &tpm_rc, *tpm_response_buf_size);
RET_VAL_CHECK(ret_val);
if (TPM_RC_SUCCESS != tpm_rc)
{
break;
}
remaining_bytes = remaining_bytes - transfer_bytes;
}
RET_VAL_CHECK(ret_val);
if (TPM_RC_SUCCESS != tpm_rc)
{
break;
}
// Set the sequence handle in the TPM2_SequenceComplete command.
memcpy(tpm2_sequence_complete + TPM_CMD_HEADER_SIZE, sequence_handle, sizeof(sequence_handle));
// Send the TPM2_SequenceComplete command to the TPM.
printf("\n\nTPM2_SequenceComplete:\n");
*tpm_response_buf_size = original_response_buf_size;
ret_val = tpmtool_transmit(tpm2_sequence_complete, sizeof(tpm2_sequence_complete), tpm_response_buf, tpm_response_buf_size);
RET_VAL_CHECK(ret_val);
// Print the TPM response.
NULL_POINTER_CHECK(tpm_response_buf_size);
ret_val = print_response_buf(tpm_response_buf, (size_t)*tpm_response_buf_size, PRINT_RESPONSE_WITH_HEADER, PRINT_RESPONSE_HEADERBLOCKS);
printf("\n\nHash value extracted from TPM response:\n");
} while (0);
MEMSET_FREE(data_string_bytes, data_string_bytes_size);
MEMSET_FREE(update_request, update_request_size);
return ret_val;
}
static int pcr_extend(char *pcr_index_str, char *pcr_digest_str, uint8_t *pcr_cmd_buf, size_t pcr_cmd_buf_size, hash_algo_enum hash_algo)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint8_t pcr_index = 0; // PCR index user input byte.
uint32_t pcr_digest_size = 0; // Sizeof PCR digest user input.
do
{
NULL_POINTER_CHECK(pcr_index_str);
NULL_POINTER_CHECK(pcr_digest_str);
NULL_POINTER_CHECK(pcr_cmd_buf);
if (TPM_REQ_MAX_SIZE < pcr_cmd_buf_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'pcr_cmd_buf_size' size must be smaller or equal to %u.\n", TPM_REQ_MAX_SIZE);
break;
}
if (sizeof(tpm2_pcr_extend) > pcr_cmd_buf_size)
{
ret_val = EINVAL;
fprintf(stderr, "Bad parameter. Value of parameter 'pcr_cmd_buf_size' must be at least %zu.\n", sizeof(tpm2_pcr_extend));
break;
}
memset(pcr_cmd_buf, 0, pcr_cmd_buf_size);
// Check and convert the command line input (PCR index) to bytes.
if (1 != strlen(pcr_index_str) / HEX_BYTE_STRING_LENGTH + strlen(pcr_index_str) % HEX_BYTE_STRING_LENGTH)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Enter a single hex value (2 characters) without leading '0x'.\n");
break;
}
ret_val = hexstr_to_bytearray(pcr_index_str, &pcr_index, 1);
RET_VAL_CHECK(ret_val);
if (23 < pcr_index)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Enter a hex value between 0x00 and 0x17 in hex without leading '0x'.\n");
break;
}
// Check the command line input (PCR digest).
pcr_digest_size = strlen(pcr_digest_str) / HEX_BYTE_STRING_LENGTH + strlen(pcr_digest_str) % HEX_BYTE_STRING_LENGTH;
if (ALG_SHA1 == hash_algo && TPM_SHA1_DIGEST_SIZE < pcr_digest_size)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Maximum SHA-1 PCR digest size is 20 byte (40 characters), but you entered %u byte.\n", pcr_digest_size);
break;
}
if (ALG_SHA256 == hash_algo && TPM_SHA256_DIGEST_SIZE < pcr_digest_size)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Maximum SHA-256 PCR digest size is 32 byte (64 characters), but you entered %u byte.\n", pcr_digest_size);
break;
}
if (ALG_SHA384 == hash_algo && TPM_SHA384_DIGEST_SIZE < pcr_digest_size)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Maximum SHA-384 PCR digest size is 48 byte (96 characters), but you entered %u byte.\n", pcr_digest_size);
break;
}
// Copy basic command bytes.
memcpy(pcr_cmd_buf, tpm2_pcr_extend, sizeof(tpm2_pcr_extend));
// Store PCR index at the correct byte index in the command byte stream.
pcr_cmd_buf[13] = pcr_index;
// Convert the PCR digest to bytes and store it at the correct byte index in the command byte stream.
ret_val = hexstr_to_bytearray(pcr_digest_str, pcr_cmd_buf + sizeof(tpm2_pcr_extend), pcr_cmd_buf_size - sizeof(tpm2_pcr_extend));
RET_VAL_CHECK(ret_val);
// Set hash algorithm and command length depending on user input option at the correct byte index in the command byte stream.
if (ALG_SHA1 == hash_algo)
{
pcr_cmd_buf[5] = sizeof(tpm2_pcr_extend) + TPM_SHA1_DIGEST_SIZE;
memcpy(pcr_cmd_buf + 31, sha1_alg, sizeof(sha1_alg));
printf("Extend PCR %i (SHA-1) with digest { ", pcr_index);
}
else if (ALG_SHA256 == hash_algo)
{
pcr_cmd_buf[5] = sizeof(tpm2_pcr_extend) + TPM_SHA256_DIGEST_SIZE;
memcpy(pcr_cmd_buf + 31, sha256_alg, sizeof(sha256_alg));
printf("Extend PCR %i (SHA-256) with digest { ", pcr_index);
}
else if (ALG_SHA384 == hash_algo)
{
pcr_cmd_buf[5] = sizeof(tpm2_pcr_extend) + TPM_SHA384_DIGEST_SIZE;
memcpy(pcr_cmd_buf + 31, sha384_alg, sizeof(sha384_alg));
printf("Extend PCR %i (SHA-384) with digest { ", pcr_index);
}
print_response_buf(pcr_cmd_buf, pcr_cmd_buf_size, sizeof(tpm2_pcr_extend), PRINT_RESPONSE_CLEAR);
printf("}:\n");
} while (0);
return ret_val;
}
static int pcr_allocate(uint8_t *pcr_cmd_buf, hash_algo_enum hash_algo)
{
int ret_val = EXIT_SUCCESS; // Return value.
unsigned char set[] = {0xFF, 0xFF, 0xFF};
unsigned char clear[] = {0x00, 0x00, 0x00};
do
{
NULL_POINTER_CHECK(pcr_cmd_buf);
// Copy basic command bytes.
memcpy(pcr_cmd_buf, tpm2_pcr_allocate, sizeof(tpm2_pcr_allocate));
// Set hash algorithm depending on user input option at the correct byte index in the command byte stream.
if (ALG_SHA1 == hash_algo)
{
memcpy(pcr_cmd_buf + 34, set, sizeof(set));
memcpy(pcr_cmd_buf + 40, clear, sizeof(clear));
memcpy(pcr_cmd_buf + 46, clear, sizeof(clear));
printf("PCR allocate SHA-1 bank\n");
}
else if (ALG_SHA256 == hash_algo)
{
memcpy(pcr_cmd_buf + 34, clear, sizeof(clear));
memcpy(pcr_cmd_buf + 40, set, sizeof(set));
memcpy(pcr_cmd_buf + 46, clear, sizeof(clear));
printf("PCR allocate SHA-256 bank\n");
}
else if (ALG_SHA384 == hash_algo)
{
memcpy(pcr_cmd_buf + 34, clear, sizeof(clear));
memcpy(pcr_cmd_buf + 40, clear, sizeof(clear));
memcpy(pcr_cmd_buf + 46, set, sizeof(set));
printf("PCR allocate SHA-384 bank\n");
}
} while (0);
return ret_val;
}
static int pcr_read(char *pcr_index_str, uint8_t *pcr_cmd_buf, hash_algo_enum hash_algo)
{
int ret_val = EXIT_SUCCESS; // Return value.
int pcr_byte_index = 0; // The location for pcr_select on pcr_cmd_buf.
uint8_t pcr_select = 0; // PCR index as mapped bit value.
uint8_t pcr_index = 0; // PCR user input byte.
do
{
NULL_POINTER_CHECK(pcr_index_str);
NULL_POINTER_CHECK(pcr_cmd_buf);
memset(pcr_cmd_buf, 0, 20);
// Convert the command line input to bytes.
if (1 != strlen(pcr_index_str) / HEX_BYTE_STRING_LENGTH + strlen(pcr_index_str) % HEX_BYTE_STRING_LENGTH)
{
// Current implementation only supports selection of one PCR at a time.
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Enter a single hex value (2 characters) without leading '0x'.\n");
break;
}
ret_val = hexstr_to_bytearray(pcr_index_str, &pcr_index, 1);
RET_VAL_CHECK(ret_val);
if (23 < pcr_index)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Enter a hex value between 0x00 and 0x17 in hex without leading '0x'.\n");
break;
}
// Copy basic command bytes.
memcpy(pcr_cmd_buf, tpm2_pcr_read, sizeof(tpm2_pcr_read));
// Calculate the location of PCR index in command byte stream depends on the PCR index value: 0 for PCRs 0-7, 1 for PCRs 8-15 and 2 for PCRs 16-23.
pcr_byte_index = pcr_index / 8;
// Set corresponding bit in PCR selection byte to 1: bit 0 for PCRs 0, 8 or 16, bit 1 for PCRs 1, 9 or 17, ... (etc.), and bit 7 for PCRs 7, 15 or 23.
// Note: This would allow to read multiple PCRs at once. For simplicity reasons, ELTT only supports reading one PCR at a time.
pcr_select = (1 << (pcr_index % 8));
// Store pcr_select at the correct byte index in the command byte stream.
pcr_cmd_buf[17 + pcr_byte_index] = pcr_select;
// Set hash algorithm depending on user input option at the correct byte index in the command byte stream.
if (ALG_SHA1 == hash_algo)
{
memcpy(pcr_cmd_buf + 14, sha1_alg, sizeof(sha1_alg));
printf("Read PCR %i (SHA-1):\n", pcr_index);
}
else if (ALG_SHA256 == hash_algo)
{
memcpy(pcr_cmd_buf + 14, sha256_alg, sizeof(sha256_alg));
printf("Read PCR %i (SHA-256):\n", pcr_index);
}
else if (ALG_SHA384 == hash_algo)
{
memcpy(pcr_cmd_buf + 14, sha384_alg, sizeof(sha384_alg));
printf("Read PCR %i (SHA-384):\n", pcr_index);
}
} while (0);
return ret_val;
}
static int pcr_reset(char *pcr_index_str, uint8_t *pcr_cmd_buf)
{
int ret_val = EXIT_SUCCESS; // Return value.
uint8_t pcr_index = 0; // PCR user input byte.
do
{
NULL_POINTER_CHECK(pcr_index_str);
NULL_POINTER_CHECK(pcr_cmd_buf);
memset(pcr_cmd_buf, 0, 27);
// Convert the command line input to bytes.
if (1 != strlen(pcr_index_str) / HEX_BYTE_STRING_LENGTH + strlen(pcr_index_str) % HEX_BYTE_STRING_LENGTH)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Enter a single hex value (2 characters) without leading '0x'.\n");
break;
}
ret_val = hexstr_to_bytearray(pcr_index_str, &pcr_index, 1);
RET_VAL_CHECK(ret_val);
if (23 < pcr_index)
{
ret_val = ERR_BAD_CMD;
fprintf(stderr, "Bad option. Enter a hex value between 0x00 and 0x17 in hex without leading '0x'.\n");
break;
}
// Copy command bytes.
memcpy(pcr_cmd_buf, tpm2_pcr_reset, sizeof(tpm2_pcr_reset));
// Store pcr_index at the correct byte index in the command byte stream.
pcr_cmd_buf[13] = pcr_index;
printf("Reset PCR %i (SHA-1, SHA-256, and SHA-384):\n", pcr_index);
} while (0);
return ret_val;
}