core/cmd_interface/device_manager.c (1,038 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 "device_manager.h" #include "platform_api.h" #include "common/buffer_util.h" #include "common/common_math.h" #include "crypto/hash.h" #include "manifest/pcd/pcd.h" #include "mctp/mctp_base_protocol.h" // Attestation status component header length #define DEVICE_MANAGER_ATTESTATION_STATUS_COMPONENT_HEADER_LEN \ (sizeof (struct pcd_supported_component)) /** * Determine if device is undergoing or failed attestation and should use the unauthenticated * cadence * * @param state Device state */ #define device_manager_is_device_unauthenticated(state) \ ((state == DEVICE_MANAGER_READY_FOR_ATTESTATION) || \ (state == DEVICE_MANAGER_ATTESTATION_FAILED) || \ (state == DEVICE_MANAGER_ATTESTATION_INTERRUPTED) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_VERSION) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_CAPS) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_ALGORITHM) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_DIGESTS) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_CERTS) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_CHALLENGE) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_MEASUREMENT) || \ (state == DEVICE_MANAGER_ATTESTATION_MEASUREMENT_MISMATCH) || \ (state == DEVICE_MANAGER_ATTESTATION_UNTRUSTED_CERTS) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_RESPONSE) || \ (state == DEVICE_MANAGER_ATTESTATION_INVALID_CFM)) /** * Determine if device is ready to be attested * * @param state Device state */ #define device_manager_can_device_be_attested(state) \ (device_manager_is_device_unauthenticated(state) || (state == DEVICE_MANAGER_AUTHENTICATED) || \ (state == DEVICE_MANAGER_AUTHENTICATED_WITHOUT_CERTS) || \ (state == DEVICE_MANAGER_AUTHENTICATED_WITH_TIMEOUT) || \ (state == DEVICE_MANAGER_AUTHENTICATED_WITHOUT_CERTS_WITH_TIMEOUT) || \ (state == DEVICE_MANAGER_NEVER_ATTESTED)) /** * Update device manager device table entry state * * @param mgr Device manager instance to utilize. * @param device_num Device table entry to update. * @param state Device state. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_device_state (struct device_manager *mgr, int device_num, enum device_manager_device_state state) { enum device_manager_device_state prev_state; uint32_t timeout = 0; if ((mgr == NULL) || (state >= MAX_DEVICE_MANAGER_STATES)) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } prev_state = mgr->entries[device_num].state; mgr->entries[device_num].state = state; if ((state == DEVICE_MANAGER_AUTHENTICATED) || (state == DEVICE_MANAGER_AUTHENTICATED_WITHOUT_CERTS)) { timeout = mgr->authenticated_cadence_ms; } else if ((device_manager_is_device_unauthenticated (state)) && (device_manager_is_device_unauthenticated (prev_state) || (prev_state == DEVICE_MANAGER_NEVER_ATTESTED))) { timeout = mgr->unauthenticated_cadence_ms; } else if (state == DEVICE_MANAGER_NEVER_ATTESTED) { timeout = 0; } return platform_init_timeout (timeout, &mgr->entries[device_num].attestation_timeout); } /** * Initialize a device manager. * * The first device entry will be for the local device, and the device capabilities will be * initialized based on the device configuration. These capabilities can be updated as necessary * with device_manager_update_device_capabilities(). * * @param mgr Device manager instance to initialize. * @param num_requester_devices Number of requester devices to manage. This must be at least 1 to * support the local device. * @param num_unique_responder_devices Number of unique responder devices to manage. * @param num_responder_devices Number of responder devices to manage. * @param hierarchy Role of the local device in the Cerberus hierarchy (PA vs. AC RoT). * @param bus_role Role the local device will take on the I2C bus. * @param unauthenticated_cadence_ms Period to wait before reauthenticating unauthenticated device. * @param authenticated_cadence_ms Period to wait before reauthenticating authenticated device. * @param unidentified_timeout_ms Timeout period to wait before reidentifying unidentified device. * @param mctp_ctrl_timeout_ms Timeout duration for MCTP control requests. * @param mctp_bridge_additional_timeout_ms Timeout adjustment to MCTP bridge communication. * @param attestation_rsp_not_ready_max_duration_ms Maximum SPDM ResponseNotReady duration. * @param attestation_rsp_not_ready_max_retry Maximum SPDM ResponseNotReady retries. * * @return Initialization status, 0 if success or an error code. */ int device_manager_init (struct device_manager *mgr, int num_requester_devices, int num_unique_responder_devices, int num_responder_devices, uint8_t hierarchy, uint8_t bus_role, uint32_t unauthenticated_cadence_ms, uint32_t authenticated_cadence_ms, uint32_t unidentified_timeout_ms, uint32_t mctp_ctrl_timeout_ms, uint32_t mctp_bridge_additional_timeout_ms, uint32_t attestation_rsp_not_ready_max_duration_ms, uint8_t attestation_rsp_not_ready_max_retry) { int total_num_devices = num_requester_devices + num_responder_devices; int status; if ((mgr == NULL) || (num_requester_devices == 0) || (hierarchy >= NUM_BUS_HIERACHY_ROLES) || (bus_role >= NUM_BUS_ROLES)) { return DEVICE_MGR_INVALID_ARGUMENT; } if (num_unique_responder_devices > num_responder_devices) { return DEVICE_MGR_INVALID_RESPONDER_COUNT; } memset (mgr, 0, sizeof (struct device_manager)); mgr->entries = platform_calloc (total_num_devices, sizeof (struct device_manager_entry)); if (mgr->entries == NULL) { return DEVICE_MGR_NO_MEMORY; } if (num_responder_devices != 0) { mgr->attestation_status = platform_malloc (num_responder_devices + (num_unique_responder_devices * DEVICE_MANAGER_ATTESTATION_STATUS_COMPONENT_HEADER_LEN)); if (mgr->attestation_status == NULL) { status = DEVICE_MGR_NO_MEMORY; goto free_entries; } } mgr->num_devices = total_num_devices; mgr->num_requester_devices = num_requester_devices; mgr->num_unique_responder_devices = num_unique_responder_devices; mgr->num_responder_devices = num_responder_devices; mgr->unauthenticated_cadence_ms = unauthenticated_cadence_ms; mgr->authenticated_cadence_ms = authenticated_cadence_ms; mgr->unidentified_timeout_ms = unidentified_timeout_ms; mgr->mctp_ctrl_timeout_ms = mctp_ctrl_timeout_ms; mgr->mctp_bridge_additional_timeout_ms = mctp_bridge_additional_timeout_ms; mgr->attestation_rsp_not_ready_max_duration_ms = attestation_rsp_not_ready_max_duration_ms; mgr->attestation_rsp_not_ready_max_retry = attestation_rsp_not_ready_max_retry; /* Initialize the local device capabilities. */ mgr->entries[DEVICE_MANAGER_SELF_DEVICE_NUM].capabilities.request.max_message_size = MCTP_BASE_PROTOCOL_MAX_MESSAGE_BODY; mgr->entries[DEVICE_MANAGER_SELF_DEVICE_NUM].capabilities.request.max_packet_size = MCTP_BASE_PROTOCOL_MAX_TRANSMISSION_UNIT; mgr->entries[DEVICE_MANAGER_SELF_DEVICE_NUM].capabilities.request.security_mode = DEVICE_MANAGER_SECURITY_AUTHENTICATION; mgr->entries[DEVICE_MANAGER_SELF_DEVICE_NUM].capabilities.request.bus_role = bus_role; mgr->entries[DEVICE_MANAGER_SELF_DEVICE_NUM].capabilities.request.hierarchy_role = hierarchy; mgr->entries[DEVICE_MANAGER_SELF_DEVICE_NUM].capabilities.max_timeout = device_manager_set_timeout_ms (MCTP_BASE_PROTOCOL_MAX_RESPONSE_TIMEOUT_MS); mgr->entries[DEVICE_MANAGER_SELF_DEVICE_NUM].capabilities.max_sig = device_manager_set_crypto_timeout_ms (MCTP_BASE_PROTOCOL_MAX_CRYPTO_TIMEOUT_MS); status = device_manager_update_device_state (mgr, DEVICE_MANAGER_SELF_DEVICE_NUM, DEVICE_MANAGER_NOT_ATTESTABLE); if (status != 0) { goto error_exit; } status = observable_init (&mgr->observable); if (status != 0) { goto error_exit; } return 0; error_exit: platform_free (mgr->attestation_status); free_entries: platform_free (mgr->entries); return status; } /** * Initialize a device manager for an AC-RoT Cerberus. This will set Cerberus hierachy role to * DEVICE_MANAGER_AC_ROT_MODE, set the number of responder devices to zero, and set all the * component attestation defaults to zero. * * @param mgr Device manager instance to initialize. * @param num_requester_devices Number of requester devices to manage. This must be at least 1 to * support the local device. * @param bus_role Role the local device will take on the I2C bus. * * @return Initialization status, 0 if success or an error code. */ int device_manager_init_ac_rot (struct device_manager *mgr, int num_requester_devices, uint8_t bus_role) { return device_manager_init (mgr, num_requester_devices, 0, 0, DEVICE_MANAGER_AC_ROT_MODE, bus_role, 0, 0, 0, 0, 0, 0, 0); } #ifdef ATTESTATION_SUPPORT_DEVICE_DISCOVERY /** * Free unidentified devices circular list * * @param mgr Device manager instance to utilize */ void device_manager_clear_unidentified_devices (struct device_manager *mgr) { struct device_manager_unidentified_entry *entry; if (mgr->unidentified != NULL) { while (mgr->unidentified->next != mgr->unidentified) { entry = mgr->unidentified->next; mgr->unidentified->next = entry->next; platform_free (entry); } platform_free (mgr->unidentified); mgr->unidentified = NULL; } } #endif /** * Release device manager * * @param mgr Device manager instance to release */ void device_manager_release (struct device_manager *mgr) { if (mgr) { platform_free (mgr->entries); platform_free (mgr->attestation_status); mgr->num_devices = 0; #ifdef ATTESTATION_SUPPORT_DEVICE_DISCOVERY device_manager_clear_unidentified_devices (mgr); #endif observable_release (&mgr->observable); } } /** * Add an observer for device manager events. * * @param mgr Device manager instance to use. * @param observer The observer to add. * * @return 0 if the observer was successfully added or an error code. */ int device_manager_add_observer (struct device_manager *mgr, struct device_manager_observer *observer) { if ((mgr == NULL) || (observer == NULL)) { return DEVICE_MANAGER_OBSERVER_INVALID_ARGUMENT; } return observable_add_observer (&mgr->observable, observer); } /** * Remove an observer from device manager events. * * @param mgr Device manager instance to deregister from. * @param observer The observer to remove. * * @return 0 if the observer was successfully removed or an error code. */ int device_manager_remove_observer (struct device_manager *mgr, struct device_manager_observer *observer) { if ((mgr == NULL) || (observer == NULL)) { return DEVICE_MANAGER_OBSERVER_INVALID_ARGUMENT; } return observable_remove_observer (&mgr->observable, observer); } /** * Find device index in device manager table using SMBUS address and EID. * * @param mgr The device manager to utilize. * @param eid The EID to find. * * @return The device number if found or an error code. */ int device_manager_get_device_num (struct device_manager *mgr, uint8_t eid) { int i_device; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } for (i_device = 0; i_device < mgr->num_devices; ++i_device) { if (mgr->entries[i_device].eid == eid) { return i_device; } } return DEVICE_MGR_UNKNOWN_DEVICE; } /** * Find device index in device manager table using Component ID and index in PCD. * * @param mgr The device manager to utilize. * @param component_id The component ID to find. * @param component_instance The component instance to find. * * * @return The device number if found or an error code. */ int device_manager_get_device_num_by_component (struct device_manager *mgr, uint32_t component_id, uint8_t component_instance) { int i_device; int i_component; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } for (i_device = 0; i_device < mgr->num_devices; ++i_device) { i_component = 0; while ((i_device < mgr->num_devices) && (mgr->entries[i_device].component_id == component_id)) { if (i_component == component_instance) { return i_device; } i_device++; i_component++; } } return DEVICE_MGR_UNKNOWN_DEVICE; } /** * Find device SMBUS address for a device in device manager table. * * @param mgr The device manager to utilize. * @param device_num The device table entry to utilize. * * @return The device address if found or an error code. */ int device_manager_get_device_addr (struct device_manager *mgr, int device_num) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } return mgr->entries[device_num].smbus_addr; } /** * Find device SMBUS address for a device in device manager table. * * @param mgr The device manager to utilize. * @param eid EID of device to utilize. * * @return The device address if found or an error code. */ int device_manager_get_device_addr_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } return device_manager_get_device_addr (mgr, device_manager_get_device_num (mgr, eid)); } /** * Find device EID for a device in device manager table. * * @param mgr The device manager to utilize. * @param device_num The device table entry to utilize. * * @return The device EID if found or an error code. */ int device_manager_get_device_eid (struct device_manager *mgr, int device_num) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } return mgr->entries[device_num].eid; } /** * Update device manager device table entry with new eid. * * @param mgr Device manager instance to utilize. * @param device_num Device table entry to update. * @param eid Device EID to use. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_device_eid (struct device_manager *mgr, int device_num, uint8_t eid) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } mgr->entries[device_num].eid = eid; if (device_num == DEVICE_MANAGER_SELF_DEVICE_NUM) { observable_notify_observers_with_ptr (&mgr->observable, offsetof (struct device_manager_observer, on_set_eid), &eid); } return 0; } /** * Update device manager device table entry. All non-attestable devices need to be in device * entries at the beginning. * * @param mgr Device manager instance to utilize. * @param device_num Device table entry to update. * @param eid Device EID. * @param smbus_addr Device SMBUS Address. * @param pcd_component_index Index of component in PCD. If not a PCD component, use * DEVICE_MANAGER_NOT_PCD_COMPONENT. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_not_attestable_device_entry (struct device_manager *mgr, int device_num, uint8_t eid, uint8_t smbus_addr, uint8_t pcd_component_index) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } mgr->entries[device_num].eid = eid; mgr->entries[device_num].smbus_addr = smbus_addr; mgr->entries[device_num].pcd_component_index = pcd_component_index; mgr->entries[device_num].state = DEVICE_MANAGER_NOT_ATTESTABLE; return platform_init_timeout (0, &mgr->entries[device_num].attestation_timeout); } /** * Update device manager device table MCTP bridge component entry. All attestable devices need to * be in device entries that follow non-attestable devices. The order in which device entries are * added needs to follow order in PCD. * * @param mgr Device manager instance to utilize. * @param device_num Device table entry to update. * @param pci_vid PCI Vendor ID. * @param pci_device_id PCI Device ID. * @param pci_subsystem_vid PCI Subsystem Vendor ID. * @param pci_subsystem_id PCI Subsystem ID. * @param components_count Number of identical components this element describes. * @param component_id Component ID in PCD and CFM. * @param pcd_component_index Index of component in PCD. If not a PCD component, use * DEVICE_MANAGER_NOT_PCD_COMPONENT. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_mctp_bridge_device_entry (struct device_manager *mgr, int device_num, uint16_t pci_vid, uint16_t pci_device_id, uint16_t pci_subsystem_vid, uint16_t pci_subsystem_id, uint8_t components_count, uint32_t component_id, uint8_t pcd_component_index) { int i_component; int status; if ((mgr == NULL) || (components_count == 0)) { return DEVICE_MGR_INVALID_ARGUMENT; } if ((device_num + components_count) > mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } for (i_component = device_num; i_component < (device_num + components_count); ++i_component) { mgr->entries[i_component].component_id = component_id; mgr->entries[i_component].pci_device_id = pci_device_id; mgr->entries[i_component].pci_vid = pci_vid; mgr->entries[i_component].pci_subsystem_id = pci_subsystem_id; mgr->entries[i_component].pci_subsystem_vid = pci_subsystem_vid; mgr->entries[i_component].smbus_addr = mgr->entries[DEVICE_MANAGER_MCTP_BRIDGE_DEVICE_NUM].smbus_addr; mgr->entries[device_num].pcd_component_index = pcd_component_index; status = device_manager_update_device_state (mgr, i_component, DEVICE_MANAGER_UNIDENTIFIED); if (status != 0) { return status; } status = platform_init_timeout (0, &mgr->entries[i_component].attestation_timeout); if (status != 0) { return status; } } return 0; } /** * Retrieve the device capabilities for a device in the device manager table. * * @param mgr The device manager to query. * @param device_num The device table entry to retrieve capabilites for. * @param capabilities Output buffer for the device capabilities. * * @return Completion status, 0 if success or an error code. */ int device_manager_get_device_capabilities (struct device_manager *mgr, int device_num, struct device_manager_full_capabilities *capabilities) { if ((mgr == NULL) || (capabilities == NULL)) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } memcpy (capabilities, &mgr->entries[device_num].capabilities, sizeof (struct device_manager_full_capabilities)); return 0; } /** * Update the device capabilities for a device in the device manager device table. * * @param mgr Device manager instance to update. * @param device_num Device table entry to update. * @param capabilities Capabilities to use for the device entry. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_device_capabilities (struct device_manager *mgr, int device_num, struct device_manager_full_capabilities *capabilities) { if ((mgr == NULL) || (capabilities == NULL)) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } memcpy (&mgr->entries[device_num].capabilities, capabilities, sizeof (struct device_manager_full_capabilities)); return 0; } /** * Retrieve the device capabilites for a request. This will only retrieve the local devices's * capabilities. * * @param mgr The device manager to query. * @param capabilites Output buffer for the device capabilities. * * @return Completion status, 0 if success or an error code. */ int device_manager_get_device_capabilities_request (struct device_manager *mgr, struct device_manager_capabilities *capabilites) { if ((mgr == NULL) || (capabilites == NULL)) { return DEVICE_MGR_INVALID_ARGUMENT; } memcpy (capabilites, &mgr->entries[0].capabilities.request, sizeof (struct device_manager_capabilities)); return 0; } /** * Update only the device capabilities from a request massage for a device in the device manager * device table. * * @param mgr Device manager instance to update. * @param device_num Device table entry to update. * @param capabilities Capabilities to use for the device entry. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_device_capabilities_request (struct device_manager *mgr, int device_num, struct device_manager_capabilities *capabilities) { if ((mgr == NULL) || (capabilities == NULL)) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } memcpy (&mgr->entries[device_num].capabilities, capabilities, sizeof (struct device_manager_capabilities)); return 0; } /** * Get the maximum message length supported by a device. For any remote device, this will be the * negotiated maximum based on shared capabilities. If the requested device is not known or has * invalid capabilites, the local device message length is used. * * @param mgr Device manager to query. * @param device_num Entry in the device table to query. * * @return The maximum message size to use when communicating with the device. */ size_t device_manager_get_max_message_len (struct device_manager *mgr, int device_num) { size_t remote_len = 0; if (mgr == NULL) { return MCTP_BASE_PROTOCOL_MAX_MESSAGE_BODY; } if (device_num < mgr->num_devices) { remote_len = mgr->entries[device_num].capabilities.request.max_message_size; } if (remote_len == 0) { remote_len = MCTP_BASE_PROTOCOL_MAX_MESSAGE_BODY; } return min (mgr->entries[0].capabilities.request.max_message_size, remote_len); } /** * Get the maximum message length supported by a device. For any remote device, this will be the * negotiated maximum based on shared capabilities. If the requested device is not known or has * invalid capabilites, the local device message length is used. * * @param mgr Device manager to query. * @param eid EID of the device entry to query. * * @return The maximum message size to use when communicating with the device. */ size_t device_manager_get_max_message_len_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return MCTP_BASE_PROTOCOL_MAX_MESSAGE_BODY; } return device_manager_get_max_message_len (mgr, device_manager_get_device_num (mgr, eid)); } /** * Get the maximum MCTP transmission unit supported by a device. For any remote device, this will * be the negotiated maximum based on shared capabilities. If the requested device is not known or * has invalid capabilites, the local device packet length is used. * * @param mgr Device manager to query. * @param device_num Entry in the device table to query. * * @return The maximum packet size to use when communicating with the device. */ size_t device_manager_get_max_transmission_unit (struct device_manager *mgr, int device_num) { size_t remote_len = 0; if (mgr == NULL) { return MCTP_BASE_PROTOCOL_MAX_TRANSMISSION_UNIT; } if (device_num < mgr->num_devices) { remote_len = mgr->entries[device_num].capabilities.request.max_packet_size; } if (remote_len == 0) { remote_len = MCTP_BASE_PROTOCOL_MAX_TRANSMISSION_UNIT; } return min (mgr->entries[0].capabilities.request.max_packet_size, remote_len); } /** * Get the maximum MCTP transmission unit supported by a device. For any remote device, this will * be the negotiated maximum based on shared capabilities. If the requested device is not known or * has invalid capabilites, the local device packet length is used. * * @param mgr Device manager to query. * @param eid EID of the device entry to query. * * @return The maximum packet size to use when communicating with the device. */ size_t device_manager_get_max_transmission_unit_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return MCTP_BASE_PROTOCOL_MAX_TRANSMISSION_UNIT; } return device_manager_get_max_transmission_unit (mgr, device_manager_get_device_num (mgr, eid)); } /** * Get the maximum amount of time to wait for a response from a remote device. If the device is not * known or has invalid capabilities, the local device timeout is assumed. * * @param mgr Device manager to query. * @param device_num Entry in the device table to query. * * @return The response timeout for the device. */ uint32_t device_manager_get_reponse_timeout (struct device_manager *mgr, int device_num) { if (mgr == NULL) { return MCTP_BASE_PROTOCOL_MAX_RESPONSE_TIMEOUT_MS; } if ((device_num >= mgr->num_devices) || (mgr->entries[device_num].capabilities.max_timeout == 0)) { return ((mgr->entries[0].capabilities.max_timeout * 10) + mgr->mctp_bridge_additional_timeout_ms); } return ((mgr->entries[device_num].capabilities.max_timeout * 10) + mgr->mctp_bridge_additional_timeout_ms); } /** * Get the maximum amount of time to wait for a response from a remote device. If the device is not * known or has invalid capabilities, the local device timeout is assumed. * * @param mgr Device manager to query. * @param eid EID of the device entry to query. * * @return The response timeout for the device. */ uint32_t device_manager_get_reponse_timeout_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return MCTP_BASE_PROTOCOL_MAX_RESPONSE_TIMEOUT_MS; } return device_manager_get_reponse_timeout (mgr, device_manager_get_device_num (mgr, eid)); } /** * Get the maximum amount of time to wait for a response from a remote device when executing * cryptographic requests. If the device is not known or has invalid capabilities, the local device * timeout is assumed. * * @param mgr Device manager to query. * @param device_num Entry in the device table to query. * * @return The cryptographic response timeout for the device. */ uint32_t device_manager_get_crypto_timeout (struct device_manager *mgr, int device_num) { if (mgr == NULL) { return MCTP_BASE_PROTOCOL_MAX_CRYPTO_TIMEOUT_MS; } if ((device_num >= mgr->num_devices) || (mgr->entries[device_num].capabilities.max_sig == 0)) { return ((mgr->entries[0].capabilities.max_sig * 100) + mgr->mctp_bridge_additional_timeout_ms); } return ((mgr->entries[device_num].capabilities.max_sig * 100) + mgr->mctp_bridge_additional_timeout_ms); } /** * Get the maximum amount of time to wait for a response from a remote device when executing * cryptographic requests. If the device is not known or has invalid capabilities, the local device * timeout is assumed. * * @param mgr Device manager to query. * @param eid EID of the device entry to query. * * @return The cryptographic response timeout for the device. */ uint32_t device_manager_get_crypto_timeout_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return MCTP_BASE_PROTOCOL_MAX_CRYPTO_TIMEOUT_MS; } return device_manager_get_crypto_timeout (mgr, device_manager_get_device_num (mgr, eid)); } /** * Get the SPDM ResponseNotReady limits. * * @param mgr Device manager to query. * @param max_timeout_ms Buffer to fill with maximum ResponseNotReady wait duration in milliseconds. * @param max_retries Buffer to fill with maximum number of ResponseNotReady retries permitted. * * @return Completion status, 0 if success or an error code. */ int device_manager_get_rsp_not_ready_limits (struct device_manager *mgr, uint32_t *max_timeout_ms, uint8_t *max_retries) { if ((mgr == NULL) || (max_timeout_ms == NULL) || (max_retries == NULL)) { return DEVICE_MGR_INVALID_ARGUMENT; } *max_timeout_ms = mgr->attestation_rsp_not_ready_max_duration_ms; *max_retries = mgr->attestation_rsp_not_ready_max_retry; return 0; } /** * Get the maximum amount of time to wait for a response from a remote device when executing * MCTP control protocol requests. * * @param mgr Device manager to query. * * @return The response timeout. */ uint32_t device_manager_get_mctp_ctrl_timeout (struct device_manager *mgr) { if (mgr == NULL) { return DEVICE_MANAGER_MCTP_CTRL_PROTOCOL_TIMEOUT_MS; } return (mgr->mctp_ctrl_timeout_ms + mgr->mctp_bridge_additional_timeout_ms); } /** * Update certificate chain digest buffer in device manager device table entry * * @param mgr Device manager instance to utilize. * @param eid EID of device to update. * @param slot_num Slot number of incoming certificate chain. * @param digest Digest buffer with a new certificate chain digest. * @param digest_len Length of digest. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_cert_chain_digest (struct device_manager *mgr, uint8_t eid, uint8_t slot_num, const uint8_t *digest, size_t digest_len) { int device_num; if ((mgr == NULL) || (digest == NULL) || (digest_len == 0)) { return DEVICE_MGR_INVALID_ARGUMENT; } if (digest_len > HASH_MAX_HASH_LEN) { return DEVICE_MGR_INPUT_TOO_LARGE; } device_num = device_manager_get_device_num (mgr, eid); if (ROT_IS_ERROR (device_num)) { return device_num; } memcpy (mgr->cert_chain_digest, digest, digest_len); mgr->hash_len = digest_len; mgr->entries[device_num].slot_num = slot_num; mgr->cert_chain_digest_eid = eid; return 0; } /** * Clear certificate chain digest buffer in device manager device table entry * * @param mgr Device manager instance to utilize. * @param eid EID of device to update. * * @return Completion status, 0 if success or an error code. */ int device_manager_clear_cert_chain_digest (struct device_manager *mgr, uint8_t eid) { int device_num; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } device_num = device_manager_get_device_num (mgr, eid); if (ROT_IS_ERROR (device_num)) { return device_num; } if (mgr->cert_chain_digest_eid == eid) { mgr->hash_len = 0; } return 0; } /** * Compare certificate chain digest with cached digest in a device manager device table entry * * @param mgr Device manager instance to utilize. * @param eid EID of device to utilize. * @param digest Buffer with digest to test. * @param digest_len Length of digest buffer. * * @return 0 if digests match or an error code. */ int device_manager_compare_cert_chain_digest (struct device_manager *mgr, uint8_t eid, const uint8_t *digest, size_t digest_len) { int device_num; int status; if ((mgr == NULL) || (digest == NULL) || (digest_len == 0)) { return DEVICE_MGR_INVALID_ARGUMENT; } device_num = device_manager_get_device_num (mgr, eid); if (ROT_IS_ERROR (device_num)) { return device_num; } if (mgr->cert_chain_digest_eid != eid) { return DEVICE_MGR_DIGEST_MISMATCH; } if (digest_len != mgr->hash_len) { return DEVICE_MGR_DIGEST_LEN_MISMATCH; } status = buffer_compare (digest, mgr->cert_chain_digest, mgr->hash_len); if (status != 0) { return DEVICE_MGR_DIGEST_MISMATCH; } return 0; } /** * Update alias certificate public key buffer in device manager device table entry * * @param mgr Device manager instance to utilize. * @param eid EID of device to update. * @param key Key buffer with a new alias key. * @param key_len Length of key. * @param key_type Alias key type. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_alias_key (struct device_manager *mgr, uint8_t eid, const uint8_t *key, size_t key_len, int key_type) { int device_num; if ((mgr == NULL) || (key == NULL) || (key_len == 0)) { return DEVICE_MGR_INVALID_ARGUMENT; } if (key_len > DEVICE_MANAGER_MAX_KEY_LEN) { return DEVICE_MGR_INPUT_TOO_LARGE; } device_num = device_manager_get_device_num (mgr, eid); if (ROT_IS_ERROR (device_num)) { return device_num; } memcpy (mgr->alias_key.key, key, key_len); mgr->alias_key.key_len = key_len; mgr->alias_key.key_type = key_type; mgr->alias_key_eid = eid; return 0; } /** * Get alias key from a device manager device table entry * * @param mgr Device manager instance to utilize. * @param eid EID of device to utilize. * * @return Alias key if found or NULL. */ const struct device_manager_key* device_manager_get_alias_key (struct device_manager *mgr, uint8_t eid) { int device_num; if (mgr == NULL) { return NULL; } device_num = device_manager_get_device_num (mgr, eid); if (ROT_IS_ERROR (device_num)) { return NULL; } if (mgr->alias_key_eid != eid) { return NULL; } return &mgr->alias_key; } /** * Clear alias key from a device manager device table entry * * @param mgr Device manager instance to utilize. * @param eid EID of device to utilize. * * @return Completion status, 0 if success or an error code. */ int device_manager_clear_alias_key (struct device_manager *mgr, uint8_t eid) { int device_num; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } device_num = device_manager_get_device_num (mgr, eid); if (ROT_IS_ERROR (device_num)) { return device_num; } if (mgr->alias_key_eid == eid) { mgr->alias_key_eid = MCTP_BASE_PROTOCOL_NULL_EID; } return 0; } /** * Find device state for a device in device manager table. * * @param mgr The device manager to utilize. * @param device_num The device table entry to utilize. * * @return The device state if found or an error code. */ int device_manager_get_device_state (struct device_manager *mgr, int device_num) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } return mgr->entries[device_num].state; } /** * Find device state for a device in device manager table. * * @param mgr The device manager to utilize. * @param eid EID of device to utilize. * * @return The device state if found or an error code. */ int device_manager_get_device_state_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } return device_manager_get_device_state (mgr, device_manager_get_device_num (mgr, eid)); } /* * Update device manager device table entry state * * @param mgr Device manager instance to utilize. * @param eid EID of device to update. * @param state Device state. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_device_state_by_eid (struct device_manager *mgr, uint8_t eid, enum device_manager_device_state state) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } return device_manager_update_device_state (mgr, device_manager_get_device_num (mgr, eid), state); } /** * Get previous device state for a device in device manager table. * * @param mgr The device manager to utilize. * @param device_num The device table entry to utilize. * * @return The previous device state if found or an error code. */ int device_manager_get_attestation_summary_prev_state (struct device_manager *mgr, int device_num) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } return mgr->entries[device_num].summary.prev_state; } /** * Get previous device state for a device in device manager table. * * @param mgr The device manager to utilize. * @param eid EID of device to utilize. * * @return The previous device state if found or an error code. */ int device_manager_get_attestation_summary_prev_state_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } return device_manager_get_attestation_summary_prev_state (mgr, device_manager_get_device_num (mgr, eid)); } /** * Update device manager device table entry previous state * * @param mgr Device manager instance to utilize. * @param device_num Device table entry to update. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_attestation_summary_prev_state (struct device_manager *mgr, int device_num) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } mgr->entries[device_num].summary.prev_state = mgr->entries[device_num].state; return 0; } /** * Update device manager device table prev state * * @param mgr Device manager instance to utilize. * @param eid EID of device to update. * @param prev_state Device state. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_attestation_summary_prev_state_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } return device_manager_update_attestation_summary_prev_state (mgr, device_manager_get_device_num (mgr, eid)); } /** * Get attestation event counters for a device in device manager table. * * @param mgr The device manager to utilize. * @param device_num The device table entry to utilize. * @param event_counters Output buffer for the event counters. * * @return Completion status, 0 if success or an error code. */ int device_manager_get_attestation_summary_event_counters (struct device_manager *mgr, int device_num, struct device_manager_attestation_summary_event_counters *event_counters) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } memcpy (event_counters, &mgr->entries[device_num].summary.event_counters, sizeof (struct device_manager_attestation_summary_event_counters)); return 0; } /** * Get attestation event counters for a device in device manager table. * * @param mgr The device manager to utilize. * @param eid EID of device to utilize. * @param event_counters Output buffer for the event counters. * * @return Completion status, 0 if success or an error code. */ int device_manager_get_attestation_summary_event_counters_by_eid (struct device_manager *mgr, uint8_t eid, struct device_manager_attestation_summary_event_counters *event_counters) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } return device_manager_get_attestation_summary_event_counters (mgr, device_manager_get_device_num (mgr, eid), event_counters); } /** * Update device manager device table entry attestation event counters * * @param mgr Device manager instance to utilize. * @param device_num Device table entry to update. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_attestation_summary_event_counters (struct device_manager *mgr, int device_num) { struct device_manager_attestation_summary_event_counters *event_counters; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } event_counters = &mgr->entries[device_num].summary.event_counters; switch (mgr->entries[device_num].state) { case DEVICE_MANAGER_AUTHENTICATED: case DEVICE_MANAGER_AUTHENTICATED_WITHOUT_CERTS: event_counters->status_success_count = common_math_saturating_increment_u16 (event_counters->status_success_count); break; case DEVICE_MANAGER_AUTHENTICATED_WITH_TIMEOUT: case DEVICE_MANAGER_AUTHENTICATED_WITHOUT_CERTS_WITH_TIMEOUT: event_counters->status_success_timeout_count = common_math_saturating_increment_u16 (event_counters->status_success_timeout_count); break; case DEVICE_MANAGER_ATTESTATION_INTERRUPTED: event_counters->status_fail_timeout_count = common_math_saturating_increment_u16 (event_counters->status_fail_timeout_count); break; case DEVICE_MANAGER_ATTESTATION_FAILED: event_counters->status_fail_internal_count = common_math_saturating_increment_u16 (event_counters->status_fail_internal_count); break; case DEVICE_MANAGER_ATTESTATION_INVALID_VERSION: case DEVICE_MANAGER_ATTESTATION_INVALID_CAPS: case DEVICE_MANAGER_ATTESTATION_INVALID_ALGORITHM: case DEVICE_MANAGER_ATTESTATION_INVALID_DIGESTS: case DEVICE_MANAGER_ATTESTATION_INVALID_CERTS: case DEVICE_MANAGER_ATTESTATION_INVALID_CHALLENGE: case DEVICE_MANAGER_ATTESTATION_INVALID_MEASUREMENT: case DEVICE_MANAGER_ATTESTATION_INVALID_RESPONSE: event_counters->status_fail_invalid_response_count = common_math_saturating_increment_u16 ( event_counters->status_fail_invalid_response_count); break; case DEVICE_MANAGER_ATTESTATION_MEASUREMENT_MISMATCH: case DEVICE_MANAGER_ATTESTATION_UNTRUSTED_CERTS: case DEVICE_MANAGER_ATTESTATION_INVALID_CFM: event_counters->status_fail_invalid_config_count = common_math_saturating_increment_u16 ( event_counters->status_fail_invalid_config_count); break; default: break; } return 0; } /** * Update device manager device table event counters * * @param mgr Device manager instance to utilize. * @param eid EID of device to update. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_attestation_summary_event_counters_by_eid (struct device_manager *mgr, uint8_t eid) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } return device_manager_update_attestation_summary_event_counters (mgr, device_manager_get_device_num (mgr, eid)); } /** * Find component ID for a device in device manager table. * * @param mgr The device manager to utilize. * @param eid The EID of the device table entry to utilize. * @param component_id Component ID in PCD and CFM. * * @return Completion status, 0 if success or an error code. */ int device_manager_get_component_id (struct device_manager *mgr, uint8_t eid, uint32_t *component_id) { int device_num; if ((mgr == NULL) || (component_id == NULL)) { return DEVICE_MGR_INVALID_ARGUMENT; } device_num = device_manager_get_device_num (mgr, eid); if (ROT_IS_ERROR (device_num)) { return device_num; } *component_id = mgr->entries[device_num].component_id; return 0; } /** * Get EID of first device that is ready for attestation. A device that is starting or has failed * attestation has a cadence of unauthenticated_cadence_ms, a device that has previously passed * attestation has a cadence of authenticated_cadence_ms. The device manager keeps track of last * device authenticated, so checking starts after that device. * * @param mgr Device manager instance to utilize. * * @return EID of device to attest or an error code. */ int device_manager_get_eid_of_next_device_to_attest (struct device_manager *mgr) { uint8_t num_checked = 0; int starting_device; int i_device; int status; if ((mgr == NULL) || (mgr->num_devices == 0)) { return DEVICE_MGR_INVALID_ARGUMENT; } starting_device = (mgr->last_device_authenticated + 1) % mgr->num_devices; for (i_device = starting_device; num_checked < mgr->num_devices; i_device = (i_device + 1) % mgr->num_devices, ++num_checked) { if (!device_manager_can_device_be_attested ((mgr->entries[i_device].state))) { continue; } status = platform_has_timeout_expired (&mgr->entries[i_device].attestation_timeout); if (ROT_IS_ERROR (status)) { return status; } if (status) { goto found; } } return DEVICE_MGR_NO_DEVICES_AVAILABLE; found: mgr->last_device_authenticated = i_device; return mgr->entries[i_device].eid; } /** * Reset all authenticated devices back to discovered state. * * @param mgr Device manager instance to utilize. * * @return Completion status, 0 if success or an error code. */ int device_manager_reset_authenticated_devices (struct device_manager *mgr) { int i_device; int status; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } for (i_device = 0; i_device < mgr->num_devices; ++i_device) { if ((mgr->entries[i_device].state == DEVICE_MANAGER_AUTHENTICATED) || (mgr->entries[i_device].state == DEVICE_MANAGER_AUTHENTICATED_WITHOUT_CERTS)) { status = device_manager_update_device_state (mgr, i_device, DEVICE_MANAGER_NEVER_ATTESTED); if (status != 0) { return status; } } } return 0; } /** * Reset all discovered devices back to unidentified state. * * @param mgr Device manager instance to utilize. * * @return Completion status, 0 if success or an error code. */ int device_manager_reset_discovered_devices (struct device_manager *mgr) { int i_device; int status; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } for (i_device = 0; i_device < mgr->num_devices; ++i_device) { if (!device_manager_can_device_be_attested (mgr->entries[i_device].state)) { continue; } status = device_manager_update_device_state (mgr, i_device, DEVICE_MANAGER_UNIDENTIFIED); if (status != 0) { return status; } } return 0; } /** * Get device manager device table entry number by device IDs * * @param mgr Device manager instance to utilize. * @param pci_vid The PCI vendor ID to utilize. * @param pci_device_id The PCI device ID to utilize. * @param pci_subsystem_vid The PCI subsystem vendor ID to utilize. * @param pci_subsystem_id The PCI subsystem ID to utilize. * * @return Device number of entry if found or an error code. */ int device_manager_get_device_num_by_device_ids (struct device_manager *mgr, uint16_t pci_vid, uint16_t pci_device_id, uint16_t pci_subsystem_vid, uint16_t pci_subsystem_id) { int i_device; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } for (i_device = 0; i_device < mgr->num_devices; ++i_device) { if (mgr->entries[i_device].state == DEVICE_MANAGER_UNIDENTIFIED) { if ((mgr->entries[i_device].pci_vid == pci_vid) && (mgr->entries[i_device].pci_device_id == pci_device_id) && (mgr->entries[i_device].pci_subsystem_vid == pci_subsystem_vid) && (mgr->entries[i_device].pci_subsystem_id == pci_subsystem_id)) { return i_device; } } } return DEVICE_MGR_UNKNOWN_DEVICE; } /** * Update device manager device table entry device IDs * * @param mgr Device manager instance to utilize. * @param device_num Device table entry to update. * @param pci_vid The PCI vendor ID to utilize. * @param pci_device_id The PCI device ID to utilize. * @param pci_subsystem_vid The PCI subsystem vendor ID to utilize. * @param pci_subsystem_id The PCI subsystem ID to utilize. * * @return Completion status, 0 if success or an error code. */ int device_manager_update_device_ids (struct device_manager *mgr, int device_num, uint16_t pci_vid, uint16_t pci_device_id, uint16_t pci_subsystem_vid, uint16_t pci_subsystem_id) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (device_num >= mgr->num_devices) { return DEVICE_MGR_UNKNOWN_DEVICE; } mgr->entries[device_num].pci_vid = pci_vid; mgr->entries[device_num].pci_device_id = pci_device_id; mgr->entries[device_num].pci_subsystem_vid = pci_subsystem_vid; mgr->entries[device_num].pci_subsystem_id = pci_subsystem_id; return 0; } #ifdef ATTESTATION_SUPPORT_DEVICE_DISCOVERY /** * Add a node to device manager unidentified device linked list. * * @param mgr Device manager instance to utilize. * @param eid EID of device to add. * * @return 0 if completed successfully or an error code. */ int device_manager_add_unidentified_device (struct device_manager *mgr, uint8_t eid) { struct device_manager_unidentified_entry *new_entry; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } new_entry = platform_calloc (1, sizeof (struct device_manager_unidentified_entry)); if (new_entry == NULL) { return DEVICE_MGR_NO_MEMORY; } new_entry->eid = eid; if (mgr->unidentified == NULL) { new_entry->next = new_entry; mgr->unidentified = new_entry; } else { new_entry->next = mgr->unidentified->next; mgr->unidentified->next = new_entry; } return platform_init_timeout (0, &new_entry->discovery_timeout); } /** * Find device entry with requested EID in unidentified device list. * * @param mgr Device manager instance to utilize. * @param eid EID of device to find. * @param entry Container to fill with pointer to requested device entry. * @param prev Container to fill with pointer to device right before requested device entry. Can be * set to NULL if not needed. * * @return 0 if completed successfully or an error code. */ static int device_manager_find_unidentified_device (struct device_manager *mgr, uint8_t eid, struct device_manager_unidentified_entry **entry, struct device_manager_unidentified_entry **prev) { struct device_manager_unidentified_entry *previous; previous = mgr->unidentified; while (previous->next->eid != eid) { previous = previous->next; if (previous == mgr->unidentified) { return DEVICE_MGR_UNKNOWN_DEVICE; } } *entry = previous->next; if (prev != NULL) { *prev = previous; } return 0; } /** * Remove a node from device manager unidentified device linked list. * * @param mgr Device manager instance to utilize. * @param eid EID of device to remove. * * @return 0 if completed successfully or an error code. */ int device_manager_remove_unidentified_device (struct device_manager *mgr, uint8_t eid) { struct device_manager_unidentified_entry *previous; struct device_manager_unidentified_entry *entry; int status; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (mgr->unidentified == NULL) { return 0; } status = device_manager_find_unidentified_device (mgr, eid, &entry, &previous); if (status != 0) { return status; } previous->next = previous->next->next; if (previous->next == entry) { mgr->unidentified = NULL; } platform_free (entry); return 0; } /** * Mark a node from device manager unidentified device linked list as timed out. * * @param mgr Device manager instance to utilize. * @param eid EID of device to utilize. * * @return 0 if completed successfully or an error code. */ int device_manager_unidentified_device_timed_out (struct device_manager *mgr, uint8_t eid) { struct device_manager_unidentified_entry *entry; int status; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (mgr->unidentified == NULL) { return DEVICE_MGR_UNKNOWN_DEVICE; } status = device_manager_find_unidentified_device (mgr, eid, &entry, NULL); if (status != 0) { return status; } entry->timeout = true; return platform_init_timeout (mgr->unidentified_timeout_ms, &entry->discovery_timeout); } /** * Get EID of next device to discover from device manager unidentified device linked list. * * @param mgr Device manager instance to utilize. * * @return EID if found or an error code. */ int device_manager_get_eid_of_next_device_to_discover (struct device_manager *mgr) { struct device_manager_unidentified_entry *runner; int status; if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } if (mgr->unidentified == NULL) { return DEVICE_MGR_NO_DEVICES_AVAILABLE; } runner = mgr->unidentified; status = platform_has_timeout_expired (&runner->discovery_timeout); if (ROT_IS_ERROR (status)) { return status; } if (!runner->timeout || status) { goto found; } while (runner->next != mgr->unidentified) { status = platform_has_timeout_expired (&runner->discovery_timeout); if (ROT_IS_ERROR (status)) { return status; } if (!runner->timeout || status) { goto found; } else { runner = runner->next; } } return DEVICE_MGR_NO_DEVICES_AVAILABLE; found: runner->timeout = 0; mgr->unidentified = runner->next; return runner->eid; } #endif /** * Check a timeout to see if it will expire before a specified duration. * * @param timeout The timeout to check. * @param duration_ms The duration to check the timeout against. * * @return The minimum of the remaining time in the specified timeout or the specified duration. */ static uint32_t device_manager_find_min_timeout (const platform_clock *timeout, uint32_t duration_ms) { uint32_t check_ms; int status; status = platform_get_timeout_remaining (timeout, &check_ms); if (status != 0) { /* If the remaining timeout could not be determined, assume the timeout has expired. */ check_ms = 0; } return min (check_ms, duration_ms); } /** * Get time in milliseconds till next attestation or discovery action. * * @param mgr Device manager instance to utilize. * * @return Time in milliseconds till next action. */ uint32_t device_manager_get_time_till_next_action (struct device_manager *mgr) { uint32_t duration_ms = DEVICE_MANAGER_MIN_ACTIVITY_CHECK; uint8_t i_device; if (mgr == NULL) { return duration_ms; } for (i_device = 0; i_device < mgr->num_devices; ++i_device) { if (!device_manager_can_device_be_attested (mgr->entries[i_device].state)) { continue; } duration_ms = device_manager_find_min_timeout (&mgr->entries[i_device].attestation_timeout, duration_ms); } #ifdef ATTESTATION_SUPPORT_DEVICE_DISCOVERY { struct device_manager_unidentified_entry *runner = mgr->unidentified; if (runner == NULL) { return duration_ms; } duration_ms = device_manager_find_min_timeout (&runner->discovery_timeout, duration_ms); while (runner->next != mgr->unidentified) { runner = runner->next; duration_ms = device_manager_find_min_timeout (&runner->discovery_timeout, duration_ms); } } #endif return duration_ms; } /** * Update attestation status buffer with attestation statuses of responder devices, then return * pointer to buffer. Byte position in buffer maps to the index of a component in the PCD. Byte * value maps to device_manager_device_state enum value. * * @param mgr Device manager instance to utilize. * @param attestation_status Buffer to fill with pointer to attestation status buffer. If the * device has no responder devices, the output will be NULL. * * @return Length of attestation_status if completed successfully or an error code. If the device * has no responder devices, the length returned shall be 0. */ int device_manager_get_attestation_status (struct device_manager *mgr, const uint8_t **attestation_status) { size_t attestation_status_len; int i_device; size_t i_entry = 0; struct pcd_supported_component *supported_component = NULL; if ((mgr == NULL) || (attestation_status == NULL)) { return DEVICE_MGR_INVALID_ARGUMENT; } attestation_status_len = mgr->num_responder_devices + (mgr->num_unique_responder_devices * DEVICE_MANAGER_ATTESTATION_STATUS_COMPONENT_HEADER_LEN); *attestation_status = mgr->attestation_status; if (mgr->num_responder_devices != 0) { // Skip the requester devices in the beginning of the list for (i_device = mgr->num_requester_devices; i_device < mgr->num_devices; ++i_device) { if ((i_device == mgr->num_requester_devices) || (supported_component->component_id != mgr->entries[i_device].component_id)) { // Add a new header if this is the first entry in the list or a new component type. supported_component = (struct pcd_supported_component*) &mgr->attestation_status[i_entry]; supported_component->component_id = mgr->entries[i_device].component_id; supported_component->component_count = 1; i_entry += DEVICE_MANAGER_ATTESTATION_STATUS_COMPONENT_HEADER_LEN; } else { // Otherwise, increment the total count for the current component type. supported_component->component_count++; } // Copy current attestation state for the component to the status output. if (!mgr->attestable_components_list_invalid) { mgr->attestation_status[i_entry] = (uint8_t) mgr->entries[i_device].state; } else { mgr->attestation_status[i_entry] = 0xFF; } i_entry += 1; } } return attestation_status_len; } int device_manager_mark_component_attestation_invalid (struct device_manager *mgr) { if (mgr == NULL) { return DEVICE_MGR_INVALID_ARGUMENT; } mgr->attestable_components_list_invalid = true; return 0; } bool device_manager_is_device_unattestable (struct device_manager *mgr, uint8_t eid) { int device_num; if (mgr == NULL) { return false; } device_num = device_manager_get_device_num (mgr, eid); if (ROT_IS_ERROR (device_num)) { return false; } return (mgr->entries[device_num].state == DEVICE_MANAGER_NOT_ATTESTABLE); }