Line data Source code
1 : /**
2 : * Copyright Notice:
3 : * Copyright 2021-2026 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_responder_lib.h"
8 : #include "internal/libspdm_secured_message_lib.h"
9 :
10 19 : libspdm_get_spdm_response_func libspdm_get_response_func_via_request_code(uint8_t request_code)
11 : {
12 : size_t index;
13 :
14 : typedef struct {
15 : uint8_t request_response_code;
16 : libspdm_get_spdm_response_func get_response_func;
17 : } libspdm_get_response_struct_t;
18 :
19 19 : libspdm_get_response_struct_t get_response_struct[] = {
20 : { SPDM_GET_VERSION, libspdm_get_response_version },
21 : { SPDM_GET_CAPABILITIES, libspdm_get_response_capabilities },
22 : { SPDM_NEGOTIATE_ALGORITHMS, libspdm_get_response_algorithms },
23 :
24 : #if LIBSPDM_ENABLE_CAPABILITY_CERT_CAP
25 : { SPDM_GET_DIGESTS, libspdm_get_response_digests },
26 : { SPDM_GET_CERTIFICATE, libspdm_get_response_certificate },
27 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CERT_CAP */
28 :
29 : #if LIBSPDM_ENABLE_CAPABILITY_CHAL_CAP
30 : { SPDM_CHALLENGE, libspdm_get_response_challenge_auth },
31 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHAL_CAP*/
32 :
33 : #if LIBSPDM_ENABLE_CAPABILITY_MEAS_CAP
34 : { SPDM_GET_MEASUREMENTS, libspdm_get_response_measurements },
35 : #endif /* LIBSPDM_ENABLE_CAPABILITY_MEAS_CAP*/
36 :
37 : #if LIBSPDM_ENABLE_CAPABILITY_MEL_CAP
38 : { SPDM_GET_MEASUREMENT_EXTENSION_LOG, libspdm_get_response_measurement_extension_log },
39 : #endif /* LIBSPDM_ENABLE_CAPABILITY_MEL_CAP */
40 :
41 : #if LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP
42 : { SPDM_KEY_EXCHANGE, libspdm_get_response_key_exchange },
43 : #endif /* LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP*/
44 :
45 : #if LIBSPDM_ENABLE_CAPABILITY_PSK_CAP
46 : { SPDM_PSK_EXCHANGE, libspdm_get_response_psk_exchange },
47 : #endif /* LIBSPDM_ENABLE_CAPABILITY_PSK_CAP*/
48 :
49 : #if LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP
50 : { SPDM_GET_ENCAPSULATED_REQUEST, libspdm_get_response_encapsulated_request },
51 : { SPDM_DELIVER_ENCAPSULATED_RESPONSE, libspdm_get_response_encapsulated_response_ack },
52 : #endif /* LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP */
53 :
54 : #if LIBSPDM_RESPOND_IF_READY_SUPPORT
55 : { SPDM_RESPOND_IF_READY, libspdm_get_response_respond_if_ready },
56 : #endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */
57 :
58 : #if LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP
59 : { SPDM_FINISH, libspdm_get_response_finish },
60 : #endif /* LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP*/
61 :
62 : #if LIBSPDM_ENABLE_CAPABILITY_PSK_CAP
63 : { SPDM_PSK_FINISH, libspdm_get_response_psk_finish },
64 : #endif /* LIBSPDM_ENABLE_CAPABILITY_PSK_CAP*/
65 :
66 : #if (LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP) || (LIBSPDM_ENABLE_CAPABILITY_PSK_CAP)
67 : { SPDM_END_SESSION, libspdm_get_response_end_session },
68 : { SPDM_HEARTBEAT, libspdm_get_response_heartbeat },
69 : { SPDM_KEY_UPDATE, libspdm_get_response_key_update },
70 : #endif /* LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP || LIBSPDM_ENABLE_CAPABILITY_PSK_CAP*/
71 :
72 : #if LIBSPDM_ENABLE_CAPABILITY_ENDPOINT_INFO_CAP
73 : { SPDM_GET_ENDPOINT_INFO, libspdm_get_response_endpoint_info },
74 : #endif /*LIBSPDM_ENABLE_CAPABILITY_ENDPOINT_INFO_CAP*/
75 :
76 : #if LIBSPDM_ENABLE_CAPABILITY_CSR_CAP
77 : { SPDM_GET_CSR, libspdm_get_response_csr },
78 : #endif /*LIBSPDM_ENABLE_CAPABILITY_CSR_CAP*/
79 :
80 : #if LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP
81 : { SPDM_SET_CERTIFICATE, libspdm_get_response_set_certificate },
82 : #endif /*LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP*/
83 :
84 : #if LIBSPDM_ENABLE_CAPABILITY_GET_KEY_PAIR_INFO_CAP
85 : { SPDM_GET_KEY_PAIR_INFO, libspdm_get_response_key_pair_info },
86 : #endif /*LIBSPDM_ENABLE_CAPABILITY_GET_KEY_PAIR_INFO_CAP*/
87 :
88 : #if LIBSPDM_ENABLE_CAPABILITY_SET_KEY_PAIR_INFO_CAP
89 : { SPDM_SET_KEY_PAIR_INFO, libspdm_get_response_set_key_pair_info_ack },
90 : #endif /*LIBSPDM_ENABLE_CAPABILITY_SET_KEY_PAIR_INFO_CAP*/
91 :
92 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
93 : { SPDM_CHUNK_GET, libspdm_get_response_chunk_get},
94 : { SPDM_CHUNK_SEND, libspdm_get_response_chunk_send},
95 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
96 :
97 : #if LIBSPDM_ENABLE_CAPABILITY_EVENT_CAP
98 : { SPDM_GET_SUPPORTED_EVENT_TYPES, libspdm_get_response_supported_event_types },
99 : { SPDM_SUBSCRIBE_EVENT_TYPES, libspdm_get_response_subscribe_event_types_ack },
100 : #endif /* LIBSPDM_ENABLE_CAPABILITY_EVENT_CAP */
101 :
102 : #if LIBSPDM_EVENT_RECIPIENT_SUPPORT
103 : { SPDM_SEND_EVENT, libspdm_get_response_event_ack },
104 : #endif /* LIBSPDM_EVENT_RECIPIENT_SUPPORT */
105 :
106 : #if LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES
107 : { SPDM_VENDOR_DEFINED_REQUEST, libspdm_get_vendor_defined_response },
108 : #endif /*LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES*/
109 : };
110 :
111 158 : for (index = 0; index < LIBSPDM_ARRAY_SIZE(get_response_struct); index++) {
112 158 : if (request_code == get_response_struct[index].request_response_code) {
113 19 : return get_response_struct[index].get_response_func;
114 : }
115 : }
116 0 : return NULL;
117 : }
118 :
119 : /**
120 : * Return the GET_SPDM_RESPONSE function via last request.
121 : *
122 : * @param spdm_context The SPDM context for the device.
123 : *
124 : * @return GET_SPDM_RESPONSE function according to the last request.
125 : **/
126 8 : static libspdm_get_spdm_response_func libspdm_get_response_func_via_last_request(
127 : libspdm_context_t *spdm_context)
128 : {
129 : spdm_message_header_t *spdm_request;
130 :
131 8 : spdm_request = (void *)spdm_context->last_spdm_request;
132 8 : return libspdm_get_response_func_via_request_code(spdm_request->request_response_code);
133 : }
134 :
135 0 : libspdm_return_t libspdm_process_request(void *spdm_context, uint32_t **session_id,
136 : bool *is_app_message,
137 : size_t request_size, void *request)
138 : {
139 : libspdm_context_t *context;
140 : void *temp_session_context;
141 : libspdm_return_t status;
142 : libspdm_session_info_t *session_info;
143 : uint32_t *message_session_id;
144 : uint8_t *decoded_message_ptr;
145 : size_t decoded_message_size;
146 : uint8_t *backup_decoded_message_ptr;
147 : size_t backup_decoded_message_size;
148 : bool result;
149 : bool reset_key_update;
150 :
151 0 : context = spdm_context;
152 : size_t transport_header_size;
153 : uint8_t *scratch_buffer;
154 : size_t scratch_buffer_size;
155 :
156 0 : if (request == NULL) {
157 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
158 : }
159 0 : if (request_size == 0) {
160 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
161 : }
162 :
163 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmReceiveRequest[.] ...\n"));
164 :
165 0 : message_session_id = NULL;
166 0 : context->last_spdm_request_session_id_valid = false;
167 0 : context->last_spdm_request_size =
168 0 : libspdm_get_scratch_buffer_last_spdm_request_capacity(context);
169 :
170 : /* always use scratch buffer to response.
171 : * if it is secured message, this scratch buffer will be used.
172 : * if it is normal message, the response ptr will point to receiver buffer. */
173 0 : transport_header_size = context->local_context.capability.transport_header_size;
174 0 : libspdm_get_scratch_buffer (context, (void **)&scratch_buffer, &scratch_buffer_size);
175 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
176 0 : decoded_message_ptr = scratch_buffer +
177 0 : libspdm_get_scratch_buffer_secure_message_offset() +
178 : transport_header_size;
179 0 : decoded_message_size = libspdm_get_scratch_buffer_secure_message_capacity(context) -
180 : transport_header_size;
181 : #else
182 : decoded_message_ptr = scratch_buffer + transport_header_size;
183 : decoded_message_size = scratch_buffer_size - transport_header_size;
184 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
185 :
186 0 : backup_decoded_message_ptr = decoded_message_ptr;
187 0 : backup_decoded_message_size = decoded_message_size;
188 :
189 0 : status = context->transport_decode_message(
190 : context, &message_session_id, is_app_message, true,
191 : request_size, request, &decoded_message_size,
192 : (void **)&decoded_message_ptr);
193 :
194 0 : reset_key_update = false;
195 0 : temp_session_context = NULL;
196 :
197 0 : if (status == LIBSPDM_STATUS_SESSION_TRY_DISCARD_KEY_UPDATE) {
198 : /* Failed to decode, but have backup keys. Try rolling back before aborting.
199 : * message_session_id must be valid for us to have attempted decryption. */
200 0 : if (message_session_id == NULL) {
201 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
202 : }
203 0 : temp_session_context = libspdm_get_secured_message_context_via_session_id(
204 : context, *message_session_id);
205 0 : if (temp_session_context == NULL) {
206 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
207 : }
208 :
209 0 : result = libspdm_activate_update_session_data_key(
210 : temp_session_context, LIBSPDM_KEY_UPDATE_ACTION_REQUESTER, false);
211 0 : if (!result) {
212 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
213 : }
214 0 : libspdm_trigger_key_update_callback(
215 : context, *message_session_id,
216 : LIBSPDM_KEY_UPDATE_OPERATION_DISCARD_UPDATE,
217 : LIBSPDM_KEY_UPDATE_ACTION_REQUESTER);
218 :
219 : /* Retry decoding message with backup Requester key.
220 : * Must reset some of the parameters in case they were modified */
221 0 : message_session_id = NULL;
222 0 : decoded_message_ptr = backup_decoded_message_ptr;
223 0 : decoded_message_size = backup_decoded_message_size;
224 0 : status = context->transport_decode_message(
225 : context, &message_session_id, is_app_message, true,
226 : request_size, request, &decoded_message_size,
227 : (void **)&decoded_message_ptr);
228 :
229 0 : reset_key_update = true;
230 : }
231 :
232 0 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
233 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "transport_decode_message : %xu\n", status));
234 0 : if (context->last_spdm_error.error_code != 0) {
235 : /* If the SPDM error code is Non-Zero, that means we need send the error message back to requester.
236 : * In this case, we need return SUCCESS and let caller invoke libspdm_build_response() to send an ERROR message.*/
237 0 : *session_id = &context->last_spdm_error.session_id;
238 0 : *is_app_message = false;
239 0 : return LIBSPDM_STATUS_SUCCESS;
240 : }
241 0 : return status;
242 : }
243 :
244 : /* Handle special case for bi-directional communication:
245 : * If the Requester returns RESPONSE_NOT_READY error to KEY_UPDATE, the Responder needs
246 : * to activate backup key to parse the error. Then later the Requester will return SUCCESS,
247 : * the Responder needs new key. So we need to restore the environment by
248 : * libspdm_create_update_session_data_key() again.*/
249 0 : if (reset_key_update) {
250 : /* temp_session_context and message_session_id must necessarily
251 : * be valid for us to reach here. */
252 0 : if (temp_session_context == NULL || message_session_id == NULL) {
253 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
254 : }
255 0 : result = libspdm_create_update_session_data_key(
256 : temp_session_context, LIBSPDM_KEY_UPDATE_ACTION_REQUESTER);
257 0 : if (!result) {
258 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
259 : }
260 0 : libspdm_trigger_key_update_callback(
261 : context, *message_session_id,
262 : LIBSPDM_KEY_UPDATE_OPERATION_CREATE_UPDATE,
263 : LIBSPDM_KEY_UPDATE_ACTION_REQUESTER);
264 : }
265 :
266 : /*
267 : * decoded_message may contain padding zeros due to transport layer alignment requirements.
268 : * trim the decoded_message size to the maximum data_transfer_size.
269 : */
270 0 : decoded_message_size = LIBSPDM_MIN(decoded_message_size,
271 : context->local_context.capability.data_transfer_size);
272 :
273 0 : context->last_spdm_request_size = decoded_message_size;
274 0 : libspdm_copy_mem (context->last_spdm_request,
275 0 : libspdm_get_scratch_buffer_last_spdm_request_capacity(context),
276 : decoded_message_ptr,
277 : decoded_message_size);
278 0 : libspdm_zero_mem (decoded_message_ptr, decoded_message_size);
279 :
280 0 : if (!(*is_app_message)) {
281 : /* Check for minimal SPDM message size. */
282 0 : if (context->last_spdm_request_size < sizeof(spdm_message_header_t)) {
283 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
284 : }
285 : }
286 :
287 0 : *session_id = message_session_id;
288 :
289 0 : if (message_session_id != NULL) {
290 0 : session_info = libspdm_get_session_info_via_session_id(context, *message_session_id);
291 0 : if (session_info == NULL) {
292 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
293 : }
294 0 : context->last_spdm_request_session_id = *message_session_id;
295 0 : context->last_spdm_request_session_id_valid = true;
296 : }
297 :
298 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmReceiveRequest[%x] msg %s(0x%x), size (0x%zx): \n",
299 : (message_session_id != NULL) ? *message_session_id : 0,
300 : libspdm_get_code_str(((spdm_message_header_t *)context->last_spdm_request)->
301 : request_response_code),
302 : ((spdm_message_header_t *)context->last_spdm_request)->request_response_code,
303 : context->last_spdm_request_size));
304 0 : LIBSPDM_INTERNAL_DUMP_HEX((uint8_t *)context->last_spdm_request,
305 : context->last_spdm_request_size);
306 :
307 0 : return LIBSPDM_STATUS_SUCCESS;
308 : }
309 :
310 : /**
311 : * Notify the session state to a session APP.
312 : *
313 : * @param spdm_context A pointer to the SPDM context.
314 : * @param session_id The session_id of a session.
315 : * @param session_state The state of a session.
316 : **/
317 24 : static void libspdm_trigger_session_state_callback(libspdm_context_t *spdm_context,
318 : uint32_t session_id,
319 : libspdm_session_state_t session_state)
320 : {
321 24 : if (spdm_context->spdm_session_state_callback != NULL) {
322 0 : ((libspdm_session_state_callback_func)
323 0 : spdm_context->spdm_session_state_callback)(spdm_context, session_id, session_state);
324 : }
325 24 : }
326 :
327 24 : void libspdm_set_session_state(libspdm_context_t *spdm_context,
328 : uint32_t session_id,
329 : libspdm_session_state_t session_state)
330 : {
331 : libspdm_session_info_t *session_info;
332 : libspdm_session_state_t old_session_state;
333 :
334 24 : session_info = libspdm_get_session_info_via_session_id(spdm_context, session_id);
335 24 : if (session_info == NULL) {
336 0 : LIBSPDM_ASSERT(false);
337 0 : return;
338 : }
339 :
340 24 : old_session_state = libspdm_secured_message_get_session_state(
341 : session_info->secured_message_context);
342 24 : if (old_session_state != session_state) {
343 24 : libspdm_secured_message_set_session_state(
344 : session_info->secured_message_context, session_state);
345 24 : libspdm_trigger_session_state_callback(
346 : spdm_context, session_info->session_id, session_state);
347 : }
348 : }
349 :
350 0 : libspdm_return_t libspdm_terminate_session(
351 : void *spdm_context, uint32_t session_id)
352 : {
353 : libspdm_session_info_t *session_info;
354 :
355 0 : session_info = libspdm_get_session_info_via_session_id(spdm_context, session_id);
356 0 : if (session_info == NULL) {
357 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
358 : }
359 :
360 0 : libspdm_set_session_state(spdm_context, session_id, LIBSPDM_SESSION_STATE_NOT_STARTED);
361 0 : libspdm_free_session_id(spdm_context, session_id);
362 0 : return LIBSPDM_STATUS_SUCCESS;
363 : }
364 :
365 : /**
366 : * Notify the connection state to an SPDM context register.
367 : *
368 : * @param spdm_context A pointer to the SPDM context.
369 : * @param connection_state Indicate the SPDM connection state.
370 : **/
371 85 : static void libspdm_trigger_connection_state_callback(libspdm_context_t *spdm_context,
372 : libspdm_connection_state_t connection_state)
373 : {
374 85 : if (spdm_context->spdm_connection_state_callback != NULL) {
375 0 : ((libspdm_connection_state_callback_func)
376 0 : spdm_context->spdm_connection_state_callback)(spdm_context, connection_state);
377 : }
378 85 : }
379 :
380 87 : void libspdm_set_connection_state(libspdm_context_t *spdm_context,
381 : libspdm_connection_state_t connection_state)
382 : {
383 87 : if (spdm_context->connection_info.connection_state != connection_state) {
384 85 : spdm_context->connection_info.connection_state = connection_state;
385 85 : libspdm_trigger_connection_state_callback(spdm_context, connection_state);
386 : }
387 87 : }
388 :
389 15 : void libspdm_trigger_key_update_callback(void *spdm_context, uint32_t session_id,
390 : libspdm_key_update_operation_t key_update_op,
391 : libspdm_key_update_action_t key_update_action)
392 : {
393 : libspdm_context_t *context;
394 :
395 15 : context = spdm_context;
396 15 : if (context->spdm_key_update_callback != NULL) {
397 0 : ((libspdm_key_update_callback_func)
398 0 : context->spdm_key_update_callback)(context, session_id, key_update_op, key_update_action);
399 : }
400 15 : }
401 :
402 8 : libspdm_return_t libspdm_build_response(void *spdm_context, const uint32_t *session_id,
403 : bool is_app_message,
404 : size_t *response_size,
405 : void **response)
406 : {
407 : libspdm_context_t *context;
408 : uint8_t *my_response;
409 : size_t my_response_size;
410 : libspdm_return_t status;
411 : libspdm_get_spdm_response_func get_response_func;
412 : libspdm_session_info_t *session_info;
413 : spdm_message_header_t *spdm_request;
414 : spdm_message_header_t *spdm_response;
415 : size_t transport_header_size;
416 : uint8_t *scratch_buffer;
417 : size_t scratch_buffer_size;
418 : uint8_t request_response_code;
419 : uint32_t actual_size;
420 :
421 : #if LIBSPDM_ENABLE_CAPABILITY_HBEAT_CAP
422 : bool result;
423 : #endif /* LIBSPDM_ENABLE_CAPABILITY_HBEAT_CAP */
424 :
425 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
426 : uint8_t *large_buffer;
427 : size_t large_buffer_size;
428 : libspdm_chunk_info_t *get_info;
429 : spdm_chunk_response_response_t *chunk_rsp;
430 : uint8_t *chunk_ptr;
431 : size_t chunk_send_ack_response_header_size;
432 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
433 :
434 8 : context = spdm_context;
435 8 : status = LIBSPDM_STATUS_UNSUPPORTED_CAP;
436 :
437 : /* For secure message, setup my_response to scratch buffer
438 : * For non-secure message, setup my_response to sender buffer*/
439 8 : transport_header_size = context->local_context.capability.transport_header_size;
440 8 : if (session_id != NULL) {
441 0 : libspdm_get_scratch_buffer (context, (void **)&scratch_buffer, &scratch_buffer_size);
442 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
443 0 : my_response = scratch_buffer + libspdm_get_scratch_buffer_secure_message_offset() +
444 : transport_header_size;
445 0 : my_response_size = libspdm_get_scratch_buffer_secure_message_capacity(context) -
446 0 : transport_header_size -
447 0 : context->local_context.capability.transport_tail_size;
448 : #else
449 : my_response = scratch_buffer + transport_header_size;
450 : my_response_size = scratch_buffer_size - transport_header_size -
451 : context->local_context.capability.transport_tail_size;
452 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
453 : } else {
454 8 : my_response = (uint8_t *)*response + transport_header_size;
455 8 : my_response_size = *response_size - transport_header_size -
456 8 : context->local_context.capability.transport_tail_size;
457 : }
458 8 : libspdm_zero_mem(my_response, my_response_size);
459 :
460 8 : spdm_response = (void *)my_response;
461 :
462 8 : if (context->last_spdm_error.error_code != 0) {
463 : /* Error in libspdm_process_request(), and we need send error message directly. */
464 0 : switch (context->last_spdm_error.error_code) {
465 0 : case SPDM_ERROR_CODE_DECRYPT_ERROR:
466 : /* session ID is valid. Use it to encrypt the error message.*/
467 0 : if ((context->handle_error_return_policy &
468 : LIBSPDM_DATA_HANDLE_ERROR_RETURN_POLICY_DROP_ON_DECRYPT_ERROR) == 0) {
469 0 : status = libspdm_generate_error_response(
470 : context, SPDM_ERROR_CODE_DECRYPT_ERROR, 0,
471 : &my_response_size, my_response);
472 : } else {
473 : /**
474 : * just ignore this message
475 : * return UNSUPPORTED and clear response_size to continue the dispatch without send response
476 : **/
477 0 : *response_size = 0;
478 0 : status = LIBSPDM_STATUS_UNSUPPORTED_CAP;
479 : }
480 0 : break;
481 0 : case SPDM_ERROR_CODE_INVALID_SESSION:
482 : /**
483 : * don't use session ID, because we dont know which right session ID should be used.
484 : * just ignore this message
485 : * return UNSUPPORTED and clear response_size to continue the dispatch without send response
486 : **/
487 0 : *response_size = 0;
488 0 : status = LIBSPDM_STATUS_UNSUPPORTED_CAP;
489 0 : break;
490 0 : default:
491 0 : LIBSPDM_ASSERT(false);
492 0 : status = LIBSPDM_STATUS_UNSUPPORTED_CAP;
493 : }
494 :
495 0 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
496 0 : if ((session_id != NULL) &&
497 0 : (context->last_spdm_error.error_code == SPDM_ERROR_CODE_DECRYPT_ERROR)) {
498 0 : libspdm_free_session_id(context, *session_id);
499 : }
500 0 : return status;
501 : }
502 :
503 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmSendResponse[%x]: msg %s(0x%x), size (0x%zx): \n",
504 : (session_id != NULL) ? *session_id : 0,
505 : libspdm_get_code_str(spdm_response->request_response_code),
506 : spdm_response->request_response_code, my_response_size));
507 0 : LIBSPDM_INTERNAL_DUMP_HEX(my_response, my_response_size);
508 :
509 0 : status = context->transport_encode_message(
510 : context, session_id, false, false,
511 : my_response_size, my_response, response_size, response);
512 0 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
513 0 : if ((session_id != NULL) &&
514 0 : ((status == LIBSPDM_STATUS_SEQUENCE_NUMBER_OVERFLOW) ||
515 : (status == LIBSPDM_STATUS_CRYPTO_ERROR))) {
516 0 : libspdm_free_session_id(context, *session_id);
517 : }
518 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "transport_encode_message : %xu\n", status));
519 0 : return status;
520 : }
521 :
522 0 : if ((session_id != NULL) &&
523 0 : (context->last_spdm_error.error_code == SPDM_ERROR_CODE_DECRYPT_ERROR)) {
524 0 : libspdm_free_session_id(context, *session_id);
525 : }
526 :
527 0 : libspdm_zero_mem(&context->last_spdm_error, sizeof(context->last_spdm_error));
528 0 : return LIBSPDM_STATUS_SUCCESS;
529 : }
530 :
531 8 : if (session_id != NULL) {
532 0 : session_info = libspdm_get_session_info_via_session_id(context, *session_id);
533 0 : if (session_info == NULL) {
534 0 : LIBSPDM_ASSERT(false);
535 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
536 : }
537 : }
538 :
539 8 : if (*response == NULL) {
540 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
541 : }
542 8 : if ((response_size == NULL) || (*response_size == 0)) {
543 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
544 : }
545 :
546 8 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmSendResponse[%x] ...\n",
547 : (session_id != NULL) ? *session_id : 0));
548 :
549 8 : spdm_request = (void *)context->last_spdm_request;
550 8 : if (context->last_spdm_request_size == 0) {
551 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
552 : }
553 :
554 8 : get_response_func = NULL;
555 8 : if (!is_app_message) {
556 8 : get_response_func = libspdm_get_response_func_via_last_request(context);
557 :
558 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
559 : /* Per DSP0274: The chunked transfer shall not be interrupted by any commands
560 : * that are not part of the chunk transfer sequence, with the exception of
561 : * GET_VERSION. The Responder shall return ErrorCode=UnexpectedRequest if an
562 : * unexpected command is received during the chunked transfer. These error codes
563 : * shall not interrupt the chunk transfer sequence. */
564 8 : if (context->chunk_context.get.chunk_in_use
565 2 : && get_response_func != libspdm_get_response_chunk_get) {
566 :
567 2 : if (get_response_func == libspdm_get_response_version) {
568 : /* GET_VERSION is allowed to interrupt chunk transfer.
569 : * Reset chunk get context and proceed normally. */
570 1 : context->chunk_context.get.chunk_in_use = false;
571 1 : context->chunk_context.get.chunk_handle++;
572 1 : context->chunk_context.get.chunk_seq_no = 0;
573 1 : context->chunk_context.get.large_message = NULL;
574 1 : context->chunk_context.get.large_message_size = 0;
575 1 : context->chunk_context.get.chunk_bytes_transferred = 0;
576 : } else {
577 : /* Reject with UnexpectedRequest without terminating
578 : * the chunk transfer sequence. */
579 1 : status = libspdm_generate_error_response(
580 : context, SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
581 : &my_response_size, my_response);
582 1 : goto response_dispatched;
583 : }
584 : }
585 7 : if (context->chunk_context.send.chunk_in_use
586 2 : && get_response_func != libspdm_get_response_chunk_send) {
587 :
588 2 : if (get_response_func == libspdm_get_response_version) {
589 : /* GET_VERSION is allowed to interrupt chunk transfer.
590 : * Reset chunk send context and proceed normally. */
591 1 : context->chunk_context.send.chunk_in_use = false;
592 1 : context->chunk_context.send.chunk_handle = 0;
593 1 : context->chunk_context.send.chunk_seq_no = 0;
594 1 : context->chunk_context.send.large_message = NULL;
595 1 : context->chunk_context.send.large_message_size = 0;
596 1 : context->chunk_context.send.chunk_bytes_transferred = 0;
597 : } else {
598 : /* Reject with UnexpectedRequest without terminating
599 : * the chunk transfer sequence. */
600 1 : status = libspdm_generate_error_response(
601 : context, SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
602 : &my_response_size, my_response);
603 1 : goto response_dispatched;
604 : }
605 : }
606 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
607 :
608 6 : if (get_response_func != NULL) {
609 6 : status = get_response_func(
610 : context,
611 : context->last_spdm_request_size,
612 6 : context->last_spdm_request,
613 : &my_response_size, my_response);
614 : }
615 : }
616 6 : if (is_app_message || (get_response_func == NULL)) {
617 0 : if (context->get_response_func != NULL) {
618 0 : status = ((libspdm_get_response_func) context->get_response_func)(
619 : context, session_id, is_app_message,
620 : context->last_spdm_request_size,
621 0 : context->last_spdm_request,
622 : &my_response_size, my_response);
623 : } else {
624 0 : status = LIBSPDM_STATUS_UNSUPPORTED_CAP;
625 : }
626 : }
627 :
628 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
629 6 : response_dispatched:
630 8 : if (libspdm_get_connection_version(context) < SPDM_MESSAGE_VERSION_14) {
631 8 : chunk_send_ack_response_header_size = sizeof(spdm_chunk_send_ack_response_t);
632 : } else {
633 0 : chunk_send_ack_response_header_size = sizeof(spdm_chunk_send_ack_response_14_t);
634 : }
635 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
636 :
637 8 : if (status == LIBSPDM_STATUS_SUCCESS) {
638 8 : LIBSPDM_ASSERT (my_response_size <= context->local_context.capability.max_spdm_msg_size);
639 : /* large SPDM message is the SPDM message whose size is greater than the DataTransferSize of the receiving
640 : * SPDM endpoint or greater than the transmit buffer size of the sending SPDM endpoint */
641 8 : if ((context->connection_info.capability.max_spdm_msg_size != 0) &&
642 6 : (my_response_size > context->connection_info.capability.max_spdm_msg_size)) {
643 1 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR, "my_response_size > req max_spdm_msg_size\n"));
644 1 : actual_size = (uint32_t)my_response_size;
645 1 : status = libspdm_generate_extended_error_response(context,
646 : SPDM_ERROR_CODE_RESPONSE_TOO_LARGE,
647 : 0,
648 : sizeof(uint32_t),
649 : (uint8_t *)&actual_size,
650 : &my_response_size, my_response);
651 7 : } else if ((((context->connection_info.capability.data_transfer_size != 0) &&
652 5 : (my_response_size > context->connection_info.capability.data_transfer_size)) ||
653 6 : ((context->local_context.capability.sender_data_transfer_size != 0) &&
654 6 : (my_response_size >
655 9 : context->local_context.capability.sender_data_transfer_size))) &&
656 3 : libspdm_is_capabilities_flag_supported(
657 : context, false, SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CHUNK_CAP,
658 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP)) {
659 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
660 :
661 3 : get_info = &context->chunk_context.get;
662 :
663 : /* Saving multiple large responses is not an expected use case.
664 : * Therefore, if the requester did not perform chunk_get requests for
665 : * previous large responses, they will be lost. */
666 3 : if (get_info->chunk_in_use) {
667 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR,
668 : "Warning: Overwriting previous unrequested chunk_get info.\n"));
669 : }
670 :
671 3 : libspdm_get_scratch_buffer(context, (void **)&scratch_buffer, &scratch_buffer_size);
672 :
673 : /* The first section of the scratch
674 : * buffer may be used for other purposes. Use only after that section. */
675 6 : large_buffer = (uint8_t *)scratch_buffer +
676 3 : libspdm_get_scratch_buffer_large_message_offset(spdm_context);
677 3 : large_buffer_size = libspdm_get_scratch_buffer_large_message_capacity(spdm_context);
678 :
679 3 : get_info->chunk_in_use = true;
680 : /* Increment chunk_handle here as opposed to end of chunk_get handler
681 : * in case requester never issues chunk_get. */
682 3 : get_info->chunk_handle++;
683 3 : get_info->chunk_seq_no = 0;
684 3 : get_info->chunk_bytes_transferred = 0;
685 :
686 3 : libspdm_zero_mem(large_buffer, large_buffer_size);
687 :
688 : /* It's possible that the large response that was to be sent to the requester was
689 : * a CHUNK_SEND_ACK + non-chunk response. In this case, to prevent chunking within
690 : * chunking, only send back the actual response, by saving only non-chunk portion
691 : * in the scratch buffer, used to respond to the next CHUNK_GET request. */
692 3 : if (((spdm_message_header_t *)my_response)
693 3 : ->request_response_code == SPDM_CHUNK_SEND_ACK) {
694 0 : libspdm_copy_mem(large_buffer, large_buffer_size,
695 0 : my_response + chunk_send_ack_response_header_size,
696 : my_response_size - chunk_send_ack_response_header_size);
697 0 : get_info->large_message = large_buffer;
698 0 : get_info->large_message_size =
699 0 : my_response_size - chunk_send_ack_response_header_size;
700 : } else {
701 3 : libspdm_copy_mem(large_buffer, large_buffer_size, my_response, my_response_size);
702 :
703 3 : get_info->large_message = large_buffer;
704 3 : get_info->large_message_size = my_response_size;
705 : }
706 :
707 3 : status = libspdm_generate_extended_error_response(context,
708 : SPDM_ERROR_CODE_LARGE_RESPONSE, 0,
709 : sizeof(uint8_t),
710 3 : &get_info->chunk_handle,
711 : &my_response_size, my_response);
712 : #else
713 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR,
714 : "Warning: Could not save chunk. Scratch buffer too small.\n"));
715 :
716 : status = libspdm_generate_extended_error_response(context,
717 : SPDM_ERROR_CODE_LARGE_RESPONSE,
718 : 0, 0, NULL,
719 : &my_response_size, my_response);
720 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
721 :
722 3 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
723 0 : return status;
724 : }
725 : }
726 : }
727 :
728 : /* if return the status: Responder drop the response
729 : * just ignore this message
730 : * return UNSUPPORTED and clear response_size to continue the dispatch without send response.*/
731 8 : if ((my_response_size == 0) && (status == LIBSPDM_STATUS_UNSUPPORTED_CAP)) {
732 0 : *response_size = 0;
733 0 : status = LIBSPDM_STATUS_UNSUPPORTED_CAP;
734 0 : goto done;
735 : }
736 :
737 8 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
738 0 : status = libspdm_generate_error_response(
739 : context, SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
740 0 : spdm_request->request_response_code, &my_response_size, my_response);
741 0 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
742 0 : goto done;
743 : }
744 : }
745 :
746 8 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmSendResponse[%x]: msg %s(0x%x), size (0x%zx): \n",
747 : (session_id != NULL) ? *session_id : 0,
748 : libspdm_get_code_str(spdm_response->request_response_code),
749 : spdm_response->request_response_code,
750 : my_response_size));
751 8 : LIBSPDM_INTERNAL_DUMP_HEX(my_response, my_response_size);
752 :
753 8 : status = context->transport_encode_message(
754 : context, session_id, is_app_message, false,
755 : my_response_size, my_response, response_size, response);
756 8 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
757 0 : if ((session_id != NULL) &&
758 0 : ((status == LIBSPDM_STATUS_SEQUENCE_NUMBER_OVERFLOW) ||
759 : (status == LIBSPDM_STATUS_CRYPTO_ERROR))) {
760 0 : libspdm_free_session_id(context, *session_id);
761 : }
762 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "transport_encode_message : %xu\n", status));
763 0 : goto done;
764 : }
765 :
766 8 : request_response_code = spdm_response->request_response_code;
767 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
768 8 : switch (request_response_code) {
769 0 : case SPDM_CHUNK_SEND_ACK:
770 0 : if (my_response_size > chunk_send_ack_response_header_size) {
771 0 : request_response_code =
772 0 : ((spdm_message_header_t *)(my_response + chunk_send_ack_response_header_size))
773 : ->request_response_code;
774 : }
775 0 : break;
776 0 : case SPDM_CHUNK_RESPONSE:
777 0 : chunk_rsp = (spdm_chunk_response_response_t *)my_response;
778 0 : chunk_ptr = (uint8_t *)(((uint32_t *)(chunk_rsp + 1)) + 1);
779 0 : if (chunk_rsp->chunk_seq_no == 0) {
780 0 : request_response_code = ((spdm_message_header_t *)chunk_ptr)->request_response_code;
781 : }
782 0 : break;
783 8 : default:
784 8 : break;
785 : }
786 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
787 :
788 8 : if (session_id != NULL) {
789 0 : switch (request_response_code) {
790 0 : case SPDM_FINISH_RSP:
791 0 : if (!libspdm_is_capabilities_flag_supported(
792 : context, false,
793 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP,
794 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP)) {
795 0 : libspdm_set_session_state(
796 : context, *session_id,
797 : LIBSPDM_SESSION_STATE_ESTABLISHED);
798 : }
799 0 : break;
800 0 : case SPDM_PSK_FINISH_RSP:
801 0 : libspdm_set_session_state(context, *session_id, LIBSPDM_SESSION_STATE_ESTABLISHED);
802 0 : break;
803 0 : case SPDM_END_SESSION_ACK:
804 : #if LIBSPDM_ENABLE_CAPABILITY_HBEAT_CAP
805 0 : if (libspdm_is_capabilities_flag_supported(
806 : context, false,
807 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HBEAT_CAP,
808 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_HBEAT_CAP)) {
809 0 : result = libspdm_stop_watchdog(*session_id);
810 0 : if (!result) {
811 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR, "libspdm_stop_watchdog error\n"));
812 : /* No need to return error for internal watchdog error. */
813 : }
814 : }
815 : #endif /* LIBSPDM_ENABLE_CAPABILITY_HBEAT_CAP */
816 0 : libspdm_terminate_session(context, *session_id);
817 0 : break;
818 0 : default:
819 : #if LIBSPDM_ENABLE_CAPABILITY_HBEAT_CAP
820 0 : if (libspdm_is_capabilities_flag_supported(
821 : context, false,
822 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HBEAT_CAP,
823 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_HBEAT_CAP)) {
824 : /* reset watchdog in any session messages. */
825 0 : result = libspdm_reset_watchdog(*session_id);
826 0 : if (!result) {
827 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR, "libspdm_reset_watchdog error\n"));
828 : /* No need to return error for internal watchdog error. */
829 : }
830 : }
831 : #endif /* LIBSPDM_ENABLE_CAPABILITY_HBEAT_CAP */
832 0 : break;
833 : }
834 : } else {
835 8 : switch (request_response_code) {
836 0 : case SPDM_FINISH_RSP:
837 0 : if (libspdm_is_capabilities_flag_supported(
838 : context, false,
839 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP,
840 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP)) {
841 0 : libspdm_set_session_state(
842 : context,
843 : context->latest_session_id,
844 : LIBSPDM_SESSION_STATE_ESTABLISHED);
845 : }
846 0 : break;
847 8 : default:
848 : /* No session state update needed */
849 8 : break;
850 : }
851 : }
852 :
853 8 : status = LIBSPDM_STATUS_SUCCESS;
854 8 : done:
855 8 : if (session_id != NULL) {
856 : /* clean plain text in stratch buffer */
857 0 : libspdm_zero_mem (my_response, my_response_size);
858 : }
859 8 : libspdm_zero_mem (context->last_spdm_request,
860 8 : libspdm_get_scratch_buffer_last_spdm_request_capacity(context));
861 8 : context->last_spdm_request_size = 0;
862 8 : context->last_spdm_request_session_id_valid = false;
863 8 : return status;
864 : }
865 :
866 0 : void libspdm_register_get_response_func(void *context, libspdm_get_response_func get_response_func)
867 : {
868 : libspdm_context_t *spdm_context;
869 :
870 0 : spdm_context = context;
871 0 : spdm_context->get_response_func = (void *)get_response_func;
872 0 : }
873 :
874 0 : void libspdm_register_session_state_callback_func(
875 : void *spdm_context,
876 : libspdm_session_state_callback_func spdm_session_state_callback)
877 : {
878 : libspdm_context_t *context;
879 :
880 0 : LIBSPDM_ASSERT(spdm_context != NULL);
881 :
882 0 : context = spdm_context;
883 :
884 0 : context->spdm_session_state_callback = (void *)spdm_session_state_callback;
885 0 : }
886 :
887 0 : void libspdm_register_connection_state_callback_func(
888 : void *spdm_context,
889 : libspdm_connection_state_callback_func spdm_connection_state_callback)
890 : {
891 : libspdm_context_t *context;
892 :
893 0 : LIBSPDM_ASSERT(spdm_context != NULL);
894 :
895 0 : context = spdm_context;
896 0 : context->spdm_connection_state_callback = (void *)spdm_connection_state_callback;
897 0 : }
898 :
899 0 : void libspdm_register_key_update_callback_func(
900 : void *spdm_context, libspdm_key_update_callback_func spdm_key_update_callback)
901 : {
902 : libspdm_context_t *context;
903 :
904 0 : LIBSPDM_ASSERT(spdm_context != NULL);
905 :
906 0 : context = spdm_context;
907 0 : context->spdm_key_update_callback = (void *)spdm_key_update_callback;
908 0 : }
909 :
910 : #if (LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP) && (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && \
911 : (LIBSPDM_SEND_GET_CERTIFICATE_SUPPORT)
912 116 : void libspdm_register_cert_chain_buffer(
913 : void *spdm_context, void *cert_chain_buffer, size_t cert_chain_buffer_max_size)
914 : {
915 : libspdm_context_t *context;
916 :
917 116 : LIBSPDM_ASSERT(spdm_context != NULL);
918 :
919 116 : context = spdm_context;
920 116 : context->mut_auth_cert_chain_buffer = cert_chain_buffer;
921 116 : context->mut_auth_cert_chain_buffer_max_size = cert_chain_buffer_max_size;
922 116 : context->mut_auth_cert_chain_buffer_size = 0;
923 116 : }
924 : #endif
|