Line data Source code
1 : /**
2 : * Copyright Notice:
3 : * Copyright 2021-2025 DMTF. All rights reserved.
4 : * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md
5 : **/
6 :
7 : #include "internal/libspdm_requester_lib.h"
8 :
9 : #if (LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP) || (LIBSPDM_ENABLE_CAPABILITY_PSK_CAP)
10 :
11 : #pragma pack(1)
12 : typedef struct {
13 : spdm_message_header_t header;
14 : uint8_t dummy_data[sizeof(spdm_error_data_response_not_ready_t)];
15 : } libspdm_heartbeat_response_mine_t;
16 : #pragma pack()
17 :
18 : /**
19 : * This function sends HEARTBEAT to an SPDM Session.
20 : *
21 : * @param spdm_context A pointer to the SPDM context.
22 : * @param session_id The session ID of the session.
23 : *
24 : **/
25 31 : static libspdm_return_t libspdm_try_heartbeat(libspdm_context_t *spdm_context, uint32_t session_id)
26 : {
27 : libspdm_return_t status;
28 : spdm_heartbeat_request_t *spdm_request;
29 : size_t spdm_request_size;
30 : libspdm_heartbeat_response_mine_t *spdm_response;
31 : size_t spdm_response_size;
32 : libspdm_session_info_t *session_info;
33 : libspdm_session_state_t session_state;
34 : uint8_t *message;
35 : size_t message_size;
36 : size_t transport_header_size;
37 :
38 : /* -=[Check Parameters Phase]=- */
39 31 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_11) {
40 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
41 : }
42 :
43 31 : session_info = libspdm_get_session_info_via_session_id(spdm_context, session_id);
44 31 : if (session_info == NULL) {
45 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
46 : }
47 :
48 : /* -=[Verify State Phase]=- */
49 31 : if (!libspdm_is_capabilities_flag_supported(
50 : spdm_context, true,
51 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HBEAT_CAP,
52 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_HBEAT_CAP)) {
53 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
54 : }
55 31 : if (spdm_context->connection_info.connection_state < LIBSPDM_CONNECTION_STATE_NEGOTIATED) {
56 1 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
57 : }
58 30 : session_state = libspdm_secured_message_get_session_state(
59 : session_info->secured_message_context);
60 30 : if (session_state != LIBSPDM_SESSION_STATE_ESTABLISHED) {
61 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
62 : }
63 30 : if (session_info->heartbeat_period == 0) {
64 1 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
65 : }
66 :
67 : /* -=[Construct Request Phase]=- */
68 29 : transport_header_size = spdm_context->local_context.capability.transport_header_size;
69 29 : status = libspdm_acquire_sender_buffer (spdm_context, &message_size, (void **)&message);
70 29 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
71 0 : return status;
72 : }
73 29 : LIBSPDM_ASSERT (message_size >= transport_header_size +
74 : spdm_context->local_context.capability.transport_tail_size);
75 29 : spdm_request = (void *)(message + transport_header_size);
76 29 : spdm_request_size = message_size - transport_header_size -
77 29 : spdm_context->local_context.capability.transport_tail_size;
78 :
79 29 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_heartbeat_request_t));
80 29 : spdm_request->header.spdm_version = libspdm_get_connection_version (spdm_context);
81 29 : spdm_request->header.request_response_code = SPDM_HEARTBEAT;
82 29 : spdm_request->header.param1 = 0;
83 29 : spdm_request->header.param2 = 0;
84 29 : spdm_request_size = sizeof(spdm_heartbeat_request_t);
85 :
86 : /* -=[Send Request Phase]=- */
87 29 : status = libspdm_send_spdm_request(spdm_context, &session_id, spdm_request_size, spdm_request);
88 29 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
89 1 : libspdm_release_sender_buffer (spdm_context);
90 1 : return status;
91 : }
92 28 : libspdm_release_sender_buffer (spdm_context);
93 28 : spdm_request = (void *)spdm_context->last_spdm_request;
94 :
95 28 : libspdm_reset_message_buffer_via_request_code(spdm_context, session_info, SPDM_HEARTBEAT);
96 :
97 : /* -=[Receive Response Phase]=- */
98 28 : status = libspdm_acquire_receiver_buffer (spdm_context, &message_size, (void **)&message);
99 28 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
100 0 : return status;
101 : }
102 28 : LIBSPDM_ASSERT (message_size >= transport_header_size);
103 28 : spdm_response = (void *)(message);
104 28 : spdm_response_size = message_size;
105 :
106 28 : status = libspdm_receive_spdm_response(
107 : spdm_context, &session_id, &spdm_response_size, (void **)&spdm_response);
108 28 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
109 0 : goto receive_done;
110 : }
111 :
112 : /* -=[Validate Response Phase]=- */
113 28 : if (spdm_response_size < sizeof(spdm_message_header_t)) {
114 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
115 0 : goto receive_done;
116 : }
117 28 : if (spdm_response->header.spdm_version != spdm_request->header.spdm_version) {
118 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
119 0 : goto receive_done;
120 : }
121 28 : if (spdm_response->header.request_response_code == SPDM_ERROR) {
122 25 : status = libspdm_handle_error_response_main(
123 : spdm_context, &session_id, &spdm_response_size,
124 : (void **)&spdm_response, SPDM_HEARTBEAT, SPDM_HEARTBEAT_ACK);
125 25 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
126 24 : goto receive_done;
127 : }
128 3 : } else if (spdm_response->header.request_response_code != SPDM_HEARTBEAT_ACK) {
129 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
130 0 : goto receive_done;
131 : }
132 : /* this message can only be in secured session
133 : * thus don't need to consider transport layer padding, just check its exact size */
134 4 : if (spdm_response_size != sizeof(spdm_heartbeat_response_t)) {
135 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
136 0 : goto receive_done;
137 : }
138 :
139 : /* -=[Log Message Phase]=- */
140 : #if LIBSPDM_ENABLE_MSG_LOG
141 4 : libspdm_append_msg_log(spdm_context, spdm_response, spdm_response_size);
142 : #endif /* LIBSPDM_ENABLE_MSG_LOG */
143 :
144 4 : status = LIBSPDM_STATUS_SUCCESS;
145 :
146 28 : receive_done:
147 28 : libspdm_release_receiver_buffer (spdm_context);
148 28 : return status;
149 : }
150 :
151 30 : libspdm_return_t libspdm_heartbeat(void *spdm_context, uint32_t session_id)
152 : {
153 : size_t retry;
154 : uint64_t retry_delay_time;
155 : libspdm_return_t status;
156 : libspdm_context_t *context;
157 :
158 30 : context = spdm_context;
159 30 : context->crypto_request = true;
160 30 : retry = context->retry_times;
161 30 : retry_delay_time = context->retry_delay_time;
162 : do {
163 31 : status = libspdm_try_heartbeat(context, session_id);
164 31 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
165 29 : return status;
166 : }
167 :
168 2 : libspdm_sleep(retry_delay_time);
169 2 : } while (retry-- != 0);
170 :
171 1 : return status;
172 : }
173 :
174 : #endif /* (LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP) || (LIBSPDM_ENABLE_CAPABILITY_PSK_CAP) */
|