From: Sansar Choinyambuu Date: Fri, 14 Oct 2011 16:19:49 +0000 (+0200) Subject: Implemented Quote Digest constructing function for IMV X-Git-Tag: 4.6.2~320 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a8be826be43c16d460436b0d589302c5d0af8524;p=thirdparty%2Fstrongswan.git Implemented Quote Digest constructing function for IMV Implemented Signature verification function to check TPM Quote Signature Implemented Handling of Simple Evidence Final attribute Fixed bug within tpm_quote function --- diff --git a/src/libimcv/plugins/imc_attestation/imc_attestation_process.c b/src/libimcv/plugins/imc_attestation/imc_attestation_process.c index ca1dbc3556..f9c6e778fb 100644 --- a/src/libimcv/plugins/imc_attestation/imc_attestation_process.c +++ b/src/libimcv/plugins/imc_attestation/imc_attestation_process.c @@ -321,8 +321,8 @@ bool imc_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, /* TODO: Implement BIOS measurement */ DBG1(DBG_IMC, "experimental implementation:" " Extend TPM with etc/tnc_config file"); - - params.flags = PTS_SIMPLE_COMP_EVID_FLAG_PCR | PTS_SIMPLE_COMP_EVID_FLAG_NO_VALID; + params.pcr_info_included = TRUE; + params.flags = PTS_SIMPLE_COMP_EVID_FLAG_NO_VALID; params.depth = 0; params.vendor_id = PEN_TCG; @@ -335,7 +335,7 @@ bool imc_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, params.extended_pcr = EXTEND_PCR; params.hash_algorithm = pts->get_meas_algorithm(pts); - if (!(params.flags & PTS_SIMPLE_COMP_EVID_FLAG_PCR)) + if (!params.pcr_info_included) { params.transformation = PTS_PCR_TRANSFORM_NO; } @@ -464,11 +464,11 @@ bool imc_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, DESTROY_IF(evidences); return FALSE; } - + /* Send Simple Evidence Final attribute */ flags = PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO; - attr = tcg_pts_attr_simple_evid_final_create(flags, 0, + attr = tcg_pts_attr_simple_evid_final_create(FALSE, flags, 0, pcr_composite, quote_signature, chunk_empty); attr_list->insert_last(attr_list, attr); diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation_process.c b/src/libimcv/plugins/imv_attestation/imv_attestation_process.c index bb5523507c..e119490342 100644 --- a/src/libimcv/plugins/imv_attestation/imv_attestation_process.c +++ b/src/libimcv/plugins/imv_attestation/imv_attestation_process.c @@ -176,6 +176,7 @@ bool imv_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, case TCG_PTS_SIMPLE_COMP_EVID: { tcg_pts_attr_simple_comp_evid_t *attr_cast; + bool pcr_info_inclided; pts_attr_simple_comp_evid_flag_t flags; u_int32_t depth, comp_vendor_id, extended_pcr; u_int8_t family, measurement_type; @@ -188,7 +189,8 @@ bool imv_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, attr_cast = (tcg_pts_attr_simple_comp_evid_t*)attr; attr_info = attr->get_value(attr); - + + pcr_info_inclided = attr_cast->is_pcr_info_included(attr_cast); flags = attr_cast->get_flags(attr_cast); depth = attr_cast->get_sub_component_depth(attr_cast); /* TODO: Implement checking of components with its sub-components */ @@ -241,14 +243,14 @@ bool imv_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, measurement_time = attr_cast->get_measurement_time(attr_cast); /* Call getters of optional fields when corresponding flag is set */ - if (flags & PTS_SIMPLE_COMP_EVID_FLAG_PCR) + if (pcr_info_inclided) { extended_pcr = attr_cast->get_extended_pcr(attr_cast); pcr_before = attr_cast->get_pcr_before_value(attr_cast); pcr_after = attr_cast->get_pcr_after_value(attr_cast); measurement = attr_cast->get_comp_measurement(attr_cast); } - if (!(flags & PTS_SIMPLE_COMP_EVID_FLAG_NO_VALID)) + if (flags != PTS_SIMPLE_COMP_EVID_FLAG_NO_VALID) { policy_uri = attr_cast->get_policy_uri(attr_cast); } @@ -262,27 +264,62 @@ bool imv_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, { tcg_pts_attr_simple_evid_final_t *attr_cast; pts_simple_evid_final_flag_t flags; - chunk_t pcr_comp = chunk_empty; - chunk_t tpm_quote_sign = chunk_empty; - chunk_t evid_sign = chunk_empty; + chunk_t pcr_comp; + chunk_t tpm_quote_sign; + chunk_t evid_sign; + bool evid_signature_included; /** TODO: Ignoring Composite Hash Algorithm field * No flag defined which indicates the precense of it */ attr_cast = (tcg_pts_attr_simple_evid_final_t*)attr; + evid_signature_included = attr_cast->is_evid_sign_included(attr_cast); flags = attr_cast->get_flags(attr_cast); - if ((flags >> 6) & PTS_SIMPLE_EVID_FINAL_FLAG_NO) + if ((flags == PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO2) || + (flags == PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO2_CAP_VER)) { + DBG1(DBG_IMV, "This version of Attestation IMV can not handle" + " TPM Quote Info2 structure"); + break; + } + if (flags == PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO) + { + chunk_t digest; pcr_comp = attr_cast->get_pcr_comp(attr_cast); tpm_quote_sign = attr_cast->get_tpm_quote_sign(attr_cast); - - /** TODO: Construct PCR Composite */ + + if (!pts->get_quote_digest(pts, &digest)) + { + DBG1(DBG_IMV, "unable to contruct TPM Quote Digest"); + chunk_clear(&digest); + return FALSE; + } + if (!pts->verify_quote_signature(pts, digest, tpm_quote_sign)) + { + DBG1(DBG_IMV, "signature verification failed"); + chunk_clear(&digest); + return FALSE; + } + + DBG2(DBG_IMV, "signature verification succeeded for TPM Quote Info"); + + if (!chunk_equals(digest, pcr_comp)) + { + DBG1(DBG_IMV, "calculated TPM Quote Info differs from received"); + DBG1(DBG_IMV, "calculated: %B", &digest); + DBG1(DBG_IMV, "received: %B", &pcr_comp); + chunk_clear(&digest); + } + chunk_clear(&digest); } - if (flags & PTS_SIMPLE_EVID_FINAL_FLAG_EVID) + + if (evid_signature_included) { /** TODO: What to do with Evidence Signature */ evid_sign = attr_cast->get_evid_sign(attr_cast); + DBG1(DBG_IMV, "This version of Attestation IMV can not handle" + " Optional Evidence Signature field"); } break; diff --git a/src/libpts/pts/pts.c b/src/libpts/pts/pts.c index 058cf3da9a..2a1c96cb24 100644 --- a/src/libpts/pts/pts.c +++ b/src/libpts/pts/pts.c @@ -17,6 +17,10 @@ #include #include +#include +#include +/* for isdigit()*/ +#include #include #include @@ -25,10 +29,6 @@ #include #include -#include -#include -#include - #define PTS_BUF_SIZE 4096 typedef struct private_pts_t private_pts_t; @@ -822,10 +822,9 @@ METHOD(pts_t, quote_tpm, bool, BYTE secret[] = TSS_WELL_KNOWN_SECRET; TSS_HPCRS hPcrComposite; TSS_VALIDATION valData; - TPM_QUOTE_INFO *quoteInfo; u_int32_t i; TSS_RESULT result; - chunk_t pcr_composite_without_nonce; + chunk_t pcr_comp, quote_sign; result = Tspi_Context_Create(&hContext); if (result != TSS_SUCCESS) @@ -897,6 +896,7 @@ METHOD(pts_t, quote_tpm, bool, valData.ulExternalDataLength = this->secret.len; valData.rgbExternalData = (BYTE *)this->secret.ptr; + /* TPM Quote */ result = Tspi_TPM_Quote(hTPM, hAIK, hPcrComposite, &valData); if (result != TSS_SUCCESS) @@ -904,8 +904,6 @@ METHOD(pts_t, quote_tpm, bool, goto err4; } - quoteInfo = (TPM_QUOTE_INFO *)valData.rgbData; - /* Display quote info */ DBG3(DBG_PTS, "version:"); for(i = 0 ; i < 4 ; i++) @@ -929,17 +927,22 @@ METHOD(pts_t, quote_tpm, bool, } /* Set output chunks */ - pcr_composite_without_nonce = chunk_alloc( - valData.ulDataLength - ASSESSMENT_SECRET_LEN); - memcpy(pcr_composite_without_nonce.ptr, valData.rgbData, - valData.ulDataLength - ASSESSMENT_SECRET_LEN); - *pcr_composite = pcr_composite_without_nonce; + pcr_comp = chunk_alloc(valData.ulDataLength - ASSESSMENT_SECRET_LEN); + memcpy(pcr_comp.ptr, valData.rgbData, + valData.ulDataLength - ASSESSMENT_SECRET_LEN); + *pcr_composite = pcr_comp; *pcr_composite = chunk_clone(*pcr_composite); - free(pcr_composite_without_nonce.ptr); + DBG3(DBG_PTS, "PCR comp: %B",pcr_composite); - *quote_signature = chunk_from_thing(valData.rgbValidationData); + quote_sign = chunk_alloc(valData.ulValidationDataLength); + memcpy(quote_sign.ptr, valData.rgbValidationData, + valData.ulValidationDataLength); + *quote_signature = quote_sign; *quote_signature = chunk_clone(*quote_signature); - + DBG3(DBG_PTS, "Quote sign: %B",quote_signature); + + chunk_clear(&pcr_comp); + chunk_clear("e_sign); Tspi_Context_FreeMemory(hContext, NULL); Tspi_Context_CloseObject(hContext, hPcrComposite); Tspi_Context_CloseObject(hContext, hAIK); @@ -964,6 +967,254 @@ METHOD(pts_t, quote_tpm, bool, return FALSE; } +/** + * Convert from string to byte array (configured PCR values) + */ +static u_int8_t* pcr_string_to_bytearray(char *str_value) +{ + u_int32_t i; + u_int8_t *ret; + + if (strlen(str_value) != PCR_LEN * 2) + { + DBG1(DBG_PTS, "expected PCR value with %d characters, current:%B", + PCR_LEN * 2, &str_value); + return NULL; + } + + ret = malloc(PCR_LEN); + for (i = 0; i < strlen(str_value)/2; i++) + { + char c1, c2; + u_int8_t d1, d2; + + c1 = str_value[i*2]; + c2 = str_value[i*2 + 1]; + + /** + * Convert characters to u_int8_t + * code taken from http://www.codeguru.com/forum/showthread.php?t=316299 + */ + + if (isdigit(c1)) + { + d1 = c1 - '0'; + } + else if (c1 >= 'A' && c1 <= 'F') + { + d1 = c1 - 'A' + 10; + } + else if (c1 >= 'a' && c1 <= 'f') + { + d1 = c1 - 'a' + 10; + } + + if (isdigit(c2)) + { + d2 = c2 - '0'; + } + else if (c2 >= 'A' && c2 <= 'F') + { + d2 = c2 - 'A' + 10; + } + else if (c2 >= 'a' && c2 <= 'f') + { + d2 = c2 - 'a' + 10; + } + /* save value of two characters in one byte */ + ret[i] = d1*16 + d2; + } + + return ret; +} + +/** + * Build PCR Entries from the configuration + */ +static bool load_pcr_entries(linked_list_t **output) +{ + linked_list_t *entries; + int i, len; + + entries = linked_list_create(); + for(i = 0; i < MAX_NUM_PCR; i++) + { + char *string_pcr_value; + pcr_entry_t *entry; + len = snprintf(NULL, 0, "%s%d", "libimcv.plugins.imv-attestation.pcr", i); + + char var[len + 1]; + len = snprintf(var, len + 1, "%s%d", "libimcv.plugins.imv-attestation.pcr", i); + string_pcr_value = lib->settings->get_str(lib->settings, var, NULL); + + if (string_pcr_value) + { + u_int8_t *pcr_value; + + entry = malloc_thing(pcr_entry_t); + entry->pcr_number = i; + + pcr_value = pcr_string_to_bytearray(string_pcr_value); + entry->pcr_value = chunk_from_thing(pcr_value); + entries->insert_last(entries, entry); + } + } + + if (entries->get_count(entries)) + { + *output = entries; + return TRUE; + } + + DBG1(DBG_PTS, "pcr value(s) not available"); + DESTROY_IF(entries); + *output = NULL; + return FALSE; +} + +/** + * 1. build a TCPA_PCR_COMPOSITE structure which contains (pcrCompositeBuf) + * TCPA_PCR_SELECTION structure (bitmask length network order + length bytes bitmask) + * UINT32 (network order) gives the number of bytes following (pcr entries * 20) + * TCPA_PCRVALUE[] with the pcr values + * + * The first two bytes of the message represent the length + * of the bitmask that follows. The bitmask represents the + * requested PCRs to be quoted. + * + * TPM Main-Part 2 TPM Structures_v1.2 8.1 + * The bitmask is in big endian order" + * + * BYTE 1 BYTE 2 ... + * Bit: 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 ... + * Pcr: 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 ... + * + * 2. SHA1(pcrCompositeBuf) + * + * 3. build a TCPA_QUOTE_INFO structure which contains + * 4 bytes of version + * 4 bytes 'Q' 'U' 'O' 'T' + * 20 byte SHA1 of TCPA_PCR_COMPOSITE + * 20 byte nonce + * + * 4. SHA1(TCPA_QUOTE_INFO) gives quoteDigest + */ +static chunk_t calculate_quote_digest(private_pts_t *this, linked_list_t *pcr_entries) +{ + enumerator_t *e; + pcr_entry_t *pcr_entry; + chunk_t digest, pcr_composite, hash_pcr_composite; + u_int32_t pcr_composite_len; + bio_writer_t *writer; + u_int8_t mask_bytes[MAX_NUM_PCR / 8], i; + hasher_t *hasher; + + pcr_composite_len = 2 + (MAX_NUM_PCR / 8) + 4 + + pcr_entries->get_count(pcr_entries) * PCR_LEN; + + writer = bio_writer_create(pcr_composite_len); + /* Lenght of the bist mask field */ + writer->write_uint16(writer, (MAX_NUM_PCR / 8)); + /* Bit mask indicating selected PCRs */ + e = pcr_entries->create_enumerator(pcr_entries); + while (e->enumerate(e, &pcr_entry)) + { + u_int32_t index = pcr_entry->pcr_number; + mask_bytes[index / 8] |= (1 << (index % 8)); + } + e->destroy(e); + for (i = 0; i< (MAX_NUM_PCR / 8) ; i++) + { + writer->write_uint8(writer, mask_bytes[i]); + } + + /* Lenght of the pcr entries */ + writer->write_uint32(writer, pcr_entries->get_count(pcr_entries) * PCR_LEN); + /* Actual PCR values */ + e = pcr_entries->create_enumerator(pcr_entries); + while (e->enumerate(e, &pcr_entry)) + { + writer->write_data(writer, pcr_entry->pcr_value); + } + + pcr_composite = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); + + writer = bio_writer_create(TPM_QUOTE_INFO_LEN); + /* Version number */ + writer->write_uint8(writer, 1); + writer->write_uint8(writer, 1); + writer->write_uint8(writer, 0); + writer->write_uint8(writer, 0); + + /* Magic QUOT value, depends on TPM Ordinal */ + writer->write_uint8(writer, 'Q'); + writer->write_uint8(writer, 'U'); + writer->write_uint8(writer, 'O'); + writer->write_uint8(writer, 'T'); + + /* SHA1 hash of PCR Composite Structure */ + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + hasher->allocate_hash(hasher, pcr_composite, &hash_pcr_composite); + writer->write_data(writer, hash_pcr_composite); + + /* Secret assessment value 20 bytes (nonce) */ + writer->write_data(writer, this->secret); + + /* TPM Quote Info expected from IMC */ + digest = chunk_clone(writer->get_buf(writer)); + + e->destroy(e); + writer->destroy(writer); + hasher->destroy(hasher); + chunk_clear(&pcr_composite); + chunk_clear(&hash_pcr_composite); + pcr_entries->destroy(pcr_entries); + + return digest; +} + +METHOD(pts_t, get_quote_digest, bool, + private_pts_t *this, chunk_t *digest) +{ + linked_list_t *entries; + + if (!load_pcr_entries(&entries)) + { + DBG1(DBG_PTS, "failed to load PCR entries"); + //DESTROY_IF(entries); + return FALSE; + } + + *digest = calculate_quote_digest(this, entries); + //DESTROY_IF(entries); + return TRUE; +} + +METHOD(pts_t, verify_quote_signature, bool, + private_pts_t *this, chunk_t data, chunk_t signature) +{ + public_key_t *aik_pub_key; + + aik_pub_key = this->aik->get_public_key(this->aik); + + if (!aik_pub_key) + { + DBG1(DBG_PTS, "failed to get public key from AIK certificate"); + return FALSE; + } + + if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1, data, signature)) + { + DBG1(DBG_PTS, "signature verification failed for TPM Quote Info"); + aik_pub_key->destroy(aik_pub_key); + return FALSE; + } + + aik_pub_key->destroy(aik_pub_key); + return TRUE; +} + METHOD(pts_t, destroy, void, private_pts_t *this) { @@ -971,6 +1222,7 @@ METHOD(pts_t, destroy, void, DESTROY_IF(this->dh); free(this->initiator_nonce.ptr); free(this->responder_nonce.ptr); + free(this->secret.ptr); free(this->platform_info); free(this->aik_blob.ptr); free(this->tpm_version_info.ptr); @@ -1157,6 +1409,8 @@ pts_t *pts_create(bool is_imc) .read_pcr = _read_pcr, .extend_pcr = _extend_pcr, .quote_tpm = _quote_tpm, + .get_quote_digest = _get_quote_digest, + .verify_quote_signature = _verify_quote_signature, .destroy = _destroy, }, .is_imc = is_imc, @@ -1184,4 +1438,3 @@ pts_t *pts_create(bool is_imc) return &this->public; } - diff --git a/src/libpts/pts/pts.h b/src/libpts/pts/pts.h index 79288973a1..6cc5b11963 100644 --- a/src/libpts/pts/pts.h +++ b/src/libpts/pts/pts.h @@ -22,6 +22,7 @@ #define PTS_H_ typedef struct pts_t pts_t; +typedef struct pcr_entry_t pcr_entry_t; #include "pts_error.h" #include "pts_proto_caps.h" @@ -54,6 +55,19 @@ typedef struct pts_t pts_t; */ #define PCR_LEN 20 +/** + * Lenght of the TPM_QUOTE_INFO structure, TPM Spec 1.2 + */ +#define TPM_QUOTE_INFO_LEN 48 + +/** + * PCR Entry structure which contains PCR number and current value + */ +struct pcr_entry_t { + u_int32_t pcr_number; + chunk_t pcr_value; +}; + /** * Class implementing the TCG Platform Trust Service (PTS) * @@ -254,6 +268,23 @@ struct pts_t { bool (*quote_tpm)(pts_t *this, u_int32_t *pcrs, u_int32_t num_of_pcrs, chunk_t *pcr_composite, chunk_t *quote_signature); + /** + * Constructs and returns PCR Quote Digest structure expected from IMC + * + * @param digest Output variable to store quote digest + * @return FALSE in case of any error, TRUE otherwise + */ + bool (*get_quote_digest)(pts_t *this, chunk_t *digest); + + /** + * Constructs and returns PCR Quote Digest structure expected from IMC + * + * @param data Calculated TPM Quote Digest + * @param signature TPM Quote Signature received from IMC + * @return FALSE in case signature is not verified, TRUE otherwise + */ + bool (*verify_quote_signature)(pts_t *this, chunk_t data, chunk_t signature); + /** * Destroys a pts_t object. */