From f0f29cbe8c208f0e1e9f723476a6435524fc4c73 Mon Sep 17 00:00:00 2001 From: "Mauro S. M. Rodrigues" Date: Mon, 1 Jun 2020 17:34:35 -0300 Subject: libstb/tss2: Add TSS wrapping functions In this commit we add some fundamental TSS operations: - tss_nv_read_public - tss_nv_read - tss_nv_write - tss_nv_write_lock - tss_nv_define_space - tss_nv_undefine_space - tss_get_defined_nv_indices - tss_pcr_extend - tss_pcr_read - tss_get_random_number - tss_set_platform_auth Co-authored-by: Eric Richter Co-authored-by: Ryan Grimm Signed-off-by: Mauro S. M. Rodrigues Signed-off-by: Mauro S. M. Rodrigues Signed-off-by: Oliver O'Halloran --- libstb/tss2/tssskiboot.c | 727 +++++++++++++++++++++++++++++++++++++++++++++++ libstb/tss2/tssskiboot.h | 26 ++ 2 files changed, 753 insertions(+) create mode 100644 libstb/tss2/tssskiboot.c create mode 100644 libstb/tss2/tssskiboot.h diff --git a/libstb/tss2/tssskiboot.c b/libstb/tss2/tssskiboot.c new file mode 100644 index 0000000..5f6d761 --- /dev/null +++ b/libstb/tss2/tssskiboot.c @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* Copyright 2020 IBM Corp. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Helper to string-fy TSS error response codes. + */ +static void tss_error_trace(const char *function, TPM_RC rc) +{ + const char *msg; + const char *submsg; + const char *num; + prlog(PR_ERR, "%s: failed, rc %08x\n", function, rc); + TSS_ResponseCode_toString(&msg, &submsg, &num, rc); + prlog(PR_ERR, "%s%s%s\n", msg, submsg, num); +} + +/* + * @brief Reads the public and name area of a NV Index. + * @param nv_index The target NV index to read public info from. + * @param nv_public buffer to save public are read from nv index + * @param nv_name buffer to save nv index name. + */ +int tss_nv_read_public(TPMI_RH_NV_INDEX nv_index, TPMS_NV_PUBLIC *nv_public, + TPM2B_NAME *nv_name) +{ + NV_ReadPublic_Out *out = NULL; + NV_ReadPublic_In *in = NULL; + TSS_CONTEXT *context = NULL; + TPM_RC rc = OPAL_SUCCESS; + + if (!nv_public || !nv_name) { + rc = OPAL_PARAMETER; + goto cleanup; + } + + in = zalloc(sizeof(NV_ReadPublic_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + out = zalloc(sizeof(NV_ReadPublic_Out)); + if (!out) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + tss_error_trace("tss_nv_read_public", rc); + rc = OPAL_NO_MEM; + goto cleanup; + } + + in->nvIndex = nv_index; + rc = TSS_Execute(context, + (RESPONSE_PARAMETERS *) out, + (COMMAND_PARAMETERS *) in, + NULL, + TPM_CC_NV_ReadPublic, + TPM_RH_NULL, NULL, 0); + if (!rc) { + memcpy(nv_public, &out->nvPublic, sizeof(TPMS_NV_PUBLIC)); + memcpy(nv_name, &out->nvName, sizeof(TPM2B_NAME)); + } + else + tss_error_trace("tss_nv_read_public", rc); +cleanup: + free(in); + free(out); + TSS_Delete(context); + return rc; +} + +/* @brief This command reads a value from an area previously defined using + * nv_define_space + * @param nv_index The target NV index to read from. + * @param buffer buffer to save the data read. + * @param buffer_size size of the buffer, to avoid overflow. + * @param offset position where to start the nv read operation. + */ +int tss_nv_read(TPMI_RH_NV_INDEX nv_index, void *buffer, + size_t buffer_size, uint16_t offset) +{ + TSS_CONTEXT *context = NULL; + NV_Read_Out *out = NULL; + NV_Read_In *in = NULL; + TPM_RC rc = OPAL_SUCCESS; + + if (!buffer) { + rc = OPAL_PARAMETER; + goto cleanup; + } + + in = zalloc(sizeof(NV_Read_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + out = zalloc(sizeof(NV_Read_Out)); + if (!out) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + tss_error_trace("tss_nv_read", rc); + rc = OPAL_NO_MEM; + goto cleanup; + } + + in->nvIndex = nv_index; + in->authHandle = nv_index; + in->offset = offset; + in->size = buffer_size; + + rc = TSS_Execute(context, + (RESPONSE_PARAMETERS *) out, + (COMMAND_PARAMETERS *) in, + NULL, + TPM_CC_NV_Read, + TPM_RS_PW, NULL, 0, + TPM_RH_NULL, NULL, 0); + + if (!rc) + memcpy(buffer, out->data.b.buffer, buffer_size); + else + tss_error_trace("tss_nv_read", rc); + +cleanup: + TSS_Delete(context); + free(in); + free(out); + return rc; +} + +/* @brief This command writes a value in an area previously defined using + * nv_define_space + * @param nv_index The target NV index to write to. + * @param buffer buffer containing the data write. + * @param buffer_size size of the buffer to write. + * @param offset position where to start the nv write operation. + */ +int tss_nv_write(TPMI_RH_NV_INDEX nv_index, void *buffer, + size_t buffer_size, uint16_t offset) +{ + TSS_CONTEXT *context = NULL; + NV_Write_In *in = NULL; + TPM_RC rc = OPAL_SUCCESS; + + if (!buffer) { + rc = OPAL_PARAMETER; + goto cleanup; + } + + in = zalloc(sizeof(NV_Write_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + tss_error_trace("tss_nv_write", rc); + rc = OPAL_NO_MEM; + goto cleanup; + } + + in->nvIndex = nv_index; + in->authHandle = TPM_RH_PLATFORM; + in->offset = offset; + rc = TSS_TPM2B_Create(&in->data.b, buffer, buffer_size, + sizeof(in->data.t.buffer)); + if (rc) { + tss_error_trace("tss_nv_write", rc); + goto cleanup; + } + + rc = TSS_Execute(context, + NULL, + (COMMAND_PARAMETERS *) in, + NULL, + TPM_CC_NV_Write, + TPM_RS_PW, NULL, 0, + TPM_RH_NULL, NULL, 0); + if (rc) + tss_error_trace("tss_nv_write", rc); +cleanup: + TSS_Delete(context); + free(in); + return rc; +} + +/* + * @brief This command locks an area, pointed by the index and previously + * defined using nv_define_space, preventing further writing operations on it. + * @param nv_index The target NV index to lock. + */ +int tss_nv_write_lock(TPMI_RH_NV_INDEX nv_index) +{ + TSS_CONTEXT *context = NULL; + NV_WriteLock_In *in = NULL; + TPM_RC rc = OPAL_SUCCESS; + + in = zalloc(sizeof(NV_WriteLock_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + in->authHandle = TPM_RH_PLATFORM; + in->nvIndex = nv_index; + rc = TSS_Execute(context, + NULL, + (COMMAND_PARAMETERS *) in, + NULL, + TPM_CC_NV_WriteLock, + TPM_RS_PW, NULL, 0, + TPM_RH_NULL, NULL, 0); + if (rc) + tss_error_trace("tss_nv_write_lock", rc); +cleanup: + TSS_Delete(context); + free(in); + return rc; +} + + /* + * @brief This command defines the area pointed by nv index and its attributes. + * @param nv_index The target NV index to define. + * @param data_size size of the area to be defined. + */ +int tss_nv_define_space(TPMI_RH_NV_INDEX nv_index, uint16_t data_size) +{ + NV_DefineSpace_In *in = NULL; + TSS_CONTEXT *context = NULL; + TPM_RC rc = OPAL_SUCCESS; + + in = zalloc(sizeof(NV_DefineSpace_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + in->authHandle = TPM_RH_PLATFORM; + + in->publicInfo.nvPublic.nvIndex = nv_index; + in->publicInfo.nvPublic.dataSize = data_size; + /* password is NULL so b.size is 0 */ + in->auth.b.size = 0; + /* Empty policy, so size is 0 */ + in->publicInfo.nvPublic.authPolicy.t.size = 0; + /* Used algorithm is SHA256 */ + in->publicInfo.nvPublic.nameAlg = TPM_ALG_SHA256; + /* + * This carries the flags set according to default settings, excepting + * for what is set by this function parameters. Further customization + * will require a different setup for nvAttribute flags as is done in + * TSS's code. + */ + in->publicInfo.nvPublic.attributes.val = (TPMA_NVA_PPWRITE | + TPMA_NVA_ORDINARY | + TPMA_NVA_WRITE_STCLEAR | + TPMA_NVA_AUTHREAD | + TPMA_NVA_PLATFORMCREATE | + TPMA_NVA_NO_DA); + + rc = TSS_Execute(context, + NULL, + (COMMAND_PARAMETERS *)in, + NULL, + TPM_CC_NV_DefineSpace, + TPM_RS_PW, NULL, 0, + TPM_RH_NULL, NULL, 0); + if (rc) { + tss_error_trace("tss_nv_define_space", rc); + switch(rc) { + case TPM_RC_NV_DEFINED: + rc = OPAL_WRONG_STATE; + break; + default: + break; + } + } +cleanup: + TSS_Delete(context); + free(in); + return rc; +} + +/* + * @brief Extends a PCR using the given hashes and digest + * @param pcr_handle The PCR to be extended + * @param alg_hashes A pointer to an array of hash algorithms, each + * one used to extend its respective PCR bank. + * @param alg_hash_count The number of elements in alg_hashes array + * @param digests The digest data. + */ +int tss_pcr_extend(TPMI_DH_PCR pcr_handle, TPMI_ALG_HASH *alg_hashes, + uint8_t alg_hash_count, const uint8_t **digests) +{ + TSS_CONTEXT *context = NULL; + uint32_t rc = OPAL_SUCCESS; + PCR_Extend_In *in = NULL; + uint16_t digest_size; + + if (!alg_hashes || !digests || pcr_handle >= IMPLEMENTATION_PCR) { + rc = OPAL_PARAMETER; + goto cleanup; + } + + in = zalloc(sizeof(PCR_Extend_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + tss_error_trace("tss_pcr_extend", rc); + rc = OPAL_NO_MEM; + goto cleanup; + } + + if (alg_hash_count >= HASH_COUNT) { + rc = OPAL_PARAMETER; + goto cleanup; + } + + in->digests.count = alg_hash_count; + in->pcrHandle = pcr_handle; + for (uint8_t i=0; i < alg_hash_count; i++) { + in->digests.digests[i].hashAlg = alg_hashes[i]; + /* memset zeroes first to assure the digest data is zero + * padded.*/ + memset((uint8_t*) &in->digests.digests[i].digest, 0, + sizeof(TPMU_HA)); + + digest_size = 0; + /* Marshal the digest in order to obtain its size. This is a + * commonly used pattern in TSS. + */ + rc = TSS_TPMU_HA_Marshalu((const TPMU_HA *)digests[i], + &digest_size, NULL, NULL , + alg_hashes[i]); + if (rc) + goto cleanup; + memcpy((uint8_t*) &in->digests.digests[i].digest, digests[i], + digest_size); + } + rc = TSS_Execute(context, + NULL, + (COMMAND_PARAMETERS *) in, + NULL, + TPM_CC_PCR_Extend, + TPM_RS_PW, NULL, 0, + TPM_RH_NULL, NULL, 0); + if (rc) + tss_error_trace("tss_pcr_extend", rc); +cleanup: + TSS_Delete(context); + free(in); + return rc; +} + +/* + * @brief reads pcr values of a given pcr handle. + * @param pcr_handle The PCR to be extended + * @param alg_hashes A pointer to an array of hash algorithms, each + * one used to extend its respective PCR bank. + * @param alg_hash_count The length of alg hashes array + */ +int tss_pcr_read(TPMI_DH_PCR pcr_handle, TPMI_ALG_HASH *alg_hashes, + uint8_t alg_hash_count) +{ + TSS_CONTEXT *context = NULL; + PCR_Read_Out *out = NULL; + PCR_Read_In *in = NULL; + uint32_t rc = OPAL_SUCCESS; + + if (!alg_hashes) { + rc = OPAL_PARAMETER; + goto cleanup; + } + + in = zalloc(sizeof(PCR_Read_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + out = zalloc(sizeof(PCR_Read_Out)); + if (!out) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (!rc) { + tss_error_trace("tss_pcr_read", rc); + rc = OPAL_NO_MEM; + goto cleanup; + } + + in->pcrSelectionIn.count = alg_hash_count; + for (int i=0; i < alg_hash_count; i++) { + in->pcrSelectionIn.pcrSelections[i].hash = alg_hashes[i]; + in->pcrSelectionIn.pcrSelections[i].sizeofSelect = 3; + in->pcrSelectionIn.pcrSelections[i].pcrSelect[0] = 0; + in->pcrSelectionIn.pcrSelections[i].pcrSelect[1] = 0; + in->pcrSelectionIn.pcrSelections[i].pcrSelect[2] = 0; + in->pcrSelectionIn.pcrSelections[i].pcrSelect[pcr_handle/8] = 1 << (pcr_handle % 8); + } + + rc = TSS_Execute(context, + (RESPONSE_PARAMETERS *) out, + (COMMAND_PARAMETERS *) in, + NULL, + TPM_CC_PCR_Read, + TPM_RH_NULL, NULL, 0); + if (rc) + tss_error_trace("tss_pcr_read", rc); +cleanup: + TSS_Delete(context); + free(in); + free(out); + return rc; +} + +/* + * @brief returns next bytes_requested bytes from the TPM RNG + * @param buffer Buffer to save the generated numbers. + * @param bytes_requested How many random bytes are requested. + */ +int tss_get_random_number(void *buffer, uint16_t bytes_requested) +{ + TSS_CONTEXT *context = NULL; + GetRandom_Out *out = NULL; + TPM_RC rc = OPAL_SUCCESS; + GetRandom_In *in = NULL; + void *p_buffer = buffer; + + if (!buffer) { + rc = OPAL_PARAMETER; + goto cleanup; + } + + in = zalloc(sizeof(GetRandom_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + out = zalloc(sizeof(GetRandom_Out)); + if (!out) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + tss_error_trace("tss_get_random_number", rc); + rc = OPAL_NO_MEM; + goto cleanup; + } + + /* + * Even though we request a specific number of bytes, there is no + * guarantee that TPM will return that number of bytes, so we ask again + * until we reach the desired total of bytes or rng function fails + */ + for (uint16_t bytes_copied = 0; bytes_copied < bytes_requested; ) { + in->bytesRequested = bytes_requested - bytes_copied; + rc = TSS_Execute(context, + (RESPONSE_PARAMETERS *)out, + (COMMAND_PARAMETERS *)in, + NULL, TPM_CC_GetRandom, + TPM_RH_NULL, NULL, 0); + if (!rc){ + memcpy(p_buffer, out->randomBytes.t.buffer, + out->randomBytes.t.size); + bytes_copied += out->randomBytes.t.size; + p_buffer += bytes_copied; + /* explicitly clean up output's buffer from memory on + * every iteration, since the size will vary, to avoid + * some kind of exploitation. + */ + memset(out->randomBytes.t.buffer, 0, + out->randomBytes.t.size); + + } + else { + tss_error_trace("tss_get_random_number", rc); + break; + } + } + +cleanup: + TSS_Delete(context); + free(in); + free(out); + return rc; +} + +/* local helper to generate random password without zeroes */ +static int generate_random_passwd(char *passwd, uint16_t passwd_len) +{ + TPM_RC rc = OPAL_SUCCESS; + char *buffer = NULL; + int bytes_copied; + int i; + + buffer = zalloc(passwd_len); + if (!buffer) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + bytes_copied = 0; + while ((rc == 0) && (bytes_copied < passwd_len)) { + rc = tss_get_random_number(buffer, passwd_len); + if (rc) + goto cleanup; + + /* Copy as many bytes as were received or until bytes requested */ + for (i = 0; (i < passwd_len) && + (bytes_copied < passwd_len); i++) { + + /* Skip zero bytes */ + if (buffer[i] == 0) + continue; + passwd[bytes_copied] = buffer[i]; + bytes_copied++; + } + } +cleanup: + free(buffer); + return rc; +} + +/* + * @brief This command allows the authorization secret for a hierarchy to be + * changed. + */ +int tss_set_platform_auth(void) +{ + HierarchyChangeAuth_In *in = NULL; + TSS_CONTEXT *context = NULL; + TPM_RC rc = OPAL_SUCCESS; + char *key_passwd = NULL; + + in = zalloc(sizeof(HierarchyChangeAuth_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + key_passwd = zalloc(TSS_AUTH_PASSWD_LEN + 1); + if (!key_passwd) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + tss_error_trace("tss_set_platform_auth", rc); + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = generate_random_passwd(key_passwd, TSS_AUTH_PASSWD_LEN); + if (rc) { + tss_error_trace("Failed to generate the auth password", rc); + goto cleanup; + } + key_passwd[TSS_AUTH_PASSWD_LEN] = 0; + + in->authHandle = TPM_RH_PLATFORM; + rc = TSS_TPM2B_StringCopy(&in->newAuth.b, key_passwd, + sizeof(in->newAuth.t.buffer)); + if (rc) { + tss_error_trace("tss_set_platform_auth", rc); + goto cleanup; + } + + rc = TSS_Execute(context, + NULL, + (COMMAND_PARAMETERS *)in, + NULL, + TPM_CC_HierarchyChangeAuth, + TPM_RS_PW, NULL, 0, + TPM_RH_NULL, NULL, 0); + if (rc) + tss_error_trace("tss_set_platform_auth", rc); + +cleanup: + TSS_Delete(context); + free(in); + /* explicitly clean up password from memory to avoid some kind of + * exploitation. + */ + memset(key_passwd, 0, TSS_AUTH_PASSWD_LEN + 1); + free(key_passwd); + return rc; +} + +/* + * @brief returns a list of defined NV indices + * @param pcr_handle The PCR to be extended + * @param alg_hashes A pointer to an array of hash algorithms, each + * one used to extend its respective PCR bank. + * @param alg_hash_count The length of alg hashes array + */ +int tss_get_defined_nv_indices(TPMI_RH_NV_INDEX **indices, size_t *count) +{ + TSS_CONTEXT *context = NULL; + GetCapability_In *in = NULL; + GetCapability_Out *out = NULL; + uint32_t rc = OPAL_SUCCESS; + TPML_HANDLE *handles; + + in = zalloc(sizeof(GetCapability_In)); + if (!in) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + out = zalloc(sizeof(GetCapability_Out)); + if (!out) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + rc = TSS_Create(&context); + if (rc) { + tss_error_trace("tss_check_nv_index", rc); + rc = OPAL_NO_MEM; + goto cleanup; + } + + in->capability = 1; + in->property = 0x01000000; + in->propertyCount = 64; + + rc = TSS_Execute(context, + (RESPONSE_PARAMETERS *) out, + (COMMAND_PARAMETERS *) in, + NULL, + TPM_CC_GetCapability, + TPM_RH_NULL, NULL, 0); + if (rc) { + tss_error_trace("tss_check_nv_index", rc); + goto cleanup; + } + + handles = (TPML_HANDLE *) &out->capabilityData.data; + *count = handles->count; + *indices = malloc(*count * sizeof(TPMI_RH_NV_INDEX)); + if (!indices) { + rc = OPAL_NO_MEM; + goto cleanup; + } + + memcpy(*indices, handles->handle, *count * sizeof(TPMI_RH_NV_INDEX)); + +cleanup: + TSS_Delete(context); + free(in); + free(out); + return rc; +} + + +int tss_nv_undefine_space(TPMI_RH_NV_INDEX nv_index) +{ + int rc; + TSS_CONTEXT *context = NULL; + NV_UndefineSpace_In in; + + rc = TSS_Create(&context); + if (rc) { + tss_error_trace("tss_check_nv_undefine_index", rc); + rc = OPAL_NO_MEM; + return rc; + } + + in.authHandle = TPM_RH_PLATFORM; + in.nvIndex = nv_index; + + rc = TSS_Execute(context, NULL, + (COMMAND_PARAMETERS *) &in, + NULL, + TPM_CC_NV_UndefineSpace, + TPM_RS_PW, NULL, 0, + TPM_RH_NULL, NULL, 0); + if (rc) + tss_error_trace("tss_check_nv_index", rc); + + TSS_Delete(context); + return rc; +} diff --git a/libstb/tss2/tssskiboot.h b/libstb/tss2/tssskiboot.h new file mode 100644 index 0000000..0b8cb74 --- /dev/null +++ b/libstb/tss2/tssskiboot.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* Copyright 2020 IBM Corp. */ + +#ifndef TSSSKIBOOT_H +#define TSSSKIBOOT_H + +#include + +#define TSS_AUTH_PASSWD_LEN 32 +int tss_nv_read_public(TPMI_RH_NV_INDEX nv_index, TPMS_NV_PUBLIC *nv_public, + TPM2B_NAME *nv_name); +int tss_nv_read(TPMI_RH_NV_INDEX nv_index, void *buffer, size_t buffer_size, + uint16_t offset); +int tss_nv_write(TPMI_RH_NV_INDEX nv_index, void *buffer, size_t buffer_size, + uint16_t offset); +int tss_nv_write_lock(TPMI_RH_NV_INDEX nv_index); +int tss_nv_define_space(TPMI_RH_NV_INDEX nv_index, uint16_t data_size); +int tss_pcr_extend(TPMI_DH_PCR pcr_handle, TPMI_ALG_HASH *alg_hashes, + uint8_t hashes_count, const uint8_t **digests); +int tss_pcr_read(TPMI_DH_PCR pcr_handle, TPMI_ALG_HASH *alg_hashes, + uint8_t hashes_count); +int tss_get_random_number(void *buffer, uint16_t bytes_requested); +int tss_set_platform_auth(void); +int tss_get_defined_nv_indices(TPMI_RH_NV_INDEX **indices, size_t *count); +int tss_nv_undefine_space(TPMI_RH_NV_INDEX nv_index); +#endif /* TSSSKIBOOT_H */ -- cgit v1.1