From af88e806f0b7f311a326c18a4bf8b2d06c38e583 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: May 10 2019 13:24:01 +0000 Subject: Add tpm2_pcrreset and tpm2_checkquote tools This change adds the backported tpm2_pcrreset and tpm2_checkquote tools and also the support to allow tpm2_makecredential tool to run off-TPM. Signed-off-by: Javier Martinez Canillas --- diff --git a/Add-ability-to-check-quotes-and-output-PCR-values-fo.patch b/Add-ability-to-check-quotes-and-output-PCR-values-fo.patch new file mode 100644 index 0000000..5c6cd2a --- /dev/null +++ b/Add-ability-to-check-quotes-and-output-PCR-values-fo.patch @@ -0,0 +1,2007 @@ +From 3f9c713d3bff4abf417edf0f74e0b049bfd1b31f Mon Sep 17 00:00:00 2001 +From: jetwhiz +Date: Fri, 3 May 2019 08:03:45 -0400 +Subject: [PATCH] Add ability to check quotes and output PCR values for + quotes + +Add new tpm2_checkquote tool for checking quotes + Pull shared functionality from tpm2_pcrlist into pcr library + is_pcr_select_bit_set moved into tpm2_util library + Add new functionality to openssl, pcr and util libraries +The tpm2_quote tool can now output PCR hash lists + +Signed-off-by: jetwhiz +--- + CHANGELOG.md | 2 + + Makefile.am | 3 + + lib/pcr.c | 232 ++++++++++++++++ + lib/pcr.h | 27 ++ + lib/tpm2_openssl.c | 163 +++++++++++ + lib/tpm2_openssl.h | 57 ++++ + lib/tpm2_util.c | 148 ++++++++++ + lib/tpm2_util.h | 33 +++ + man/tpm2_checkquote.1.md | 95 +++++++ + man/tpm2_quote.1.md | 9 +- + test/system/test_tpm2_checkquote.sh | 86 ++++++ + test/system/test_tpm2_quote.sh | 12 +- + tools/tpm2_checkquote.c | 409 ++++++++++++++++++++++++++++ + tools/tpm2_pcrlist.c | 205 +------------- + tools/tpm2_quote.c | 124 ++++++++- + 15 files changed, 1398 insertions(+), 207 deletions(-) + create mode 100644 man/tpm2_checkquote.1.md + create mode 100755 test/system/test_tpm2_checkquote.sh + create mode 100644 tools/tpm2_checkquote.c + +diff --git a/CHANGELOG.md b/CHANGELOG.md +index a8e4f39afde..7e83c6b7e6f 100644 +--- a/CHANGELOG.md ++++ b/CHANGELOG.md +@@ -1,5 +1,7 @@ + ## Changelog + ### 3.2.0 - next ++* tpm2_checkquote: Introduce new tool for checking validity of quotes. ++* tpm2_quote: Add ability to output PCR values for quotes. + * tpm2_makecredential: add support for executing tool off-TPM. + * tpm2_pcrreset: introduce new tool for resetting PCRs. + * tpm2_quote: Fix AK auth password not being used. +diff --git a/Makefile.am b/Makefile.am +index 2195537ce01..854c24a03e3 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -59,6 +59,7 @@ LDADD = \ + + # keep me sorted + bin_PROGRAMS = \ ++ tools/tpm2_checkquote \ + tools/tpm2_activatecredential \ + tools/tpm2_certify \ + tools/tpm2_create \ +@@ -145,6 +146,7 @@ lib_libcommon_a_SOURCES = \ + + TOOL_SRC := tools/tpm2_tool.c tools/tpm2_tool.h + ++tools_tpm2_checkquote_SOURCES = tools/tpm2_checkquote.c $(TOOL_SRC) + tools_tpm2_create_SOURCES = tools/tpm2_create.c $(TOOL_SRC) + tools_tpm2_createprimary_SOURCES = tools/tpm2_createprimary.c $(TOOL_SRC) + tools_tpm2_getcap_SOURCES = tools/tpm2_getcap.c $(TOOL_SRC) +@@ -259,6 +261,7 @@ if HAVE_MAN_PAGES + man1_MANS := \ + man/man1/tpm2_activatecredential.1 \ + man/man1/tpm2_certify.1 \ ++ man/man1/tpm2_checkquote.1 \ + man/man1/tpm2_create.1 \ + man/man1/tpm2_createpolicy.1 \ + man/man1/tpm2_createprimary.1 \ +diff --git a/lib/pcr.c b/lib/pcr.c +index 5552b336f66..9b00dd6e0e3 100644 +--- a/lib/pcr.c ++++ b/lib/pcr.c +@@ -33,13 +33,21 @@ + #include + + #include ++#include + #include + + #include "pcr.h" + #include "log.h" ++#include "tpm2_tool.h" + #include "tpm2_util.h" + #include "tpm2_alg_util.h" + ++static inline void set_pcr_select_size(TPMS_PCR_SELECTION *pcr_selection, ++ UINT8 size) { ++ ++ pcr_selection->sizeofSelect = size; ++} ++ + static int pcr_get_id(const char *arg, UINT32 *pcrId) + { + UINT32 n = 0; +@@ -96,6 +104,111 @@ static bool pcr_parse_selection(const char *str, size_t len, TPMS_PCR_SELECTION + return true; + } + ++static void shrink_pcr_selection(TPML_PCR_SELECTION *s) { ++ ++ UINT32 i, j; ++ ++ //seek for the first empty item ++ for (i = 0; i < s->count; i++) ++ if (!s->pcrSelections[i].hash) ++ break; ++ j = i + 1; ++ ++ for (; i < s->count; i++) { ++ if (!s->pcrSelections[i].hash) { ++ for (; j < s->count; j++) ++ if (s->pcrSelections[j].hash) ++ break; ++ if (j >= s->count) ++ break; ++ ++ memcpy(&s->pcrSelections[i], &s->pcrSelections[j], sizeof(s->pcrSelections[i])); ++ s->pcrSelections[j].hash = 0; ++ j++; ++ } ++ } ++ ++ s->count = i; ++} ++ ++static void pcr_update_pcr_selections(TPML_PCR_SELECTION *s1, TPML_PCR_SELECTION *s2) { ++ UINT32 i1, i2, j; ++ for (i2 = 0; i2 < s2->count; i2++) { ++ for (i1 = 0; i1 < s1->count; i1++) { ++ if (s2->pcrSelections[i2].hash != s1->pcrSelections[i1].hash) ++ continue; ++ ++ for (j = 0; j < s1->pcrSelections[i1].sizeofSelect; j++) ++ s1->pcrSelections[i1].pcrSelect[j] &= ++ ~s2->pcrSelections[i2].pcrSelect[j]; ++ } ++ } ++} ++ ++static bool pcr_unset_pcr_sections(TPML_PCR_SELECTION *s) { ++ UINT32 i, j; ++ for (i = 0; i < s->count; i++) { ++ for (j = 0; j < s->pcrSelections[i].sizeofSelect; j++) { ++ if (s->pcrSelections[i].pcrSelect[j]) { ++ return false; ++ } ++ } ++ } ++ ++ return true; ++} ++ ++bool pcr_print_pcr_struct(TPML_PCR_SELECTION *pcrSelect, tpm2_pcrs *pcrs) { ++ ++ UINT32 vi = 0, di = 0, i; ++ bool result = true; ++ ++ tpm2_tool_output("pcrs:\n"); ++ ++ // Loop through all PCR/hash banks ++ for (i = 0; i < pcrSelect->count; i++) { ++ const char *alg_name = tpm2_alg_util_algtostr(pcrSelect->pcrSelections[i].hash); ++ ++ tpm2_tool_output(" %s:\n", alg_name); ++ ++ // Loop through all PCRs in this bank ++ UINT8 pcr_id; ++ for (pcr_id = 0; pcr_id < pcrSelect->pcrSelections[i].sizeofSelect * 8; pcr_id++) { ++ if (!tpm2_util_is_pcr_select_bit_set(&pcrSelect->pcrSelections[i], ++ pcr_id)) { ++ // skip non-selected banks ++ continue; ++ } ++ if (vi >= pcrs->count || di >= pcrs->pcr_values[vi].count) { ++ LOG_ERR("Something wrong, trying to print but nothing more"); ++ return false; ++ } ++ ++ // Print out PCR ID ++ tpm2_tool_output(" %-2d: 0x", pcr_id); ++ ++ // Print out current PCR digest value ++ TPM2B_DIGEST *b = &pcrs->pcr_values[vi].digests[di]; ++ int k; ++ for (k = 0; k < b->size; k++) { ++ tpm2_tool_output("%02X", b->buffer[k]); ++ } ++ tpm2_tool_output("\n"); ++ ++ if (++di < pcrs->pcr_values[vi].count) { ++ continue; ++ } ++ ++ di = 0; ++ if (++vi < pcrs->count) { ++ continue; ++ } ++ } ++ } ++ ++ return result; ++} ++ + + bool pcr_parse_selections(const char *arg, TPML_PCR_SELECTION *pcrSels) { + const char *strLeft = arg; +@@ -194,3 +307,122 @@ TSS2_RC get_max_supported_pcrs(TSS2_SYS_CONTEXT *sapi_context, UINT32 *max_pcrs) + + return TPM2_RC_SUCCESS; + } ++ ++bool pcr_get_banks(TSS2_SYS_CONTEXT *sapi_context, TPMS_CAPABILITY_DATA *capability_data, tpm2_algorithm *algs) { ++ ++ TPMI_YES_NO more_data; ++ UINT32 rval; ++ ++ rval = TSS2_RETRY_EXP(Tss2_Sys_GetCapability(sapi_context, no_argument, TPM2_CAP_PCRS, no_argument, required_argument, ++ &more_data, capability_data, 0)); ++ if (rval != TPM2_RC_SUCCESS) { ++ LOG_ERR( ++ "GetCapability: Get PCR allocation status Error. TPM Error:0x%x......", ++ rval); ++ return false; ++ } ++ ++ unsigned i; ++ ++ // If the TPM support more bank algorithm that we currently ++ // able to manage, throw an error ++ if (capability_data->data.assignedPCR.count > sizeof(algs->alg)) { ++ LOG_ERR("Current implementation does not support more than %zu banks, " ++ "got %" PRIu32 " banks supported by TPM", ++ sizeof(algs->alg), ++ capability_data->data.assignedPCR.count); ++ return false; ++ } ++ ++ for (i = 0; i < capability_data->data.assignedPCR.count; i++) { ++ algs->alg[i] = ++ capability_data->data.assignedPCR.pcrSelections[i].hash; ++ } ++ algs->count = capability_data->data.assignedPCR.count; ++ ++ return true; ++} ++ ++bool pcr_init_pcr_selection(TPMS_CAPABILITY_DATA *cap_data, TPML_PCR_SELECTION *pcr_sel, TPMI_ALG_HASH alg_id) { ++ ++ UINT32 i, j; ++ ++ pcr_sel->count = 0; ++ ++ for (i = 0; i < cap_data->data.assignedPCR.count; i++) { ++ if (alg_id && (cap_data->data.assignedPCR.pcrSelections[i].hash != alg_id)) ++ continue; ++ pcr_sel->pcrSelections[pcr_sel->count].hash = cap_data->data.assignedPCR.pcrSelections[i].hash; ++ set_pcr_select_size(&pcr_sel->pcrSelections[pcr_sel->count], cap_data->data.assignedPCR.pcrSelections[i].sizeofSelect); ++ for (j = 0; j < pcr_sel->pcrSelections[pcr_sel->count].sizeofSelect; j++) ++ pcr_sel->pcrSelections[pcr_sel->count].pcrSelect[j] = cap_data->data.assignedPCR.pcrSelections[i].pcrSelect[j]; ++ pcr_sel->count++; ++ } ++ ++ if (pcr_sel->count == 0) ++ return false; ++ ++ return true; ++} ++ ++bool pcr_check_pcr_selection(TPMS_CAPABILITY_DATA *cap_data, TPML_PCR_SELECTION *pcr_sel) { ++ ++ UINT32 i, j, k; ++ ++ for (i = 0; i < pcr_sel->count; i++) { ++ for (j = 0; j < cap_data->data.assignedPCR.count; j++) { ++ if (pcr_sel->pcrSelections[i].hash == cap_data->data.assignedPCR.pcrSelections[j].hash) { ++ for (k = 0; k < pcr_sel->pcrSelections[i].sizeofSelect; k++) ++ pcr_sel->pcrSelections[i].pcrSelect[k] &= cap_data->data.assignedPCR.pcrSelections[j].pcrSelect[k]; ++ break; ++ } ++ } ++ ++ if (j >= cap_data->data.assignedPCR.count) { ++ const char *alg_name = tpm2_alg_util_algtostr(pcr_sel->pcrSelections[i].hash); ++ LOG_WARN("Ignore unsupported bank/algorithm: %s(0x%04x)", alg_name, pcr_sel->pcrSelections[i].hash); ++ pcr_sel->pcrSelections[i].hash = 0; //mark it as to be removed ++ } ++ } ++ ++ shrink_pcr_selection(pcr_sel); ++ if (pcr_sel->count == 0) ++ return false; ++ ++ return true; ++} ++ ++bool pcr_read_pcr_values(TSS2_SYS_CONTEXT *sapi_context, TPML_PCR_SELECTION *pcrSelections, tpm2_pcrs *pcrs) { ++ ++ TPML_PCR_SELECTION pcr_selection_tmp; ++ TPML_PCR_SELECTION pcr_selection_out; ++ UINT32 pcr_update_counter; ++ ++ //1. prepare pcrSelectionIn with g_pcrSelections ++ memcpy(&pcr_selection_tmp, pcrSelections, sizeof(pcr_selection_tmp)); ++ ++ //2. call pcr_read ++ pcrs->count = 0; ++ do { ++ UINT32 rval = TSS2_RETRY_EXP(Tss2_Sys_PCR_Read(sapi_context, no_argument, &pcr_selection_tmp, ++ &pcr_update_counter, &pcr_selection_out, ++ &pcrs->pcr_values[pcrs->count], 0)); ++ ++ if (rval != TPM2_RC_SUCCESS) { ++ LOG_ERR("read pcr failed. tpm error 0x%0x", rval); ++ return -1; ++ } ++ ++ //3. unmask pcrSelectionOut bits from pcrSelectionIn ++ pcr_update_pcr_selections(&pcr_selection_tmp, &pcr_selection_out); ++ ++ //4. goto step 2 if pcrSelctionIn still has bits set ++ } while (++pcrs->count < sizeof(pcrs->pcr_values) && !pcr_unset_pcr_sections(&pcr_selection_tmp)); ++ ++ if (pcrs->count >= sizeof(pcrs->pcr_values) && !pcr_unset_pcr_sections(&pcr_selection_tmp)) { ++ LOG_ERR("too much pcrs to get! try to split into multiple calls..."); ++ return false; ++ } ++ ++ return true; ++} +diff --git a/lib/pcr.h b/lib/pcr.h +index ad6946b3c04..82d5dd696d4 100644 +--- a/lib/pcr.h ++++ b/lib/pcr.h +@@ -35,8 +35,35 @@ + + #include + ++typedef struct tpm2_algorithm tpm2_algorithm; ++struct tpm2_algorithm { ++ int count; ++ TPMI_ALG_HASH alg[TPM2_NUM_PCR_BANKS]; ++}; ++ ++typedef struct tpm2_pcrs tpm2_pcrs; ++struct tpm2_pcrs { ++ size_t count; ++ TPML_DIGEST pcr_values[TPM2_MAX_PCRS]; ++}; ++ ++/** ++ * Echo out all PCR banks according to g_pcrSelection & g_pcrs->. ++ * @param pcrSelect ++ * Description of which PCR registers are selected. ++ * @param pcrs ++ * Struct containing PCR digests. ++ * @return ++ * True on success, false otherwise. ++ */ ++bool pcr_print_pcr_struct(TPML_PCR_SELECTION *pcrSelect, tpm2_pcrs *pcrs); ++ + bool pcr_parse_selections(const char *arg, TPML_PCR_SELECTION *pcrSels); + bool pcr_parse_list(const char *str, size_t len, TPMS_PCR_SELECTION *pcrSel); + TSS2_RC get_max_supported_pcrs(TSS2_SYS_CONTEXT *sapi_context, UINT32 *max_pcrs); ++bool pcr_get_banks(TSS2_SYS_CONTEXT *sapi_context, TPMS_CAPABILITY_DATA *capability_data, tpm2_algorithm *algs); ++bool pcr_init_pcr_selection(TPMS_CAPABILITY_DATA *cap_data, TPML_PCR_SELECTION *pcr_sel, TPMI_ALG_HASH alg_id); ++bool pcr_check_pcr_selection(TPMS_CAPABILITY_DATA *cap_data, TPML_PCR_SELECTION *pcr_sel); ++bool pcr_read_pcr_values(TSS2_SYS_CONTEXT *sapi_context, TPML_PCR_SELECTION *pcrSelections, tpm2_pcrs *pcrs); + + #endif /* SRC_PCR_H_ */ +diff --git a/lib/tpm2_openssl.c b/lib/tpm2_openssl.c +index 0bfc95bd1ef..8d7314cba8e 100644 +--- a/lib/tpm2_openssl.c ++++ b/lib/tpm2_openssl.c +@@ -44,9 +44,26 @@ + #include "files.h" + #include "log.h" + #include "tpm2_alg_util.h" ++#include "tpm_kdfa.h" + #include "tpm2_openssl.h" + #include "tpm2_util.h" + ++int tpm2_openssl_halgid_from_tpmhalg(TPMI_ALG_HASH algorithm) { ++ ++ switch (algorithm) { ++ case TPM2_ALG_SHA1: ++ return NID_sha1; ++ case TPM2_ALG_SHA256: ++ return NID_sha256; ++ case TPM2_ALG_SHA384: ++ return NID_sha384; ++ case TPM2_ALG_SHA512: ++ return NID_sha512; ++ default: ++ return NID_sha256; ++ } ++ /* no return, not possible */ ++} + + const EVP_MD *tpm2_openssl_halg_from_tpmhalg(TPMI_ALG_HASH algorithm) { + +@@ -122,6 +139,127 @@ void tpm2_openssl_cipher_free(EVP_CIPHER_CTX *ctx) { + #endif + } + ++bool tpm2_openssl_hash_compute_data(TPMI_ALG_HASH halg, ++ BYTE *buffer, UINT16 length, TPM2B_DIGEST *digest) { ++ ++ bool result = false; ++ ++ const EVP_MD *md = tpm2_openssl_halg_from_tpmhalg(halg); ++ if (!md) { ++ return false; ++ } ++ ++ EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); ++ if (!mdctx) { ++ LOG_ERR("%s", get_openssl_err()); ++ return false; ++ } ++ ++ int rc = EVP_DigestInit_ex(mdctx, md, NULL); ++ if (!rc) { ++ LOG_ERR("%s", get_openssl_err()); ++ goto out; ++ } ++ ++ rc = EVP_DigestUpdate(mdctx, buffer, length); ++ if (!rc) { ++ LOG_ERR("%s", get_openssl_err()); ++ goto out; ++ } ++ ++ unsigned size = EVP_MD_size(md); ++ rc = EVP_DigestFinal_ex(mdctx, digest->buffer, &size); ++ if (!rc) { ++ LOG_ERR("%s", get_openssl_err()); ++ goto out; ++ } ++ ++ digest->size = size; ++ ++ result = true; ++ ++out: ++ EVP_MD_CTX_destroy(mdctx); ++ return result; ++} ++ ++// show all PCR banks according to g_pcrSelection & g_pcrs-> ++bool tpm2_openssl_hash_pcr_banks(TPMI_ALG_HASH hashAlg, ++ TPML_PCR_SELECTION *pcrSelect, ++ tpm2_pcrs *pcrs, TPM2B_DIGEST *digest) { ++ ++ UINT32 vi = 0, di = 0, i; ++ bool result = false; ++ ++ const EVP_MD *md = tpm2_openssl_halg_from_tpmhalg(hashAlg); ++ if (!md) { ++ return false; ++ } ++ ++ EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); ++ if (!mdctx) { ++ LOG_ERR("%s", get_openssl_err()); ++ return false; ++ } ++ ++ int rc = EVP_DigestInit_ex(mdctx, md, NULL); ++ if (!rc) { ++ LOG_ERR("%s", get_openssl_err()); ++ goto out; ++ } ++ ++ // Loop through all PCR/hash banks ++ for (i = 0; i < pcrSelect->count; i++) { ++ ++ // Loop through all PCRs in this bank ++ UINT8 pcr_id; ++ for (pcr_id = 0; pcr_id < pcrSelect->pcrSelections[i].sizeofSelect * 8; pcr_id++) { ++ if (!tpm2_util_is_pcr_select_bit_set(&pcrSelect->pcrSelections[i], ++ pcr_id)) { ++ // skip non-selected banks ++ continue; ++ } ++ if (vi >= pcrs->count || di >= pcrs->pcr_values[vi].count) { ++ LOG_ERR("Something wrong, trying to print but nothing more"); ++ goto out; ++ } ++ ++ // Update running digest (to compare with quote) ++ TPM2B_DIGEST *b = &pcrs->pcr_values[vi].digests[di]; ++ rc = EVP_DigestUpdate(mdctx, b->buffer, b->size); ++ if (!rc) { ++ LOG_ERR("%s", get_openssl_err()); ++ goto out; ++ } ++ ++ if (++di < pcrs->pcr_values[vi].count) { ++ continue; ++ } ++ ++ di = 0; ++ if (++vi < pcrs->count) { ++ continue; ++ } ++ } ++ } ++ ++ // Finalize running digest ++ unsigned size = EVP_MD_size(md); ++ rc = EVP_DigestFinal_ex(mdctx, digest->buffer, &size); ++ if (!rc) { ++ LOG_ERR("%s", get_openssl_err()); ++ goto out; ++ } ++ ++ digest->size = size; ++ ++ result = true; ++ ++out: ++ EVP_MD_CTX_destroy(mdctx); ++ return result; ++} ++ + digester tpm2_openssl_halg_to_digester(TPMI_ALG_HASH halg) { + + switch(halg) { +@@ -160,3 +298,28 @@ digester tpm2_openssl_halg_to_digester(TPMI_ALG_HASH halg) { + */ + + typedef bool (*pfn_ossl_pw_handler)(const char *passin, char **pass); ++ ++ ++RSA *tpm2_openssl_get_public_RSA_from_pem(FILE *f, const char *path) { ++ ++ /* ++ * Public PEM files appear in two formats: ++ * 1. PEM format, read with PEM_read_RSA_PUBKEY ++ * 2. PKCS#1 format, read with PEM_read_RSAPublicKey ++ * ++ * See: ++ * - https://stackoverflow.com/questions/7818117/why-i-cant-read-openssl-generated-rsa-pub-key-with-pem-read-rsapublickey ++ */ ++ RSA *pub = PEM_read_RSA_PUBKEY(f, NULL, NULL, NULL); ++ if (!pub) { ++ pub = PEM_read_RSAPublicKey(f, NULL, NULL, NULL); ++ } ++ ++ if (!pub) { ++ ERR_print_errors_fp (stderr); ++ LOG_ERR("Reading public PEM file \"%s\" failed", path); ++ return NULL; ++ } ++ ++ return pub; ++} +diff --git a/lib/tpm2_openssl.h b/lib/tpm2_openssl.h +index d749cb350ac..d3f4a0d7a32 100644 +--- a/lib/tpm2_openssl.h ++++ b/lib/tpm2_openssl.h +@@ -34,6 +34,8 @@ + #include + #include + ++#include "pcr.h" ++ + #if (OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) /* OpenSSL 1.1.0 */ + #define LIB_TPM2_OPENSSL_OPENSSL_PRE11 + #endif +@@ -60,6 +62,16 @@ int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); + */ + typedef unsigned char *(*digester)(const unsigned char *d, size_t n, unsigned char *md); + ++/** ++ ++ * Get an openssl hash algorithm ID from a tpm hashing algorithm ID. ++ * @param algorithm ++ * The tpm algorithm to get the corresponding openssl version of. ++ * @return ++ * The openssl hash algorithm id. ++ */ ++int tpm2_openssl_halgid_from_tpmhalg(TPMI_ALG_HASH algorithm); ++ + /** + * Get an openssl message digest from a tpm hashing algorithm. + * @param algorithm +@@ -86,6 +98,39 @@ EVP_CIPHER_CTX *tpm2_openssl_cipher_new(void); + */ + void tpm2_openssl_cipher_free(EVP_CIPHER_CTX *ctx); + ++/** ++ * Hash a byte buffer. ++ * @param halg ++ * The hashing algorithm to use. ++ * @param buffer ++ * The byte buffer to be hashed. ++ * @param length ++ * The length of the byte buffer to hash. ++^ * @param digest ++^ * The result of hashing digests with halg. ++ * @return ++ * true on success, false on error. ++ */ ++bool tpm2_openssl_hash_compute_data(TPMI_ALG_HASH halg, ++ BYTE *buffer, UINT16 length, TPM2B_DIGEST *digest); ++ ++/** ++ * Hash a list of PCR digests, supporting multiple banks. ++ * @param halg ++ * The hashing algorithm to use. ++ * @param pcrSelect ++ * The list that specifies which PCRs are selected. ++ * @param pcrs ++ * The list of PCR banks, each containing a list of PCR digests to hash. ++^ * @param digest ++^ * The result of hashing digests with halg. ++ * @return ++ * true on success, false on error. ++ */ ++bool tpm2_openssl_hash_pcr_banks(TPMI_ALG_HASH hashAlg, ++ TPML_PCR_SELECTION *pcrSelect, ++ tpm2_pcrs *pcrs, TPM2B_DIGEST *digest); ++ + /** + * Returns a function pointer capable of performing the + * given digest from a TPMI_HASH_ALG. +@@ -105,4 +150,16 @@ enum tpm2_openssl_load_rc { + }; + + ++/** ++ * Retrieves a public portion of an RSA key from a PEM file. ++ * ++ * @param f ++ * The FILE object that is open for reading the path. ++ * @param path ++ * The path to load from. ++ * @return ++ * The public structure. ++ */ ++RSA* tpm2_openssl_get_public_RSA_from_pem(FILE *f, const char *path); ++ + #endif /* LIB_TPM2_OPENSSL_H_ */ +diff --git a/lib/tpm2_util.c b/lib/tpm2_util.c +index 57d6c762a70..edfda4a8b0b 100644 +--- a/lib/tpm2_util.c ++++ b/lib/tpm2_util.c +@@ -41,6 +41,154 @@ + #include "tpm2_tool.h" + #include "tpm2_util.h" + ++ ++bool tpm2_util_get_digest_from_quote(TPM2B_ATTEST *quoted, TPM2B_DIGEST *digest, TPM2B_DATA *extraData) { ++ TPM2_GENERATED magic; ++ TPMI_ST_ATTEST type; ++ UINT16 nameSize = 0; ++ UINT32 i = 0; ++ ++ // Ensure required headers are at least there ++ if (quoted->size < 6) { ++ LOG_ERR("Malformed TPM2B_ATTEST headers"); ++ return false; ++ } ++ ++ memcpy(&magic, "ed->attestationData[i], 4);i += 4; ++ memcpy(&type, "ed->attestationData[i], 2);i += 2; ++ if (!tpm2_util_is_big_endian()) { ++ magic = tpm2_util_endian_swap_32(magic); ++ type = tpm2_util_endian_swap_16(type); ++ } ++ ++ if (magic != TPM2_GENERATED_VALUE) { ++ LOG_ERR("Malformed TPM2_GENERATED magic value"); ++ return false; ++ } ++ ++ if (type != TPM2_ST_ATTEST_QUOTE) { ++ LOG_ERR("Malformed TPMI_ST_ATTEST quote value"); ++ return false; ++ } ++ ++ // Qualified signer name (skip) ++ if (i+2 >= quoted->size) { ++ LOG_ERR("Malformed TPM2B_NAME value"); ++ return false; ++ } ++ memcpy(&nameSize, "ed->attestationData[i], 2);i += 2; ++ if (!tpm2_util_is_big_endian()) { ++ nameSize = tpm2_util_endian_swap_16(nameSize); ++ } ++ i += nameSize; ++ ++ // Extra data (skip) ++ if (i+2 >= quoted->size) { ++ LOG_ERR("Malformed TPM2B_DATA value"); ++ return false; ++ } ++ memcpy(&extraData->size, "ed->attestationData[i], 2);i += 2; ++ if (!tpm2_util_is_big_endian()) { ++ extraData->size = tpm2_util_endian_swap_16(extraData->size); ++ } ++ if (extraData->size+i > quoted->size) { ++ LOG_ERR("Malformed extraData TPM2B_DATA value"); ++ return false; ++ } ++ memcpy(&extraData->buffer, "ed->attestationData[i], extraData->size);i += extraData->size; ++ ++ // Clock info (skip) ++ i += 17; ++ if (i >= quoted->size) { ++ LOG_ERR("Malformed TPMS_CLOCK_INFO value"); ++ return false; ++ } ++ ++ // Firmware info (skip) ++ i += 8; ++ if (i >= quoted->size) { ++ LOG_ERR("Malformed firmware version value"); ++ return false; ++ } ++ ++ // PCR select info ++ UINT8 sos; ++ TPMI_ALG_HASH hashAlg; ++ UINT32 pcrSelCount = 0, j = 0; ++ if (i+4 >= quoted->size) { ++ LOG_ERR("Malformed TPML_PCR_SELECTION value"); ++ return false; ++ } ++ memcpy(&pcrSelCount, "ed->attestationData[i], 4);i += 4; ++ if (!tpm2_util_is_big_endian()) { ++ pcrSelCount = tpm2_util_endian_swap_32(pcrSelCount); ++ } ++ for (j = 0; j < pcrSelCount; j++) { ++ // Hash ++ if (i+2 >= quoted->size) { ++ LOG_ERR("Malformed TPMS_PCR_SELECTION value"); ++ return false; ++ } ++ memcpy(&hashAlg, "ed->attestationData[i], 2);i += 2; ++ if (!tpm2_util_is_big_endian()) { ++ hashAlg = tpm2_util_endian_swap_16(hashAlg); ++ } ++ ++ // SizeOfSelected ++ if (i+1 >= quoted->size) { ++ LOG_ERR("Malformed TPMS_PCR_SELECTION value"); ++ return false; ++ } ++ memcpy(&sos, "ed->attestationData[i], 1);i += 1; ++ ++ // PCR Select (skip) ++ i += sos; ++ if (i >= quoted->size) { ++ LOG_ERR("Malformed TPMS_PCR_SELECTION value"); ++ return false; ++ } ++ } ++ ++ // Digest ++ if (i+2 >= quoted->size) { ++ LOG_ERR("Malformed TPM2B_DIGEST value"); ++ return false; ++ } ++ memcpy(&digest->size, "ed->attestationData[i], 2);i += 2; ++ if (!tpm2_util_is_big_endian()) { ++ digest->size = tpm2_util_endian_swap_16(digest->size); ++ } ++ ++ if (digest->size+i > quoted->size) { ++ LOG_ERR("Malformed TPM2B_DIGEST value"); ++ return false; ++ } ++ memcpy(&digest->buffer, "ed->attestationData[i], digest->size); ++ ++ return true; ++} ++ ++// verify that the quote digest equals the digest we calculated ++bool tpm2_util_verify_digests(TPM2B_DIGEST *quoteDigest, TPM2B_DIGEST *pcrDigest) { ++ ++ // Sanity check -- they should at least be same size! ++ if (quoteDigest->size != pcrDigest->size) { ++ LOG_ERR("FATAL ERROR: PCR values failed to match quote's digest!"); ++ return false; ++ } ++ ++ // Compare running digest with quote's digest ++ int k; ++ for (k = 0; k < quoteDigest->size; k++) { ++ if (quoteDigest->buffer[k] != pcrDigest->buffer[k]) { ++ LOG_ERR("FATAL ERROR: PCR values failed to match quote's digest!"); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ + bool tpm2_util_concat_buffer(TPM2B_MAX_BUFFER *result, TPM2B *append) { + + if (!result || !append) { +diff --git a/lib/tpm2_util.h b/lib/tpm2_util.h +index e803dc1c30e..8b77c9e5374 100644 +--- a/lib/tpm2_util.h ++++ b/lib/tpm2_util.h +@@ -111,6 +111,30 @@ struct TPM2B { + + int tpm2_util_hex_to_byte_structure(const char *inStr, UINT16 *byteLenth, BYTE *byteBuffer); + ++/** ++ * Pulls the TPM2B_DIGEST out of a TPM2B_ATTEST quote. ++ * @param quoted ++ * The attestation quote structure. ++^ * @param digest ++^ * The digest from the quote. ++^ * @param extraData ++^ * The extraData from the quote. ++ * @return ++ * True on success, false otherwise. ++ */ ++bool tpm2_util_get_digest_from_quote(TPM2B_ATTEST *quoted, TPM2B_DIGEST *digest, TPM2B_DATA *extraData); ++ ++/** ++ * Compares two digests to ensure they are equal (for validation). ++ * @param quoteDigest ++ * The digest from the quote. ++ * @param pcrDigest ++ * The digest calculated off-TMP from the PCRs. ++ * @return ++ * True on success, false otherwise. ++ */ ++bool tpm2_util_verify_digests(TPM2B_DIGEST *quoteDigest, TPM2B_DIGEST *pcrDigest); ++ + /** + * Appends a TPM2B_DIGEST buffer to a TPM2B_MAX buffer. + * @param result +@@ -170,6 +194,15 @@ static inline void tpm2_util_print_tpm2b(TPM2B *buffer) { + + void tpm2_util_print_tpm2b(TPM2B *buffer); + ++/** ++ * Determines if given PCR value is selected in TPMS_PCR_SELECTION structure. ++ * @param pcr_selection the TPMS_PCR_SELECTION structure to check pcr against. ++ * @param pcr the PCR ID to check selection status of. ++ */ ++static inline bool tpm2_util_is_pcr_select_bit_set(TPMS_PCR_SELECTION *pcr_selection, UINT32 pcr) { ++ return (pcr_selection->pcrSelect[((pcr) / 8)] & (1 << ((pcr) % 8))); ++} ++ + /** + * Copies a tpm2b from dest to src and clears dest if src is NULL. + * If src is NULL, it is a NOP. +diff --git a/man/tpm2_checkquote.1.md b/man/tpm2_checkquote.1.md +new file mode 100644 +index 00000000000..00bb4bee9a7 +--- /dev/null ++++ b/man/tpm2_checkquote.1.md +@@ -0,0 +1,95 @@ ++% tpm2_checkquote(1) tpm2-tools | General Commands Manual ++% ++% JANUARY 2019 ++ ++# NAME ++ ++**tpm2_checkquote**(1) - Validates a quote provided by a TPM. ++ ++# SYNOPSIS ++ ++**tpm2_checkquote** [*OPTIONS*] ++ ++# DESCRIPTION ++ ++**tpm2_checkquote**(1) - Uses the public portion of the provided key to validate a quote ++generated by a TPM. This will validate the signature against the quote message and, if ++provided, verify that the qualifying data and PCR values match those in the quote. ++ ++# OPTIONS ++ ++ * **-c**, **--key-context**=_KEY\_CONTEXT\_OBJECT_: ++ ++ Context object for the key context used for the operation. Either a file ++ or a handle number. See section "Context Object Format". ++ ++ * **-G**, **--halg**=_HASH\_ALGORITHM_: ++ ++ The hash algorithm used to digest the message. ++ Algorithms should follow the "formatting standards", see section ++ "Algorithm Specifiers". ++ Also, see section "Supported Hash Algorithms" for a list of supported hash ++ algorithms. ++ ++ * **-m**, **--message**=_MSG\_FILE_: ++ ++ The quote message that makes up the data that is signed by the TPM. ++ ++ * **-s**, **--sig**=_SIG\_FILE_: ++ ++ The input signature file of the signature to be validated. ++ ++ * **-f**, **--format**: ++ ++ Set the input signature file to a specified format. The default is the TPM2.0 **TPMT_SIGNATURE** ++ data format, however different schemes can be selected if the data came from an external ++ source like OpenSSL. The tool currently only supports rsassa. ++ ++ Algorithms should follow the "formatting standards", see section ++ "Algorithm Specifiers". ++ Also, see section "Supported Signing Schemes" for a list of supported hash ++ algorithms. ++ ++ * **-p**, **--pcrs**: ++ ++ PCR output file, optional, records the list of PCR values that were included ++ in the quote. ++ ++ * **-q**, **--qualify-data**: ++ ++ Data given as a hex string that was used to qualify the quote. This is typically ++ used to add a nonce against replay attacks. ++ ++[common options](common/options.md) ++ ++[common tcti options](common/tcti.md) ++ ++[context object format](common/ctxobj.md) ++ ++[authorization formatting](common/password.md) ++ ++[supported hash algorithms](common/hash.md) ++ ++[supported signing schemes](common/signschemes.md) ++ ++[algorithm specifiers](common/alg.md) ++ ++# EXAMPLES ++ ++## Generate a quote with a TPM, then verify it ++``` ++tpm2_createprimary -H e -g sha256 -G rsa -C primary.ctx ++tpm2_create -g sha256 -G rsa -u ak.pub -r ak.priv -c primary.ctx ++tpm2_load -c primary.ctx -u ak.pub -r ak.priv -n ak.name -C ak.ctx ++tpm2_readpublic -c ak.ctx -o akpub.pem -f pem ++ ++tpm2_quote -c ak.ctx -L sha256:15,16,22 -q abc123 -m quote.out -s sig.out -p pcrs.out -G sha256 ++ ++tpm2_checkquote -c akpub.pem -m quote.out -s sig.out -p pcrs.out -G sha256 -q abc123 ++``` ++ ++# RETURNS ++ ++0 on success or 1 on failure. ++ ++[footer](common/footer.md) +diff --git a/man/tpm2_quote.1.md b/man/tpm2_quote.1.md +index 88c37e040c1..491848201d9 100644 +--- a/man/tpm2_quote.1.md ++++ b/man/tpm2_quote.1.md +@@ -53,6 +53,13 @@ + + Format selection for the signature output file. See section "Signature Format Specifiers". + ++ * **-p**, **--pcrs**: ++ ++ PCR output file, optional, records the list of PCR values as defined ++ by **-l** or **-L**. Note that only the digest of these values is stored in the ++ signed quote message -- these values themselves are not signed or ++ stored in the message. ++ + * **-q**, **--qualify-data**: + + Data given as a Hex string to qualify the quote, optional. This is typically +@@ -63,7 +70,7 @@ + + * **-G**, **--sig-hash-algorithm**: + +- Hash algorithm for signature. ++ Hash algorithm for signature. Required if **-p** is given. + + [common options](common/options.md) + +diff --git a/test/system/test_tpm2_checkquote.sh b/test/system/test_tpm2_checkquote.sh +new file mode 100755 +index 00000000000..670e3a737d1 +--- /dev/null ++++ b/test/system/test_tpm2_checkquote.sh +@@ -0,0 +1,86 @@ ++#!/bin/bash ++#;**********************************************************************; ++# ++# Copyright (c) 2019 Massachusetts Institute of Technology. ++# All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions are met: ++# ++# 1. Redistributions of source code must retain the above copyright notice, ++# this list of conditions and the following disclaimer. ++# ++# 2. Redistributions in binary form must reproduce the above copyright notice, ++# this list of conditions and the following disclaimer in the documentation ++# and/or other materials provided with the distribution. ++# ++# 3. Neither the name of 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 HOLDER OR CONTRIBUTORS BE ++# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++# THE POSSIBILITY OF SUCH DAMAGE. ++#;**********************************************************************; ++ ++source test_helpers.sh ++ ++alg_primary_obj=sha256 ++alg_primary_key=rsa ++alg_create_obj=sha256 ++alg_create_key=rsa ++ ++file_primary_key_ctx=context.p_"$alg_primary_obj"_"$alg_primary_key" ++file_quote_key_pub=opu_"$alg_create_obj"_"$alg_create_key" ++file_quote_key_priv=opr_"$alg_create_obj"_"$alg_create_key" ++file_quote_key_name=name.load_"$alg_primary_obj"_"$alg_primary_key"-"$alg_create_obj"_"$alg_create_key" ++file_quote_key_ctx=ctx_load_out_"$alg_primary_obj"_"$alg_primary_key"-"$alg_create_obj"_"$alg_create_key" ++output_ak_pub_pem=akpub.pem ++output_quote=quote.out ++output_quotesig=quotesig.out ++output_quotepcr=quotepcr.out ++ ++maxdigest=$(tpm2_getcap -c properties-fixed | grep TPM_PT_MAX_DIGEST | sed -r -e 's/.*(0x[0-9a-f]+)/\1/g') ++if ! [[ "$maxdigest" =~ ^(0x)*[0-9]+$ ]] ; then ++ echo "error: not a number, got: \"$maxdigest\"" >&2 ++ exit 1 ++fi ++ ++nonce=12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde12345abcde ++nonce=${nonce:0:2*$maxdigest} ++ ++cleanup() { ++ rm -f $file_primary_key_ctx $file_quote_key_pub $file_quote_key_priv \ ++ $file_quote_key_name $file_quote_key_ctx $output_ak_pub_pem \ ++ $output_quote $output_quotesig $output_quotepcr ++} ++trap cleanup EXIT ++ ++cleanup ++ ++ ++tpm2_takeownership -c ++ ++# Key generation ++tpm2_createprimary -Q -H e -g $alg_primary_obj -G $alg_primary_key -C $file_primary_key_ctx ++tpm2_create -Q -g $alg_create_obj -G $alg_create_key -u $file_quote_key_pub -r $file_quote_key_priv -c $file_primary_key_ctx ++tpm2_load -Q -c $file_primary_key_ctx -u $file_quote_key_pub -r $file_quote_key_priv -n $file_quote_key_name -C $file_quote_key_ctx ++ ++# Get the PEM version of pub ak cert ++tpm2_readpublic -Q -c $file_quote_key_ctx -o $output_ak_pub_pem -f pem ++ ++# Quoting ++tpm2_quote -Q -c $file_quote_key_ctx -L sha256:15,16,22 -q $nonce -m $output_quote -s $output_quotesig -p $output_quotepcr -G $alg_primary_obj ++ ++# Verify quote ++tpm2_checkquote -Q -c $output_ak_pub_pem -m $output_quote -s $output_quotesig -p $output_quotepcr -G $alg_primary_obj -q $nonce ++ ++exit 0 +diff --git a/test/system/test_tpm2_quote.sh b/test/system/test_tpm2_quote.sh +index 231bed326ec..aa06a3d7040 100755 +--- a/test/system/test_tpm2_quote.sh ++++ b/test/system/test_tpm2_quote.sh +@@ -52,6 +52,8 @@ Handle_ek_quote=0x81010017 + Handle_ak_quote2=0x81010018 + Handle_ak_quote3=0x81010019 + ++toss_out=junk.out ++ + maxdigest=$(tpm2_getcap -c properties-fixed | grep TPM_PT_MAX_DIGEST | sed -r -e 's/.*(0x[0-9a-f]+)/\1/g') + if ! [[ "$maxdigest" =~ ^(0x)*[0-9]+$ ]] ; then + echo "error: not a number, got: \"$maxdigest\"" >&2 +@@ -69,7 +71,7 @@ trap onerror ERR + + cleanup() { + rm -f $file_primary_key_ctx $file_quote_key_pub $file_quote_key_priv \ +- $file_quote_key_name $file_quote_key_ctx ek.pub2 ak.pub2 ak.name_2 \ ++ $file_quote_key_name $file_quote_key_ctx $toss_out ek.pub2 ak.pub2 ak.name_2 \ + + tpm2_evictcontrol -Q -Ao -H $Handle_ek_quote 2>/dev/null || true + tpm2_evictcontrol -Q -Ao -H $Handle_ak_quote 2>/dev/null || true +@@ -90,21 +92,21 @@ tpm2_load -Q -c $file_primary_key_ctx -u $file_quote_key_pub -r $file_quote_ke + + tpm2_quote -Q -c $file_quote_key_ctx -g $alg_quote -l 16,17,18 -q $nonce + +-tpm2_quote -Q -c $file_quote_key_ctx -L $alg_quote:16,17,18+$alg_quote1:16,17,18 -q $nonce ++tpm2_quote -Q -c $file_quote_key_ctx -L $alg_quote:16,17,18+$alg_quote1:16,17,18 -q $nonce -m $toss_out -s $toss_out -p $toss_out -G $alg_primary_obj + + #####handle testing + tpm2_evictcontrol -Q -A o -c $file_quote_key_ctx -S $Handle_ak_quote + +-tpm2_quote -Q -k $Handle_ak_quote -g $alg_quote -l 16,17,18 -q $nonce ++tpm2_quote -Q -k $Handle_ak_quote -g $alg_quote -l 16,17,18 -q $nonce -m $toss_out -s $toss_out -p $toss_out -G $alg_primary_obj + +-tpm2_quote -Q -k $Handle_ak_quote -L $alg_quote:16,17,18+$alg_quote1:16,17,18 -q $nonce ++tpm2_quote -Q -k $Handle_ak_quote -L $alg_quote:16,17,18+$alg_quote1:16,17,18 -q $nonce -m $toss_out -s $toss_out -p $toss_out -G $alg_primary_obj + + #####AK + tpm2_getpubek -Q -H $Handle_ek_quote -g 0x01 -f ek.pub2 + + tpm2_getpubak -Q -E $Handle_ek_quote -k $Handle_ak_quote2 -f ak.pub2 -n ak.name_2 + +-tpm2_quote -Q -k $Handle_ak_quote -g $alg_quote -l 16,17,18 -q $nonce ++tpm2_quote -Q -k $Handle_ak_quote -g $alg_quote -l 16,17,18 -q $nonce -m $toss_out -s $toss_out -p $toss_out -G $alg_primary_obj + + #####AK with password + tpm2_getpubak -Q -E $Handle_ek_quote -k $Handle_ak_quote3 -f ak.pub2 -n ak.name_2 -P abc123 +diff --git a/tools/tpm2_checkquote.c b/tools/tpm2_checkquote.c +new file mode 100644 +index 00000000000..0efd7f3ca88 +--- /dev/null ++++ b/tools/tpm2_checkquote.c +@@ -0,0 +1,409 @@ ++//**********************************************************************; ++// Copyright (c) 2019 Massachusetts Institute of Technology. ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// 1. Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// ++// 3. Neither the name of 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 HOLDER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++// THE POSSIBILITY OF SUCH DAMAGE. ++//**********************************************************************; ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#include "files.h" ++#include "log.h" ++#include "pcr.h" ++#include "tpm2_alg_util.h" ++#include "conversion.h" ++#include "tpm_hash.h" ++#include "tpm2_openssl.h" ++#include "tpm2_options.h" ++#include "tpm2_tool.h" ++#include "tpm2_util.h" ++ ++typedef struct tpm2_verifysig_ctx tpm2_verifysig_ctx; ++struct tpm2_verifysig_ctx { ++ union { ++ struct { ++ UINT8 halg :1; ++ UINT8 msg :1; ++ UINT8 sig :1; ++ UINT8 pcr :1; ++ UINT8 extra :1; ++ UINT8 key_context :1; ++ UINT8 fmt; ++ }; ++ UINT8 all; ++ } flags; ++ TPMI_ALG_SIG_SCHEME format; ++ TPMI_ALG_HASH halg; ++ TPM2B_DIGEST msgHash; ++ TPM2B_DIGEST pcrHash; ++ TPM2B_DIGEST quoteHash; ++ TPM2B_DATA quoteExtraData; ++ TPM2B_DATA extraData; ++ TPMT_SIGNATURE signature; ++ char *msg_file_path; ++ char *sig_file_path; ++ char *out_file_path; ++ char *pcr_file_path; ++ const char *pubkey_file_path; ++}; ++ ++tpm2_verifysig_ctx ctx = { ++ .format = TPM2_ALG_ERROR, ++ .halg = TPM2_ALG_SHA1, ++ .msgHash = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer), ++ .pcrHash = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer), ++ .quoteHash = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer), ++ .quoteExtraData = TPM2B_TYPE_INIT(TPM2B_DATA, buffer), ++ .extraData = TPM2B_TYPE_INIT(TPM2B_DATA, buffer), ++}; ++ ++static bool verify_signature() { ++ ++ bool result = false; ++ ++ // Read in the AKpub they provided as an RSA object ++ FILE *pubkey_input = fopen(ctx.pubkey_file_path, "rb"); ++ if (!pubkey_input) { ++ LOG_ERR("Could not open RSA pubkey input file \"%s\" error: \"%s\"", ++ ctx.pubkey_file_path, strerror(errno)); ++ return false; ++ } ++ RSA *pubKey = tpm2_openssl_get_public_RSA_from_pem(pubkey_input, ctx.pubkey_file_path); ++ if (pubKey == NULL) { ++ LOG_ERR("Failed to load RSA public key from file"); ++ goto err; ++ } ++ ++ // Get the signature ready ++ if (ctx.signature.sigAlg != TPM2_ALG_RSASSA) { ++ LOG_ERR("Only RSASSA is supported for signatures"); ++ goto err; ++ } ++ TPM2B_PUBLIC_KEY_RSA sig = ctx.signature.signature.rsassa.sig; ++ tpm2_tool_output("sigBuffer: "); ++ tpm2_util_hexdump(sig.buffer, sig.size, true); ++ tpm2_tool_output("\n"); ++ ++ // Verify the signature matches message digest ++ int opensslHash = tpm2_openssl_halgid_from_tpmhalg(ctx.signature.signature.rsassa.hash); ++ if (!RSA_verify(opensslHash, ctx.msgHash.buffer, ctx.msgHash.size, ++ sig.buffer, sig.size, pubKey)) { ++ LOG_ERR("Error validating signed message with public key provided"); ++ goto err; ++ } ++ ++ // Ensure nonce is the same as given ++ if (ctx.flags.extra) { ++ if ( ++ ctx.quoteExtraData.size != ctx.extraData.size ++ || memcmp(ctx.quoteExtraData.buffer, ctx.extraData.buffer, ctx.extraData.size) != 0 ++ ) { ++ LOG_ERR("Error validating nonce from quote"); ++ goto err; ++ } ++ } ++ ++ // Also ensure digest from quote matches PCR digest ++ if (ctx.flags.pcr) { ++ if (!tpm2_util_verify_digests(&ctx.quoteHash, &ctx.pcrHash)) { ++ LOG_ERR("Error validating PCR composite against signed message"); ++ goto err; ++ } ++ } ++ ++ result = true; ++ ++err: ++ if (pubkey_input) { ++ fclose(pubkey_input); ++ } ++ ++ RSA_free(pubKey); ++ ++ return result; ++} ++ ++static TPM2B_ATTEST *message_from_file(const char *msg_file_path) { ++ ++ unsigned long size; ++ ++ bool result = files_get_file_size_path(msg_file_path, &size); ++ if (!result) { ++ return NULL; ++ } ++ ++ if (!size) { ++ LOG_ERR("The msg file \"%s\" is empty", msg_file_path); ++ return NULL; ++ } ++ ++ TPM2B_ATTEST *msg = (TPM2B_ATTEST *) calloc(1, sizeof(TPM2B_ATTEST) + size); ++ if (!msg) { ++ LOG_ERR("OOM"); ++ return NULL; ++ } ++ ++ UINT16 tmp = msg->size = size; ++ if (!files_load_bytes_from_path(msg_file_path, msg->attestationData, &tmp)) { ++ free(msg); ++ return NULL; ++ } ++ return msg; ++} ++ ++static bool pcrs_from_file(const char *pcr_file_path, ++ TPML_PCR_SELECTION *pcrSel, tpm2_pcrs *pcrs) { ++ ++ bool result = false; ++ unsigned long size; ++ ++ if (!files_get_file_size_path(pcr_file_path, &size)) { ++ return false; ++ } ++ ++ if (!size) { ++ LOG_ERR("The pcr file \"%s\" is empty", pcr_file_path); ++ return false; ++ } ++ ++ FILE *pcr_input = fopen(pcr_file_path, "rb"); ++ if (!pcr_input) { ++ LOG_ERR("Could not open PCRs input file \"%s\" error: \"%s\"", ++ pcr_file_path, strerror(errno)); ++ goto out; ++ } ++ ++ // Import TPML_PCR_SELECTION structure to pcr outfile ++ if (fread(pcrSel, sizeof(TPML_PCR_SELECTION), 1, pcr_input) != 1) { ++ LOG_ERR("Failed to read PCR selection from file"); ++ goto out; ++ } ++ ++ // Import PCR digests to pcr outfile ++ if (fread(&pcrs->count, sizeof(UINT32), 1, pcr_input) != 1) { ++ LOG_ERR("Failed to read PCR digests header from file"); ++ goto out; ++ } ++ ++ UINT32 j; ++ for (j = 0; j < pcrs->count; j++) { ++ if (fread(&pcrs->pcr_values[j], sizeof(TPML_DIGEST), 1, pcr_input) != 1) { ++ LOG_ERR("Failed to read PCR digest from file"); ++ goto out; ++ } ++ } ++ ++ result = true; ++ ++out: ++ if (pcr_input) { ++ fclose(pcr_input); ++ } ++ ++ return result; ++} ++ ++static bool init() { ++ ++ /* check flags for mismatches */ ++ if (!(ctx.pubkey_file_path && ctx.flags.sig && ctx.flags.msg && ctx.flags.halg)) { ++ LOG_ERR( ++ "--pubkey (-c), --msg (-m), --halg (-g) and --sig (-s) are required"); ++ return false; ++ } ++ ++ TPM2B_ATTEST *msg = NULL; ++ TPML_PCR_SELECTION pcrSel; ++ tpm2_pcrs pcrs; ++ bool return_value = false; ++ ++ if (ctx.flags.msg) { ++ msg = message_from_file(ctx.msg_file_path); ++ if (!msg) { ++ /* message_from_file() logs specific error no need to here */ ++ return false; ++ } ++ } ++ ++ if (ctx.flags.sig) { ++ bool res = files_load_signature(ctx.sig_file_path, &ctx.signature); ++ if (!res) { ++ goto err; ++ } ++ } ++ ++ /* If no digest is specified, compute it */ ++ if (!ctx.flags.msg) { ++ /* ++ * This is a redundant check since main() checks this case, but we'll add it here to silence any ++ * complainers. ++ */ ++ LOG_ERR("No digest set and no message file to compute from, cannot compute message hash!"); ++ goto err; ++ } ++ ++ if (ctx.flags.pcr) { ++ if (!pcrs_from_file(ctx.pcr_file_path, &pcrSel, &pcrs)) { ++ /* pcrs_from_file() logs specific error no need to here */ ++ goto err; ++ } ++ ++ if (!tpm2_openssl_hash_pcr_banks(ctx.halg, &pcrSel, &pcrs, &ctx.pcrHash)) { ++ LOG_ERR("Failed to hash PCR values related to quote!"); ++ goto err; ++ } ++ if (!pcr_print_pcr_struct(&pcrSel, &pcrs)) { ++ LOG_ERR("Failed to print PCR values related to quote!"); ++ goto err; ++ } ++ tpm2_tool_output("calcDigest: "); ++ tpm2_util_hexdump(ctx.pcrHash.buffer, ctx.pcrHash.size, true); ++ tpm2_tool_output("\n"); ++ } ++ ++ // Figure out the extra data (nonce) from this message ++ if (!tpm2_util_get_digest_from_quote(msg, &ctx.quoteHash, &ctx.quoteExtraData)) { ++ LOG_ERR("Failed to get digest from quote!"); ++ goto err; ++ } ++ ++ // Figure out the digest for this message ++ bool res = tpm2_openssl_hash_compute_data(ctx.halg, msg->attestationData, ++ msg->size, &ctx.msgHash); ++ if (!res) { ++ LOG_ERR("Compute message hash failed!"); ++ goto err; ++ } ++ tpm2_tool_output("msgDigest: "); ++ tpm2_util_hexdump(ctx.msgHash.buffer, ctx.msgHash.size, true); ++ tpm2_tool_output("\n"); ++ ++ return_value = true; ++ ++err: ++ free(msg); ++ return return_value; ++ ++} ++ ++static bool on_option(char key, char *value) { ++ ++ switch (key) { ++ case 'c': ++ ctx.pubkey_file_path = value; ++ break; ++ case 'G': { ++ ctx.halg = tpm2_alg_util_from_optarg(value); ++ if (ctx.halg == TPM2_ALG_ERROR) { ++ LOG_ERR("Unable to convert algorithm, got: \"%s\"", value); ++ return false; ++ } ++ ctx.flags.halg = 1; ++ } ++ break; ++ case 'm': { ++ ctx.msg_file_path = value; ++ ctx.flags.msg = 1; ++ } ++ break; ++ case 'f': { ++ ctx.format = tpm2_alg_util_from_optarg(value); ++ if (ctx.format == TPM2_ALG_ERROR) { ++ LOG_ERR("Unknown signing scheme, got: \"%s\"", value); ++ return false; ++ } ++ ++ ctx.flags.fmt = 1; ++ } break; ++ case 'q': ++ ctx.extraData.size = sizeof(ctx.extraData) - 2; ++ if(tpm2_util_hex_to_byte_structure(value, &ctx.extraData.size, ctx.extraData.buffer) != 0) ++ { ++ LOG_ERR("Could not convert \"%s\" from a hex string to byte array!", value); ++ return false; ++ } ++ ctx.flags.extra = 1; ++ break; ++ case 's': ++ ctx.sig_file_path = value; ++ ctx.flags.sig = 1; ++ break; ++ case 'p': ++ ctx.pcr_file_path = value; ++ ctx.flags.pcr = 1; ++ break; ++ /* no default */ ++ } ++ ++ return true; ++} ++ ++bool tpm2_tool_onstart(tpm2_options **opts) { ++ ++ const struct option topts[] = { ++ { "halg", required_argument, NULL, 'G' }, ++ { "message", required_argument, NULL, 'm' }, ++ { "format", required_argument, NULL, 'f' }, ++ { "sig", required_argument, NULL, 's' }, ++ { "pcrs", required_argument, NULL, 'p' }, ++ { "pubkey", required_argument, NULL, 'c' }, ++ { "qualify-data", required_argument, NULL, 'q' }, ++ }; ++ ++ ++ *opts = tpm2_options_new("G:m:f:s:t:c:p:q:", ARRAY_LEN(topts), topts, ++ on_option, NULL, TPM2_OPTIONS_NO_SAPI); ++ ++ return *opts != NULL; ++} ++ ++int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) { ++ ++ UNUSED(sapi_context); ++ UNUSED(flags); ++ ++ /* initialize and process */ ++ bool res = init(); ++ if (!res) { ++ return 1; ++ } ++ ++ res = verify_signature(); ++ if (!res) { ++ LOG_ERR("Verify signature failed!"); ++ return 1; ++ } ++ ++ return 0; ++} +diff --git a/tools/tpm2_pcrlist.c b/tools/tpm2_pcrlist.c +index 581bcecbd63..9a1ee457ce1 100644 +--- a/tools/tpm2_pcrlist.c ++++ b/tools/tpm2_pcrlist.c +@@ -44,17 +44,6 @@ + #include "tpm2_alg_util.h" + #include "tpm2_tool.h" + +-typedef struct tpm2_algorithm tpm2_algorithm; +-struct tpm2_algorithm { +- int count; +- TPMI_ALG_HASH alg[8]; //XXX Why 8? +-}; +- +-typedef struct tpm2_pcrs tpm2_pcrs; +-struct tpm2_pcrs { +- size_t count; +- TPML_DIGEST pcr_values[24]; //XXX Why 24? +-}; + + typedef struct listpcr_context listpcr_context; + struct listpcr_context { +@@ -85,163 +74,6 @@ static listpcr_context ctx = { + }, + }; + +-static inline void set_pcr_select_size(TPMS_PCR_SELECTION *pcr_selection, +- UINT8 size) { +- +- pcr_selection->sizeofSelect = size; +-} +- +-static bool is_pcr_select_bit_set(TPMS_PCR_SELECTION *pcr_selection, UINT32 pcr) { +- +- return (pcr_selection->pcrSelect[((pcr) / 8)] & (1 << ((pcr) % 8))); +-} +- +-static void update_pcr_selections(TPML_PCR_SELECTION *s1, TPML_PCR_SELECTION *s2) { +- +- UINT32 i1, i2, j; +- for (i2 = 0; i2 < s2->count; i2++) { +- for (i1 = 0; i1 < s1->count; i1++) { +- if (s2->pcrSelections[i2].hash != s1->pcrSelections[i1].hash) +- continue; +- +- for (j = 0; j < s1->pcrSelections[i1].sizeofSelect; j++) +- s1->pcrSelections[i1].pcrSelect[j] &= +- ~s2->pcrSelections[i2].pcrSelect[j]; +- } +- } +-} +- +-static bool unset_pcr_sections(TPML_PCR_SELECTION *s) { +- +- UINT32 i, j; +- for (i = 0; i < s->count; i++) { +- for (j = 0; j < s->pcrSelections[i].sizeofSelect; j++) { +- if (s->pcrSelections[i].pcrSelect[j]) { +- return false; +- } +- } +- } +- +- return true; +-} +- +-static bool read_pcr_values(TSS2_SYS_CONTEXT *sapi_context) { +- +- TPML_PCR_SELECTION pcr_selection_tmp; +- TPML_PCR_SELECTION pcr_selection_out; +- UINT32 pcr_update_counter; +- +- //1. prepare pcrSelectionIn with g_pcrSelections +- memcpy(&pcr_selection_tmp, &ctx.pcr_selections, sizeof(pcr_selection_tmp)); +- +- //2. call pcr_read +- ctx.pcrs.count = 0; +- do { +- UINT32 rval = TSS2_RETRY_EXP(Tss2_Sys_PCR_Read(sapi_context, no_argument, &pcr_selection_tmp, +- &pcr_update_counter, &pcr_selection_out, +- &ctx.pcrs.pcr_values[ctx.pcrs.count], 0)); +- +- if (rval != TPM2_RC_SUCCESS) { +- LOG_ERR("read pcr failed. tpm error 0x%0x", rval); +- return -1; +- } +- +- //3. unmask pcrSelectionOut bits from pcrSelectionIn +- update_pcr_selections(&pcr_selection_tmp, &pcr_selection_out); +- +- //4. goto step 2 if pcrSelctionIn still has bits set +- } while (++ctx.pcrs.count < 24 && !unset_pcr_sections(&pcr_selection_tmp)); +- +- if (ctx.pcrs.count >= 24 && !unset_pcr_sections(&pcr_selection_tmp)) { +- LOG_ERR("too much pcrs to get! try to split into multiple calls..."); +- return false; +- } +- +- return true; +-} +- +-static bool init_pcr_selection(void) { +- +- TPMS_CAPABILITY_DATA *cap_data = &ctx.cap_data; +- TPML_PCR_SELECTION *pcr_sel = &ctx.pcr_selections; +- UINT32 i, j; +- +- TPMI_ALG_HASH alg_id = ctx.selected_algorithm; +- +- pcr_sel->count = 0; +- +- for (i = 0; i < cap_data->data.assignedPCR.count; i++) { +- if (alg_id && (cap_data->data.assignedPCR.pcrSelections[i].hash != alg_id)) +- continue; +- pcr_sel->pcrSelections[pcr_sel->count].hash = cap_data->data.assignedPCR.pcrSelections[i].hash; +- set_pcr_select_size(&pcr_sel->pcrSelections[pcr_sel->count], cap_data->data.assignedPCR.pcrSelections[i].sizeofSelect); +- for (j = 0; j < pcr_sel->pcrSelections[pcr_sel->count].sizeofSelect; j++) +- pcr_sel->pcrSelections[pcr_sel->count].pcrSelect[j] = cap_data->data.assignedPCR.pcrSelections[i].pcrSelect[j]; +- pcr_sel->count++; +- } +- +- if (pcr_sel->count == 0) +- return false; +- +- return true; +-} +- +-static void shrink_pcr_selection(TPML_PCR_SELECTION *s) { +- +- UINT32 i, j; +- +- //seek for the first empty item +- for (i = 0; i < s->count; i++) +- if (!s->pcrSelections[i].hash) +- break; +- j = i + 1; +- +- for (; i < s->count; i++) { +- if (!s->pcrSelections[i].hash) { +- for (; j < s->count; j++) +- if (s->pcrSelections[j].hash) +- break; +- if (j >= s->count) +- break; +- +- memcpy(&s->pcrSelections[i], &s->pcrSelections[j], sizeof(s->pcrSelections[i])); +- s->pcrSelections[j].hash = 0; +- j++; +- } +- } +- +- s->count = i; +-} +- +-static bool check_pcr_selection(void) { +- +- TPMS_CAPABILITY_DATA *cap_data = &ctx.cap_data; +- TPML_PCR_SELECTION *pcr_sel = &ctx.pcr_selections; +- UINT32 i, j, k; +- +- for (i = 0; i < pcr_sel->count; i++) { +- for (j = 0; j < cap_data->data.assignedPCR.count; j++) { +- if (pcr_sel->pcrSelections[i].hash == cap_data->data.assignedPCR.pcrSelections[j].hash) { +- for (k = 0; k < pcr_sel->pcrSelections[i].sizeofSelect; k++) +- pcr_sel->pcrSelections[i].pcrSelect[k] &= cap_data->data.assignedPCR.pcrSelections[j].pcrSelect[k]; +- break; +- } +- } +- +- if (j >= cap_data->data.assignedPCR.count) { +- const char *alg_name = tpm2_alg_util_algtostr(pcr_sel->pcrSelections[i].hash); +- LOG_WARN("Ignore unsupported bank/algorithm: %s(0x%04x)", alg_name, pcr_sel->pcrSelections[i].hash); +- pcr_sel->pcrSelections[i].hash = 0; //mark it as to be removed +- } +- } +- +- shrink_pcr_selection(pcr_sel); +- if (pcr_sel->count == 0) +- return false; +- +- return true; +-} +- + // show all PCR banks according to g_pcrSelection & g_pcrs-> + static bool show_pcr_values(void) { + +@@ -255,7 +87,7 @@ static bool show_pcr_values(void) { + + UINT32 pcr_id; + for (pcr_id = 0; pcr_id < ctx.pcr_selections.pcrSelections[i].sizeofSelect * 8; pcr_id++) { +- if (!is_pcr_select_bit_set(&ctx.pcr_selections.pcrSelections[i], ++ if (!tpm2_util_is_pcr_select_bit_set(&ctx.pcr_selections.pcrSelections[i], + pcr_id)) { + continue; + } +@@ -296,10 +128,10 @@ static bool show_pcr_values(void) { + + static bool show_selected_pcr_values(TSS2_SYS_CONTEXT *sapi_context, bool check) { + +- if (check && !check_pcr_selection()) ++ if (check && !pcr_check_pcr_selection(&ctx.cap_data, &ctx.pcr_selections)) + return false; + +- if (!read_pcr_values(sapi_context)) ++ if (!pcr_read_pcr_values(sapi_context, &ctx.pcr_selections, &ctx.pcrs)) + return false; + + if (!show_pcr_values()) +@@ -310,7 +142,7 @@ static bool show_selected_pcr_values(TSS2_SYS_CONTEXT *sapi_context, bool check) + + static bool show_all_pcr_values(TSS2_SYS_CONTEXT *sapi_context) { + +- if (!init_pcr_selection()) ++ if (!pcr_init_pcr_selection(&ctx.cap_data, &ctx.pcr_selections, ctx.selected_algorithm)) + return false; + + return show_selected_pcr_values(sapi_context, false); +@@ -318,37 +150,12 @@ static bool show_all_pcr_values(TSS2_SYS_CONTEXT *sapi_context) { + + static bool show_alg_pcr_values(TSS2_SYS_CONTEXT *sapi_context) { + +- if (!init_pcr_selection()) ++ if (!pcr_init_pcr_selection(&ctx.cap_data, &ctx.pcr_selections, ctx.selected_algorithm)) + return false; + + return show_selected_pcr_values(sapi_context, false); + } + +-static bool get_banks(TSS2_SYS_CONTEXT *sapi_context) { +- +- TPMI_YES_NO more_data; +- TPMS_CAPABILITY_DATA *capability_data = &ctx.cap_data; +- UINT32 rval; +- +- rval = TSS2_RETRY_EXP(Tss2_Sys_GetCapability(sapi_context, no_argument, TPM2_CAP_PCRS, no_argument, required_argument, +- &more_data, capability_data, 0)); +- if (rval != TPM2_RC_SUCCESS) { +- LOG_ERR( +- "GetCapability: Get PCR allocation status Error. TPM Error:0x%x......", +- rval); +- return false; +- } +- +- unsigned i; +- for (i = 0; i < capability_data->data.assignedPCR.count; i++) { +- ctx.algs.alg[i] = +- capability_data->data.assignedPCR.pcrSelections[i].hash; +- } +- ctx.algs.count = capability_data->data.assignedPCR.count; +- +- return true; +-} +- + static void show_banks(tpm2_algorithm *g_banks) { + + tpm2_tool_output("Supported Bank/Algorithm:"); +@@ -432,7 +239,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) { + } + } + +- success = get_banks(sapi_context); ++ success = pcr_get_banks(sapi_context, &ctx.cap_data, &ctx.algs); + if (!success) { + goto error; + } +diff --git a/tools/tpm2_quote.c b/tools/tpm2_quote.c +index 05b6d641656..2efba240340 100644 +--- a/tools/tpm2_quote.c ++++ b/tools/tpm2_quote.c +@@ -42,6 +42,7 @@ + #include "conversion.h" + #include "tpm2_alg_util.h" + #include "tpm2_password_util.h" ++#include "tpm2_openssl.h" + #include "tpm2_tool.h" + #include "tpm2_util.h" + +@@ -54,15 +55,27 @@ static TPMS_AUTH_COMMAND sessionData = TPMS_AUTH_COMMAND_INIT(TPM2_RS_PW); + static char *outFilePath; + static char *signature_path; + static char *message_path; ++static char *pcr_path; ++static FILE *pcr_output; ++static TPMS_CAPABILITY_DATA cap_data; + static signature_format sig_format; + static TPMI_ALG_HASH sig_hash_algorithm; ++static tpm2_algorithm algs = { ++ .count = 3, ++ .alg = { ++ TPM2_ALG_SHA1, ++ TPM2_ALG_SHA256, ++ TPM2_ALG_SHA384 ++ } ++}; + static TPM2B_DATA qualifyingData = TPM2B_EMPTY_INIT; + static TPML_PCR_SELECTION pcrSelections; + static bool is_auth_session; + static TPMI_SH_AUTH_SESSION auth_session_handle; +-static int k_flag, c_flag, l_flag, g_flag, L_flag, o_flag, G_flag, P_flag; ++static int k_flag, c_flag, l_flag, g_flag, L_flag, o_flag, G_flag, P_flag, p_flag; + static char *contextFilePath; + static TPM2_HANDLE akHandle; ++static tpm2_pcrs pcrs; + + static void PrintBuffer( UINT8 *buffer, UINT32 size ) + { +@@ -74,6 +87,40 @@ static void PrintBuffer( UINT8 *buffer, UINT32 size ) + tpm2_tool_output("\n"); + } + ++ ++// write all PCR banks according to g_pcrSelection & g_pcrs-> ++static bool write_pcr_values() { ++ ++ // PCR output to file wasn't requested ++ if (pcr_output == NULL) { ++ return true; ++ } ++ ++ // Export TPML_PCR_SELECTION structure to pcr outfile ++ if (fwrite(&pcrSelections, ++ sizeof(TPML_PCR_SELECTION), 1, ++ pcr_output) != 1) { ++ LOG_ERR("write to output file failed: %s", strerror(errno)); ++ return false; ++ } ++ ++ // Export PCR digests to pcr outfile ++ if (fwrite(&pcrs.count, sizeof(UINT32), 1, pcr_output) != 1) { ++ LOG_ERR("write to output file failed: %s", strerror(errno)); ++ return false; ++ } ++ ++ UINT32 j; ++ for (j = 0; j < pcrs.count; j++) { ++ if (fwrite(&pcrs.pcr_values[j], sizeof(TPML_DIGEST), 1, pcr_output) != 1) { ++ LOG_ERR("write to output file failed: %s", strerror(errno)); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ + static bool write_output_files(TPM2B_ATTEST *quoted, TPMT_SIGNATURE *signature) { + + bool res = true; +@@ -87,6 +134,8 @@ static bool write_output_files(TPM2B_ATTEST *quoted, TPMT_SIGNATURE *signature) + quoted->size); + } + ++ res &= write_pcr_values(); ++ + return res; + } + +@@ -125,7 +174,53 @@ static int quote(TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE akHandle, TPML_PCR_ + PrintBuffer( (UINT8 *)&signature, sizeof(signature) ); + //PrintTPMT_SIGNATURE(&signature); + ++ if (pcr_output) { ++ // Filter out invalid/unavailable PCR selections ++ if (!pcr_check_pcr_selection(&cap_data, &pcrSelections)) { ++ LOG_ERR("Failed to filter unavailable PCR values for quote!"); ++ return false; ++ } ++ ++ // Gather PCR values from the TPM (the quote doesn't have them!) ++ if (!pcr_read_pcr_values(sapi_context, &pcrSelections, &pcrs)) { ++ LOG_ERR("Failed to retrieve PCR values related to quote!"); ++ return false; ++ } ++ ++ // Grab the digest from the quote ++ TPM2B_DIGEST quoteDigest = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer); ++ TPM2B_DATA extraData = TPM2B_TYPE_INIT(TPM2B_DATA, buffer); ++ if (!tpm2_util_get_digest_from_quote("ed, "eDigest, &extraData)) { ++ LOG_ERR("Failed to get digest from quote!"); ++ return false; ++ } ++ ++ // Print out PCR values as output ++ if (!pcr_print_pcr_struct(&pcrSelections, &pcrs)) { ++ LOG_ERR("Failed to print PCR values related to quote!"); ++ return false; ++ } ++ ++ // Calculate the digest from our selected PCR values (to ensure correctness) ++ TPM2B_DIGEST pcr_digest = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer); ++ if (!tpm2_openssl_hash_pcr_banks(sig_hash_algorithm, &pcrSelections, &pcrs, &pcr_digest)) { ++ LOG_ERR("Failed to hash PCR values related to quote!"); ++ return false; ++ } ++ tpm2_tool_output("calcDigest: "); ++ tpm2_util_hexdump(pcr_digest.buffer, pcr_digest.size, true); ++ tpm2_tool_output("\n"); ++ ++ // Make sure digest from quote matches calculated PCR digest ++ if (!tpm2_util_verify_digests("eDigest, &pcr_digest)) { ++ LOG_ERR("Error validating calculated PCR composite with quote"); ++ return false; ++ } ++ } ++ ++ // Write everything out + bool res = write_output_files("ed, &signature); ++ + return res == true ? 0 : 1; + } + +@@ -206,6 +301,10 @@ static bool on_option(char key, char *value) { + case 'm': + message_path = optarg; + break; ++ case 'p': ++ pcr_path = optarg; ++ p_flag = 1; ++ break; + case 'f': + sig_format = tpm2_parse_signature_format(optarg); + +@@ -239,11 +338,12 @@ bool tpm2_tool_onstart(tpm2_options **opts) { + { "input-session-handle", required_argument, NULL, 'S' }, + { "signature", required_argument, NULL, 's' }, + { "message", required_argument, NULL, 'm' }, ++ { "pcrs", required_argument, NULL, 'p' }, + { "format", required_argument, NULL, 'f' }, + { "sig-hash-algorithm", required_argument, NULL, 'G' } + }; + +- *opts = tpm2_options_new("k:c:P:l:g:L:S:q:s:m:f:G:", ARRAY_LEN(topts), topts, ++ *opts = tpm2_options_new("k:c:P:l:g:L:S:q:s:m:p:f:G:", ARRAY_LEN(topts), topts, + on_option, NULL, TPM2_OPTIONS_SHOW_USAGE); + + return *opts != NULL; +@@ -270,5 +370,25 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) { + sessionData.hmac.size = 0; + } + ++ if (p_flag) { ++ if (!G_flag) { ++ LOG_ERR("Must specify -G if -p is requested."); ++ return -1; ++ } ++ pcr_output = fopen(pcr_path, "wb+"); ++ if (!pcr_output) { ++ LOG_ERR("Could not open PCR output file \"%s\" error: \"%s\"", ++ pcr_path, strerror(errno)); ++ return 1; ++ } ++ } ++ ++ if (!pcr_get_banks(sapi_context, &cap_data, &algs)) { ++ if (pcr_output) { ++ fclose(pcr_output); ++ } ++ return 1; ++ } ++ + return quote(sapi_context, akHandle, &pcrSelections); + } +-- +2.21.0 + diff --git a/Add-ability-to-run-tpm2_makecredential-without-a-TPM.patch b/Add-ability-to-run-tpm2_makecredential-without-a-TPM.patch new file mode 100644 index 0000000..9929d13 --- /dev/null +++ b/Add-ability-to-run-tpm2_makecredential-without-a-TPM.patch @@ -0,0 +1,1305 @@ +From d74b094191f2e09b29cfad4f03322e0cd64497ab Mon Sep 17 00:00:00 2001 +From: jetwhiz +Date: Tue, 5 Feb 2019 17:24:44 -0500 +Subject: [PATCH] Add ability to run tpm2_makecredential without a TPM + +Used some functions from tpm2_import for off-TPM functionality + - Move needed, overlap code into tpm2_identity_util +Add new global -X/--openssl-backend option for any tools that can operate w/o TPM + +Signed-off-by: jetwhiz +--- + Makefile.am | 6 +- + lib/tpm2_identity_util.c | 480 ++++++++++++++++++++++++ + lib/tpm2_identity_util.h | 141 +++++++ + lib/tpm2_openssl.c | 162 ++++++++ + lib/tpm2_openssl.h | 108 ++++++ + lib/tpm2_options.c | 36 +- + lib/tpm2_options.h | 1 + + man/common/tcti.md | 4 + + man/tpm2_activatecredential.1.md | 2 +- + man/tpm2_certify.1.md | 2 +- + man/tpm2_create.1.md | 2 +- + man/tpm2_encryptdecrypt.1.md | 2 +- + man/tpm2_getpubak.1.md | 2 +- + man/tpm2_makecredential.1.md | 3 +- + test/system/test_tpm2_makecredential.sh | 2 + + tools/tpm2_makecredential.c | 103 ++++- + 16 files changed, 1033 insertions(+), 23 deletions(-) + create mode 100644 lib/tpm2_identity_util.c + create mode 100644 lib/tpm2_identity_util.h + create mode 100644 lib/tpm2_openssl.c + create mode 100644 lib/tpm2_openssl.h + +diff --git a/Makefile.am b/Makefile.am +index ffe22f383e3..2195537ce01 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -119,7 +119,11 @@ lib_libcommon_a_SOURCES = \ + lib/tpm2_errata.c \ + lib/tpm2_errata.h \ + lib/tpm2_header.h \ ++ lib/tpm2_identity_util.c \ ++ lib/tpm2_identity_util.h \ + lib/tpm2_nv_util.h \ ++ lib/tpm2_openssl.c \ ++ lib/tpm2_openssl.h \ + lib/tpm2_password_util.c \ + lib/tpm2_password_util.h \ + lib/tpm2_policy.c \ +@@ -347,4 +351,4 @@ man/man1/%.1 : man/%.1.md $(MARKDOWN_COMMON_DEPS) + -e '/\[object attribute specifiers\]/d' \ + < $< | pandoc -s -t man > $@ + +-CLEANFILES = $(man1_MANS) ++CLEANFILES = $(man1_MANS) +\ No newline at end of file +diff --git a/lib/tpm2_identity_util.c b/lib/tpm2_identity_util.c +new file mode 100644 +index 00000000000..70bf03647eb +--- /dev/null ++++ b/lib/tpm2_identity_util.c +@@ -0,0 +1,480 @@ ++//**********************************************************************; ++// Copyright (c) 2017, Intel Corporation ++// Copyright (c) 2019 Massachusetts Institute of Technology ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// 1. Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++// THE POSSIBILITY OF SUCH DAMAGE. ++//********************************************************************** ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "files.h" ++#include "log.h" ++#include "tpm2_alg_util.h" ++#include "tpm_kdfa.h" ++#include "tpm2_openssl.h" ++#include "tpm2_identity_util.h" ++#include "tpm2_util.h" ++ ++ ++// Identity-related functionality that the TPM normally does, but using OpenSSL ++ ++#if defined(LIBRESSL_VERSION_NUMBER) ++static int RSA_padding_add_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, ++ const unsigned char *from, int flen, const unsigned char *param, int plen, ++ const EVP_MD *md, const EVP_MD *mgf1md) { ++ ++ int ret = 0; ++ int i, emlen = tlen - 1; ++ unsigned char *db, *seed; ++ unsigned char *dbmask, seedmask[EVP_MAX_MD_SIZE]; ++ int mdlen; ++ ++ if (md == NULL) ++ md = EVP_sha1(); ++ if (mgf1md == NULL) ++ mgf1md = md; ++ ++ mdlen = EVP_MD_size(md); ++ ++ if (flen > emlen - 2 * mdlen - 1) { ++ RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, ++ RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE); ++ return 0; ++ } ++ ++ if (emlen < 2 * mdlen + 1) { ++ RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, ++ RSA_R_KEY_SIZE_TOO_SMALL); ++ return 0; ++ } ++ ++ to[0] = 0; ++ seed = to + 1; ++ db = to + mdlen + 1; ++ ++ if (!EVP_Digest((void *)param, plen, db, NULL, md, NULL)) ++ return 0; ++ memset(db + mdlen, 0, emlen - flen - 2 * mdlen - 1); ++ db[emlen - flen - mdlen - 1] = 0x01; ++ memcpy(db + emlen - flen - mdlen, from, (unsigned int)flen); ++ if (RAND_bytes(seed, mdlen) <= 0) ++ return 0; ++ ++ dbmask = OPENSSL_malloc(emlen - mdlen); ++ if (dbmask == NULL) { ++ RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, ERR_R_MALLOC_FAILURE); ++ return 0; ++ } ++ ++ if (PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md) < 0) ++ goto err; ++ for (i = 0; i < emlen - mdlen; i++) ++ db[i] ^= dbmask[i]; ++ ++ if (PKCS1_MGF1(seedmask, mdlen, db, emlen - mdlen, mgf1md) < 0) ++ goto err; ++ for (i = 0; i < mdlen; i++) ++ seed[i] ^= seedmask[i]; ++ ++ ret = 1; ++ ++ err: ++ OPENSSL_free(dbmask); ++ ++ return ret; ++} ++#endif ++ ++static TPM2_KEY_BITS get_pub_asym_key_bits(TPM2B_PUBLIC *public) { ++ ++ TPMU_PUBLIC_PARMS *p = &public->publicArea.parameters; ++ switch(public->publicArea.type) { ++ case TPM2_ALG_ECC: ++ /* fall-thru */ ++ case TPM2_ALG_RSA: ++ return p->asymDetail.symmetric.keyBits.sym; ++ /* no default */ ++ } ++ ++ return 0; ++} ++ ++static bool encrypt_seed_with_tpm2_rsa_public_key(TPM2B_DIGEST *protection_seed, ++ TPM2B_PUBLIC *parent_pub, unsigned char *label, int labelLen, ++ TPM2B_ENCRYPTED_SECRET *encrypted_protection_seed) { ++ bool rval = false; ++ RSA *rsa = NULL; ++ ++ // Public modulus (RSA-only!) ++ TPMI_RSA_KEY_BITS mod_size_bits = parent_pub->publicArea.parameters.rsaDetail.keyBits; ++ UINT16 mod_size = mod_size_bits / 8; ++ TPM2B *pub_key_val = (TPM2B *)&parent_pub->publicArea.unique.rsa; ++ unsigned char *pub_modulus = malloc(mod_size); ++ if (pub_modulus == NULL) { ++ LOG_ERR("Failed to allocate memory to store public key's modulus."); ++ return false; ++ } ++ memcpy(pub_modulus, pub_key_val->buffer, mod_size); ++ ++ TPMI_ALG_HASH parent_name_alg = parent_pub->publicArea.nameAlg; ++ ++ /* ++ * This is the biggest buffer value, so it should always be sufficient. ++ */ ++ unsigned char encoded[TPM2_MAX_DIGEST_BUFFER]; ++ int return_code = RSA_padding_add_PKCS1_OAEP_mgf1(encoded, ++ mod_size, protection_seed->buffer, protection_seed->size, label, labelLen, ++ tpm2_openssl_halg_from_tpmhalg(parent_name_alg), NULL); ++ if (return_code != 1) { ++ LOG_ERR("Failed RSA_padding_add_PKCS1_OAEP_mgf1\n"); ++ goto error; ++ } ++ BIGNUM* bne = BN_new(); ++ if (!bne) { ++ LOG_ERR("BN_new for bne failed\n"); ++ goto error; ++ } ++ return_code = BN_set_word(bne, RSA_F4); ++ if (return_code != 1) { ++ LOG_ERR("BN_set_word failed\n"); ++ BN_free(bne); ++ goto error; ++ } ++ rsa = RSA_new(); ++ if (!rsa) { ++ LOG_ERR("RSA_new failed\n"); ++ BN_free(bne); ++ goto error; ++ } ++ return_code = RSA_generate_key_ex(rsa, mod_size_bits, bne, NULL); ++ BN_free(bne); ++ if (return_code != 1) { ++ LOG_ERR("RSA_generate_key_ex failed\n"); ++ goto error; ++ } ++ BIGNUM *n = BN_bin2bn(pub_modulus, mod_size, NULL); ++ if (n == NULL) { ++ LOG_ERR("BN_bin2bn failed\n"); ++ goto error; ++ } ++ if (!RSA_set0_key(rsa, n, NULL, NULL)) { ++ LOG_ERR("RSA_set0_key failed\n"); ++ BN_free(n); ++ goto error; ++ } ++ // Encrypting ++ encrypted_protection_seed->size = mod_size; ++ return_code = RSA_public_encrypt(mod_size, encoded, ++ encrypted_protection_seed->secret, rsa, RSA_NO_PADDING); ++ if (return_code < 0) { ++ LOG_ERR("Failed RSA_public_encrypt\n"); ++ goto error; ++ } ++ ++ rval = true; ++ ++error: ++ free(pub_modulus); ++ RSA_free(rsa); ++ return rval; ++} ++ ++bool tpm2_identity_util_calc_outer_integrity_hmac_key_and_dupsensitive_enc_key( ++ TPM2B_PUBLIC *parent_pub, ++ TPM2B_NAME *pubname, ++ TPM2B_DIGEST *protection_seed, ++ TPM2B_MAX_BUFFER *protection_hmac_key, ++ TPM2B_MAX_BUFFER *protection_enc_key) { ++ ++ TPM2B null_2b = { .size = 0 }; ++ ++ TPMI_ALG_HASH parent_alg = parent_pub->publicArea.nameAlg; ++ UINT16 parent_hash_size = tpm2_alg_util_get_hash_size(parent_alg); ++ ++ TSS2_RC rval = tpm_kdfa(parent_alg, (TPM2B *)protection_seed, "INTEGRITY", ++ &null_2b, &null_2b, parent_hash_size * 8, protection_hmac_key); ++ if (rval != TPM2_RC_SUCCESS) { ++ return false; ++ } ++ ++ TPM2_KEY_BITS pub_key_bits = get_pub_asym_key_bits(parent_pub); ++ ++ rval = tpm_kdfa(parent_alg, (TPM2B *)protection_seed, "STORAGE", ++ (TPM2B *)pubname, &null_2b, pub_key_bits, ++ protection_enc_key); ++ if (rval != TPM2_RC_SUCCESS) { ++ return false; ++ } ++ ++ return true; ++} ++ ++ ++bool tpm2_identity_util_encrypt_seed_with_public_key(TPM2B_DIGEST *protection_seed, ++ TPM2B_PUBLIC *parent_pub, unsigned char *label, int labelLen, ++ TPM2B_ENCRYPTED_SECRET *encrypted_protection_seed) { ++ bool result = false; ++ TPMI_ALG_PUBLIC alg = parent_pub->publicArea.type; ++ ++ switch (alg) { ++ case TPM2_ALG_RSA: ++ result = encrypt_seed_with_tpm2_rsa_public_key(protection_seed, ++ parent_pub, label, labelLen, encrypted_protection_seed); ++ break; ++ case TPM2_ALG_ECC: ++ LOG_ERR("Algorithm '%s' not supported yet", tpm2_alg_util_algtostr(alg)); ++ result = false; ++ break; ++ default: ++ LOG_ERR("Cannot handle algorithm, got: %s", tpm2_alg_util_algtostr(alg)); ++ return false; ++ } ++ ++ return result; ++} ++ ++static const EVP_CIPHER *tpm_alg_to_ossl(TPMT_SYM_DEF_OBJECT *sym) { ++ ++ switch(sym->algorithm) { ++ case TPM2_ALG_AES: { ++ switch (sym->keyBits.aes) { ++ case 128: ++ return EVP_aes_128_cfb(); ++ case 256: ++ return EVP_aes_256_cfb(); ++ /* no default */ ++ } ++ } ++ /* no default */ ++ } ++ ++ LOG_ERR("Unsupported parent key symmetric parameters"); ++ ++ return NULL; ++} ++ ++static bool aes_encrypt_buffers(TPMT_SYM_DEF_OBJECT *sym, uint8_t *encryption_key, ++ uint8_t *buf1, size_t buf1_len, ++ uint8_t *buf2, size_t buf2_len, ++ TPM2B_MAX_BUFFER *cipher_text) { ++ ++ bool result = false; ++ ++ unsigned offset = 0; ++ size_t total_len = buf1_len + buf2_len; ++ ++ if (total_len > sizeof(cipher_text->buffer)) { ++ LOG_ERR("Plaintext too big, got %zu, expected less then %zu", ++ total_len, sizeof(cipher_text->buffer)); ++ return false; ++ } ++ ++ const EVP_CIPHER *cipher = tpm_alg_to_ossl(sym); ++ if (!cipher) { ++ return false; ++ } ++ ++ const unsigned char iv[512] = { 0 }; ++ ++ if (((unsigned long)EVP_CIPHER_iv_length(cipher)) > sizeof(iv)) { ++ LOG_ERR("IV size is bigger then IV buffer size"); ++ return false; ++ } ++ ++ EVP_CIPHER_CTX *ctx = tpm2_openssl_cipher_new(); ++ ++ int rc = EVP_EncryptInit_ex(ctx, cipher, NULL, encryption_key, iv); ++ if (!rc) { ++ return false; ++ } ++ ++ EVP_CIPHER_CTX_set_padding(ctx, 0); ++ ++ uint8_t *bufs[2] = { ++ buf1, ++ buf2 ++ }; ++ ++ size_t lens[ARRAY_LEN(bufs)] = { ++ buf1_len, ++ buf2_len ++ }; ++ ++ unsigned i; ++ for (i=0; i < ARRAY_LEN(bufs); i++) { ++ ++ uint8_t *b = bufs[i]; ++ size_t l = lens[i]; ++ ++ if (!b) { ++ continue; ++ } ++ ++ int output_len = total_len - offset; ++ ++ rc = EVP_EncryptUpdate(ctx, &cipher_text->buffer[offset], &output_len, b, l); ++ if (!rc) { ++ LOG_ERR("Encrypt failed"); ++ goto out; ++ } ++ ++ offset += l; ++ } ++ ++ int tmp_len = 0; ++ rc = EVP_EncryptFinal_ex(ctx, NULL, &tmp_len); ++ if (!rc) { ++ LOG_ERR("Encrypt failed"); ++ goto out; ++ } ++ ++ cipher_text->size = total_len; ++ ++ result = true; ++ ++out: ++ tpm2_openssl_cipher_free(ctx); ++ ++ return result; ++} ++ ++static void hmac_outer_integrity( ++ TPMI_ALG_HASH parent_name_alg, ++ uint8_t *buffer1, uint16_t buffer1_size, ++ uint8_t *buffer2, uint16_t buffer2_size, uint8_t *hmac_key, ++ TPM2B_DIGEST *outer_integrity_hmac) { ++ ++ uint8_t to_hmac_buffer[TPM2_MAX_DIGEST_BUFFER]; ++ memcpy(to_hmac_buffer, buffer1, buffer1_size); ++ memcpy(to_hmac_buffer + buffer1_size, buffer2, buffer2_size); ++ uint32_t size = 0; ++ ++ UINT16 hash_size = tpm2_alg_util_get_hash_size(parent_name_alg); ++ ++ HMAC(tpm2_openssl_halg_from_tpmhalg(parent_name_alg), hmac_key, hash_size, to_hmac_buffer, ++ buffer1_size + buffer2_size, outer_integrity_hmac->buffer, &size); ++ outer_integrity_hmac->size = size; ++} ++ ++bool tpm2_identity_util_calculate_inner_integrity( ++ TPMI_ALG_HASH name_alg, ++ TPM2B_SENSITIVE *sensitive, ++ TPM2B_NAME *pubname, ++ TPM2B_DATA *enc_sensitive_key, ++ TPMT_SYM_DEF_OBJECT *sym_alg, ++ TPM2B_MAX_BUFFER *encrypted_inner_integrity) { ++ ++ //Marshal sensitive area ++ uint8_t buffer_marshalled_sensitiveArea[TPM2_MAX_DIGEST_BUFFER] = { 0 }; ++ size_t marshalled_sensitive_size = 0; ++ Tss2_MU_TPMT_SENSITIVE_Marshal(&sensitive->sensitiveArea, ++ buffer_marshalled_sensitiveArea + sizeof(uint16_t), TPM2_MAX_DIGEST_BUFFER, ++ &marshalled_sensitive_size); ++ size_t marshalled_sensitive_size_info = 0; ++ Tss2_MU_UINT16_Marshal(marshalled_sensitive_size, buffer_marshalled_sensitiveArea, ++ sizeof(uint16_t), &marshalled_sensitive_size_info); ++ ++ //concatenate NAME ++ memcpy( ++ buffer_marshalled_sensitiveArea + marshalled_sensitive_size ++ + marshalled_sensitive_size_info, ++ pubname->name, ++ pubname->size); ++ ++ //Digest marshalled-sensitive || name ++ uint8_t *marshalled_sensitive_and_name_digest = ++ buffer_marshalled_sensitiveArea + marshalled_sensitive_size ++ + marshalled_sensitive_size_info ++ + pubname->size; ++ size_t digest_size_info = 0; ++ UINT16 hash_size = tpm2_alg_util_get_hash_size(name_alg); ++ Tss2_MU_UINT16_Marshal(hash_size, marshalled_sensitive_and_name_digest, ++ sizeof(uint16_t), &digest_size_info); ++ ++ digester d = tpm2_openssl_halg_to_digester(name_alg); ++ d(buffer_marshalled_sensitiveArea, ++ marshalled_sensitive_size_info + marshalled_sensitive_size ++ + pubname->size, ++ marshalled_sensitive_and_name_digest + digest_size_info); ++ ++ //Inner integrity ++ encrypted_inner_integrity->size = marshalled_sensitive_size_info ++ + marshalled_sensitive_size + pubname->size; ++ ++ return aes_encrypt_buffers( ++ sym_alg, ++ enc_sensitive_key->buffer, ++ marshalled_sensitive_and_name_digest, ++ hash_size + digest_size_info, ++ buffer_marshalled_sensitiveArea, ++ marshalled_sensitive_size_info + marshalled_sensitive_size, ++ encrypted_inner_integrity); ++} ++ ++void tpm2_identity_util_calculate_outer_integrity( ++ TPMI_ALG_HASH parent_name_alg, ++ TPM2B_NAME *pubname, ++ TPM2B_MAX_BUFFER *marshalled_sensitive, ++ TPM2B_MAX_BUFFER *protection_hmac_key, ++ TPM2B_MAX_BUFFER *protection_enc_key, ++ TPMT_SYM_DEF_OBJECT *sym_alg, ++ TPM2B_MAX_BUFFER *encrypted_duplicate_sensitive, ++ TPM2B_DIGEST *outer_hmac) { ++ ++ //Calculate dupSensitive ++ encrypted_duplicate_sensitive->size = ++ marshalled_sensitive->size; ++ ++ aes_encrypt_buffers( ++ sym_alg, ++ protection_enc_key->buffer, ++ marshalled_sensitive->buffer, ++ marshalled_sensitive->size, ++ NULL, 0, ++ encrypted_duplicate_sensitive); ++ //Calculate outerHMAC ++ hmac_outer_integrity( ++ parent_name_alg, ++ encrypted_duplicate_sensitive->buffer, ++ encrypted_duplicate_sensitive->size, ++ pubname->name, ++ pubname->size, ++ protection_hmac_key->buffer, ++ outer_hmac); ++} +diff --git a/lib/tpm2_identity_util.h b/lib/tpm2_identity_util.h +new file mode 100644 +index 00000000000..49f231a3347 +--- /dev/null ++++ b/lib/tpm2_identity_util.h +@@ -0,0 +1,141 @@ ++//**********************************************************************; ++// Copyright (c) 2017, Intel Corporation ++// Copyright (c) 2019 Massachusetts Institute of Technology ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// 1. Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++// THE POSSIBILITY OF SUCH DAMAGE. ++//********************************************************************** ++ ++#ifndef LIB_TPM2_IDENTITY_UTIL_H_ ++#define LIB_TPM2_IDENTITY_UTIL_H_ ++ ++#include ++ ++#include ++#include ++#include ++ ++ ++/** ++ * Generates HMAC integrity and symmetric encryption keys for TPM2 identies. ++ * ++ * @param parent_pub ++ * The public key used for seed generation and protection. ++ * @param pubname ++ * The Name object associated with the parent_pub credential. ++ * @param protection_seed ++ * The symmetric seed value used to generate protection keys. ++ * @param protection_hmac_key ++ * The HMAC integrity key to populate. ++ * @param protection_enc_key ++ * The symmetric encryption key to populate. ++ * @return ++ * True on success, false on failure. ++ */ ++bool tpm2_identity_util_calc_outer_integrity_hmac_key_and_dupsensitive_enc_key( ++ TPM2B_PUBLIC *parent_pub, ++ TPM2B_NAME *pubname, ++ TPM2B_DIGEST *protection_seed, ++ TPM2B_MAX_BUFFER *protection_hmac_key, ++ TPM2B_MAX_BUFFER *protection_enc_key); ++ ++/** ++ * Encrypts seed with parent public key for TPM2 credential protection process. ++ * ++ * @param protection_seed ++ * The identity structure protection seed that is to be encrypted. ++ * @param parent_pub ++ * The public key used for encryption. ++ * @param label ++ * Indicates label for the seed, such as "IDENTITY" or "DUPLICATE". ++ * @param labelLen ++ * Length of label. ++ * @param encrypted_protection_seed ++ * The encrypted protection seed to populate. ++ * @return ++ * True on success, false on failure. ++ */ ++bool tpm2_identity_util_encrypt_seed_with_public_key( ++ TPM2B_DIGEST *protection_seed, ++ TPM2B_PUBLIC *parent_pub, ++ unsigned char *label, ++ int labelLen, ++ TPM2B_ENCRYPTED_SECRET *encrypted_protection_seed); ++ ++/** ++ * Marshalls Credential Value and encrypts it with the symmetric encryption key. ++ * ++ * @param name_alg ++ * Hash algorithm used to compute Name of the public key. ++ * @param sensitive ++ * The Credential Value to be marshalled and encrypted with symmetric key. ++ * @param pubname ++ * The Name object corresponding to the public key. ++ * @param enc_sensitive_key ++ * The symmetric encryption key. ++ * @param sym_alg ++ * The algorithm used for the symmetric encryption key. ++ * @param encrypted_inner_integrity ++ * The encrypted, marshalled Credential Value to populate. ++ * @return ++ * True on success, false on failure. ++ */ ++bool tpm2_identity_util_calculate_inner_integrity( ++ TPMI_ALG_HASH name_alg, ++ TPM2B_SENSITIVE *sensitive, ++ TPM2B_NAME *pubname, ++ TPM2B_DATA *enc_sensitive_key, ++ TPMT_SYM_DEF_OBJECT *sym_alg, ++ TPM2B_MAX_BUFFER *encrypted_inner_integrity); ++ ++/** ++ * Encrypts Credential Value with enc key and calculates HMAC with hmac key. ++ * ++ * @param parent_name_alg ++ * Hash algorithm used to compute Name of the public key. ++ * @param pubname ++ * The Name object corresponding to the public key. ++ * @param marshalled_sensitive ++ * Marshalled Credential Value to be encrypted with symmetric encryption key. ++ * @param protection_hmac_key ++ * The HMAC integrity key. ++ * @param protection_enc_key ++ * The symmetric encryption key. ++ * @param sym_alg ++ * The algorithm used for the symmetric encryption key. ++ * @param encrypted_duplicate_sensitive ++ * The encrypted Credential Value to populate. ++ * @param outer_hmac ++ * The outer HMAC structure to populate. ++ */ ++void tpm2_identity_util_calculate_outer_integrity( ++ TPMI_ALG_HASH parent_name_alg, ++ TPM2B_NAME *pubname, ++ TPM2B_MAX_BUFFER *marshalled_sensitive, ++ TPM2B_MAX_BUFFER *protection_hmac_key, ++ TPM2B_MAX_BUFFER *protection_enc_key, ++ TPMT_SYM_DEF_OBJECT *sym_alg, ++ TPM2B_MAX_BUFFER *encrypted_duplicate_sensitive, ++ TPM2B_DIGEST *outer_hmac); ++ ++#endif /* LIB_TPM2_IDENTITY_UTIL_H_ */ +diff --git a/lib/tpm2_openssl.c b/lib/tpm2_openssl.c +new file mode 100644 +index 00000000000..0bfc95bd1ef +--- /dev/null ++++ b/lib/tpm2_openssl.c +@@ -0,0 +1,162 @@ ++//**********************************************************************; ++// Copyright (c) 2017, 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: ++// ++// 1. Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++// THE POSSIBILITY OF SUCH DAMAGE. ++//********************************************************************** ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "files.h" ++#include "log.h" ++#include "tpm2_alg_util.h" ++#include "tpm2_openssl.h" ++#include "tpm2_util.h" ++ ++ ++const EVP_MD *tpm2_openssl_halg_from_tpmhalg(TPMI_ALG_HASH algorithm) { ++ ++ switch (algorithm) { ++ case TPM2_ALG_SHA1: ++ return EVP_sha1(); ++ case TPM2_ALG_SHA256: ++ return EVP_sha256(); ++ case TPM2_ALG_SHA384: ++ return EVP_sha384(); ++ case TPM2_ALG_SHA512: ++ return EVP_sha512(); ++ default: ++ return NULL; ++ } ++ /* no return, not possible */ ++} ++ ++#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11) ++int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) { ++ ++ if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) { ++ return 0; ++ } ++ ++ if (n != NULL) { ++ BN_free(r->n); ++ r->n = n; ++ } ++ ++ if (e != NULL) { ++ BN_free(r->e); ++ r->e = e; ++ } ++ ++ if (d != NULL) { ++ BN_free(r->d); ++ r->d = d; ++ } ++ ++ return 1; ++} ++#endif ++ ++static inline const char *get_openssl_err(void) { ++ return ERR_error_string(ERR_get_error(), NULL); ++} ++ ++ ++EVP_CIPHER_CTX *tpm2_openssl_cipher_new(void) { ++ EVP_CIPHER_CTX *ctx; ++#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11) ++ ctx = malloc(sizeof(*ctx)); ++#else ++ ctx = EVP_CIPHER_CTX_new(); ++#endif ++ if (!ctx) ++ return NULL; ++ ++#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11) ++ EVP_CIPHER_CTX_init(ctx); ++#endif ++ ++ return ctx; ++} ++ ++void tpm2_openssl_cipher_free(EVP_CIPHER_CTX *ctx) { ++#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11) ++ EVP_CIPHER_CTX_cleanup(ctx); ++ free(ctx); ++#else ++ EVP_CIPHER_CTX_free(ctx); ++#endif ++} ++ ++digester tpm2_openssl_halg_to_digester(TPMI_ALG_HASH halg) { ++ ++ switch(halg) { ++ case TPM2_ALG_SHA1: ++ return SHA1; ++ case TPM2_ALG_SHA256: ++ return SHA256; ++ case TPM2_ALG_SHA384: ++ return SHA384; ++ case TPM2_ALG_SHA512: ++ return SHA512; ++ /* no default */ ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Per man openssl(1), handle the following --passin formats: ++ * pass:password ++ * the actual password is password. Since the password is visible to utilities (like 'ps' under Unix) this form should only be used where security is not ++ * important. ++ * ++ * env:var obtain the password from the environment variable var. Since the environment of other processes is visible on certain platforms (e.g. ps under certain ++ * Unix OSes) this option should be used with caution. ++ * ++ * file:pathname ++ * the first line of pathname is the password. If the same pathname argument is supplied to -passin and -passout arguments then the first line will be used ++ * for the input password and the next line for the output password. pathname need not refer to a regular file: it could for example refer to a device or ++ * named pipe. ++ * ++ * fd:number read the password from the file descriptor number. This can be used to send the data via a pipe for example. ++ * ++ * stdin read the password from standard input. ++ * ++ */ ++ ++typedef bool (*pfn_ossl_pw_handler)(const char *passin, char **pass); +diff --git a/lib/tpm2_openssl.h b/lib/tpm2_openssl.h +new file mode 100644 +index 00000000000..d749cb350ac +--- /dev/null ++++ b/lib/tpm2_openssl.h +@@ -0,0 +1,108 @@ ++//**********************************************************************; ++// Copyright (c) 2017, 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: ++// ++// 1. Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++// THE POSSIBILITY OF SUCH DAMAGE. ++//********************************************************************** ++ ++#ifndef LIB_TPM2_OPENSSL_H_ ++#define LIB_TPM2_OPENSSL_H_ ++ ++#include ++ ++#include ++#include ++#include ++ ++#if (OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) /* OpenSSL 1.1.0 */ ++#define LIB_TPM2_OPENSSL_OPENSSL_PRE11 ++#endif ++ ++ ++#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11) ++int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); ++#endif ++ ++ ++/** ++ * Function prototype for a hashing routine. ++ * ++ * This is a wrapper around OSSL SHA256|384 and etc digesters. ++ * ++ * @param d ++ * The data to digest. ++ * @param n ++ * The length of the data to digest. ++ * @param md ++ * The output message digest. ++ * @return ++ * A pointer to the digest or NULL on error. ++ */ ++typedef unsigned char *(*digester)(const unsigned char *d, size_t n, unsigned char *md); ++ ++/** ++ * Get an openssl message digest from a tpm hashing algorithm. ++ * @param algorithm ++ * The tpm algorithm to get the corresponding openssl version of. ++ * @return ++ * A pointer to a message digester or NULL on failure. ++ */ ++const EVP_MD *tpm2_openssl_halg_from_tpmhalg(TPMI_ALG_HASH algorithm); ++ ++/** ++ * Obtains an OpenSSL EVP_CIPHER_CTX dealing with version ++ * API changes in OSSL. ++ * ++ * @return ++ * An Initialized OpenSSL EVP_CIPHER_CTX. ++ */ ++EVP_CIPHER_CTX *tpm2_openssl_cipher_new(void); ++ ++/** ++ * Free's an EVP_CIPHER_CTX obtained via tpm2_openssl_cipher_new() ++ * dealing with OSSL API version changes. ++ * @param ctx ++ * The EVP_CIPHER_CTX to free. ++ */ ++void tpm2_openssl_cipher_free(EVP_CIPHER_CTX *ctx); ++ ++/** ++ * Returns a function pointer capable of performing the ++ * given digest from a TPMI_HASH_ALG. ++ * ++ * @param halg ++ * The hashing algorithm to use. ++ * @return ++ * NULL on failure or a valid digester on success. ++ */ ++digester tpm2_openssl_halg_to_digester(TPMI_ALG_HASH halg); ++ ++typedef enum tpm2_openssl_load_rc tpm2_openssl_load_rc; ++enum tpm2_openssl_load_rc { ++ lprc_error = 0, /* an error has occurred */ ++ lprc_private = 1 << 0, /* successfully loaded a private portion of object */ ++ lprc_public = 1 << 1, /* successfully loaded a public portion of object */ ++}; ++ ++ ++#endif /* LIB_TPM2_OPENSSL_H_ */ +diff --git a/lib/tpm2_options.c b/lib/tpm2_options.c +index c2962ee95d4..8c3f36cacea 100644 +--- a/lib/tpm2_options.c ++++ b/lib/tpm2_options.c +@@ -268,9 +268,8 @@ static char* parse_socket_tcti(void) { + + static tcti_conf tcti_get_config(const char *optstr) { + +- tcti_conf conf = { +- .name = NULL +- }; ++ /* set up the default configuration */ ++ tcti_conf conf = { 0 }; + + /* no tcti config supplied, get it from env */ + if (!optstr) { +@@ -294,6 +293,11 @@ static tcti_conf tcti_get_config(const char *optstr) { + } + } + } else { ++ /* handle case of TCTI set as "-T none" */ ++ if (!strcmp(optstr, "none")) { ++ return conf; ++ } ++ + parse_env_tcti(optstr, &conf); + } + +@@ -400,7 +404,7 @@ tpm2_option_code tpm2_handle_options (int argc, char **argv, + * grep -rn case\ \'[a-zA-Z]\' | awk '{print $3}' | sed s/\'//g | sed s/\://g | sort | uniq | less + */ + struct option long_options [] = { +- { "tcti", required_argument, NULL, 'T' }, ++ { "tcti", optional_argument, NULL, 'T' }, + { "help", no_argument, NULL, 'h' }, + { "verbose", no_argument, NULL, 'V' }, + { "quiet", no_argument, NULL, 'Q' }, +@@ -492,17 +496,23 @@ tpm2_option_code tpm2_handle_options (int argc, char **argv, + if (!tool_opts || !(tool_opts->flags & TPM2_OPTIONS_NO_SAPI)) { + tcti_conf conf = tcti_get_config(tcti_conf_option); + +- *tcti = tpm2_tcti_ldr_load(conf.name, conf.opts); +- if (!*tcti) { +- LOG_ERR("Could not load tcti, got: \"%s\"", conf.name); +- goto out; +- } ++ /* name can be NULL for optional SAPI tools */ ++ if (conf.name) { ++ *tcti = tpm2_tcti_ldr_load(conf.name, conf.opts); ++ if (!*tcti) { ++ LOG_ERR("Could not load tcti, got: \"%s\"", conf.name); ++ goto out; ++ } + +- if (!flags->enable_errata) { +- flags->enable_errata = !!getenv (TPM2TOOLS_ENV_ENABLE_ERRATA); ++ if (!flags->enable_errata) { ++ flags->enable_errata = !!getenv (TPM2TOOLS_ENV_ENABLE_ERRATA); ++ } ++ free(conf.name); ++ free(conf.opts); ++ } else if (!tool_opts || !(tool_opts->flags & TPM2_OPTIONS_OPTIONAL_SAPI)) { ++ LOG_ERR("Requested no tcti, but tool requires TCTI."); ++ goto out; + } +- free(conf.name); +- free(conf.opts); + } + + rc = tpm2_option_code_continue; +diff --git a/lib/tpm2_options.h b/lib/tpm2_options.h +index 860d9b0deee..e16c5205044 100644 +--- a/lib/tpm2_options.h ++++ b/lib/tpm2_options.h +@@ -105,6 +105,7 @@ typedef bool (*tpm2_arg_handler)(int argc, char **argv); + */ + #define TPM2_OPTIONS_SHOW_USAGE 0x1 + #define TPM2_OPTIONS_NO_SAPI 0x2 ++#define TPM2_OPTIONS_OPTIONAL_SAPI 0x4 + + struct tpm2_options { + struct { +diff --git a/man/common/tcti.md b/man/common/tcti.md +index fd5f1683dfe..0cb06e3c403 100644 +--- a/man/common/tcti.md ++++ b/man/common/tcti.md +@@ -18,6 +18,10 @@ The variables respected depend on how the software was configured. + * socket - Typically used with the old resource manager, or talking directly to + a simulator. + * device - Used when talking directly to a TPM device file. ++ * none - Do not initialize a connection with the TPM. Some tools allow for off-tpm ++ options and thus support not using a TCTI. Tools that do not support it ++ will error when attempted to be used without a TCTI connection. Does not ++ support *ANY* options and *MUST BE* presented as the exact text of "none". + + * _TPM2TOOLS\_DEVICE\_FILE_: + When using the device TCTI, specify the TPM device file. The default is +diff --git a/man/tpm2_activatecredential.1.md b/man/tpm2_activatecredential.1.md +index 25478790baf..b15569ae5cf 100644 +--- a/man/tpm2_activatecredential.1.md ++++ b/man/tpm2_activatecredential.1.md +@@ -56,7 +56,7 @@ These options control the object verification: + ``` + tpm2_activatecredential -H 0x81010002 -k 0x81010001 -P abc123 -e abc123 -f -o + tpm2_activatecredential -c ak.context -C ek.context -P abc123 -e abc123 -f -o +-tpm2_activatecredential -H 0x81010002 -k 0x81010001 -P 123abc -e 1a1b1c -X -f -o ++tpm2_activatecredential -H 0x81010002 -k 0x81010001 -P 123abc -e 1a1b1c -f -o + ``` + + # RETURNS +diff --git a/man/tpm2_certify.1.md b/man/tpm2_certify.1.md +index fba4a8f7c65..f4a78c70c00 100644 +--- a/man/tpm2_certify.1.md ++++ b/man/tpm2_certify.1.md +@@ -71,7 +71,7 @@ These options control the ceritifcation: + ``` + tpm2_certify -H 0x81010002 -k 0x81010001 -P 0x0011 -K 0x00FF -g 0x00B -a -s + tpm2_certify -C obj.context -c key.context -P 0x0011 -K 0x00FF -g 0x00B -a -s +-tpm2_certify -H 0x81010002 -k 0x81010001 -P 0011 -K 00FF -X -g 0x00B -a -s ++tpm2_certify -H 0x81010002 -k 0x81010001 -P 0011 -K 00FF -g 0x00B -a -s + ``` + + # RETURNS +diff --git a/man/tpm2_create.1.md b/man/tpm2_create.1.md +index f6aed4a7756..51d71db2c75 100644 +--- a/man/tpm2_create.1.md ++++ b/man/tpm2_create.1.md +@@ -86,7 +86,7 @@ These options for creating the tpm entity: + ``` + tpm2_create -H 0x81010001 -P abc123 -K def456 -g sha256 -G keyedhash-I data.File + tpm2_create -c parent.context -P abc123 -K def456 -g sha256 -G keyedhash -I data.File +-tpm2_create -H 0x81010001 -P 123abc -K 456def -X -g sha256 -G keyedhash -I data.File ++tpm2_create -H 0x81010001 -P 123abc -K 456def -g sha256 -G keyedhash -I data.File + ``` + + # RETURNS +diff --git a/man/tpm2_encryptdecrypt.1.md b/man/tpm2_encryptdecrypt.1.md +index ea349bec1e8..350737182dd 100644 +--- a/man/tpm2_encryptdecrypt.1.md ++++ b/man/tpm2_encryptdecrypt.1.md +@@ -48,7 +48,7 @@ specified symmetric key. + ``` + tpm2_encryptdecrypt -k 0x81010001 -P abc123 -D NO -I -o + tpm2_encryptdecrypt -c key.context -P abc123 -D NO -I -o +-tpm2_encryptdecrypt -k 0x81010001 -P 123abca -X -D NO -I -o ++tpm2_encryptdecrypt -k 0x81010001 -P 123abca -D NO -I -o + ``` + + # RETURNS +diff --git a/man/tpm2_getpubak.1.md b/man/tpm2_getpubak.1.md +index 22ade8f0b33..1a71b49c110 100644 +--- a/man/tpm2_getpubak.1.md ++++ b/man/tpm2_getpubak.1.md +@@ -80,7 +80,7 @@ loaded-key: + + ``` + tpm2_getpubak -e abc123 -P abc123 -o passwd -E 0x81010001 -k 0x81010002 -f ./ak.pub -n ./ak.name +-tpm2_getpubak -e 1a1b1c -P 123abc -o 1a1b1c -X -E 0x81010001 -k 0x81010002 -f ./ak.pub -n ./ak.name ++tpm2_getpubak -e 1a1b1c -P 123abc -o 1a1b1c -E 0x81010001 -k 0x81010002 -f ./ak.pub -n ./ak.name + ``` + + # RETURNS +diff --git a/man/tpm2_makecredential.1.md b/man/tpm2_makecredential.1.md +index 736ead591b8..1008682fe7f 100644 +--- a/man/tpm2_makecredential.1.md ++++ b/man/tpm2_makecredential.1.md +@@ -14,7 +14,8 @@ TPM. + # DESCRIPTION + + **tpm2_makecredential**(1) - Use a TPM public key to protect a secret that is used +-to encrypt the AK certififcate. ++to encrypt the AK certificate. This can be used without a TPM by using ++the **none** TCTI option. + + # OPTIONS + +diff --git a/test/system/test_tpm2_makecredential.sh b/test/system/test_tpm2_makecredential.sh +index cc920ccae07..84f77b3647b 100755 +--- a/test/system/test_tpm2_makecredential.sh ++++ b/test/system/test_tpm2_makecredential.sh +@@ -72,4 +72,6 @@ Loadkeyname=`cat $output_ak_pub_name | xxd -p -c $file_size` + + tpm2_makecredential -Q -e $output_ek_pub -s $file_input_data -n $Loadkeyname -o $output_mkcredential + ++tpm2_makecredential -T none -Q -e $output_ek_pub -s $file_input_data -n $Loadkeyname -o $output_mkcredential ++ + exit 0 +diff --git a/tools/tpm2_makecredential.c b/tools/tpm2_makecredential.c +index c8f49fe0207..259d39f30f3 100644 +--- a/tools/tpm2_makecredential.c ++++ b/tools/tpm2_makecredential.c +@@ -1,5 +1,6 @@ + //**********************************************************************; + // Copyright (c) 2015-2018, Intel Corporation ++// Copyright (c) 2019 Massachusetts Institute of Technology + // All rights reserved. + // + // Redistribution and use in source and binary forms, with or without +@@ -38,12 +39,15 @@ + + #include + #include ++#include + + #include "files.h" + #include "tpm2_options.h" + #include "log.h" + #include "files.h" +-#include "tpm2_options.h" ++#include "tpm2_alg_util.h" ++#include "tpm2_openssl.h" ++#include "tpm2_identity_util.h" + #include "tpm2_tool.h" + #include "tpm2_util.h" + +@@ -117,6 +121,93 @@ out: + return result; + } + ++static bool make_external_credential_and_save() { ++ ++ /* ++ * Get name_alg from the public key ++ */ ++ TPMI_ALG_HASH name_alg = ctx.public.publicArea.nameAlg; ++ ++ ++ /* ++ * Generate and encrypt seed ++ */ ++ TPM2B_DIGEST seed = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer); ++ seed.size = tpm2_alg_util_get_hash_size(name_alg); ++ RAND_bytes(seed.buffer, seed.size); ++ ++ TPM2B_ENCRYPTED_SECRET encrypted_seed = TPM2B_EMPTY_INIT; ++ unsigned char label[10] = { 'I', 'D', 'E', 'N', 'T', 'I', 'T', 'Y', 0 }; ++ bool res = tpm2_identity_util_encrypt_seed_with_public_key(&seed, ++ &ctx.public, label, 9, ++ &encrypted_seed); ++ if (!res) { ++ LOG_ERR("Failed Seed Encryption\n"); ++ return false; ++ } ++ ++ /* ++ * Perform identity structure calculations (off of the TPM) ++ */ ++ TPM2B_MAX_BUFFER hmac_key; ++ TPM2B_MAX_BUFFER enc_key; ++ tpm2_identity_util_calc_outer_integrity_hmac_key_and_dupsensitive_enc_key( ++ &ctx.public, ++ &ctx.object_name, ++ &seed, ++ &hmac_key, ++ &enc_key); ++ ++ /* ++ * The ctx.credential needs to be marshalled into struct with ++ * both size and contents together (to be encrypted as a block) ++ */ ++ TPM2B_MAX_BUFFER marshalled_inner_integrity = TPM2B_EMPTY_INIT; ++ marshalled_inner_integrity.size = ctx.credential.size + sizeof(ctx.credential.size); ++ UINT16 credSize = ctx.credential.size; ++ if (!tpm2_util_is_big_endian()) { ++ credSize = tpm2_util_endian_swap_16(credSize); ++ } ++ memcpy(marshalled_inner_integrity.buffer, &credSize, sizeof(credSize)); ++ memcpy(&marshalled_inner_integrity.buffer[2], ctx.credential.buffer, ctx.credential.size); ++ ++ /* ++ * Perform inner encryption (encIdentity) and outer HMAC (outerHMAC) ++ */ ++ TPM2B_DIGEST outer_hmac = TPM2B_EMPTY_INIT; ++ TPM2B_MAX_BUFFER encrypted_sensitive = TPM2B_EMPTY_INIT; ++ tpm2_identity_util_calculate_outer_integrity( ++ name_alg, ++ &ctx.object_name, ++ &marshalled_inner_integrity, ++ &hmac_key, ++ &enc_key, ++ &ctx.public.publicArea.parameters.rsaDetail.symmetric, ++ &encrypted_sensitive, ++ &outer_hmac); ++ ++ /* ++ * Package up the info to save ++ * cred_bloc = outer_hmac || encrypted_sensitive ++ * secret = encrypted_seed (with pubEK) ++ */ ++ TPM2B_ID_OBJECT cred_blob = TPM2B_TYPE_INIT(TPM2B_ID_OBJECT, credential); ++ ++ UINT16 outer_hmac_size = outer_hmac.size; ++ if (!tpm2_util_is_big_endian()) { ++ outer_hmac_size = tpm2_util_endian_swap_16(outer_hmac_size); ++ } ++ int offset = 0; ++ memcpy(cred_blob.credential + offset, &outer_hmac_size, sizeof(outer_hmac.size));offset += sizeof(outer_hmac.size); ++ memcpy(cred_blob.credential + offset, outer_hmac.buffer, outer_hmac.size);offset += outer_hmac.size; ++ //NOTE: do NOT include the encrypted_sensitive size, since it is encrypted with the blob! ++ memcpy(cred_blob.credential + offset, encrypted_sensitive.buffer, encrypted_sensitive.size); ++ ++ cred_blob.size = outer_hmac.size + encrypted_sensitive.size + sizeof(outer_hmac.size); ++ ++ return write_cred_and_secret(ctx.out_file_path, &cred_blob, &encrypted_seed); ++} ++ + static bool make_credential_and_save(TSS2_SYS_CONTEXT *sapi_context) + { + TSS2L_SYS_AUTH_RESPONSE sessions_data_out; +@@ -198,7 +289,8 @@ bool tpm2_tool_onstart(tpm2_options **opts) { + }; + + *opts = tpm2_options_new("e:s:n:o:", ARRAY_LEN(topts), topts, +- on_option, NULL, TPM2_OPTIONS_SHOW_USAGE); ++ on_option, NULL, ++ TPM2_OPTIONS_SHOW_USAGE | TPM2_OPTIONS_OPTIONAL_SAPI); + + return *opts != NULL; + } +@@ -212,5 +304,10 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) { + return false; + } + +- return make_credential_and_save(sapi_context) != true; ++ printf("make credential has SAPI CTX: %p", sapi_context); ++ ++ bool result = sapi_context ? make_credential_and_save(sapi_context) : ++ make_external_credential_and_save(); ++ ++ return result != true; + } +-- +2.21.0 + diff --git a/Add-attestation-test-which-ensures-full-attestation-.patch b/Add-attestation-test-which-ensures-full-attestation-.patch new file mode 100644 index 0000000..2424416 --- /dev/null +++ b/Add-attestation-test-which-ensures-full-attestation-.patch @@ -0,0 +1,146 @@ +From 70733e919aaa72aa03cccf6cd453bbe0da752de1 Mon Sep 17 00:00:00 2001 +From: jetwhiz +Date: Tue, 9 Apr 2019 17:57:36 -0400 +Subject: [PATCH] Add attestation test, which ensures full attestation + process works + +Signed-off-by: jetwhiz +--- + test/system/test_attestation.sh | 125 ++++++++++++++++++++++++++++++++ + 1 file changed, 125 insertions(+) + create mode 100755 test/system/test_attestation.sh + +diff --git a/test/system/test_attestation.sh b/test/system/test_attestation.sh +new file mode 100755 +index 00000000000..ea9da13a419 +--- /dev/null ++++ b/test/system/test_attestation.sh +@@ -0,0 +1,125 @@ ++#!/bin/bash ++#;**********************************************************************; ++# ++# Copyright (c) 2019 Massachusetts Institute of Technology. ++# All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions are met: ++# ++# 1. Redistributions of source code must retain the above copyright notice, ++# this list of conditions and the following disclaimer. ++# ++# 2. Redistributions in binary form must reproduce the above copyright notice, ++# this list of conditions and the following disclaimer in the documentation ++# and/or other materials provided with the distribution. ++# ++# 3. Neither the name of 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 HOLDER OR CONTRIBUTORS BE ++# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++# THE POSSIBILITY OF SUCH DAMAGE. ++#;**********************************************************************; ++ ++source test_helpers.sh ++ ++handle_ek=0x81010007 ++handle_ak=0x81010008 ++handle_nv=0x1500018 ++handle_hier=0x40000001 ++ek_alg=rsa ++ak_alg=rsa ++digestAlg=sha256 ++signAlg=rsassa ++ownerpw=ownerpass ++endorsepw=endorsepass ++ekpw=ekpass ++akpw=akpass ++ ++file_input_data=secret.data ++file_input_key=nv.data ++output_ek_pub_pem=ekpub.pem ++output_ek_pub=ek.pub ++output_ak_pub_pem=akpub.pem ++output_ak_pub=ak.pub ++output_ak_priv=ak.priv ++output_ak_pub_name=ak.name ++output_mkcredential=mkcred.out ++output_actcredential=actcred.out ++output_quote=quote.out ++output_quotesig=quotesig.out ++output_quotepcr=quotepcr.out ++ ++cleanup() { ++ rm -f $output_ak_priv \ ++ $file_input_data $file_input_key $output_ek_pub $output_ek_pub_pem $output_ak_pub \ ++ $output_ak_pub_pem $output_ak_pub_name $output_mkcredential \ ++ $output_actcredential $output_quote $output_quotesig $output_quotepcr rand.out ++ ++ tpm2_pcrreset 16 ++ tpm2_evictcontrol -Q -Ao -c $handle_ek 2>/dev/null || true ++ tpm2_evictcontrol -Q -Ao -c $handle_ak 2>/dev/null || true ++ ++ tpm2_nvrelease -Q -x $handle_nv -a $handle_hier -P "$ownerpw" 2>/dev/null || true ++ ++ tpm2_takeownership -c 2>/dev/null || true ++} ++trap cleanup EXIT ++ ++ ++cleanup ++ ++echo "12345678" > $file_input_data ++echo "1234567890123456789012345678901" > $file_input_key ++ ++getrandom() { ++ tpm2_getrandom -Q -o rand.out $1 ++ local file_size=`stat --printf="%s" rand.out` ++ loaded_randomness=`cat rand.out | xxd -p -c $file_size` ++} ++ ++ ++tpm2_takeownership -o "$ownerpw" -e "$endorsepw" ++ ++# Key generation ++tpm2_getpubek -Q -H $handle_ek -g $ek_alg -f $output_ek_pub -P "$ekpw" -o "$ownerpw" -e "$endorsepw" ++tpm2_readpublic -Q -H $handle_ek -o $output_ek_pub_pem -f pem ++tpm2_getpubak -Q -E $handle_ek -k $handle_ak -g $ak_alg -D $digestAlg -s $signAlg -f $output_ak_pub -n $output_ak_pub_name -e "$endorsepw" -P "$akpw" -o "$ownerpw" ++tpm2_readpublic -Q -H $handle_ak -o $output_ak_pub_pem -f pem ++ ++# Validate keys (registrar) ++file_size=`stat --printf="%s" $output_ak_pub_name` ++loaded_key_name=`cat $output_ak_pub_name | xxd -p -c $file_size` ++tpm2_makecredential -Q -T none -e $output_ek_pub -s $file_input_data -n $loaded_key_name -o $output_mkcredential ++tpm2_activatecredential -Q -H $handle_ak -k $handle_ek -f $output_mkcredential -o $output_actcredential -P "$akpw" -e "$endorsepw" ++diff $file_input_data $output_actcredential ++ ++ ++# Quoting ++tpm2_pcrreset -Q 16 ++tpm2_pcrextend -Q 16:sha256=6ea40aa7267bb71251c1de1c3605a3df759b86b22fa9f62aa298d4197cd88a38 ++tpm2_pcrlist -Q ++getrandom 20 ++tpm2_quote -Q -k $handle_ak -L $digestAlg:15,16,22 -q $loaded_randomness -m $output_quote -s $output_quotesig -p $output_quotepcr -G $digestAlg -P "$akpw" ++ ++ ++# Verify quote ++tpm2_checkquote -Q -c $output_ak_pub_pem -m $output_quote -s $output_quotesig -p $output_quotepcr -G $digestAlg -q $loaded_randomness ++ ++ ++# Save U key from verifier ++tpm2_nvdefine -Q -x $handle_nv -a $handle_hier -s 32 -t "ownerread|policywrite|ownerwrite" -I "indexpass" -P "$ownerpw" ++tpm2_nvwrite -Q -x $handle_nv -a $handle_hier -P "$ownerpw" $file_input_key ++tpm2_nvread -Q -x $handle_nv -a $handle_hier -s 32 -P "$ownerpw" ++ ++exit 0 +-- +2.21.0 + diff --git a/Fix-ups-in-tpm2_quote.md.1.patch b/Fix-ups-in-tpm2_quote.md.1.patch new file mode 100644 index 0000000..00ee2e8 --- /dev/null +++ b/Fix-ups-in-tpm2_quote.md.1.patch @@ -0,0 +1,34 @@ +From 6c25de22d981d26d0ae80d99706f86c52f713b06 Mon Sep 17 00:00:00 2001 +From: jetwhiz +Date: Fri, 3 May 2019 13:46:41 -0400 +Subject: [PATCH] Fix-ups in tpm2_quote.md.1 + +Capitalization fixes to follow preferred format + +Signed-off-by: jetwhiz +--- + man/tpm2_quote.1.md | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/man/tpm2_quote.1.md b/man/tpm2_quote.1.md +index 491848201d9..c926f4d045d 100644 +--- a/man/tpm2_quote.1.md ++++ b/man/tpm2_quote.1.md +@@ -41,12 +41,12 @@ + + * **-m**, **--message**: + +- message output file, records the quote message that makes up the data that ++ Message output file, records the quote message that makes up the data that + is signed by the TPM. + + * **-s**, **--signature**: + +- signature output file, records the signature in the format specified via the **-f** ++ Signature output file, records the signature in the format specified via the **-f** + option. + + * **-f**, **--format** +-- +2.21.0 + diff --git a/Update-CHANGELOG.md.patch b/Update-CHANGELOG.md.patch new file mode 100644 index 0000000..ad330a7 --- /dev/null +++ b/Update-CHANGELOG.md.patch @@ -0,0 +1,26 @@ +From edbbe53e225bcc68c6ee0a5a85bec82ae6cdc398 Mon Sep 17 00:00:00 2001 +From: William Roberts +Date: Thu, 2 May 2019 08:44:48 -0700 +Subject: [PATCH] Update CHANGELOG.md + +--- + CHANGELOG.md | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/CHANGELOG.md b/CHANGELOG.md +index 19e831d7e7f..a8e4f39afde 100644 +--- a/CHANGELOG.md ++++ b/CHANGELOG.md +@@ -1,4 +1,9 @@ + ## Changelog ++### 3.2.0 - next ++* tpm2_makecredential: add support for executing tool off-TPM. ++* tpm2_pcrreset: introduce new tool for resetting PCRs. ++* tpm2_quote: Fix AK auth password not being used. ++ + ### 3.1.4 - 2019-03-14 + * Fix various man pages + * tpm2_getmanufec: fix OSSL build warnings +-- +2.21.0 + diff --git a/Wire-up-support-for-ak-auth-password-in-tpm2_quote-t.patch b/Wire-up-support-for-ak-auth-password-in-tpm2_quote-t.patch new file mode 100644 index 0000000..fbb65db --- /dev/null +++ b/Wire-up-support-for-ak-auth-password-in-tpm2_quote-t.patch @@ -0,0 +1,97 @@ +From 993da58a612238bf2dd53a015dfdb2a6c0eb00b9 Mon Sep 17 00:00:00 2001 +From: jetwhiz +Date: Mon, 22 Apr 2019 09:48:56 -0400 +Subject: [PATCH 1/6] Wire up support for ak auth password in tpm2_quote tool + +Add regression test + +Signed-off-by: jetwhiz +--- + test/system/test_tpm2_quote.sh | 9 ++++++++- + tools/tpm2_quote.c | 11 ++++++++--- + 2 files changed, 16 insertions(+), 4 deletions(-) + +diff --git a/test/system/test_tpm2_quote.sh b/test/system/test_tpm2_quote.sh +index d845ea1bdb1..231bed326ec 100755 +--- a/test/system/test_tpm2_quote.sh ++++ b/test/system/test_tpm2_quote.sh +@@ -50,6 +50,7 @@ file_quote_key_ctx=ctx_load_out_"$alg_primary_obj"_"$alg_primary_key"-"$alg_crea + Handle_ak_quote=0x81010016 + Handle_ek_quote=0x81010017 + Handle_ak_quote2=0x81010018 ++Handle_ak_quote3=0x81010019 + + maxdigest=$(tpm2_getcap -c properties-fixed | grep TPM_PT_MAX_DIGEST | sed -r -e 's/.*(0x[0-9a-f]+)/\1/g') + if ! [[ "$maxdigest" =~ ^(0x)*[0-9]+$ ]] ; then +@@ -73,6 +74,7 @@ cleanup() { + tpm2_evictcontrol -Q -Ao -H $Handle_ek_quote 2>/dev/null || true + tpm2_evictcontrol -Q -Ao -H $Handle_ak_quote 2>/dev/null || true + tpm2_evictcontrol -Q -Ao -H $Handle_ak_quote2 2>/dev/null || true ++ tpm2_evictcontrol -Q -Ao -H $Handle_ak_quote3 2>/dev/null || true + } + trap cleanup EXIT + +@@ -104,4 +106,9 @@ tpm2_getpubak -Q -E $Handle_ek_quote -k $Handle_ak_quote2 -f ak.pub2 -n ak.nam + + tpm2_quote -Q -k $Handle_ak_quote -g $alg_quote -l 16,17,18 -q $nonce + +-exit 0 ++#####AK with password ++tpm2_getpubak -Q -E $Handle_ek_quote -k $Handle_ak_quote3 -f ak.pub2 -n ak.name_2 -P abc123 ++ ++tpm2_quote -Q -k $Handle_ak_quote3 -g $alg_quote -l 16,17,18 -q $nonce -P abc123 ++ ++exit 0 +\ No newline at end of file +diff --git a/tools/tpm2_quote.c b/tools/tpm2_quote.c +index 3538947db31..05b6d641656 100644 +--- a/tools/tpm2_quote.c ++++ b/tools/tpm2_quote.c +@@ -50,7 +50,7 @@ typedef struct { + UINT32 id[24]; + } PCR_LIST; + +-static TPMS_AUTH_COMMAND sessionData; ++static TPMS_AUTH_COMMAND sessionData = TPMS_AUTH_COMMAND_INIT(TPM2_RS_PW); + static char *outFilePath; + static char *signature_path; + static char *message_path; +@@ -60,7 +60,7 @@ static TPM2B_DATA qualifyingData = TPM2B_EMPTY_INIT; + static TPML_PCR_SELECTION pcrSelections; + static bool is_auth_session; + static TPMI_SH_AUTH_SESSION auth_session_handle; +-static int k_flag, c_flag, l_flag, g_flag, L_flag, o_flag, G_flag; ++static int k_flag, c_flag, l_flag, g_flag, L_flag, o_flag, G_flag, P_flag; + static char *contextFilePath; + static TPM2_HANDLE akHandle; + +@@ -94,7 +94,7 @@ static int quote(TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE akHandle, TPML_PCR_ + { + UINT32 rval; + TPMT_SIG_SCHEME inScheme; +- TSS2L_SYS_AUTH_COMMAND sessionsData = { 1, {{.sessionHandle=TPM2_RS_PW}}}; ++ TSS2L_SYS_AUTH_COMMAND sessionsData = { 1, { sessionData }}; + TSS2L_SYS_AUTH_RESPONSE sessionsDataOut; + TPM2B_ATTEST quoted = TPM2B_TYPE_INIT(TPM2B_ATTEST, attestationData); + TPMT_SIGNATURE signature; +@@ -152,6 +152,7 @@ static bool on_option(char key, char *value) { + LOG_ERR("Invalid AK password, got\"%s\"", value); + return false; + } ++ P_flag = 1; + } break; + case 'l': + if(!pcr_parse_list(value, strlen(value), &pcrSelections.pcrSelections[0])) +@@ -265,5 +266,9 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) { + } + } + ++ if (P_flag == 0) { ++ sessionData.hmac.size = 0; ++ } ++ + return quote(sapi_context, akHandle, &pcrSelections); + } +-- +2.21.0 + diff --git a/lib-tpm2_util.c-string_to_uint32-ensure-the-string-d.patch b/lib-tpm2_util.c-string_to_uint32-ensure-the-string-d.patch new file mode 100644 index 0000000..ff0162b --- /dev/null +++ b/lib-tpm2_util.c-string_to_uint32-ensure-the-string-d.patch @@ -0,0 +1,47 @@ +From 9685ea263f994537430323fb1681b210395eee7c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=D0=94=D0=B8=D0=BB=D1=8F=D0=BD=20=D0=9F=D0=B0=D0=BB=D0=B0?= + =?UTF-8?q?=D1=83=D0=B7=D0=BE=D0=B2?= +Date: Tue, 2 Apr 2019 16:18:32 +0000 +Subject: [PATCH] lib/tpm2_util.c:string_to_uint32: ensure the string does not + overflow in uint32 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Before this change input of "4294967295" generated output of 4294967295, which +is UINT32_MAX = 2**32 - 1. But input "4294967296" created output of 0. The +function is supposed to fail if the number is too big, rather than silently +convert unsigned long int to uint32_t, ignoring some bits. + +Signed-Off-By: Дилян Палаузов +--- + lib/tpm2_util.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/lib/tpm2_util.c b/lib/tpm2_util.c +index edfda4a8b0b..ca9d8b7f4d7 100644 +--- a/lib/tpm2_util.c ++++ b/lib/tpm2_util.c +@@ -236,8 +236,8 @@ bool tpm2_util_string_to_uint32(const char *str, uint32_t *value) { + + /* clear errno before the call, should be 0 afterwards */ + errno = 0; +- uint32_t tmp = strtoul(str, &endptr, 0); +- if (errno) { ++ unsigned long int tmp = strtoul(str, &endptr, 0); ++ if (errno || tmp > UINT32_MAX) { + return false; + } + +@@ -250,7 +250,7 @@ bool tpm2_util_string_to_uint32(const char *str, uint32_t *value) { + return false; + } + +- *value = tmp; ++ *value = (uint32_t) tmp; + return true; + } + +-- +2.21.0 + diff --git a/tpm2-tools.spec b/tpm2-tools.spec index c061d9e..41bfef4 100644 --- a/tpm2-tools.spec +++ b/tpm2-tools.spec @@ -1,12 +1,21 @@ Name: tpm2-tools Version: 3.1.4 -Release: 1%{?dist} +Release: 2%{?dist} Summary: A TPM2.0 testing tool build upon TPM2.0-TSS License: BSD URL: https://github.com/tpm2-software/tpm2-tools Source0: https://github.com/tpm2-software/tpm2-tools/releases/download/%{version}/%{name}-%{version}.tar.gz +Patch0: Wire-up-support-for-ak-auth-password-in-tpm2_quote-t.patch +Patch1: tpm2_pcrreset-new-tools.patch +Patch2: Add-ability-to-run-tpm2_makecredential-without-a-TPM.patch +Patch3: Update-CHANGELOG.md.patch +Patch4: Add-ability-to-check-quotes-and-output-PCR-values-fo.patch +Patch5: Add-attestation-test-which-ensures-full-attestation-.patch +Patch6: Fix-ups-in-tpm2_quote.md.1.patch +Patch7: lib-tpm2_util.c-string_to_uint32-ensure-the-string-d.patch + BuildRequires: gcc-c++ BuildRequires: libtool BuildRequires: autoconf-archive @@ -45,6 +54,10 @@ tpm2-tools is a batch of testing tools for tpm2.0. It is based on tpm2-tss. %{_mandir}/man1/tpm2_*.1.gz %changelog +* Fri May 10 2019 Javier Martinez Canillas - 3.1.4-2 +- Allow tpm2_makecredential to run without a TPM (jetwhiz) +- Add tpm2_pcrreset and tpm2_checkquote tools (jetwhiz) + * Fri Mar 15 2019 Yunying Sun - 3.1.4-1 - Update to 3.1.4 release - Removed the 4 patches since all have been included in 3.1.4 release diff --git a/tpm2_pcrreset-new-tools.patch b/tpm2_pcrreset-new-tools.patch new file mode 100644 index 0000000..ba6ca0b --- /dev/null +++ b/tpm2_pcrreset-new-tools.patch @@ -0,0 +1,313 @@ +From 016ef077a2e81fab14cbcd5ba6fae10a6681688b Mon Sep 17 00:00:00 2001 +From: jetwhiz +Date: Mon, 1 Oct 2018 17:55:13 -0400 +Subject: [PATCH 2/6] tpm2_pcrreset new tools + +New tool to allow resetting PCR registers, backport from 0ef0f31775 + +Signed-off-by: jetwhiz +--- + Makefile.am | 3 + + man/tpm2_pcrreset.1.md | 58 ++++++++++++++ + test/system/test_tpm2_pcrreset.sh | 59 ++++++++++++++ + tools/tpm2_pcrreset.c | 129 ++++++++++++++++++++++++++++++ + 4 files changed, 249 insertions(+) + create mode 100644 man/tpm2_pcrreset.1.md + create mode 100755 test/system/test_tpm2_pcrreset.sh + create mode 100644 tools/tpm2_pcrreset.c + +diff --git a/Makefile.am b/Makefile.am +index 3856bcb400c..ffe22f383e3 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -87,6 +87,7 @@ bin_PROGRAMS = \ + tools/tpm2_pcrevent \ + tools/tpm2_pcrextend \ + tools/tpm2_pcrlist \ ++ tools/tpm2_pcrreset \ + tools/tpm2_quote \ + tools/tpm2_rc_decode \ + tools/tpm2_readpublic \ +@@ -179,6 +180,7 @@ tools_tpm2_unseal_SOURCES = tools/tpm2_unseal.c $(TOOL_SRC) + tools_tpm2_dictionarylockout_SOURCES = tools/tpm2_dictionarylockout.c $(TOOL_SRC) + tools_tpm2_createpolicy_SOURCES = tools/tpm2_createpolicy.c $(TOOL_SRC) + tools_tpm2_pcrextend_SOURCES = tools/tpm2_pcrextend.c $(TOOL_SRC) ++tools_tpm2_pcrreset_SOURCES = tools/tpm2_pcrreset.c $(TOOL_SRC) + tools_tpm2_pcrevent_SOURCES = tools/tpm2_pcrevent.c $(TOOL_SRC) + tools_tpm2_rc_decode_SOURCES = tools/tpm2_rc_decode.c $(TOOL_SRC) + +@@ -279,6 +281,7 @@ if HAVE_MAN_PAGES + man/man1/tpm2_pcrevent.1 \ + man/man1/tpm2_pcrextend.1 \ + man/man1/tpm2_pcrlist.1 \ ++ man/man1/tpm2_pcrreset.1 \ + man/man1/tpm2_quote.1 \ + man/man1/tpm2_rc_decode.1 \ + man/man1/tpm2_readpublic.1 \ +diff --git a/man/tpm2_pcrreset.1.md b/man/tpm2_pcrreset.1.md +new file mode 100644 +index 00000000000..d5637137796 +--- /dev/null ++++ b/man/tpm2_pcrreset.1.md +@@ -0,0 +1,58 @@ ++% tpm2_pcrreset(1) tpm2-tools | General Commands Manual ++% ++% JANUARY 2019 ++ ++# NAME ++ ++**tpm2_pcrreset**(1) - Reset one or more PCR banks ++ ++# SYNOPSIS ++ ++**tpm2_pcrreset** [*OPTIONS*] _PCR\_INDEX_ ... ++ ++# DESCRIPTION ++ ++**tpm2_pcrreset**(1) - Reset PCR value in all banks for specified index. ++More than one PCR index can be specified. ++ ++The reset value is manufacturer-dependent and is either sequence of 00 or FF ++on the length of the hash algorithm for each supported bank ++ ++_PCR\_INDEX_ is a space separated list of PCR indexes to be reset when issuing ++the command. ++ ++# OPTIONS ++ ++This tool accepts no tool specific options. ++ ++[common options](common/options.md) ++ ++[common tcti options](common/tcti.md) ++ ++# EXAMPLES ++ ++## Reset a single PCR ++``` ++tpm2_pcrreset 23 ++``` ++ ++## Reset multiple PCRs ++``` ++tpm2_pcrreset 16 23 ++``` ++ ++# NOTES ++ ++On operating system's locality (generally locality 0), only PCR 23 can be reset. ++PCR-16 can also be reset on this locality, depending on TPM manufacturers ++which could define this PCR as resettable. ++ ++PCR 0 to 15 are not resettable (being part of SRTM). PCR 16 to 22 are mostly ++reserved for DRTM or dedicated to specific localities and might not ++be resettable depending on current TPM locality. ++ ++# RETURNS ++ ++0 on success or 1 on failure. ++ ++[footer](common/footer.md) +diff --git a/test/system/test_tpm2_pcrreset.sh b/test/system/test_tpm2_pcrreset.sh +new file mode 100755 +index 00000000000..962de780ab4 +--- /dev/null ++++ b/test/system/test_tpm2_pcrreset.sh +@@ -0,0 +1,59 @@ ++#!/bin/bash ++#;**********************************************************************; ++# ++# Copyright (c) 2019, Sebastien LE STUM ++# All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions are met: ++# ++# 1. Redistributions of source code must retain the above copyright notice, ++# this list of conditions and the following disclaimer. ++# ++# 2. Redistributions in binary form must reproduce the above copyright notice, ++# this list of conditions and the following disclaimer in the documentation ++# and/or other materials provided with the distribution. ++# ++# 3. Neither the name of 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 HOLDER OR CONTRIBUTORS BE ++# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++# THE POSSIBILITY OF SUCH DAMAGE. ++#;**********************************************************************; ++ ++source test_helpers.sh ++ ++# Reset a resettable PCR ++tpm2_pcrreset 23 ++ ++# Reset more than one resettable PCR ++tpm2_pcrreset 16 23 ++ ++# Get PCR_Reset out of bound index error ++tpm2_pcrreset 999 2>&1 1>/dev/null | grep -q "out of bound PCR" ++ ++# Get PCR_Reset wrong index error ++tpm2_pcrreset toto 2>&1 1>/dev/null | grep -q "invalid PCR" ++ ++# Get PCR_Reset index out of range error ++if tpm2_pcrreset 29 2>&1 1>/dev/null ; then ++ echo "tpm2_pcrreset on out of range PCR index didn't fail" ++ exit 1 ++else ++ true ++fi ++ ++# Get PCR_Reset bad locality error ++tpm2_pcrreset 0 2>&1 1>/dev/null | grep -q "0x907" ++ ++exit 0 +diff --git a/tools/tpm2_pcrreset.c b/tools/tpm2_pcrreset.c +new file mode 100644 +index 00000000000..5fa1de121e7 +--- /dev/null ++++ b/tools/tpm2_pcrreset.c +@@ -0,0 +1,129 @@ ++//**********************************************************************; ++// Copyright (c) 2017, 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: ++// ++// 1. Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// ++// 3. Neither the name of 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 HOLDER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++// THE POSSIBILITY OF SUCH DAMAGE. ++//**********************************************************************; ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "log.h" ++#include "pcr.h" ++#include "tpm2_options.h" ++#include "tpm2_tool.h" ++#include "tpm2_util.h" ++ ++typedef struct tpm_pcr_reset_ctx tpm_pcr_reset_ctx; ++struct tpm_pcr_reset_ctx { ++ bool pcr_list[TPM2_MAX_PCRS]; ++}; ++ ++static tpm_pcr_reset_ctx ctx; ++ ++static bool pcr_reset_one(TSS2_SYS_CONTEXT *sapi_context, TPMI_DH_PCR pcr_index) { ++ TSS2L_SYS_AUTH_RESPONSE sessions_data_out; ++ TSS2L_SYS_AUTH_COMMAND sessions_data = { 1, {{ .sessionHandle=TPM2_RS_PW }}}; ++ ++ TSS2_RC rval = TSS2_RETRY_EXP(Tss2_Sys_PCR_Reset(sapi_context, pcr_index, &sessions_data, ++ &sessions_data_out)); ++ if (rval != TSS2_RC_SUCCESS) { ++ LOG_ERR("Could not reset PCR index: %d", pcr_index); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool pcr_reset(TSS2_SYS_CONTEXT *sapi_context) { ++ size_t i; ++ ++ for (i = 0; i < TPM2_MAX_PCRS; i++) { ++ if(!ctx.pcr_list[i]) ++ continue; ++ ++ bool result = pcr_reset_one(sapi_context, i); ++ if (!result) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static bool on_arg(int argc, char** argv){ ++ int i; ++ uint32_t pcr; ++ ++ memset(ctx.pcr_list, 0, TPM2_MAX_PCRS); ++ ++ if (argc < 1) { ++ LOG_ERR("Expected at least one PCR index" ++ "ie: , got: 0"); ++ return false; ++ } ++ ++ for(i = 0; i < argc; i++){ ++ if(!tpm2_util_string_to_uint32(argv[i], &pcr)){ ++ LOG_ERR("Got invalid PCR Index: \"%s\"", argv[i]); ++ return false; ++ } ++ ++ /* ++ * If any specified PCR index is greater than the last valid ++ * index supported in the spec, throw an error ++ */ ++ if(pcr > TPM2_MAX_PCRS - 1){ ++ LOG_ERR("Got out of bound PCR Index: \"%s\"", argv[i]); ++ return false; ++ } ++ ++ ctx.pcr_list[pcr] = 1; ++ } ++ ++ return true; ++} ++ ++bool tpm2_tool_onstart(tpm2_options **opts) { ++ ++ *opts = tpm2_options_new(NULL, 0, NULL, NULL, on_arg, 0); ++ ++ return *opts != NULL; ++} ++ ++int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) { ++ ++ UNUSED(flags); ++ ++ return pcr_reset(sapi_context) != true; ++} ++ +-- +2.21.0 +