core/firmware/firmware_pfm_verify.c (253 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <stddef.h> #include <stdlib.h> #include <string.h> #include "firmware_pfm_verify.h" #include "common/buffer_util.h" #include "common/unused.h" #include "host_fw/host_fw_util.h" #include "manifest/manifest_logging.h" #include "manifest/manifest_manager.h" #include "manifest/manifest_pcr.h" /** * Update the measurements associated with the verification handler. * * @param fw_verify The handler whose measurements should be updated. * * @return 0 if all measurements were updated successfully or an error code. */ static int firmware_pfm_verify_update_measurements (const struct firmware_pfm_verify *fw_verify) { const struct manifest *active; int status; /* If the verification result is successful, measure the PFM. If not, report no PFM and clear * any data in the version buffer. */ if (fw_verify->state->result == 0) { active = &fw_verify->pfm->base; } else { active = NULL; memset (fw_verify->version, 0, fw_verify->max_version); } status = pcr_store_update_versioned_buffer (fw_verify->pcr, fw_verify->hash, fw_verify->measurement_result, (uint8_t*) &fw_verify->state->result, sizeof (fw_verify->state->result), true, 0); if (status != 0) { debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_MANIFEST, MANIFEST_LOGGING_RECORD_MEASUREMENT_FAIL, fw_verify->measurement_result, status); return status; } status = pcr_store_update_versioned_buffer (fw_verify->pcr, fw_verify->hash, fw_verify->measurement_version, (uint8_t*) fw_verify->version, strlen (fw_verify->version) + 1, true, 0); if (status != 0) { debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_MANIFEST, MANIFEST_LOGGING_RECORD_MEASUREMENT_FAIL, fw_verify->measurement_version, status); return status; } return manifest_pcr_measure_manifest (active, fw_verify->hash, fw_verify->pcr, fw_verify->measurement_pfm, fw_verify->measurement_pfm_id, fw_verify->measurement_platform_id); } /** * Initialize a handler for device firmware that leverages a PFM for providing verification and * measurement of the image. * * @param fw_verify The verification handler to initialize. * @param state Variable context for firmware verification. This must be uninitialized. * @param flash Flash device that contains the firmware image that will be verified. * @param pfm The PFM to use for firmware verification and measurement. * @param hash Hash engine to use for firmware image verification. * @param sig_verify Engine to use for PFM verification. * @param pcr Manager for PCRs to use for storing verification measurements. * @param version_buffer Buffer that will be used to store the firmware version string from the PFM. * @param max_version_length Length of the firmware version buffer, including the NULL terminator. * @param measurement_result PCR measurement index to update with the result of PFM verification. * @param measurement_version PCR measurement index to update with the firmware version string from * the PFM. * @param measurement_pfm PCR measurement index to update with the PFM digest. * @param measurement_pfm_id PCR measurement index to update with the PFM ID. * @param measurement_platform_id PCR measurement index to update with the PFM platform ID. * * @return 0 if the handler was initialized successfully or an error code. */ int firmware_pfm_verify_init (struct firmware_pfm_verify *fw_verify, struct firmware_pfm_verify_state *state, const struct flash *flash, const struct pfm *pfm, const struct hash_engine *hash, const struct signature_verification *sig_verify, struct pcr_store *pcr, char *version_buffer, size_t max_version_length, uint16_t measurement_result, uint16_t measurement_version, uint16_t measurement_pfm, uint16_t measurement_pfm_id, uint16_t measurement_platform_id) { if (fw_verify == NULL) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } memset (fw_verify, 0, sizeof (*fw_verify)); fw_verify->state = state; fw_verify->flash = flash; fw_verify->pfm = pfm; fw_verify->hash = hash; fw_verify->sig_verify = sig_verify; fw_verify->pcr = pcr; fw_verify->version = version_buffer; fw_verify->max_version = max_version_length; fw_verify->measurement_result = measurement_result; fw_verify->measurement_version = measurement_version; fw_verify->measurement_pfm = measurement_pfm; fw_verify->measurement_pfm_id = measurement_pfm_id; fw_verify->measurement_platform_id = measurement_platform_id; return firmware_pfm_verify_init_state (fw_verify); } /** * Initialize only the variable state for the firmware verification handler. The rest of the * instance is assumed to have already been initialized. * * This would generally be used with a statically initialized instance. * * @param fw_verify The firmware verification instance that contains the state to initialize. * * @return 0 if the state was successfully initialized or an error code. */ int firmware_pfm_verify_init_state (const struct firmware_pfm_verify *fw_verify) { if ((fw_verify == NULL) || (fw_verify->state == NULL) || (fw_verify->flash == NULL) || (fw_verify->pfm == NULL) || (fw_verify->hash == NULL) || (fw_verify->sig_verify == NULL) || (fw_verify->pcr == NULL) || (fw_verify->version == NULL) || (fw_verify->max_version == 0)) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } fw_verify->state->result = FIRMWARE_PFM_VERIFY_NOT_VERIFIED; return firmware_pfm_verify_update_measurements (fw_verify); } /** * Release the resources used for firmware verification with a PFM. * * @param fw_verify The verification handler to release. */ void firmware_pfm_verify_release (const struct firmware_pfm_verify *fw_verify) { UNUSED (fw_verify); } /** * Run verification of the firmware image using the PFM included with the image. Measurements will * be updated whether verification is successful or not. * * @param fw_verify The handler to use for image verification. * * @return 0 if verification was completed successfully or an error code. Failure to update PCRs * will generate log messages but will not report a verification failure. */ int firmware_pfm_verify_run_verification (const struct firmware_pfm_verify *fw_verify) { struct pfm_firmware fw_list; struct pfm_firmware_versions version_list; struct pfm_image_list img_list; int status; if (fw_verify == NULL) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } /* Verify that the PFM is authentic and constructed correctly. Meaning, it's not empty and has * only one firmware component. */ status = fw_verify->pfm->base.verify (&fw_verify->pfm->base, fw_verify->hash, fw_verify->sig_verify, NULL, 0); if (status != 0) { goto measure; } status = fw_verify->pfm->base.is_empty (&fw_verify->pfm->base); if (status != 0) { if (status == 1) { status = FIRMWARE_PFM_VERIFY_EMPTY_PFM; } goto measure; } status = fw_verify->pfm->get_firmware (fw_verify->pfm, &fw_list); if (status == 0) { if (fw_list.count > 1) { status = FIRMWARE_PFM_VERIFY_PFM_MULTI_FW; } fw_verify->pfm->free_firmware (fw_verify->pfm, &fw_list); } if (status != 0) { goto measure; } /* Get the firmware image details for the verification. There can only be one version listed * in the PFM. */ status = fw_verify->pfm->get_supported_versions (fw_verify->pfm, NULL, &version_list); if (status == 0) { if (version_list.count == 0) { status = FIRMWARE_PFM_VERIFY_PFM_NO_VERSION; } else if (version_list.count > 1) { status = FIRMWARE_PFM_VERIFY_PFM_MULTI_VERSION; } if (status != 0) { goto free_versions; } } else { goto measure; } status = fw_verify->pfm->get_firmware_images (fw_verify->pfm, NULL, version_list.versions[0].fw_version_id, &img_list); if (status != 0) { goto free_versions; } /* Verify the flash contents based on the image details from the PFM. */ if (img_list.count == 0) { status = FIRMWARE_PFM_VERIFY_PFM_NO_IMAGE; } if (status == 0) { status = host_fw_verify_images (fw_verify->flash, &img_list, fw_verify->hash, NULL); } fw_verify->pfm->free_firmware_images (fw_verify->pfm, &img_list); free_versions: if (status == 0) { /* Copy only the amount of the version string that fits in the provided buffer. This may * mean that the measured value doesn't completely match the one used for verification, but * it's better than failing verification just because of a measurement issue. */ strncpy (fw_verify->version, version_list.versions[0].fw_version_id, fw_verify->max_version); fw_verify->version[fw_verify->max_version - 1] = '\0'; } fw_verify->pfm->free_fw_versions (fw_verify->pfm, &version_list); measure: fw_verify->state->result = status; firmware_pfm_verify_update_measurements (fw_verify); return status; } /** * Get the firmware version string that was measured during the last verification. If there has not * been any successful verification, this will be an empty string. * * @param fw_verify The verification handler to query. * @param offset The offset to read data from. * @param buffer The output buffer to be filled with measured data. * @param length Maximum length of the buffer. * @param total_len Output buffer with total length of version string measurement. This will always * contain the total length of the data, even if it's only partially returned. * * @return Length of the measured data if successfully retrieved or an error code. */ int firmware_pfm_verify_get_fw_version_measured_data (const struct firmware_pfm_verify *fw_verify, size_t offset, uint8_t *buffer, size_t length, uint32_t *total_len) { if ((fw_verify == NULL) || (buffer == NULL) || (total_len == NULL)) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } *total_len = strlen (fw_verify->version) + 1; return buffer_copy ((uint8_t*) fw_verify->version, *total_len, &offset, &length, buffer); } /** * Update a hash context with the data used for the firmware version string measurement. * * @param fw_verify The verification handler to query. * @param hash Hash engine to update. * * @return 0 if the hash was updated successfully or an error code. */ int firmware_pfm_verify_hash_fw_version_measured_data (const struct firmware_pfm_verify *fw_verify, const struct hash_engine *hash) { if ((fw_verify == NULL) || (hash == NULL)) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } return hash->update (hash, (uint8_t*) fw_verify->version, strlen (fw_verify->version) + 1); } /** * Get the digest of the PFM that was measured with the last verification. If there has not been * any successful verification, this will be all zeros. * * @param fw_verify The verification handler to query. * @param offset The offset to read data from. * @param buffer The output buffer to be filled with measured data. * @param length Maximum length of the buffer. * @param total_len Output buffer with total length of PFM digest. This will always contain the * total length of the data even, if it's only partially returned. * * @return Length of the measured data if successfully retrieved or an error code. */ int firmware_pfm_verify_get_pfm_digest_measured_data (const struct firmware_pfm_verify *fw_verify, size_t offset, uint8_t *buffer, size_t length, uint32_t *total_len) { if (fw_verify == NULL) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } if (fw_verify->state->result == 0) { return manifest_manager_get_manifest_digest_measured_data (&fw_verify->pfm->base, fw_verify->hash, offset, buffer, length, total_len); } else { return manifest_manager_get_manifest_digest_measured_data (NULL, fw_verify->hash, offset, buffer, length, total_len); } } /** * Update a hash context with the digest of the PFM that was last measured. * * NOTE: This cannot be passed the same hash engine instance that has been assigned to the * verification handler. * * @param fw_verify The verification handler to query. * @param hash Hash engine to update. This must be different than the hash engine used by the * verification handler. * * @return 0 if the hash was updated successfully or an error code. */ int firmware_pfm_verify_hash_pfm_digest_measured_data (const struct firmware_pfm_verify *fw_verify, const struct hash_engine *hash) { if (fw_verify == NULL) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } if (fw_verify->state->result == 0) { return manifest_manager_hash_manifest_digest_measured_data (&fw_verify->pfm->base, fw_verify->hash, hash); } else { return manifest_manager_hash_manifest_digest_measured_data (NULL, fw_verify->hash, hash); } } /** * Get the measurement data of the PFM ID that was measured with the last verification. If there * has not been any successful verification, this will be all zeros. * * @param fw_verify The verification handler to query. * @param offset The offset to read data from. * @param buffer The output buffer to be filled with measured data. * @param length Maximum length of the buffer. * @param total_len Output buffer with total length of PFM ID. This will always contain the total * length of the data even, if it's only partially returned. * * @return Length of the measured data if successfully retrieved or an error code. */ int firmware_pfm_verify_get_pfm_id_measured_data (const struct firmware_pfm_verify *fw_verify, size_t offset, uint8_t *buffer, size_t length, uint32_t *total_len) { if (fw_verify == NULL) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } if (fw_verify->state->result == 0) { return manifest_manager_get_id_measured_data (&fw_verify->pfm->base, offset, buffer, length, total_len); } else { return manifest_manager_get_id_measured_data (NULL, offset, buffer, length, total_len); } } /** * Update a hash context with the ID of the PFM that was last measured. * * @param fw_verify The verification handler to query. * @param hash Hash engine to update. * * @return 0 if the hash was updated successfully or an error code. */ int firmware_pfm_verify_hash_pfm_id_measured_data (const struct firmware_pfm_verify *fw_verify, const struct hash_engine *hash) { if (fw_verify == NULL) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } if (fw_verify->state->result == 0) { return manifest_manager_hash_id_measured_data (&fw_verify->pfm->base, hash); } else { return manifest_manager_hash_id_measured_data (NULL, hash); } } /** * Get the PFM platform ID that was measured with the last verification. If there has not been any * successful verification, this will be an empty string. * * @param fw_verify The verification handler to query. * @param offset The offset to read data from. * @param buffer The output buffer to be filled with measured data. * @param length Maximum length of the buffer. * @param total_len Output buffer with total length of PFM platform ID. This will always contain the * total length of the data even, if it's only partially returned. * * @return Length of the measured data if successfully retrieved or an error code. */ int firmware_pfm_verify_get_pfm_platform_id_measured_data ( const struct firmware_pfm_verify *fw_verify, size_t offset, uint8_t *buffer, size_t length, uint32_t *total_len) { if (fw_verify == NULL) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } if (fw_verify->state->result == 0) { return manifest_manager_get_platform_id_measured_data (&fw_verify->pfm->base, offset, buffer, length, total_len); } else { return manifest_manager_get_platform_id_measured_data (NULL, offset, buffer, length, total_len); } } /** * Update a hash context with the platform ID of the PFM that was last measured. * * @param fw_verify The verification handler to query. * @param hash Hash engine to update. * * @return 0 if the hash was updated successfully or an error code. */ int firmware_pfm_verify_hash_pfm_platform_id_measured_data ( const struct firmware_pfm_verify *fw_verify, const struct hash_engine *hash) { if (fw_verify == NULL) { return FIRMWARE_PFM_VERIFY_INVALID_ARGUMENT; } if (fw_verify->state->result == 0) { return manifest_manager_hash_platform_id_measured_data (&fw_verify->pfm->base, hash); } else { return manifest_manager_hash_platform_id_measured_data (NULL, hash); } }