Blob Blame History Raw
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