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_responder_lib.h"
8 : :
9 : : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
10 : :
11 : 64 : libspdm_return_t libspdm_get_response_chunk_send(libspdm_context_t *spdm_context,
12 : : size_t request_size,
13 : : const void *request,
14 : : size_t *response_size,
15 : : void *response)
16 : : {
17 : : const spdm_chunk_send_request_t *spdm_request;
18 : : const spdm_chunk_send_request_14_t *spdm_request_14;
19 : : spdm_chunk_send_ack_response_t *spdm_response;
20 : : spdm_chunk_send_ack_response_14_t *spdm_response_14;
21 : : size_t response_header_size;
22 : : libspdm_chunk_info_t *send_info;
23 : 64 : libspdm_return_t status = LIBSPDM_STATUS_SUCCESS;
24 : : const uint8_t *chunk;
25 : : uint32_t large_message_size;
26 : : uint32_t calc_max_chunk_size;
27 : : uint8_t *scratch_buffer;
28 : : size_t scratch_buffer_size;
29 : : uint8_t* chunk_response;
30 : : size_t chunk_response_size;
31 : : uint64_t max_chunk_data_transfer_size;
32 : : uint32_t chunk_seq_no;
33 : :
34 : 64 : spdm_request = (const spdm_chunk_send_request_t*) request;
35 : :
36 [ - + ]: 64 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_12) {
37 : 0 : return libspdm_generate_error_response(spdm_context,
38 : : SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
39 : : SPDM_CHUNK_SEND,
40 : : response_size, response);
41 : : }
42 : :
43 [ + + ]: 64 : if (!libspdm_is_capabilities_flag_supported(
44 : : spdm_context, false, SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CHUNK_CAP,
45 : : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP)) {
46 : 2 : return libspdm_generate_error_response(
47 : : spdm_context,
48 : : SPDM_ERROR_CODE_UNSUPPORTED_REQUEST, SPDM_CHUNK_SEND,
49 : : response_size, response);
50 : : }
51 : :
52 : : /*chunk mechanism can be used for normal or encap state*/
53 [ + + ]: 62 : if (spdm_context->response_state != LIBSPDM_RESPONSE_STATE_NORMAL) {
54 : 2 : return libspdm_responder_handle_response_state(
55 : : spdm_context,
56 : 2 : spdm_request->header.request_response_code,
57 : : response_size, response);
58 : : }
59 : :
60 [ + + ]: 60 : if (spdm_context->connection_info.connection_state <
61 : : LIBSPDM_CONNECTION_STATE_AFTER_CAPABILITIES) {
62 : 2 : libspdm_generate_error_response(
63 : : spdm_context, SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
64 : : response_size, response);
65 : 2 : return LIBSPDM_STATUS_SUCCESS;
66 : : }
67 : :
68 : : LIBSPDM_ASSERT(sizeof(spdm_chunk_send_request_t) == sizeof(spdm_chunk_send_request_14_t));
69 [ + + ]: 58 : if (request_size < sizeof(spdm_chunk_send_request_t)) {
70 : 2 : libspdm_generate_error_response(
71 : : spdm_context, SPDM_ERROR_CODE_INVALID_REQUEST, 0,
72 : : response_size, response);
73 : 2 : return LIBSPDM_STATUS_SUCCESS;
74 : : }
75 : :
76 [ + + ]: 56 : if (spdm_request->header.spdm_version < SPDM_MESSAGE_VERSION_12) {
77 : 2 : return libspdm_generate_error_response(
78 : : spdm_context, SPDM_ERROR_CODE_UNSUPPORTED_REQUEST, SPDM_CHUNK_SEND,
79 : : response_size, response);
80 : : }
81 : :
82 [ + + ]: 54 : if (spdm_request->header.spdm_version
83 : 54 : != libspdm_get_connection_version(spdm_context)) {
84 : 2 : return libspdm_generate_error_response(
85 : : spdm_context, SPDM_ERROR_CODE_VERSION_MISMATCH, 0,
86 : : response_size, response);
87 : : }
88 : :
89 [ + + ]: 52 : if (spdm_context->chunk_context.get.chunk_in_use) {
90 : : /* Specification does not support simultaneous chunk send and chunk get. */
91 : 2 : return libspdm_generate_error_response(
92 : : spdm_context, SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
93 : : response_size, response);
94 : : }
95 : :
96 [ + + ]: 50 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_14) {
97 : 46 : chunk_seq_no = spdm_request->chunk_seq_no;
98 : : } else {
99 : 4 : spdm_request_14 = (const spdm_chunk_send_request_14_t *) spdm_request;
100 : 4 : chunk_seq_no = spdm_request_14->chunk_seq_no;
101 : : }
102 : :
103 : 50 : send_info = &spdm_context->chunk_context.send;
104 : :
105 [ + + + + ]: 50 : if (send_info->chunk_in_use && !libspdm_chunk_is_same_session(spdm_context, send_info)) {
106 : : /* Another session owns the transfer. Leave it untouched. */
107 : 4 : return libspdm_generate_error_response(
108 : : spdm_context, SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
109 : : response_size, response);
110 : : }
111 : :
112 [ + + ]: 46 : if (!send_info->chunk_in_use) {
113 : :
114 [ - + ]: 20 : if (request_size < sizeof(spdm_chunk_send_request_t) + sizeof(uint32_t)) {
115 : 0 : libspdm_generate_error_response(
116 : : spdm_context, SPDM_ERROR_CODE_INVALID_REQUEST, 0,
117 : : response_size, response);
118 : 0 : return LIBSPDM_STATUS_SUCCESS;
119 : : }
120 : :
121 : 20 : large_message_size = libspdm_read_uint32((const uint8_t *)(spdm_request + 1));
122 : 20 : chunk = (((const uint8_t*) (spdm_request + 1)) + sizeof(uint32_t));
123 : 20 : calc_max_chunk_size =
124 : 20 : (uint32_t)request_size - (sizeof(spdm_chunk_send_request_t) + sizeof(uint32_t));
125 : :
126 [ + + ]: 20 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_14) {
127 : 18 : max_chunk_data_transfer_size =
128 : 18 : ((size_t) spdm_context->local_context.capability.data_transfer_size
129 : 18 : - sizeof(spdm_chunk_send_request_t)) * 65536 - sizeof(uint32_t);
130 : : } else {
131 : : /* chunk seq no wrap not considered in spdm 1.4+ */
132 : 2 : max_chunk_data_transfer_size = UINT64_MAX;
133 : : }
134 : :
135 [ + + ]: 20 : if (chunk_seq_no != 0
136 [ + - ]: 18 : || (spdm_request->chunk_size
137 : : < SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12
138 : : - sizeof(spdm_chunk_send_request_t)
139 : : - sizeof(uint32_t))
140 [ + + ]: 18 : || spdm_request->chunk_size > calc_max_chunk_size
141 [ + + ]: 16 : || (uint32_t)request_size > spdm_context->local_context.capability.data_transfer_size
142 [ + + ]: 14 : || large_message_size > spdm_context->local_context.capability.max_spdm_msg_size
143 [ + - ]: 10 : || large_message_size > max_chunk_data_transfer_size
144 [ + - ]: 10 : || large_message_size <= SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12
145 [ + + ]: 10 : || (spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK)
146 : : ) {
147 : 12 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
148 : : } else {
149 : 8 : libspdm_get_scratch_buffer(spdm_context, (void**) &scratch_buffer,
150 : : &scratch_buffer_size);
151 : :
152 : 8 : send_info->chunk_in_use = true;
153 : 8 : send_info->session_id_valid = spdm_context->last_spdm_request_session_id_valid;
154 : 8 : send_info->session_id = spdm_context->last_spdm_request_session_id;
155 : 8 : send_info->chunk_handle = spdm_request->header.param2;
156 : 8 : send_info->chunk_seq_no = chunk_seq_no;
157 : 16 : send_info->large_message = scratch_buffer +
158 : 8 : libspdm_get_scratch_buffer_large_message_offset(spdm_context);
159 : 8 : send_info->large_message_capacity =
160 : 8 : libspdm_get_scratch_buffer_large_message_capacity(spdm_context);
161 : 8 : send_info->large_message_size = large_message_size;
162 : 8 : send_info->chunk_bytes_transferred = spdm_request->chunk_size;
163 : :
164 : 8 : libspdm_copy_mem(
165 : : send_info->large_message, send_info->large_message_capacity,
166 : 8 : chunk, spdm_request->chunk_size);
167 : : }
168 : : } else {
169 : :
170 : 26 : chunk = (const uint8_t*) (spdm_request + 1);
171 : 26 : calc_max_chunk_size =
172 : 26 : (uint32_t)request_size - sizeof(spdm_chunk_send_request_t);
173 : :
174 [ + + ]: 26 : if (chunk_seq_no != send_info->chunk_seq_no + 1) {
175 : 10 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
176 : : }
177 : :
178 [ + + ]: 26 : if (spdm_request->header.param2 != send_info->chunk_handle
179 [ + - ]: 24 : || spdm_request->chunk_size > calc_max_chunk_size
180 : 24 : || spdm_request->chunk_size + send_info->chunk_bytes_transferred
181 [ + + ]: 24 : > send_info->large_message_size) {
182 : 8 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
183 [ + + ]: 18 : } else if ((spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK)
184 : 8 : && (spdm_request->chunk_size + send_info->chunk_bytes_transferred
185 [ - + ]: 8 : != send_info->large_message_size)) {
186 : 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
187 [ + + ]: 18 : } else if (!(spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK)
188 : 10 : && ((spdm_request->chunk_size + send_info->chunk_bytes_transferred
189 [ + - ]: 10 : > send_info->large_message_size)
190 [ + + ]: 10 : || (spdm_request->chunk_size
191 : : < SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12
192 : : - sizeof(spdm_chunk_send_request_t))
193 : 4 : || ((uint32_t) request_size
194 [ - + ]: 4 : > spdm_context->local_context.capability.data_transfer_size))) {
195 : 6 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
196 [ + + ]: 12 : } else if (chunk_seq_no == 0) {
197 : : /* Chunk seq no wrapped */
198 : 2 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
199 : : } else {
200 : :
201 : 10 : libspdm_copy_mem(
202 : 10 : (uint8_t*)send_info->large_message + send_info->chunk_bytes_transferred,
203 : 10 : send_info->large_message_size - send_info->chunk_bytes_transferred,
204 : 10 : chunk, spdm_request->chunk_size);
205 : :
206 : 10 : send_info->chunk_seq_no = chunk_seq_no;
207 : 10 : send_info->chunk_bytes_transferred += spdm_request->chunk_size;
208 [ + + ]: 10 : if (spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK) {
209 : 8 : send_info->chunk_in_use= false;
210 : : }
211 : : }
212 : : }
213 : :
214 [ + + ]: 46 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_14) {
215 : 42 : response_header_size = sizeof(spdm_chunk_send_ack_response_t);
216 : : } else {
217 : 4 : response_header_size = sizeof(spdm_chunk_send_ack_response_14_t);
218 : : }
219 : 46 : LIBSPDM_ASSERT(*response_size >= response_header_size);
220 : 46 : libspdm_zero_mem(response, *response_size);
221 : 46 : spdm_response = response;
222 : :
223 : 46 : spdm_response->header.spdm_version = spdm_request->header.spdm_version;
224 : 46 : spdm_response->header.request_response_code = SPDM_CHUNK_SEND_ACK;
225 : 46 : spdm_response->header.param1 = 0;
226 : 46 : spdm_response->header.param2 = spdm_request->header.param2; /* handle */
227 : :
228 [ + + ]: 46 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_14) {
229 : 42 : spdm_response->chunk_seq_no = (uint16_t) chunk_seq_no;
230 : : } else {
231 : 4 : spdm_response_14 = response;
232 : 4 : spdm_response_14->chunk_seq_no = chunk_seq_no;
233 : : }
234 : :
235 : 46 : chunk_response = (uint8_t*) spdm_response + response_header_size;
236 : 46 : chunk_response_size = *response_size - response_header_size;
237 : :
238 [ + + ]: 46 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
239 : : /* Set the EARLY_ERROR_DETECTED bit here, because one of the CHUNK_SEND requests failed.
240 : : * If there is an error after all chunks have been sent by the requester correctly,
241 : : * the responder reflects the error in the ChunkSendAck.ResponseToLargeRequest buffer,
242 : : * and not in the EARLY_ERROR_DETECTED bit. */
243 : :
244 : 28 : spdm_response->header.param1
245 : 28 : |= SPDM_CHUNK_SEND_ACK_RESPONSE_ATTRIBUTE_EARLY_ERROR_DETECTED;
246 : :
247 : 28 : libspdm_generate_error_response(
248 : : spdm_context, SPDM_ERROR_CODE_INVALID_REQUEST, 0,
249 : : &chunk_response_size, chunk_response);
250 : :
251 : 28 : *response_size = response_header_size + chunk_response_size;
252 : :
253 : : /* Return success so that libspdm_build_response() does not overwrite the
254 : : * CHUNK_SEND_ACK with EarlyErrorDetected with a generic error. */
255 : 28 : status = LIBSPDM_STATUS_SUCCESS;
256 : :
257 [ + + ]: 28 : if (send_info->large_message != NULL) {
258 : 2 : libspdm_zero_mem(send_info->large_message, send_info->large_message_capacity);
259 : : }
260 : :
261 : 28 : send_info->chunk_in_use = false;
262 : 28 : send_info->chunk_handle = 0;
263 : 28 : send_info->chunk_seq_no = 0;
264 : 28 : send_info->chunk_bytes_transferred = 0;
265 : 28 : send_info->large_message = NULL;
266 : 28 : send_info->large_message_size = 0;
267 : 28 : send_info->large_message_capacity = 0;
268 [ + + ]: 18 : } else if (send_info->chunk_bytes_transferred == send_info->large_message_size) {
269 : : uint8_t opcode;
270 : 8 : uint8_t reject_error_code = 0;
271 : :
272 : 8 : opcode = ((spdm_message_header_t*)send_info->large_message)->request_response_code;
273 : : libspdm_get_spdm_response_func response_func =
274 : 8 : libspdm_get_response_func_via_request_code(opcode);
275 : :
276 [ + - + - ]: 8 : if ((response_func == NULL) ||
277 [ - + ]: 8 : (opcode == SPDM_CHUNK_SEND) || (opcode == SPDM_CHUNK_GET)) {
278 : 0 : status = LIBSPDM_STATUS_SUCCESS;
279 : 0 : libspdm_generate_error_response(
280 : : spdm_context, SPDM_ERROR_CODE_INVALID_REQUEST, 0,
281 : : &chunk_response_size, chunk_response);
282 [ + + ]: 8 : } else if (libspdm_is_request_unexpected_for_mut_auth_encap(
283 : : spdm_context,
284 [ - + ]: 8 : spdm_context->last_spdm_request_session_id_valid ?
285 : : &spdm_context->last_spdm_request_session_id : NULL,
286 : : opcode, &reject_error_code)) {
287 : : /* libspdm_build_response() saw CHUNK_SEND, so the ordering checks are applied
288 : : * here to the assembled request. */
289 : 2 : status = LIBSPDM_STATUS_SUCCESS;
290 : 2 : libspdm_generate_error_response(
291 : : spdm_context, reject_error_code, 0,
292 : : &chunk_response_size, chunk_response);
293 : : } else {
294 : 6 : status = response_func(
295 : : spdm_context,
296 : 6 : send_info->large_message_size, send_info->large_message,
297 : : &chunk_response_size, chunk_response);
298 : : }
299 : :
300 : 8 : send_info->chunk_in_use = false;
301 : 8 : send_info->chunk_handle = 0;
302 : 8 : send_info->chunk_seq_no = 0;
303 : 8 : send_info->chunk_bytes_transferred = 0;
304 [ + - ]: 8 : if (send_info->large_message != NULL) {
305 : 8 : libspdm_zero_mem(send_info->large_message, send_info->large_message_capacity);
306 : : }
307 : 8 : send_info->large_message = NULL;
308 : 8 : send_info->large_message_size = 0;
309 : 8 : send_info->large_message_capacity = 0;
310 : :
311 : 8 : *response_size = response_header_size + chunk_response_size;
312 : : } else {
313 : 10 : *response_size = response_header_size;
314 : : }
315 : :
316 : 46 : return status;
317 : : }
318 : :
319 : : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
|