tpm2-tools/Add-ability-to-check-quotes-and-output-PCR-values-fo.patch
Javier Martinez Canillas af88e806f0
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 <javierm@redhat.com>
2019-05-10 15:24:01 +02:00

2008 lines
64 KiB
Diff

From 3f9c713d3bff4abf417edf0f74e0b049bfd1b31f Mon Sep 17 00:00:00 2001
From: jetwhiz <Charles.Munson@ll.mit.edu>
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 <Charles.Munson@ll.mit.edu>
---
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 <string.h>
#include <tss2/tss2_sys.h>
+#include <inttypes.h>
#include <stdbool.h>
#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 <tss2/tss2_sys.h>
+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 <openssl/hmac.h>
#include <openssl/rsa.h>
+#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, &quoted->attestationData[i], 4);i += 4;
+ memcpy(&type, &quoted->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, &quoted->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, &quoted->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, &quoted->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, &quoted->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, &quoted->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, &quoted->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, &quoted->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, &quoted->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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <tss2/tss2_esys.h>
+
+#include <openssl/rsa.h>
+
+#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(&quoted, &quoteDigest, &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(&quoteDigest, &pcr_digest)) {
+ LOG_ERR("Error validating calculated PCR composite with quote");
+ return false;
+ }
+ }
+
+ // Write everything out
bool res = write_output_files(&quoted, &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