/** * @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] ': 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 ': Hash Sequence SHA-256\n"); printf(" -> Data bytes: Enter a byte sequence like '0F56...' for {0x0f, 0x56, ...}\n"); printf("'-b ': 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\n"); printf(" -> Shutdown types: clear [default], state\n"); printf("'-e [hash algorithm] ': 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 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 ': 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 ': PCR allocate SHA-1/256/384\n"); printf(" -> Hash algorithm: Enter hash algorithm like 'sha1', 'sha256', 'sha384'\n"); printf("'-r [hash algorithm] ': 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 Read SHA-256\n"); printf(" -> PCR index: Enter PCR number in hex like '17' for 0x17\n"); printf("'-s [hash algorithm] ': 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 ': Hash SHA-256\n"); printf(" -> Data bytes: Enter a byte sequence like '0F56...' for {0x0f, 0x56, ...}\n"); printf("'-t ': SelfTest\n"); printf(" -> Selftest type: not_full [default], full, incremental\n"); printf("'-T': Get Test Result\n"); printf("'-u ': Startup\n"); printf(" -> Startup types: clear [default], state\n"); printf("'-z ': 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= 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; }