int spdm_negotiate_algorithms()

in core/spdm/spdm_commands.c [2171:2351]


int spdm_negotiate_algorithms (const struct cmd_interface_spdm_responder *spdm_responder,
	struct cmd_interface_msg *request)
{
	int status;
	int spdm_error;
	struct spdm_protocol_header *header;
	struct spdm_negotiate_algorithms_request *rq;
	struct spdm_negotiate_algorithms_response_no_ext_alg resp_no_ext_alg;
	struct spdm_algorithm_request *algstruct_table;
	size_t i_algstruct;
	uint8_t spdm_version;
	uint8_t alg_type_pre;
	uint16_t ext_alg_total_count = 0;
	size_t request_size;
	const struct spdm_transcript_manager *transcript_manager;
	struct spdm_state *state;
	const struct spdm_device_capability *local_capabilities;
	const struct spdm_local_device_algorithms *local_algorithms;

	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;
	local_algorithms = spdm_responder->local_algorithms;

	/* Validate the request. */
	header = (struct spdm_protocol_header*) request->payload;
	spdm_version = SPDM_MAKE_VERSION (header->spdm_major_version, 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 the 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_AFTER_CAPABILITIES) {
		status = CMD_HANDLER_SPDM_RESPONDER_UNEXPECTED_REQUEST;
		spdm_error = SPDM_ERROR_UNEXPECTED_REQUEST;
		goto exit;
	}

	/* Check request size. */
	rq = (struct spdm_negotiate_algorithms_request*) request->payload;
	if ((request->payload_length < sizeof (struct spdm_negotiate_algorithms_request)) ||
		(request->payload_length < spdm_negotiate_algorithms_min_req_length (rq))) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
		spdm_error = SPDM_ERROR_INVALID_REQUEST;
		goto exit;
	}

	if (rq->length > SPDM_NEGOTIATE_ALGORITHMS_REQUEST_MAX_LENGTH) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
		spdm_error = SPDM_ERROR_INVALID_REQUEST;
		goto exit;
	}

	/* Validate the algorithm structs. */
	algstruct_table = spdm_negotiate_algorithms_req_algstruct_table (rq);
	for (i_algstruct = 0; i_algstruct < rq->num_alg_structure_tables; i_algstruct++) {
		/* Check if alg_type is valid. */
		if ((algstruct_table->alg_type < SPDM_ALG_REQ_STRUCT_ALG_TYPE_DHE) ||
			(algstruct_table->alg_type > SPDM_ALG_REQ_STRUCT_ALG_TYPE_KEY_SCHEDULE)) {
			status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
			spdm_error = SPDM_ERROR_INVALID_REQUEST;
			goto exit;
		}

		/* Check if alg_type is monotonically increasing for subsequent entries. */
		if ((i_algstruct != 0) && (algstruct_table->alg_type <= alg_type_pre)) {
			status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
			spdm_error = SPDM_ERROR_INVALID_REQUEST;
			goto exit;
		}
		alg_type_pre = algstruct_table->alg_type;
		ext_alg_total_count += algstruct_table->ext_alg_count;
		if (algstruct_table->fixed_alg_count != 2) {
			status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
			spdm_error = SPDM_ERROR_INVALID_REQUEST;
			goto exit;
		}

		/* Check if payload contains the extended algorithm(s) in the algstruct_table entry. */
		if (spdm_negotiate_algorithms_actual_extended_algo_size (rq, algstruct_table) <
			spdm_negotiate_algorithms_expected_extended_algo_size (algstruct_table)) {
			status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
			spdm_error = SPDM_ERROR_INVALID_REQUEST;
			goto exit;
		}

		/* Go to the next algstruct_table entry. */
		algstruct_table =
			spdm_negotiate_algorithms_get_next_alg_struct_table_entry (algstruct_table);
	}
	ext_alg_total_count += (rq->ext_asym_count + rq->ext_hash_count);

	/* Check the algorithm count and message size. */
	if (ext_alg_total_count > SPDM_NEGOTIATE_ALGORITHMS_REQUEST_MAX_EXT_ALG_COUNT_VERSION) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
		spdm_error = SPDM_ERROR_INVALID_REQUEST;
		goto exit;
	}

	/* Check Opaque Data Format. */
	if (spdm_version >= SPDM_VERSION_1_2) {
		/* Opaque data format is 4 bits, [1:0] are used to communicate requestor supported
		 * formats and could be any combination of the bits, [3:2] are reserved  */
		if ((rq->other_params_support.opaque_data_format >> 2) != 0) {
			status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
			spdm_error = SPDM_ERROR_INVALID_REQUEST;
			goto exit;
		}
	}

	request_size = (size_t) algstruct_table - (size_t) rq;
	if (request_size != rq->length) {
		status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
		spdm_error = SPDM_ERROR_INVALID_REQUEST;
		goto exit;
	}

	/* Construct the response. */
	status = spdm_negotiate_algorithms_construct_response (state, local_capabilities,
		local_algorithms, rq, &resp_no_ext_alg, &spdm_error);
	if (status != 0) {
		goto exit;
	}

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

	/* Append the request to the VCA buffer. */
	status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_VCA,
		(const uint8_t*) rq, request_size, false, SPDM_MAX_SESSION_COUNT);
	if (status != 0) {
		spdm_error = SPDM_ERROR_UNSPECIFIED;
		goto exit;
	}

	/* Append the response to the VCA buffer. */
	status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_VCA,
		(const uint8_t*) &resp_no_ext_alg, resp_no_ext_alg.base.length, false,
		SPDM_MAX_SESSION_COUNT);
	if (status != 0) {
		spdm_error = SPDM_ERROR_UNSPECIFIED;
		goto exit;
	}

	/* Set the negotiated hash algorithm on the Transcript Manager. */
	if (state->connection_info.peer_algorithms.base_hash_algo != 0) {
		status = transcript_manager->set_hash_algo (transcript_manager,
			spdm_get_hash_type (state->connection_info.peer_algorithms.base_hash_algo));
		if (status != 0) {
			spdm_error = SPDM_ERROR_UNSPECIFIED;
			goto exit;
		}
	}

	/* Copy response in the payload buffer. */
	memcpy (request->payload, &resp_no_ext_alg, resp_no_ext_alg.base.length);
	cmd_interface_msg_set_message_payload_length (request, resp_no_ext_alg.base.length);

	/* Update the connection state */
	spdm_set_connection_state (state, SPDM_CONNECTION_STATE_NEGOTIATED);

exit:
	if (status != 0) {
		spdm_generate_error_response (request, state->connection_info.version.minor_version,
			spdm_error, 0x00, NULL, 0, SPDM_REQUEST_NEGOTIATE_ALGORITHMS, status);
	}

	return 0;
}