core/spdm/cmd_interface_spdm_responder.c (233 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "cmd_interface_spdm_responder.h"
#include "spdm_commands.h"
#include "spdm_secure_session_manager.h"
#include "common/unused.h"
/**
* Process an SPDM protocol message.
*
* @param intf SPDM command responder interface.
* @param request SPDM request message.
*
* @return 0 if the message was successfully processed or an error code.
*/
int cmd_interface_spdm_process_request (const struct cmd_interface *intf,
struct cmd_interface_msg *request)
{
const struct cmd_interface_spdm_responder *spdm_responder =
(const struct cmd_interface_spdm_responder*) intf;
uint8_t req_code;
int status = 0;
struct spdm_secure_session_manager *session_manager;
if ((spdm_responder == NULL) || (request == NULL)) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_ARGUMENT;
goto exit;
}
session_manager = spdm_responder->session_manager;
/* Reset the validity of the last session id. */
if (session_manager != NULL) {
session_manager->reset_last_session_id_validity (session_manager);
}
/* If the request is secure, decode it. */
if (request->is_encrypted == true) {
if (session_manager == NULL) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
goto exit;
}
status = session_manager->decode_secure_message (session_manager, request);
if (status != 0) {
/* Note: Response is not being encoded in case of a decode failure. */
spdm_generate_error_response (request, 0, SPDM_ERROR_DECRYPT_ERROR, 0x00, NULL, 0, 0,
status);
status = 0;
goto exit;
}
}
/* Pre-process the request and get the command Id. */
status = spdm_get_command_id (request, &req_code);
if (status != 0) {
goto exit;
}
switch (req_code) {
case SPDM_REQUEST_GET_VERSION:
status = spdm_get_version (spdm_responder, request);
break;
case SPDM_REQUEST_GET_CAPABILITIES:
status = spdm_get_capabilities (spdm_responder, request);
break;
case SPDM_REQUEST_NEGOTIATE_ALGORITHMS:
status = spdm_negotiate_algorithms (spdm_responder, request);
break;
case SPDM_REQUEST_GET_DIGESTS:
status = spdm_get_digests (spdm_responder, request);
break;
case SPDM_REQUEST_GET_CERTIFICATE:
status = spdm_get_certificate (spdm_responder, request);
break;
case SPDM_REQUEST_CHALLENGE:
status = spdm_challenge (spdm_responder, request);
break;
case SPDM_REQUEST_GET_MEASUREMENTS:
status = spdm_get_measurements (spdm_responder, request);
break;
case SPDM_REQUEST_KEY_EXCHANGE:
status = spdm_key_exchange (spdm_responder, request);
break;
case SPDM_REQUEST_FINISH:
status = spdm_finish (spdm_responder, request);
break;
case SPDM_REQUEST_END_SESSION:
status = spdm_end_session (spdm_responder, request);
break;
case SPDM_REQUEST_VENDOR_DEFINED_REQUEST:
status = spdm_vendor_defined_request (spdm_responder, request);
break;
default:
spdm_generate_error_response (request, 0, SPDM_ERROR_UNSUPPORTED_REQUEST, 0x00, NULL, 0,
req_code, CMD_HANDLER_SPDM_RESPONDER_UNSUPPORTED_OPERATION);
break;
}
if ((status == 0) && (request->is_encrypted == true)) {
/* If the request was encoded and was succesfully decoded, encode the response. */
status = session_manager->encode_secure_message (session_manager, request);
if (status != 0) {
/**
* Note: An error of SPDM_ERROR_DECRYPT_ERROR is being sent to the requester to
* to terminate the session. */
spdm_generate_error_response (request, 0, SPDM_ERROR_DECRYPT_ERROR, 0x00, NULL, 0,
req_code, status);
status = 0;
goto exit;
}
}
exit:
return status;
}
/**
* Process an SPDM protocol response.
*
* @param intf SPDM command responder interface.
* @param response SPDM response message.
*
* @return 0 if the message was successfully processed or an error code.
*/
#ifdef CMD_ENABLE_ISSUE_REQUEST
int cmd_interface_spdm_process_response (const struct cmd_interface *intf,
struct cmd_interface_msg *response)
{
UNUSED (intf);
UNUSED (response);
return CMD_HANDLER_SPDM_RESPONDER_UNSUPPORTED_OPERATION;
}
#endif
/**
* Initialize the SPDM responder instance.
*
* @param spdm_responder SPDM responder instance.
* @param state SPDM state.
* @param transcript_manager SPDM transcript manager.
* @param hash_engine Hash engine instances.
* @param hash_engine_count Number of hash engine instances
* @param version_num Supported SPDM version number entries.
* @param version_num_count Number of version numbers entries.
* @param secure_message_version_num Supported secured message version number entries.
* @param secure_message_version_num_count Number of secured message version number entries.
* @param local_capabilities Local SPDM capabilities.
* @param local_algorithms Local SPDM algorithms.
* @param key_manager RIoT device key manager.
* @param measurements Measurements for the device.
* @param ecc_engine ECC engine instance.
* @param rng_engine RNG engine instance.
*
* @return 0 if the SPDM responder instance was initialized successfully or an error code.
*/
int cmd_interface_spdm_responder_init (struct cmd_interface_spdm_responder *spdm_responder,
struct spdm_state *state, const struct spdm_transcript_manager *transcript_manager,
const struct hash_engine *const *hash_engine, uint8_t hash_engine_count,
const struct spdm_version_num_entry *version_num, uint8_t version_num_count,
const struct spdm_version_num_entry *secure_message_version_num,
uint8_t secure_message_version_num_count,
const struct spdm_device_capability *local_capabilities,
const struct spdm_local_device_algorithms *local_algorithms,
const struct riot_key_manager *key_manager, const struct spdm_measurements *measurements,
const struct ecc_engine *ecc_engine, const struct rng_engine *rng_engine,
struct spdm_secure_session_manager *session_manager, const struct cmd_interface *vdm_handler)
{
int status;
if (spdm_responder == NULL) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_ARGUMENT;
goto exit;
}
memset (spdm_responder, 0, sizeof (struct cmd_interface_spdm_responder));
spdm_responder->state = state;
spdm_responder->hash_engine = hash_engine;
spdm_responder->hash_engine_count = hash_engine_count;
spdm_responder->transcript_manager = transcript_manager;
spdm_responder->version_num = version_num;
spdm_responder->version_num_count = version_num_count;
spdm_responder->secure_message_version_num = secure_message_version_num;
spdm_responder->secure_message_version_num_count = secure_message_version_num_count;
spdm_responder->local_capabilities = local_capabilities;
spdm_responder->local_algorithms = local_algorithms;
spdm_responder->key_manager = key_manager;
spdm_responder->measurements = measurements;
spdm_responder->ecc_engine = ecc_engine;
spdm_responder->rng_engine = rng_engine;
spdm_responder->session_manager = session_manager;
spdm_responder->vdm_handler = vdm_handler;
spdm_responder->base.process_request = cmd_interface_spdm_process_request;
#ifdef CMD_ENABLE_ISSUE_REQUEST
spdm_responder->base.process_response = cmd_interface_spdm_process_response;
#endif
status = cmd_interface_spdm_responder_init_state (spdm_responder);
if (status != 0) {
goto exit;
}
exit:
return status;
}
/**
* Get the maximum supported version from the version number table.
*
* @param version_num Version number table.
* @param version_num_count Number of entries in the version number table.
*
* @return Maximum supported version.
*/
static uint8_t cmd_interface_spdm_responder_get_max_supported_version (
const struct spdm_version_num_entry *version_num, const uint8_t version_num_count)
{
uint8_t max_version = 0;
uint8_t temp_version;
uint8_t i;
for (i = 0; i < version_num_count; i++) {
temp_version =
SPDM_MAKE_VERSION (version_num[i].major_version, version_num[i].minor_version);
if (temp_version > max_version) {
max_version = temp_version;
}
}
return max_version;
}
/**
* Validate the capabilites of the local SPDM device.
*
* @param local_capabilities Local SPDM device capabilities.
* @param supported_max_version Maximum supported SPDM version.
*
* @return 0 if capabilities are valid or an error code.
*/
static int cmd_interface_spdm_responder_validate_local_capabilities (
const struct spdm_device_capability *local_capabilities, uint8_t supported_max_version)
{
int status = 0;
if (spdm_check_request_flag_compatibility (local_capabilities->flags,
supported_max_version) == false) {
status = CMD_HANDLER_SPDM_RESPONDER_INCOMPATIBLE_CAPABILITIES;
goto exit;
}
if (local_capabilities->ct_exponent > SPDM_MAX_CT_EXPONENT) {
status = CMD_HANDLER_SPDM_RESPONDER_UNSUPPORTED_CAPABILITY;
goto exit;
}
if ((local_capabilities->data_transfer_size < SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_1_2) ||
(local_capabilities->data_transfer_size > local_capabilities->max_spdm_msg_size) ||
((local_capabilities->flags.chunk_cap == 0) &&
(local_capabilities->data_transfer_size != local_capabilities->max_spdm_msg_size))) {
status = CMD_HANDLER_SPDM_RESPONDER_UNSUPPORTED_CAPABILITY;
goto exit;
}
exit:
return status;
}
/**
* Initialize the SPDM responder state.
*
* @param spdm_responder SPDM responder instance.
*
* @return 0 if the SPDM responder state was initialized successfully or an error code.
*/
int cmd_interface_spdm_responder_init_state (
const struct cmd_interface_spdm_responder *spdm_responder)
{
int status;
uint8_t supported_max_version;
uint8_t idx;
if ((spdm_responder == NULL) || (spdm_responder->hash_engine == NULL) ||
(spdm_responder->hash_engine_count < SPDM_RESPONDER_HASH_ENGINE_REQUIRED_COUNT) ||
(spdm_responder->transcript_manager == NULL) || (spdm_responder->version_num == NULL) ||
(spdm_responder->version_num_count == 0) ||
(spdm_responder->local_capabilities == NULL) ||
(spdm_responder->local_algorithms == NULL) || (spdm_responder->key_manager == NULL) ||
(spdm_responder->measurements == NULL) || (spdm_responder->ecc_engine == NULL) ||
(spdm_responder->rng_engine == NULL)) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_ARGUMENT;
goto exit;
}
/* Optional objects for secure session support */
if (spdm_responder->secure_message_version_num_count != 0) {
if ((spdm_responder->secure_message_version_num == NULL) ||
(spdm_responder->session_manager == NULL)) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_ARGUMENT;
goto exit;
}
}
/* Check if the hash engine instances are valid. */
for (idx = 0; idx < spdm_responder->hash_engine_count; idx++) {
if (spdm_responder->hash_engine[idx] == NULL) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_ARGUMENT;
goto exit;
}
}
/* Validate the local device capabilities. */
supported_max_version =
cmd_interface_spdm_responder_get_max_supported_version (spdm_responder->version_num,
spdm_responder->version_num_count);
status =
cmd_interface_spdm_responder_validate_local_capabilities (
spdm_responder->local_capabilities, supported_max_version);
if (status != 0) {
goto exit;
}
/* Initialize the SPDM state. */
status = spdm_init_state (spdm_responder->state);
if (status != 0) {
goto exit;
}
exit:
return status;
}
/**
* Deinitialize the SPDM responder instance.
*
* @param intf SPDM responder instance.
*/
void cmd_interface_spdm_responder_deinit (const struct cmd_interface_spdm_responder *spdm_responder)
{
UNUSED (spdm_responder);
}