core/cmd_interface/cerberus_protocol_optional_commands.c (737 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "attestation_cmd_interface.h"
#include "cerberus_protocol.h"
#include "cerberus_protocol_master_commands.h"
#include "cerberus_protocol_optional_commands.h"
#include "cerberus_protocol_required_commands.h"
#include "cmd_authorization.h"
#include "cmd_background.h"
#include "cmd_interface.h"
#include "session_manager.h"
#include "attestation/attestation.h"
#include "common/certificate.h"
#include "common/common_math.h"
#include "common/unused.h"
#include "firmware/firmware_update_control.h"
#include "host_fw/host_processor.h"
#include "i2c/i2c_slave_common.h"
#include "logging/debug_log.h"
#include "logging/logging_flash.h"
#include "manifest/manifest_cmd_interface.h"
#include "manifest/pfm/pfm_manager.h"
#include "recovery/recovery_image.h"
/**
* Get PFM interface for a specified PFM location.
*
* @param pfm_mgr PFM manager for the requested port.
* @param region The PFM region to query. 0 for active, 1 for pending.
* @param pfm Output for the PFM.
*
* @return 0 if the operation was successful or an error code.
*/
static int cerberus_protocol_get_curr_pfm (const struct pfm_manager *pfm_mgr, uint8_t region,
const struct pfm **pfm)
{
if (pfm_mgr == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
if (region == 0) {
*pfm = pfm_mgr->get_active_pfm (pfm_mgr);
}
else if (region == 1) {
*pfm = pfm_mgr->get_pending_pfm (pfm_mgr);
}
else {
return CMD_HANDLER_OUT_OF_RANGE;
}
return 0;
}
/**
* Get the recovery image command interface for a specified recovery image.
*
* @param recovery_0 The recovery image command interface instance for port 0.
* @param recovery_1 The recovery image command interface instance for port 1.
* @param port The port to query.
*
* @return The recovery image command interface or null.
*/
const struct recovery_image_cmd_interface* cerberus_protocol_get_recovery_image_cmd_interface (
const struct recovery_image_cmd_interface *recovery_0,
const struct recovery_image_cmd_interface *recovery_1, uint8_t port)
{
if (port == 0) {
return recovery_0;
}
else if (port == 1) {
return recovery_1;
}
return NULL;
}
/**
* Get recovery image manager instance for a specified port.
*
* @param recovery_manager_0 Recovery image manager instance for port 0.
* @param recovery_manager_1 Recovery image manager instance for port 1.
* @param port The recovery image port to query.
*
* @return The recovery image manager instance if a valid manager was found or null.
*/
struct recovery_image_manager* cerberus_protocol_get_recovery_image_manager (
struct recovery_image_manager *recovery_manager_0,
struct recovery_image_manager *recovery_manager_1, uint8_t port)
{
if (port == 0) {
return recovery_manager_0;
}
else if (port == 1) {
return recovery_manager_1;
}
return NULL;
}
/**
* Process FW update init request
*
* @param control Firmware update control instance to utilize
* @param request FW update request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_fw_update_init (const struct firmware_update_control *control,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_prepare_fw_update *rq =
(struct cerberus_protocol_prepare_fw_update*) request->data;
if (request->length != sizeof (struct cerberus_protocol_prepare_fw_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
request->length = 0;
return control->prepare_staging (control, rq->total_size);
}
/**
* Process FW update request
*
* @param control Firmware update control instance to utilize
* @param request FW update request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_fw_update (const struct firmware_update_control *control,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_fw_update *rq = (struct cerberus_protocol_fw_update*) request->data;
int status;
if (request->length < sizeof (struct cerberus_protocol_fw_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
status = control->write_staging (control, &rq->payload,
cerberus_protocol_fw_update_length (request));
request->length = 0;
return status;
}
/**
* Process FW update start request
*
* @param control Firmware update control instance to utilize
* @param request FW update start request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_fw_update_start (const struct firmware_update_control *control,
struct cmd_interface_msg *request)
{
if (request->length != sizeof (struct cerberus_protocol_complete_fw_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
request->length = 0;
return control->start_update (control);
}
/**
* Process log info request
*
* @param pcr_store PCR store instance to utilize
* @param request Log info request to process
*
* @return 0 if request completed successfully or an error code.
*/
int cerberus_protocol_get_log_info (struct pcr_store *pcr_store, struct cmd_interface_msg *request)
{
struct cerberus_protocol_get_log_info_response *rsp =
(struct cerberus_protocol_get_log_info_response*) request->data;
int log_length;
if (request->length != sizeof (struct cerberus_protocol_get_log_info)) {
return CMD_HANDLER_BAD_LENGTH;
}
log_length = debug_log_get_size ();
if (ROT_IS_ERROR (log_length)) {
log_length = 0;
}
rsp->debug_log_length = log_length;
log_length = pcr_store_get_attestation_log_size (pcr_store);
if (ROT_IS_ERROR (log_length)) {
log_length = 0;
}
rsp->attestation_log_length = log_length;
rsp->tamper_log_length = 0;
request->length = sizeof (struct cerberus_protocol_get_log_info_response);
return 0;
}
/**
* Process log read request
*
* @param pcr_store PCR store instance to utilize
* @param hash Hash engine to utilize
* @param request Log read request to process
*
* @return 0 if request completed successfully or an error code.
*/
int cerberus_protocol_log_read (struct pcr_store *pcr_store, const struct hash_engine *hash,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_get_log *rq = (struct cerberus_protocol_get_log*) request->data;
struct cerberus_protocol_get_log_response *rsp =
(struct cerberus_protocol_get_log_response*) request->data;
int log_length;
if (request->length != sizeof (struct cerberus_protocol_get_log)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->log_type == CERBERUS_PROTOCOL_DEBUG_LOG) {
log_length = debug_log_read_contents (rq->offset, cerberus_protocol_log_data (rsp),
CERBERUS_PROTOCOL_MAX_LOG_DATA (request));
}
else if (rq->log_type == CERBERUS_PROTOCOL_ATTESTATION_LOG) {
log_length = pcr_store_get_attestation_log (pcr_store, hash, rq->offset,
cerberus_protocol_log_data (rsp), CERBERUS_PROTOCOL_MAX_LOG_DATA (request));
}
else if (rq->log_type == CERBERUS_PROTOCOL_TCG_LOG) {
log_length = pcr_store_get_tcg_log (pcr_store, rq->offset, cerberus_protocol_log_data (rsp),
CERBERUS_PROTOCOL_MAX_LOG_DATA (request));
}
else {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
if (ROT_IS_ERROR (log_length)) {
return log_length;
}
request->length = cerberus_protocol_get_log_response_length (log_length);
return 0;
}
/**
* Process log clear request
*
* @param background Command background instance to utilize
* @param request Log clear request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_log_clear (const struct cmd_background *background,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_clear_log *rq = (struct cerberus_protocol_clear_log*) request->data;
if (request->length != sizeof (struct cerberus_protocol_clear_log)) {
return CMD_HANDLER_BAD_LENGTH;
}
request->length = 0;
#ifdef CMD_ENABLE_DEBUG_LOG
if (rq->log_type == CERBERUS_PROTOCOL_DEBUG_LOG) {
return background->debug_log_clear (background);
}
else
#endif
if (rq->log_type == CERBERUS_PROTOCOL_ATTESTATION_LOG) {
return 0;
}
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
/**
* Process PFM ID version request
*
* @param pfm PFM to query
* @param request PFM ID request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
static int cerberus_protocol_get_pfm_id_version (const struct pfm *pfm,
struct cmd_interface_msg *request)
{
return cerberus_protocol_get_manifest_id_version (&pfm->base, request);
}
/**
* Process PFM ID platform request
*
* @param pfm PFM to query
* @param request PFM ID request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
static int cerberus_protocol_get_pfm_id_platform (const struct pfm *pfm,
struct cmd_interface_msg *request)
{
return cerberus_protocol_get_manifest_id_platform (&pfm->base, request);
}
/**
* Process PFM ID request
*
* @param pfm_mgr List of PFM managers for all available ports
* @param num_ports Numbers of available ports
* @param request PFM ID request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_get_pfm_id (const struct pfm_manager *pfm_mgr[], uint8_t num_ports,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_get_pfm_id *rq = (struct cerberus_protocol_get_pfm_id*) request->data;
const struct pfm *curr_pfm = NULL;
uint8_t port;
uint8_t id;
int status = 0;
if (request->length == (sizeof (struct cerberus_protocol_get_pfm_id) - sizeof (rq->id))) {
rq->id = 0;
}
else if (request->length != sizeof (struct cerberus_protocol_get_pfm_id)) {
return CMD_HANDLER_BAD_LENGTH;
}
port = rq->port_id;
id = rq->id;
if ((port >= num_ports) || (id > 1)) {
return CMD_HANDLER_OUT_OF_RANGE;
}
status = cerberus_protocol_get_curr_pfm (pfm_mgr[port], rq->region, &curr_pfm);
/* When there's no valid PFM manager, return a success
* with response indicating no valid manifest */
if ((status != 0) && (status != CMD_HANDLER_UNSUPPORTED_INDEX)) {
return status;
}
if (id == 0) {
status = cerberus_protocol_get_pfm_id_version (curr_pfm, request);
}
else {
status = cerberus_protocol_get_pfm_id_platform (curr_pfm, request);
}
if (pfm_mgr[port] != NULL) {
pfm_mgr[port]->free_pfm (pfm_mgr[port], curr_pfm);
}
return status;
}
/**
* Process PFM fw request
*
* @param pfm_mgr List of PFM managers for all available ports
* @param num_ports Numbers of available ports
* @param request PFM supported FW request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_get_pfm_fw (const struct pfm_manager *pfm_mgr[], uint8_t num_ports,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_get_pfm_supported_fw *rq =
(struct cerberus_protocol_get_pfm_supported_fw*) request->data;
struct cerberus_protocol_get_pfm_supported_fw_response *rsp =
(struct cerberus_protocol_get_pfm_supported_fw_response*) request->data;
const struct pfm *curr_pfm = NULL;
size_t offset;
uint32_t port;
char *fw_id = NULL;
int status;
if (request->length < sizeof (struct cerberus_protocol_get_pfm_supported_fw)) {
return CMD_HANDLER_BAD_LENGTH;
}
if ((request->length > sizeof (struct cerberus_protocol_get_pfm_supported_fw)) &&
(request->length != cerberus_protocol_get_pfm_supported_fw_request_length_with_id (rq))) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->port_id >= num_ports) {
return CMD_HANDLER_OUT_OF_RANGE;
}
offset = rq->offset;
port = rq->port_id;
status = cerberus_protocol_get_curr_pfm (pfm_mgr[port], rq->region, &curr_pfm);
if (status != 0) {
if (status == CMD_HANDLER_UNSUPPORTED_INDEX) {
status = 0;
rsp->valid = 0;
rsp->version = 0;
request->length = cerberus_protocol_get_pfm_supported_fw_response_length (0);
goto exit;
}
else {
return status;
}
}
if (curr_pfm != NULL) {
rsp->valid = 1;
status = curr_pfm->base.get_id (&curr_pfm->base, &rsp->version);
if (status != 0) {
goto exit;
}
if ((request->length > sizeof (struct cerberus_protocol_get_pfm_supported_fw)) &&
(cerberus_protocol_get_pfm_supported_fw_id_length (rq) != 0)) {
fw_id = cerberus_protocol_get_pfm_supported_fw_id (rq);
}
status = curr_pfm->buffer_supported_versions (curr_pfm, fw_id, offset,
CERBERUS_PROTOCOL_MAX_PFM_VERSIONS (request), cerberus_protocol_pfm_supported_fw (rsp));
if (ROT_IS_ERROR (status)) {
goto exit;
}
request->length = cerberus_protocol_get_pfm_supported_fw_response_length (status);
status = 0;
}
else {
rsp->valid = 0;
rsp->version = 0;
request->length = cerberus_protocol_get_pfm_supported_fw_response_length (0);
}
exit:
if (pfm_mgr[port] != NULL) {
pfm_mgr[port]->free_pfm (pfm_mgr[port], curr_pfm);
}
return status;
}
/**
* Process PFM update init request
*
* @param pfm_cmd List of PFM command interfaces for all available ports
* @param num_ports Number of available ports
* @param request PFM update init request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_pfm_update_init (const struct manifest_cmd_interface *pfm_cmd[],
uint8_t num_ports, struct cmd_interface_msg *request)
{
struct cerberus_protocol_prepare_pfm_update *rq =
(struct cerberus_protocol_prepare_pfm_update*) request->data;
if (request->length != sizeof (struct cerberus_protocol_prepare_pfm_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->port_id >= num_ports) {
return CMD_HANDLER_OUT_OF_RANGE;
}
if (pfm_cmd[rq->port_id] == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
request->length = 0;
return pfm_cmd[rq->port_id]->prepare_manifest (pfm_cmd[rq->port_id], rq->size);
}
/**
* Process PFM update request
*
* @param pfm_cmd List of PFM command interface for all available ports.
* @param num_ports Number of available ports.
* @param request PFM update request to process.
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_pfm_update (const struct manifest_cmd_interface *pfm_cmd[], uint8_t num_ports,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_pfm_update *rq = (struct cerberus_protocol_pfm_update*) request->data;
int status;
if (request->length < sizeof (struct cerberus_protocol_pfm_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->port_id >= num_ports) {
return CMD_HANDLER_OUT_OF_RANGE;
}
if (pfm_cmd[rq->port_id] == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
status = pfm_cmd[rq->port_id]->store_manifest (pfm_cmd[rq->port_id], &rq->payload,
cerberus_protocol_pfm_update_length (request));
request->length = 0;
return status;
}
/**
* Process PFM update complete request
*
* @param pfm_cmd List of PFM command interface for all available ports.
* @param num_ports Numbers of available ports.
* @param request PFM update complete request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_pfm_update_complete (const struct manifest_cmd_interface *pfm_cmd[],
uint8_t num_ports, struct cmd_interface_msg *request)
{
struct cerberus_protocol_complete_pfm_update *rq =
(struct cerberus_protocol_complete_pfm_update*) request->data;
if (request->length != sizeof (struct cerberus_protocol_complete_pfm_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->port_id >= num_ports) {
return CMD_HANDLER_OUT_OF_RANGE;
}
if (pfm_cmd[rq->port_id] == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
request->length = 0;
return pfm_cmd[rq->port_id]->finish_manifest (pfm_cmd[rq->port_id], rq->activation);
}
/**
* Process get host reset status
*
* @param host_0_ctrl Port 0 host control instance
* @param host_1_ctrl Port 1 host control instance
* @param request Host reset status request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_get_host_reset_status (const struct host_control *host_0_ctrl,
const struct host_control *host_1_ctrl, struct cmd_interface_msg *request)
{
struct cerberus_protocol_get_host_state *rq =
(struct cerberus_protocol_get_host_state*) request->data;
struct cerberus_protocol_get_host_state_response *rsp =
(struct cerberus_protocol_get_host_state_response*) request->data;
const struct host_control *control;
int status;
if (request->length != sizeof (struct cerberus_protocol_get_host_state)) {
return CMD_HANDLER_BAD_LENGTH;
}
switch (rq->port_id) {
case 0:
control = host_0_ctrl;
break;
case 1:
control = host_1_ctrl;
break;
default:
return CMD_HANDLER_OUT_OF_RANGE;
}
if (control == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
status = control->is_processor_in_reset (control);
if (status == 0) {
rsp->reset_status = CERBERUS_PROTOCOL_HOST_RUNNING;
}
else if (status == 1) {
status = control->is_processor_held_in_reset (control);
if (status == 1) {
rsp->reset_status = CERBERUS_PROTOCOL_HOST_HELD_IN_RESET;
}
else if (status == 0) {
rsp->reset_status = CERBERUS_PROTOCOL_HOST_IN_RESET;
}
else {
return status;
}
}
else {
return status;
}
request->length = sizeof (struct cerberus_protocol_get_host_state_response);
return 0;
}
/**
* Process unseal message request
*
* @param background Command background instance to utilize
* @param request Unseal request to process
*
* @return 0 if processing completed successfully or an error code.
*/
int cerberus_protocol_unseal_message (const struct cmd_background *background,
struct cmd_interface_msg *request)
{
#ifdef CMD_ENABLE_UNSEAL
struct cerberus_protocol_message_unseal *rq =
(struct cerberus_protocol_message_unseal*) request->data;
uint8_t *end = request->data + request->length;
int status;
request->crypto_timeout = true;
if (request->length < sizeof (struct cerberus_protocol_message_unseal)) {
return CMD_HANDLER_BAD_LENGTH;
}
if ((rq->hmac_type != CERBERUS_PROTOCOL_UNSEAL_HMAC_SHA256) ||
(rq->seed_type > CERBERUS_PROTOCOL_UNSEAL_SEED_ECDH)) {
return CMD_HANDLER_OUT_OF_RANGE;
}
if ((rq->seed_type == CERBERUS_PROTOCOL_UNSEAL_SEED_RSA) &&
(rq->seed_params.rsa.padding > CERBERUS_PROTOCOL_UNSEAL_RSA_OAEP_SHA256)) {
return CMD_HANDLER_OUT_OF_RANGE;
}
if ((buffer_unaligned_read16 (&rq->seed_length) == 0) ||
(cerberus_protocol_unseal_ciphertext_length_ptr (rq) >= end)) {
return CMD_HANDLER_BAD_LENGTH;
}
if ((buffer_unaligned_read16 ((uint16_t*) cerberus_protocol_unseal_ciphertext_length_ptr (
rq)) == 0) ||
(cerberus_protocol_unseal_hmac_length_ptr (rq) >= end)) {
return CMD_HANDLER_BAD_LENGTH;
}
if ((buffer_unaligned_read16 ((uint16_t*) cerberus_protocol_unseal_hmac_length_ptr (rq)) !=
SHA256_HASH_LENGTH) ||
((uint8_t*) cerberus_protocol_get_unseal_pmr_sealing (rq) >= end)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (((uint8_t*) cerberus_protocol_get_unseal_pmr_sealing (rq) +
sizeof (struct cerberus_protocol_unseal_pmrs)) != end) {
return CMD_HANDLER_BAD_LENGTH;
}
status = background->unseal_start (background, request->data, request->length);
request->length = 0;
return status;
#else
UNUSED (background);
UNUSED (request);
return CMD_HANDLER_UNSUPPORTED_COMMAND;
#endif
}
/**
* Process unseal message result request
*
* @param background Command background instance to utilize
* @param request Unseal result request to process
*
* @return 0 if processing completed successfully or an error code.
*/
int cerberus_protocol_unseal_message_result (const struct cmd_background *background,
struct cmd_interface_msg *request)
{
#ifdef CMD_ENABLE_UNSEAL
struct cerberus_protocol_message_unseal_result_completed_response *rsp =
(struct cerberus_protocol_message_unseal_result_completed_response*) request->data;
size_t max_buf_len;
int status;
if (request->length != sizeof (struct cerberus_protocol_message_unseal_result)) {
return CMD_HANDLER_BAD_LENGTH;
}
max_buf_len = CERBERUS_PROTOCOL_MAX_UNSEAL_KEY_DATA (request);
if (background != NULL) {
status = background->unseal_result (background, &rsp->key, &max_buf_len,
&rsp->unseal_status);
if (ROT_IS_ERROR (status)) {
return status;
}
}
else {
return CMD_HANDLER_UNSUPPORTED_COMMAND;
}
if (rsp->unseal_status == ATTESTATION_CMD_STATUS_SUCCESS) {
rsp->key_length = max_buf_len;
request->length = cerberus_protocol_get_unseal_response_length (max_buf_len);
}
else {
request->length = sizeof (struct cerberus_protocol_message_unseal_result_response);
}
return 0;
#else
UNUSED (background);
UNUSED (request);
return CMD_HANDLER_UNSUPPORTED_COMMAND;
#endif
}
/**
* Process a request to reset the device configuration.
*
* @param cmd_auth Command authorization instance to utilize
* @param background Command background instance to utilize
* @param request Reset configuration request to process
*
* @return 0 if processing completed successfully or an error code.
*/
int cerberus_protocol_reset_config (const struct cmd_authorization *cmd_auth,
const struct cmd_background *background, struct cmd_interface_msg *request)
{
#ifdef CMD_ENABLE_RESET_CONFIG
struct cerberus_protocol_reset_config *rq =
(struct cerberus_protocol_reset_config*) request->data;
struct cerberus_protocol_reset_config_response *rsp =
(struct cerberus_protocol_reset_config_response*) request->data;
const struct authorized_execution *execution;
const uint8_t *nonce = NULL;
size_t length;
int status;
request->crypto_timeout = true;
if (request->length < sizeof (struct cerberus_protocol_reset_config)) {
return CMD_HANDLER_BAD_LENGTH;
}
length = cerberus_protocol_reset_authorization_length (request);
if (length != 0) {
nonce = cerberus_protocol_reset_authorization (rq);
}
status = cmd_auth->authorize_operation (cmd_auth, rq->type, &nonce, &length, &execution);
switch (status) {
case 0:
if (execution != NULL) {
status = background->execute_authorized_operation (background, execution);
request->length = 0;
}
else {
status = CMD_HANDLER_UNSUPPORTED_OPERATION;
}
break;
case AUTHORIZATION_CHALLENGE:
if (length > CERBERUS_PROTOCOL_MAX_AUTHORIZATION_DATA (request)) {
return CMD_HANDLER_BUF_TOO_SMALL;
}
memcpy (cerberus_protocol_reset_authorization (rsp), nonce, length);
request->length = cerberus_protocol_get_reset_config_response_length (length);
status = 0;
break;
case CMD_AUTHORIZATION_UNSUPPORTED_OP:
status = CMD_HANDLER_UNSUPPORTED_INDEX;
break;
}
return status;
#else
UNUSED (background);
UNUSED (request);
UNUSED (cmd_auth);
return CMD_HANDLER_UNSUPPORTED_COMMAND;
#endif
}
/**
* Process a prepare recovery image request.
*
* @param recovery_0 Recovery image update command interface instance for port 0.
* @param recovery_1 Recovery image update command interface instance for port 1.
* @param request Recovery image prepare request to process.
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_prepare_recovery_image (const struct recovery_image_cmd_interface *recovery_0,
const struct recovery_image_cmd_interface *recovery_1, struct cmd_interface_msg *request)
{
struct cerberus_protocol_prepare_recovery_image_update *rq =
(struct cerberus_protocol_prepare_recovery_image_update*) request->data;
const struct recovery_image_cmd_interface *recovery_interface;
if (request->length != sizeof (struct cerberus_protocol_prepare_recovery_image_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->port_id > 1) {
return CMD_HANDLER_OUT_OF_RANGE;
}
recovery_interface = cerberus_protocol_get_recovery_image_cmd_interface (recovery_0, recovery_1,
rq->port_id);
if (recovery_interface == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
request->length = 0;
return recovery_interface->prepare_recovery_image (recovery_interface, rq->size);
}
/**
* Process an update recovery image request.
*
* @param recovery_0 Recovery image update command interface instance for port 0.
* @param recovery_1 Recovery image update command interface instance for port 1.
* @param request Recovery image update request to process.
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_update_recovery_image (const struct recovery_image_cmd_interface *recovery_0,
const struct recovery_image_cmd_interface *recovery_1, struct cmd_interface_msg *request)
{
struct cerberus_protocol_recovery_image_update *rq =
(struct cerberus_protocol_recovery_image_update*) request->data;
const struct recovery_image_cmd_interface *recovery_interface;
int status;
if (request->length < sizeof (struct cerberus_protocol_recovery_image_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->port_id > 1) {
return CMD_HANDLER_OUT_OF_RANGE;
}
recovery_interface = cerberus_protocol_get_recovery_image_cmd_interface (recovery_0, recovery_1,
rq->port_id);
if (recovery_interface == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
status = recovery_interface->update_recovery_image (recovery_interface, &rq->payload,
cerberus_protocol_recovery_image_update_length (request));
request->length = 0;
return status;
}
/**
* Process an activate recovery image request.
*
* @param recovery_0 Recovery image update command interface instance for port 0.
* @param recovery_1 Recovery image update command interface instance for port 1.
* @param request Recovery image activate request to process.
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_activate_recovery_image (
const struct recovery_image_cmd_interface *recovery_0,
const struct recovery_image_cmd_interface *recovery_1, struct cmd_interface_msg *request)
{
struct cerberus_protocol_complete_recovery_image_update *rq =
(struct cerberus_protocol_complete_recovery_image_update*) request->data;
const struct recovery_image_cmd_interface *recovery_interface;
if (request->length != sizeof (struct cerberus_protocol_complete_recovery_image_update)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->port_id > 1) {
return CMD_HANDLER_OUT_OF_RANGE;
}
recovery_interface = cerberus_protocol_get_recovery_image_cmd_interface (recovery_0, recovery_1,
rq->port_id);
if (recovery_interface == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
request->length = 0;
return recovery_interface->activate_recovery_image (recovery_interface);
}
/**
* Process get recovery image ID request.
*
* @param manager_0 Recovery image manager instance for port 0.
* @param manager_1 Recovery image manager instance for port 1.
* @param request Recovery image get request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_get_recovery_image_id (struct recovery_image_manager *manager_0,
struct recovery_image_manager *manager_1, struct cmd_interface_msg *request)
{
struct cerberus_protocol_get_recovery_image_id *rq =
(struct cerberus_protocol_get_recovery_image_id*) request->data;
struct cerberus_protocol_get_recovery_image_id_version_response *rsp =
(struct cerberus_protocol_get_recovery_image_id_version_response*) request->data;
struct recovery_image *curr_recovery_image;
struct recovery_image_manager *curr_mgr;
int status = 0;
if (request->length ==
(sizeof (struct cerberus_protocol_get_recovery_image_id) - sizeof (rq->id))) {
rq->id = 0;
}
else if (request->length != sizeof (struct cerberus_protocol_get_recovery_image_id)) {
return CMD_HANDLER_BAD_LENGTH;
}
if (rq->port_id > 1) {
return CMD_HANDLER_OUT_OF_RANGE;
}
curr_mgr = cerberus_protocol_get_recovery_image_manager (manager_0, manager_1, rq->port_id);
if (curr_mgr == NULL) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
memset (rsp->version, 0, sizeof (rsp->version));
curr_recovery_image = curr_mgr->get_active_recovery_image (curr_mgr);
if (curr_recovery_image != NULL) {
status = curr_recovery_image->get_version (curr_recovery_image, rsp->version,
sizeof (rsp->version));
if (status != 0) {
goto exit;
}
}
request->length = sizeof (struct cerberus_protocol_get_recovery_image_id_version_response);
exit:
if (curr_recovery_image != NULL) {
curr_mgr->free_recovery_image (curr_mgr, curr_recovery_image);
}
return status;
}
/**
* Process get attestation data request
*
* @param pcr_store PCR store instance to utilize
* @param request Log read request to process
*
* @return 0 if request completed successfully or an error code.
*/
int cerberus_protocol_get_attestation_data (struct pcr_store *pcr_store,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_get_attestation_data *rq =
(struct cerberus_protocol_get_attestation_data*) request->data;
struct cerberus_protocol_get_attestation_data_response *resp =
(struct cerberus_protocol_get_attestation_data_response*) request->data;
int status;
if (request->length != sizeof (struct cerberus_protocol_get_attestation_data)) {
return CMD_HANDLER_BAD_LENGTH;
}
status = pcr_store_get_measurement_data (pcr_store, PCR_MEASUREMENT (rq->pmr, rq->entry),
rq->offset, cerberus_protocol_attestation_data (resp),
CERBERUS_PROTOCOL_MAX_ATTESTATION_DATA (request));
if (ROT_IS_ERROR (status)) {
return status;
}
cmd_interface_msg_set_message_payload_length (request,
cerberus_protocol_get_attestation_data_response_length (status));
return 0;
}
/**
* Process get attestation summary request
*
* @param device_mgr Device manager instance to utilize
* @param request Attestation summary request to process
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_get_attestation_summary (struct device_manager *device_mgr,
struct cmd_interface_msg *request)
{
struct cerberus_protocol_get_attestation_summary *rq =
(struct cerberus_protocol_get_attestation_summary*) request->data;
struct cerberus_protocol_get_attestation_summary_response *rsp =
(struct cerberus_protocol_get_attestation_summary_response*) request->data;
int device_num;
if (request->length != sizeof (struct cerberus_protocol_get_attestation_summary)) {
return CMD_HANDLER_BAD_LENGTH;
}
device_num = device_manager_get_device_num_by_component (device_mgr, rq->component_id,
rq->component_instance);
if (ROT_IS_ERROR (device_num)) {
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
/* Below call can't fail due to known good inputs */
rsp->summary.prev_state = device_manager_get_attestation_summary_prev_state (device_mgr,
device_num);
/* Below call can't fail due to known good inputs */
device_manager_get_attestation_summary_event_counters (device_mgr, device_num,
&rsp->summary.event_counters);
cmd_interface_msg_set_message_payload_length (request,
sizeof (struct cerberus_protocol_get_attestation_summary_response));
return 0;
}
/**
* Process a key exchange request.
*
* @param session Session manager to utilize.
* @param request Key exchange request to process.
* @param encrypted Flag indicating if request was received in an encrypted session.
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_key_exchange (struct session_manager *session,
struct cmd_interface_msg *request, uint8_t encrypted)
{
struct cerberus_protocol_key_exchange_type_1 *type1_rq =
(struct cerberus_protocol_key_exchange_type_1*) request->data;
struct cerberus_protocol_key_exchange_type_2 *type2_rq =
(struct cerberus_protocol_key_exchange_type_2*) request->data;
int status;
request->crypto_timeout = true;
if (session == NULL) {
return CMD_HANDLER_UNSUPPORTED_COMMAND;
}
if (request->length <= sizeof (struct cerberus_protocol_key_exchange)) {
return CMD_HANDLER_BAD_LENGTH;
}
switch (type1_rq->common.key_type) {
case CERBERUS_PROTOCOL_SESSION_KEY:
return session->establish_session (session, request);
case CERBERUS_PROTOCOL_PAIRED_KEY_HMAC:
if (!encrypted) {
return CMD_HANDLER_CMD_SHOULD_BE_ENCRYPTED;
}
status = session->setup_paired_session (session, request->source_eid,
type1_rq->pairing_key_len,
cerberus_protocol_key_exchange_type_1_hmac_data (type1_rq),
cerberus_protocol_key_exchange_type_1_hmac_len (request));
break;
case CERBERUS_PROTOCOL_DELETE_SESSION_KEY:
if (!encrypted) {
return CMD_HANDLER_CMD_SHOULD_BE_ENCRYPTED;
}
status = session->reset_session (session, request->source_eid,
cerberus_protocol_key_exchange_type_2_hmac_data (type2_rq),
cerberus_protocol_key_exchange_type_2_hmac_len (request));
if (status == 0) {
type2_rq->common.header.crypt = 0;
}
break;
default:
return CMD_HANDLER_UNSUPPORTED_INDEX;
}
if (status == 0) {
cmd_interface_msg_set_message_payload_length (request,
sizeof (struct cerberus_protocol_key_exchange_response));
}
return status;
}
/**
* Process a session sync request.
*
* @param session Session manager to utilize.
* @param request Session sync request to process.
* @param encrypted Flag indicating if request was received in an encrypted session.
*
* @return 0 if request processing completed successfully or an error code.
*/
int cerberus_protocol_session_sync (struct session_manager *session,
struct cmd_interface_msg *request, uint8_t encrypted)
{
struct cerberus_protocol_session_sync *rq =
(struct cerberus_protocol_session_sync*) request->data;
int status;
request->crypto_timeout = true;
if (session == NULL) {
return CMD_HANDLER_UNSUPPORTED_COMMAND;
}
if (!encrypted) {
return CMD_HANDLER_CMD_SHOULD_BE_ENCRYPTED;
}
if (request->length != sizeof (struct cerberus_protocol_session_sync)) {
return CMD_HANDLER_BAD_LENGTH;
}
status = session->session_sync (session, request->source_eid, rq->rn_req,
cerberus_protocol_session_sync_hmac_data (rq),
CERBERUS_PROTOCOL_MAX_SESSION_SYNC_HMAC_LEN (request));
if (ROT_IS_ERROR (status)) {
return status;
}
cmd_interface_msg_set_message_payload_length (request,
cerberus_protocol_session_sync_length (status));
return 0;
}