diff options
Diffstat (limited to 'src/security/tpm/tss')
-rw-r--r-- | src/security/tpm/tss/tcg-1.2/tss.c | 353 | ||||
-rw-r--r-- | src/security/tpm/tss/tcg-1.2/tss_internal.h | 65 | ||||
-rw-r--r-- | src/security/tpm/tss/tcg-1.2/tss_structures.h | 164 | ||||
-rw-r--r-- | src/security/tpm/tss/tcg-2.0/tss.c | 438 | ||||
-rw-r--r-- | src/security/tpm/tss/tcg-2.0/tss_marshaling.c | 559 | ||||
-rw-r--r-- | src/security/tpm/tss/tcg-2.0/tss_marshaling.h | 46 | ||||
-rw-r--r-- | src/security/tpm/tss/tcg-2.0/tss_structures.h | 373 |
7 files changed, 1998 insertions, 0 deletions
diff --git a/src/security/tpm/tss/tcg-1.2/tss.c b/src/security/tpm/tss/tcg-1.2/tss.c new file mode 100644 index 0000000000..b7b2d94930 --- /dev/null +++ b/src/security/tpm/tss/tcg-1.2/tss.c @@ -0,0 +1,353 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* A lightweight TPM command library. + * + * The general idea is that TPM commands are array of bytes whose + * fields are mostly compile-time constant. The goal is to build much + * of the commands at compile time (or build time) and change some of + * the fields at run time as needed. The code in + * utility/tlcl_generator.c builds structures containing the commands, + * as well as the offsets of the fields that need to be set at run + * time. + */ + +#include <arch/early_variables.h> +#include <assert.h> +#include <string.h> +#include <security/tpm/tis.h> +#include <vb2_api.h> +#include <security/tpm/tss.h> +#include "tss_internal.h" +#include "tss_structures.h" + +#ifdef FOR_TEST +#include <stdio.h> +#define VBDEBUG(format, args...) printf(format, ## args) +#else +#include <console/console.h> +#define VBDEBUG(format, args...) printk(BIOS_DEBUG, format, ## args) +#endif + +static int tpm_send_receive(const uint8_t *request, + uint32_t request_length, + uint8_t *response, + uint32_t *response_length) +{ + size_t len = *response_length; + if (tis_sendrecv(request, request_length, response, &len)) + return VB2_ERROR_UNKNOWN; + /* check 64->32bit overflow and (re)check response buffer overflow */ + if (len > *response_length) + return VB2_ERROR_UNKNOWN; + *response_length = len; + return VB2_SUCCESS; +} + +/* Sets the size field of a TPM command. */ +static inline void set_tpm_command_size(uint8_t *buffer, uint32_t size) +{ + to_tpm_uint32(buffer + sizeof(uint16_t), size); +} + +/* Gets the size field of a TPM command. */ +__attribute__((unused)) +static inline int tpm_command_size(const uint8_t *buffer) +{ + uint32_t size; + from_tpm_uint32(buffer + sizeof(uint16_t), &size); + return (int) size; +} + +/* Gets the code field of a TPM command. */ +static inline int tpm_command_code(const uint8_t *buffer) +{ + uint32_t code; + from_tpm_uint32(buffer + sizeof(uint16_t) + sizeof(uint32_t), &code); + return code; +} + +/* Gets the return code field of a TPM result. */ +static inline int tpm_return_code(const uint8_t *buffer) +{ + return tpm_command_code(buffer); +} + +/* Like TlclSendReceive below, but do not retry if NEEDS_SELFTEST or + * DOING_SELFTEST errors are returned. + */ +static uint32_t tlcl_send_receive_no_retry(const uint8_t *request, + uint8_t *response, int max_length) +{ + uint32_t response_length = max_length; + uint32_t result; + + result = tpm_send_receive(request, tpm_command_size(request), + response, &response_length); + if (result != 0) { + /* Communication with TPM failed, so response is garbage */ + VBDEBUG("TPM: command 0x%x send/receive failed: 0x%x\n", + tpm_command_code(request), result); + return result; + } + /* Otherwise, use the result code from the response */ + result = tpm_return_code(response); + + /* TODO: add paranoia about returned response_length vs. max_length + * (and possibly expected length from the response header). See + * crosbug.com/17017 */ + + VBDEBUG("TPM: command 0x%x returned 0x%x\n", + tpm_command_code(request), result); + +return result; +} + + +/* Sends a TPM command and gets a response. Returns 0 if success or the TPM + * error code if error. Waits for the self test to complete if needed. */ +uint32_t tlcl_send_receive(const uint8_t *request, uint8_t *response, + int max_length) +{ + uint32_t result = tlcl_send_receive_no_retry(request, response, + max_length); + /* If the command fails because the self test has not completed, try it + * again after attempting to ensure that the self test has completed. */ + if (result == TPM_E_NEEDS_SELFTEST || result == TPM_E_DOING_SELFTEST) { + result = tlcl_continue_self_test(); + if (result != TPM_SUCCESS) + return result; +#if defined(TPM_BLOCKING_CONTINUESELFTEST) || defined(VB_RECOVERY_MODE) + /* Retry only once */ + result = tlcl_send_receive_no_retry(request, response, + max_length); +#else + /* This needs serious testing. The TPM specification says: "iii. + * The caller MUST wait for the actions of TPM_ContinueSelfTest + * to complete before reissuing the command C1." But, if + * ContinueSelfTest is non-blocking, how do we know that the + * actions have completed other than trying again? */ + do { + result = tlcl_send_receive_no_retry(request, response, + max_length); + } while (result == TPM_E_DOING_SELFTEST); +#endif + } + return result; +} + +/* Sends a command and returns the error code. */ +static uint32_t send(const uint8_t *command) +{ + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + return tlcl_send_receive(command, response, sizeof(response)); +} + +/* Exported functions. */ + +static uint8_t tlcl_init_done CAR_GLOBAL; + +uint32_t tlcl_lib_init(void) +{ + uint8_t done = car_get_var(tlcl_init_done); + if (done) + return VB2_SUCCESS; + + if (tis_init()) + return VB2_ERROR_UNKNOWN; + if (tis_open()) + return VB2_ERROR_UNKNOWN; + + car_set_var(tlcl_init_done, 1); + + return VB2_SUCCESS; +} + +uint32_t tlcl_startup(void) +{ + VBDEBUG("TPM: Startup\n"); + return send(tpm_startup_cmd.buffer); +} + +uint32_t tlcl_resume(void) +{ + VBDEBUG("TPM: Resume\n"); + return send(tpm_resume_cmd.buffer); +} + +uint32_t tlcl_self_test_full(void) +{ + VBDEBUG("TPM: Self test full\n"); + return send(tpm_selftestfull_cmd.buffer); +} + +uint32_t tlcl_continue_self_test(void) +{ + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + VBDEBUG("TPM: Continue self test\n"); + /* Call the No Retry version of SendReceive to avoid recursion. */ + return tlcl_send_receive_no_retry(tpm_continueselftest_cmd.buffer, + response, sizeof(response)); +} + +uint32_t tlcl_define_space(uint32_t index, uint32_t perm, uint32_t size) +{ + struct s_tpm_nv_definespace_cmd cmd; + VBDEBUG("TPM: TlclDefineSpace(0x%x, 0x%x, %d)\n", index, perm, size); + memcpy(&cmd, &tpm_nv_definespace_cmd, sizeof(cmd)); + to_tpm_uint32(cmd.buffer + tpm_nv_definespace_cmd.index, index); + to_tpm_uint32(cmd.buffer + tpm_nv_definespace_cmd.perm, perm); + to_tpm_uint32(cmd.buffer + tpm_nv_definespace_cmd.size, size); + return send(cmd.buffer); +} + +uint32_t tlcl_write(uint32_t index, const void *data, uint32_t length) +{ + struct s_tpm_nv_write_cmd cmd; + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + const int total_length = + kTpmRequestHeaderLength + kWriteInfoLength + length; + + VBDEBUG("TPM: tlcl_write(0x%x, %d)\n", index, length); + memcpy(&cmd, &tpm_nv_write_cmd, sizeof(cmd)); + assert(total_length <= TPM_LARGE_ENOUGH_COMMAND_SIZE); + set_tpm_command_size(cmd.buffer, total_length); + + to_tpm_uint32(cmd.buffer + tpm_nv_write_cmd.index, index); + to_tpm_uint32(cmd.buffer + tpm_nv_write_cmd.length, length); + memcpy(cmd.buffer + tpm_nv_write_cmd.data, data, length); + + return tlcl_send_receive(cmd.buffer, response, sizeof(response)); +} + +uint32_t tlcl_read(uint32_t index, void *data, uint32_t length) +{ + struct s_tpm_nv_read_cmd cmd; + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t result_length; + uint32_t result; + + VBDEBUG("TPM: tlcl_read(0x%x, %d)\n", index, length); + memcpy(&cmd, &tpm_nv_read_cmd, sizeof(cmd)); + to_tpm_uint32(cmd.buffer + tpm_nv_read_cmd.index, index); + to_tpm_uint32(cmd.buffer + tpm_nv_read_cmd.length, length); + + result = tlcl_send_receive(cmd.buffer, response, sizeof(response)); + if (result == TPM_SUCCESS && length > 0) { + uint8_t *nv_read_cursor = response + kTpmResponseHeaderLength; + from_tpm_uint32(nv_read_cursor, &result_length); + nv_read_cursor += sizeof(uint32_t); + memcpy(data, nv_read_cursor, result_length); + } + + return result; +} + + +uint32_t tlcl_assert_physical_presence(void) +{ + VBDEBUG("TPM: Asserting physical presence\n"); + return send(tpm_ppassert_cmd.buffer); +} + +uint32_t tlcl_physical_presence_cmd_enable(void) +{ + VBDEBUG("TPM: Enable the physical presence command\n"); + return send(tpm_ppenable_cmd.buffer); +} + +uint32_t tlcl_finalize_physical_presence(void) +{ + VBDEBUG("TPM: Enable PP cmd, disable HW pp, and set lifetime lock\n"); + return send(tpm_finalizepp_cmd.buffer); +} + +uint32_t tlcl_set_nv_locked(void) +{ + VBDEBUG("TPM: Set NV locked\n"); + return tlcl_define_space(TPM_NV_INDEX_LOCK, 0, 0); +} + +uint32_t tlcl_force_clear(void) +{ + VBDEBUG("TPM: Force clear\n"); + return send(tpm_forceclear_cmd.buffer); +} + +uint32_t tlcl_set_enable(void) +{ + VBDEBUG("TPM: Enabling TPM\n"); + return send(tpm_physicalenable_cmd.buffer); +} + +uint32_t tlcl_set_deactivated(uint8_t flag) +{ + struct s_tpm_physicalsetdeactivated_cmd cmd; + VBDEBUG("TPM: SetDeactivated(%d)\n", flag); + memcpy(&cmd, &tpm_physicalsetdeactivated_cmd, sizeof(cmd)); + *(cmd.buffer + cmd.deactivated) = flag; + return send(cmd.buffer); +} + +uint32_t tlcl_get_permanent_flags(TPM_PERMANENT_FLAGS *pflags) +{ + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t size; + uint32_t result = tlcl_send_receive(tpm_getflags_cmd.buffer, response, + sizeof(response)); + if (result != TPM_SUCCESS) + return result; + from_tpm_uint32(response + kTpmResponseHeaderLength, &size); + assert(size == sizeof(TPM_PERMANENT_FLAGS)); + memcpy(pflags, response + kTpmResponseHeaderLength + sizeof(size), + sizeof(TPM_PERMANENT_FLAGS)); + return result; +} + +uint32_t tlcl_get_flags(uint8_t *disable, uint8_t *deactivated, + uint8_t *nvlocked) +{ + TPM_PERMANENT_FLAGS pflags; + uint32_t result = tlcl_get_permanent_flags(&pflags); + if (result == TPM_SUCCESS) { + if (disable) + *disable = pflags.disable; + if (deactivated) + *deactivated = pflags.deactivated; + if (nvlocked) + *nvlocked = pflags.nvLocked; + VBDEBUG("TPM: flags disable=%d, deactivated=%d, nvlocked=%d\n", + pflags.disable, pflags.deactivated, pflags.nvLocked); + } + return result; +} + +uint32_t tlcl_set_global_lock(void) +{ + uint32_t x; + VBDEBUG("TPM: Set global lock\n"); + return tlcl_write(TPM_NV_INDEX0, (uint8_t *) &x, 0); +} + +uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest, + uint8_t *out_digest) +{ + struct s_tpm_extend_cmd cmd; + uint8_t response[kTpmResponseHeaderLength + kPcrDigestLength]; + uint32_t result; + + memcpy(&cmd, &tpm_extend_cmd, sizeof(cmd)); + to_tpm_uint32(cmd.buffer + tpm_extend_cmd.pcrNum, pcr_num); + memcpy(cmd.buffer + cmd.inDigest, in_digest, kPcrDigestLength); + + result = tlcl_send_receive(cmd.buffer, response, sizeof(response)); + if (result != TPM_SUCCESS) + return result; + + if (out_digest) + memcpy(out_digest, response + kTpmResponseHeaderLength, + kPcrDigestLength); + return result; +} diff --git a/src/security/tpm/tss/tcg-1.2/tss_internal.h b/src/security/tpm/tss/tcg-1.2/tss_internal.h new file mode 100644 index 0000000000..01912bb78e --- /dev/null +++ b/src/security/tpm/tss/tcg-1.2/tss_internal.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef TCG_TSS_INTERNAL_H_ +#define TCG_TSS_INTERNAL_H_ + +/* + * These numbers derive from adding the sizes of command fields as shown in the + * TPM commands manual. + */ +#define kTpmRequestHeaderLength 10 +#define kTpmResponseHeaderLength 10 +#define kTpmReadInfoLength 12 +#define kEncAuthLength 20 +#define kPcrDigestLength 20 + + +/* + * Conversion functions. to_tpm_TYPE puts a value of type TYPE into a TPM + * command buffer. from_tpm_TYPE gets a value of type TYPE from a TPM command + * buffer into a variable. + */ +__attribute__((unused)) +static inline void to_tpm_uint32(uint8_t *buffer, uint32_t x) +{ + buffer[0] = (uint8_t)(x >> 24); + buffer[1] = (uint8_t)((x >> 16) & 0xff); + buffer[2] = (uint8_t)((x >> 8) & 0xff); + buffer[3] = (uint8_t)(x & 0xff); +} + +/* + * See comment for above function. + */ +__attribute__((unused)) +static inline void from_tpm_uint32(const uint8_t *buffer, uint32_t *x) +{ + *x = ((buffer[0] << 24) | + (buffer[1] << 16) | + (buffer[2] << 8) | + buffer[3]); +} + +/* + * See comment for above function. + */ +__attribute__((unused)) +static inline void to_tpm_uint16(uint8_t *buffer, uint16_t x) +{ + buffer[0] = (uint8_t)(x >> 8); + buffer[1] = (uint8_t)(x & 0xff); +} + +/* + * See comment for above function. + */ +__attribute__((unused)) +static inline void from_tpm_uint16(const uint8_t *buffer, uint16_t *x) +{ + *x = (buffer[0] << 8) | buffer[1]; +} + +#endif /* TCG_TSS_INTERNAL_H_ */ diff --git a/src/security/tpm/tss/tcg-1.2/tss_structures.h b/src/security/tpm/tss/tcg-1.2/tss_structures.h new file mode 100644 index 0000000000..880864ee50 --- /dev/null +++ b/src/security/tpm/tss/tcg-1.2/tss_structures.h @@ -0,0 +1,164 @@ +/* This file is automatically generated */ + +const struct s_tpm_extend_cmd{ + uint8_t buffer[34]; + uint16_t pcrNum; + uint16_t inDigest; +} tpm_extend_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x14, }, +10, 14, }; + +const struct s_tpm_get_random_cmd{ + uint8_t buffer[14]; + uint16_t bytesRequested; +} tpm_get_random_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x46, }, +10, }; + +const struct s_tpm_getownership_cmd{ + uint8_t buffer[22]; +} tpm_getownership_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x65, + 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x11, }, +}; + +const struct s_tpm_getpermissions_cmd{ + uint8_t buffer[22]; + uint16_t index; +} tpm_getpermissions_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x65, + 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x4, }, +18, }; + +const struct s_tpm_getstclearflags_cmd{ + uint8_t buffer[22]; +} tpm_getstclearflags_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x65, + 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x9, }, +}; + +const struct s_tpm_getflags_cmd{ + uint8_t buffer[22]; +} tpm_getflags_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x65, + 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x8, }, +}; + +const struct s_tpm_physicalsetdeactivated_cmd{ + uint8_t buffer[11]; + uint16_t deactivated; +} tpm_physicalsetdeactivated_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x72, }, +10, }; + +const struct s_tpm_physicalenable_cmd{ + uint8_t buffer[10]; +} tpm_physicalenable_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x6f, }, +}; + +const struct s_tpm_physicaldisable_cmd{ + uint8_t buffer[10]; +} tpm_physicaldisable_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x70, }, +}; + +const struct s_tpm_forceclear_cmd{ + uint8_t buffer[10]; +} tpm_forceclear_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x5d, }, +}; + +const struct s_tpm_readpubek_cmd{ + uint8_t buffer[30]; +} tpm_readpubek_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7c, }, +}; + +const struct s_tpm_continueselftest_cmd{ + uint8_t buffer[10]; +} tpm_continueselftest_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53, }, +}; + +const struct s_tpm_selftestfull_cmd{ + uint8_t buffer[10]; +} tpm_selftestfull_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50, }, +}; + +const struct s_tpm_resume_cmd{ + uint8_t buffer[12]; +} tpm_resume_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x2, }, +}; + +const struct s_tpm_savestate_cmd{ + uint8_t buffer[10]; +} tpm_savestate_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x98, }, +}; + +const struct s_tpm_startup_cmd{ + uint8_t buffer[12]; +} tpm_startup_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x1, }, +}; + +const struct s_tpm_finalizepp_cmd{ + uint8_t buffer[12]; +} tpm_finalizepp_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x2, 0xa0, }, +}; + +const struct s_tpm_pplock_cmd{ + uint8_t buffer[12]; +} tpm_pplock_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x4, }, +}; + +const struct s_tpm_ppenable_cmd{ + uint8_t buffer[12]; +} tpm_ppenable_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x20, }, +}; + +const struct s_tpm_ppassert_cmd{ + uint8_t buffer[12]; +} tpm_ppassert_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x8, }, +}; + +const struct s_tpm_pcr_read_cmd{ + uint8_t buffer[14]; + uint16_t pcrNum; +} tpm_pcr_read_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x15, }, +10, }; + +const struct s_tpm_nv_read_cmd{ + uint8_t buffer[22]; + uint16_t index; + uint16_t length; +} tpm_nv_read_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcf, }, +10, 18, }; + +const struct s_tpm_nv_write_cmd{ + uint8_t buffer[256]; + uint16_t index; + uint16_t length; + uint16_t data; +} tpm_nv_write_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd, }, +10, 18, 22, }; + +const struct s_tpm_nv_definespace_cmd{ + uint8_t buffer[101]; + uint16_t index; + uint16_t perm; + uint16_t size; +} tpm_nv_definespace_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0xcc, + 0x0, 0x18, 0, 0, 0, 0, 0x0, 0x3, 0, 0, 0, 0x1f, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x0, 0x3, 0, 0, 0, 0x1f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x17, + }, + 12, 70, 77, +}; + +const int kWriteInfoLength = 12; +const int kNvDataPublicPermissionsOffset = 60; diff --git a/src/security/tpm/tss/tcg-2.0/tss.c b/src/security/tpm/tss/tcg-2.0/tss.c new file mode 100644 index 0000000000..cde9ea2946 --- /dev/null +++ b/src/security/tpm/tss/tcg-2.0/tss.c @@ -0,0 +1,438 @@ +/* + * Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <arch/early_variables.h> +#include <console/console.h> +#include <endian.h> +#include <string.h> +#include <vb2_api.h> +#include <security/tpm/tis.h> +#include <security/tpm/antirollback.h> + +#include "tss_structures.h" +#include "tss_marshaling.h" + +/* + * This file provides interface between firmware and TPM2 device. The TPM1.2 + * API was copied as is and relevant functions modified to comply with the + * TPM2 specification. + */ + +static void *tpm_process_command(TPM_CC command, void *command_body) +{ + struct obuf ob; + struct ibuf ib; + size_t out_size; + size_t in_size; + const uint8_t *sendb; + /* Command/response buffer. */ + static uint8_t cr_buffer[TPM_BUFFER_SIZE] CAR_GLOBAL; + + uint8_t *cr_buffer_ptr = car_get_var_ptr(cr_buffer); + + obuf_init(&ob, cr_buffer_ptr, sizeof(cr_buffer)); + + if (tpm_marshal_command(command, command_body, &ob) < 0) { + printk(BIOS_ERR, "command %#x\n", command); + return NULL; + } + + sendb = obuf_contents(&ob, &out_size); + + in_size = sizeof(cr_buffer); + if (tis_sendrecv(sendb, out_size, cr_buffer_ptr, &in_size)) { + printk(BIOS_ERR, "tpm transaction failed\n"); + return NULL; + } + + ibuf_init(&ib, cr_buffer_ptr, in_size); + + return tpm_unmarshal_response(command, &ib); +} + + +uint32_t tlcl_get_permanent_flags(TPM_PERMANENT_FLAGS *pflags) +{ + printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__); + return TPM_SUCCESS; +} + +static uint32_t tlcl_send_startup(TPM_SU type) +{ + struct tpm2_startup startup; + struct tpm2_response *response; + + startup.startup_type = type; + response = tpm_process_command(TPM2_Startup, &startup); + + if (response && response->hdr.tpm_code && + (response->hdr.tpm_code != TPM_RC_INITIALIZE)) { + printk(BIOS_INFO, "%s: Startup return code is %x\n", + __func__, response->hdr.tpm_code); + return TPM_E_IOERROR; + } + return TPM_SUCCESS; + +} + +uint32_t tlcl_resume(void) +{ + return tlcl_send_startup(TPM_SU_STATE); +} + +uint32_t tlcl_assert_physical_presence(void) +{ + /* + * Nothing to do on TPM2 for this, use platform hierarchy availability + * instead. + */ + return TPM_SUCCESS; +} + +/* + * The caller will provide the digest in a 32 byte buffer, let's consider it a + * sha256 digest. + */ +uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest, + uint8_t *out_digest) +{ + struct tpm2_pcr_extend_cmd pcr_ext_cmd; + struct tpm2_response *response; + + pcr_ext_cmd.pcrHandle = HR_PCR + pcr_num; + pcr_ext_cmd.digests.count = 1; + pcr_ext_cmd.digests.digests[0].hashAlg = TPM_ALG_SHA256; + memcpy(pcr_ext_cmd.digests.digests[0].digest.sha256, in_digest, + sizeof(pcr_ext_cmd.digests.digests[0].digest.sha256)); + + response = tpm_process_command(TPM2_PCR_Extend, &pcr_ext_cmd); + + printk(BIOS_INFO, "%s: response is %x\n", + __func__, response ? response->hdr.tpm_code : -1); + if (!response || response->hdr.tpm_code) + return TPM_E_IOERROR; + + return TPM_SUCCESS; +} + +uint32_t tlcl_finalize_physical_presence(void) +{ + /* Nothing needs to be done with tpm2. */ + printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__); + return TPM_SUCCESS; +} + +uint32_t tlcl_force_clear(void) +{ + struct tpm2_response *response; + + response = tpm_process_command(TPM2_Clear, NULL); + printk(BIOS_INFO, "%s: response is %x\n", + __func__, response ? response->hdr.tpm_code : -1); + + if (!response || response->hdr.tpm_code) + return TPM_E_IOERROR; + + return TPM_SUCCESS; +} + +uint32_t tlcl_get_flags(uint8_t *disable, uint8_t *deactivated, + uint8_t *nvlocked) +{ + /* + * TPM2 does not map directly into these flags TPM1.2 based firmware + * expects to be able to retrieve. + * + * In any case, if any of these conditions are present, the following + * firmware flow would be interrupted and will have a chance to report + * an error. Let's just hardcode an "All OK" response for now. + */ + + if (disable) + *disable = 0; + + if (nvlocked) + *nvlocked = 1; + + if (deactivated) + *deactivated = 0; + + return TPM_SUCCESS; +} + +static uint8_t tlcl_init_done CAR_GLOBAL; + +/* This function is called directly by vboot, uses vboot return types. */ +uint32_t tlcl_lib_init(void) +{ + uint8_t done = car_get_var(tlcl_init_done); + if (done) + return VB2_SUCCESS; + + if (tis_init()) + return VB2_ERROR_UNKNOWN; + if (tis_open()) + return VB2_ERROR_UNKNOWN; + + car_set_var(tlcl_init_done, 1); + + return VB2_SUCCESS; +} + +uint32_t tlcl_physical_presence_cmd_enable(void) +{ + printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__); + return TPM_SUCCESS; +} + +uint32_t tlcl_read(uint32_t index, void *data, uint32_t length) +{ + struct tpm2_nv_read_cmd nv_readc; + struct tpm2_response *response; + + memset(&nv_readc, 0, sizeof(nv_readc)); + + nv_readc.nvIndex = HR_NV_INDEX + index; + nv_readc.size = length; + + response = tpm_process_command(TPM2_NV_Read, &nv_readc); + + /* Need to map tpm error codes into internal values. */ + if (!response) + return TPM_E_READ_FAILURE; + + printk(BIOS_INFO, "%s:%d index %#x return code %x\n", + __FILE__, __LINE__, index, response->hdr.tpm_code); + switch (response->hdr.tpm_code) { + case 0: + break; + + /* Uninitialized, returned if the space hasn't been written. */ + case TPM_RC_NV_UNINITIALIZED: + /* + * Bad index, cr50 specific value, returned if the space + * hasn't been defined. + */ + case TPM_RC_CR50_NV_UNDEFINED: + return TPM_E_BADINDEX; + + default: + return TPM_E_READ_FAILURE; + } + + if (length > response->nvr.buffer.t.size) + return TPM_E_RESPONSE_TOO_LARGE; + + if (length < response->nvr.buffer.t.size) + return TPM_E_READ_EMPTY; + + memcpy(data, response->nvr.buffer.t.buffer, length); + + return TPM_SUCCESS; +} + +uint32_t tlcl_self_test_full(void) +{ + struct tpm2_self_test st; + struct tpm2_response *response; + + st.yes_no = 1; + + response = tpm_process_command(TPM2_SelfTest, &st); + printk(BIOS_INFO, "%s: response is %x\n", + __func__, response ? response->hdr.tpm_code : -1); + return TPM_SUCCESS; +} + +uint32_t tlcl_set_deactivated(uint8_t flag) +{ + printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__); + return TPM_SUCCESS; +} + +uint32_t tlcl_set_enable(void) +{ + printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__); + return TPM_SUCCESS; +} + +uint32_t tlcl_lock_nv_write(uint32_t index) +{ + struct tpm2_response *response; + /* TPM Wll reject attempts to write at non-defined index. */ + struct tpm2_nv_write_lock_cmd nv_wl = { + .nvIndex = HR_NV_INDEX + index, + }; + + response = tpm_process_command(TPM2_NV_WriteLock, &nv_wl); + + printk(BIOS_INFO, "%s: response is %x\n", + __func__, response ? response->hdr.tpm_code : -1); + + if (!response || response->hdr.tpm_code) + return TPM_E_IOERROR; + + return TPM_SUCCESS; +} + +uint32_t tlcl_startup(void) +{ + return tlcl_send_startup(TPM_SU_CLEAR); +} + +uint32_t tlcl_write(uint32_t index, const void *data, uint32_t length) +{ + struct tpm2_nv_write_cmd nv_writec; + struct tpm2_response *response; + + memset(&nv_writec, 0, sizeof(nv_writec)); + + nv_writec.nvIndex = HR_NV_INDEX + index; + nv_writec.data.t.size = length; + nv_writec.data.t.buffer = data; + + response = tpm_process_command(TPM2_NV_Write, &nv_writec); + + printk(BIOS_INFO, "%s: response is %x\n", + __func__, response ? response->hdr.tpm_code : -1); + + /* Need to map tpm error codes into internal values. */ + if (!response || response->hdr.tpm_code) + return TPM_E_WRITE_FAILURE; + + return TPM_SUCCESS; +} + +uint32_t tlcl_define_space(uint32_t space_index, size_t space_size) +{ + struct tpm2_nv_define_space_cmd nvds_cmd; + struct tpm2_response *response; + /* + * Different sets of NVRAM space attributes apply to the "ro" spaces, + * i.e. those which should not be possible to delete or modify once + * the RO exits, and the rest of the NVRAM spaces. + */ + const TPMA_NV ro_space_attributes = { + .TPMA_NV_PPWRITE = 1, + .TPMA_NV_AUTHREAD = 1, + .TPMA_NV_PPREAD = 1, + .TPMA_NV_PLATFORMCREATE = 1, + .TPMA_NV_WRITE_STCLEAR = 1, + .TPMA_NV_POLICY_DELETE = 1, + }; + const TPMA_NV default_space_attributes = { + .TPMA_NV_PPWRITE = 1, + .TPMA_NV_AUTHREAD = 1, + .TPMA_NV_PPREAD = 1, + .TPMA_NV_PLATFORMCREATE = 1, + }; + + /* Prepare the define space command structure. */ + memset(&nvds_cmd, 0, sizeof(nvds_cmd)); + + nvds_cmd.publicInfo.dataSize = space_size; + nvds_cmd.publicInfo.nvIndex = HR_NV_INDEX + space_index; + nvds_cmd.publicInfo.nameAlg = TPM_ALG_SHA256; + + /* RO only NV spaces should be impossible to destroy. */ + if ((space_index == FIRMWARE_NV_INDEX) || + (space_index == REC_HASH_NV_INDEX)) { + /* + * This policy digest was obtained using TPM2_PolicyPCR + * selecting only PCR_0 with a value of all zeros. + */ + const uint8_t pcr0_unchanged_policy[] = { + 0x09, 0x93, 0x3C, 0xCE, 0xEB, 0xB4, 0x41, 0x11, + 0x18, 0x81, 0x1D, 0xD4, 0x47, 0x78, 0x80, 0x08, + 0x88, 0x86, 0x62, 0x2D, 0xD7, 0x79, 0x94, 0x46, + 0x62, 0x26, 0x68, 0x8E, 0xEE, 0xE6, 0x6A, 0xA1 + }; + + nvds_cmd.publicInfo.attributes = ro_space_attributes; + /* + * Use policy digest based on default pcr0 value. This makes + * sure that the space can not be deleted as soon as PCR0 + * value has been extended from default. + */ + nvds_cmd.publicInfo.authPolicy.t.buffer = pcr0_unchanged_policy; + nvds_cmd.publicInfo.authPolicy.t.size = + sizeof(pcr0_unchanged_policy); + } else { + nvds_cmd.publicInfo.attributes = default_space_attributes; + } + + response = tpm_process_command(TPM2_NV_DefineSpace, &nvds_cmd); + printk(BIOS_INFO, "%s: response is %x\n", + __func__, response ? response->hdr.tpm_code : -1); + + if (!response) + return TPM_E_NO_DEVICE; + + /* Map TPM2 retrun codes into common vboot represenation. */ + switch (response->hdr.tpm_code) { + case TPM2_RC_SUCCESS: + return TPM_SUCCESS; + case TPM2_RC_NV_DEFINED: + return TPM_E_NV_DEFINED; + default: + return TPM_E_INTERNAL_INCONSISTENCY; + } +} + +uint32_t tlcl_disable_platform_hierarchy(void) +{ + struct tpm2_response *response; + struct tpm2_hierarchy_control_cmd hc = { + .enable = TPM_RH_PLATFORM, + .state = 0, + }; + + response = tpm_process_command(TPM2_Hierarchy_Control, &hc); + + if (!response || response->hdr.tpm_code) + return TPM_E_INTERNAL_INCONSISTENCY; + + return TPM_SUCCESS; +} + +uint32_t tlcl_cr50_enable_nvcommits(void) +{ + uint16_t sub_command = TPM2_CR50_SUB_CMD_NVMEM_ENABLE_COMMITS; + struct tpm2_response *response; + + printk(BIOS_INFO, "Enabling cr50 nvmem commmits\n"); + + response = tpm_process_command(TPM2_CR50_VENDOR_COMMAND, &sub_command); + + if (response == NULL || (response && response->hdr.tpm_code)) { + if (response) + printk(BIOS_INFO, "%s: failed %x\n", __func__, + response->hdr.tpm_code); + else + printk(BIOS_INFO, "%s: failed\n", __func__); + return TPM_E_IOERROR; + } + return TPM_SUCCESS; +} + +uint32_t tlcl_cr50_enable_update(uint16_t timeout_ms, + uint8_t *num_restored_headers) +{ + struct tpm2_response *response; + uint16_t command_body[] = { + TPM2_CR50_SUB_CMD_TURN_UPDATE_ON, timeout_ms + }; + + printk(BIOS_INFO, "Checking cr50 for pending updates\n"); + + response = tpm_process_command(TPM2_CR50_VENDOR_COMMAND, command_body); + + if (!response || response->hdr.tpm_code) + return TPM_E_INTERNAL_INCONSISTENCY; + + *num_restored_headers = response->vcr.num_restored_headers; + return TPM_SUCCESS; +} diff --git a/src/security/tpm/tss/tcg-2.0/tss_marshaling.c b/src/security/tpm/tss/tcg-2.0/tss_marshaling.c new file mode 100644 index 0000000000..b1d666362b --- /dev/null +++ b/src/security/tpm/tss/tcg-2.0/tss_marshaling.c @@ -0,0 +1,559 @@ +/* + * Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <arch/early_variables.h> +#include <commonlib/iobuf.h> +#include <console/console.h> +#include <stdlib.h> +#include <string.h> + +#include "tss_marshaling.h" + +static uint16_t tpm_tag CAR_GLOBAL; /* Depends on the command type. */ + +#define unmarshal_TPM_CAP(a, b) ibuf_read_be32(a, b) +#define unmarshal_TPM_CC(a, b) ibuf_read_be32(a, b) +#define unmarshal_TPM_PT(a, b) ibuf_read_be32(a, b) +#define unmarshal_TPM_HANDLE(a, b) ibuf_read_be32(a, b) + +#define marshal_TPM_HANDLE(a, b) obuf_write_be32(a, b) +#define marshal_TPMI_ALG_HASH(a, b) obuf_write_be16(a, b) + +static int marshal_startup(struct obuf *ob, struct tpm2_startup *cmd_body) +{ + return obuf_write_be16(ob, cmd_body->startup_type); +} + +static int marshal_get_capability(struct obuf *ob, + struct tpm2_get_capability *cmd_body) +{ + int rc = 0; + + rc |= obuf_write_be32(ob, cmd_body->capability); + rc |= obuf_write_be32(ob, cmd_body->property); + rc |= obuf_write_be32(ob, cmd_body->propertyCount); + + return rc; +} + +static int marshal_TPM2B(struct obuf *ob, TPM2B *data) +{ + int rc = 0; + + rc |= obuf_write_be16(ob, data->size); + rc |= obuf_write(ob, data->buffer, data->size); + + return rc; +} + +static int marshal_TPMA_NV(struct obuf *ob, TPMA_NV *nv) +{ + uint32_t v; + + memcpy(&v, nv, sizeof(v)); + return obuf_write_be32(ob, v); +} + +static int marshal_TPMS_NV_PUBLIC(struct obuf *ob, TPMS_NV_PUBLIC *nvpub) +{ + int rc = 0; + + rc |= marshal_TPM_HANDLE(ob, nvpub->nvIndex); + rc |= marshal_TPMI_ALG_HASH(ob, nvpub->nameAlg); + rc |= marshal_TPMA_NV(ob, &nvpub->attributes); + rc |= marshal_TPM2B(ob, &nvpub->authPolicy.b); + rc |= obuf_write_be16(ob, nvpub->dataSize); + + return rc; +} + +static int marshal_TPMT_HA(struct obuf *ob, TPMT_HA *tpmtha) +{ + int rc = 0; + + rc |= marshal_TPMI_ALG_HASH(ob, tpmtha->hashAlg); + rc |= obuf_write(ob, tpmtha->digest.sha256, + sizeof(tpmtha->digest.sha256)); + + return rc; +} + +static int marshal_TPML_DIGEST_VALUES(struct obuf *ob, + TPML_DIGEST_VALUES *dvalues) +{ + int i; + int rc = 0; + + rc |= obuf_write_be32(ob, dvalues->count); + for (i = 0; i < dvalues->count; i++) + rc |= marshal_TPMT_HA(ob, &dvalues->digests[i]); + + return rc; +} + +static int marshal_session_header(struct obuf *ob, + struct tpm2_session_header *session_header) +{ + int rc = 0; + struct obuf ob_sz; + size_t prev_written; + + /* Snapshot current location to place size of header. */ + if (obuf_splice_current(ob, &ob_sz, sizeof(uint32_t)) < 0) + return -1; + + /* Write a size placeholder. */ + rc |= obuf_write_be32(ob, 0); + + /* Keep track of session header data size by tracking num written. */ + prev_written = obuf_nr_written(ob); + + rc |= obuf_write_be32(ob, session_header->session_handle); + rc |= obuf_write_be16(ob, session_header->nonce_size); + rc |= obuf_write(ob, session_header->nonce, session_header->nonce_size); + rc |= obuf_write_be8(ob, session_header->session_attrs); + rc |= obuf_write_be16(ob, session_header->auth_size); + rc |= obuf_write(ob, session_header->auth, session_header->auth_size); + + /* Fill back in proper size of session header. */ + rc |= obuf_write_be32(&ob_sz, obuf_nr_written(ob) - prev_written); + + return rc; +} + +/* + * Common session header can include one or two handles and an empty + * session_header structure. + */ +static int marshal_common_session_header(struct obuf *ob, + const uint32_t *handles, + size_t handle_count) +{ + size_t i; + struct tpm2_session_header session_header; + int rc = 0; + + car_set_var(tpm_tag, TPM_ST_SESSIONS); + + for (i = 0; i < handle_count; i++) + rc |= marshal_TPM_HANDLE(ob, handles[i]); + + memset(&session_header, 0, sizeof(session_header)); + session_header.session_handle = TPM_RS_PW; + rc |= marshal_session_header(ob, &session_header); + + return rc; +} + +static int marshal_nv_define_space(struct obuf *ob, + struct tpm2_nv_define_space_cmd *nvd_in) +{ + const uint32_t handle[] = { TPM_RH_PLATFORM }; + struct obuf ob_sz; + size_t prev_written; + int rc = 0; + + rc |= marshal_common_session_header(ob, handle, ARRAY_SIZE(handle)); + rc |= marshal_TPM2B(ob, &nvd_in->auth.b); + + /* Snapshot current location to place size field. */ + if (obuf_splice_current(ob, &ob_sz, sizeof(uint16_t)) < 0) + return -1; + + /* Put placeholder for size */ + rc |= obuf_write_be16(ob, 0); + + /* Keep track of nv define space data size by tracking num written. */ + prev_written = obuf_nr_written(ob); + + rc |= marshal_TPMS_NV_PUBLIC(ob, &nvd_in->publicInfo); + rc |= obuf_write_be16(&ob_sz, obuf_nr_written(ob) - prev_written); + + return rc; +} + +static int marshal_nv_write(struct obuf *ob, + struct tpm2_nv_write_cmd *command_body) +{ + int rc = 0; + uint32_t handles[] = { TPM_RH_PLATFORM, command_body->nvIndex }; + + rc |= marshal_common_session_header(ob, handles, ARRAY_SIZE(handles)); + rc |= marshal_TPM2B(ob, &command_body->data.b); + rc |= obuf_write_be16(ob, command_body->offset); + + return rc; +} + +static int marshal_nv_write_lock(struct obuf *ob, + struct tpm2_nv_write_lock_cmd *command_body) +{ + uint32_t handles[] = { TPM_RH_PLATFORM, command_body->nvIndex }; + + return marshal_common_session_header(ob, handles, ARRAY_SIZE(handles)); +} + +static int marshal_pcr_extend(struct obuf *ob, + struct tpm2_pcr_extend_cmd *command_body) +{ + int rc = 0; + uint32_t handles[] = { command_body->pcrHandle }; + + rc |= marshal_common_session_header(ob, handles, ARRAY_SIZE(handles)); + rc |= marshal_TPML_DIGEST_VALUES(ob, &command_body->digests); + + return rc; +} + +static int marshal_nv_read(struct obuf *ob, + struct tpm2_nv_read_cmd *command_body) +{ + int rc = 0; + uint32_t handles[] = { TPM_RH_PLATFORM, command_body->nvIndex }; + + rc |= marshal_common_session_header(ob, handles, ARRAY_SIZE(handles)); + rc |= obuf_write_be16(ob, command_body->size); + rc |= obuf_write_be16(ob, command_body->offset); + + return rc; +} + +/* TPM2_Clear command does not require paramaters. */ +static int marshal_clear(struct obuf *ob) +{ + const uint32_t handle[] = { TPM_RH_PLATFORM }; + + return marshal_common_session_header(ob, handle, ARRAY_SIZE(handle)); +} + +static int marshal_selftest(struct obuf *ob, + struct tpm2_self_test *command_body) +{ + return obuf_write_be8(ob, command_body->yes_no); +} + +static int marshal_hierarchy_control(struct obuf *ob, + struct tpm2_hierarchy_control_cmd *command_body) +{ + int rc = 0; + struct tpm2_session_header session_header; + + car_set_var(tpm_tag, TPM_ST_SESSIONS); + + rc |= marshal_TPM_HANDLE(ob, TPM_RH_PLATFORM); + memset(&session_header, 0, sizeof(session_header)); + session_header.session_handle = TPM_RS_PW; + rc |= marshal_session_header(ob, &session_header); + + rc |= marshal_TPM_HANDLE(ob, command_body->enable); + rc |= obuf_write_be8(ob, command_body->state); + + return rc; +} + +static int marshal_cr50_vendor_command(struct obuf *ob, void *command_body) +{ + int rc = 0; + uint16_t *sub_command = command_body; + + switch (*sub_command) { + case TPM2_CR50_SUB_CMD_NVMEM_ENABLE_COMMITS: + rc |= obuf_write_be16(ob, *sub_command); + break; + case TPM2_CR50_SUB_CMD_TURN_UPDATE_ON: + rc |= obuf_write_be16(ob, sub_command[0]); + rc |= obuf_write_be16(ob, sub_command[1]); + break; + default: + /* Unsupported subcommand. */ + printk(BIOS_WARNING, "Unsupported cr50 subcommand: 0x%04x\n", + *sub_command); + rc = -1; + break; + } + return rc; +} + +int tpm_marshal_command(TPM_CC command, void *tpm_command_body, struct obuf *ob) +{ + struct obuf ob_hdr; + const size_t hdr_sz = sizeof(uint16_t) + 2 * sizeof(uint32_t); + int rc = 0; + + car_set_var(tpm_tag, TPM_ST_NO_SESSIONS); + + if (obuf_splice_current(ob, &ob_hdr, hdr_sz) < 0) + return -1; + + /* Write TPM command header with placeholder field values. */ + rc |= obuf_write_be16(ob, 0); + rc |= obuf_write_be32(ob, 0); + rc |= obuf_write_be32(ob, command); + + if (rc != 0) + return rc; + + switch (command) { + case TPM2_Startup: + rc |= marshal_startup(ob, tpm_command_body); + break; + + case TPM2_GetCapability: + rc |= marshal_get_capability(ob, tpm_command_body); + break; + + case TPM2_NV_Read: + rc |= marshal_nv_read(ob, tpm_command_body); + break; + + case TPM2_NV_DefineSpace: + rc |= marshal_nv_define_space(ob, tpm_command_body); + break; + + case TPM2_NV_Write: + rc |= marshal_nv_write(ob, tpm_command_body); + break; + + case TPM2_NV_WriteLock: + rc |= marshal_nv_write_lock(ob, tpm_command_body); + break; + + case TPM2_SelfTest: + rc |= marshal_selftest(ob, tpm_command_body); + break; + + case TPM2_Hierarchy_Control: + rc |= marshal_hierarchy_control(ob, tpm_command_body); + break; + + case TPM2_Clear: + rc |= marshal_clear(ob); + break; + + case TPM2_PCR_Extend: + rc |= marshal_pcr_extend(ob, tpm_command_body); + break; + + case TPM2_CR50_VENDOR_COMMAND: + rc |= marshal_cr50_vendor_command(ob, tpm_command_body); + break; + + default: + printk(BIOS_INFO, "%s:%d:Request to marshal unsupported command %#x\n", + __FILE__, __LINE__, command); + rc = -1; + } + + if (rc != 0) + return rc; + + /* Fix up the command header with known values. */ + rc |= obuf_write_be16(&ob_hdr, car_get_var(tpm_tag)); + rc |= obuf_write_be32(&ob_hdr, obuf_nr_written(ob)); + + return rc; +} + +static int unmarshal_get_capability(struct ibuf *ib, + struct get_cap_response *gcr) +{ + int i; + int rc = 0; + + rc |= ibuf_read_be8(ib, &gcr->more_data); + rc |= unmarshal_TPM_CAP(ib, &gcr->cd.capability); + + if (rc != 0) + return rc; + + switch (gcr->cd.capability) { + case TPM_CAP_TPM_PROPERTIES: + if (ibuf_read_be32(ib, &gcr->cd.data.tpmProperties.count)) + return -1; + if (gcr->cd.data.tpmProperties.count > ARRAY_SIZE + (gcr->cd.data.tpmProperties.tpmProperty)) { + printk(BIOS_INFO, "%s:%s:%d - %d - too many properties\n", + __FILE__, __func__, __LINE__, + gcr->cd.data.tpmProperties.count); + return -1; + } + for (i = 0; i < gcr->cd.data.tpmProperties.count; i++) { + TPMS_TAGGED_PROPERTY *pp; + + pp = gcr->cd.data.tpmProperties.tpmProperty + i; + rc |= unmarshal_TPM_PT(ib, &pp->property); + rc |= ibuf_read_be32(ib, &pp->value); + } + break; + default: + printk(BIOS_ERR, + "%s:%d - unable to unmarshal capability response", + __func__, __LINE__); + printk(BIOS_ERR, " for %d\n", gcr->cd.capability); + rc = -1; + break; + } + + return rc; +} + +static int unmarshal_TPM2B_MAX_NV_BUFFER(struct ibuf *ib, + TPM2B_MAX_NV_BUFFER *nv_buffer) +{ + if (ibuf_read_be16(ib, &nv_buffer->t.size)) + return -1; + + nv_buffer->t.buffer = ibuf_oob_drain(ib, nv_buffer->t.size); + + if (nv_buffer->t.buffer == NULL) { + printk(BIOS_ERR, "%s:%d - " + "size mismatch: expected %d, remaining %zd\n", + __func__, __LINE__, nv_buffer->t.size, + ibuf_remaining(ib)); + return -1; + } + + return 0; +} + +static int unmarshal_nv_read(struct ibuf *ib, struct nv_read_response *nvr) +{ + /* Total size of the parameter field. */ + if (ibuf_read_be32(ib, &nvr->params_size)) + return -1; + + if (unmarshal_TPM2B_MAX_NV_BUFFER(ib, &nvr->buffer)) + return -1; + + if (nvr->params_size != + (nvr->buffer.t.size + sizeof(nvr->buffer.t.size))) { + printk(BIOS_ERR, + "%s:%d - parameter/buffer %d/%d size mismatch", + __func__, __LINE__, nvr->params_size, + nvr->buffer.t.size); + return -1; + } + + /* + * Let's ignore the authorisation section. It should be 5 bytes total, + * just confirm that this is the case and report any discrepancy. + */ + if (ibuf_remaining(ib) != 5) + printk(BIOS_ERR, + "%s:%d - unexpected authorisation seciton size %zd\n", + __func__, __LINE__, ibuf_remaining(ib)); + + ibuf_oob_drain(ib, ibuf_remaining(ib)); + + return 0; +} + +static int unmarshal_vendor_command(struct ibuf *ib, + struct vendor_command_response *vcr) +{ + if (ibuf_read_be16(ib, &vcr->vc_subcommand)) + return -1; + + switch (vcr->vc_subcommand) { + case TPM2_CR50_SUB_CMD_NVMEM_ENABLE_COMMITS: + break; + case TPM2_CR50_SUB_CMD_TURN_UPDATE_ON: + return ibuf_read_be8(ib, &vcr->num_restored_headers); + break; + default: + printk(BIOS_ERR, + "%s:%d - unsupported vendor command %#04x!\n", + __func__, __LINE__, vcr->vc_subcommand); + return -1; + } + + return 0; +} + +struct tpm2_response *tpm_unmarshal_response(TPM_CC command, struct ibuf *ib) +{ + static struct tpm2_response tpm2_static_resp CAR_GLOBAL; + struct tpm2_response *tpm2_resp = car_get_var_ptr(&tpm2_static_resp); + int rc = 0; + + rc |= ibuf_read_be16(ib, &tpm2_resp->hdr.tpm_tag); + rc |= ibuf_read_be32(ib, &tpm2_resp->hdr.tpm_size); + rc |= unmarshal_TPM_CC(ib, &tpm2_resp->hdr.tpm_code); + + if (rc != 0) + return NULL; + + if (ibuf_remaining(ib) == 0) { + if (tpm2_resp->hdr.tpm_size != ibuf_nr_read(ib)) + printk(BIOS_ERR, + "%s: size mismatch in response to command %#x\n", + __func__, command); + return tpm2_resp; + } + + switch (command) { + case TPM2_Startup: + break; + + case TPM2_GetCapability: + rc |= unmarshal_get_capability(ib, &tpm2_resp->gc); + break; + + case TPM2_NV_Read: + rc |= unmarshal_nv_read(ib, &tpm2_resp->nvr); + break; + + case TPM2_Hierarchy_Control: + case TPM2_Clear: + case TPM2_NV_DefineSpace: + case TPM2_NV_Write: + case TPM2_NV_WriteLock: + case TPM2_PCR_Extend: + /* Session data included in response can be safely ignored. */ + ibuf_oob_drain(ib, ibuf_remaining(ib)); + break; + + case TPM2_CR50_VENDOR_COMMAND: + rc |= unmarshal_vendor_command(ib, &tpm2_resp->vcr); + break; + + default: + { + size_t i; + size_t sz_left; + const uint8_t *data; + + printk(BIOS_INFO, "%s:%d:" + "Request to unmarshal unexpected command %#x," + " code %#x", + __func__, __LINE__, command, + tpm2_resp->hdr.tpm_code); + + sz_left = ibuf_remaining(ib); + data = ibuf_oob_drain(ib, sz_left); + + for (i = 0; i < sz_left; i++) { + if (!(i % 16)) + printk(BIOS_INFO, "\n"); + printk(BIOS_INFO, "%2.2x ", data[i]); + } + } + printk(BIOS_INFO, "\n"); + return NULL; + } + + if (ibuf_remaining(ib)) { + printk(BIOS_INFO, + "%s:%d got %d bytes back in response to %#x," + " failed to parse (%zd)\n", + __func__, __LINE__, tpm2_resp->hdr.tpm_size, + command, ibuf_remaining(ib)); + return NULL; + } + + /* The entire message have been parsed. */ + return tpm2_resp; +} diff --git a/src/security/tpm/tss/tcg-2.0/tss_marshaling.h b/src/security/tpm/tss/tcg-2.0/tss_marshaling.h new file mode 100644 index 0000000000..d34756d566 --- /dev/null +++ b/src/security/tpm/tss/tcg-2.0/tss_marshaling.h @@ -0,0 +1,46 @@ +/* + * Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef TCG2_TSS_MARSHALING_H_ +#define TCG2_TSS_MARSHALING_H_ + +#include <commonlib/iobuf.h> +#include "tss_structures.h" + +/* The below functions are used to serialize/deserialize TPM2 commands. */ + +/** + * tpm_marshal_command + * + * Given a structure containing a TPM2 command, serialize the structure for + * sending it to the TPM. + * + * @command: code of the TPM2 command to marshal + * @tpm_command_body: a pointer to the command specific structure + * @ob: output buffer where command is marshaled to + * + * Returns 0 on success or -1 on error. + * + */ +int tpm_marshal_command(TPM_CC command, void *tpm_command_body, + struct obuf *ob); + +/** + * tpm_unmarshal_response + * + * Given a buffer received from the TPM in response to a certain command, + * deserialize the buffer into the expeced response structure. + * + * struct tpm2_response is a union of all possible responses. + * + * @command: code of the TPM2 command for which a response is unmarshaled + * @ib: input buffer containing the serialized response. + * + * Returns a pointer to the deserialized response or NULL in case of + * unmarshaling problems. + */ +struct tpm2_response *tpm_unmarshal_response(TPM_CC command, struct ibuf *ib); + +#endif // TCG2_TSS_MARSHALING_H_ diff --git a/src/security/tpm/tss/tcg-2.0/tss_structures.h b/src/security/tpm/tss/tcg-2.0/tss_structures.h new file mode 100644 index 0000000000..962e20ca8f --- /dev/null +++ b/src/security/tpm/tss/tcg-2.0/tss_structures.h @@ -0,0 +1,373 @@ +/* + * Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef TCG2_TSS_STRUCTURES_H_ +#define TCG2_TSS_STRUCTURES_H_ + +/* + * This file includes a subset of definitions of TPM protocol version 2.x + * constants and structures needed for functions used in coreboot. + */ +#include <stdint.h> +#include <compiler.h> +#include <types.h> + +#include <security/tpm/tss.h> + +/* This should be plenty for what firmware needs. */ +#define TPM_BUFFER_SIZE 256 + +/* Basic TPM2 types. */ +typedef uint16_t TPM_SU; +typedef uint16_t TPM_ALG_ID; +typedef uint32_t TPM_CC; +typedef uint32_t TPM_HANDLE; +typedef uint32_t TPM_RC; +typedef uint8_t TPMI_YES_NO; +typedef TPM_ALG_ID TPMI_ALG_HASH; +typedef TPM_HANDLE TPMI_DH_PCR; +typedef TPM_HANDLE TPMI_RH_NV_INDEX; +typedef TPM_HANDLE TPMI_RH_ENABLES; +typedef TPM_HANDLE TPMI_SH_AUTH_SESSION; +typedef TPM_HANDLE TPM_RH; + +/* Some hardcoded algorithm values. */ +#define TPM_ALG_HMAC ((TPM_ALG_ID)0x0005) +#define TPM_ALG_NULL ((TPM_ALG_ID)0x0010) +#define TPM_ALG_SHA1 ((TPM_ALG_ID)0x0004) +#define TPM_ALG_SHA256 ((TPM_ALG_ID)0x000b) + +#define SHA256_DIGEST_SIZE 32 + +/* Some hardcoded hierarchies. */ +#define TPM_RH_NULL 0x40000007 +#define TPM_RS_PW 0x40000009 +#define TPM_RH_PLATFORM 0x4000000C + +typedef struct { + uint16_t size; + uint8_t *buffer; +} TPM2B; + +/* Relevant TPM Command's structures. */ +/* Common command/response header. */ +struct tpm_header { + uint16_t tpm_tag; + uint32_t tpm_size; + TPM_CC tpm_code; +} __packed; + +/* TPM command codes. */ +#define TPM2_Hierarchy_Control ((TPM_CC)0x00000121) +#define TPM2_Clear ((TPM_CC)0x00000126) +#define TPM2_NV_DefineSpace ((TPM_CC)0x0000012A) +#define TPM2_NV_Write ((TPM_CC)0x00000137) +#define TPM2_NV_WriteLock ((TPM_CC)0x00000138) +#define TPM2_SelfTest ((TPM_CC)0x00000143) +#define TPM2_Startup ((TPM_CC)0x00000144) +#define TPM2_NV_Read ((TPM_CC)0x0000014E) +#define TPM2_GetCapability ((TPM_CC)0x0000017A) +#define TPM2_PCR_Extend ((TPM_CC)0x00000182) +/* TPM2 specifies vendor commands need to have this bit set. Vendor command + space is defined by the lower 16 bits. */ +#define TPM_CC_VENDOR_BIT_MASK 0x20000000 +/* FIXME: below is not enough to differentiate between vendors commands + of numerous devices. However, the current tpm2 APIs aren't very amenable + to extending generically because the marshaling code is assuming all + knowledge of all commands. */ +#define TPM2_CR50_VENDOR_COMMAND ((TPM_CC)(TPM_CC_VENDOR_BIT_MASK | 0)) +#define TPM2_CR50_SUB_CMD_NVMEM_ENABLE_COMMITS (21) +#define TPM2_CR50_SUB_CMD_TURN_UPDATE_ON (24) + +/* Startup values. */ +#define TPM_SU_CLEAR 0 +#define TPM_SU_STATE 1 + +#define TPM_HT_PCR 0x00 +#define TPM_HT_NV_INDEX 0x01 +#define TPM_HT_HMAC_SESSION 0x02 +#define TPM_HT_POLICY_SESSION 0x03 + +#define HR_SHIFT 24 +#define HR_PCR (TPM_HT_PCR << HR_SHIFT) +#define HR_HMAC_SESSION (TPM_HT_HMAC_SESSION << HR_SHIFT) +#define HR_POLICY_SESSION (TPM_HT_POLICY_SESSION << HR_SHIFT) +#define HR_TRANSIENT (TPM_HT_TRANSIENT << HR_SHIFT) +#define HR_PERSISTENT (TPM_HT_PERSISTENT << HR_SHIFT) +#define HR_NV_INDEX (TPM_HT_NV_INDEX << HR_SHIFT) +#define HR_PERMANENT (TPM_HT_PERMANENT << HR_SHIFT) +#define PCR_FIRST (HR_PCR + 0) +#define PCR_LAST (PCR_FIRST + IMPLEMENTATION_PCR-1) +#define HMAC_SESSION_FIRST (HR_HMAC_SESSION + 0) +#define HMAC_SESSION_LAST (HMAC_SESSION_FIRST+MAX_ACTIVE_SESSIONS-1) +#define LOADED_SESSION_FIRST HMAC_SESSION_FIRST +#define LOADED_SESSION_LAST HMAC_SESSION_LAST +#define POLICY_SESSION_FIRST (HR_POLICY_SESSION + 0) +#define POLICY_SESSION_LAST (POLICY_SESSION_FIRST + MAX_ACTIVE_SESSIONS-1) +#define TRANSIENT_FIRST (HR_TRANSIENT + 0) +#define ACTIVE_SESSION_FIRST POLICY_SESSION_FIRST +#define ACTIVE_SESSION_LAST POLICY_SESSION_LAST +#define TRANSIENT_LAST (TRANSIENT_FIRST+MAX_LOADED_OBJECTS-1) +#define PERSISTENT_FIRST (HR_PERSISTENT + 0) +#define PERSISTENT_LAST (PERSISTENT_FIRST + 0x00FFFFFF) +#define PLATFORM_PERSISTENT (PERSISTENT_FIRST + 0x00800000) +#define NV_INDEX_FIRST (HR_NV_INDEX + 0) +#define NV_INDEX_LAST (NV_INDEX_FIRST + 0x00FFFFFF) +#define PERMANENT_FIRST TPM_RH_FIRST +#define PERMANENT_LAST TPM_RH_LAST + +/* Tpm2 command tags. */ +#define TPM_ST_NO_SESSIONS 0x8001 +#define TPM_ST_SESSIONS 0x8002 + +/* Values copied from tpm2/tpm_types.h */ +#define RC_VER1 0x100 +#define TPM_RC_INITIALIZE ((TPM_RC)(RC_VER1 + 0x000)) +#define TPM_RC_NV_UNINITIALIZED ((TPM_RC)(RC_VER1 + 0x04A)) + +/* + * Cr50 returns this code when an attempt is made to read an NV location which + * has not yet been defined. This is an aggregation of various return code + * extensions which may or may not match if a different TPM2 device is + * used. + */ +#define TPM_RC_CR50_NV_UNDEFINED 0x28b + +/* TPM command structures. */ + +struct tpm2_startup { + TPM_SU startup_type; +}; + +/* Various TPM capability types to use when querying the device. */ +typedef uint32_t TPM_CAP; +#define TPM_CAP_TPM_PROPERTIES ((TPM_CAP)0x00000006) + +typedef TPM_HANDLE TPMI_RH_NV_AUTH; +typedef TPM_HANDLE TPMI_RH_NV_INDEX; + +/* TPM Property capability constants. */ +typedef uint32_t TPM_PT; +#define PT_GROUP 0x00000100 +#define PT_VAR (PT_GROUP * 2) +#define TPM_PT_PERMANENT ((TPM_PT)(PT_VAR + 0)) + +/* Structures of payloads of various TPM2 commands. */ +struct tpm2_get_capability { + TPM_CAP capability; + uint32_t property; + uint32_t propertyCount; +}; + +/* get_capability response when PT_PERMANENT is requested. */ +typedef struct { + uint32_t ownerAuthSet : 1; + uint32_t endorsementAuthSet : 1; + uint32_t lockoutAuthSet : 1; + uint32_t reserved3_7 : 5; + uint32_t disableClear : 1; + uint32_t inLockout : 1; + uint32_t tpmGeneratedEPS : 1; + uint32_t reserved11_31 : 21; +} TPMA_PERMANENT; + +typedef struct { + uint32_t TPMA_NV_PPWRITE : 1; + uint32_t TPMA_NV_OWNERWRITE : 1; + uint32_t TPMA_NV_AUTHWRITE : 1; + uint32_t TPMA_NV_POLICYWRITE : 1; + uint32_t TPMA_NV_COUNTER : 1; + uint32_t TPMA_NV_BITS : 1; + uint32_t TPMA_NV_EXTEND : 1; + uint32_t reserved7_9 : 3; + uint32_t TPMA_NV_POLICY_DELETE : 1; + uint32_t TPMA_NV_WRITELOCKED : 1; + uint32_t TPMA_NV_WRITEALL : 1; + uint32_t TPMA_NV_WRITEDEFINE : 1; + uint32_t TPMA_NV_WRITE_STCLEAR : 1; + uint32_t TPMA_NV_GLOBALLOCK : 1; + uint32_t TPMA_NV_PPREAD : 1; + uint32_t TPMA_NV_OWNERREAD : 1; + uint32_t TPMA_NV_AUTHREAD : 1; + uint32_t TPMA_NV_POLICYREAD : 1; + uint32_t reserved20_24 : 5; + uint32_t TPMA_NV_NO_DA : 1; + uint32_t TPMA_NV_ORDERLY : 1; + uint32_t TPMA_NV_CLEAR_STCLEAR : 1; + uint32_t TPMA_NV_READLOCKED : 1; + uint32_t TPMA_NV_WRITTEN : 1; + uint32_t TPMA_NV_PLATFORMCREATE : 1; + uint32_t TPMA_NV_READ_STCLEAR : 1; +} TPMA_NV; + +typedef union { + struct { + uint16_t size; + const uint8_t *buffer; + } t; + TPM2B b; +} TPM2B_DIGEST; + +typedef TPM2B_DIGEST TPM2B_AUTH; +typedef TPM2B_DIGEST TPM2B_NONCE; + +typedef struct { + TPM_PT property; + uint32_t value; +} TPMS_TAGGED_PROPERTY; + +#define MAX_CAP_DATA (TPM_BUFFER_SIZE - sizeof(struct tpm_header) - \ + sizeof(TPMI_YES_NO) - sizeof(TPM_CAP) - sizeof(uint32_t)) +#define MAX_TPM_PROPERTIES (MAX_CAP_DATA/sizeof(TPMS_TAGGED_PROPERTY)) + +/* Somewhat arbitrary, leave enough room for command wrappers. */ +#define MAX_NV_BUFFER_SIZE (TPM_BUFFER_SIZE - sizeof(struct tpm_header) - 50) + +typedef struct { + uint32_t count; + TPMS_TAGGED_PROPERTY tpmProperty[MAX_TPM_PROPERTIES]; +} TPML_TAGGED_TPM_PROPERTY; + +typedef union { + TPML_TAGGED_TPM_PROPERTY tpmProperties; +} TPMU_CAPABILITIES; + +typedef struct { + TPM_CAP capability; + TPMU_CAPABILITIES data; +} TPMS_CAPABILITY_DATA; + +struct get_cap_response { + TPMI_YES_NO more_data; + TPMS_CAPABILITY_DATA cd; +}; + +typedef struct { + TPMI_RH_NV_INDEX nvIndex; + TPMI_ALG_HASH nameAlg; + TPMA_NV attributes; + TPM2B_DIGEST authPolicy; + uint16_t dataSize; +} TPMS_NV_PUBLIC; + +typedef union { + struct { + uint16_t size; + TPMS_NV_PUBLIC nvPublic; + } t; + TPM2B b; +} TPM2B_NV_PUBLIC; + +typedef union { + struct { + uint16_t size; + const uint8_t *buffer; + } t; + TPM2B b; +} TPM2B_MAX_NV_BUFFER; + +/* + * This is a union, but as of now we support just one digest - sha256, so + * there is just one element. + */ +typedef union { + uint8_t sha256[SHA256_DIGEST_SIZE]; +} TPMU_HA; + +typedef struct { + TPMI_ALG_HASH hashAlg; + TPMU_HA digest; +} TPMT_HA; + +typedef struct { + uint32_t count; + TPMT_HA digests[1]; /* Limit max number of hashes to 1. */ +} TPML_DIGEST_VALUES; + +struct nv_read_response { + uint32_t params_size; + TPM2B_MAX_NV_BUFFER buffer; +}; + +struct vendor_command_response { + uint16_t vc_subcommand; + union { + uint8_t num_restored_headers; + }; +}; + +struct tpm2_session_attrs { + uint8_t continueSession : 1; + uint8_t auditExclusive : 1; + uint8_t auditReset : 1; + uint8_t reserved3_4 : 2; + uint8_t decrypt : 1; + uint8_t encrypt : 1; + uint8_t audit : 1; +}; + +/* + * TPM session header for commands requiring session information. Also + * included in the responses to those commands. + */ +struct tpm2_session_header { + uint32_t session_handle; + uint16_t nonce_size; + uint8_t *nonce; + union { + struct tpm2_session_attrs session_attr_bits; + uint8_t session_attrs; + } __packed; + uint16_t auth_size; + uint8_t *auth; +}; + +struct tpm2_response { + struct tpm_header hdr; + union { + struct get_cap_response gc; + struct nv_read_response nvr; + struct tpm2_session_header def_space; + struct vendor_command_response vcr; + }; +}; + +struct tpm2_nv_define_space_cmd { + TPM2B_AUTH auth; + TPMS_NV_PUBLIC publicInfo; +}; + +struct tpm2_nv_write_cmd { + TPMI_RH_NV_INDEX nvIndex; + TPM2B_MAX_NV_BUFFER data; + uint16_t offset; +}; + +struct tpm2_self_test { + TPMI_YES_NO yes_no; +}; + +struct tpm2_nv_read_cmd { + TPMI_RH_NV_INDEX nvIndex; + uint16_t size; + uint16_t offset; +}; + +struct tpm2_nv_write_lock_cmd { + TPMI_RH_NV_INDEX nvIndex; +}; + +struct tpm2_pcr_extend_cmd { + TPMI_DH_PCR pcrHandle; + TPML_DIGEST_VALUES digests; +}; + +struct tpm2_hierarchy_control_cmd { + TPMI_RH_ENABLES enable; + TPMI_YES_NO state; +}; + +#endif // TCG2_TSS_STRUCTURES_H_ |