core/manifest/cfm/cfm_flash.c (1,038 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 "cfm_flash.h"
#include "cfm_format.h"
#include "platform_api.h"
#include "common/buffer_util.h"
#include "common/common_math.h"
#include "common/unused.h"
#include "flash/flash_util.h"
#include "manifest/manifest_flash.h"
int cfm_flash_verify (const struct manifest *cfm, const struct hash_engine *hash,
const struct signature_verification *verification, uint8_t *hash_out, size_t hash_length)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
if (cfm_flash == NULL) {
return CFM_INVALID_ARGUMENT;
}
/* CFM only supports v2 manifests. */
return manifest_flash_v2_verify (&cfm_flash->base_flash, hash, verification, hash_out,
hash_length);
}
int cfm_flash_get_id (const struct manifest *cfm, uint32_t *id)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
if (cfm_flash == NULL) {
return CFM_INVALID_ARGUMENT;
}
return manifest_flash_get_id (&cfm_flash->base_flash, id);
}
int cfm_flash_get_platform_id (const struct manifest *cfm, char **id, size_t length)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
if (cfm_flash == NULL) {
return CFM_INVALID_ARGUMENT;
}
return manifest_flash_get_platform_id (&cfm_flash->base_flash, id, length);
}
void cfm_flash_free_platform_id (const struct manifest *manifest, char *id)
{
UNUSED (manifest);
UNUSED (id);
/* Don't need to do anything. Manifest allocated buffers use the internal static buffer. */
}
int cfm_flash_get_hash (const struct manifest *cfm, const struct hash_engine *hash,
uint8_t *hash_out, size_t hash_length)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
if (cfm_flash == NULL) {
return CFM_INVALID_ARGUMENT;
}
return manifest_flash_get_hash (&cfm_flash->base_flash, hash, hash_out, hash_length);
}
int cfm_flash_get_signature (const struct manifest *cfm, uint8_t *signature, size_t length)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
if (cfm_flash == NULL) {
return CFM_INVALID_ARGUMENT;
}
return manifest_flash_get_signature (&cfm_flash->base_flash, signature, length);
}
int cfm_flash_is_empty (const struct manifest *cfm)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
if (cfm_flash == NULL) {
return CFM_INVALID_ARGUMENT;
}
if (!cfm_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
/* Every CFM must have a platform ID. If that is all we have, then it is an empty manifest. */
return (cfm_flash->base_flash.state->toc_header.entry_count == 1);
}
/**
* Find component device element for the specified component ID.
*
* @param cfm_flash The CFM to query.
* @param component_id The component ID to find.
* @param component Output for the component device data.
* @param entry Optional input for starting entry to use, then output for the entry index
* following the matching component device element if found. This can be null if not needed.
*
* @return 0 if the component device element was found or an error code.
*/
static int cfm_flash_get_component_device_with_starting_entry (const struct cfm_flash *cfm_flash,
uint32_t component_id, struct cfm_component_device_element *component, uint8_t *entry)
{
uint8_t element_entry = 0;
int status;
if ((cfm_flash == NULL) || (component == NULL)) {
return CFM_INVALID_ARGUMENT;
}
if (entry != NULL) {
element_entry = *entry;
}
do {
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_COMPONENT_DEVICE, element_entry, MANIFEST_NO_PARENT, 0,
&element_entry, NULL, NULL, (uint8_t**) &component,
sizeof (struct cfm_component_device_element));
if (ROT_IS_ERROR (status)) {
return status;
}
if (status < (int) (sizeof (struct cfm_component_device_element))) {
return CFM_MALFORMED_COMPONENT_DEVICE_ENTRY;
}
else if (component->transcript_hash_type > MANIFEST_HASH_SHA512) {
return CFM_INVALID_TRANSCRIPT_HASH_TYPE;
}
else if (component->measurement_hash_type > MANIFEST_HASH_SHA512) {
return CFM_INVALID_MEASUREMENT_HASH_TYPE;
}
element_entry++;
} while (component_id != component->component_id);
if (entry != NULL) {
*entry = element_entry;
}
return 0;
}
/**
* Get PMR ID list for the specified entry.
*
* @param cfm_flash The CFM to query.
* @param entry Starting entry to use.
* @param pmr_list List of PMR IDs.
*
* @return The count of PMR IDs found or an error code.
*/
static int cfm_flash_get_pmr_id_list (const struct cfm_flash *cfm_flash, uint8_t entry,
uint8_t **pmr_list)
{
struct cfm_pmr_digest_element pmr_digest_element;
struct cfm_pmr_digest_element *pmr_digest_element_ptr = &pmr_digest_element;
int num_pmr_ids;
int i_pmr_id;
int status;
status = manifest_flash_get_child_elements_info (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, entry, CFM_COMPONENT_DEVICE, MANIFEST_NO_PARENT, CFM_PMR_DIGEST,
NULL, &num_pmr_ids, NULL);
if (status != 0) {
return status;
}
if (num_pmr_ids == 0) {
*pmr_list = NULL;
return 0;
}
*pmr_list = platform_malloc (sizeof (uint8_t) * num_pmr_ids);
if (*pmr_list == NULL) {
return CFM_NO_MEMORY;
}
for (i_pmr_id = 0; i_pmr_id < num_pmr_ids; ++i_pmr_id) {
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_PMR_DIGEST, entry, CFM_COMPONENT_DEVICE, 0, &entry,
NULL, NULL, (uint8_t**) &pmr_digest_element_ptr,
sizeof (struct cfm_pmr_digest_element));
if (ROT_IS_ERROR (status)) {
if (status == MANIFEST_CHILD_NOT_FOUND) {
status = CFM_ENTRY_NOT_FOUND;
}
goto fail;
}
if (status < (int) (sizeof (struct cfm_pmr_digest_element))) {
status = CFM_MALFORMED_PMR_DIGEST_ENTRY;
goto fail;
}
(*pmr_list)[i_pmr_id] = pmr_digest_element.pmr_id;
++entry;
}
return num_pmr_ids;
fail:
platform_free (*pmr_list);
*pmr_list = NULL;
return status;
}
int cfm_flash_get_component_device (const struct cfm *cfm, uint32_t component_id,
struct cfm_component_device *component)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
struct cfm_component_device_element component_element;
uint8_t entry = 0;
int status;
if (component == NULL) {
return CFM_INVALID_ARGUMENT;
}
status = cfm_flash_get_component_device_with_starting_entry (cfm_flash, component_id,
&component_element, &entry);
if (status == 0) {
component->attestation_protocol =
(enum cfm_attestation_type) component_element.attestation_protocol;
component->transcript_hash_type =
manifest_convert_manifest_hash_type (component_element.transcript_hash_type);
component->measurement_hash_type =
manifest_convert_manifest_hash_type (component_element.measurement_hash_type);
component->cert_slot = component_element.cert_slot;
component->component_id = component_element.component_id;
status = cfm_flash_get_pmr_id_list (cfm_flash, entry, (uint8_t**) &component->pmr_id_list);
if (ROT_IS_ERROR (status)) {
return status;
}
component->num_pmr_ids = status;
return 0;
}
return status;
}
void cfm_flash_free_component_device (const struct cfm *cfm, struct cfm_component_device *component)
{
UNUSED (cfm);
if (component != NULL) {
platform_free ((void*) component->pmr_id_list);
}
}
int cfm_flash_buffer_supported_components (const struct cfm *cfm, size_t offset, size_t length,
uint8_t *component_ids)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
struct cfm_component_device_element component;
uint8_t *component_ptr;
size_t i_components = 0;
size_t remaining_len = length;
size_t component_len;
uint8_t entry = 0;
int status;
if ((cfm_flash == NULL) || (component_ids == NULL) || (length == 0)) {
return CFM_INVALID_ARGUMENT;
}
if (!cfm_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
while (i_components < length) {
component_ptr = (uint8_t*) &component;
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_COMPONENT_DEVICE, entry, MANIFEST_NO_PARENT, 0, &entry,
NULL, &component_len, &component_ptr, sizeof (struct cfm_component_device_element));
if (ROT_IS_ERROR (status)) {
if ((status == MANIFEST_ELEMENT_NOT_FOUND) && (i_components != 0)) {
goto done;
}
return status;
}
if (status < (int) (sizeof (struct cfm_component_device_element))) {
return CFM_MALFORMED_COMPONENT_DEVICE_ENTRY;
}
++entry;
i_components += buffer_copy ((uint8_t*) &component.component_id,
sizeof (component.component_id), &offset, &remaining_len, &component_ids[i_components]);
}
done:
return i_components;
}
/**
* Common function used to get next element data for the specified component ID and element type.
*
* @param cfm_flash The CFM to query.
* @param component_id The component ID to find element for. This is unused if entry is not NULL.
* @param buffer A container to be updated with the pointer to requested element data. If the
* buffer is null, a buffer will by dynamically allocated to fit the entire element. This buffer
* must be freed by the caller.
* @param buffer_len Length of the element output buffer incoming if the buffer is not null. Buffer
* will then be updated with length of buffer. If buffer is null, then optional and can be null if
* not needed.
* @param entry Optional input for starting entry to use, then output for the entry index following
* the matching element if found. This can be null if not needed.
* @param element_type Element type to retrieve.
*
* @return 0 if the element was found or an error code.
*/
static int cfm_flash_get_next_element (const struct cfm_flash *cfm_flash, uint32_t component_id,
uint8_t **buffer, size_t *buffer_len, uint8_t *entry, int element_type)
{
struct cfm_component_device_element component;
uint8_t element_entry = 0;
size_t *buffer_len_ptr = buffer_len;
size_t buffer_len_buf;
int num_element;
int status;
if ((cfm_flash == NULL) || (buffer == NULL) || ((*buffer != NULL) && (buffer_len == NULL))) {
return CFM_INVALID_ARGUMENT;
}
if (!cfm_flash->base_flash.state->manifest_valid) {
return MANIFEST_NO_MANIFEST;
}
if (entry != NULL) {
element_entry = *entry;
}
if (element_entry == 0) {
status = cfm_flash_get_component_device_with_starting_entry (cfm_flash, component_id,
&component, &element_entry);
if (status != 0) {
return status;
}
}
if (buffer_len == NULL) {
buffer_len_ptr = &buffer_len_buf;
}
status = manifest_flash_get_child_elements_info (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, element_entry, CFM_COMPONENT_DEVICE, MANIFEST_NO_PARENT,
element_type, NULL, &num_element, NULL);
if (status != 0) {
return status;
}
if (num_element == 0) {
goto not_found;
}
status = manifest_flash_read_element_data (&cfm_flash->base_flash, cfm_flash->base_flash.hash,
element_type, element_entry, CFM_COMPONENT_DEVICE, 0, &element_entry, NULL, buffer_len_ptr,
(uint8_t**) buffer, (buffer_len == NULL) ? 0 : *buffer_len);
if (ROT_IS_ERROR (status)) {
if (status == MANIFEST_CHILD_NOT_FOUND) {
goto not_found;
}
return status;
}
if (entry != NULL) {
*entry = element_entry + 1;
}
return 0;
not_found:
return CFM_ENTRY_NOT_FOUND;
}
int cfm_flash_get_component_pmr (const struct cfm *cfm, uint32_t component_id, uint8_t pmr_id,
struct cfm_pmr *pmr)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
union {
struct cfm_component_device_element component;
struct cfm_pmr_element pmr_element;
} buffer;
struct cfm_component_device_element *component_ptr = &buffer.component;
struct cfm_pmr_element *pmr_element_ptr = &buffer.pmr_element;
size_t pmr_element_len;
uint8_t entry = 0;
enum hash_type hash_type;
int hash_len;
int status;
if ((cfm == NULL) || (pmr == NULL)) {
return CFM_INVALID_ARGUMENT;
}
status = cfm_flash_get_component_device_with_starting_entry (cfm_flash, component_id,
component_ptr, &entry);
if (status != 0) {
return status;
}
hash_type = manifest_convert_manifest_hash_type (component_ptr->measurement_hash_type);
while (1) {
pmr_element_len = sizeof (struct cfm_pmr_element);
status = cfm_flash_get_next_element (cfm_flash, component_id, (uint8_t**) &pmr_element_ptr,
&pmr_element_len, &entry, CFM_PMR);
if (status != 0) {
if (status == CFM_ENTRY_NOT_FOUND) {
return CFM_PMR_NOT_FOUND;
}
return status;
}
if (buffer.pmr_element.pmr_id == pmr_id) {
hash_len = hash_get_hash_length (hash_type);
if (ROT_IS_ERROR (hash_len)) {
return hash_len;
}
pmr->pmr_id = buffer.pmr_element.pmr_id;
pmr->initial_value_len = hash_len;
pmr->hash_type = hash_type;
memcpy ((void*) pmr->initial_value, buffer.pmr_element.initial_value, hash_len);
return 0;
}
}
}
/**
* Common function used to free cfm_digests container.
*
* @param cfm_flash The CFM to query.
* @param digests The CFM digests container with content to free.
*/
static void cfm_flash_free_cfm_digests (const struct cfm_flash *cfm_flash,
struct cfm_digests *digests)
{
UNUSED (cfm_flash);
platform_free ((void*) digests->digests);
digests->digests = NULL;
}
/**
* Free cfm_allowable_digests container.
*
* @param cfm_flash The CFM to query.
* @param digests The CFM allowable digests container with content to free.
*/
static void cfm_flash_free_cfm_allowable_digests (const struct cfm_flash *cfm_flash,
struct cfm_allowable_digests *allowable_digests, size_t allowable_digests_count)
{
uint8_t i;
if (allowable_digests == NULL) {
return;
}
for (i = 0; i < allowable_digests_count; ++i) {
cfm_flash_free_cfm_digests (cfm_flash, &allowable_digests[i].digests);
}
platform_free ((void*) allowable_digests);
}
/**
* Common function used to read a list of digests from CFM entry and offset provided, then
* generating a cfm_digests container with the output.
*
* @param cfm_flash The CFM to query.
* @param digests The cfm_digests container to fill up.
* @param digest_count The number of digests to read.
* @param hash_type The type of digests to read.
* @param element_type The type of element digests list is part of.
* @param entry The entry number of the element being read.
* @param offset The offset into the element being read the digests list is at.
*
* @return 0 if the container was generated successfully or an error code.
*/
static int cfm_flash_populate_digests (const struct cfm_flash *cfm_flash,
struct cfm_digests *digests, size_t digest_count, enum hash_type hash_type,
uint8_t element_type, int entry, uint32_t offset)
{
size_t digests_len;
int hash_len;
int status;
hash_len = hash_get_hash_length (hash_type);
if (ROT_IS_ERROR (hash_len)) {
return hash_len;
}
digests_len = hash_len * digest_count;
digests->digests = platform_malloc (digests_len);
if (digests->digests == NULL) {
return CFM_NO_MEMORY;
}
digests->digest_count = digest_count;
digests->hash_type = hash_type;
status = manifest_flash_read_element_data (&cfm_flash->base_flash, cfm_flash->base_flash.hash,
element_type, entry, CFM_COMPONENT_DEVICE, offset, NULL, NULL, NULL,
(uint8_t**) &digests->digests, digests_len);
if (ROT_IS_ERROR (status)) {
if (status == MANIFEST_CHILD_NOT_FOUND) {
status = CFM_ENTRY_MISSING_DIGESTS;
}
cfm_flash_free_cfm_digests (cfm_flash, digests);
return status;
}
return 0;
}
/**
* Read a list of Allowable Digests from CFM entry and offset provided, then generate a
* cfm_allowable_digests container list with the output.
*
* @param cfm_flash The CFM to query.
* @param allowable_digests The cfm_allowable_digests container list to fill up.
* @param digest_count The number of digests to read.
* @param hash_type The type of digests to read.
* @param entry The entry number of the element being read.
* @param offset The offset into the element being read the digests list is at.
*
* @return 0 if the container list was generated successfully or an error code.
*/
static int cfm_flash_populate_allowable_digests (const struct cfm_flash *cfm_flash,
struct cfm_allowable_digests *allowable_digests, size_t allowable_digest_count,
enum hash_type hash_type, int entry, uint32_t offset)
{
struct cfm_allowable_digest_element allowable_digest;
struct cfm_allowable_digest_element *allowable_digest_ptr = &allowable_digest;
struct cfm_allowable_digests *curr_allowable_digest;
size_t digests_len;
uint8_t i_allowable_digest;
int hash_len;
int status;
hash_len = hash_get_hash_length (hash_type);
if (ROT_IS_ERROR (hash_len)) {
return hash_len;
}
// Read each Allowable Digest and fill in allowable_digests
for (i_allowable_digest = 0; i_allowable_digest < allowable_digest_count;
i_allowable_digest++) {
curr_allowable_digest = &allowable_digests[i_allowable_digest];
// Read Allowable Digest information
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_MEASUREMENT, entry, CFM_COMPONENT_DEVICE, offset, NULL,
NULL, NULL, (uint8_t**) &allowable_digest_ptr,
sizeof (struct cfm_allowable_digest_element));
if (ROT_IS_ERROR (status)) {
if (status == MANIFEST_CHILD_NOT_FOUND) {
status = CFM_ENTRY_MISSING_DIGESTS;
}
return status;
}
if (status < (int) (sizeof (struct cfm_allowable_digest_element))) {
return CFM_MALFORMED_MEASUREMENT_ENTRY;
}
// Get & set fields for current Allowable Digest
curr_allowable_digest->version_set = allowable_digest_ptr->version_set;
curr_allowable_digest->digests.hash_type = hash_type;
curr_allowable_digest->digests.digest_count = allowable_digest_ptr->digest_count;
digests_len = curr_allowable_digest->digests.digest_count * hash_len;
// Advance to start of digests
offset += sizeof (struct cfm_allowable_digest_element);
// Allocate space for digests list
curr_allowable_digest->digests.digests = platform_malloc (digests_len);
if (curr_allowable_digest->digests.digests == NULL) {
return CFM_NO_MEMORY;
}
// Read digests from current Allowable Digest element
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_MEASUREMENT, entry, CFM_COMPONENT_DEVICE, offset, NULL,
NULL, NULL, (uint8_t**) &curr_allowable_digest->digests.digests, digests_len);
if (ROT_IS_ERROR (status)) {
if (status == MANIFEST_CHILD_NOT_FOUND) {
status = CFM_ENTRY_MISSING_DIGESTS;
}
return status;
}
if (status < (int) (digests_len)) {
return CFM_MALFORMED_MEASUREMENT_ENTRY;
}
// Advance to start of cfm_allowable_digest_element
offset += digests_len;
}
return 0;
}
void cfm_flash_free_component_pmr_digest (const struct cfm *cfm, struct cfm_pmr_digest *pmr_digest)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
if (pmr_digest != NULL) {
cfm_flash_free_cfm_digests (cfm_flash, &pmr_digest->digests);
}
}
int cfm_flash_get_component_pmr_digest (const struct cfm *cfm, uint32_t component_id,
uint8_t pmr_id, struct cfm_pmr_digest *pmr_digest)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
union {
struct cfm_component_device_element component;
struct cfm_pmr_digest_element pmr_digest_element;
} buffer;
struct cfm_component_device_element *component_ptr = &buffer.component;
struct cfm_pmr_digest_element *pmr_digest_element_ptr = &buffer.pmr_digest_element;
size_t pmr_digest_element_len;
uint8_t entry = 0;
enum hash_type hash_type;
int status;
if (pmr_digest == NULL) {
return CFM_INVALID_ARGUMENT;
}
status = cfm_flash_get_component_device_with_starting_entry (cfm_flash, component_id,
component_ptr, &entry);
if (status != 0) {
return status;
}
hash_type = manifest_convert_manifest_hash_type (component_ptr->measurement_hash_type);
while (1) {
pmr_digest_element_len = sizeof (struct cfm_pmr_digest_element);
status = cfm_flash_get_next_element (cfm_flash, component_id,
(uint8_t**) &pmr_digest_element_ptr, &pmr_digest_element_len, &entry, CFM_PMR_DIGEST);
if (status != 0) {
if (status == CFM_ENTRY_NOT_FOUND) {
return CFM_PMR_DIGEST_NOT_FOUND;
}
return status;
}
if (pmr_digest_element_len < sizeof (struct cfm_pmr_digest_element)) {
return CFM_MALFORMED_PMR_DIGEST_ENTRY;
}
if (buffer.pmr_digest_element.pmr_id == pmr_id) {
pmr_digest->pmr_id = buffer.pmr_digest_element.pmr_id;
return cfm_flash_populate_digests (cfm_flash, &pmr_digest->digests,
buffer.pmr_digest_element.digest_count, hash_type, CFM_PMR_DIGEST, entry - 1,
sizeof (struct cfm_pmr_digest_element));
}
}
}
/**
* Find next measurement entry after provided entry ID.
*
* @param cfm The CFM to query.
* @param pmr_measurement A container to be updated with the component measurement information.
* @param entry Entry ID to start from, then output for the entry index following the matching
* measurement element if found.
*
* @return 0 if the measurement was found or an error code.
*/
static int cfm_flash_get_next_measurement (const struct cfm *cfm,
struct cfm_measurement_digest *pmr_measurement, enum hash_type hash_type, uint8_t *entry)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
struct cfm_measurement_element measurement_element;
struct cfm_measurement_element *measurement_element_ptr =
(struct cfm_measurement_element*) &measurement_element;
size_t measurement_element_len = sizeof (struct cfm_measurement_element);
int status;
pmr_measurement->allowable_digests = NULL;
pmr_measurement->allowable_digests_count = 0;
// Get the next Measurement element
status = cfm_flash_get_next_element (cfm_flash, 0, (uint8_t**) &measurement_element_ptr,
&measurement_element_len, entry, CFM_MEASUREMENT);
if (status != 0) {
return status;
}
// Verify that an entire Measurement element was read
if (measurement_element_len < (int) (sizeof (struct cfm_measurement_element))) {
return CFM_MALFORMED_MEASUREMENT_ENTRY;
}
pmr_measurement->pmr_id = measurement_element.pmr_id;
pmr_measurement->measurement_id = measurement_element.measurement_id;
pmr_measurement->allowable_digests_count = measurement_element.allowable_digest_count;
pmr_measurement->allowable_digests = platform_calloc (pmr_measurement->allowable_digests_count,
sizeof (struct cfm_allowable_digests));
// Retrieve list of cfm_allowable_digests
status = cfm_flash_populate_allowable_digests (cfm_flash, pmr_measurement->allowable_digests,
pmr_measurement->allowable_digests_count, hash_type, *entry - 1,
sizeof (struct cfm_measurement_element));
if (status != 0) {
cfm_flash_free_cfm_allowable_digests (cfm_flash, pmr_measurement->allowable_digests,
pmr_measurement->allowable_digests_count);
pmr_measurement->allowable_digests = NULL;
pmr_measurement->allowable_digests_count = 0;
}
return status;
}
/**
* Free content within a measurement data container.
*
* @param cfm The CFM instance that provided the measurement data.
* @param measurement_data The measurement data container with content to free.
*/
static void cfm_flash_free_measurement_data (const struct cfm *cfm,
struct cfm_measurement_data *measurement_data)
{
uint8_t i_check;
uint8_t i_data;
struct cfm_allowable_data_entry *curr_allowable_data;
UNUSED (cfm);
if (measurement_data != NULL) {
for (i_check = 0; i_check < measurement_data->data_checks_count; ++i_check) {
platform_free ((void*) measurement_data->data_checks[i_check].bitmask);
measurement_data->data_checks[i_check].bitmask = NULL;
curr_allowable_data = measurement_data->data_checks[i_check].allowable_data;
if (curr_allowable_data != NULL) {
for (i_data = 0; i_data < measurement_data->data_checks[i_check].data_count;
i_data++) {
platform_free ((void*) curr_allowable_data[i_data].data);
curr_allowable_data[i_data].data = NULL;
}
}
platform_free (curr_allowable_data);
measurement_data->data_checks[i_check].allowable_data = NULL;
}
platform_free (measurement_data->data_checks);
measurement_data->data_checks = NULL;
measurement_data->data_checks_count = 0;
}
}
/**
* Internal measurement container free function. This function does not free container context.
*
* @param cfm The CFM instance that provided the measurement data.
* @param measurement_data The measurement data container with content to free.
*/
static void cfm_flash_free_measurement_container_internal (const struct cfm *cfm,
struct cfm_measurement_container *container)
{
if (container != NULL) {
if (container->measurement_type == CFM_MEASUREMENT_TYPE_DIGEST) {
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
cfm_flash_free_cfm_allowable_digests (cfm_flash,
container->measurement.digest.allowable_digests,
container->measurement.digest.allowable_digests_count);
container->measurement.digest.allowable_digests = NULL;
}
else if (container->measurement_type == CFM_MEASUREMENT_TYPE_DATA) {
cfm_flash_free_measurement_data (cfm, &container->measurement.data);
}
}
}
void cfm_flash_free_measurement_container (const struct cfm *cfm,
struct cfm_measurement_container *container)
{
if ((cfm != NULL) && (container != NULL)) {
cfm_flash_free_measurement_container_internal (cfm, container);
platform_free (container->context);
container->context = NULL;
}
}
/**
* Find next measurement data after provided entry ID.
*
* @param cfm The CFM to query.
* @param measurement_data A container to be updated with the component measurement data
* information.
* @param entry Entry ID to start from, then output for the entry index following the matching
* measurement element if found.
*
* @return 0 if the measurement data was found or an error code.
*/
static int cfm_flash_get_next_measurement_data (const struct cfm *cfm,
struct cfm_measurement_data *measurement_data, uint8_t *entry)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
union {
struct cfm_measurement_data_element measurement_data_element;
struct cfm_allowable_data_element allowable_data_element;
struct cfm_allowable_data_element_entry allowable_data_element_entry;
} buffer;
struct cfm_measurement_data_element *measurement_data_element_ptr =
&buffer.measurement_data_element;
struct cfm_allowable_data_element *allowable_data_element_ptr = &buffer.allowable_data_element;
struct cfm_allowable_data_element_entry *allowable_data_element_entry_ptr =
&buffer.allowable_data_element_entry;
struct cfm_allowable_data *allowable_data_ptr;
size_t measurement_data_element_len = sizeof (struct cfm_measurement_data_element);
size_t offset;
uint8_t i_allowable_data;
int num_allowable_data;
int status;
measurement_data->data_checks = NULL;
measurement_data->data_checks_count = 0;
// Get Measurement Data element
status = cfm_flash_get_next_element (cfm_flash, 0, (uint8_t**) &measurement_data_element_ptr,
&measurement_data_element_len, entry, CFM_MEASUREMENT_DATA);
if (status != 0) {
return status;
}
// Verify that an entire Measurement Data element was read
if (measurement_data_element_len < (int) (sizeof (struct cfm_measurement_data_element))) {
return CFM_MALFORMED_MEASUREMENT_DATA_ENTRY;
}
measurement_data->pmr_id = measurement_data_element_ptr->pmr_id;
measurement_data->measurement_id = measurement_data_element_ptr->measurement_id;
// Get count of Allowable Data elements
status = manifest_flash_get_child_elements_info (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, *entry, CFM_MEASUREMENT_DATA, CFM_COMPONENT_DEVICE,
CFM_ALLOWABLE_DATA, NULL, &num_allowable_data, NULL);
if (status != 0) {
return status;
}
// Allocate space for Allowable Datas and set fields of measurement_data
measurement_data->data_checks_count = num_allowable_data;
measurement_data->data_checks =
platform_calloc (measurement_data->data_checks_count, sizeof (struct cfm_allowable_data));
if (measurement_data->data_checks == NULL) {
return CFM_NO_MEMORY;
}
// Read each Allowable Data element and fill in measurement_data
for (i_allowable_data = 0; i_allowable_data < num_allowable_data; ++i_allowable_data) {
// Read Allowable Data element
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_ALLOWABLE_DATA, *entry, CFM_MEASUREMENT_DATA, 0, NULL,
NULL, NULL, (uint8_t**) &allowable_data_element_ptr,
sizeof (struct cfm_allowable_data_element));
if (ROT_IS_ERROR (status)) {
if (status == MANIFEST_CHILD_NOT_FOUND) {
status = CFM_ENTRY_NOT_FOUND;
}
goto free_allowable_data;
}
// Verify that an entire Allowable Data element was read
if (status < (int) (sizeof (struct cfm_allowable_data_element))) {
status = CFM_MALFORMED_ALLOWABLE_DATA_ENTRY;
goto free_allowable_data;
}
allowable_data_ptr = &measurement_data->data_checks[i_allowable_data];
allowable_data_ptr->check = (enum cfm_check) allowable_data_element_ptr->check.check;
allowable_data_ptr->big_endian =
(allowable_data_element_ptr->check.endianness == CFM_MULTIBYTE_BIG_ENDIAN);
allowable_data_ptr->data_count = allowable_data_element_ptr->num_data;
allowable_data_ptr->bitmask_length = allowable_data_element_ptr->bitmask_length;
// Update offset to start of bitmask
offset = sizeof (struct cfm_allowable_data_element);
// Read bitmask, if one exists for this Allowable Data
if (allowable_data_ptr->bitmask_length) {
// Allocate space for bitmask
allowable_data_ptr->bitmask = platform_malloc (allowable_data_ptr->bitmask_length);
if (allowable_data_ptr->bitmask == NULL) {
status = CFM_NO_MEMORY;
goto free_allowable_data;
}
// Read bitmask from Allowable Data element
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_ALLOWABLE_DATA, *entry, CFM_MEASUREMENT_DATA,
offset, NULL, NULL, NULL, (uint8_t**) &allowable_data_ptr->bitmask,
allowable_data_ptr->bitmask_length);
if (ROT_IS_ERROR (status)) {
goto free_allowable_data;
}
// Ensure that the full bitmask was read
if (status < (int) (allowable_data_ptr->bitmask_length)) {
status = CFM_MALFORMED_ALLOWABLE_DATA_ENTRY;
goto free_allowable_data;
}
// Advance offset for 4-byte alignment
offset += (((size_t) allowable_data_ptr->bitmask_length + 3) & ~((size_t) 3));
}
// Allocate space for Data entries
allowable_data_ptr->allowable_data = platform_calloc (allowable_data_ptr->data_count,
sizeof (struct cfm_allowable_data_entry));
if (allowable_data_ptr->allowable_data == NULL) {
status = CFM_NO_MEMORY;
goto free_allowable_data;
}
// Read all Data entries of current Allowable Data element
for (uint8_t i_data = 0; i_data < allowable_data_ptr->data_count; i_data++) {
// Read Data header
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_ALLOWABLE_DATA, *entry, CFM_MEASUREMENT_DATA,
offset, entry, NULL, NULL, (uint8_t**) &allowable_data_element_entry_ptr,
sizeof (struct cfm_allowable_data_element_entry));
if (ROT_IS_ERROR (status)) {
goto free_allowable_data;
}
if (status < (int) (sizeof (struct cfm_allowable_data_element_entry))) {
status = CFM_MALFORMED_ALLOWABLE_DATA_ENTRY;
goto free_allowable_data;
}
allowable_data_ptr->allowable_data[i_data].version_set =
allowable_data_element_entry_ptr->version_set;
allowable_data_ptr->allowable_data[i_data].data_len =
allowable_data_element_entry_ptr->data_length;
offset += sizeof (struct cfm_allowable_data_element_entry);
allowable_data_ptr->allowable_data[i_data].data =
platform_malloc (allowable_data_ptr->allowable_data[i_data].data_len);
// Read Data
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_ALLOWABLE_DATA, *entry, CFM_MEASUREMENT_DATA,
offset, entry, NULL, NULL,
(uint8_t**) &allowable_data_ptr->allowable_data[i_data].data,
allowable_data_ptr->allowable_data[i_data].data_len);
if (ROT_IS_ERROR (status)) {
goto free_allowable_data;
}
if (status < (int) (allowable_data_ptr->allowable_data[i_data].data_len)) {
status = CFM_MALFORMED_ALLOWABLE_DATA_ENTRY;
goto free_allowable_data;
}
offset += (((size_t) allowable_data_ptr->allowable_data[i_data].data_len + 3) &
~((size_t) 3));
}
// Advance to next Allowable Data
*entry = *entry + 1;
}
return 0;
free_allowable_data:
cfm_flash_free_measurement_data (cfm, measurement_data);
return status;
}
/**
* Determine if the unique element to be used to determine device version set is a Measurement
* element or Measurement Data element.
*
* @param cfm The CFM to query.
* @param component_id The component ID to find version set element for.
* @param comp_device_entry Optional input for starting entry to use, then output for the entry
* index following the matching component device element if found. This can be null if not needed.
* @param comp_device_hash_type Buffer to update with component device hash type.
*
* @return Element tag used to determine version set, or an error code.
*/
static int cfm_flash_determine_version_set_element (const struct cfm *cfm, uint32_t component_id,
uint8_t *comp_device_entry, enum hash_type *comp_device_hash_type)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
struct cfm_component_device_element component_element;
int measurement_data_entry;
int measurement_entry;
int status;
status = cfm_flash_get_component_device_with_starting_entry (cfm_flash, component_id,
&component_element, comp_device_entry);
if (status != 0) {
return status;
}
*comp_device_hash_type =
manifest_convert_manifest_hash_type (component_element.measurement_hash_type);
status = manifest_flash_get_child_elements_info (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, *comp_device_entry, CFM_COMPONENT_DEVICE, MANIFEST_NO_PARENT,
CFM_MEASUREMENT, NULL, NULL, &measurement_entry);
if (status == MANIFEST_CHILD_NOT_FOUND) {
measurement_entry = MANIFEST_CHILD_NOT_FOUND;
}
else if (status != 0) {
return status;
}
status = manifest_flash_get_child_elements_info (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, *comp_device_entry, CFM_COMPONENT_DEVICE, MANIFEST_NO_PARENT,
CFM_MEASUREMENT_DATA, NULL, NULL, &measurement_data_entry);
if (status == MANIFEST_CHILD_NOT_FOUND) {
measurement_data_entry = MANIFEST_CHILD_NOT_FOUND;
}
else if (status != 0) {
return status;
}
// If both Measurement and Measurement Data elements not found, return error
if ((measurement_entry == MANIFEST_CHILD_NOT_FOUND) &&
(measurement_data_entry == MANIFEST_CHILD_NOT_FOUND)) {
return MANIFEST_CHILD_NOT_FOUND;
}
/* If Measurement Data element not found, or comes after Measurement element, select Measurement
* element. */
if ((measurement_data_entry == MANIFEST_CHILD_NOT_FOUND) ||
(measurement_data_entry > measurement_entry)) {
return CFM_MEASUREMENT;
}
/* If Measurement element not found, or comes after Measurement Data element, select Measurement
* Data element. */
return CFM_MEASUREMENT_DATA;
}
/**
* Implementation context for the get_next_measurement_or_measurement_data function.
*/
struct cfm_flash_measurement_context {
enum hash_type comp_device_hash_type; /**< Hash type of component device. */
uint8_t element_entry; /**< Entry for next element to read back. */
int version_set_element; /**< Element type for version set selection. */
};
int cfm_flash_get_next_measurement_or_measurement_data (const struct cfm *cfm,
uint32_t component_id, struct cfm_measurement_container *container, bool first)
{
struct cfm_flash_measurement_context *context;
uint8_t comp_device_entry = 0;
int status;
/* This function assumes all Measurement and Measurement Data entries are contiguous. */
if ((cfm == NULL) || (container == NULL)) {
return CFM_INVALID_ARGUMENT;
}
if (first) {
memset (container, 0, sizeof (struct cfm_measurement_container));
container->context = platform_calloc (sizeof (struct cfm_flash_measurement_context), 1);
context = (struct cfm_flash_measurement_context*) container->context;
context->version_set_element = cfm_flash_determine_version_set_element (cfm, component_id,
&comp_device_entry, &context->comp_device_hash_type);
if (ROT_IS_ERROR (context->version_set_element)) {
status = context->version_set_element;
platform_free (container->context);
container->context = NULL;
return status;
}
context->element_entry = comp_device_entry;
container->measurement_type =
(context->version_set_element == CFM_MEASUREMENT) ?
CFM_MEASUREMENT_TYPE_DIGEST : CFM_MEASUREMENT_TYPE_DATA;
}
else {
cfm_flash_free_measurement_container_internal (cfm, container);
}
context = (struct cfm_flash_measurement_context*) container->context;
// TODO: Support interleaved measurement and measurement block entries
if (container->measurement_type == CFM_MEASUREMENT_TYPE_DIGEST) {
status = cfm_flash_get_next_measurement (cfm, &container->measurement.digest,
context->comp_device_hash_type, &context->element_entry);
if ((status == CFM_ENTRY_NOT_FOUND) && (context->version_set_element == CFM_MEASUREMENT)) {
container->measurement_type = CFM_MEASUREMENT_TYPE_DATA;
status = cfm_flash_get_next_measurement_data (cfm, &container->measurement.data,
&context->element_entry);
}
}
else {
status = cfm_flash_get_next_measurement_data (cfm, &container->measurement.data,
&context->element_entry);
if ((status == CFM_ENTRY_NOT_FOUND) &&
(context->version_set_element == CFM_MEASUREMENT_DATA)) {
container->measurement_type = CFM_MEASUREMENT_TYPE_DIGEST;
status = cfm_flash_get_next_measurement (cfm, &container->measurement.digest,
context->comp_device_hash_type, &context->element_entry);
}
}
if ((status != 0) && first) {
cfm_flash_free_measurement_container (cfm, container);
}
return status;
}
void cfm_flash_free_root_ca_digest (const struct cfm *cfm,
struct cfm_root_ca_digests *root_ca_digest)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
if (root_ca_digest != NULL) {
cfm_flash_free_cfm_digests (cfm_flash, &root_ca_digest->digests);
}
}
int cfm_flash_get_root_ca_digest (const struct cfm *cfm, uint32_t component_id,
struct cfm_root_ca_digests *root_ca_digest)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
union {
struct cfm_component_device_element component;
struct cfm_root_ca_digests_element root_ca_digests_element;
} buffer;
struct cfm_component_device_element *component_ptr = &buffer.component;
struct cfm_root_ca_digests_element *root_ca_digests_element_ptr =
&buffer.root_ca_digests_element;
size_t root_ca_digests_element_len = sizeof (struct cfm_root_ca_digests_element);
uint8_t entry = 0;
enum hash_type hash_type;
int status;
if (root_ca_digest == NULL) {
return CFM_INVALID_ARGUMENT;
}
status = cfm_flash_get_component_device_with_starting_entry (cfm_flash, component_id,
component_ptr, &entry);
if (status != 0) {
return status;
}
hash_type = manifest_convert_manifest_hash_type (component_ptr->measurement_hash_type);
status = cfm_flash_get_next_element (cfm_flash, component_id,
(uint8_t**) &root_ca_digests_element_ptr, &root_ca_digests_element_len, &entry,
CFM_ROOT_CA);
if (status != 0) {
if (status == CFM_ENTRY_NOT_FOUND) {
return CFM_ROOT_CA_NOT_FOUND;
}
return status;
}
if (root_ca_digests_element_len < (int) (sizeof (struct cfm_root_ca_digests_element))) {
return CFM_MALFORMED_ROOT_CA_DIGESTS_ENTRY;
}
return cfm_flash_populate_digests (cfm_flash, &root_ca_digest->digests,
buffer.root_ca_digests_element.ca_count, hash_type, CFM_ROOT_CA, entry - 1,
sizeof (struct cfm_root_ca_digests_element));
}
void cfm_flash_free_manifest (const struct cfm *cfm, struct cfm_manifest *manifest)
{
uint8_t i_check;
UNUSED (cfm);
if (manifest != NULL) {
for (i_check = 0; i_check < manifest->check_count; ++i_check) {
platform_free ((void*) manifest->check[i_check].allowable_id);
}
platform_free (manifest->check);
platform_free ((void*) manifest->platform_id);
manifest->check = NULL;
manifest->platform_id = NULL;
}
}
/**
* Common function used to find next allowable manifest element for the specified component ID.
*
* @param cfm The CFM to query.
* @param component_id The component ID to find allowable manifest for.
* @param manifest_type The manifest type to find.
* @param allowable_manifest A container to be updated with the component allowable manifest
* information. Contents of the container are dynamically allocated and need to be freed using
* free_manifest.
* @param first Fetch first allowable manifest from CFM, or next allowable manifest since last call.
*
* @return 0 if the allowable manifest element was found or an error code.
*/
static int cfm_flash_get_next_manifest (const struct cfm *cfm, uint32_t component_id,
int manifest_type, struct cfm_manifest *allowable_manifest, bool first)
{
const struct cfm_flash *cfm_flash = (const struct cfm_flash*) cfm;
union {
struct cfm_allowable_pfm_element allowable_pfm_element;
struct cfm_allowable_id_element allowable_id_element;
} buffer;
struct cfm_allowable_pfm_element *allowable_pfm_element_ptr = &buffer.allowable_pfm_element;
struct cfm_allowable_id_element *allowable_id_element_ptr = &buffer.allowable_id_element;
struct cfm_allowable_id *allowable_id_ptr;
size_t allowable_pfm_element_len = sizeof (struct cfm_allowable_pfm_element);
size_t ids_len;
size_t offset;
uint8_t *element_entry_ptr;
int num_allowable_id;
int i_allowable_id;
int status;
if ((cfm == NULL) || (allowable_manifest == NULL)) {
return CFM_INVALID_ARGUMENT;
}
element_entry_ptr = (uint8_t*) &allowable_manifest->context;
if (first) {
*element_entry_ptr = 0;
}
else {
cfm_flash_free_manifest (cfm, allowable_manifest);
}
allowable_manifest->check_count = 0;
allowable_manifest->check = NULL;
allowable_manifest->platform_id = NULL;
// All allowable manifest elements have the same format, so use allowable PFM element containers
status = cfm_flash_get_next_element (cfm_flash, component_id,
(uint8_t**) &allowable_pfm_element_ptr, &allowable_pfm_element_len, element_entry_ptr,
manifest_type);
if (ROT_IS_ERROR (status)) {
return status;
}
allowable_manifest->manifest_index = allowable_pfm_element_ptr->port_id;
allowable_pfm_element_ptr->manifest.platform_id[
allowable_pfm_element_ptr->manifest.platform_id_len] = '\0';
allowable_manifest->platform_id =
strdup ((char*) allowable_pfm_element_ptr->manifest.platform_id);
if (allowable_manifest->platform_id == NULL) {
return CFM_NO_MEMORY;
}
status = manifest_flash_get_child_elements_info (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, *element_entry_ptr, manifest_type, CFM_COMPONENT_DEVICE,
CFM_ALLOWABLE_ID, NULL, &num_allowable_id, NULL);
if (status != 0) {
goto free_manifest;
}
allowable_manifest->check_count = num_allowable_id;
allowable_manifest->check = platform_calloc (allowable_manifest->check_count,
sizeof (struct cfm_allowable_id));
if (allowable_manifest->check == NULL) {
status = CFM_NO_MEMORY;
goto free_manifest;
}
for (i_allowable_id = 0; i_allowable_id < num_allowable_id; ++i_allowable_id) {
// Read Allowable ID element
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_ALLOWABLE_ID, *element_entry_ptr, manifest_type, 0,
NULL, NULL, NULL, (uint8_t**) &allowable_id_element_ptr,
sizeof (struct cfm_allowable_id_element));
if (ROT_IS_ERROR (status)) {
if (status == MANIFEST_CHILD_NOT_FOUND) {
status = CFM_ENTRY_NOT_FOUND;
}
goto free_manifest;
}
if (status < (int) (sizeof (struct cfm_allowable_id_element))) {
status = CFM_MALFORMED_ALLOWABLE_ID_ENTRY;
goto free_manifest;
}
allowable_id_ptr = &allowable_manifest->check[i_allowable_id];
allowable_id_ptr->check = (enum cfm_check) allowable_id_element_ptr->check.check;
allowable_id_ptr->id_count = allowable_id_element_ptr->num_id;
offset = sizeof (struct cfm_allowable_id_element);
ids_len = allowable_id_ptr->id_count * sizeof (uint32_t);
allowable_id_ptr->allowable_id = platform_malloc (ids_len);
if (allowable_id_ptr->allowable_id == NULL) {
status = CFM_NO_MEMORY;
goto free_manifest;
}
// Read each ID
status = manifest_flash_read_element_data (&cfm_flash->base_flash,
cfm_flash->base_flash.hash, CFM_ALLOWABLE_ID, *element_entry_ptr, manifest_type, offset,
element_entry_ptr, NULL, NULL, (uint8_t**) &allowable_id_ptr->allowable_id, ids_len);
if (ROT_IS_ERROR (status)) {
goto free_manifest;
}
if (status < (int) (ids_len)) {
status = CFM_MALFORMED_ALLOWABLE_ID_ENTRY;
goto free_manifest;
}
if (allowable_id_element_ptr->check.endianness == CFM_MULTIBYTE_BIG_ENDIAN) {
for (uint8_t i_id = 0; i_id < allowable_id_ptr->id_count; i_id++) {
*((uint32_t*) &allowable_id_ptr->allowable_id[i_id]) =
SWAP_BYTES_UINT32 (allowable_id_ptr->allowable_id[i_id]);
}
}
*element_entry_ptr = *element_entry_ptr + 1;
}
return 0;
free_manifest:
cfm_flash_free_manifest (cfm, allowable_manifest);
return status;
}
int cfm_flash_get_next_pfm (const struct cfm *cfm, uint32_t component_id,
struct cfm_manifest *allowable_pfm, bool first)
{
return cfm_flash_get_next_manifest (cfm, component_id, CFM_ALLOWABLE_PFM, allowable_pfm, first);
}
int cfm_flash_get_next_cfm (const struct cfm *cfm, uint32_t component_id,
struct cfm_manifest *allowable_cfm, bool first)
{
return cfm_flash_get_next_manifest (cfm, component_id, CFM_ALLOWABLE_CFM, allowable_cfm, first);
}
int cfm_flash_get_pcd (const struct cfm *cfm, uint32_t component_id,
struct cfm_manifest *allowable_pcd)
{
return cfm_flash_get_next_manifest (cfm, component_id, CFM_ALLOWABLE_PCD, allowable_pcd, true);
}
/**
* Initialize the interface to a CFM residing in flash memory. CFMs only support manifest version
* 2.
*
* @param cfm The CFM instance to initialize.
* @param state Variable context for the CFM instance. This must be uninitialized.
* @param flash The flash device that contains the CFM.
* @param hash A hash engine to use for validating run-time access to CFM information. If it is
* possible for any CFM information to be requested concurrently by different threads, this hash
* engine MUST be thread-safe. There is no internal synchronization around the hashing operations.
* @param base_addr The starting address of the CFM storage location.
* @param signature_cache Buffer to hold the manifest signature.
* @param max_signature The maximum supported length for a manifest signature.
* @param platform_id_cache Buffer to hold the manifest platform ID.
* @param max_platform_id The maximum platform ID length supported, including the NULL terminator.
*
* @return 0 if the CFM instance was initialized successfully or an error code.
*/
int cfm_flash_init (struct cfm_flash *cfm, struct cfm_flash_state *state, const struct flash *flash,
const struct hash_engine *hash, uint32_t base_addr, uint8_t *signature_cache,
size_t max_signature, uint8_t *platform_id_cache, size_t max_platform_id)
{
int status;
if ((cfm == NULL) || (state == NULL)) {
return CFM_INVALID_ARGUMENT;
}
memset (cfm, 0, sizeof (struct cfm_flash));
status = manifest_flash_v2_init (&cfm->base_flash, &state->base, flash, hash, base_addr,
MANIFEST_NOT_SUPPORTED, CFM_V2_MAGIC_NUM, signature_cache, max_signature, platform_id_cache,
max_platform_id);
if (status != 0) {
return status;
}
cfm->base.base.verify = cfm_flash_verify;
cfm->base.base.get_id = cfm_flash_get_id;
cfm->base.base.get_platform_id = cfm_flash_get_platform_id;
cfm->base.base.free_platform_id = cfm_flash_free_platform_id;
cfm->base.base.get_hash = cfm_flash_get_hash;
cfm->base.base.get_signature = cfm_flash_get_signature;
cfm->base.base.is_empty = cfm_flash_is_empty;
cfm->base.get_component_device = cfm_flash_get_component_device;
cfm->base.free_component_device = cfm_flash_free_component_device;
cfm->base.buffer_supported_components = cfm_flash_buffer_supported_components;
cfm->base.get_component_pmr = cfm_flash_get_component_pmr;
cfm->base.get_component_pmr_digest = cfm_flash_get_component_pmr_digest;
cfm->base.free_component_pmr_digest = cfm_flash_free_component_pmr_digest;
cfm->base.get_next_measurement_or_measurement_data =
cfm_flash_get_next_measurement_or_measurement_data;
cfm->base.free_measurement_container = cfm_flash_free_measurement_container;
cfm->base.get_root_ca_digest = cfm_flash_get_root_ca_digest;
cfm->base.free_root_ca_digest = cfm_flash_free_root_ca_digest;
cfm->base.get_next_pfm = cfm_flash_get_next_pfm;
cfm->base.get_next_cfm = cfm_flash_get_next_cfm;
cfm->base.get_pcd = cfm_flash_get_pcd;
cfm->base.free_manifest = cfm_flash_free_manifest;
return 0;
}
/**
* Initialize only the variable state for a CFM on flash. The rest of the handler is assumed to
* have already been initialized.
*
* This would generally be used with a statically initialized instance.
*
* @param cfm The CFM that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int cfm_flash_init_state (const struct cfm_flash *cfm)
{
if (cfm == NULL) {
return CFM_INVALID_ARGUMENT;
}
return manifest_flash_init_state (&cfm->base_flash);
}
/**
* Release the resources used by the CFM interface.
*
* @param cfm The CFM instance to release.
*/
void cfm_flash_release (const struct cfm_flash *cfm)
{
if (cfm != NULL) {
manifest_flash_release (&cfm->base_flash);
}
}