From d455a53124544860d870d269c7b9636ccc8e644a Mon Sep 17 00:00:00 2001 From: Steven Bellock Date: Fri, 14 Feb 2025 10:42:00 -0800 Subject: [PATCH] Encapsulate SEND_EVENT for Requester as recipient Signed-off-by: Steven Bellock --- include/industry_standard/spdm.h | 43 +- include/internal/libspdm_common_lib.h | 15 + include/internal/libspdm_requester_lib.h | 10 +- include/library/spdm_common_lib.h | 42 ++ .../libspdm_com_context_data.c | 13 +- library/spdm_common_lib/libspdm_com_support.c | 23 + library/spdm_requester_lib/CMakeLists.txt | 1 + .../libspdm_req_encap_event_ack.c | 328 ++++++++++++ .../libspdm_req_encap_request.c | 6 +- .../spdm_unit_test_common/event_support.c | 49 +- .../spdm_unit_test_common/spdm_unit_test.h | 5 +- unit_test/test_spdm_requester/CMakeLists.txt | 2 + .../test_spdm_requester/encap_event_ack.c | 474 ++++++++++++++++++ .../error_test/encap_event_ack_err.c | 380 ++++++++++++++ .../test_spdm_requester/test_spdm_requester.c | 14 +- 15 files changed, 1397 insertions(+), 8 deletions(-) create mode 100644 library/spdm_requester_lib/libspdm_req_encap_event_ack.c create mode 100644 unit_test/test_spdm_requester/encap_event_ack.c create mode 100644 unit_test/test_spdm_requester/error_test/encap_event_ack_err.c diff --git a/include/industry_standard/spdm.h b/include/industry_standard/spdm.h index bdf94e53cc5..367c53aeca0 100644 --- a/include/industry_standard/spdm.h +++ b/include/industry_standard/spdm.h @@ -57,6 +57,7 @@ /* SPDM response code (1.3) */ #define SPDM_SUPPORTED_EVENT_TYPES 0x62 #define SPDM_SUBSCRIBE_EVENT_TYPES_ACK 0x70 +#define SPDM_EVENT_ACK 0x71 #define SPDM_MEASUREMENT_EXTENSION_LOG 0x6F #define SPDM_KEY_PAIR_INFO 0x7C #define SPDM_SET_KEY_PAIR_INFO_ACK 0x7D @@ -92,6 +93,7 @@ /* SPDM request code (1.3) */ #define SPDM_GET_SUPPORTED_EVENT_TYPES 0xE2 #define SPDM_SUBSCRIBE_EVENT_TYPES 0xF0 +#define SPDM_SEND_EVENT 0xF1 #define SPDM_GET_MEASUREMENT_EXTENSION_LOG 0xEF #define SPDM_GET_KEY_PAIR_INFO 0xFC #define SPDM_SET_KEY_PAIR_INFO 0xFD @@ -1305,6 +1307,20 @@ typedef struct { * param2 == RSVD */ } spdm_subscribe_event_types_ack_response_t; +typedef struct { + spdm_message_header_t header; + /* param1 == RSVD + * param2 == RSVD */ + uint32_t event_count; + /* event_list[event_count]*/ +} spdm_send_event_request_t; + +typedef struct { + spdm_message_header_t header; + /* param1 == RSVD + * param2 == RSVD */ +} spdm_event_ack_response_t; + /* SPDM GET_MEASUREMENT_EXTENSION_LOG request */ typedef struct { spdm_message_header_t header; @@ -1415,8 +1431,6 @@ typedef struct { * param2 == RSVD*/ } spdm_set_key_pair_info_ack_response_t; -#pragma pack() - #define SPDM_VERSION_1_1_BIN_CONCAT_LABEL "spdm1.1 " #define SPDM_VERSION_1_2_BIN_CONCAT_LABEL "spdm1.2 " #define SPDM_VERSION_1_3_BIN_CONCAT_LABEL "spdm1.3 " @@ -1468,9 +1482,34 @@ typedef struct { #define SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE 3 #define SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED 4 +/* DMTF Event sizes in bytes. */ +#define SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE 8 +#define SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED_SIZE 32 +#define SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE_SIZE 32 +#define SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE 1 + +typedef struct { + uint32_t last_acked_event_inst_id; + uint32_t last_lost_event_inst_id; +} spdm_dmtf_event_type_event_lost_t; + +typedef struct { + uint8_t changed_measurements[SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED_SIZE]; +} spdm_dmtf_event_type_measurement_changed_t; + +typedef struct { + uint8_t pre_update_measurement_changes[SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE_SIZE]; +} spdm_dmtf_event_type_measurement_pre_update_t; + +typedef struct { + uint8_t certificate_changed; +} spdm_dmtf_event_type_certificate_changed_t; + /*SPDM SET_KEY_PAIR_INFO operation*/ #define SPDM_SET_KEY_PAIR_INFO_CHANGE_OPERATION 0 #define SPDM_SET_KEY_PAIR_INFO_ERASE_OPERATION 1 #define SPDM_SET_KEY_PAIR_INFO_GENERATE_OPERATION 2 +#pragma pack() + #endif /* SPDM_H */ diff --git a/include/internal/libspdm_common_lib.h b/include/internal/libspdm_common_lib.h index 834867604c4..fee81bb3179 100644 --- a/include/internal/libspdm_common_lib.h +++ b/include/internal/libspdm_common_lib.h @@ -623,6 +623,10 @@ typedef struct { libspdm_vendor_response_callback_func vendor_response_callback; libspdm_vendor_get_id_callback_func vendor_response_get_id; #endif /* LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES */ + +#if LIBSPDM_EVENT_RECIPIENT_SUPPORT + libspdm_process_event_func process_event; +#endif /* LIBSPDM_EVENT_RECIPIENT_SUPPORT */ } libspdm_context_t; #define LIBSPDM_CONTEXT_SIZE_WITHOUT_SECURED_CONTEXT (sizeof(libspdm_context_t)) @@ -1780,4 +1784,15 @@ uint8_t libspdm_mask_mel_specification(libspdm_context_t *spdm_context, uint8_t */ uint32_t libspdm_mask_base_asym_algo(libspdm_context_t *spdm_context, uint32_t base_asym_algo); +/** + * Check if the combination of SVH ID and VendorIDLen are legal. + * + * @param id Registry or standards body identifier (SPDM_REGISTRY_ID_*). + * @param vendor_id_len Length, in bytes, of the VendorID field. + * + * @retval true The ID and VendorIDLen are legal. + * @retval false The ID and VendorIDLen are illegal. + */ +bool libspdm_validate_svh_vendor_id_len(uint8_t id, uint8_t vendor_id_len); + #endif /* SPDM_COMMON_LIB_INTERNAL_H */ diff --git a/include/internal/libspdm_requester_lib.h b/include/internal/libspdm_requester_lib.h index 050cda95ab8..7917679c7d4 100644 --- a/include/internal/libspdm_requester_lib.h +++ b/include/internal/libspdm_requester_lib.h @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -473,6 +473,14 @@ libspdm_return_t libspdm_get_encap_response_key_update(void *spdm_context, void *request, size_t *response_size, void *response); + +#if LIBSPDM_EVENT_RECIPIENT_SUPPORT +libspdm_return_t libspdm_get_encap_response_event_ack(void *spdm_context, + size_t request_size, + void *request, + size_t *response_size, + void *response); +#endif /* LIBSPDM_EVENT_RECIPIENT_SUPPORT */ #endif /* LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP */ /** diff --git a/include/library/spdm_common_lib.h b/include/library/spdm_common_lib.h index 9fefa6caa00..83ca2c09de7 100644 --- a/include/library/spdm_common_lib.h +++ b/include/library/spdm_common_lib.h @@ -1005,6 +1005,48 @@ typedef libspdm_return_t (*libspdm_vendor_response_callback_func)( #endif /* LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES */ +#if LIBSPDM_EVENT_RECIPIENT_SUPPORT +/** + * SPDM Event callback function. + * + * When an event is received the library performs basic validation to ensure that SVH ID and SVH + * VendorIDLen are legal. If the event is of a DMTF event type then the library will ensure that + * EventTypeId and EventDetailLen are legal. If a SEND_EVENT message contains multiple events then + * the library will call this function with sequentially-increasing Event Instance ID. + * + * @param spdm_context A pointer to the SPDM context. + * @param session_id Secure session ID. + * @param event_instance_id Counter that increases by one for each event. + * @param svh_id Registry or standards body identifier (SPDM_REGISTRY_ID_*). + * @param svh_vendor_id_len Length, in bytes, of the svh_vendor_id field. + * @param svh_vendor_id Vendor ID assigned by the Registry or Standards Body. If the value of + * svh_vendor_id_len is 0 then this is NULL. + * @param event_type_id Event type identifier. If svh_id is SPDM_REGISTRY_ID_ DMTF then this is + * one of the SPDM_DMTF_EVENT_TYPE_* macros. + * @param event_detail_len Size, in bytes, of event_detail. + * @param event_detail Details of the event. + **/ +typedef libspdm_return_t (*libspdm_process_event_func)(void *spdm_context, + uint32_t session_id, + uint32_t event_instance_id, + uint8_t svh_id, + uint8_t svh_vendor_id_len, + void *svh_vendor_id, + uint16_t event_type_id, + uint16_t event_detail_len, + void *event_detail); + +/** + * Register callback to process SPDM events. + * + * @param spdm_context A pointer to the SPDM context. + * @param process_event_func Function that processes individual SPDM events. If NULL then function + * will not be called as events are processed. + **/ +void libspdm_register_event_callback(void *spdm_context, + libspdm_process_event_func process_event_func); +#endif /* LIBSPDM_EVENT_RECIPIENT_SUPPORT */ + #ifdef __cplusplus } #endif diff --git a/library/spdm_common_lib/libspdm_com_context_data.c b/library/spdm_common_lib/libspdm_com_context_data.c index 7476abfb8a4..b20ea0f3d40 100644 --- a/library/spdm_common_lib/libspdm_com_context_data.c +++ b/library/spdm_common_lib/libspdm_com_context_data.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -3278,3 +3278,14 @@ bool libspdm_negotiate_connection_version(spdm_version_number_t *common_version, } return false; } + +#if LIBSPDM_EVENT_RECIPIENT_SUPPORT +void libspdm_register_event_callback(void *context, + libspdm_process_event_func process_event_func) +{ + libspdm_context_t *spdm_context; + + spdm_context = context; + spdm_context->process_event = process_event_func; +} +#endif /* LIBSPDM_EVENT_RECIPIENT_SUPPORT */ diff --git a/library/spdm_common_lib/libspdm_com_support.c b/library/spdm_common_lib/libspdm_com_support.c index 992557cc360..b00527e3c2e 100644 --- a/library/spdm_common_lib/libspdm_com_support.c +++ b/library/spdm_common_lib/libspdm_com_support.c @@ -457,3 +457,26 @@ uint16_t libspdm_mask_alg_supported(libspdm_context_t *spdm_context, uint8_t alg return 0; } } + +bool libspdm_validate_svh_vendor_id_len(uint8_t id, uint8_t vendor_id_len) +{ + switch (id) { + case SPDM_REGISTRY_ID_DMTF: + case SPDM_REGISTRY_ID_VESA: + return (vendor_id_len == 0); + case SPDM_REGISTRY_ID_TCG: + case SPDM_REGISTRY_ID_USB: + case SPDM_REGISTRY_ID_PCISIG: + case SPDM_REGISTRY_ID_MIPI: + case SPDM_REGISTRY_ID_CXL: + case SPDM_REGISTRY_ID_JEDEC: + return ((vendor_id_len == 0) || (vendor_id_len == 2)); + case SPDM_REGISTRY_ID_IANA: + case SPDM_REGISTRY_ID_HDBASET: + return ((vendor_id_len == 0) || (vendor_id_len == 4)); + case SPDM_REGISTRY_ID_IANA_CBOR: + return true; + default: + return false; + } +} diff --git a/library/spdm_requester_lib/CMakeLists.txt b/library/spdm_requester_lib/CMakeLists.txt index f449843caa7..98048f4b158 100644 --- a/library/spdm_requester_lib/CMakeLists.txt +++ b/library/spdm_requester_lib/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(spdm_requester_lib libspdm_req_encap_digests.c libspdm_req_encap_error.c libspdm_req_encap_key_update.c + libspdm_req_encap_event_ack.c libspdm_req_encap_request.c libspdm_req_end_session.c libspdm_req_finish.c diff --git a/library/spdm_requester_lib/libspdm_req_encap_event_ack.c b/library/spdm_requester_lib/libspdm_req_encap_event_ack.c new file mode 100644 index 00000000000..832225876d5 --- /dev/null +++ b/library/spdm_requester_lib/libspdm_req_encap_event_ack.c @@ -0,0 +1,328 @@ +/** + * Copyright Notice: + * Copyright 2025 DMTF. All rights reserved. + * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md + **/ + + #include "internal/libspdm_requester_lib.h" + + #if (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && (LIBSPDM_EVENT_RECIPIENT_SUPPORT) + +static bool validate_dmtf_event_type(uint16_t event_type_id, uint16_t event_detail_len) +{ + switch (event_type_id) { + case SPDM_DMTF_EVENT_TYPE_EVENT_LOST: + return (event_detail_len == SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE); + case SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED: + return (event_detail_len == SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED_SIZE); + case SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE: + return (event_detail_len == SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE_SIZE); + case SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED: + return (event_detail_len == SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE); + default: + return false; + } +} + +static bool parse_and_send_event(libspdm_context_t *context, uint32_t session_id, + void *event_data, void **next_event_data) +{ + libspdm_return_t status; + uint8_t *ptr; + uint32_t event_instance_id; + uint8_t svh_id; + uint8_t svh_vendor_id_len; + void *svh_vendor_id; + uint16_t event_type_id; + uint16_t event_detail_len; + + LIBSPDM_ASSERT(context->process_event != NULL); + + ptr = event_data; + event_instance_id = libspdm_read_uint32(ptr); + + ptr += sizeof(uint32_t); + ptr += sizeof(uint32_t); + svh_id = *ptr; + ptr++; + svh_vendor_id_len = *ptr; + ptr++; + + if (svh_vendor_id_len == 0) { + svh_vendor_id = NULL; + } else { + svh_vendor_id = ptr; + } + ptr += svh_vendor_id_len; + + event_type_id = libspdm_read_uint16(ptr); + ptr += sizeof(uint16_t); + event_detail_len = libspdm_read_uint16(ptr); + ptr += sizeof(uint16_t); + + status = context->process_event(context, session_id, event_instance_id, svh_id, + svh_vendor_id_len, svh_vendor_id, event_type_id, + event_detail_len, ptr); + + if (next_event_data != NULL) { + ptr += event_detail_len; + *next_event_data = ptr; + } + + return (status == LIBSPDM_STATUS_SUCCESS); +} + +static void *find_event_instance_id(void *events_list_start, uint32_t event_count, + uint32_t target_event_instance_id) +{ + uint32_t index; + uint8_t *ptr; + + ptr = events_list_start; + + for (index = 0; index < event_count; index++) { + uint32_t event_instance_id; + + event_instance_id = libspdm_read_uint32(ptr); + + if (event_instance_id == target_event_instance_id) { + return ptr; + } else { + uint8_t vendor_id_len; + uint16_t event_detail_len; + + ptr += sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); + vendor_id_len = *ptr; + ptr += sizeof(uint8_t); + ptr += vendor_id_len; + ptr += sizeof(uint16_t); + event_detail_len = libspdm_read_uint16(ptr); + ptr += sizeof(uint16_t); + ptr += event_detail_len; + } + } + + return NULL; +} + +static bool check_for_space(const uint8_t *ptr, const uint8_t *end_ptr, size_t increment) +{ + LIBSPDM_ASSERT(ptr <= end_ptr); + + return ((uintptr_t)(end_ptr - ptr) >= increment); +} + +libspdm_return_t libspdm_get_encap_response_event_ack(void *spdm_context, + size_t request_size, + void *request, + size_t *response_size, + void *response) +{ + spdm_send_event_request_t *spdm_request; + spdm_event_ack_response_t *spdm_response; + libspdm_context_t *context; + uint32_t session_id; + libspdm_session_info_t *session_info; + libspdm_session_state_t session_state; + uint64_t index; + uint32_t prev_event_instance_id; + uint32_t event_instance_id_min; + uint32_t event_instance_id_max; + bool events_list_is_sequential; + uint8_t *ptr; + const uint8_t *end_ptr = (uint8_t *)request + request_size; + size_t calculated_request_size; + + context = spdm_context; + spdm_request = request; + + if (libspdm_get_connection_version(context) < SPDM_MESSAGE_VERSION_13) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_UNSUPPORTED_REQUEST, SPDM_SEND_EVENT, response_size, response); + } + if (spdm_request->header.spdm_version != libspdm_get_connection_version(context)) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_VERSION_MISMATCH, 0, response_size, response); + } + if (!libspdm_is_capabilities_flag_supported( + context, true, 0, SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_EVENT_CAP)) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_UNSUPPORTED_REQUEST, SPDM_SEND_EVENT, response_size, response); + } + if (!context->last_spdm_request_session_id_valid) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_SESSION_REQUIRED, 0, response_size, response); + } + + session_id = context->last_spdm_request_session_id; + session_info = libspdm_get_session_info_via_session_id(context, session_id); + if (session_info == NULL) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + session_state = libspdm_secured_message_get_session_state( + session_info->secured_message_context); + if (session_state != LIBSPDM_SESSION_STATE_ESTABLISHED) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + libspdm_reset_message_buffer_via_request_code(context, NULL, + spdm_request->header.request_response_code); + + if (!check_for_space((uint8_t *)request, end_ptr, sizeof(spdm_send_event_request_t))) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + if (spdm_request->event_count == 0) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + ptr = (uint8_t *)(spdm_request + 1); + + event_instance_id_min = UINT32_MAX; + event_instance_id_max = 0; + events_list_is_sequential = true; + + calculated_request_size = sizeof(spdm_send_event_request_t); + + /* Parse and validate all events for size and fields. */ + for (index = 0; index < spdm_request->event_count; index++) { + uint32_t event_instance_id; + uint8_t svh_id; + uint8_t svh_vendor_id_len; + uint16_t event_type_id; + uint16_t event_detail_len; + + if (!check_for_space(ptr, end_ptr, + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) + + sizeof(uint8_t))) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + event_instance_id = libspdm_read_uint32(ptr); + + if ((index != 0) && events_list_is_sequential) { + if (event_instance_id != (prev_event_instance_id + 1)) { + events_list_is_sequential = false; + } + } + if (event_instance_id < event_instance_id_min) { + event_instance_id_min = event_instance_id; + } + if (event_instance_id > event_instance_id_max) { + event_instance_id_max = event_instance_id; + } + prev_event_instance_id = event_instance_id; + + ptr += sizeof(uint32_t) + sizeof(uint32_t); + svh_id = *ptr; + ptr += sizeof(uint8_t); + svh_vendor_id_len = *ptr; + ptr += sizeof(uint8_t); + + if (!libspdm_validate_svh_vendor_id_len(svh_id, svh_vendor_id_len)) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + if (!check_for_space(ptr, end_ptr, + (size_t)svh_vendor_id_len + sizeof(uint16_t) + sizeof(uint16_t))) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + ptr += svh_vendor_id_len; + + event_type_id = libspdm_read_uint16(ptr); + ptr += sizeof(uint16_t); + event_detail_len = libspdm_read_uint16(ptr); + ptr += sizeof(uint16_t); + + if (svh_id == SPDM_REGISTRY_ID_DMTF) { + if (!validate_dmtf_event_type(event_type_id, event_detail_len)) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + } + + if (!check_for_space(ptr, end_ptr, (size_t)event_detail_len)) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + ptr += event_detail_len; + calculated_request_size += sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) + + sizeof(uint8_t) + (size_t)svh_vendor_id_len + sizeof(uint16_t) + + sizeof(uint16_t) + (size_t)event_detail_len; + } + + /* Event must be sent in a secure session so message size can be calculated exactly. */ + if (request_size != calculated_request_size) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + /* If event instance IDs are not sequential then ensure there are no gaps or duplicates before + * sending individual events to Integrator. */ + if (!events_list_is_sequential) { + void *event_data = spdm_request + 1; + + if ((event_instance_id_max - event_instance_id_min + 1) != spdm_request->event_count) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + for (index = 0; index < spdm_request->event_count; index++) { + if (find_event_instance_id(event_data, spdm_request->event_count, + event_instance_id_min + (uint32_t)index) == NULL) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + } + } + + if (context->process_event != NULL) { + void *next_event_data = spdm_request + 1; + + for (index = 0; index < spdm_request->event_count; index++) { + if (events_list_is_sequential) { + if (!parse_and_send_event(context, session_id, next_event_data, &next_event_data)) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + } else { + void *event_data; + + event_data = find_event_instance_id(next_event_data, spdm_request->event_count, + event_instance_id_min + (uint32_t)index); + if (event_data == NULL) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + + if (!parse_and_send_event(context, session_id, event_data, NULL)) { + return libspdm_generate_encap_error_response( + context, SPDM_ERROR_CODE_INVALID_REQUEST, 0, response_size, response); + } + } + } + } + + spdm_response = response; + + spdm_response->header.spdm_version = spdm_request->header.spdm_version; + spdm_response->header.request_response_code = SPDM_EVENT_ACK; + spdm_response->header.param1 = 0; + spdm_response->header.param2 = 0; + + *response_size = sizeof(spdm_event_ack_response_t); + + return LIBSPDM_STATUS_SUCCESS; +} + +#endif /* (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && (LIBSPDM_EVENT_RECIPIENT_SUPPORT) */ diff --git a/library/spdm_requester_lib/libspdm_req_encap_request.c b/library/spdm_requester_lib/libspdm_req_encap_request.c index 2b94340a408..ad84f475740 100644 --- a/library/spdm_requester_lib/libspdm_req_encap_request.c +++ b/library/spdm_requester_lib/libspdm_req_encap_request.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -48,6 +48,10 @@ libspdm_get_encap_response_func_via_request_code(uint8_t request_response_code) #endif /* LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP */ { SPDM_KEY_UPDATE, libspdm_get_encap_response_key_update }, + + #if LIBSPDM_EVENT_RECIPIENT_SUPPORT + { SPDM_SEND_EVENT, libspdm_get_encap_response_event_ack }, + #endif /* LIBSPDM_EVENT_RECIPIENT_SUPPORT */ }; for (index = 0; index < LIBSPDM_ARRAY_SIZE(get_encap_response_struct); index++) { diff --git a/unit_test/spdm_unit_test_common/event_support.c b/unit_test/spdm_unit_test_common/event_support.c index 4c24dab39bd..deb6a384a4c 100644 --- a/unit_test/spdm_unit_test_common/event_support.c +++ b/unit_test/spdm_unit_test_common/event_support.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2022 DMTF. All rights reserved. + * Copyright 2023-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -29,6 +29,14 @@ typedef struct { uint16_t event_type_id; uint16_t reserved; } event_type_t; + +typedef struct { + uint32_t event_instance_id; + uint8_t reserved[4]; + event_group_id_0byte_t event_group_id; + uint16_t event_type_id; + uint16_t event_detail_len; +} event_data_dmtf_t; #pragma pack() void generate_dmtf_event_group(void *buffer, uint8_t *total_bytes, uint32_t attributes, @@ -97,3 +105,42 @@ void generate_dmtf_event_group(void *buffer, uint8_t *total_bytes, uint32_t attr *total_bytes += (uint8_t)sizeof(event_type_t); } } + +void generate_dmtf_event_data(void *buffer, uint8_t *total_bytes, uint32_t event_instance_id, + uint16_t event_type_id, void *event_detail) +{ + event_data_dmtf_t *event_data; + uint16_t event_detail_len; + + event_data = buffer; + + event_data->event_instance_id = event_instance_id; + event_data->reserved[0] = 0; + event_data->reserved[1] = 0; + event_data->reserved[2] = 0; + event_data->reserved[3] = 0; + event_data->event_group_id.id = SPDM_REGISTRY_ID_DMTF; + event_data->event_group_id.vendor_id_len = 0; + event_data->event_type_id = event_type_id; + + switch (event_type_id) { + case SPDM_DMTF_EVENT_TYPE_EVENT_LOST: + event_detail_len = SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE; + break; + case SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED: + event_detail_len = SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED_SIZE; + break; + case SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE: + event_detail_len = SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE_SIZE; + break; + case SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED: + event_detail_len = SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE; + break; + } + + event_data->event_detail_len = event_detail_len; + + memcpy(event_data + 1, event_detail, (size_t)event_detail_len); + + *total_bytes = (uint8_t)sizeof(event_data_dmtf_t) + (uint8_t)event_detail_len; +} diff --git a/unit_test/spdm_unit_test_common/spdm_unit_test.h b/unit_test/spdm_unit_test_common/spdm_unit_test.h index 89649677f84..18033fceacd 100644 --- a/unit_test/spdm_unit_test_common/spdm_unit_test.h +++ b/unit_test/spdm_unit_test_common/spdm_unit_test.h @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -166,4 +166,7 @@ void generate_dmtf_event_group(void *buffer, uint8_t *total_bytes, uint32_t attr bool inc_event_lost, bool inc_meas_changed, bool inc_meas_pre_update, bool inc_cert_changed); +void generate_dmtf_event_data(void *buffer, uint8_t *total_bytes, uint32_t event_instance_id, + uint16_t event_type_id, void *event_detail); + #endif diff --git a/unit_test/test_spdm_requester/CMakeLists.txt b/unit_test/test_spdm_requester/CMakeLists.txt index 0e371a4e62d..60e90b63363 100644 --- a/unit_test/test_spdm_requester/CMakeLists.txt +++ b/unit_test/test_spdm_requester/CMakeLists.txt @@ -37,6 +37,7 @@ target_sources(test_spdm_requester encap_certificate.c encap_challenge_auth.c encap_digests.c + encap_event_ack.c encap_key_update.c encap_request.c set_certificate.c @@ -57,6 +58,7 @@ target_sources(test_spdm_requester error_test/vendor_request_err.c error_test/get_key_pair_info_err.c error_test/set_key_pair_info_err.c + error_test/encap_event_ack_err.c ${LIBSPDM_DIR}/unit_test/spdm_unit_test_common/common.c ${LIBSPDM_DIR}/unit_test/spdm_unit_test_common/algo.c ${LIBSPDM_DIR}/unit_test/spdm_unit_test_common/support.c diff --git a/unit_test/test_spdm_requester/encap_event_ack.c b/unit_test/test_spdm_requester/encap_event_ack.c new file mode 100644 index 00000000000..0dd44ecdf2a --- /dev/null +++ b/unit_test/test_spdm_requester/encap_event_ack.c @@ -0,0 +1,474 @@ +/** + * Copyright Notice: + * Copyright 2025 DMTF. All rights reserved. + * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md + **/ + +#include "spdm_unit_test.h" +#include "internal/libspdm_requester_lib.h" + +#if (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && (LIBSPDM_EVENT_RECIPIENT_SUPPORT) + +static uint8_t m_spdm_request_buffer[0x1000]; +static uint8_t m_spdm_response_buffer[0x1000]; + +static const uint32_t m_session_id = 0xffffffff; + +typedef struct { + uint32_t event_instance_id; + uint8_t svh_id; + uint8_t svh_vendor_id_len; + uint8_t svh_vendor_id[4]; + uint16_t event_type_id; + uint16_t event_detail_len; + uint8_t event_detail[100]; +} expected_event_t; + +static expected_event_t m_expected_event[4]; +static uint32_t m_event_counter; + +static libspdm_return_t process_event(void *spdm_context, + uint32_t session_id, + uint32_t event_instance_id, + uint8_t svh_id, + uint8_t svh_vendor_id_len, + void *svh_vendor_id, + uint16_t event_type_id, + uint16_t event_detail_len, + void *event_detail) +{ + printf("Event Received\n"); + printf("Event Instance ID = [0x%x]\n", event_instance_id); + printf("SVH ID = [0x%x], SVH VendorIDLen = [0x%x]\n", svh_id, svh_vendor_id_len); + if (svh_vendor_id_len != 0) { + printf("SVH VendorID\n"); + libspdm_dump_hex(svh_vendor_id, svh_vendor_id_len); + printf("\n"); + } + printf("EventTypeID = [0x%x], EventDetailLen = [0x%x]\n", event_type_id, event_detail_len); + printf("Event Detail\n"); + libspdm_dump_hex(event_detail, event_detail_len); + + assert_int_equal(session_id, m_session_id); + assert_int_equal(event_instance_id, m_expected_event[m_event_counter].event_instance_id); + assert_int_equal(event_type_id, m_expected_event[m_event_counter].event_type_id); + assert_int_equal(svh_id, m_expected_event[m_event_counter].svh_id); + assert_int_equal(svh_vendor_id_len, m_expected_event[m_event_counter].svh_vendor_id_len); + if (svh_vendor_id_len == 0) { + assert_ptr_equal(svh_vendor_id, NULL); + } + assert_int_equal(event_detail_len, m_expected_event[m_event_counter].event_detail_len); + assert_memory_equal(m_expected_event[m_event_counter].event_detail, event_detail, + event_detail_len); + m_event_counter++; + + return LIBSPDM_STATUS_SUCCESS; +} + +static void set_standard_state(libspdm_context_t *spdm_context) +{ + libspdm_session_info_t *session_info; + + spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_13 << + SPDM_VERSION_NUMBER_SHIFT_BIT; + spdm_context->connection_info.connection_state = LIBSPDM_CONNECTION_STATE_NEGOTIATED; + + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_EVENT_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCRYPT_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MAC_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_KEY_EX_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCAP_CAP; + + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCRYPT_CAP; + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MAC_CAP; + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_KEY_EX_CAP; + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCAP_CAP; + + spdm_context->connection_info.algorithm.base_hash_algo = m_libspdm_use_hash_algo; + spdm_context->connection_info.algorithm.base_asym_algo = m_libspdm_use_asym_algo; + spdm_context->connection_info.algorithm.dhe_named_group = m_libspdm_use_dhe_algo; + spdm_context->connection_info.algorithm.aead_cipher_suite = m_libspdm_use_aead_algo; + + spdm_context->latest_session_id = m_session_id; + spdm_context->last_spdm_request_session_id_valid = true; + spdm_context->last_spdm_request_session_id = m_session_id; + session_info = &spdm_context->session_info[0]; + libspdm_session_info_init(spdm_context, session_info, m_session_id, true); + libspdm_secured_message_set_session_state( + session_info->secured_message_context, + LIBSPDM_SESSION_STATE_ESTABLISHED); + + libspdm_register_event_callback(spdm_context, process_event); +} + +/* Send exactly one event. */ +static void test_libspdm_requester_encap_event_ack_case1(void **state) +{ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + spdm_send_event_request_t *send_event; + size_t request_size; + spdm_event_ack_response_t *event_ack; + size_t response_size = sizeof(m_spdm_response_buffer); + uint8_t event_data_size; + spdm_dmtf_event_type_event_lost_t event_lost; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x01; + + set_standard_state(spdm_context); + + send_event = (spdm_send_event_request_t *)m_spdm_request_buffer; + + send_event->header.spdm_version = SPDM_MESSAGE_VERSION_13; + send_event->header.request_response_code = SPDM_SEND_EVENT; + send_event->header.param1 = 0; + send_event->header.param2 = 0; + send_event->event_count = 1; + + event_lost.last_acked_event_inst_id = 0xffeeddcc; + event_lost.last_lost_event_inst_id = 0x55667788; + + generate_dmtf_event_data(send_event + 1, &event_data_size, 0x11223344, + SPDM_DMTF_EVENT_TYPE_EVENT_LOST, &event_lost); + + m_event_counter = 0; + m_expected_event[0].event_instance_id = 0x11223344; + m_expected_event[0].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[0].svh_vendor_id_len = 0; + m_expected_event[0].event_type_id = SPDM_DMTF_EVENT_TYPE_EVENT_LOST; + m_expected_event[0].event_detail_len = SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE; + memcpy(m_expected_event[0].event_detail, &event_lost, SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE); + + request_size = sizeof(spdm_send_event_request_t) + event_data_size; + + status = libspdm_get_encap_response_event_ack(spdm_context, + request_size, + m_spdm_request_buffer, + &response_size, + m_spdm_response_buffer); + + event_ack = (spdm_event_ack_response_t *)m_spdm_response_buffer; + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + + assert_int_equal(response_size, sizeof(spdm_event_ack_response_t)); + assert_int_equal(event_ack->header.spdm_version, SPDM_MESSAGE_VERSION_13); + assert_int_equal(event_ack->header.request_response_code, SPDM_EVENT_ACK); + assert_int_equal(event_ack->header.param1, 0); + assert_int_equal(event_ack->header.param2, 0); + + assert_int_equal(m_event_counter, 1); +} + +/* Send two events with in-order event instance IDs. */ +static void test_libspdm_requester_encap_event_ack_case2(void **state) +{ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + spdm_send_event_request_t *send_event; + size_t request_size; + spdm_event_ack_response_t *event_ack; + size_t response_size = sizeof(m_spdm_response_buffer); + uint8_t event_data_size[2]; + spdm_dmtf_event_type_event_lost_t event_lost; + spdm_dmtf_event_type_certificate_changed_t certificate_changed; + uint8_t *ptr; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x02; + + set_standard_state(spdm_context); + + send_event = (spdm_send_event_request_t *)m_spdm_request_buffer; + + send_event->header.spdm_version = SPDM_MESSAGE_VERSION_13; + send_event->header.request_response_code = SPDM_SEND_EVENT; + send_event->header.param1 = 0; + send_event->header.param2 = 0; + send_event->event_count = 2; + + certificate_changed.certificate_changed = 5; + + event_lost.last_acked_event_inst_id = 0xffeeddcc; + event_lost.last_lost_event_inst_id = 0x55667788; + + ptr = (uint8_t *)(send_event + 1); + + generate_dmtf_event_data(ptr, &event_data_size[0], 0x11223343, + SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED, &certificate_changed); + ptr += event_data_size[0]; + + generate_dmtf_event_data(ptr, &event_data_size[1], 0x11223344, + SPDM_DMTF_EVENT_TYPE_EVENT_LOST, &event_lost); + ptr += event_data_size[1]; + + m_event_counter = 0; + + m_expected_event[0].event_instance_id = 0x11223343; + m_expected_event[0].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[0].svh_vendor_id_len = 0; + m_expected_event[0].event_type_id = SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED; + m_expected_event[0].event_detail_len = SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE; + memcpy(m_expected_event[0].event_detail, &certificate_changed, + SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE); + + m_expected_event[1].event_instance_id = 0x11223344; + m_expected_event[1].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[1].svh_vendor_id_len = 0; + m_expected_event[1].event_type_id = SPDM_DMTF_EVENT_TYPE_EVENT_LOST; + m_expected_event[1].event_detail_len = SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE; + memcpy(m_expected_event[1].event_detail, &event_lost, SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE); + + request_size = sizeof(spdm_send_event_request_t) + event_data_size[0] + event_data_size[1]; + + status = libspdm_get_encap_response_event_ack(spdm_context, + request_size, + m_spdm_request_buffer, + &response_size, + m_spdm_response_buffer); + + event_ack = (spdm_event_ack_response_t *)m_spdm_response_buffer; + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + + assert_int_equal(response_size, sizeof(spdm_event_ack_response_t)); + assert_int_equal(event_ack->header.spdm_version, SPDM_MESSAGE_VERSION_13); + assert_int_equal(event_ack->header.request_response_code, SPDM_EVENT_ACK); + assert_int_equal(event_ack->header.param1, 0); + assert_int_equal(event_ack->header.param2, 0); + + assert_int_equal(m_event_counter, 2); +} + +/* Send two events with out-of-order event instance IDs. */ +static void test_libspdm_requester_encap_event_ack_case3(void **state) +{ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + spdm_send_event_request_t *send_event; + size_t request_size; + spdm_event_ack_response_t *event_ack; + size_t response_size = sizeof(m_spdm_response_buffer); + uint8_t event_data_size[2]; + spdm_dmtf_event_type_event_lost_t event_lost; + spdm_dmtf_event_type_certificate_changed_t certificate_changed; + uint8_t *ptr; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x03; + + set_standard_state(spdm_context); + + send_event = (spdm_send_event_request_t *)m_spdm_request_buffer; + + send_event->header.spdm_version = SPDM_MESSAGE_VERSION_13; + send_event->header.request_response_code = SPDM_SEND_EVENT; + send_event->header.param1 = 0; + send_event->header.param2 = 0; + send_event->event_count = 2; + + certificate_changed.certificate_changed = 5; + + event_lost.last_acked_event_inst_id = 0xffeeddcc; + event_lost.last_lost_event_inst_id = 0x55667788; + + ptr = (uint8_t *)(send_event + 1); + + generate_dmtf_event_data(ptr, &event_data_size[0], 0x11223344, + SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED, &certificate_changed); + ptr += event_data_size[0]; + + generate_dmtf_event_data(ptr, &event_data_size[1], 0x11223343, + SPDM_DMTF_EVENT_TYPE_EVENT_LOST, &event_lost); + ptr += event_data_size[1]; + + m_event_counter = 0; + + m_expected_event[0].event_instance_id = 0x11223343; + m_expected_event[0].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[0].svh_vendor_id_len = 0; + m_expected_event[0].event_type_id = SPDM_DMTF_EVENT_TYPE_EVENT_LOST; + m_expected_event[0].event_detail_len = SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE; + memcpy(m_expected_event[0].event_detail, &event_lost, SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE); + + m_expected_event[1].event_instance_id = 0x11223344; + m_expected_event[1].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[1].svh_vendor_id_len = 0; + m_expected_event[1].event_type_id = SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED; + m_expected_event[1].event_detail_len = SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE; + memcpy(m_expected_event[1].event_detail, &certificate_changed, + SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE); + + request_size = sizeof(spdm_send_event_request_t) + event_data_size[0] + event_data_size[1]; + + status = libspdm_get_encap_response_event_ack(spdm_context, + request_size, + m_spdm_request_buffer, + &response_size, + m_spdm_response_buffer); + + event_ack = (spdm_event_ack_response_t *)m_spdm_response_buffer; + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + + assert_int_equal(response_size, sizeof(spdm_event_ack_response_t)); + assert_int_equal(event_ack->header.spdm_version, SPDM_MESSAGE_VERSION_13); + assert_int_equal(event_ack->header.request_response_code, SPDM_EVENT_ACK); + assert_int_equal(event_ack->header.param1, 0); + assert_int_equal(event_ack->header.param2, 0); + + assert_int_equal(m_event_counter, 2); +} + +/* Send four events with in-order event instance IDs. */ +static void test_libspdm_requester_encap_event_ack_case4(void **state) +{ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + spdm_send_event_request_t *send_event; + size_t request_size; + spdm_event_ack_response_t *event_ack; + size_t response_size = sizeof(m_spdm_response_buffer); + uint8_t event_data_size[4]; + spdm_dmtf_event_type_event_lost_t event_lost; + spdm_dmtf_event_type_certificate_changed_t certificate_changed; + spdm_dmtf_event_type_measurement_changed_t measurement_changed; + spdm_dmtf_event_type_measurement_pre_update_t measurement_pre_update; + uint8_t *ptr; + uint32_t index; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x04; + + set_standard_state(spdm_context); + + send_event = (spdm_send_event_request_t *)m_spdm_request_buffer; + + send_event->header.spdm_version = SPDM_MESSAGE_VERSION_13; + send_event->header.request_response_code = SPDM_SEND_EVENT; + send_event->header.param1 = 0; + send_event->header.param2 = 0; + send_event->event_count = 4; + + ptr = (uint8_t *)(send_event + 1); + + certificate_changed.certificate_changed = 5; + generate_dmtf_event_data(ptr, &event_data_size[0], 0x11223343, + SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED, &certificate_changed); + ptr += event_data_size[0]; + + event_lost.last_acked_event_inst_id = 0xffeeddcc; + event_lost.last_lost_event_inst_id = 0x55667788; + generate_dmtf_event_data(ptr, &event_data_size[1], 0x11223344, + SPDM_DMTF_EVENT_TYPE_EVENT_LOST, &event_lost); + ptr += event_data_size[1]; + + for (index = 0; index < SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED_SIZE; index++) { + measurement_changed.changed_measurements[index] = (uint8_t)index; + } + generate_dmtf_event_data(ptr, &event_data_size[2], 0x11223345, + SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED, &measurement_changed); + ptr += event_data_size[2]; + + for (index = 0; index < SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE_SIZE; index++) { + measurement_pre_update.pre_update_measurement_changes[index] = (uint8_t)(2 * index); + } + generate_dmtf_event_data(ptr, &event_data_size[3], 0x11223346, + SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE, &measurement_pre_update); + ptr += event_data_size[3]; + + m_event_counter = 0; + + m_expected_event[0].event_instance_id = 0x11223343; + m_expected_event[0].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[0].svh_vendor_id_len = 0; + m_expected_event[0].event_type_id = SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED; + m_expected_event[0].event_detail_len = SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE; + memcpy(m_expected_event[0].event_detail, &certificate_changed, + SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED_SIZE); + + m_expected_event[1].event_instance_id = 0x11223344; + m_expected_event[1].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[1].svh_vendor_id_len = 0; + m_expected_event[1].event_type_id = SPDM_DMTF_EVENT_TYPE_EVENT_LOST; + m_expected_event[1].event_detail_len = SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE; + memcpy(m_expected_event[1].event_detail, &event_lost, SPDM_DMTF_EVENT_TYPE_EVENT_LOST_SIZE); + + m_expected_event[2].event_instance_id = 0x11223345; + m_expected_event[2].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[2].svh_vendor_id_len = 0; + m_expected_event[2].event_type_id = SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED; + m_expected_event[2].event_detail_len = SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED_SIZE; + memcpy(m_expected_event[2].event_detail, &measurement_changed, + SPDM_DMTF_EVENT_TYPE_MEASUREMENT_CHANGED_SIZE); + + m_expected_event[3].event_instance_id = 0x11223346; + m_expected_event[3].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[3].svh_vendor_id_len = 0; + m_expected_event[3].event_type_id = SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE; + m_expected_event[3].event_detail_len = SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE_SIZE; + memcpy(m_expected_event[3].event_detail, &measurement_pre_update, + SPDM_DMTF_EVENT_TYPE_MEASUREMENT_PRE_UPDATE_SIZE); + + request_size = sizeof(spdm_send_event_request_t) + event_data_size[0] + event_data_size[1] + + event_data_size[2] + event_data_size[3]; + + status = libspdm_get_encap_response_event_ack(spdm_context, + request_size, + m_spdm_request_buffer, + &response_size, + m_spdm_response_buffer); + + event_ack = (spdm_event_ack_response_t *)m_spdm_response_buffer; + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + + assert_int_equal(response_size, sizeof(spdm_event_ack_response_t)); + assert_int_equal(event_ack->header.spdm_version, SPDM_MESSAGE_VERSION_13); + assert_int_equal(event_ack->header.request_response_code, SPDM_EVENT_ACK); + assert_int_equal(event_ack->header.param1, 0); + assert_int_equal(event_ack->header.param2, 0); + + assert_int_equal(m_event_counter, 4); +} + +int libspdm_requester_encap_event_ack_test_main(void) +{ + const struct CMUnitTest spdm_requester_event_ack_tests[] = { + cmocka_unit_test(test_libspdm_requester_encap_event_ack_case1), + cmocka_unit_test(test_libspdm_requester_encap_event_ack_case2), + cmocka_unit_test(test_libspdm_requester_encap_event_ack_case3), + cmocka_unit_test(test_libspdm_requester_encap_event_ack_case4) + }; + + libspdm_test_context_t test_context = { + LIBSPDM_TEST_CONTEXT_VERSION, + false, + }; + + libspdm_setup_test_context(&test_context); + + return cmocka_run_group_tests(spdm_requester_event_ack_tests, + libspdm_unit_test_group_setup, + libspdm_unit_test_group_teardown); +} + +#endif /* (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && (LIBSPDM_EVENT_RECIPIENT_SUPPORT) */ diff --git a/unit_test/test_spdm_requester/error_test/encap_event_ack_err.c b/unit_test/test_spdm_requester/error_test/encap_event_ack_err.c new file mode 100644 index 00000000000..6c1e584b71d --- /dev/null +++ b/unit_test/test_spdm_requester/error_test/encap_event_ack_err.c @@ -0,0 +1,380 @@ +/** + * Copyright Notice: + * Copyright 2025 DMTF. All rights reserved. + * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md + **/ + +#include "spdm_unit_test.h" +#include "internal/libspdm_requester_lib.h" + +#if (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && (LIBSPDM_EVENT_RECIPIENT_SUPPORT) + +static uint8_t m_spdm_request_buffer[0x1000]; +static uint8_t m_spdm_response_buffer[0x1000]; + +static const uint32_t m_session_id = 0xffffffff; + +typedef struct { + uint32_t event_instance_id; + uint8_t svh_id; + uint8_t svh_vendor_id_len; + uint8_t svh_vendor_id[4]; + uint16_t event_type_id; + uint16_t event_detail_len; + uint8_t event_detail[100]; +} expected_event_t; + +static expected_event_t m_expected_event[4]; +static uint32_t m_event_counter; + +static libspdm_return_t process_event(void *spdm_context, + uint32_t session_id, + uint32_t event_instance_id, + uint8_t svh_id, + uint8_t svh_vendor_id_len, + void *svh_vendor_id, + uint16_t event_type_id, + uint16_t event_detail_len, + void *event_detail) +{ + printf("Event Received\n"); + printf("Event Instance ID = [0x%x]\n", event_instance_id); + printf("SVH ID = [0x%x], SVH VendorIDLen = [0x%x]\n", svh_id, svh_vendor_id_len); + if (svh_vendor_id_len != 0) { + printf("SVH VendorID\n"); + libspdm_dump_hex(svh_vendor_id, svh_vendor_id_len); + printf("\n"); + } + printf("EventTypeID = [0x%x], EventDetailLen = [0x%x]\n", event_type_id, event_detail_len); + printf("Event Detail\n"); + libspdm_dump_hex(event_detail, event_detail_len); + + assert_int_equal(session_id, m_session_id); + assert_int_equal(event_instance_id, m_expected_event[m_event_counter].event_instance_id); + assert_int_equal(event_type_id, m_expected_event[m_event_counter].event_type_id); + assert_int_equal(svh_id, m_expected_event[m_event_counter].svh_id); + assert_int_equal(svh_vendor_id_len, m_expected_event[m_event_counter].svh_vendor_id_len); + if (svh_vendor_id_len == 0) { + assert_ptr_equal(svh_vendor_id, NULL); + } + + m_event_counter++; + + return LIBSPDM_STATUS_SUCCESS; +} + +static void set_standard_state(libspdm_context_t *spdm_context) +{ + libspdm_session_info_t *session_info; + + spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_13 << + SPDM_VERSION_NUMBER_SHIFT_BIT; + spdm_context->connection_info.connection_state = LIBSPDM_CONNECTION_STATE_NEGOTIATED; + + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_EVENT_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCRYPT_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MAC_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_KEY_EX_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCAP_CAP; + + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCRYPT_CAP; + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MAC_CAP; + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_KEY_EX_CAP; + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCAP_CAP; + + spdm_context->connection_info.algorithm.base_hash_algo = m_libspdm_use_hash_algo; + spdm_context->connection_info.algorithm.base_asym_algo = m_libspdm_use_asym_algo; + spdm_context->connection_info.algorithm.dhe_named_group = m_libspdm_use_dhe_algo; + spdm_context->connection_info.algorithm.aead_cipher_suite = m_libspdm_use_aead_algo; + + spdm_context->latest_session_id = m_session_id; + spdm_context->last_spdm_request_session_id_valid = true; + spdm_context->last_spdm_request_session_id = m_session_id; + session_info = &spdm_context->session_info[0]; + libspdm_session_info_init(spdm_context, session_info, m_session_id, true); + libspdm_secured_message_set_session_state( + session_info->secured_message_context, + LIBSPDM_SESSION_STATE_ESTABLISHED); + + libspdm_register_event_callback(spdm_context, process_event); +} + +/* Set EventCount to 0. */ +static void test_libspdm_requester_encap_event_ack_case1(void **state) +{ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + spdm_send_event_request_t *send_event; + size_t request_size; + spdm_event_ack_response_t *event_ack; + size_t response_size = sizeof(m_spdm_response_buffer); + uint8_t event_data_size; + spdm_dmtf_event_type_event_lost_t event_lost; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x01; + + set_standard_state(spdm_context); + + send_event = (spdm_send_event_request_t *)m_spdm_request_buffer; + + send_event->header.spdm_version = SPDM_MESSAGE_VERSION_13; + send_event->header.request_response_code = SPDM_SEND_EVENT; + send_event->header.param1 = 0; + send_event->header.param2 = 0; + /* Illegal value for EventCount. */ + send_event->event_count = 0; + + event_lost.last_acked_event_inst_id = 0xffeeddcc; + event_lost.last_lost_event_inst_id = 0x55667788; + + generate_dmtf_event_data(send_event + 1, &event_data_size, 0x11223344, + SPDM_DMTF_EVENT_TYPE_EVENT_LOST, &event_lost); + + m_event_counter = 0; + + request_size = sizeof(spdm_send_event_request_t) + event_data_size; + + status = libspdm_get_encap_response_event_ack(spdm_context, + request_size, + m_spdm_request_buffer, + &response_size, + m_spdm_response_buffer); + + event_ack = (spdm_event_ack_response_t *)m_spdm_response_buffer; + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + + assert_int_equal(response_size, sizeof(spdm_error_response_t)); + assert_int_equal(event_ack->header.spdm_version, SPDM_MESSAGE_VERSION_13); + assert_int_equal(event_ack->header.request_response_code, SPDM_ERROR); + assert_int_equal(event_ack->header.param1, SPDM_ERROR_CODE_INVALID_REQUEST); + assert_int_equal(event_ack->header.param2, 0); + + assert_int_equal(m_event_counter, 0); +} + +/* Send two events with gap in event instance IDs. */ +static void test_libspdm_requester_encap_event_ack_case2(void **state) +{ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + spdm_send_event_request_t *send_event; + size_t request_size; + spdm_event_ack_response_t *event_ack; + size_t response_size = sizeof(m_spdm_response_buffer); + uint8_t event_data_size[2]; + spdm_dmtf_event_type_event_lost_t event_lost; + spdm_dmtf_event_type_certificate_changed_t certificate_changed; + uint8_t *ptr; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x02; + + set_standard_state(spdm_context); + + send_event = (spdm_send_event_request_t *)m_spdm_request_buffer; + + send_event->header.spdm_version = SPDM_MESSAGE_VERSION_13; + send_event->header.request_response_code = SPDM_SEND_EVENT; + send_event->header.param1 = 0; + send_event->header.param2 = 0; + send_event->event_count = 2; + + certificate_changed.certificate_changed = 5; + + event_lost.last_acked_event_inst_id = 0xffeeddcc; + event_lost.last_lost_event_inst_id = 0x55667788; + + ptr = (uint8_t *)(send_event + 1); + + generate_dmtf_event_data(ptr, &event_data_size[0], 0x11223343, + SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED, &certificate_changed); + ptr += event_data_size[0]; + + generate_dmtf_event_data(ptr, &event_data_size[1], 0x11223345, + SPDM_DMTF_EVENT_TYPE_EVENT_LOST, &event_lost); + ptr += event_data_size[1]; + + m_event_counter = 0; + + m_expected_event[0].event_instance_id = 0x11223343; + m_expected_event[0].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[0].svh_vendor_id_len = 0; + m_expected_event[0].event_type_id = SPDM_DMTF_EVENT_TYPE_CERTIFICATE_CHANGED; + + m_expected_event[1].event_instance_id = 0x11223344; + m_expected_event[1].svh_id = SPDM_REGISTRY_ID_DMTF; + m_expected_event[1].svh_vendor_id_len = 0; + m_expected_event[1].event_type_id = SPDM_DMTF_EVENT_TYPE_EVENT_LOST; + + request_size = sizeof(spdm_send_event_request_t) + event_data_size[0] + event_data_size[1]; + + status = libspdm_get_encap_response_event_ack(spdm_context, + request_size, + m_spdm_request_buffer, + &response_size, + m_spdm_response_buffer); + + event_ack = (spdm_event_ack_response_t *)m_spdm_response_buffer; + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + + assert_int_equal(response_size, sizeof(spdm_error_response_t)); + assert_int_equal(event_ack->header.spdm_version, SPDM_MESSAGE_VERSION_13); + assert_int_equal(event_ack->header.request_response_code, SPDM_ERROR); + assert_int_equal(event_ack->header.param1, SPDM_ERROR_CODE_INVALID_REQUEST); + assert_int_equal(event_ack->header.param2, 0); + + assert_int_equal(m_event_counter, 0); +} + +/* Send one event but the value of EventCount is two. */ +static void test_libspdm_requester_encap_event_ack_case3(void **state) +{ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + spdm_send_event_request_t *send_event; + size_t request_size; + spdm_event_ack_response_t *event_ack; + size_t response_size = sizeof(m_spdm_response_buffer); + uint8_t event_data_size; + spdm_dmtf_event_type_event_lost_t event_lost; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x03; + + set_standard_state(spdm_context); + + send_event = (spdm_send_event_request_t *)m_spdm_request_buffer; + + send_event->header.spdm_version = SPDM_MESSAGE_VERSION_13; + send_event->header.request_response_code = SPDM_SEND_EVENT; + send_event->header.param1 = 0; + send_event->header.param2 = 0; + /* Only one event but event_count is two. */ + send_event->event_count = 2; + + event_lost.last_acked_event_inst_id = 0xffeeddcc; + event_lost.last_lost_event_inst_id = 0x55667788; + + generate_dmtf_event_data(send_event + 1, &event_data_size, 0x11223344, + SPDM_DMTF_EVENT_TYPE_EVENT_LOST, &event_lost); + + m_event_counter = 0; + + request_size = sizeof(spdm_send_event_request_t) + event_data_size; + + status = libspdm_get_encap_response_event_ack(spdm_context, + request_size, + m_spdm_request_buffer, + &response_size, + m_spdm_response_buffer); + + event_ack = (spdm_event_ack_response_t *)m_spdm_response_buffer; + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + + assert_int_equal(response_size, sizeof(spdm_error_response_t)); + assert_int_equal(event_ack->header.spdm_version, SPDM_MESSAGE_VERSION_13); + assert_int_equal(event_ack->header.request_response_code, SPDM_ERROR); + assert_int_equal(event_ack->header.param1, SPDM_ERROR_CODE_INVALID_REQUEST); + assert_int_equal(event_ack->header.param2, 0); + + assert_int_equal(m_event_counter, 0); +} + +/* Send one event but request_size is not exact. */ +static void test_libspdm_requester_encap_event_ack_case4(void **state) +{ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + spdm_send_event_request_t *send_event; + size_t request_size; + spdm_event_ack_response_t *event_ack; + size_t response_size = sizeof(m_spdm_response_buffer); + uint8_t event_data_size; + spdm_dmtf_event_type_event_lost_t event_lost; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x04; + + set_standard_state(spdm_context); + + send_event = (spdm_send_event_request_t *)m_spdm_request_buffer; + + send_event->header.spdm_version = SPDM_MESSAGE_VERSION_13; + send_event->header.request_response_code = SPDM_SEND_EVENT; + send_event->header.param1 = 0; + send_event->header.param2 = 0; + send_event->event_count = 1; + + event_lost.last_acked_event_inst_id = 0xffeeddcc; + event_lost.last_lost_event_inst_id = 0x55667788; + + generate_dmtf_event_data(send_event + 1, &event_data_size, 0x11223344, + SPDM_DMTF_EVENT_TYPE_EVENT_LOST, &event_lost); + + m_event_counter = 0; + + /* request_size is not exact (+ 1). */ + request_size = sizeof(spdm_send_event_request_t) + event_data_size + 1; + + status = libspdm_get_encap_response_event_ack(spdm_context, + request_size, + m_spdm_request_buffer, + &response_size, + m_spdm_response_buffer); + + event_ack = (spdm_event_ack_response_t *)m_spdm_response_buffer; + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + + assert_int_equal(response_size, sizeof(spdm_error_response_t)); + assert_int_equal(event_ack->header.spdm_version, SPDM_MESSAGE_VERSION_13); + assert_int_equal(event_ack->header.request_response_code, SPDM_ERROR); + assert_int_equal(event_ack->header.param1, SPDM_ERROR_CODE_INVALID_REQUEST); + assert_int_equal(event_ack->header.param2, 0); + + assert_int_equal(m_event_counter, 0); +} + +int libspdm_requester_encap_event_ack_error_test_main(void) +{ + const struct CMUnitTest test_list[] = { + cmocka_unit_test(test_libspdm_requester_encap_event_ack_case1), + cmocka_unit_test(test_libspdm_requester_encap_event_ack_case2), + cmocka_unit_test(test_libspdm_requester_encap_event_ack_case3), + cmocka_unit_test(test_libspdm_requester_encap_event_ack_case4), + }; + + libspdm_test_context_t test_context = { + LIBSPDM_TEST_CONTEXT_VERSION, + false, + }; + + libspdm_setup_test_context(&test_context); + + return cmocka_run_group_tests(test_list, + libspdm_unit_test_group_setup, + libspdm_unit_test_group_teardown); +} + + #endif /* (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && (LIBSPDM_EVENT_RECIPIENT_SUPPORT) */ diff --git a/unit_test/test_spdm_requester/test_spdm_requester.c b/unit_test/test_spdm_requester/test_spdm_requester.c index 3f3b6d0f4e4..9e064388e8b 100644 --- a/unit_test/test_spdm_requester/test_spdm_requester.c +++ b/unit_test/test_spdm_requester/test_spdm_requester.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2022 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -61,6 +61,10 @@ int libspdm_requester_encap_certificate_test_main(void); int libspdm_requester_encap_challenge_auth_test_main(void); #endif /* LIBSPDM_ENABLE_CAPABILITY_CERT_CAP */ #endif /* LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP */ +#if LIBSPDM_EVENT_RECIPIENT_SUPPORT +int libspdm_requester_encap_event_ack_test_main(void); +int libspdm_requester_encap_event_ack_error_test_main(void); +#endif /* #if LIBSPDM_EVENT_RECIPIENT_SUPPORT */ int libspdm_requester_encap_key_update_test_main(void); #endif /* LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP */ @@ -212,6 +216,14 @@ int main(void) if (libspdm_requester_encap_key_update_test_main() != 0) { return_value = 1; } + #if LIBSPDM_EVENT_RECIPIENT_SUPPORT + if (libspdm_requester_encap_event_ack_test_main() != 0) { + return_value = 1; + } + if (libspdm_requester_encap_event_ack_error_test_main() != 0) { + return_value = 1; + } + #endif /* LIBSPDM_EVENT_RECIPIENT_SUPPORT */ #endif /* LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP */ #if LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP