Branch data 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_requester_lib.h"
8 : : #include "internal/libspdm_secured_message_lib.h"
9 : :
10 : 6252 : libspdm_return_t libspdm_send_request(void *spdm_context, const uint32_t *session_id,
11 : : bool is_app_message,
12 : : size_t request_size, void *request)
13 : : {
14 : : libspdm_context_t *context;
15 : : libspdm_return_t status;
16 : : uint8_t *message;
17 : : size_t message_size;
18 : : uint64_t timeout;
19 : : uint8_t *scratch_buffer;
20 : : size_t scratch_buffer_size;
21 : : size_t transport_header_size;
22 : : uint8_t *sender_buffer;
23 : : size_t sender_buffer_size;
24 : :
25 : 6252 : context = spdm_context;
26 : :
27 [ + + ]: 6252 : if (is_app_message) {
28 [ + - ]: 10 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
29 : : "libspdm_send_spdm_request[%x] app msg, size (0x%zx): \n",
30 : : (session_id != NULL) ? *session_id : 0x0, request_size));
31 : : } else {
32 [ + + ]: 6242 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
33 : : "libspdm_send_spdm_request[%x] msg %s(0x%x), size (0x%zx): \n",
34 : : (session_id != NULL) ? *session_id : 0x0,
35 : : libspdm_get_code_str(((spdm_message_header_t *)request)->
36 : : request_response_code),
37 : : ((spdm_message_header_t *)request)->request_response_code, request_size));
38 : : }
39 : 6252 : LIBSPDM_INTERNAL_DUMP_HEX(request, request_size);
40 : :
41 : 6252 : transport_header_size = context->local_context.capability.transport_header_size;
42 : 6252 : libspdm_get_scratch_buffer(context, (void **)&scratch_buffer, &scratch_buffer_size);
43 : 6252 : libspdm_get_sender_buffer(context, (void **)&sender_buffer, &sender_buffer_size);
44 : :
45 : : /* This is a problem because original code assumes request is in the sender buffer,
46 : : * when it can really be using the scratch space for chunking.
47 : : * Did not want to modify all request handlers to pass this information,
48 : : * so just making the determination here by examining scratch/sender buffers.
49 : : * This may be something that should be refactored in the future. */
50 : : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
51 : : /* If message is in the scratch buffer then sender_buffer can/will be NULL, so first check for
52 : : * NULLness before performing pointer arithmetic with it. */
53 [ + + + - ]: 6252 : if ((sender_buffer != NULL) && ((uint8_t *)request >= sender_buffer) &&
54 [ - + ]: 6076 : ((uint8_t *)request < (sender_buffer + sender_buffer_size))) {
55 : 0 : message = sender_buffer;
56 : 0 : message_size = sender_buffer_size;
57 : : } else {
58 : 6252 : if ((uint8_t *)request >=
59 [ + - ]: 6252 : scratch_buffer + libspdm_get_scratch_buffer_sender_receiver_offset(spdm_context)
60 : 6252 : && (uint8_t *)request <
61 : 12504 : scratch_buffer + libspdm_get_scratch_buffer_sender_receiver_offset(spdm_context)
62 [ + + ]: 6252 : + libspdm_get_scratch_buffer_sender_receiver_capacity(spdm_context)) {
63 : 424 : message = scratch_buffer +
64 : 212 : libspdm_get_scratch_buffer_sender_receiver_offset(spdm_context);
65 : 212 : message_size = libspdm_get_scratch_buffer_sender_receiver_capacity(spdm_context);
66 : 6040 : } else if ((uint8_t *)request >=
67 : 12080 : scratch_buffer +
68 [ + - ]: 6040 : libspdm_get_scratch_buffer_large_sender_receiver_offset(spdm_context)
69 : 6040 : && (uint8_t *)request <
70 : : scratch_buffer +
71 : 6040 : libspdm_get_scratch_buffer_large_sender_receiver_offset(spdm_context) +
72 [ + - ]: 6040 : libspdm_get_scratch_buffer_large_sender_receiver_capacity(spdm_context)) {
73 : 12080 : message = scratch_buffer +
74 : 6040 : libspdm_get_scratch_buffer_large_sender_receiver_offset(spdm_context);
75 : 6040 : message_size = libspdm_get_scratch_buffer_large_sender_receiver_capacity(spdm_context);
76 : : }
77 : : }
78 : : #else /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
79 : : message = sender_buffer;
80 : : message_size = sender_buffer_size;
81 : : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
82 : :
83 [ + + ]: 6252 : if (session_id != NULL) {
84 : : /* For secure message, message is in sender buffer, we need copy it to scratch buffer.
85 : : * transport_message is always in sender buffer. */
86 : :
87 : 612 : libspdm_copy_mem (scratch_buffer + transport_header_size,
88 : : scratch_buffer_size - transport_header_size,
89 : : request, request_size);
90 : 612 : request = scratch_buffer + transport_header_size;
91 : : }
92 : :
93 : : /* backup it to last_spdm_request, because the caller wants to compare it with response */
94 [ + + ]: 6252 : if (((const spdm_message_header_t *)request)->request_response_code != SPDM_RESPOND_IF_READY
95 [ + + ]: 6186 : && ((const spdm_message_header_t *)request)->request_response_code != SPDM_CHUNK_GET
96 [ + + ]: 6010 : && ((const spdm_message_header_t *)request)->request_response_code != SPDM_CHUNK_SEND) {
97 : 5974 : libspdm_copy_mem (context->last_spdm_request,
98 : 5974 : libspdm_get_scratch_buffer_last_spdm_request_capacity(context),
99 : : request,
100 : : request_size);
101 : 5974 : context->last_spdm_request_size = request_size;
102 : : }
103 : :
104 : 6252 : status = context->transport_encode_message(
105 : : context, session_id, is_app_message, true, request_size,
106 : : request, &message_size, (void **)&message);
107 [ + + ]: 6252 : if (session_id != NULL) {
108 : : /* clean up secure message which was copied to scratch buffer */
109 : 612 : libspdm_zero_mem(request, request_size);
110 : : }
111 [ - + ]: 6252 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
112 : 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "transport_encode_message status - %x\n", status));
113 [ # # # # ]: 0 : if ((session_id != NULL) &&
114 [ # # ]: 0 : ((status == LIBSPDM_STATUS_SEQUENCE_NUMBER_OVERFLOW) ||
115 : : (status == LIBSPDM_STATUS_CRYPTO_ERROR))) {
116 : 0 : libspdm_free_session_id(context, *session_id);
117 : : }
118 : 0 : return status;
119 : : }
120 : :
121 : 6252 : timeout = context->local_context.capability.rtt;
122 : 6252 : status = context->send_message(context, message_size, message, timeout);
123 : :
124 [ + + ]: 6252 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
125 [ + + ]: 62 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "libspdm_send_spdm_request[%x] status - %x\n",
126 : : (session_id != NULL) ? *session_id : 0x0, status));
127 : : }
128 : :
129 : 6252 : return status;
130 : : }
131 : :
132 : 6156 : libspdm_return_t libspdm_receive_response(void *spdm_context, const uint32_t *session_id,
133 : : bool is_app_message,
134 : : size_t *response_size,
135 : : void **response)
136 : : {
137 : : libspdm_context_t *context;
138 : : void *temp_session_context;
139 : : libspdm_return_t status;
140 : : uint8_t *message;
141 : : size_t message_size;
142 : : uint32_t *message_session_id;
143 : : uint32_t message_id;
144 : : bool is_message_app_message;
145 : : uint64_t timeout;
146 : : size_t transport_header_size;
147 : : uint8_t *scratch_buffer;
148 : : size_t scratch_buffer_size;
149 : : void *backup_response;
150 : : size_t backup_response_size;
151 : : bool reset_key_update;
152 : : bool result;
153 : :
154 : 6156 : context = spdm_context;
155 : :
156 [ + + ]: 6156 : if (context->crypto_request) {
157 : 5458 : timeout = context->local_context.capability.rtt +
158 : 5458 : ((uint64_t)1 << context->connection_info.capability.ct_exponent);
159 : : } else {
160 : 698 : timeout = context->local_context.capability.rtt + context->local_context.capability.st1;
161 : : }
162 : :
163 : 6156 : message = *response;
164 : 6156 : message_size = *response_size;
165 : 6156 : status = context->receive_message(context, &message_size, (void **)&message, timeout);
166 [ + + ]: 6156 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
167 [ + + ]: 56 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
168 : : "libspdm_receive_spdm_response[%x] status - %x\n",
169 : : (session_id != NULL) ? *session_id : 0x0, status));
170 : 56 : return status;
171 : : }
172 : :
173 : : /*
174 : : * The storage transport encoding, defined by DSP0286, does not indicate
175 : : * if we are/are not in a secure session in the transport data. This is
176 : : * different to most other transport encodings, which includes session
177 : : * information in the encoding.
178 : : *
179 : : * As such if we are in a secure session, session_id != NULL, we set
180 : : * message_session_id to be non-NULL to indicate to the lower layer
181 : : * that we are in a secure session.
182 : : */
183 [ + + ]: 6100 : if (session_id != NULL) {
184 : 582 : message_session_id = &message_id;
185 : 582 : message_id = *session_id;
186 : : } else {
187 : 5518 : message_session_id = NULL;
188 : : }
189 : 6100 : is_message_app_message = false;
190 : :
191 : : /* always use scratch buffer for the response.
192 : : * if it is secured message, this scratch buffer will be used.
193 : : * if it is normal message, the response ptr will point to receiver buffer. */
194 : 6100 : transport_header_size = context->local_context.capability.transport_header_size;
195 : 6100 : libspdm_get_scratch_buffer (context, (void **)&scratch_buffer, &scratch_buffer_size);
196 : : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
197 : 6100 : *response = scratch_buffer + libspdm_get_scratch_buffer_secure_message_offset() +
198 : : transport_header_size;
199 : 6100 : *response_size = libspdm_get_scratch_buffer_secure_message_capacity(context) -
200 : : transport_header_size;
201 : : #else
202 : : *response = scratch_buffer + transport_header_size;
203 : : *response_size = scratch_buffer_size - transport_header_size;
204 : : #endif
205 : :
206 : 6100 : backup_response = *response;
207 : 6100 : backup_response_size = *response_size;
208 : :
209 : 6100 : status = context->transport_decode_message(
210 : : context, &message_session_id, &is_message_app_message,
211 : : false, message_size, message, response_size, response);
212 : :
213 : 6100 : reset_key_update = false;
214 : 6100 : temp_session_context = NULL;
215 : :
216 [ + + ]: 6100 : if (status == LIBSPDM_STATUS_SESSION_TRY_DISCARD_KEY_UPDATE) {
217 : : /* Failed to decode, but have backup keys. Try rolling back before aborting.
218 : : * message_session_id must be valid for us to have attempted decryption. */
219 [ - + ]: 54 : if (message_session_id == NULL) {
220 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
221 : : }
222 : 54 : temp_session_context = libspdm_get_secured_message_context_via_session_id(
223 : : context, *message_session_id);
224 [ - + ]: 54 : if (temp_session_context == NULL) {
225 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
226 : : }
227 : :
228 : 54 : result = libspdm_activate_update_session_data_key(
229 : : temp_session_context, LIBSPDM_KEY_UPDATE_ACTION_RESPONDER, false);
230 [ - + ]: 54 : if (!result) {
231 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
232 : : }
233 : :
234 : : /* Retry decoding message with backup Requester key.
235 : : * Must reset some of the parameters in case they were modified */
236 [ + - ]: 54 : if (session_id != NULL) {
237 : 54 : *message_session_id = *session_id;
238 : : } else {
239 : 0 : message_session_id = NULL;
240 : : }
241 : 54 : is_message_app_message = false;
242 : 54 : *response = backup_response;
243 : 54 : *response_size = backup_response_size;
244 : 54 : status = context->transport_decode_message(
245 : : context, &message_session_id, &is_message_app_message,
246 : : false, message_size, message, response_size, response);
247 : :
248 : 54 : reset_key_update = true;
249 : : }
250 : :
251 : : /*
252 : : * decoded_message may contain padding zeros due to transport layer alignment requirements.
253 : : * trim the decoded_message size to the maximum data_transfer_size.
254 : : */
255 : 6100 : *response_size = LIBSPDM_MIN(*response_size, context->local_context.capability.data_transfer_size);
256 : :
257 [ + + ]: 6100 : if (session_id != NULL) {
258 [ + + ]: 582 : if (message_session_id == NULL) {
259 [ + - ]: 4 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
260 : : "libspdm_receive_spdm_response[%x] GetSessionId - NULL\n",
261 : : (session_id != NULL) ? *session_id : 0x0));
262 : 4 : goto error;
263 : : }
264 [ - + ]: 578 : if (*message_session_id != *session_id) {
265 [ # # ]: 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
266 : : "libspdm_receive_spdm_response[%x] GetSessionId - %x\n",
267 : : (session_id != NULL) ? *session_id : 0x0, *message_session_id));
268 : 0 : goto error;
269 : : }
270 : : } else {
271 [ - + ]: 5518 : if (message_session_id != NULL) {
272 [ # # ]: 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
273 : : "libspdm_receive_spdm_response[%x] GetSessionId - %x\n",
274 : : (session_id != NULL) ? *session_id : 0x0, *message_session_id));
275 : 0 : goto error;
276 : : }
277 : : }
278 : :
279 [ + + + - ]: 6096 : if ((is_app_message && !is_message_app_message) ||
280 [ + + - + ]: 6096 : (!is_app_message && is_message_app_message)) {
281 [ # # ]: 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
282 : : "libspdm_receive_spdm_response[%x] app_message mismatch\n",
283 : : (session_id != NULL) ? *session_id : 0x0));
284 : 0 : goto error;
285 : : }
286 : :
287 [ - + ]: 6096 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
288 [ # # ]: 0 : if ((session_id != NULL) &&
289 [ # # ]: 0 : (context->last_spdm_error.error_code == SPDM_ERROR_CODE_DECRYPT_ERROR)) {
290 : 0 : libspdm_free_session_id(context, *session_id);
291 : : }
292 [ # # ]: 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
293 : : "libspdm_receive_spdm_response[%x] status - %x\n",
294 : : (session_id != NULL) ? *session_id : 0x0, status));
295 [ + + ]: 6096 : } else if (is_app_message) {
296 [ + - ]: 6 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
297 : : "libspdm_receive_spdm_response[%x] app msg, size (0x%zx): \n",
298 : : (session_id != NULL) ? *session_id : 0x0, *response_size));
299 : 6 : LIBSPDM_INTERNAL_DUMP_HEX(*response, *response_size);
300 : : } else {
301 [ + + ]: 6090 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
302 : : "libspdm_receive_spdm_response[%x] msg %s(0x%x), size (0x%zx): \n",
303 : : (session_id != NULL) ? *session_id : 0x0,
304 : : libspdm_get_code_str(((spdm_message_header_t *)*response)->
305 : : request_response_code),
306 : : ((spdm_message_header_t *)*response)->request_response_code,
307 : : *response_size));
308 : 6090 : LIBSPDM_INTERNAL_DUMP_HEX(*response, *response_size);
309 : : }
310 : :
311 : : /* Handle special case:
312 : : * If the Responder returns RESPONSE_NOT_READY error to KEY_UPDATE, the Requester needs
313 : : * to activate backup key to parse the error. Then later the Responder will return SUCCESS,
314 : : * the Requester needs new key. So we need to restore the environment by
315 : : * libspdm_create_update_session_data_key() again.*/
316 [ + + ]: 6096 : if (reset_key_update) {
317 : : /* temp_session_context and message_session_id must necessarily
318 : : * be valid for us to reach here. */
319 [ + - - + ]: 54 : if (temp_session_context == NULL || message_session_id == NULL) {
320 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
321 : : }
322 : 54 : result = libspdm_create_update_session_data_key(
323 : : temp_session_context, LIBSPDM_KEY_UPDATE_ACTION_RESPONDER);
324 [ - + ]: 54 : if (!result) {
325 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
326 : : }
327 : : }
328 : :
329 : 6096 : return status;
330 : :
331 : 4 : error:
332 [ - + ]: 4 : if (context->last_spdm_error.error_code == SPDM_ERROR_CODE_DECRYPT_ERROR) {
333 : 0 : return LIBSPDM_STATUS_SESSION_MSG_ERROR;
334 : : } else {
335 : 4 : return LIBSPDM_STATUS_RECEIVE_FAIL;
336 : : }
337 : : }
338 : :
339 : : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
340 : 30 : static libspdm_return_t libspdm_handle_large_request(
341 : : libspdm_context_t *spdm_context,
342 : : const uint32_t *session_id,
343 : : size_t request_size, void *request)
344 : : {
345 : : libspdm_return_t status;
346 : :
347 : : spdm_chunk_send_request_t *spdm_request;
348 : : spdm_chunk_send_request_14_t *spdm_request_14;
349 : : size_t spdm_request_size;
350 : : spdm_chunk_send_ack_response_t *spdm_response;
351 : : spdm_chunk_send_ack_response_14_t *spdm_response_14;
352 : : size_t response_header_size;
353 : : uint8_t *message;
354 : : size_t message_size;
355 : : void *response;
356 : : size_t response_size;
357 : : size_t transport_header_size;
358 : :
359 : : uint8_t *scratch_buffer;
360 : : size_t scratch_buffer_size;
361 : :
362 : : uint8_t *chunk_ptr;
363 : : size_t copy_size;
364 : : libspdm_chunk_info_t *send_info;
365 : : uint32_t min_data_transfer_size;
366 : : uint64_t max_chunk_data_transfer_size;
367 : : spdm_error_response_t *spdm_error;
368 : :
369 [ - + ]: 30 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_12) {
370 : 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
371 : : }
372 : :
373 : : /* Fail if requester or responder does not support chunk cap */
374 [ - + ]: 30 : if (!libspdm_is_capabilities_flag_supported(
375 : : spdm_context, true,
376 : : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CHUNK_CAP,
377 : : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP)) {
378 : 0 : return LIBSPDM_STATUS_ERROR_PEER;
379 : : }
380 : :
381 : : /* Fail if exceed max chunks */
382 : 30 : min_data_transfer_size = LIBSPDM_MIN(
383 : : spdm_context->connection_info.capability.data_transfer_size,
384 : : spdm_context->local_context.capability.sender_data_transfer_size);
385 : :
386 [ + + ]: 30 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_14) {
387 : : /* chunk seq no wrap not considered in spdm 1.4+ */
388 : :
389 : 26 : max_chunk_data_transfer_size =
390 : 26 : ((size_t) min_data_transfer_size - sizeof(spdm_chunk_send_request_t)) * 65536 -
391 : : sizeof(uint32_t);
392 : : /* max_spdm_msg_size is already checked in caller */
393 : :
394 [ + + ]: 26 : if (request_size > max_chunk_data_transfer_size) {
395 : 2 : return LIBSPDM_STATUS_BUFFER_TOO_SMALL;
396 : : }
397 : : }
398 : : /* now we can get sender buffer */
399 : 28 : transport_header_size = spdm_context->local_context.capability.transport_header_size;
400 : :
401 : 28 : libspdm_get_scratch_buffer(spdm_context, (void **)&scratch_buffer, &scratch_buffer_size);
402 : :
403 : : /* Temporary send/receive buffers for chunking are in the scratch space */
404 : 28 : message = scratch_buffer + libspdm_get_scratch_buffer_sender_receiver_offset(spdm_context);
405 : 28 : message_size = libspdm_get_scratch_buffer_sender_receiver_capacity(spdm_context);
406 : :
407 : 28 : send_info = &spdm_context->chunk_context.send;
408 : 28 : send_info->chunk_in_use = true;
409 : :
410 : : /* The first section of the scratch
411 : : * buffer may be used for other purposes. Use only after that section. */
412 : 56 : send_info->large_message = scratch_buffer +
413 : 28 : libspdm_get_scratch_buffer_large_message_offset(spdm_context);
414 : 28 : send_info->large_message_capacity =
415 : 28 : libspdm_get_scratch_buffer_large_message_capacity(spdm_context);
416 : :
417 : 28 : libspdm_zero_mem(send_info->large_message, send_info->large_message_capacity);
418 : 28 : libspdm_copy_mem(send_info->large_message, send_info->large_message_capacity,
419 : : request, request_size);
420 : :
421 : 28 : send_info->large_message_size = request_size;
422 : 28 : send_info->chunk_bytes_transferred = 0;
423 : 28 : send_info->chunk_seq_no = 0;
424 : 28 : request = NULL; /* Invalidate to prevent accidental use. */
425 : 28 : request_size = 0;
426 : :
427 : : do {
428 : 36 : LIBSPDM_ASSERT(send_info->large_message_capacity >= transport_header_size);
429 : 36 : spdm_request = (spdm_chunk_send_request_t *)((uint8_t *)message + transport_header_size);
430 : 36 : spdm_request_size = message_size - transport_header_size;
431 : :
432 : 36 : spdm_request->header.spdm_version = libspdm_get_connection_version(spdm_context);
433 : 36 : spdm_request->header.request_response_code = SPDM_CHUNK_SEND;
434 : 36 : spdm_request->header.param1 = 0;
435 : 36 : spdm_request->header.param2 = send_info->chunk_handle;
436 : :
437 [ + + ]: 36 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_14) {
438 : 28 : spdm_request->chunk_seq_no = (uint16_t) send_info->chunk_seq_no;
439 : 28 : spdm_request->reserved = 0;
440 : 28 : chunk_ptr = (uint8_t *)(spdm_request + 1);
441 : : } else {
442 : 8 : spdm_request_14 = (spdm_chunk_send_request_14_t *)spdm_request;
443 : 8 : spdm_request_14->chunk_seq_no = send_info->chunk_seq_no;
444 : 8 : chunk_ptr = (uint8_t *)(spdm_request_14 + 1);
445 : : }
446 : :
447 : : LIBSPDM_ASSERT(sizeof(spdm_chunk_send_request_t) == sizeof(spdm_chunk_send_request_14_t));
448 : 36 : if ((min_data_transfer_size - sizeof(spdm_chunk_send_request_t)) <
449 [ + + ]: 36 : (send_info->large_message_size - send_info->chunk_bytes_transferred)) {
450 : 28 : copy_size = min_data_transfer_size - sizeof(spdm_chunk_send_request_t);
451 : : } else {
452 : 8 : copy_size = (send_info->large_message_size - send_info->chunk_bytes_transferred);
453 : : }
454 : :
455 [ + + ]: 36 : if (send_info->chunk_seq_no == 0) {
456 : 28 : libspdm_write_uint32((uint8_t *)(spdm_request + 1),
457 : 28 : (uint32_t)send_info->large_message_size);
458 : 28 : chunk_ptr += sizeof(uint32_t);
459 : 28 : copy_size -= sizeof(uint32_t);
460 : : }
461 : :
462 : 36 : spdm_request->chunk_size = (uint32_t)copy_size;
463 : :
464 : 36 : libspdm_copy_mem(
465 : 36 : chunk_ptr, spdm_request_size - ((uint8_t *)spdm_request - (uint8_t *)message),
466 : 36 : (uint8_t *)send_info->large_message + send_info->chunk_bytes_transferred, copy_size);
467 : :
468 : 36 : send_info->chunk_bytes_transferred += copy_size;
469 [ + + ]: 36 : if (send_info->chunk_bytes_transferred >= send_info->large_message_size) {
470 : 8 : spdm_request->header.param1 |= SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK;
471 : : }
472 : :
473 : 36 : spdm_request_size = (chunk_ptr + copy_size) - (uint8_t *)spdm_request;
474 : 36 : status = libspdm_send_request(
475 : : spdm_context, session_id, false,
476 : : spdm_request_size, spdm_request);
477 : :
478 [ + + ]: 36 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
479 : 2 : break;
480 : : }
481 : :
482 : 34 : response = message;
483 : 34 : response_size = message_size;
484 : :
485 : 34 : libspdm_zero_mem(response, response_size);
486 : :
487 : 34 : status = libspdm_receive_response(
488 : : spdm_context, session_id, false,
489 : : &response_size, &response);
490 : :
491 [ + + ]: 34 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
492 : 2 : break;
493 : : }
494 : 32 : spdm_response = (void *)(response);
495 : :
496 [ - + ]: 32 : if (response_size < sizeof(spdm_message_header_t)) {
497 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
498 : 0 : break;
499 : : }
500 : :
501 [ + + ]: 32 : if (spdm_response->header.request_response_code == SPDM_ERROR
502 [ + + ]: 6 : && spdm_response->header.param1 != SPDM_ERROR_CODE_LARGE_RESPONSE) {
503 : 4 : status = libspdm_handle_simple_error_response(spdm_context,
504 : 4 : spdm_response->header.param1);
505 : 4 : break;
506 : : }
507 : :
508 [ + + ]: 28 : if (spdm_response->header.spdm_version != libspdm_get_connection_version(spdm_context)) {
509 : 2 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
510 : 2 : break;
511 : : }
512 : :
513 [ + + ]: 26 : if (spdm_response->header.request_response_code == SPDM_ERROR
514 [ + - ]: 2 : && spdm_response->header.param1 == SPDM_ERROR_CODE_LARGE_RESPONSE) {
515 : :
516 : : /* It is possible that the CHUNK_SEND_ACK + chunk response is larger
517 : : * than the DATA_TRANSFER_SIZE. In this case an ERROR_LARGE_RESPONSE
518 : : * is returned directly in the response buffer rather than part of
519 : : * the CHUNK_SEND_ACK. Store this error response in scratch buffer
520 : : * to be handled when reading response. Also note that in this case
521 : : * of large response, the CHUNK_SEND_ACK portion is not sent.
522 : : * Only the response portion that requires the CHUNK_GET is sent */
523 [ + - ]: 2 : if (response_size < send_info->large_message_capacity) {
524 : 2 : libspdm_copy_mem(
525 : : send_info->large_message, send_info->large_message_capacity,
526 : : spdm_response, response_size);
527 : 2 : send_info->large_message_size = response_size;
528 : 2 : break;
529 : : } else {
530 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
531 : 0 : break;
532 : : }
533 : : } else {
534 [ - + ]: 24 : if (spdm_response->header.request_response_code != SPDM_CHUNK_SEND_ACK) {
535 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
536 : 0 : break;
537 : : }
538 : :
539 [ + + ]: 24 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_14) {
540 : 18 : response_header_size = sizeof(spdm_chunk_send_ack_response_t);
541 : : } else {
542 : 6 : response_header_size = sizeof(spdm_chunk_send_ack_response_14_t);
543 : 6 : spdm_response_14 = (spdm_chunk_send_ack_response_14_t *)spdm_response;
544 : : }
545 : :
546 [ + + ]: 24 : if (response_size < response_header_size) {
547 : 2 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
548 : 2 : break;
549 : : }
550 : 22 : if (spdm_response->header.param1
551 [ + + ]: 22 : & SPDM_CHUNK_SEND_ACK_RESPONSE_ATTRIBUTE_EARLY_ERROR_DETECTED) {
552 : :
553 : 4 : spdm_error = (spdm_error_response_t *)((uint8_t *)spdm_response + response_header_size);
554 [ - + ]: 4 : if (response_size < (response_header_size +
555 : : sizeof(spdm_error_response_t))) {
556 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
557 : 0 : break;
558 : : }
559 [ + - ]: 4 : if ((spdm_error->header.spdm_version !=
560 : 4 : libspdm_get_connection_version(spdm_context)) ||
561 [ - + ]: 4 : (spdm_error->header.request_response_code != SPDM_ERROR)) {
562 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
563 : 0 : break;
564 : : }
565 [ + + ]: 4 : if (spdm_error->header.param1 == SPDM_ERROR_CODE_LARGE_RESPONSE) {
566 : 2 : status = LIBSPDM_STATUS_ERROR_PEER;
567 : 2 : break;
568 : : }
569 : :
570 : : /* Store the error response in scratch buffer to be read by
571 : : * libspdm_receive_spdm_response and returned to its caller
572 : : * and handled in the error response handling flow */
573 : 2 : libspdm_copy_mem(
574 : : send_info->large_message,
575 : : send_info->large_message_capacity,
576 : : (uint8_t *)spdm_response + response_header_size,
577 : : response_size - response_header_size);
578 : :
579 : 2 : send_info->large_message_size =
580 : 2 : (response_size - response_header_size);
581 : 2 : status = LIBSPDM_STATUS_SUCCESS;
582 : 2 : break;
583 : : }
584 [ + + ]: 18 : if (spdm_response->header.param2 != send_info->chunk_handle) {
585 : 2 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
586 : 2 : break;
587 : : }
588 [ + + ]: 16 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_14) {
589 [ + + ]: 10 : if (send_info->chunk_seq_no != spdm_response->chunk_seq_no) {
590 : 2 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
591 : 2 : break;
592 : : }
593 : : } else {
594 [ - + ]: 6 : if (send_info->chunk_seq_no != spdm_response_14->chunk_seq_no) {
595 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
596 : 0 : break;
597 : : }
598 : : }
599 : :
600 : 14 : chunk_ptr = (uint8_t *)spdm_response + response_header_size;
601 : 14 : send_info->chunk_seq_no++;
602 : :
603 [ + + ]: 14 : if (send_info->chunk_bytes_transferred >= send_info->large_message_size) {
604 : : /* All bytes have been transferred. Store response in scratch buffer
605 : : * to be read by libspdm_receive_spdm_response */
606 : 6 : libspdm_copy_mem(
607 : : send_info->large_message, send_info->large_message_capacity,
608 : : chunk_ptr, response_size - response_header_size);
609 : 6 : send_info->large_message_size =
610 : 6 : (response_size - response_header_size);
611 : 6 : break;
612 : : }
613 : : }
614 : :
615 : 8 : } while (LIBSPDM_STATUS_IS_SUCCESS(status)
616 [ + - + - ]: 8 : && send_info->chunk_bytes_transferred < send_info->large_message_size);
617 : :
618 [ + + ]: 28 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
619 : 18 : libspdm_zero_mem(send_info->large_message, send_info->large_message_capacity);
620 : 18 : send_info->chunk_in_use = false;
621 : 18 : send_info->chunk_handle++; /* Implicit wrap-around*/
622 : 18 : send_info->chunk_seq_no = 0;
623 : 18 : send_info->chunk_bytes_transferred = 0;
624 : 18 : send_info->large_message = NULL;
625 : 18 : send_info->large_message_size = 0;
626 : 18 : send_info->large_message_capacity = 0;
627 : : }
628 : :
629 : 28 : return status;
630 : : }
631 : : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
632 : :
633 : 6238 : libspdm_return_t libspdm_send_spdm_request(libspdm_context_t *spdm_context,
634 : : const uint32_t *session_id,
635 : : size_t request_size, void *request)
636 : : {
637 : : libspdm_session_info_t *session_info;
638 : : libspdm_session_state_t session_state;
639 : : libspdm_return_t status;
640 : : #if LIBSPDM_ENABLE_MSG_LOG
641 : : size_t msg_log_size;
642 : : #endif /* LIBSPDM_ENABLE_MSG_LOG */
643 : :
644 : : /* If chunking is not supported then message must fit in both the send buffer and the receive
645 : : * buffer. */
646 [ + + ]: 6238 : if (!libspdm_is_capabilities_flag_supported(
647 : : spdm_context, true,
648 : : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CHUNK_CAP,
649 : : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP)) {
650 [ + + ]: 5972 : if ((spdm_context->connection_info.capability.data_transfer_size != 0) &&
651 [ - + ]: 32 : (request_size > spdm_context->connection_info.capability.data_transfer_size)) {
652 : 0 : return LIBSPDM_STATUS_PEER_BUFFER_TOO_SMALL;
653 : : }
654 [ + - ]: 5972 : if ((spdm_context->local_context.capability.sender_data_transfer_size != 0) &&
655 [ - + ]: 5972 : (request_size > spdm_context->local_context.capability.sender_data_transfer_size)) {
656 : 0 : return LIBSPDM_STATUS_BUFFER_TOO_SMALL;
657 : : }
658 : : }
659 : :
660 [ + + + + ]: 6922 : if ((session_id != NULL) &&
661 : 684 : libspdm_is_capabilities_flag_supported(
662 : : spdm_context, true,
663 : : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP,
664 : : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP)) {
665 : 82 : session_info = libspdm_get_session_info_via_session_id(spdm_context, *session_id);
666 : 82 : LIBSPDM_ASSERT(session_info != NULL);
667 [ - + ]: 82 : if (session_info == NULL) {
668 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
669 : : }
670 : 82 : session_state = libspdm_secured_message_get_session_state(
671 : : session_info->secured_message_context);
672 [ + - + - ]: 82 : if ((session_state == LIBSPDM_SESSION_STATE_HANDSHAKING) && !session_info->use_psk) {
673 : 82 : session_id = NULL;
674 : : }
675 : : }
676 : :
677 [ + + ]: 6238 : if ((spdm_context->connection_info.capability.max_spdm_msg_size != 0) &&
678 [ + + ]: 144 : (request_size > spdm_context->connection_info.capability.max_spdm_msg_size)) {
679 : 2 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR, "request_size > rsp max_spdm_msg_size\n"));
680 : 2 : return LIBSPDM_STATUS_PEER_BUFFER_TOO_SMALL;
681 : : }
682 : 6236 : LIBSPDM_ASSERT (request_size <= spdm_context->local_context.capability.max_spdm_msg_size);
683 : :
684 : : #if LIBSPDM_ENABLE_MSG_LOG
685 : : /* First save the size of the message log buffer. If there is an error it will be reverted. */
686 : 6236 : msg_log_size = libspdm_get_msg_log_size(spdm_context);
687 : 6236 : libspdm_append_msg_log(spdm_context, request, request_size);
688 : : #endif /* LIBSPDM_ENABLE_MSG_LOG */
689 : :
690 : : /* large SPDM message is the SPDM message whose size is greater than the DataTransferSize of the receiving
691 : : * SPDM endpoint or greater than the transmit buffer size of the sending SPDM endpoint */
692 [ + + ]: 6236 : if (((const spdm_message_header_t *)request)->request_response_code != SPDM_GET_VERSION
693 [ + + ]: 6062 : && ((const spdm_message_header_t *)request)->request_response_code != SPDM_GET_CAPABILITIES
694 [ + + ]: 5804 : && ((spdm_context->connection_info.capability.data_transfer_size != 0 &&
695 [ + + ]: 100 : request_size > spdm_context->connection_info.capability.data_transfer_size) ||
696 [ + - ]: 5776 : (spdm_context->local_context.capability.sender_data_transfer_size != 0 &&
697 [ + + ]: 5776 : request_size > spdm_context->local_context.capability.sender_data_transfer_size))) {
698 : :
699 : : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
700 : : /* libspdm_send_request is not called with the original request in this flow.
701 : : * This leads to the last_spdm_request field not having the original request value.
702 : : * The caller assumes the request has been copied to last_spdm_request,
703 : : * so that it can compare last_spdm_request's fields with response fields
704 : : * Therefore the request must be copied to last_spdm_request here. */
705 : :
706 [ + - ]: 30 : if (((const spdm_message_header_t *)request)->request_response_code != SPDM_RESPOND_IF_READY
707 [ + - ]: 30 : && ((const spdm_message_header_t *)request)->request_response_code != SPDM_CHUNK_GET
708 [ + - ]: 30 : && ((const spdm_message_header_t *)request)->request_response_code != SPDM_CHUNK_SEND) {
709 : 30 : libspdm_copy_mem(
710 : : spdm_context->last_spdm_request,
711 : 30 : libspdm_get_scratch_buffer_last_spdm_request_capacity(spdm_context),
712 : : request, request_size);
713 : 30 : spdm_context->last_spdm_request_size = request_size;
714 : : }
715 : :
716 : 30 : status = libspdm_handle_large_request(
717 : : spdm_context, session_id, request_size, request);
718 : : #else /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP*/
719 : : status = LIBSPDM_STATUS_BUFFER_TOO_SMALL;
720 : : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP*/
721 : : } else {
722 : 6206 : status = libspdm_send_request(spdm_context, session_id, false, request_size, request);
723 : : }
724 : :
725 : : #if LIBSPDM_ENABLE_MSG_LOG
726 : : /* If there is an error in sending the request then revert the request in the message log. */
727 [ + + ]: 6236 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
728 : 80 : spdm_context->msg_log.buffer_size = msg_log_size;
729 : : }
730 : : #endif /* LIBSPDM_ENABLE_MSG_LOG */
731 : :
732 : 6236 : return status;
733 : : }
734 : :
735 : 6124 : libspdm_return_t libspdm_receive_spdm_response(libspdm_context_t *spdm_context,
736 : : const uint32_t *session_id,
737 : : size_t *response_size,
738 : : void **response)
739 : : {
740 : : libspdm_return_t status;
741 : : libspdm_session_info_t *session_info;
742 : : libspdm_session_state_t session_state;
743 : :
744 : : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
745 : : spdm_message_header_t *spdm_response;
746 : : size_t response_capacity;
747 : : libspdm_chunk_info_t *send_info;
748 : 6124 : bool response_from_chunk = false;
749 : : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
750 : :
751 [ + + + + ]: 6786 : if ((session_id != NULL) &&
752 : 662 : libspdm_is_capabilities_flag_supported(
753 : : spdm_context, true,
754 : : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP,
755 : : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_HANDSHAKE_IN_THE_CLEAR_CAP)) {
756 : 80 : session_info = libspdm_get_session_info_via_session_id(spdm_context, *session_id);
757 : 80 : LIBSPDM_ASSERT(session_info != NULL);
758 [ - + ]: 80 : if (session_info == NULL) {
759 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
760 : : }
761 : 80 : session_state = libspdm_secured_message_get_session_state(
762 : : session_info->secured_message_context);
763 [ + - + - ]: 80 : if ((session_state == LIBSPDM_SESSION_STATE_HANDSHAKING) && !session_info->use_psk) {
764 : 80 : session_id = NULL;
765 : : }
766 : : }
767 : :
768 : : #if !(LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP)
769 : : status = libspdm_receive_response(spdm_context, session_id, false, response_size, response);
770 : : #else /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
771 : 6124 : send_info = &spdm_context->chunk_context.send;
772 [ + + ]: 6124 : if (send_info->chunk_in_use) {
773 : 10 : libspdm_copy_mem(*response, *response_size,
774 : 10 : send_info->large_message, send_info->large_message_size);
775 : 10 : *response_size = send_info->large_message_size;
776 : 10 : response_capacity = send_info->large_message_capacity;
777 : 10 : response_from_chunk = true;
778 : :
779 : : /* This response may either be an actual response or ERROR_LARGE_RESPONSE,
780 : : * the latter which should be handled in the large response handler. */
781 : 10 : libspdm_zero_mem(send_info->large_message, send_info->large_message_capacity);
782 : 10 : send_info->chunk_in_use = false;
783 : 10 : send_info->chunk_handle++; /* Implicit wrap-around*/
784 : 10 : send_info->chunk_seq_no = 0;
785 : 10 : send_info->chunk_bytes_transferred = 0;
786 : 10 : send_info->large_message = NULL;
787 : 10 : send_info->large_message_size = 0;
788 : 10 : send_info->large_message_capacity = 0;
789 : 10 : status = LIBSPDM_STATUS_SUCCESS;
790 : : } else {
791 : 6114 : response_capacity = *response_size;
792 : 6114 : status = libspdm_receive_response(spdm_context, session_id, false, response_size, response);
793 [ + + ]: 6114 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
794 : 56 : goto receive_done;
795 : : }
796 : : }
797 : :
798 : 6068 : spdm_response = (spdm_message_header_t *)(*response);
799 : :
800 [ - + ]: 6068 : if (*response_size < sizeof(spdm_message_header_t)) {
801 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
802 : 0 : goto receive_done;
803 : : }
804 : :
805 [ + + ]: 6068 : if (spdm_response->request_response_code == SPDM_ERROR
806 [ + + ]: 1090 : && spdm_response->param1 == SPDM_ERROR_CODE_LARGE_RESPONSE
807 [ + + ]: 26 : && !spdm_context->chunk_context.get.chunk_in_use) {
808 : 24 : status = libspdm_handle_error_large_response(
809 : : spdm_context, session_id,
810 : : response_size, (void *)spdm_response, response_capacity, response_from_chunk);
811 : :
812 [ + + ]: 24 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
813 : 10 : goto receive_done;
814 : : }
815 : :
816 [ - + ]: 14 : if (*response_size < sizeof(spdm_message_header_t)) {
817 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
818 : 0 : goto receive_done;
819 : : }
820 : :
821 : : /* Per the spec, SPDM_VERSION shall not be chunked
822 : : * and should be an unexpected error. */
823 [ - + ]: 14 : if (spdm_response->request_response_code == SPDM_VERSION) {
824 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
825 : 0 : goto receive_done;
826 : : }
827 : :
828 : : /* Per the spec, SPDM_CAPABILITIES shall not be chunked unless
829 : : * the response includes Supported Algorithms. */
830 [ + + ]: 14 : if (spdm_response->request_response_code == SPDM_CAPABILITIES) {
831 [ + - ]: 2 : if ((spdm_response->param1 &
832 : : SPDM_CAPABILITIES_RESPONSE_PARAM1_SUPPORTED_ALGORITHMS) == 0) {
833 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
834 : 0 : goto receive_done;
835 : : }
836 : : }
837 : : }
838 : :
839 : 6058 : receive_done:
840 : : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
841 : :
842 : 6124 : return status;
843 : : }
|