/* * Copyright (C) 2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #ifndef TPM_TAG_QUOTE_INFO2 #define TPM_TAG_QUOTE_INFO2 0x0036 #endif #ifndef TPM_LOC_ZERO #define TPM_LOC_ZERO 0x01 #endif typedef struct private_tpm_tss_quote_info_t private_tpm_tss_quote_info_t; /** * Private data of an tpm_tss_quote_info_t object. */ struct private_tpm_tss_quote_info_t { /** * Public tpm_tss_quote_info_t interface. */ tpm_tss_quote_info_t public; /** * TPM Quote Mode */ tpm_quote_mode_t quote_mode; /** * TPM Qualified Signer */ chunk_t qualified_signer; /** * TPM Clock Info */ chunk_t clock_info; /** * TPM Version Info */ chunk_t version_info; /** * TPM PCR Selection */ chunk_t pcr_select; /** * TPM PCR Composite Hash */ chunk_t pcr_digest; /** * TPM PCR Composite Hash algorithm */ hash_algorithm_t pcr_digest_alg; /** * Reference count */ refcount_t ref; }; METHOD(tpm_tss_quote_info_t, get_quote_mode, tpm_quote_mode_t, private_tpm_tss_quote_info_t *this) { return this->quote_mode; } METHOD(tpm_tss_quote_info_t, get_pcr_digest_alg, hash_algorithm_t, private_tpm_tss_quote_info_t *this) { return this->pcr_digest_alg; } METHOD(tpm_tss_quote_info_t, get_pcr_digest, chunk_t, private_tpm_tss_quote_info_t *this) { return this->pcr_digest; } METHOD(tpm_tss_quote_info_t, get_quote, bool, private_tpm_tss_quote_info_t *this, chunk_t nonce, tpm_tss_pcr_composite_t *composite, chunk_t *quoted) { chunk_t pcr_composite, pcr_digest; bio_writer_t *writer; hasher_t *hasher; bool equal_digests; /* Construct PCR Composite */ writer = bio_writer_create(32); switch (this->quote_mode) { case TPM_QUOTE: case TPM_QUOTE2: case TPM_QUOTE2_VERSION_INFO: writer->write_data16(writer, composite->pcr_select); writer->write_data32(writer, composite->pcr_composite); break; case TPM_QUOTE_TPM2: writer->write_data(writer, composite->pcr_composite); break; case TPM_QUOTE_NONE: break; } pcr_composite = writer->extract_buf(writer); writer->destroy(writer); DBG2(DBG_PTS, "constructed PCR Composite: %B", &pcr_composite); /* Compute PCR Composite Hash */ hasher = lib->crypto->create_hasher(lib->crypto, this->pcr_digest_alg); if (!hasher || !hasher->allocate_hash(hasher, pcr_composite, &pcr_digest)) { DESTROY_IF(hasher); chunk_free(&pcr_composite); return FALSE; } hasher->destroy(hasher); chunk_free(&pcr_composite); DBG2(DBG_PTS, "constructed PCR Composite digest: %B", &pcr_digest); equal_digests = chunk_equals(pcr_digest, this->pcr_digest); /* Construct Quote Info */ writer = bio_writer_create(32); switch (this->quote_mode) { case TPM_QUOTE: /* Version number */ writer->write_data(writer, chunk_from_chars(1, 1, 0, 0)); /* Magic QUOT value */ writer->write_data(writer, chunk_from_str("QUOT")); /* PCR Composite Hash */ writer->write_data(writer, pcr_digest); /* Secret assessment value 20 bytes (nonce) */ writer->write_data(writer, nonce); break; case TPM_QUOTE2: case TPM_QUOTE2_VERSION_INFO: /* TPM Structure Tag */ writer->write_uint16(writer, TPM_TAG_QUOTE_INFO2); /* Magic QUT2 value */ writer->write_data(writer, chunk_from_str("QUT2")); /* Secret assessment value 20 bytes (nonce) */ writer->write_data(writer, nonce); /* PCR selection */ writer->write_data16(writer, composite->pcr_select); /* TPM Locality Selection */ writer->write_uint8(writer, TPM_LOC_ZERO); /* PCR Composite Hash */ writer->write_data(writer, pcr_digest); if (this->quote_mode == TPM_QUOTE2_VERSION_INFO) { /* TPM version Info */ writer->write_data(writer, this->version_info); } break; case TPM_QUOTE_TPM2: /* Magic */ writer->write_data(writer, chunk_from_chars(0xff,0x54,0x43,0x47)); /* Type */ writer->write_uint16(writer, 0x8018); /* Qualified Signer */ writer->write_data16(writer, this->qualified_signer); /* Extra Data */ writer->write_data16(writer, nonce); /* Clock Info */ writer->write_data(writer, this->clock_info); /* Firmware Version */ writer->write_data(writer, this->version_info); /* PCR Selection */ writer->write_data(writer, this->pcr_select); /* PCR Composite Hash */ writer->write_data16(writer, pcr_digest); break; case TPM_QUOTE_NONE: break; } chunk_free(&pcr_digest); *quoted = writer->extract_buf(writer); writer->destroy(writer); DBG2(DBG_PTS, "constructed TPM Quote Info: %B", quoted); if (!equal_digests) { DBG1(DBG_IMV, "received PCR Composite digest does not match " "constructed one"); chunk_free(quoted); } return equal_digests; } METHOD(tpm_tss_quote_info_t, set_version_info, void, private_tpm_tss_quote_info_t *this, chunk_t version_info) { chunk_free(&this->version_info); this->version_info = chunk_clone(version_info); } METHOD(tpm_tss_quote_info_t, get_version_info, chunk_t, private_tpm_tss_quote_info_t *this) { return this->version_info; } METHOD(tpm_tss_quote_info_t, set_tpm2_info, void, private_tpm_tss_quote_info_t *this, chunk_t qualified_signer, chunk_t clock_info, chunk_t pcr_select) { chunk_free(&this->qualified_signer); this->qualified_signer = chunk_clone(qualified_signer); chunk_free(&this->clock_info); this->clock_info = chunk_clone(clock_info); chunk_free(&this->pcr_select); this->pcr_select = chunk_clone(pcr_select); } METHOD(tpm_tss_quote_info_t, get_tpm2_info, void, private_tpm_tss_quote_info_t *this, chunk_t *qualified_signer, chunk_t *clock_info, chunk_t *pcr_select) { if (qualified_signer) { *qualified_signer = this->qualified_signer; } if (clock_info) { *clock_info = this->clock_info; } if (pcr_select) { *pcr_select = this->pcr_select; } } METHOD(tpm_tss_quote_info_t, get_ref, tpm_tss_quote_info_t*, private_tpm_tss_quote_info_t *this) { ref_get(&this->ref); return &this->public; } METHOD(tpm_tss_quote_info_t, destroy, void, private_tpm_tss_quote_info_t *this) { if (ref_put(&this->ref)) { chunk_free(&this->qualified_signer); chunk_free(&this->clock_info); chunk_free(&this->version_info); chunk_free(&this->pcr_select); chunk_free(&this->pcr_digest); free(this); } } /** * See header */ tpm_tss_quote_info_t *tpm_tss_quote_info_create(tpm_quote_mode_t quote_mode, hash_algorithm_t pcr_digest_alg, chunk_t pcr_digest) { private_tpm_tss_quote_info_t *this; INIT(this, .public = { .get_quote_mode = _get_quote_mode, .get_pcr_digest_alg = _get_pcr_digest_alg, .get_pcr_digest = _get_pcr_digest, .get_quote = _get_quote, .set_version_info = _set_version_info, .get_version_info = _get_version_info, .set_tpm2_info = _set_tpm2_info, .get_tpm2_info = _get_tpm2_info, .get_ref = _get_ref, .destroy = _destroy, }, .quote_mode = quote_mode, .pcr_digest_alg = pcr_digest_alg, .pcr_digest = chunk_clone(pcr_digest), .ref = 1, ); return &this->public; }