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