in core/spdm/spdm_commands.c [3255:3523]
int spdm_get_measurements (const struct cmd_interface_spdm_responder *spdm_responder,
struct cmd_interface_msg *request)
{
int status;
int spdm_error;
const struct spdm_get_measurements_request *spdm_request;
struct spdm_get_measurements_response *spdm_response;
uint8_t spdm_version;
const struct spdm_transcript_manager *transcript_manager;
struct spdm_state *state;
const struct spdm_device_capability *local_capabilities;
const struct hash_engine *hash_engine;
enum hash_type hash_type;
size_t signature_size;
size_t request_size;
size_t response_size;
const struct spdm_measurements *measurements;
const struct rng_engine *rng_engine;
uint8_t measurement_operation;
int measurement_count;
int measurement_length;
bool raw_bit_stream_requested;
bool signature_requested;
struct spdm_secure_session_manager *session_manager;
struct spdm_secure_session *session = NULL;
uint8_t session_idx = SPDM_MAX_SESSION_COUNT;
if ((spdm_responder == NULL) || (request == NULL)) {
return CMD_HANDLER_SPDM_RESPONDER_INVALID_ARGUMENT;
}
transcript_manager = spdm_responder->transcript_manager;
state = spdm_responder->state;
local_capabilities = spdm_responder->local_capabilities;
hash_engine = spdm_responder->hash_engine[0];
hash_type = spdm_get_hash_type (state->connection_info.peer_algorithms.base_hash_algo);
measurements = spdm_responder->measurements;
rng_engine = spdm_responder->rng_engine;
session_manager = spdm_responder->session_manager;
/* Check if a session is ongoing. */
if ((session_manager != NULL) &&
(session_manager->is_last_session_id_valid (session_manager) == true)) {
session = session_manager->get_session (session_manager,
session_manager->get_last_session_id (session_manager));
if (session == NULL) {
/* Don't reset the L1L2 hash context in this failure case as the correct session context
* is not known. This behavior is per libSPDM. */
spdm_generate_error_response (request, state->connection_info.version.minor_version,
SPDM_ERROR_UNEXPECTED_REQUEST, 0x00, NULL, 0, SPDM_REQUEST_GET_MEASUREMENTS,
CMD_HANDLER_SPDM_RESPONDER_UNEXPECTED_REQUEST);
return 0;
}
session_idx = session->session_index;
/* Check session state. */
if (session->session_state != SPDM_SESSION_STATE_ESTABLISHED) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_SESSION_STATE;
spdm_error = SPDM_ERROR_UNEXPECTED_REQUEST;
goto exit;
}
}
/* Validate the request. */
if (request->payload_length < sizeof (struct spdm_get_measurements_request)) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
spdm_error = SPDM_ERROR_INVALID_REQUEST;
goto exit;
}
request_size = sizeof (struct spdm_get_measurements_request);
spdm_request = (struct spdm_get_measurements_request*) request->payload;
spdm_version = SPDM_MAKE_VERSION (spdm_request->header.spdm_major_version,
spdm_request->header.spdm_minor_version);
if (spdm_version != spdm_get_connection_version (state)) {
status = CMD_HANDLER_SPDM_RESPONDER_VERSION_MISMATCH;
spdm_error = SPDM_ERROR_VERSION_MISMATCH;
goto exit;
}
/* Verify SPDM state. */
if (state->response_state != SPDM_RESPONSE_STATE_NORMAL) {
/* [TODO] Handle RESPOND_IF_READY condition when supported. */
spdm_handle_response_state (state, &spdm_error);
status = CMD_HANDLER_SPDM_RESPONDER_INTERNAL_ERROR;
goto exit;
}
if (state->connection_info.connection_state < SPDM_CONNECTION_STATE_NEGOTIATED) {
status = CMD_HANDLER_SPDM_RESPONDER_UNEXPECTED_REQUEST;
spdm_error = SPDM_ERROR_UNEXPECTED_REQUEST;
goto exit;
}
/* Check if the measurement capability is supported. */
if (local_capabilities->flags.meas_cap == 0) {
status = CMD_HANDLER_SPDM_RESPONDER_UNSUPPORTED_CAPABILITY;
spdm_error = SPDM_ERROR_UNSUPPORTED_REQUEST;
goto exit;
}
/* Check if the negotiated parameters for measurement are valid. */
if ((state->connection_info.peer_algorithms.measurement_spec == 0) ||
(state->connection_info.peer_algorithms.measurement_hash_algo == 0)) {
status = CMD_HANDLER_SPDM_RESPONDER_UNEXPECTED_REQUEST;
spdm_error = SPDM_ERROR_UNEXPECTED_REQUEST;
goto exit;
}
/* If signature generation is requested, check if responder has the support for it. */
signature_requested = spdm_request->sig_required;
if (signature_requested == true) {
if (local_capabilities->flags.meas_cap != SPDM_MEAS_CAP_WITH_SIG) {
status = CMD_HANDLER_SPDM_RESPONDER_UNSUPPORTED_CAPABILITY;
spdm_error = SPDM_ERROR_UNSUPPORTED_REQUEST;
goto exit;
}
request_size += (SPDM_NONCE_LEN + sizeof (uint8_t));
if (request->payload_length < request_size) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
spdm_error = SPDM_ERROR_INVALID_REQUEST;
goto exit;
}
/* Check if slot id is valid. */
if (*(spdm_get_measurements_rq_slot_id_ptr (spdm_request)) != 0) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
spdm_error = SPDM_ERROR_INVALID_REQUEST;
goto exit;
}
signature_size =
spdm_get_asym_signature_size (state->connection_info.peer_algorithms.base_asym_algo);
}
else {
signature_size = 0;
}
/* Check if sufficient buffer is available for the response including the optional signature. */
response_size = SPDM_GET_MEASUREMENTS_RESP_MIN_LENGTH + signature_size;
if (cmd_interface_msg_get_max_response (request) < response_size) {
status = CMD_HANDLER_SPDM_RESPONDER_RESPONSE_TOO_LARGE;
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
raw_bit_stream_requested = spdm_request->raw_bit_stream_requested;
measurement_operation = spdm_request->measurement_operation;
/* Reset transcript manager state as per request code. */
spdm_reset_transcript_via_request_code (state, transcript_manager,
SPDM_REQUEST_GET_MEASUREMENTS);
/* Add request to L1L2 hash context. */
status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_L1L2,
request->payload, request_size, (session != NULL), session_idx);
if (status != 0) {
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
/* Construct the response. */
spdm_response = (struct spdm_get_measurements_response*) request->payload;
memset (spdm_response, 0, response_size);
spdm_populate_header (&spdm_response->header, SPDM_RESPONSE_GET_MEASUREMENTS,
SPDM_GET_MINOR_VERSION (spdm_version));
/* Get the total number of measurement(s) available on the device. */
measurement_count = measurements->get_measurement_count (measurements);
if (ROT_IS_ERROR (measurement_count)) {
status = measurement_count;
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
switch (measurement_operation) {
case SPDM_GET_MEASUREMENTS_REQUEST_MEASUREMENT_OPERATION_TOTAL_NUMBER_OF_MEASUREMENTS:
spdm_response->num_measurement_indices = measurement_count;
break;
case SPDM_GET_MEASUREMENTS_REQUEST_MEASUREMENT_OPERATION_ALL_MEASUREMENTS:
/* Get the measurement record. */
measurement_length = measurements->get_all_measurement_blocks (measurements,
raw_bit_stream_requested, hash_engine, hash_type,
spdm_get_measurements_resp_measurement_record (spdm_response),
(cmd_interface_msg_get_max_response (request) - response_size));
if (ROT_IS_ERROR (measurement_length)) {
status = measurement_length;
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
response_size += measurement_length;
spdm_response->number_of_blocks = (uint8_t) measurement_count;
buffer_unaligned_write24 (spdm_response->measurement_record_len,
(uint32_t) measurement_length);
break;
default:
/* Get a single measurement. */
measurement_length = measurements->get_measurement_block (measurements,
measurement_operation, raw_bit_stream_requested, hash_engine, hash_type,
spdm_get_measurements_resp_measurement_record (spdm_response),
(cmd_interface_msg_get_max_response (request) - response_size));
if (ROT_IS_ERROR (measurement_length)) {
status = measurement_length;
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
response_size += measurement_length;
spdm_response->number_of_blocks = 1;
buffer_unaligned_write24 (spdm_response->measurement_record_len,
(uint32_t) measurement_length);
break;
}
/* Add the random nonce. */
status = rng_engine->generate_random_buffer (rng_engine, SPDM_NONCE_LEN,
spdm_get_measurements_resp_nonce (spdm_response));
if (status != 0) {
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
/* Set opaque data size to 0 */
buffer_unaligned_write16 ((uint16_t*) (spdm_get_measurements_resp_nonce (spdm_response) +
SPDM_NONCE_LEN), 0);
/* Add response to L1L2 hash context. Signature is not included in the hash. */
status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_L1L2,
(const uint8_t*) spdm_response, response_size - signature_size, (session != NULL),
session_idx);
if (status != 0) {
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
/* Generate the signature, if requested. */
if (signature_requested == true) {
status = spdm_generate_measurement_signature (transcript_manager, state,
spdm_responder->key_manager, spdm_responder->ecc_engine, hash_engine, session,
spdm_get_measurements_resp_signature (spdm_response), signature_size);
if (status != 0) {
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
}
/* Set the payload length. */
cmd_interface_msg_set_message_payload_length (request, response_size);
exit:
if (status != 0) {
/* Reset L1L2 hash context on error. */
transcript_manager->reset_transcript (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_L1L2,
(session != NULL), session_idx);
spdm_generate_error_response (request, state->connection_info.version.minor_version,
spdm_error, 0x00, NULL, 0, SPDM_REQUEST_GET_MEASUREMENTS, status);
}
return 0;
}