core/attestation/pcr_store.c (580 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdint.h>
#include <string.h>
#include "pcr.h"
#include "pcr_data.h"
#include "pcr_store.h"
#include "common/buffer_util.h"
#include "common/common_math.h"
/**
* Get the PCR index from a measurement identifier.
*/
#define PCR_STORE_PCR_INDEX(x) (((x) >> 8) & 0xff)
/**
* Get the measurement index within a PCR from the measurement identifier.
*/
#define PCR_STORE_MEASUREMENT_INDEX(x) ((x) & 0xff)
/**
* Initialize storage for all device PCRs.
*
* @param store The PCR storage to initialize.
* @param pcr_config An array containing configuration information for each of the PCRs that should
* be managed. If any PCR is configured to hold no measurements, a single, explicit measurement can
* be stored in that PCR.
* @param num_pcrs The number of PCR configurations provided, which indicates the number of PCRs
* that will be managed. This is limited to 256 PCRs.
*
* @return Completion status, 0 if success or an error code.
*/
int pcr_store_init (struct pcr_store *store, const struct pcr_config *pcr_config, uint8_t num_pcrs)
{
size_t i;
int status;
if ((store == NULL) || (pcr_config == NULL) || (num_pcrs == 0)) {
return PCR_INVALID_ARGUMENT;
}
store->pcrs = platform_malloc (sizeof (struct pcr_bank) * num_pcrs);
if (store->pcrs == NULL) {
return PCR_NO_MEMORY;
}
store->num_pcrs = num_pcrs;
for (i = 0; i < num_pcrs; ++i) {
status = pcr_init (&store->pcrs[i], &pcr_config[i]);
if (status != 0) {
while (i > 0) {
pcr_release (&store->pcrs[--i]);
}
platform_free (store->pcrs);
return status;
}
}
return status;
}
/**
* Release resources used for PCR storage.
*
* @param store The PCR storage to release.
*/
void pcr_store_release (struct pcr_store *store)
{
size_t i;
if (store != NULL) {
for (i = 0; i < store->num_pcrs; ++i) {
pcr_release (&store->pcrs[i]);
}
platform_free (store->pcrs);
}
}
/**
* Indicate if a measurement type is valid for the PCR store.
*
* @param store The PCR store to query.
* @param measurement_type Measurement identifier to check for validity.
*
* @return 0 if the measurement type is valid or an error code.
*/
int pcr_store_check_measurement_type (struct pcr_store *store, uint16_t measurement_type)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_check_measurement_index (&store->pcrs[pcr_index],
PCR_STORE_MEASUREMENT_INDEX (measurement_type));
}
/**
* Determine the measurement type identifier for a measurement referenced with a sequential
* identifier.
*
* The mapping will be determined by assigning the first measurement in the first PCR to 0, followed
* by the rest of the measurements in the first PCR. Once those are exhausted, the next sequential
* ID will assigned to the first measurement of the second PCR, and so on.
*
* For example, the sequential IDs for measurements in 3 PCRs with 3, 2, and 1 measurements would
* look like:
* 0: PCR 0, measurement 0
* 1: PCR 0, measurement 1
* 2: PCR 0, measurement 2
* 3: PCR 1, measurement 0
* 4: PCR 1, measurement 1
* 5: PCR 2, measurement 0
*
* Explicit PCRs will be skipped.
*
* @param store The PCR store to query.
* @param sequential_id The 0-based ID for the requested measurement.
*
* @return The measurement type identifier that maps to the sequential ID or an error code.
*/
int pcr_store_get_measurement_type (struct pcr_store *store, size_t sequential_id)
{
uint8_t pcr_index = 0;
uint8_t measurement_index;
int num_measurements;
bool valid = false;
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
while (!valid && (pcr_index < store->num_pcrs)) {
num_measurements = pcr_get_num_measurements (&store->pcrs[pcr_index]);
if (num_measurements == 0) {
/* Skip explicit PCRs. */
pcr_index++;
}
else if (sequential_id >= (size_t) num_measurements) {
/* Not enough measurements in this PCR. Move to the next one. */
sequential_id -= num_measurements;
pcr_index++;
}
else {
/* Valid mapping in this PCR. */
measurement_index = sequential_id;
valid = true;
}
}
if (!valid) {
return PCR_INVALID_SEQUENTIAL_ID;
}
return PCR_MEASUREMENT (pcr_index, measurement_index);
}
/**
* Retrieve the total number of PCRs in the store.
*
* @param store The PCR store to query.
*
* @return The number of PCRs or an error code.
*/
int pcr_store_get_num_pcrs (struct pcr_store *store)
{
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
return store->num_pcrs;
}
/**
* Get the total number of measurements in all PCRs.
*
* @param store PCR store to query.
*
* @return The total number of measurements or an error code.
*/
int pcr_store_get_num_total_measurements (struct pcr_store *store)
{
int num_measurements = 0;
size_t i;
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
for (i = 0; i < store->num_pcrs; ++i) {
num_measurements += pcr_get_num_measurements (&store->pcrs[i]);
}
return num_measurements;
}
/**
* Get the number of measurements in a single PCR. An explicit PCR value will report has having no
* measurements.
*
* @param store The PCR store that contains the PCR to query.
* @param pcr_num The index of the PCR to query.
*
* @return The number of measurements in the PCR or an error code.
*/
int pcr_store_get_num_pcr_measurements (struct pcr_store *store, uint8_t pcr_num)
{
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_num >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_get_num_measurements (&store->pcrs[pcr_num]);
}
/**
* Get the digest length used by a single PCR.
*
* @param store The PCR store that contains the PCR to query.
* @param pcr_num The index of the PCR to query.
*
* @return The digest length used by the PCR or an error code.
*/
int pcr_store_get_pcr_digest_length (struct pcr_store *store, uint8_t pcr_num)
{
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_num >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_get_digest_length (&store->pcrs[pcr_num]);
}
/**
* Set the TCG event type for a single measurement.
*
* @param store The PCR store containing the measurement to update.
* @param measurement_type Identifier for the measurement to update.
* @param event_type TCG event type to associate with the measurement.
*
* @return 0 if the event type was set successfully or an error code.
*/
int pcr_store_set_tcg_event_type (struct pcr_store *store, uint16_t measurement_type,
uint32_t event_type)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_set_tcg_event_type (&store->pcrs[pcr_index], measurement_index, event_type);
}
/**
* Set the DMTF value type for a single measurement.
*
* @param store The PCR store containing the measurement to update.
* @param measurement_type Identifier for the measurement to update.
* @param value_type DMTF value type to associate with the measurement.
* @param is_not_tcb Flag to indicate that a measurement should not be considered part of the TCB
* when responding to SPDM requests.
*
* @return 0 if the value type was set successfully or an error code.
*/
int pcr_store_set_dmtf_value_type (struct pcr_store *store, uint16_t measurement_type,
enum pcr_dmtf_value_type value_type, bool is_not_tcb)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_set_dmtf_value_type (&store->pcrs[pcr_index], measurement_index, value_type,
is_not_tcb);
}
/**
* Get the DMTF value type for a single measurement.
*
* @param store The PCR store containing the measurement to query.
* @param measurement_type Identifier for the measurement to query.
* @param value_type Output for the DMTF value type for the measurement.
*
* @return 0 if the value type was retrieved successfully or an error code.
*/
int pcr_store_get_dmtf_value_type (struct pcr_store *store, uint16_t measurement_type,
enum pcr_dmtf_value_type *value_type)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if ((store == NULL) || (value_type == NULL)) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_get_dmtf_value_type (&store->pcrs[pcr_index], measurement_index, value_type);
}
/**
* Determine if a single measurement is part of the Trusted Computing Base (TCB) for the device.
*
* @param store The PCR store containing the measurement to query.
* @param measurement_type Identifier for the measurement to query.
*
* @return 1 if the measurement is part of the TCB, 0 if not, or an error code.
*/
int pcr_store_is_measurement_in_tcb (struct pcr_store *store, uint16_t measurement_type)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_is_measurement_in_tcb (&store->pcrs[pcr_index], measurement_index);
}
/**
* Store a pre-computed digest for a single measurement in the PCR store.
*
* @param store The PCR store containing measurement to update.
* @param measurement_type Identifier for the measurement to update.
* @param digest The digest data that should be stored for the measurement.
* @param digest_len Length of digest. This must match exactly the digest length for the PCR.
*
* @return 0 if the digest was stored successfully or an error code.
*/
int pcr_store_update_digest (struct pcr_store *store, uint16_t measurement_type,
const uint8_t *digest, size_t digest_len)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_update_digest (&store->pcrs[pcr_index], measurement_index, digest, digest_len);
}
/**
* Update a specified measurement in the PCR store by computing the digest of a data buffer.
*
* @param store The PCR store containing measurement to update.
* @param hash Hashing engine to use for digest calculation.
* @param measurement_type Identifier for the measurement to update.
* @param buf Buffer holding the data to measure.
* @param buf_len Length of data buffer.
* @param include_event Flag that indicates whether to include the event type in measurement
* calculations.
*
* @return 0 if the measurement was updated successfully or an error code.
*/
int pcr_store_update_buffer (struct pcr_store *store, const struct hash_engine *hash,
uint16_t measurement_type, const uint8_t *buf, size_t buf_len, bool include_event)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_update_buffer (&store->pcrs[pcr_index], hash, measurement_index, buf, buf_len,
include_event);
}
/**
* Update a specified measurement in the PCR store by computing the digest of a versioned data
* buffer.
*
* @param store The PCR store containing measurement to update.
* @param hash Hashing engine to use for digest calculation.
* @param measurement_type Identifier for the measurement to update.
* @param buf Buffer holding the data to measure.
* @param buf_len Length of data buffer.
* @param include_event Flag that indicates whether to include the event type in measurement
* calculations
* @param version The version associated with the measurement data, which will prepended when
* calculating the digest.
*
* @return 0 if the measurement was updated successfully or an error code.
*/
int pcr_store_update_versioned_buffer (struct pcr_store *store, const struct hash_engine *hash,
uint16_t measurement_type, const uint8_t *buf, size_t buf_len, bool include_event,
uint8_t version)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_update_versioned_buffer (&store->pcrs[pcr_index], hash, measurement_index, buf,
buf_len, include_event, version);
}
/**
* Store a pre-computed digest for a single measurement in the PCR store. Future updates to the
* measurement will be prevented.
*
* @param store The PCR store containing measurement to update.
* @param measurement_type Identifier for the measurement to update.
* @param digest The digest data that should be stored for the measurement.
* @param digest_len Length of digest. This must match exactly the digest length for the PCR.
*
* @return 0 if the digest was stored successfully or an error code.
*/
int pcr_store_const_update_digest (struct pcr_store *store, uint16_t measurement_type,
const uint8_t *digest, size_t digest_len)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_const_update_digest (&store->pcrs[pcr_index], measurement_index, digest, digest_len);
}
/**
* Update a specified measurement in the PCR store by computing the digest of a data buffer. Future
* updates to the measurement will be prevented.
*
* @param store The PCR store containing measurement to update.
* @param hash Hashing engine to use for digest calculation.
* @param measurement_type Identifier for the measurement to update.
* @param buf Buffer holding the data to measure.
* @param buf_len Length of data buffer.
* @param include_event Flag that indicates whether to include the event type in measurement
* calculations.
*
* @return 0 if the measurement was updated successfully or an error code.
*/
int pcr_store_const_update_buffer (struct pcr_store *store, const struct hash_engine *hash,
uint16_t measurement_type, const uint8_t *buf, size_t buf_len, bool include_event)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_const_update_buffer (&store->pcrs[pcr_index], hash, measurement_index, buf, buf_len,
include_event);
}
/**
* Update a specified measurement in the PCR store by computing the digest of a versioned data
* buffer. Future updates to the measurement will be prevented.
*
* @param store The PCR store containing measurement to update.
* @param hash Hashing engine to use for digest calculation.
* @param measurement_type Identifier for the measurement to update.
* @param buf Buffer holding the data to measure.
* @param buf_len Length of data buffer.
* @param include_event Flag that indicates whether to include the event type in measurement
* calculations
* @param version The version associated with the measurement data, which will prepended when
* calculating the digest.
*
* @return 0 if the measurement was updated successfully or an error code.
*/
int pcr_store_const_update_versioned_buffer (struct pcr_store *store,
const struct hash_engine *hash, uint16_t measurement_type, const uint8_t *buf, size_t buf_len,
bool include_event, uint8_t version)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_const_update_versioned_buffer (&store->pcrs[pcr_index], hash, measurement_index, buf,
buf_len, include_event, version);
}
/**
* Clear the currently stored digest for a measurement in the PCR store.
*
* @param store The PCR store containing measurement to be cleared.
* @param measurement_type Identifier for the measurement to clear.
*
* @return 0 if the measurement digest was cleared successfully or an error code.
*/
int pcr_store_invalidate_measurement (struct pcr_store *store, uint16_t measurement_type)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_invalidate_measurement (&store->pcrs[pcr_index], measurement_index);
}
/**
* Compute a single PCR value based on the current state of the measurements in the PCR store. All
* measurements for the PCR will be included in the calculation, even if they have not been updated
* with a value or if they have been invalidated.
*
* @param store The PCR store that contains the PCR value to calculate.
* @param hash Hashing engine to use for the calculation.
* @param pcr_num The index of the PCR to compute.
* @param measurement Optional output buffer to return the PCR value. Setting this to null will
* still refresh the measurement state.
* @param length Size of the PCR output buffer, if one is provided.
*
* @return Length of the generated PCR value or an error code. Use ROT_IS_ERROR to check the return
* status.
*/
int pcr_store_compute_pcr (struct pcr_store *store, const struct hash_engine *hash, uint8_t pcr_num,
uint8_t *measurement, size_t length)
{
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_num >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_compute (&store->pcrs[pcr_num], hash, true, measurement, length);
}
/**
* Retrieve a specific measurement from the PCR store.
*
* @param store The PCR store containing the requested measurement.
* @param measurement_type Identifier for the measurement to retrieve.
* @param measurement Output for the measurement information.
*
* @return Length of the measurement digest or an error code. Use ROT_IS_ERROR to check the return
* status.
*/
int pcr_store_get_measurement (struct pcr_store *store, uint16_t measurement_type,
struct pcr_measurement *measurement)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_get_measurement (&store->pcrs[pcr_index], measurement_index, measurement);
}
/**
* Provide a descriptor for accessing the raw data that was measured for a specific measurement in
* the PCR store.
*
* @param store The PCR store containing the measurement to update with the data descriptor.
* @param measurement_type Identifier for the measurement to update.
* @param measurement_data Descriptor for the raw data associated with the measurement.
*
* @return 0 if the measured data descriptor was set successfully or an error code.
*/
int pcr_store_set_measurement_data (struct pcr_store *store, uint16_t measurement_type,
const struct pcr_measured_data *measured_data)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_set_measurement_data (&store->pcrs[pcr_index], measurement_index, measured_data);
}
/**
* Retrieve the raw data that was used to generate a measurement in the PCR store.
*
* Measured data will be read until there is no more data or the output buffer is full, which ever
* comes first. There is no direct indication that the complete data has been retrieved. A
* subsequent call with the offset and/or length adjusted would be needed to determine if there is
* more data available.
*
* No data will be written and no error will be generated if the measurement has not been provided
* with access to the raw data.
*
* @param store The PCR store containing the measurement to query.
* @param measurement_type Identifier for the measurement to query.
* @param offset An offset indicating where in the measurement data to start reading from.
* @param buffer Output buffer for the measurement data.
* @param length Length of the output buffer.
*
* @return The amount of data that was written into the output buffer or an error code. Use
* ROT_IS_ERROR to check the return value.
*/
int pcr_store_get_measurement_data (struct pcr_store *store, uint16_t measurement_type,
size_t offset, uint8_t *buffer, size_t length)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
size_t total_len;
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_get_measurement_data (&store->pcrs[pcr_index], measurement_index, offset, buffer,
length, &total_len);
}
/**
* Get the measurement hash for a single measurement. As long as the raw measurement data is
* available, this hash does not need to match the hash used by the PCR. If the raw measurement
* data is not available, the hash algorithm must match the PCR hash algorithm.
*
* @param store The PCR store containing the measurement to hash.
* @param measurement_type Identifier for the measurement to hash.
* @param hash Hash engine to use for calculating the digest, if necessary.
* @param hash_type The hash algorithm that should be used for digest calculation.
* @param buffer Output buffer for the measurement hash.
* @param length Size of the output buffer.
*
* @return Length of the hash that was generated for the measurement or an error code. Use
* ROT_IS_ERROR to check the return value.
*/
int pcr_store_hash_measurement_data (struct pcr_store *store, uint16_t measurement_type,
const struct hash_engine *hash, enum hash_type hash_type, uint8_t *buffer, size_t length)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_hash_measurement_data (&store->pcrs[pcr_index], measurement_index, hash, hash_type,
buffer, length);
}
/**
* Indicate if the requested measurement has access to the raw data that was measured.
*
* @param store The PCR store containing the measurement to query.
* @param measurement_type Identifier for the measurement to query.
*
* @return 1 if the measurement has access to the raw data, 0 if not, or an error code.
*/
int pcr_store_is_measurement_data_available (struct pcr_store *store, uint16_t measurement_type)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
return pcr_is_measurement_data_available (&store->pcrs[pcr_index], measurement_index);
}
/**
* Determine the total length of the measured data for a single measurement.
*
* @param store The PCR store containing the measurement to query.
* @param measurement_type Identifier for the measurement to query.
*
* @return The total length of the measured data or an error code. Use ROT_IS_ERROR to check the
* return value.
*/
int pcr_store_get_measurement_data_length (struct pcr_store *store, uint16_t measurement_type)
{
uint8_t pcr_index = PCR_STORE_PCR_INDEX (measurement_type);
uint8_t measurement_index = PCR_STORE_MEASUREMENT_INDEX (measurement_type);
uint8_t tmp;
size_t total_len;
int status;
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
if (pcr_index >= store->num_pcrs) {
return PCR_INVALID_PCR;
}
status = pcr_get_measurement_data (&store->pcrs[pcr_index], measurement_index, 0, &tmp, 0,
&total_len);
if (status != 0) {
return status;
}
return (total_len == 0) ? PCR_MEASURED_DATA_NOT_AVIALABLE : total_len;
}
/**
* Get the total size of attestation log for the PCR store.
*
* @param store The PCR store to query.
*
* @return Total length of the attestation log or an error code. Use ROT_IS_ERROR to check the
* return value.
*/
int pcr_store_get_attestation_log_size (struct pcr_store *store)
{
size_t log_size = 0;
size_t i;
int count;
if (store == NULL) {
return PCR_INVALID_ARGUMENT;
}
for (i = 0; i < store->num_pcrs; i++) {
count = pcr_get_num_measurements (&store->pcrs[i]);
switch (pcr_get_hash_algorithm (&store->pcrs[i])) {
case HASH_TYPE_SHA256:
log_size += (count * sizeof (struct pcr_store_attestation_log_entry_sha256));
break;
#if PCR_MAX_DIGEST_LENGTH >= SHA384_HASH_LENGTH
case HASH_TYPE_SHA384:
log_size += (count * sizeof (struct pcr_store_attestation_log_entry_sha384));
break;
#endif
#if PCR_MAX_DIGEST_LENGTH >= SHA512_HASH_LENGTH
case HASH_TYPE_SHA512:
log_size += (count * sizeof (struct pcr_store_attestation_log_entry_sha512));
break;
#endif
default:
/* Not possible. */
break;
}
}
return log_size;
}
/**
* Read the attestation log from the PCR store, which will cause the current value of all PCRs to be
* calculated.
*
* Only data that will fit into the provided buffer will be returned. Additional calls with
* different length/offset values would be needed to get the remaining data.
*
* @param store The PCR store to query for log data.
* @param hash Hashing engine to use for PCR calculations.
* @param offset Offset within the log to start reading data.
* @param contents Output buffer for the log contents.
* @param length Maximum number of bytes to read from the log.
*
* @return The number of bytes read from the log or an error code.
*/
int pcr_store_get_attestation_log (struct pcr_store *store, const struct hash_engine *hash,
size_t offset, uint8_t *contents, size_t length)
{
union {
struct pcr_store_attestation_log_entry_base base;
struct pcr_store_attestation_log_entry_sha256 sha256;
struct pcr_store_attestation_log_entry_sha384 sha384;
struct pcr_store_attestation_log_entry_sha512 sha512;
} log_entry;
const struct pcr_measurement *measurements;
uint8_t *digest;
uint32_t *measurement_size;
uint8_t *measurement;
uint32_t entry_length;
uint16_t algorithm_id;
uint32_t entry_id = 0;
size_t entry_bytes;
size_t total_bytes = 0;
size_t pcr;
int digest_length;
int num_measurements;
int i;
int status;
if ((store == NULL) || (hash == NULL) || (contents == NULL)) {
return PCR_INVALID_ARGUMENT;
}
pcr = 0;
while ((pcr < store->num_pcrs) && (length > 0)) {
num_measurements = pcr_get_num_measurements (&store->pcrs[pcr]);
if (num_measurements == 0) {
pcr++;
continue;
}
status = pcr_lock (&store->pcrs[pcr]);
if (status != 0) {
return status;
}
status = pcr_compute (&store->pcrs[pcr], hash, false, NULL, 0);
if (ROT_IS_ERROR (status)) {
pcr_unlock (&store->pcrs[pcr]);
return status;
}
digest_length = pcr_get_all_measurements (&store->pcrs[pcr], &measurements);
switch (digest_length) {
default:
/* This isn't possible under normal conditions since invalid digest lengths would be
* rejected during init. Fall through to SHA-256 in unexpected scenarios.
* In unexpected scenarios digest_length returns with PCR_INVALID_ARGUMENT or HASH_ENGINE_UNKNOWN_HASH,
* to avoid undefined behavior, assigning the digest_length with SHA256_HASH_LENGTH */
case SHA256_HASH_LENGTH:
digest = log_entry.sha256.entry.digest;
measurement_size = &log_entry.sha256.entry.measurement_size;
measurement = log_entry.sha256.entry.measurement;
entry_length = sizeof (log_entry.sha256);
algorithm_id = PCR_TCG_SHA256_ALG_ID;
digest_length = SHA256_HASH_LENGTH;
break;
#if PCR_MAX_DIGEST_LENGTH >= SHA384_HASH_LENGTH
case SHA384_HASH_LENGTH:
digest = log_entry.sha384.entry.digest;
measurement_size = &log_entry.sha384.entry.measurement_size;
measurement = log_entry.sha384.entry.measurement;
entry_length = sizeof (log_entry.sha384);
algorithm_id = PCR_TCG_SHA384_ALG_ID;
break;
#endif
#if PCR_MAX_DIGEST_LENGTH >= SHA512_HASH_LENGTH
case SHA512_HASH_LENGTH:
digest = log_entry.sha512.entry.digest;
measurement_size = &log_entry.sha512.entry.measurement_size;
measurement = log_entry.sha512.entry.measurement;
entry_length = sizeof (log_entry.sha512);
algorithm_id = PCR_TCG_SHA512_ALG_ID;
break;
#endif
}
i = 0;
while ((i < num_measurements) && (length > 0)) {
log_entry.base.header.log_magic = LOGGING_MAGIC_START;
log_entry.base.header.length = entry_length;
log_entry.base.header.entry_id = entry_id++;
log_entry.base.info.digest_algorithm_id = algorithm_id;
log_entry.base.info.digest_count = 1;
log_entry.base.info.event_type = measurements[i].event_type;
log_entry.base.info.measurement_type = PCR_MEASUREMENT (pcr, i);
*measurement_size = digest_length;
memcpy (digest, measurements[i].digest, digest_length);
memcpy (measurement, measurements[i].measurement, digest_length);
entry_bytes = buffer_copy ((uint8_t*) &log_entry, entry_length, &offset, &length,
contents);
contents += entry_bytes;
total_bytes += entry_bytes;
i++;
}
pcr_unlock (&store->pcrs[pcr]);
pcr++;
}
return total_bytes;
}
/**
* Generate TCG formatted log for all measurements in the PCR store.
*
* Only data that will fit into the provided buffer will be returned. Additional calls with
* different length/offset values would be needed to get the remaining data.
*
* @param store The PCR store to query for log data.
* @param offset Offset within the log to start reading data.
* @param buffer Output buffer to populate with requested log contents.
* @param length Maximum number of bytes to read from the log.
*
* @return The number of bytes read from the log or an error code.
*/
int pcr_store_get_tcg_log (struct pcr_store *store, size_t offset, uint8_t *buffer, size_t length)
{
struct pcr_tcg_event v1_event;
struct pcr_tcg_log_header header;
size_t header_length;
uint8_t algo_added = 0;
size_t num_bytes = 0;
size_t v1_length;
size_t measurement_length;
size_t i;
int status;
if ((store == NULL) || (buffer == NULL)) {
return PCR_INVALID_ARGUMENT;
}
/* Construct the TCG log header. The structure is variable length, depending on the total
* number of hash algorithms supported. Clearing the entire structure will ensure
* vendor_info_size is always 0. */
memset (&header, 0, sizeof (header));
memcpy (header.signature, PCR_TCG_LOG_SIGNATURE, sizeof (header.signature));
header.signature[15] = '\0';
header.platform_class = PCR_TCG_SERVER_PLATFORM_CLASS;
header.spec_version_minor = 0;
header.spec_version_major = 2;
header.spec_errata = 0;
header.uintn_size = PCR_TCG_UINT_SIZE_32;
/* Determine the set of unique hash algorithms used by all PCRs being managed. */
for (i = 0; i < store->num_pcrs; i++) {
switch (pcr_get_hash_algorithm (&store->pcrs[i])) {
case HASH_TYPE_SHA256:
if (!(algo_added & 0x1)) {
header.digest_size[header.num_algorithms].digest_algorithm_id =
PCR_TCG_SHA256_ALG_ID;
header.digest_size[header.num_algorithms++].digest_size = SHA256_HASH_LENGTH;
algo_added |= 0x1;
}
break;
#if PCR_MAX_DIGEST_LENGTH >= SHA384_HASH_LENGTH
case HASH_TYPE_SHA384:
if (!(algo_added & 0x2)) {
header.digest_size[header.num_algorithms].digest_algorithm_id =
PCR_TCG_SHA384_ALG_ID;
header.digest_size[header.num_algorithms++].digest_size = SHA384_HASH_LENGTH;
algo_added |= 0x2;
}
break;
#endif
#if PCR_MAX_DIGEST_LENGTH >= SHA512_HASH_LENGTH
case HASH_TYPE_SHA512:
if (!(algo_added & 0x4)) {
header.digest_size[header.num_algorithms].digest_algorithm_id =
PCR_TCG_SHA512_ALG_ID;
header.digest_size[header.num_algorithms++].digest_size = SHA512_HASH_LENGTH;
algo_added |= 0x4;
}
break;
#endif
default:
/* Not possible. */
break;
}
}
header_length = sizeof (struct pcr_tcg_log_header) - sizeof (header.digest_size) +
(sizeof (header.digest_size[0]) * header.num_algorithms);
/* Now that the header event length is known, construct the v1 event header for it. */
v1_event.event_type = PCR_TCG_EFI_NO_ACTION_EVENT_TYPE;
v1_event.event_size = header_length;
v1_event.pcr_index = 0;
memset (v1_event.digest, 0, sizeof (v1_event.digest));
/* Add the v1 event header to the output buffer. */
v1_length = buffer_copy ((uint8_t*) &v1_event, sizeof (v1_event), &offset, &length, buffer);
num_bytes += v1_length;
buffer += v1_length;
/* Add the TCG log header to the v1 event. */
v1_length = buffer_copy ((uint8_t*) &header, header_length, &offset, &length, buffer);
num_bytes += v1_length;
buffer += v1_length;
/* Add measurements for each PCR. */
i = 0;
while ((i < store->num_pcrs) && (length > 0)) {
status = pcr_get_tcg_log (&store->pcrs[i], i, offset, buffer, length, &measurement_length);
if (ROT_IS_ERROR (status)) {
return status;
}
if (status == 0) {
offset -= measurement_length;
}
else {
num_bytes += status;
buffer += status;
length -= status;
offset = 0;
}
i++;
}
return num_bytes;
}