int spdm_get_measurements()

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;
}