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