Implemented Quote Digest constructing function for IMV
authorSansar Choinyambuu <schoinya@hsr.ch>
Fri, 14 Oct 2011 16:19:49 +0000 (18:19 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 28 Nov 2011 13:39:50 +0000 (14:39 +0100)
Implemented Signature verification function to check TPM Quote Signature
Implemented Handling of Simple Evidence Final attribute
Fixed bug within tpm_quote function

src/libimcv/plugins/imc_attestation/imc_attestation_process.c
src/libimcv/plugins/imv_attestation/imv_attestation_process.c
src/libpts/pts/pts.c
src/libpts/pts/pts.h

index ca1dbc3..f9c6e77 100644 (file)
@@ -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);
                                        
index bb55235..e119490 100644 (file)
@@ -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;
index 058cf3d..2a1c96c 100644 (file)
 
 #include <debug.h>
 #include <crypto/hashers/hasher.h>
+#include <bio/bio_writer.h>
+#include <bio/bio_reader.h>
+/* for isdigit()*/
+#include <ctype.h>
 
 #include <trousers/tss.h>
 #include <trousers/trousers.h>
 #include <sys/utsname.h>
 #include <errno.h>
 
-#include <openssl/asn1t.h>
-#include <openssl/x509.h>
-#include <openssl/rsa.h>
-
 #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(&quote_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;
 }
-
index 7928897..6cc5b11 100644 (file)
@@ -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"
@@ -55,6 +56,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.
         */