int spdm_finish()

in core/spdm/spdm_commands.c [4056:4242]


int spdm_finish (const struct cmd_interface_spdm_responder *spdm_responder,
	struct cmd_interface_msg *request)
{
	int status;
	int spdm_error;
	uint8_t spdm_version;
	struct spdm_finish_request *spdm_request;
	struct spdm_finish_response *spdm_response;
	size_t response_size;
	uint32_t session_id;
	struct spdm_secure_session *session;
	uint32_t hmac_size;
	uint32_t sig_size = 0;
	struct spdm_state *state;
	const struct spdm_transcript_manager *transcript_manager;
	const struct spdm_device_capability *local_capabilities;
	struct spdm_secure_session_manager *session_manager;
	const uint8_t *hmac_ptr;

	if ((spdm_responder == NULL) || (request == NULL)) {
		return CMD_HANDLER_SPDM_RESPONDER_INVALID_ARGUMENT;
	}

	state = spdm_responder->state;
	transcript_manager = spdm_responder->transcript_manager;
	local_capabilities = spdm_responder->local_capabilities;
	session_manager = spdm_responder->session_manager;

	if (session_manager == NULL) {
		status = CMD_HANDLER_SPDM_RESPONDER_UNEXPECTED_REQUEST;
		spdm_error = SPDM_ERROR_INVALID_REQUEST;
		goto exit;
	}

	/* Validate the request. */
	if (request->payload_length < sizeof (struct spdm_finish_request)) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
		spdm_error = SPDM_ERROR_INVALID_REQUEST;
		goto exit;
	}
	spdm_request = (struct spdm_finish_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)) {
		spdm_error = SPDM_ERROR_VERSION_MISMATCH;
		status = CMD_HANDLER_SPDM_RESPONDER_VERSION_MISMATCH;
		goto exit;
	}

	/* Verify SPDM state. */
	if (state->response_state != SPDM_RESPONSE_STATE_NORMAL) {
		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;
	}

	/* Confirm that we are in a session.*/
	if ((local_capabilities->flags.handshake_in_the_clear_cap == 0) ||
		(state->connection_info.peer_capabilities.flags.handshake_in_the_clear_cap == 0)) {
		if (session_manager->is_last_session_id_valid (session_manager) == false) {
			status = CMD_HANDLER_SPDM_RESPONDER_INVALID_CONNECTION_STATE;
			spdm_error = SPDM_ERROR_SESSION_REQUIRED;
			goto exit;
		}
	}
	else {
		/* [TODO] Check if handshake in clear needs to be supported.*/
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_CONNECTION_STATE;
		spdm_error = SPDM_ERROR_SESSION_REQUIRED;
		goto exit;
	}

	/* Session id is retrieved from the secure session message header. */
	session_id = session_manager->get_last_session_id (session_manager);
	session = session_manager->get_session (session_manager, session_id);
	if (session == NULL) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_CONNECTION_STATE;
		spdm_error = SPDM_ERROR_SESSION_REQUIRED;
		goto exit;
	}

	/* Check session state. */
	if (session->session_state != SPDM_SESSION_STATE_HANDSHAKING) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_CONNECTION_STATE;
		spdm_error = SPDM_ERROR_UNEXPECTED_REQUEST;
		goto exit;
	}

	/* Since mutual auth is not supported, requester should not have included a signature. */
	if ((spdm_request->signature_included &
		SPDM_FINISH_REQUEST_ATTRIBUTES_SIGNATURE_INCLUDED) != 0) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
		spdm_error = SPDM_ERROR_INVALID_REQUEST;
		goto exit;
	}

	hmac_size = session->hash_size;
	sig_size = 0;

	if (request->payload_length <
		(sizeof (struct spdm_finish_request) + sig_size + hmac_size)) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
		spdm_error = SPDM_ERROR_INVALID_REQUEST;
		goto exit;
	}

	/* Reset the transcript manager state as per the request code. */
	spdm_reset_transcript_via_request_code (state, transcript_manager, SPDM_REQUEST_FINISH);

	/* Add the FINISH request (no HMAC) to the TH hash context. */
	status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_TH,
		(const uint8_t*) spdm_request, sizeof (struct spdm_finish_request), true,
		session->session_index);
	if (status != 0) {
		status = CMD_HANDLER_SPDM_RESPONDER_INTERNAL_ERROR;
		spdm_error = SPDM_ERROR_UNSPECIFIED;
		goto exit;
	}

	/* Verify the request HMAC. */
	hmac_ptr = spdm_finish_rq_hmac (spdm_request, sig_size);
	status = spdm_verify_finish_req_hmac (transcript_manager, spdm_responder->hash_engine[0],
		session, hmac_ptr, hmac_size);
	if (status != 0) {
		if ((state->handle_error_return_policy &
			SPDM_DATA_HANDLE_ERROR_RETURN_POLICY_DROP_ON_DECRYPT_ERROR) == 0) {
			spdm_error = SPDM_ERROR_DECRYPT_ERROR;
			goto exit;
		}
		else {
			/* Ignore this message. Don't send a response back. */
			return status;
		}
	}

	/* Add the HMAC from the FINISH request to the TH hash context. */
	status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_TH, hmac_ptr,
		hmac_size, true, session->session_index);
	if (status != 0) {
		status = CMD_HANDLER_SPDM_RESPONDER_INTERNAL_ERROR;
		spdm_error = SPDM_ERROR_UNSPECIFIED;
		goto exit;
	}

	/* Construct the response. Payload buffer is guranteed to be greater than response size. */
	response_size = sizeof (struct spdm_finish_response);
	spdm_response = (struct spdm_finish_response*) request->payload;
	memset (spdm_response, 0, response_size);

	spdm_populate_header (&spdm_response->header, SPDM_RESPONSE_FINISH,
		SPDM_GET_MINOR_VERSION (spdm_version));
	spdm_response->reserved1 = 0;
	spdm_response->reserved2 = 0;

	/* Add the FINISH response to the TH hash context. */
	status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_TH,
		(const uint8_t*) spdm_response, response_size, true, session->session_index);
	if (status != 0) {
		status = CMD_HANDLER_SPDM_RESPONDER_INTERNAL_ERROR;
		spdm_error = SPDM_ERROR_UNSPECIFIED;
		goto exit;
	}

	/* Generate the session data keys. */
	status = session_manager->generate_session_data_keys (session_manager, session);
	if (status != 0) {
		status = CMD_HANDLER_SPDM_RESPONDER_INTERNAL_ERROR;
		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) {
		spdm_generate_error_response (request, state->connection_info.version.minor_version,
			spdm_error, 0x00, NULL, 0, SPDM_REQUEST_FINISH, status);
	}

	return 0;
}