From 4c87a1868835d05f1cadae7b8ad6a7c95d9d9c0e Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Tue, 14 Mar 2017 15:40:33 -0400 Subject: Initial commit of EFI TBOOT work from internal project. Signed-off-by: Ross Philipson --- tboot/tpm_12.c | 1952 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1952 insertions(+) create mode 100644 tboot/tpm_12.c (limited to 'tboot/tpm_12.c') diff --git a/tboot/tpm_12.c b/tboot/tpm_12.c new file mode 100644 index 0000000..6e326d1 --- /dev/null +++ b/tboot/tpm_12.c @@ -0,0 +1,1952 @@ +/* + * tpm_12.c: TPM1.2-related support functions + * + * Copyright (c) 2006-2013, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * return code: + * The TPM has five types of return code. One indicates successful operation + * and four indicate failure. + * TPM_SUCCESS (00000000) indicates successful execution. + * The failure reports are: + * TPM defined fatal errors (00000001 to 000003FF) + * vendor defined fatal errors (00000400 to 000007FF) + * TPM defined non-fatal errors (00000800 to 00000BFF) + * vendor defined non-fatal errors (00000C00 to 00000FFF). + * Here only give definitions for a few commonly used return code. + */ +#define TPM_BASE 0x00000000 +#define TPM_NON_FATAL 0x00000800 +#define TPM_SUCCESS TPM_BASE +#define TPM_BADINDEX (TPM_BASE + 2) +#define TPM_BAD_PARAMETER (TPM_BASE + 3) +#define TPM_DEACTIVATED (TPM_BASE + 6) +#define TPM_DISABLED (TPM_BASE + 7) +#define TPM_FAIL (TPM_BASE + 9) +#define TPM_BAD_ORDINAL (TPM_BASE + 10) +#define TPM_NOSPACE (TPM_BASE + 17) +#define TPM_NOTRESETABLE (TPM_BASE + 50) +#define TPM_NOTLOCAL (TPM_BASE + 51) +#define TPM_BAD_LOCALITY (TPM_BASE + 61) +#define TPM_READ_ONLY (TPM_BASE + 62) +#define TPM_NOT_FULLWRITE (TPM_BASE + 70) +#define TPM_RETRY (TPM_BASE + TPM_NON_FATAL) + +typedef uint8_t tpm_locality_selection_t; +#define TPM_LOC_ZERO 0x01 +#define TPM_LOC_ONE 0x02 +#define TPM_LOC_TWO 0x04 +#define TPM_LOC_THREE 0x08 +#define TPM_LOC_FOUR 0x10 +#define TPM_LOC_RSVD 0xE0 + +/* ~5 secs are required for Infineon that requires this, so leave some extra */ +#define MAX_SAVESTATE_RETRIES 60 + +#define TPM_TAG_RQU_COMMAND 0x00C1 +#define TPM_TAG_RQU_AUTH1_COMMAND 0x00C2 +#define TPM_TAG_RQU_AUTH2_COMMAND 0x00C3 +#define TPM_ORD_PCR_EXTEND 0x00000014 +#define TPM_ORD_PCR_READ 0x00000015 +#define TPM_ORD_PCR_RESET 0x000000C8 +#define TPM_ORD_NV_READ_VALUE 0x000000CF +#define TPM_ORD_NV_WRITE_VALUE 0x000000CD +#define TPM_ORD_GET_CAPABILITY 0x00000065 +#define TPM_ORD_SEAL 0x00000017 +#define TPM_ORD_UNSEAL 0x00000018 +#define TPM_ORD_OSAP 0x0000000B +#define TPM_ORD_OIAP 0x0000000A +#define TPM_ORD_SAVE_STATE 0x00000098 +#define TPM_ORD_GET_RANDOM 0x00000046 + +#define TPM_TAG_PCR_INFO_LONG 0x0006 +#define TPM_TAG_STORED_DATA12 0x0016 + +/* + * specified as minimum cmd buffer size should be supported by all 1.2 TPM + * device in the TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf + */ +#define TPM_CMD_SIZE_MAX 768 +#define TPM_RSP_SIZE_MAX 768 + +/* + * The _tpm12_submit_cmd function comes with 2 global buffers: cmd_buf & rsp_buf. + * Before calling, caller should fill cmd arguements into cmd_buf via + * WRAPPER_IN_BUF macro. After calling, caller should fetch result from + * rsp_buffer via WRAPPER_OUT_BUF macro. + * cmd_buf content: + * 0 1 2 3 4 5 6 7 8 9 10 ... + * ------------------------------------------------------------- + * | TAG | SIZE | ORDINAL | arguments ... + * ------------------------------------------------------------- + * rsp_buf content: + * 0 1 2 3 4 5 6 7 8 9 10 ... + * ------------------------------------------------------------- + * | TAG | SIZE | RETURN CODE | other data ... + * ------------------------------------------------------------- + * + * locality : TPM locality (0 - 4) + * tag : The TPM command tag + * cmd : The TPM command ordinal + * arg_size : Size of argument data. + * out_size : IN/OUT paramter. The IN is the expected size of out data; + * the OUT is the size of output data within out buffer. + * The out_size MUST NOT be NULL. + * return : TPM_SUCCESS for success, for other error code, refer to the .h + */ +static uint8_t cmd_buf[TPM_CMD_SIZE_MAX]; +static uint8_t rsp_buf[TPM_RSP_SIZE_MAX]; +#define WRAPPER_IN_BUF (cmd_buf + CMD_HEAD_SIZE) +#define WRAPPER_OUT_BUF (rsp_buf + RSP_HEAD_SIZE) +#define WRAPPER_IN_MAX_SIZE (TPM_CMD_SIZE_MAX - CMD_HEAD_SIZE) +#define WRAPPER_OUT_MAX_SIZE (TPM_RSP_SIZE_MAX - RSP_HEAD_SIZE) + +static uint32_t _tpm12_submit_cmd(uint32_t locality, uint16_t tag, uint32_t cmd, uint32_t arg_size, uint32_t *out_size) +{ + uint32_t ret; + uint32_t cmd_size, rsp_size = 0; + + if ( out_size == NULL ) { + printk(TBOOT_WARN"TPM: invalid param for _tpm12_submit_cmd()\n"); + return TPM_BAD_PARAMETER; + } + + /* + * real cmd size should add 10 more bytes: + * 2 bytes for tag + * 4 bytes for size + * 4 bytes for ordinal + */ + cmd_size = CMD_HEAD_SIZE + arg_size; + + if ( cmd_size > TPM_CMD_SIZE_MAX ) { + printk(TBOOT_WARN"TPM: cmd exceeds the max supported size.\n"); + return TPM_BAD_PARAMETER; + } + + /* copy tag, size & ordinal into buf in a reversed byte order */ + reverse_copy(cmd_buf, &tag, sizeof(tag)); + reverse_copy(cmd_buf + CMD_SIZE_OFFSET, &cmd_size, sizeof(cmd_size)); + reverse_copy(cmd_buf + CMD_CC_OFFSET, &cmd, sizeof(cmd)); + + rsp_size = RSP_HEAD_SIZE + *out_size; + rsp_size = (rsp_size > TPM_RSP_SIZE_MAX) ? TPM_RSP_SIZE_MAX: rsp_size; + if ( !tpm_submit_cmd(locality, cmd_buf, cmd_size, rsp_buf, &rsp_size) ) return TPM_FAIL; + + /* + * should subtract 10 bytes from real response size: + * 2 bytes for tag + * 4 bytes for size + * 4 bytes for return code + */ + rsp_size -= (rsp_size > RSP_HEAD_SIZE) ? RSP_HEAD_SIZE : rsp_size; + + reverse_copy(&ret, rsp_buf + RSP_RST_OFFSET, sizeof(uint32_t)); + if ( ret != TPM_SUCCESS ) return ret; + + if ( *out_size == 0 || rsp_size == 0 ) *out_size = 0; + else + *out_size = (rsp_size < *out_size) ? rsp_size : *out_size; + + return ret; +} + +static inline uint32_t tpm12_submit_cmd(uint32_t locality, uint32_t cmd, uint32_t arg_size, uint32_t *out_size) +{ + return _tpm12_submit_cmd(locality, TPM_TAG_RQU_COMMAND, cmd, arg_size, out_size); +} + +static inline uint32_t tpm12_submit_cmd_auth1(uint32_t locality, uint32_t cmd, + uint32_t arg_size, uint32_t *out_size) +{ + return _tpm12_submit_cmd(locality, TPM_TAG_RQU_AUTH1_COMMAND, cmd, + arg_size, out_size); +} + +static inline uint32_t tpm12_submit_cmd_auth2(uint32_t locality, uint32_t cmd, + uint32_t arg_size, uint32_t *out_size) +{ + return _tpm12_submit_cmd(locality, TPM_TAG_RQU_AUTH2_COMMAND, cmd, + arg_size, out_size); +} + +typedef struct __packed { + uint8_t digest[SHA1_LENGTH]; +} tpm12_digest_t; + +#define TPM_NR_PCRS 24 +static bool tpm12_pcr_read(struct tpm_if *ti, uint32_t locality, + uint32_t pcr, tpm_pcr_value_t *out) +{ + uint32_t ret, out_size = sizeof(*out); + + if ( out == NULL || pcr >= TPM_NR_PCRS) { + ti->error = TPM_BAD_PARAMETER; + return false; + } + + /* copy pcr into buf in reversed byte order */ + reverse_copy(WRAPPER_IN_BUF, &pcr, sizeof(pcr)); + + ret = tpm12_submit_cmd(locality, TPM_ORD_PCR_READ, sizeof(pcr), &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: Pcr %d Read return value = %08X\n", pcr, ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: Pcr %d Read return value = %08X\n", pcr, ret); + ti->error = ret; + return false; + } + + if ( out_size > sizeof(*out) ) + out_size = sizeof(*out); + memcpy((void *)out, WRAPPER_OUT_BUF, out_size); + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, ((tpm12_digest_t *)out)->digest, out_size); + } +#endif + + return true; +} + +static bool _tpm12_pcr_extend(struct tpm_if *ti, uint32_t locality, + uint32_t pcr, const tpm_digest_t* in) +{ + uint32_t ret, in_size = 0, out_size; + tpm12_digest_t * out = NULL; + + if ( ti == NULL ) + return false; + + if ( in == NULL || pcr >= TPM_NR_PCRS){ + ti->error = TPM_BAD_PARAMETER; + return false; + } + if ( out == NULL ) + out_size = 0; + else + out_size = sizeof(*out); + + /* copy pcr into buf in reversed byte order, then copy in data */ + reverse_copy(WRAPPER_IN_BUF, &pcr, sizeof(pcr)); + in_size += sizeof(pcr); + memcpy(WRAPPER_IN_BUF + in_size, (void *)in, sizeof(*(tpm12_digest_t *)in)); + in_size += sizeof(*(tpm12_digest_t *)in); + + ret = tpm12_submit_cmd(locality, TPM_ORD_PCR_EXTEND, in_size, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: Pcr %d extend, return value = %08X\n", pcr, ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: Pcr %d extend, return value = %08X\n", pcr, ret); + ti->error = ret; + return false; + } + + if ( out != NULL && out_size > 0 ) { + out_size = (out_size > sizeof(*out)) ? sizeof(*out) : out_size; + memcpy((void *)out, WRAPPER_OUT_BUF, out_size); + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, out->digest, out_size); + } +#endif + + return true; +} + +static bool tpm12_pcr_extend(struct tpm_if *ti, uint32_t locality, + uint32_t pcr, const hash_list_t *in) +{ + tpm_digest_t digest; + + if ( ti == NULL ) + return false; + + if ( in == NULL || in->count != 1 || + in->entries[0].alg != TB_HALG_SHA1 ) { + ti->error = TPM_BAD_PARAMETER; + return false; + } + + digest = in->entries[0].hash; + + return _tpm12_pcr_extend(ti, locality, pcr, &digest); +} + +typedef struct __packed { + uint16_t size_of_select; + uint8_t pcr_select[3]; +} tpm_pcr_selection_t; + +/* PCRs lower than 16 are not resetable */ +#define TPM_PCR_RESETABLE_MIN 16 +static bool tpm12_pcr_reset(struct tpm_if *ti, uint32_t locality, uint32_t pcr) +{ + uint32_t ret, in_size, out_size = 0; + uint16_t size_of_select; + tpm_pcr_selection_t pcr_sel = {0,{0,}}; + + if ( ti == NULL ) + return false; + + if ( pcr >= TPM_NR_PCRS || pcr < TPM_PCR_RESETABLE_MIN ) { + ti->error = TPM_BAD_PARAMETER; + return false; + } + + /* the pcr_sel.pcr_select[size_of_select - 1] should not be 0 */ + size_of_select = pcr / 8 + 1; + reverse_copy(&pcr_sel.size_of_select, &size_of_select, + sizeof(size_of_select)); + pcr_sel.pcr_select[pcr / 8] = 1 << (pcr % 8); + + in_size = sizeof(pcr_sel); + memcpy(WRAPPER_IN_BUF, (void *)&pcr_sel, in_size); + + ret = tpm12_submit_cmd(locality, TPM_ORD_PCR_RESET, in_size, &out_size); + if ( ret != TPM_SUCCESS ) { + ti->error = ret; + return false; + } + + printk(TBOOT_DETA"TPM: Pcr %d reset, return value = %08X\n", pcr, ret); + + return true; +} + +#define TPM_NV_READ_VALUE_DATA_SIZE_MAX (TPM_RSP_SIZE_MAX - 14) +static bool tpm12_nv_read_value(struct tpm_if *ti, uint32_t locality, + uint32_t index, uint32_t offset, + uint8_t *data, uint32_t *data_size) +{ + uint32_t ret, in_size = 0, out_size; + + if ( ti == NULL ) + return false; + + if ( data == NULL || data_size == NULL || *data_size == 0 ) { + ti->error = TPM_BAD_PARAMETER; + return false; + } + if ( *data_size > TPM_NV_READ_VALUE_DATA_SIZE_MAX ) + *data_size = TPM_NV_READ_VALUE_DATA_SIZE_MAX; + + /* copy the index, offset and *data_size into buf in reversed byte order */ + reverse_copy(WRAPPER_IN_BUF, &index, sizeof(index)); + in_size += sizeof(index); + reverse_copy(WRAPPER_IN_BUF + in_size, &offset, sizeof(offset)); + in_size += sizeof(offset); + reverse_copy(WRAPPER_IN_BUF + in_size, data_size, sizeof(*data_size)); + in_size += sizeof(*data_size); + + out_size = *data_size + sizeof(*data_size); + ret = tpm12_submit_cmd(locality, TPM_ORD_NV_READ_VALUE, in_size, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: read nv index %08x from offset %08x, return value = %08X\n", + index, offset, ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: read nv index %08x offset %08x, return value = %08X\n", + index, offset, ret); + ti->error = ret; + return false; + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, WRAPPER_OUT_BUF, out_size); + } +#endif + + if ( out_size <= sizeof(*data_size) ) { + *data_size = 0; + return true; + } + + out_size -= sizeof(*data_size); + reverse_copy(data_size, WRAPPER_OUT_BUF, sizeof(*data_size)); + *data_size = (*data_size > out_size) ? out_size : *data_size; + if( *data_size > 0 ) + memcpy(data, WRAPPER_OUT_BUF + sizeof(*data_size), *data_size); + + return true; +} + +#define TPM_NV_WRITE_VALUE_DATA_SIZE_MAX (TPM_CMD_SIZE_MAX - 22) +static bool tpm12_nv_write_value(struct tpm_if *ti, uint32_t locality, + uint32_t index, uint32_t offset, + const uint8_t *data, uint32_t data_size) +{ + uint32_t ret, in_size = 0, out_size = 0; + + if ( ti == NULL ) + return false; + + if ( data == NULL || data_size == 0 + || data_size > TPM_NV_WRITE_VALUE_DATA_SIZE_MAX ) { + ti->error = TPM_BAD_PARAMETER; + return false; + } + + /* copy index, offset and *data_size into buf in reversed byte order */ + reverse_copy(WRAPPER_IN_BUF, &index, sizeof(index)); + in_size += sizeof(index); + reverse_copy(WRAPPER_IN_BUF + in_size, &offset, sizeof(offset)); + in_size += sizeof(offset); + reverse_copy(WRAPPER_IN_BUF + in_size, &data_size, sizeof(data_size)); + in_size += sizeof(data_size); + memcpy(WRAPPER_IN_BUF + in_size, data, data_size); + in_size += data_size; + + ret = tpm12_submit_cmd(locality, TPM_ORD_NV_WRITE_VALUE, + in_size, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: write nv %08x, offset %08x, %08x bytes, return = %08X\n", + index, offset, data_size, ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: write nv %08x, offset %08x, %08x bytes, return = %08X\n", + index, offset, data_size, ret); + ti->error = ret; + return false; + } + + return true; +} + +#define TPM_CAP_VERSION_VAL 0x1A + +typedef uint16_t tpm_structure_tag_t; + +typedef struct __packed { + uint8_t major; + uint8_t minor; + uint8_t rev_major; + uint8_t rev_minor; +} tpm_version_t; + +typedef struct __packed { + tpm_structure_tag_t tag; + tpm_version_t version; + uint16_t specLevel; + uint8_t errataRev; + uint8_t tpmVendorID[4]; + uint16_t vendorSpecificSize; + uint8_t vendorSpecific[]; +} tpm_cap_version_info_t; + +#define HMAC_BLOCK_SIZE 64 +#define HMAC_OUTPUT_SIZE 20 + +static bool hmac(const uint8_t key[HMAC_OUTPUT_SIZE], const uint8_t *msg, + uint32_t len, uint8_t md[HMAC_OUTPUT_SIZE]) +{ + uint8_t ipad[HMAC_BLOCK_SIZE], opad[HMAC_BLOCK_SIZE]; + uint32_t i; + SHA_CTX ctx; + + COMPILE_TIME_ASSERT(HMAC_OUTPUT_SIZE <= HMAC_BLOCK_SIZE); + + for ( i = 0; i < HMAC_BLOCK_SIZE; i++ ) { + ipad[i] = 0x36; + opad[i] = 0x5C; + } + + for ( i = 0; i < HMAC_OUTPUT_SIZE; i++ ) { + ipad[i] ^= key[i]; + opad[i] ^= key[i]; + } + + SHA1_Init(&ctx); + SHA1_Update(&ctx, ipad, HMAC_BLOCK_SIZE); + SHA1_Update(&ctx, msg, len); + SHA1_Final(md, &ctx); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, opad, HMAC_BLOCK_SIZE); + SHA1_Update(&ctx, md, HMAC_OUTPUT_SIZE); + SHA1_Final(md, &ctx); + + return true; +} + +typedef uint16_t tpm_entity_type_t; +typedef uint32_t tpm_authhandle_t; +typedef struct __packed { + uint8_t nonce[20]; +} tpm_nonce_t; + +#define TPM_ET_SRK 0x0004 +#define TPM_KH_SRK 0x40000000 + +typedef uint32_t tpm_key_handle_t; + +typedef tpm12_digest_t tpm_composite_hash_t; +typedef struct __packed { + tpm_structure_tag_t tag; + tpm_locality_selection_t locality_at_creation; + tpm_locality_selection_t locality_at_release; + tpm_pcr_selection_t creation_pcr_selection; + tpm_pcr_selection_t release_pcr_selection; + tpm_composite_hash_t digest_at_creation; + tpm_composite_hash_t digest_at_release; +} tpm_pcr_info_long_t; + +typedef uint8_t tpm_authdata_t[20]; +typedef tpm_authdata_t tpm_encauth_t; + +typedef struct __packed { + tpm_structure_tag_t tag; + tpm_entity_type_t et; + uint32_t seal_info_size; +} tpm_stored_data12_header_t; + +typedef struct __packed { + tpm_stored_data12_header_t header; + uint32_t enc_data_size; + uint8_t enc_data[]; +} tpm_stored_data12_short_t; + +typedef struct __packed { + tpm_stored_data12_header_t header; + tpm_pcr_info_long_t seal_info; + uint32_t enc_data_size; + uint8_t enc_data[]; +} tpm_stored_data12_t; + +#define UNLOAD_INTEGER(buf, offset, var) {\ + reverse_copy(buf + offset, &(var), sizeof(var));\ + offset += sizeof(var);\ +} + +#define UNLOAD_BLOB(buf, offset, blob, size) {\ + memcpy(buf + offset, blob, size);\ + offset += size;\ +} + +#define UNLOAD_BLOB_TYPE(buf, offset, blob) \ + UNLOAD_BLOB(buf, offset, blob, sizeof(*(blob))) + +#define UNLOAD_PCR_SELECTION(buf, offset, sel) {\ + UNLOAD_INTEGER(buf, offset, (sel)->size_of_select);\ + UNLOAD_BLOB(buf, offset, (sel)->pcr_select, (sel)->size_of_select);\ +} + +#define UNLOAD_PCR_INFO_LONG(buf, offset, info) {\ + UNLOAD_INTEGER(buf, offset, (info)->tag);\ + UNLOAD_BLOB_TYPE(buf, offset, &(info)->locality_at_creation);\ + UNLOAD_BLOB_TYPE(buf, offset, &(info)->locality_at_release);\ + UNLOAD_PCR_SELECTION(buf, offset, &(info)->creation_pcr_selection);\ + UNLOAD_PCR_SELECTION(buf, offset, &(info)->release_pcr_selection);\ + UNLOAD_BLOB_TYPE(buf, offset, &(info)->digest_at_creation);\ + UNLOAD_BLOB_TYPE(buf, offset, &(info)->digest_at_release);\ +} + +#define UNLOAD_STORED_DATA12(buf, offset, hdr) {\ + UNLOAD_INTEGER(buf, offset, ((tpm_stored_data12_header_t *)(hdr))->tag);\ + UNLOAD_INTEGER(buf, offset, ((tpm_stored_data12_header_t *)(hdr))->et);\ + UNLOAD_INTEGER(buf, offset,\ + ((tpm_stored_data12_header_t *)(hdr))->seal_info_size);\ + if ( ((tpm_stored_data12_header_t *)(hdr))->seal_info_size == 0 ) {\ + UNLOAD_INTEGER(buf, offset,\ + ((tpm_stored_data12_short_t *)hdr)->enc_data_size);\ + UNLOAD_BLOB(buf, offset,\ + ((tpm_stored_data12_short_t *)hdr)->enc_data,\ + ((tpm_stored_data12_short_t *)hdr)->enc_data_size);\ + }\ + else {\ + UNLOAD_PCR_INFO_LONG(buf, offset,\ + &((tpm_stored_data12_t *)hdr)->seal_info);\ + UNLOAD_INTEGER(buf, offset,\ + ((tpm_stored_data12_t *)hdr)->enc_data_size);\ + UNLOAD_BLOB(buf, offset,\ + ((tpm_stored_data12_t *)hdr)->enc_data,\ + ((tpm_stored_data12_t *)hdr)->enc_data_size);\ + }\ +} + +#define LOAD_INTEGER(buf, offset, var) {\ + reverse_copy(&(var), buf + offset, sizeof(var));\ + offset += sizeof(var);\ +} + +#define LOAD_BLOB(buf, offset, blob, size) {\ + memcpy(blob, buf + offset, size);\ + offset += size;\ +} + +#define LOAD_BLOB_TYPE(buf, offset, blob) \ + LOAD_BLOB(buf, offset, blob, sizeof(*(blob))) + +#define LOAD_PCR_SELECTION(buf, offset, sel) {\ + LOAD_INTEGER(buf, offset, (sel)->size_of_select);\ + LOAD_BLOB(buf, offset, (sel)->pcr_select, (sel)->size_of_select);\ +} + +#define LOAD_PCR_INFO_LONG(buf, offset, info) {\ + LOAD_INTEGER(buf, offset, (info)->tag);\ + LOAD_BLOB_TYPE(buf, offset, &(info)->locality_at_creation);\ + LOAD_BLOB_TYPE(buf, offset, &(info)->locality_at_release);\ + LOAD_PCR_SELECTION(buf, offset, &(info)->creation_pcr_selection);\ + LOAD_PCR_SELECTION(buf, offset, &(info)->release_pcr_selection);\ + LOAD_BLOB_TYPE(buf, offset, &(info)->digest_at_creation);\ + LOAD_BLOB_TYPE(buf, offset, &(info)->digest_at_release);\ +} + +#define LOAD_STORED_DATA12(buf, offset, hdr) {\ + LOAD_INTEGER(buf, offset, ((tpm_stored_data12_header_t *)(hdr))->tag);\ + LOAD_INTEGER(buf, offset, ((tpm_stored_data12_header_t *)(hdr))->et);\ + LOAD_INTEGER(buf, offset, \ + ((tpm_stored_data12_header_t *)(hdr))->seal_info_size);\ + if ( ((tpm_stored_data12_header_t *)(hdr))->seal_info_size == 0 ) {\ + LOAD_INTEGER(buf, offset,\ + ((tpm_stored_data12_short_t *)hdr)->enc_data_size);\ + LOAD_BLOB(buf, offset,\ + ((tpm_stored_data12_short_t *)hdr)->enc_data,\ + ((tpm_stored_data12_short_t *)hdr)->enc_data_size);\ + }\ + else {\ + LOAD_PCR_INFO_LONG(buf, offset,\ + &((tpm_stored_data12_t *)hdr)->seal_info);\ + LOAD_INTEGER(buf, offset,\ + ((tpm_stored_data12_t *)hdr)->enc_data_size);\ + LOAD_BLOB(buf, offset,\ + ((tpm_stored_data12_t *)hdr)->enc_data,\ + ((tpm_stored_data12_t *)hdr)->enc_data_size);\ + }\ +} + +static uint32_t tpm12_oiap(uint32_t locality, tpm_authhandle_t *hauth, + tpm_nonce_t *nonce_even) +{ + uint32_t ret, offset, out_size; + + if ( hauth == NULL || nonce_even == NULL ) + return TPM_BAD_PARAMETER; + + offset = 0; + + out_size = sizeof(*hauth) + sizeof(*nonce_even); + + ret = tpm12_submit_cmd(locality, TPM_ORD_OIAP, offset, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: start OIAP, return value = %08X\n", ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: start OIAP, return value = %08X\n", ret); + return ret; + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, WRAPPER_OUT_BUF, out_size); + } +#endif + + offset = 0; + LOAD_INTEGER(WRAPPER_OUT_BUF, offset, *hauth); + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, nonce_even); + + return ret; +} + +static uint32_t tpm12_osap(uint32_t locality, tpm_entity_type_t ent_type, + uint32_t ent_value, const tpm_nonce_t *odd_osap, + tpm_authhandle_t *hauth, tpm_nonce_t *nonce_even, + tpm_nonce_t *even_osap) +{ + uint32_t ret, offset, out_size; + + if ( odd_osap == NULL || hauth == NULL || + nonce_even == NULL || even_osap == NULL ) + return TPM_BAD_PARAMETER; + + offset = 0; + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, ent_type); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, ent_value); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, odd_osap); + + out_size = sizeof(*hauth) + sizeof(*nonce_even) + sizeof(*even_osap); + ret = tpm12_submit_cmd(locality, TPM_ORD_OSAP, offset, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: start OSAP, return value = %08X\n", ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: start OSAP, return value = %08X\n", ret); + return ret; + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, WRAPPER_OUT_BUF, out_size); + } +#endif + + offset = 0; + LOAD_INTEGER(WRAPPER_OUT_BUF, offset, *hauth); + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, nonce_even); + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, even_osap); + + return ret; +} + +static uint32_t _tpm12_seal(uint32_t locality, tpm_key_handle_t hkey, + const tpm_encauth_t *enc_auth, uint32_t pcr_info_size, + const tpm_pcr_info_long_t *pcr_info, uint32_t in_data_size, + const uint8_t *in_data, + tpm_authhandle_t hauth, const tpm_nonce_t *nonce_odd, + uint8_t *cont_session, const tpm_authdata_t *pub_auth, + uint32_t *sealed_data_size, uint8_t *sealed_data, + tpm_nonce_t *nonce_even, tpm_authdata_t *res_auth) +{ + uint32_t ret, offset, out_size; + + if ( enc_auth == NULL || pcr_info == NULL || in_data == NULL || + nonce_odd == NULL || cont_session == NULL || pub_auth == NULL || + sealed_data_size == NULL || sealed_data == NULL || + nonce_even == NULL || res_auth == NULL ) { + printk(TBOOT_WARN"TPM: _tpm12_seal() bad parameter\n"); + return TPM_BAD_PARAMETER; + } + + offset = 0; + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, hkey); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, enc_auth); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, pcr_info_size); + UNLOAD_PCR_INFO_LONG(WRAPPER_IN_BUF, offset, pcr_info); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, in_data_size); + UNLOAD_BLOB(WRAPPER_IN_BUF, offset, in_data, in_data_size); + + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, hauth); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, nonce_odd); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, *cont_session); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, pub_auth); + + out_size = WRAPPER_OUT_MAX_SIZE; + + ret = tpm12_submit_cmd_auth1(locality, TPM_ORD_SEAL, offset, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: seal data, return value = %08X\n", ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: seal data, return value = %08X\n", ret); + return ret; + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, WRAPPER_OUT_BUF, out_size); + } +#endif + + if ( *sealed_data_size < + ( out_size - sizeof(*nonce_even) - sizeof(*cont_session) + - sizeof(*res_auth) ) ) { + printk(TBOOT_WARN"TPM: sealed blob is too small\n"); + return TPM_NOSPACE; + } + + offset = 0; + LOAD_STORED_DATA12(WRAPPER_OUT_BUF, offset, sealed_data); + *sealed_data_size = offset; + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, nonce_even); + LOAD_INTEGER(WRAPPER_OUT_BUF, offset, *cont_session); + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, res_auth); + + return ret; +} + +static uint32_t _tpm12_unseal(uint32_t locality, tpm_key_handle_t hkey, + const uint8_t *in_data, + tpm_authhandle_t hauth, const tpm_nonce_t *nonce_odd, + uint8_t *cont_session, const tpm_authdata_t *auth, + tpm_authhandle_t hauth_d, const tpm_nonce_t *nonce_odd_d, + uint8_t *cont_session_d, const tpm_authdata_t *auth_d, + uint32_t *secret_size, uint8_t *secret, + tpm_nonce_t *nonce_even, tpm_authdata_t *res_auth, + tpm_nonce_t *nonce_even_d, tpm_authdata_t *res_auth_d) +{ + uint32_t ret, offset, out_size; + + if ( in_data == NULL || nonce_odd == NULL || cont_session == NULL || + auth == NULL || nonce_odd_d == NULL || cont_session_d == NULL || + auth_d == NULL || secret_size == NULL || secret == NULL || + nonce_even == NULL || res_auth == NULL || nonce_even_d == NULL || + res_auth_d == NULL ) { + printk(TBOOT_WARN"TPM: _tpm_unseal() bad parameter\n"); + return TPM_BAD_PARAMETER; + } + + offset = 0; + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, hkey); + UNLOAD_STORED_DATA12(WRAPPER_IN_BUF, offset, in_data); + + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, hauth); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, nonce_odd); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, *cont_session); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, auth); + + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, hauth_d); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, nonce_odd_d); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, *cont_session_d); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, auth_d); + + out_size = WRAPPER_OUT_MAX_SIZE; + + ret = tpm12_submit_cmd_auth2(locality, TPM_ORD_UNSEAL, offset, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: unseal data, return value = %08X\n", ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: unseal data, return value = %08X\n", ret); + return ret; + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, WRAPPER_OUT_BUF, out_size); + } +#endif + + if ( *secret_size < + ( out_size - sizeof(*secret_size) - sizeof(*nonce_even) + - sizeof(*cont_session) - sizeof(*res_auth) - sizeof(*nonce_even_d) + - sizeof(*cont_session_d) - sizeof(*res_auth_d) ) ) { + printk(TBOOT_WARN"TPM: unsealed data too small\n"); + return TPM_NOSPACE; + } + + offset = 0; + LOAD_INTEGER(WRAPPER_OUT_BUF, offset, *secret_size); + LOAD_BLOB(WRAPPER_OUT_BUF, offset, secret, *secret_size); + + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, nonce_even); + LOAD_INTEGER(WRAPPER_OUT_BUF, offset, *cont_session); + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, res_auth); + + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, nonce_even_d); + LOAD_INTEGER(WRAPPER_OUT_BUF, offset, *cont_session_d); + LOAD_BLOB_TYPE(WRAPPER_OUT_BUF, offset, res_auth_d); + + return ret; +} + +#define XOR_BLOB_TYPE(data, pad) {\ + for ( uint32_t i = 0; i < sizeof(*(data)); i++ ) \ + ((uint8_t *)data)[i] ^= ((uint8_t *)pad)[i % sizeof(*(pad))];\ +} + +static const tpm_authdata_t srk_authdata = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const tpm_authdata_t blob_authdata = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static uint32_t _tpm12_wrap_seal(uint32_t locality, + const tpm_pcr_info_long_t *pcr_info, + uint32_t in_data_size, const uint8_t *in_data, + uint32_t *sealed_data_size, uint8_t *sealed_data) +{ + uint32_t ret; + tpm_nonce_t odd_osap, even_osap, nonce_even, nonce_odd; + tpm_authhandle_t hauth; + tpm_authdata_t shared_secret, pub_auth, res_auth; + tpm_encauth_t enc_auth; + uint8_t cont_session = false; + tpm_key_handle_t hkey = TPM_KH_SRK; + uint32_t pcr_info_size = sizeof(*pcr_info); + uint32_t offset; + uint32_t ordinal = TPM_ORD_SEAL; + tpm12_digest_t digest; + + /* skip generate nonce for odd_osap, just use the random value in stack */ + + /* establish a osap session */ + ret = tpm12_osap(locality, TPM_ET_SRK, TPM_KH_SRK, &odd_osap, &hauth, + &nonce_even, &even_osap); + if ( ret != TPM_SUCCESS ) + return ret; + + /* calculate the shared secret + shared-secret = HMAC(srk_auth, even_osap || odd_osap) */ + offset = 0; + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &even_osap); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &odd_osap); + hmac((uint8_t *)&srk_authdata, WRAPPER_IN_BUF, offset, + (uint8_t *)&shared_secret); + + /* generate ecrypted authdata for data + enc_auth = XOR(authdata, sha1(shared_secret || last_even_nonce)) */ + offset = 0; + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &shared_secret); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &nonce_even); + sha1_buffer(WRAPPER_IN_BUF, offset, (uint8_t *)&digest); + memcpy(&enc_auth, &blob_authdata, sizeof(blob_authdata)); + XOR_BLOB_TYPE(&enc_auth, &digest); + + /* skip generate nonce for nonce_odd, just use the random value in stack */ + + /* calculate authdata */ + /* in_param_digest = sha1(1S ~ 6S) */ + offset = 0; + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, ordinal); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &enc_auth); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, pcr_info_size); + UNLOAD_PCR_INFO_LONG(WRAPPER_IN_BUF, offset, pcr_info); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, in_data_size); + UNLOAD_BLOB(WRAPPER_IN_BUF, offset, in_data, in_data_size); + sha1_buffer(WRAPPER_IN_BUF, offset, (uint8_t *)&digest); + + /* authdata = hmac(key, in_param_digest || auth_params) */ + offset = 0; + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &digest); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &nonce_even); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &nonce_odd); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, cont_session); + hmac((uint8_t *)&shared_secret, WRAPPER_IN_BUF, offset, + (uint8_t *)&pub_auth); + + /* call the simple seal function */ + ret = _tpm12_seal(locality, hkey, (const tpm_encauth_t *)&enc_auth, + pcr_info_size, pcr_info, in_data_size, in_data, + hauth, &nonce_odd, &cont_session, + (const tpm_authdata_t *)&pub_auth, + sealed_data_size, sealed_data, + &nonce_even, &res_auth); + + /* skip check for res_auth */ + + return ret; +} + +static uint32_t _tpm12_wrap_unseal(uint32_t locality, const uint8_t *in_data, + uint32_t *secret_size, uint8_t *secret) +{ + uint32_t ret; + tpm_nonce_t odd_osap, even_osap; + tpm_nonce_t nonce_even, nonce_odd, nonce_even_d, nonce_odd_d; + tpm_authhandle_t hauth, hauth_d; + tpm_authdata_t shared_secret; + tpm_authdata_t pub_auth, res_auth, pub_auth_d, res_auth_d; + uint8_t cont_session = false, cont_session_d = false; + tpm_key_handle_t hkey = TPM_KH_SRK; + uint32_t offset; + uint32_t ordinal = TPM_ORD_UNSEAL; + tpm12_digest_t digest; + + /* skip generate nonce for odd_osap, just use the random value in stack */ + + /* establish a osap session */ + ret = tpm12_osap(locality, TPM_ET_SRK, TPM_KH_SRK, &odd_osap, &hauth, + &nonce_even, &even_osap); + if ( ret != TPM_SUCCESS ) + return ret; + + /* calculate the shared secret + shared-secret = HMAC(auth, even_osap || odd_osap) */ + offset = 0; + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &even_osap); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &odd_osap); + hmac((uint8_t *)&srk_authdata, WRAPPER_IN_BUF, offset, + (uint8_t *)&shared_secret); + + /* establish a oiap session */ + ret = tpm12_oiap(locality, &hauth_d, &nonce_even_d); + if ( ret != TPM_SUCCESS ) + return ret; + + /* skip generate nonce_odd & nonce_odd_d, just use the random values */ + + /* calculate authdata */ + /* in_param_digest = sha1(1S ~ 6S) */ + offset = 0; + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, ordinal); + UNLOAD_STORED_DATA12(WRAPPER_IN_BUF, offset, in_data); + sha1_buffer(WRAPPER_IN_BUF, offset, (uint8_t *)&digest); + + /* authdata1 = hmac(key, in_param_digest || auth_params1) */ + offset = 0; + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &digest); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &nonce_even); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &nonce_odd); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, cont_session); + hmac((uint8_t *)&shared_secret, WRAPPER_IN_BUF, offset, + (uint8_t *)&pub_auth); + + /* authdata2 = hmac(key, in_param_digest || auth_params2) */ + offset = 0; + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &digest); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &nonce_even_d); + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, &nonce_odd_d); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, cont_session_d); + hmac((uint8_t *)&blob_authdata, WRAPPER_IN_BUF, offset, + (uint8_t *)&pub_auth_d); + + /* call the simple seal function */ + ret = _tpm12_unseal(locality, hkey, in_data, + hauth, &nonce_odd, &cont_session, + (const tpm_authdata_t *)&pub_auth, + hauth_d, &nonce_odd_d, &cont_session_d, + (const tpm_authdata_t *)&pub_auth_d, + secret_size, secret, + &nonce_even, &res_auth, &nonce_even_d, &res_auth_d); + + /* skip check for res_auth */ + + return ret; +} + +static bool init_pcr_info(uint32_t locality, + tpm_locality_selection_t release_locs, + uint32_t nr_create, const uint8_t indcs_create[], + uint32_t nr_release, const uint8_t indcs_release[], + const tpm12_digest_t *values_release[], + tpm_pcr_info_long_t *pcr_info) +{ + uint32_t offset; + uint32_t i, blob_size; + static tpm_locality_selection_t localities[TPM_NR_LOCALITIES] = { + TPM_LOC_ZERO, TPM_LOC_ONE, TPM_LOC_TWO, TPM_LOC_THREE, TPM_LOC_FOUR + }; + + + if ( (release_locs & TPM_LOC_RSVD) != 0 ) + return false; + if ( pcr_info == NULL ) + return false; + if ( locality >= TPM_NR_LOCALITIES ) + return false; + if ( indcs_create == NULL ) + nr_create = 0; + if ( indcs_release == NULL || values_release == NULL ) + nr_release = 0; + for ( i = 0; i < nr_create; i++ ) + if ( indcs_create[i] >= TPM_NR_PCRS ) + return false; + for ( i = 0; i < nr_release; i++ ) { + if ( indcs_release[i] >= TPM_NR_PCRS || values_release[i] == NULL ) + return false; + } + + memset(pcr_info, 0, sizeof(*pcr_info)); + pcr_info->tag = TPM_TAG_PCR_INFO_LONG; + pcr_info->locality_at_creation = localities[locality]; + pcr_info->locality_at_release = release_locs; + pcr_info->creation_pcr_selection.size_of_select = 3; + for ( i = 0; i < nr_create; i++ ) + pcr_info->creation_pcr_selection.pcr_select[indcs_create[i]/8] |= + 1 << (indcs_create[i] % 8); + pcr_info->release_pcr_selection.size_of_select = 3; + for ( i = 0; i < nr_release; i++ ) + pcr_info->release_pcr_selection.pcr_select[indcs_release[i]/8] |= + 1 << (indcs_release[i] % 8); + + if ( nr_release > 0 ) { + offset = 0; + UNLOAD_PCR_SELECTION(WRAPPER_IN_BUF, offset, + &pcr_info->release_pcr_selection); + blob_size = sizeof(tpm12_digest_t) * nr_release; + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, blob_size); + for ( i = 0; i < nr_release; i++ ) + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, values_release[i]); + sha1_buffer(WRAPPER_IN_BUF, offset, + (uint8_t *)&pcr_info->digest_at_release); + } + + return true; +} + +static bool tpm12_seal(struct tpm_if *ti, uint32_t locality, + uint32_t in_data_size, const uint8_t *in_data, + uint32_t *sealed_data_size, uint8_t *sealed_data) +{ + const uint8_t pcr_indcs_create[] = {17, 18}; + const uint8_t pcr_indcs_release[] = {17, 18}; + const tpm12_digest_t *pcr_values_release[] = {NULL, NULL}; /* TODO {(tpm12_digest_t *)&post_launch_pcr17, + (tpm12_digest_t *)&post_launch_pcr18};*/ + uint32_t pcr_nr_create = ARRAY_SIZE(pcr_indcs_create); + uint32_t pcr_nr_release = ARRAY_SIZE(pcr_indcs_release); + uint32_t ret; + tpm_pcr_info_long_t pcr_info; + tpm_locality_selection_t release_locs = 1 << locality; + + if ( ti == NULL ) + return false; + + if ( locality >= TPM_NR_LOCALITIES || + in_data_size == 0 || in_data == NULL || + sealed_data_size == NULL || sealed_data == NULL || + *sealed_data_size == 0 ) { + printk(TBOOT_WARN"TPM: tpm12_seal() bad parameter\n"); + ti->error = TPM_BAD_PARAMETER; + return false; + } + + if ( !init_pcr_info(locality, release_locs, pcr_nr_create, + pcr_indcs_create, pcr_nr_release, pcr_indcs_release, + pcr_values_release, &pcr_info) ) { + printk(TBOOT_WARN"TPM: tpm12_seal() bad parameter\n"); + ti->error = TPM_BAD_PARAMETER; + return false; + } + + ret = _tpm12_wrap_seal(locality, &pcr_info, in_data_size, in_data, + sealed_data_size, sealed_data); + if ( ret != TPM_SUCCESS ) { + ti->error = ret; + return false; + } + + return true; +} + +static bool check_sealed_data(uint32_t size, const uint8_t *data) +{ + if ( size < sizeof(tpm_stored_data12_header_t) ) + return false; + if ( ((tpm_stored_data12_header_t *)data)->tag != TPM_TAG_STORED_DATA12 ) + return false; + + if ( ((tpm_stored_data12_header_t *)data)->seal_info_size == 0 ) { + tpm_stored_data12_short_t *data12_s; + + if ( size < sizeof(*data12_s) ) + return false; + data12_s = (tpm_stored_data12_short_t *)data; + if ( size != sizeof(*data12_s) + data12_s->enc_data_size ) + return false; + } + else { + tpm_stored_data12_t *data12; + + if ( size < sizeof(*data12) ) + return false; + data12 = (tpm_stored_data12_t *)data; + if ( size != sizeof(*data12) + data12->enc_data_size ) + return false; + } + + return true; +} + +static bool tpm12_unseal(struct tpm_if *ti, uint32_t locality, + uint32_t sealed_data_size, const uint8_t *sealed_data, + uint32_t *secret_size, uint8_t *secret) +{ + uint32_t ret; + + if ( ti == NULL ) + return false; + + if ( sealed_data == NULL || + secret_size == NULL || secret == NULL ) { + printk(TBOOT_WARN"TPM: tpm12_unseal() bad parameter\n"); + ti->error = TPM_BAD_PARAMETER; + return false; + } + + if ( !check_sealed_data(sealed_data_size, sealed_data) ) { + printk(TBOOT_WARN"TPM: tpm12_unseal() blob invalid\n"); + ti->error = TPM_BAD_PARAMETER; + return false; + } + + ret = _tpm12_wrap_unseal(locality, sealed_data, secret_size, secret); + if ( ret != TPM_SUCCESS ) { + ti->error = ret; + return false; + } + + return true; +} + +static void calc_pcr_composition(uint32_t nr, const uint8_t indcs[], + const tpm12_digest_t *values[], + tpm_composite_hash_t *composite) +{ + uint32_t i, offset, blob_size; + tpm_pcr_selection_t sel; + + if ( nr == 0 || indcs == NULL || values == NULL || composite == NULL) + return; + + sel.size_of_select = 3; + sel.pcr_select[0] = sel.pcr_select[1] = sel.pcr_select[2] = 0; + for ( i = 0; i < nr; i++ ) + sel.pcr_select[indcs[i]/8] |= 1 << (indcs[i] % 8); + + offset = 0; + UNLOAD_PCR_SELECTION(WRAPPER_IN_BUF, offset, &sel); + blob_size = sizeof(tpm12_digest_t) * nr; + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, blob_size); + for ( i = 0; i < nr; i++ ) + UNLOAD_BLOB_TYPE(WRAPPER_IN_BUF, offset, values[i]); + sha1_buffer(WRAPPER_IN_BUF, offset, (uint8_t *)composite); +} + +static tpm_composite_hash_t *get_cre_pcr_composite(uint8_t *data) +{ + if ( ((tpm_stored_data12_header_t *)data)->seal_info_size == 0 ) + return NULL; + else + return &((tpm_stored_data12_t *)data)->seal_info.digest_at_creation; +} + +static bool tpm12_cmp_creation_pcrs(uint32_t pcr_nr_create, + const uint8_t pcr_indcs_create[], + const tpm12_digest_t *pcr_values_create[], + uint32_t sealed_data_size, uint8_t *sealed_data) +{ + uint32_t i; + tpm_composite_hash_t composite = {{0,}}, *cre_composite; + + if ( pcr_indcs_create == NULL ) + pcr_nr_create = 0; + for ( i = 0; i < pcr_nr_create; i++ ) { + if ( pcr_indcs_create[i] >= TPM_NR_PCRS ) + return false; + } + if ( !check_sealed_data(sealed_data_size, sealed_data) ) { + printk(TBOOT_WARN"TPM: Bad blob.\n"); + return false; + } + + if ( pcr_nr_create > 0 ) + calc_pcr_composition(pcr_nr_create, pcr_indcs_create, + pcr_values_create, &composite); + + cre_composite = get_cre_pcr_composite(sealed_data); + if ( cre_composite == NULL ) + return false; + if ( memcmp(&composite, cre_composite, sizeof(composite)) ) { + printk(TBOOT_WARN"TPM: Not equal to creation composition:\n"); + print_hex(NULL, (uint8_t *)&composite, sizeof(composite)); + print_hex(NULL, (uint8_t *)cre_composite, sizeof(composite)); + return false; + } + + return true; +} + +static bool tpm12_verify_creation(struct tpm_if *ti, uint32_t sealed_data_size, + uint8_t *sealed_data) +{ + uint8_t pcr_indcs_create[] = {17, 18}; + tpm12_digest_t pcr17, pcr18; + const tpm12_digest_t *pcr_values_create[] = {&pcr17, &pcr18}; + int i; + + if ( ti == NULL || sealed_data == NULL ) + return false; + + tpm12_pcr_read(ti, 2, 17, (tpm_pcr_value_t *)&pcr17); + tpm12_pcr_read(ti, 2, 18, (tpm_pcr_value_t *)&pcr18); + + /* to prevent rollback attack using old sealed measurements, + verify that (creation) PCRs at mem integrity seal time are same as + if we extend current PCRs with unsealed VL measurements */ + /* TBD: we should check all DRTM PCRs */ + /* TODO + for ( i = 0; i < g_pre_k_s3_state.num_vl_entries; i++ ) { + if ( g_pre_k_s3_state.vl_entries[i].pcr == 17 ) + extend_hash((tb_hash_t *)&pcr17, + &g_pre_k_s3_state.vl_entries[i].hl.entries[0].hash, TB_HALG_SHA1); + else if ( g_pre_k_s3_state.vl_entries[i].pcr == 18 ) + extend_hash((tb_hash_t *)&pcr18, + &g_pre_k_s3_state.vl_entries[i].hl.entries[0].hash, TB_HALG_SHA1); + }*/ + if ( !tpm12_cmp_creation_pcrs(ARRAY_SIZE(pcr_indcs_create), + pcr_indcs_create, pcr_values_create, + sealed_data_size, + sealed_data) ) { + printk(TBOOT_WARN"extended PCR values don't match creation values in sealed blob.\n"); + return false; + } + + return true; +} + +typedef uint32_t tpm_capability_area_t; + +#define TPM_CAP_NV_INDEX 0x00000011 + +static uint32_t tpm12_get_capability(uint32_t locality, tpm_capability_area_t cap_area, + uint32_t sub_cap_size, const uint8_t *sub_cap, + uint32_t *resp_size, uint8_t *resp) +{ + uint32_t ret, offset, out_size; + + if ( sub_cap == NULL || resp_size == NULL || resp == NULL ) { + printk(TBOOT_WARN"TPM: tpm12_get_capability() bad parameter\n"); + return TPM_BAD_PARAMETER; + } + + offset = 0; + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, cap_area); + UNLOAD_INTEGER(WRAPPER_IN_BUF, offset, sub_cap_size); + UNLOAD_BLOB(WRAPPER_IN_BUF, offset, sub_cap, sub_cap_size); + + out_size = sizeof(*resp_size) + *resp_size; + + ret = tpm12_submit_cmd(locality, TPM_ORD_GET_CAPABILITY, offset, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: get capability, return value = %08X\n", ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: get capability, return value = %08X\n", ret); + return ret; + } + + offset = 0; + LOAD_INTEGER(WRAPPER_OUT_BUF, offset, *resp_size); + if ( out_size < sizeof(*resp_size) + *resp_size ) { + printk(TBOOT_WARN"TPM: capability response too small\n"); + return TPM_FAIL; + } + LOAD_BLOB(WRAPPER_OUT_BUF, offset, resp, *resp_size); + + return ret; +} + +typedef struct __packed { + tpm_pcr_selection_t pcr_selection; + tpm_locality_selection_t locality_at_release; + tpm_composite_hash_t digest_at_release; +} tpm_pcr_info_short_t; + +typedef struct __packed { + tpm_structure_tag_t tag; + uint32_t attributes; +} tpm_nv_attributes_t; + +typedef struct __packed { + tpm_structure_tag_t tag; + uint32_t nv_index; + tpm_pcr_info_short_t pcr_info_read; + tpm_pcr_info_short_t pcr_info_write; + tpm_nv_attributes_t permission; + uint8_t b_read_st_clear; + uint8_t b_write_st_clear; + uint8_t b_write_define; + uint32_t data_size; +} tpm_nv_data_public_t; + +static bool tpm12_get_nvindex_size(struct tpm_if *ti, uint32_t locality, + uint32_t index, uint32_t *size) +{ + uint32_t ret, offset, resp_size; + uint8_t sub_cap[sizeof(index)]; + uint8_t resp[sizeof(tpm_nv_data_public_t)]; + uint32_t idx; + + if ( ti == NULL ) + return false; + + if ( size == NULL ) { + printk(TBOOT_WARN"TPM: tpm12_get_nvindex_size() bad parameter\n"); + ti->error = TPM_BAD_PARAMETER; + return false; + } + + offset = 0; + UNLOAD_INTEGER(sub_cap, offset, index); + + resp_size = sizeof(resp); + ret = tpm12_get_capability(locality, TPM_CAP_NV_INDEX, sizeof(sub_cap), + sub_cap, &resp_size, resp); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: get nvindex size, return value = %08X\n", ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_DETA"TPM: fail to get public data of 0x%08X in TPM NV\n", index); + ti->error = ret; + return false; + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, resp, resp_size); + } +#endif + + /* check size */ + if ( resp_size == 0 ) { + printk(TBOOT_WARN"TPM: Index 0x%08X does not exist\n", index); + ti->error = TPM_BADINDEX; + return false; + } + + /* check index */ + offset = sizeof(tpm_structure_tag_t); + LOAD_INTEGER(resp, offset, idx); +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: get index value = %08X\n", idx); +#endif + + if ( idx != index ) { + printk(TBOOT_DETA"TPM: Index 0x%08X is not the one expected 0x%08X\n", + idx, index); + ti->error = TPM_BADINDEX; + return false; + } + + if ( resp_size != sizeof(resp) ) { + printk(TBOOT_WARN"TPM: public data size of Index 0x%08X responsed incorrect\n", + index); + ti->error = TPM_FAIL; + return false; + } + + offset = resp_size - sizeof(uint32_t); + LOAD_INTEGER(resp, offset, *size); + + return true; +} + +static bool tpm12_get_nvindex_permission(struct tpm_if *ti, uint32_t locality, + uint32_t index, uint32_t *attribute) +{ + uint32_t ret, offset, resp_size; + uint8_t sub_cap[sizeof(index)]; + uint8_t resp[sizeof(tpm_nv_data_public_t)]; + uint32_t idx; + + if ( ti == NULL ) + return false; + + if ( attribute == NULL ) { + printk(TBOOT_WARN"TPM: tpm12_get_nvindex_permission() bad parameter\n"); + ti->error = TPM_BAD_PARAMETER; + return false; + } + + offset = 0; + UNLOAD_INTEGER(sub_cap, offset, index); + + resp_size = sizeof(resp); + ret = tpm12_get_capability(locality, TPM_CAP_NV_INDEX, sizeof(sub_cap), + sub_cap, &resp_size, resp); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: get nvindex permission, return value = %08X\n", ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_WARN"TPM: fail to get public data of 0x%08X in TPM NV\n", index); + ti->error = ret; + return false; + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, resp, resp_size); + } +#endif + + /* check size */ + if ( resp_size == 0 ) { + printk(TBOOT_WARN"TPM: Index 0x%08X does not exist\n", index); + ti->error = TPM_BADINDEX; + return false; + } + + /* check index */ + offset = sizeof(tpm_structure_tag_t); + LOAD_INTEGER(resp, offset, idx); +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: get index value = %08X\n", idx); +#endif + + if ( idx != index ) { + printk(TBOOT_WARN"TPM: Index 0x%08X is not the one expected 0x%08X\n", + idx, index); + ti->error = TPM_BADINDEX; + return false; + } + + if ( resp_size != sizeof(resp) ) { + printk(TBOOT_ERR"TPM: public data size of Index 0x%08X responsed incorrect\n", + index); + ti->error = TPM_FAIL; + return false; + } + + offset = resp_size - sizeof(uint32_t) - 3 * sizeof(uint8_t) - sizeof(uint32_t); + LOAD_INTEGER(resp, offset, *attribute); + + return true; +} + +typedef struct __packed { + tpm_structure_tag_t tag; + uint8_t disable; + uint8_t ownership; + uint8_t deactivated; + uint8_t read_pubek; + uint8_t disable_owner_clear; + uint8_t allow_maintenance; + uint8_t physical_presence_lifetime_lock; + uint8_t physical_presence_hw_enable; + uint8_t physical_presence_cmd_enable; + uint8_t cekp_used; + uint8_t tpm_post; + uint8_t tpm_post_lock; + uint8_t fips; + uint8_t operator; + uint8_t enable_revoke_ek; + uint8_t nv_locked; + uint8_t read_srk_pub; + uint8_t tpm_established; + uint8_t maintenance_done; + uint8_t disable_full_da_logic_info; +} tpm_permanent_flags_t; + +typedef struct __packed { + tpm_structure_tag_t tag; + uint8_t deactivated; + uint8_t disable_force_clear; + uint8_t physical_presence; + uint8_t phycical_presence_lock; + uint8_t b_global_lock; +} tpm_stclear_flags_t; + +#define TPM_CAP_FLAG 0x00000004 +#define TPM_CAP_FLAG_PERMANENT 0x00000108 +#define TPM_CAP_FLAG_VOLATILE 0x00000109 + +static uint32_t tpm12_get_flags(uint32_t locality, uint32_t flag_id, + uint8_t *flags, uint32_t flag_size) +{ + uint32_t ret, offset, resp_size; + uint8_t sub_cap[sizeof(flag_id)]; + tpm_structure_tag_t tag; + + if ( flags == NULL ) { + printk(TBOOT_WARN"TPM: tpm12_get_flags() bad parameter\n"); + return TPM_BAD_PARAMETER; + } + + offset = 0; + UNLOAD_INTEGER(sub_cap, offset, flag_id); + + resp_size = flag_size; + ret = tpm12_get_capability(locality, TPM_CAP_FLAG, sizeof(sub_cap), + sub_cap, &resp_size, flags); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: get flags %08X, return value = %08X\n", flag_id, ret); +#endif + if ( ret != TPM_SUCCESS ) + return ret; + + /* 1.2 spec, main part 2, rev 103 add one more byte to permanent flags, to + be backward compatible, not assume all expected bytes can be gotten */ + if ( resp_size > flag_size ) { + printk(TBOOT_WARN"TPM: tpm12_get_flags() response size too small\n"); + return TPM_FAIL; + } + + offset = 0; + LOAD_INTEGER(flags, offset, tag); + offset = 0; + UNLOAD_BLOB_TYPE(flags, offset, &tag); + + return ret; +} + +#define TPM_CAP_PROPERTY 0x00000005 +#define TPM_CAP_PROP_TIS_TIMEOUT 0x00000115 + +static uint32_t tpm12_get_timeout(uint32_t locality, + uint8_t *prop, uint32_t prop_size) +{ + uint32_t ret, offset, resp_size, prop_id = TPM_CAP_PROP_TIS_TIMEOUT; + uint8_t sub_cap[sizeof(prop_id)]; + uint32_t resp[4]; + + if ( (prop == NULL) || (prop_size < sizeof(resp)) ) { + printk(TBOOT_WARN"TPM: tpm12_get_timeout() bad parameter\n"); + return TPM_BAD_PARAMETER; + } + + offset = 0; + UNLOAD_INTEGER(sub_cap, offset, prop_id); + + resp_size = prop_size; + ret = tpm12_get_capability(locality, TPM_CAP_PROPERTY, sizeof(sub_cap), + sub_cap, &resp_size, prop); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: get prop %08X, return value = %08X\n", prop_id, ret); +#endif + if ( ret != TPM_SUCCESS ) + return ret; + + if ( resp_size != prop_size ) { + printk(TBOOT_WARN"TPM: tpm_get_property() response size incorrect\n"); + return TPM_FAIL; + } + + offset = 0; + LOAD_INTEGER(prop, offset, resp); + offset = 0; + UNLOAD_BLOB_TYPE(prop, offset, &resp); + + return ret; +} + +/* ensure TPM is ready to accept commands */ +static bool tpm12_init(struct tpm_if *ti) +{ + tpm_permanent_flags_t pflags; + tpm_stclear_flags_t vflags; + uint32_t timeout[4]; + uint32_t locality; + uint32_t ret; + + if ( ti == NULL ) + return false; + + locality = ti->cur_loc; + if ( !tpm_validate_locality(locality) ) { + printk(TBOOT_WARN"TPM is not available.\n"); + return false; + } + + /* make sure tpm is not disabled/deactivated */ + memset(&pflags, 0, sizeof(pflags)); + ret = tpm12_get_flags(locality, TPM_CAP_FLAG_PERMANENT, + (uint8_t *)&pflags, sizeof(pflags)); + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_WARN"TPM is disabled or deactivated.\n"); + ti->error = ret; + return false; + } + if ( pflags.disable ) { + printk(TBOOT_WARN"TPM is disabled.\n"); + return false; + } + + memset(&vflags, 0, sizeof(vflags)); + ret = tpm12_get_flags(locality, TPM_CAP_FLAG_VOLATILE, + (uint8_t *)&vflags, sizeof(vflags)); + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_WARN"TPM is disabled or deactivated.\n"); + ti->error = ret; + return false; + } + if ( vflags.deactivated ) { + printk(TBOOT_WARN"TPM is deactivated.\n"); + return false; + } + + printk(TBOOT_INFO"TPM is ready\n"); + printk(TBOOT_DETA"TPM nv_locked: %s\n", (pflags.nv_locked != 0) ? "TRUE" : "FALSE"); + + /* get tpm timeout values */ + ret = tpm12_get_timeout(locality, (uint8_t *)&timeout, sizeof(timeout)); + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_WARN"TPM timeout values are not achieved, " + "default values will be used.\n"); + ti->error = ret; + } else { + /* + * timeout_x represents the number of milliseconds for the timeout + * and timeout[x] represents the number of microseconds. + */ + ti->timeout.timeout_a = timeout[0]/1000; + ti->timeout.timeout_b = timeout[1]/1000; + ti->timeout.timeout_c = timeout[2]/1000; + ti->timeout.timeout_d = timeout[3]/1000; + printk(TBOOT_DETA"TPM timeout values: A: %u, B: %u, C: %u, D: %u\n", + ti->timeout.timeout_a, ti->timeout.timeout_b, ti->timeout.timeout_c, + ti->timeout.timeout_d); + /* + * if any timeout values are less than default values, set to default + * value (due to bug with some TPMs) + */ + if ( ti->timeout.timeout_a < TIMEOUT_A ) { + ti->timeout.timeout_a = TIMEOUT_A; + printk(TBOOT_WARN"Wrong timeout A, fallback to %u\n", TIMEOUT_A); + } + if ( ti->timeout.timeout_b < TIMEOUT_B ) { + ti->timeout.timeout_b = TIMEOUT_B; + printk(TBOOT_WARN"Wrong timeout B, fallback to %u\n", TIMEOUT_B); + } + if ( ti->timeout.timeout_c < TIMEOUT_C ) { + ti->timeout.timeout_c = TIMEOUT_C; + printk(TBOOT_WARN"Wrong timeout C, fallback to %u\n", TIMEOUT_C); + } + if ( ti->timeout.timeout_d < TIMEOUT_D ) { + ti->timeout.timeout_d = TIMEOUT_D; + printk(TBOOT_WARN"Wrong timeout D, fallback to %u\n", TIMEOUT_D); + } + } + + /* init version */ + ti->major = TPM12_VER_MAJOR; + ti->minor = TPM12_VER_MINOR; + + /* init supported alg list */ + ti->banks = 1; + ti->alg_count = 1; + ti->algs[0] = TB_HALG_SHA1; + ti->extpol = TB_EXTPOL_FIXED; + ti->cur_alg = TB_HALG_SHA1; + + /* init NV index */ + ti->tb_policy_index = 0x20000001; + ti->lcp_own_index = 0x40000001; + ti->tb_err_index = 0x20000002; + ti->sgx_svn_index = 0x50000004; + + return true; +} + +static uint32_t tpm12_save_state(struct tpm_if *ti, uint32_t locality) +{ + uint32_t ret, offset, out_size; + uint32_t retries = 0; + + if ( ti == NULL ) + return TPM_BAD_PARAMETER; + + do { + offset = 0; + out_size = 0; + + ret = tpm12_submit_cmd(locality, TPM_ORD_SAVE_STATE, offset, &out_size); + if ( retries == 0 ) + printk(TBOOT_INFO"TPM: save state, return value = %08X\n", ret); + else if ( retries == 1 ) + printk(TBOOT_INFO"retrying command: ."); + else + printk(TBOOT_INFO"."); + + if ( ret != TPM_RETRY ) + break; + + retries++; + delay(100); + } while ( retries < MAX_SAVESTATE_RETRIES ); + if ( retries >= MAX_SAVESTATE_RETRIES ) + printk(TBOOT_INFO"TIMEOUT!"); + if ( retries > 0 ) + printk(TBOOT_INFO"\n"); + + return ret; +} + +static bool tpm12_get_random(struct tpm_if *ti, uint32_t locality, + uint8_t *random_data, uint32_t *data_size) +{ + uint32_t ret, in_size = 0, out_size, requested_size; + static bool first_attempt; + + if ( ti == NULL ) + return false; + + if ( random_data == NULL || data_size == NULL || *data_size == 0 ) { + ti->error = TPM_BAD_PARAMETER; + return false; + } + + first_attempt = true; + requested_size = *data_size; + + /* copy the *data_size into buf in reversed byte order */ + reverse_copy(WRAPPER_IN_BUF + in_size, data_size, sizeof(*data_size)); + in_size += sizeof(*data_size); + + out_size = *data_size + sizeof(*data_size); + ret = tpm12_submit_cmd(locality, TPM_ORD_GET_RANDOM, in_size, &out_size); + +#ifdef TPM_TRACE + printk(TBOOT_DETA"TPM: get random %u bytes, return value = %08X\n", *data_size, ret); +#endif + if ( ret != TPM_SUCCESS ) { + printk(TBOOT_WARN"TPM: get random %u bytes, return value = %08X\n", *data_size, ret); + ti->error = ret; + return false; + } + +#ifdef TPM_TRACE + { + printk(TBOOT_INFO"TPM: "); + print_hex(NULL, WRAPPER_OUT_BUF, out_size); + } +#endif + + if ( out_size <= sizeof(*data_size) ) { + *data_size = 0; + return true; + } + + out_size -= sizeof(*data_size); + reverse_copy(data_size, WRAPPER_OUT_BUF, sizeof(*data_size)); + if ( *data_size > 0 ) + memcpy(random_data, WRAPPER_OUT_BUF + sizeof(*data_size), *data_size); + + /* data might be used as key, so clear from buffer memory */ + memset(WRAPPER_OUT_BUF + sizeof(*data_size), 0, *data_size); + + /* if TPM doesn't return all requested random bytes, try one more time */ + if ( *data_size < requested_size ) { + printk(TBOOT_WARN"requested %x random bytes but only got %x\n", requested_size, + *data_size); + /* we're only going to try twice */ + if ( first_attempt ) { + first_attempt = false; + uint32_t second_size = requested_size - *data_size; + printk(TBOOT_INFO"trying one more time to get remaining %x bytes\n", + second_size); + if (!tpm12_get_random(ti, locality, random_data + *data_size, + &second_size)) + return false; + *data_size += second_size; + } + } + + return true; +} + +static bool tpm12_cap_pcrs(struct tpm_if *ti, u32 locality, int pcr) +{ + bool was_capped[TPM_NR_PCRS] = {false}; + tpm_pcr_value_t cap_val; /* use whatever val is on stack */ + + if ( ti == NULL ) + return false; + + if (pcr >= 0) { + _tpm12_pcr_extend(ti, locality, pcr, &cap_val); + return true; + } + + /* ensure PCRs 17 + 18 are always capped */ + _tpm12_pcr_extend(ti, locality, 17, &cap_val); + _tpm12_pcr_extend(ti, locality, 18, &cap_val); + was_capped[17] = was_capped[18] = true; + + /* also cap every dynamic PCR we extended (only once) */ + /* don't cap static PCRs since then they would be wrong after S3 resume */ + /* TODO + memset(&was_capped, true, TPM_PCR_RESETABLE_MIN*sizeof(bool)); + for ( int i = 0; i < g_pre_k_s3_state.num_vl_entries; i++ ) { + if ( !was_capped[g_pre_k_s3_state.vl_entries[i].pcr] ) { + _tpm12_pcr_extend(ti, locality, g_pre_k_s3_state.vl_entries[i].pcr, &cap_val); + was_capped[g_pre_k_s3_state.vl_entries[i].pcr] = true; + } + }*/ + + printk(TBOOT_INFO"cap'ed dynamic PCRs\n"); + return true; +} + +static bool tpm12_check(void) +{ + uint32_t ret, out_size = 0; + + ret = tpm12_submit_cmd(0, 0xFFFFFFFF, 0, &out_size); + + return ( ret == TPM_BAD_ORDINAL ); +} + +struct tpm_if tpm_12_if = { + .init = tpm12_init, + .pcr_read = tpm12_pcr_read, + .pcr_extend = tpm12_pcr_extend, + .pcr_reset = tpm12_pcr_reset, + .nv_read = tpm12_nv_read_value, + .nv_write = tpm12_nv_write_value, + .get_nvindex_size = tpm12_get_nvindex_size, + .get_nvindex_permission = tpm12_get_nvindex_permission, + .seal = tpm12_seal, + .unseal = tpm12_unseal, + .verify_creation = tpm12_verify_creation, + .get_random = tpm12_get_random, + .save_state = tpm12_save_state, + .cap_pcrs = tpm12_cap_pcrs, + .check = tpm12_check, + .cur_loc = 0, + .timeout.timeout_a = TIMEOUT_A, + .timeout.timeout_b = TIMEOUT_B, + .timeout.timeout_c = TIMEOUT_C, + .timeout.timeout_d = TIMEOUT_D, +}; + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- cgit v1.2.3