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