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_ENCAP_CAP
10 : :
11 : 92 : void libspdm_register_encap_flow_handler(void *spdm_context,
12 : : libspdm_encap_flow_handler_func encap_flow_handler)
13 : : {
14 : : libspdm_context_t *context;
15 : :
16 : 92 : context = spdm_context;
17 : :
18 : 92 : context->encap_flow_handler_callback = (void *)encap_flow_handler;
19 : 92 : }
20 : :
21 : 8 : libspdm_return_t libspdm_get_encap_payload_size(void *spdm_context,
22 : : const uint32_t *session_id,
23 : : size_t *payload_size)
24 : : {
25 : : libspdm_encap_context_t *encap_context;
26 : :
27 : 8 : encap_context = libspdm_get_encap_context(spdm_context, session_id);
28 [ + + ]: 8 : if (encap_context == NULL) {
29 : : /* session_id does not refer to an existing session. */
30 : 1 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
31 : : }
32 : :
33 : 7 : *payload_size = encap_context->payload_buffer_size;
34 : :
35 : 7 : return LIBSPDM_STATUS_SUCCESS;
36 : : }
37 : :
38 : : /**
39 : : * Process the encapsulated response received from the Requester. Dispatches to the correct
40 : : * response processing function based on the last request code.
41 : : **/
42 : 39 : static libspdm_return_t libspdm_dispatch_process_encap_response(
43 : : libspdm_context_t *spdm_context, uint8_t last_request_code,
44 : : size_t encap_response_size, const void *encap_response, bool *need_continue)
45 : : {
46 [ + + + + : 39 : switch (last_request_code) {
+ + - ]
47 : : #if LIBSPDM_SEND_GET_CERTIFICATE_SUPPORT
48 : 17 : case SPDM_GET_DIGESTS:
49 : 17 : return libspdm_process_encap_response_digest(
50 : : spdm_context, encap_response_size, encap_response, need_continue);
51 : 11 : case SPDM_GET_CERTIFICATE:
52 : 11 : return libspdm_process_encap_response_certificate(
53 : : spdm_context, encap_response_size, encap_response, need_continue);
54 : : #endif /* LIBSPDM_SEND_GET_CERTIFICATE_SUPPORT */
55 : : #if (LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP) && (LIBSPDM_SEND_CHALLENGE_SUPPORT)
56 : 2 : case SPDM_CHALLENGE:
57 : 2 : return libspdm_process_encap_response_challenge_auth(
58 : : spdm_context, encap_response_size, encap_response, need_continue);
59 : : #endif
60 : 5 : case SPDM_KEY_UPDATE:
61 : 5 : return libspdm_process_encap_response_key_update(
62 : : spdm_context, encap_response_size, encap_response, need_continue);
63 : : #if LIBSPDM_SEND_GET_ENDPOINT_INFO_SUPPORT
64 : 2 : case SPDM_GET_ENDPOINT_INFO:
65 : 2 : return libspdm_process_encap_response_endpoint_info(
66 : : spdm_context, encap_response_size, encap_response, need_continue);
67 : : #endif /* LIBSPDM_SEND_GET_ENDPOINT_INFO_SUPPORT */
68 : : #if LIBSPDM_ENABLE_CAPABILITY_EVENT_CAP
69 : 2 : case SPDM_SEND_EVENT:
70 : 2 : return libspdm_process_encap_response_event_ack(
71 : : spdm_context, encap_response_size, encap_response, need_continue);
72 : : #endif /* LIBSPDM_ENABLE_CAPABILITY_EVENT_CAP */
73 : 0 : default:
74 : 0 : LIBSPDM_ASSERT(false);
75 : 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
76 : : }
77 : : }
78 : :
79 : : /**
80 : : * Determine whether processing the encapsulated response failed because of the Responder's own
81 : : * state or resources rather than because of anything the Requester sent.
82 : : *
83 : : * @param status The status that libspdm_dispatch_process_encap_response returned.
84 : : *
85 : : * @retval true The failure is local, so the Requester is not at fault.
86 : : * @retval false The Requester's encapsulated response could not be processed.
87 : : **/
88 : 7 : static bool libspdm_is_local_process_failure(libspdm_return_t status)
89 : : {
90 [ + - ]: 5 : return (status == LIBSPDM_STATUS_BUFFER_TOO_SMALL) ||
91 [ + - ]: 5 : (status == LIBSPDM_STATUS_BUFFER_FULL) ||
92 [ + - ]: 5 : (status == LIBSPDM_STATUS_INVALID_STATE_LOCAL) ||
93 [ + + - + ]: 12 : (status == LIBSPDM_STATUS_UNSUPPORTED_CAP) ||
94 : : (status == LIBSPDM_STATUS_CRYPTO_ERROR);
95 : : }
96 : :
97 : : /**
98 : : * When a multi-message operation (GET_CERTIFICATE or KEY_UPDATE) requires a follow-up request,
99 : : * build the next request without calling the Integrator's handler.
100 : : **/
101 : 6 : static libspdm_return_t libspdm_dispatch_encap_need_continue(
102 : : libspdm_context_t *spdm_context, const uint32_t *session_id, uint8_t last_request_code,
103 : : size_t *encap_request_size, void *encap_request)
104 : : {
105 : : libspdm_encap_context_t *encap_context;
106 : :
107 : 6 : encap_context = libspdm_get_encap_context(spdm_context, session_id);
108 : :
109 : : /* session_id comes from the request that libspdm is responding to, so it is necessarily
110 : : * valid. */
111 [ - + ]: 6 : LIBSPDM_ASSERT(encap_context != NULL);
112 : :
113 [ + + - ]: 6 : switch (last_request_code) {
114 : : #if LIBSPDM_SEND_GET_CERTIFICATE_SUPPORT
115 : 4 : case SPDM_GET_CERTIFICATE:
116 : 4 : return libspdm_get_encap_request_get_certificate_continue(
117 : 4 : spdm_context, session_id, encap_context->req_slot_id,
118 : : encap_request_size, encap_request);
119 : : #endif /* LIBSPDM_SEND_GET_CERTIFICATE_SUPPORT */
120 : 2 : case SPDM_KEY_UPDATE:
121 : 2 : return libspdm_get_encap_request_key_update(
122 : : spdm_context, *session_id, SPDM_KEY_UPDATE_OPERATIONS_VERIFY_NEW_KEY,
123 : : encap_request_size, encap_request);
124 : 0 : default:
125 : 0 : LIBSPDM_ASSERT(false);
126 : 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
127 : : }
128 : : }
129 : :
130 : : /**
131 : : * Determine whether an encapsulated request is legal for the flow it would be sent in.
132 : : *
133 : : * Encapsulated requests are limited by message type and by whether the flow belongs to a session.
134 : : *
135 : : * @param flow_type The encapsulated flow that is in progress.
136 : : * @param in_session True if the flow belongs to a session. Note that this is not the same as
137 : : * the messages being sent within that session, as session-based mutual
138 : : * authentication is conducted outside of a session when both endpoints have
139 : : * set HANDSHAKE_IN_THE_CLEAR_CAP.
140 : : * @param request_code The request code that the Integrator's handler produced.
141 : : *
142 : : * @retval true The request is legal for this flow.
143 : : * @retval false The request is not legal and must not be sent.
144 : : **/
145 : 35 : static bool libspdm_is_encap_request_legal(libspdm_encap_flow_type_t flow_type,
146 : : bool in_session,
147 : : uint8_t request_code)
148 : : {
149 [ + + + - ]: 35 : switch (flow_type) {
150 : 10 : case LIBSPDM_ENCAP_FLOW_BASIC_MUT_AUTH:
151 : : /* All messages are sent outside of a session. */
152 [ + + ]: 10 : if (in_session) {
153 : 1 : return false;
154 : : }
155 [ + + + + : 9 : return (request_code == SPDM_CHALLENGE) || (request_code == SPDM_GET_DIGESTS) ||
+ + ]
156 : : (request_code == SPDM_GET_CERTIFICATE);
157 : 6 : case LIBSPDM_ENCAP_FLOW_SESS_MUT_AUTH:
158 : : /* All messages belong to the same session, and are only to retrieve the Requester's
159 : : * certificate chain. */
160 [ + + ]: 6 : if (!in_session) {
161 : 1 : return false;
162 : : }
163 [ + + + + ]: 5 : return (request_code == SPDM_GET_DIGESTS) || (request_code == SPDM_GET_CERTIFICATE);
164 : 19 : case LIBSPDM_ENCAP_FLOW_GENERAL:
165 [ + + + ]: 19 : switch (request_code) {
166 : 10 : case SPDM_GET_DIGESTS:
167 : : case SPDM_GET_CERTIFICATE:
168 : : case SPDM_GET_ENDPOINT_INFO:
169 : 10 : return true;
170 : 7 : case SPDM_SEND_EVENT:
171 : : case SPDM_KEY_UPDATE:
172 : : /* Only legal within a session. */
173 : 7 : return in_session;
174 : 2 : default:
175 : 2 : return false;
176 : : }
177 : 0 : default:
178 : 0 : return false;
179 : : }
180 : : }
181 : :
182 : :
183 : 14 : void libspdm_reset_all_encap_state(libspdm_context_t *spdm_context)
184 : : {
185 : : size_t index;
186 : :
187 : 14 : spdm_context->encap_context.flow_type = LIBSPDM_ENCAP_FLOW_NONE;
188 : : #if LIBSPDM_RESPOND_IF_READY_SUPPORT
189 : 14 : spdm_context->encap_context.response_not_ready = false;
190 : : #endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */
191 : :
192 [ + + ]: 70 : for (index = 0; index < LIBSPDM_MAX_SESSION_COUNT; index++) {
193 : 56 : spdm_context->session_info[index].encap_context.flow_type = LIBSPDM_ENCAP_FLOW_NONE;
194 : : #if LIBSPDM_RESPOND_IF_READY_SUPPORT
195 : 56 : spdm_context->session_info[index].encap_context.response_not_ready = false;
196 : : #endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */
197 : : }
198 : 14 : }
199 : :
200 : : #define MAX_ERROR_MSG_SIZE 36
201 : :
202 : : /**
203 : : * Propagate an ERROR response that the Integrator's handler produced in place of an encapsulated
204 : : * request. The ERROR replaces the ENCAPSULATED_REQUEST or ENCAPSULATED_RESPONSE_ACK that would
205 : : * otherwise have been sent.
206 : : *
207 : : * @param response The response buffer. It overlaps encap_error.
208 : : * @param response_size On input the size of the response buffer, on output the size of the
209 : : * ERROR response. Unchanged if this function returns an error.
210 : : * @param encap_error The ERROR message that the handler produced.
211 : : * @param encap_error_size Size, in bytes, of encap_error.
212 : : *
213 : : * @retval LIBSPDM_STATUS_SUCCESS The ERROR response was placed in the response buffer.
214 : : * @retval LIBSPDM_STATUS_INVALID_MSG_SIZE encap_error_size is not a plausible ERROR size.
215 : : **/
216 : 3 : static libspdm_return_t libspdm_propagate_encap_error(void *response, size_t *response_size,
217 : : const void *encap_error,
218 : : size_t encap_error_size)
219 : : {
220 : : uint8_t error_response_buffer[MAX_ERROR_MSG_SIZE];
221 : :
222 : : /* The handler supplies encap_error_size, so it is bounded here before it reaches
223 : : * libspdm_copy_mem, which does not clamp. */
224 [ + + + + ]: 3 : if ((encap_error_size < sizeof(spdm_error_response_t)) ||
225 : 1 : (encap_error_size > sizeof(error_response_buffer)) ||
226 [ - + ]: 1 : (encap_error_size > *response_size)) {
227 : 2 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR,
228 : : "encapsulated ERROR size 0x%zx is not valid\n", encap_error_size));
229 : 2 : return LIBSPDM_STATUS_INVALID_MSG_SIZE;
230 : : }
231 : :
232 : : /* Copy through a temporary buffer since the source and destination overlap and memmove is
233 : : * not available. */
234 : 1 : libspdm_copy_mem(error_response_buffer, sizeof(error_response_buffer),
235 : : encap_error, encap_error_size);
236 : 1 : libspdm_copy_mem(response, *response_size, error_response_buffer, encap_error_size);
237 : 1 : *response_size = encap_error_size;
238 : :
239 : 1 : return LIBSPDM_STATUS_SUCCESS;
240 : : }
241 : :
242 : : /* Tracks the session's encapsulated DIGESTS window, so it does nothing outside a session.
243 : : * libspdm_reset_message_buffer_via_request_code() is not used here, as an encapsulated
244 : : * request the Responder sends must not reset its own transcripts. */
245 : 25 : static void libspdm_reset_transcript_via_encap_request(libspdm_context_t *spdm_context,
246 : : const uint32_t *session_id,
247 : : const void *encap_request)
248 : : {
249 [ + + ]: 25 : if (session_id == NULL) {
250 : 14 : return;
251 : : }
252 : :
253 : 11 : libspdm_reset_message_buffer_via_encap_request_code(
254 : : spdm_context,
255 : : libspdm_get_session_info_via_session_id(spdm_context, *session_id),
256 : 11 : ((const spdm_message_header_t *)encap_request)->request_response_code);
257 : : }
258 : :
259 : 50 : libspdm_return_t libspdm_get_response_encapsulated_request(
260 : : libspdm_context_t *spdm_context, size_t request_size, const void *request,
261 : : size_t *response_size, void *response)
262 : : {
263 : : spdm_encapsulated_request_response_t *spdm_response;
264 : : void *encap_request;
265 : : size_t encap_request_size;
266 : : libspdm_return_t status;
267 : : const spdm_get_encapsulated_request_request_t *spdm_request;
268 : : spdm_error_response_t *error_response;
269 : : bool terminate_flow;
270 : : bool recovering;
271 : : const uint32_t *session_id;
272 : : libspdm_encap_context_t *encap_context;
273 : :
274 : 50 : spdm_request = request;
275 : 50 : encap_context = libspdm_get_encap_context_via_last_request(spdm_context);
276 : 50 : recovering = false;
277 : :
278 : : /* LIBSPDM_ASSERT(spdm_request->header.request_response_code == SPDM_GET_ENCAPSULATED_REQUEST);
279 : : */
280 : :
281 [ + + ]: 50 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_11) {
282 : 3 : return libspdm_generate_error_response(spdm_context,
283 : : SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
284 : : SPDM_GET_ENCAPSULATED_REQUEST,
285 : : response_size, response);
286 : : }
287 : :
288 [ - + ]: 47 : if (!libspdm_is_encap_supported(spdm_context)) {
289 : 0 : return libspdm_generate_error_response(
290 : : spdm_context, SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
291 : : SPDM_GET_ENCAPSULATED_REQUEST, response_size, response);
292 : : }
293 : :
294 [ + + ]: 47 : if (spdm_context->encap_flow_handler_callback == NULL) {
295 : : /* If ENCAP_CAP is set then the handler must also be registered. */
296 : 1 : return libspdm_generate_error_response(
297 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
298 : : }
299 : :
300 [ - + ]: 46 : if (request_size < sizeof(spdm_get_encapsulated_request_request_t)) {
301 : 0 : return libspdm_generate_error_response(spdm_context,
302 : : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
303 : : response_size, response);
304 : : }
305 [ - + ]: 46 : if (spdm_request->header.spdm_version != libspdm_get_connection_version(spdm_context)) {
306 : 0 : return libspdm_generate_error_response(spdm_context,
307 : : SPDM_ERROR_CODE_VERSION_MISMATCH, 0,
308 : : response_size, response);
309 : : }
310 : :
311 [ + + ]: 46 : if (spdm_context->response_state != LIBSPDM_RESPONSE_STATE_NORMAL) {
312 : 1 : return libspdm_responder_handle_response_state(
313 : : spdm_context,
314 : 1 : spdm_request->header.request_response_code,
315 : : response_size, response);
316 : : }
317 : :
318 [ + + ]: 45 : if (encap_context->flow_type == LIBSPDM_ENCAP_FLOW_NONE) {
319 : : #if LIBSPDM_RESPOND_IF_READY_SUPPORT
320 [ + + ]: 13 : if (encap_context->response_not_ready) {
321 : : /* Resume the flow that an encapsulated ERROR(ResponseNotReady) terminated. The
322 : : * outstanding request is left in last_encap_request_header so that the response, when
323 : : * it eventually arrives, is dispatched to the request that asked for it. Entering the
324 : : * flow consumes the ResponseNotReady. */
325 : 4 : encap_context->flow_type = encap_context->response_not_ready_flow_type;
326 : 4 : encap_context->response_not_ready = false;
327 : 4 : recovering = true;
328 : : }
329 : : #endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */
330 : :
331 [ + + ]: 13 : if (!recovering) {
332 : : /* General encap flow; initialize the encap context. The mutual
333 : : * authentication flows have already set flow_type in CHALLENGE_AUTH or
334 : : * KEY_EXCHANGE_RSP. */
335 : 9 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_GENERAL;
336 : 9 : encap_context->request_id = 0;
337 : 9 : encap_context->last_encap_request_size = 0;
338 : 9 : libspdm_zero_mem(&encap_context->last_encap_request_header,
339 : : sizeof(encap_context->last_encap_request_header));
340 : : }
341 : : }
342 : :
343 : 45 : libspdm_reset_message_buffer_via_request_code(spdm_context, NULL,
344 : 45 : spdm_request->header.request_response_code);
345 : :
346 [ - + ]: 45 : LIBSPDM_ASSERT(*response_size > sizeof(spdm_encapsulated_request_response_t));
347 : 45 : libspdm_zero_mem(response, *response_size);
348 : :
349 : 45 : spdm_response = response;
350 : 45 : spdm_response->header.spdm_version = spdm_request->header.spdm_version;
351 : 45 : spdm_response->header.request_response_code = SPDM_ENCAPSULATED_REQUEST;
352 : 45 : spdm_response->header.param1 = encap_context->request_id;
353 : 45 : spdm_response->header.param2 = 0;
354 : :
355 : 45 : encap_request_size = *response_size - sizeof(spdm_encapsulated_request_response_t);
356 : 45 : encap_request = spdm_response + 1;
357 : 45 : terminate_flow = false;
358 : :
359 : : /* This is the session the flow belongs to, which is not necessarily the session the message
360 : : * arrived on. See libspdm_get_encap_session_id_via_last_request. */
361 : 45 : session_id = libspdm_get_encap_session_id_via_last_request(spdm_context);
362 : :
363 : : #if LIBSPDM_RESPOND_IF_READY_SUPPORT
364 [ + + ]: 45 : if (recovering) {
365 : : /* The Requester asked for more time rather than declining the request, so libspdm reissues
366 : : * the outstanding request itself. The Integrator's handler is not consulted, as the flow
367 : : * continues with the request it already has. */
368 : 4 : status = libspdm_get_encap_request_respond_if_ready(
369 : : spdm_context, session_id, &encap_request_size, encap_request);
370 : :
371 [ + + ]: 4 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
372 : : /* The failure is local to the Responder; nothing was wrong with the Requester's
373 : : * message. */
374 : 1 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
375 : 1 : return libspdm_generate_error_response(
376 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
377 : : }
378 : :
379 : : /* The Request ID is unchanged, since this reissues the request that already carries it. */
380 : 3 : *response_size = sizeof(spdm_encapsulated_request_response_t) + encap_request_size;
381 : :
382 : 3 : return LIBSPDM_STATUS_SUCCESS;
383 : : }
384 : : #endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */
385 : :
386 [ - + ]: 41 : LIBSPDM_ASSERT(encap_context->flow_type != LIBSPDM_ENCAP_FLOW_NONE);
387 : :
388 : 41 : status = ((libspdm_encap_flow_handler_func)spdm_context->encap_flow_handler_callback)(
389 : : spdm_context, session_id, encap_context->flow_type, 0, 0,
390 : : &terminate_flow, &encap_request_size, encap_request);
391 : :
392 [ + + ]: 41 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
393 : : /* The failure is local to the Responder; nothing was wrong with the Requester's
394 : : * message. */
395 : 3 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
396 : 3 : return libspdm_generate_error_response(
397 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0,
398 : : response_size, response);
399 : : }
400 : :
401 : 38 : error_response = (spdm_error_response_t *)encap_request;
402 : :
403 [ + + ]: 38 : if (error_response->header.request_response_code == SPDM_ERROR) {
404 : : /* Handler generated an error response; propagate it directly. */
405 : 1 : status = libspdm_propagate_encap_error(response, response_size,
406 : : encap_request, encap_request_size);
407 [ + - ]: 1 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
408 : 1 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
409 : 1 : return libspdm_generate_error_response(
410 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
411 : : }
412 : 0 : return LIBSPDM_STATUS_SUCCESS;
413 [ + + ]: 37 : } else if (terminate_flow) {
414 [ + + ]: 6 : if (encap_context->flow_type != LIBSPDM_ENCAP_FLOW_GENERAL) {
415 : : /* The Responder asked for this flow in CHALLENGE_AUTH or KEY_EXCHANGE_RSP, so it
416 : : * cannot then report that it has no request pending. */
417 : 2 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR,
418 : : "encapsulated flow type %d cannot terminate without a request\n",
419 : : encap_context->flow_type));
420 : 2 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
421 : 2 : return libspdm_generate_error_response(
422 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
423 : : }
424 : :
425 : : /* The Requester polled and the Responder has nothing to send, so no flow is in progress. */
426 : 4 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
427 : :
428 [ + + ]: 4 : if (libspdm_get_connection_version(spdm_context) >= SPDM_MESSAGE_VERSION_13) {
429 : 1 : return libspdm_generate_error_response(
430 : : spdm_context,
431 : : SPDM_ERROR_CODE_NO_PENDING_REQUESTS, 0,
432 : : response_size, response);
433 : : } else {
434 : 3 : return libspdm_generate_error_response(
435 : : spdm_context,
436 : : SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
437 : : response_size, response);
438 : : }
439 : : } else {
440 [ + + ]: 31 : if ((encap_request_size != 0) &&
441 [ + + ]: 30 : !libspdm_is_encap_request_legal(encap_context->flow_type,
442 : : session_id != NULL,
443 : 30 : error_response->header.request_response_code)) {
444 : : /* The Integrator produced a request that is not permitted in this flow. */
445 : 9 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR,
446 : : "encapsulated request 0x%x is not legal in flow type %d\n",
447 : : error_response->header.request_response_code,
448 : : encap_context->flow_type));
449 : 9 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
450 : 9 : return libspdm_generate_error_response(
451 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
452 : : }
453 [ + + ]: 22 : if (encap_request_size != 0) {
454 : 21 : libspdm_reset_transcript_via_encap_request(spdm_context, session_id, encap_request);
455 : : }
456 : 22 : *response_size = sizeof(spdm_encapsulated_request_response_t) + encap_request_size;
457 : : }
458 : :
459 [ + + ]: 22 : if (encap_request_size == 0) {
460 : 1 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
461 : : }
462 : :
463 : 22 : return LIBSPDM_STATUS_SUCCESS;
464 : : }
465 : :
466 : 55 : libspdm_return_t libspdm_get_response_encapsulated_response_ack(
467 : : libspdm_context_t *spdm_context, size_t request_size, const void *request,
468 : : size_t *response_size, void *response)
469 : : {
470 : : const spdm_deliver_encapsulated_response_request_t *spdm_request;
471 : : size_t spdm_request_size;
472 : : spdm_encapsulated_response_ack_response_t *spdm_response;
473 : : const void *encap_response;
474 : : size_t encap_response_size;
475 : : void *encap_request;
476 : : size_t encap_request_size;
477 : : libspdm_return_t status;
478 : : size_t ack_header_size;
479 : : bool terminate_flow;
480 : : bool need_continue;
481 : : bool response_not_ready;
482 : : bool encap_error_received;
483 : : uint8_t last_request_code;
484 : : uint8_t error_code;
485 : : const uint32_t *session_id;
486 : : libspdm_encap_context_t *encap_context;
487 : :
488 : 55 : spdm_request = request;
489 : 55 : encap_context = libspdm_get_encap_context_via_last_request(spdm_context);
490 : :
491 : : /* LIBSPDM_ASSERT(spdm_request->header.request_response_code ==
492 : : * SPDM_DELIVER_ENCAPSULATED_RESPONSE); */
493 : :
494 [ + + ]: 55 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_11) {
495 : 2 : return libspdm_generate_error_response(spdm_context,
496 : : SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
497 : : SPDM_DELIVER_ENCAPSULATED_RESPONSE,
498 : : response_size, response);
499 : : }
500 : :
501 [ - + ]: 53 : if (!libspdm_is_encap_supported(spdm_context)) {
502 : 0 : return libspdm_generate_error_response(
503 : : spdm_context, SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
504 : : SPDM_DELIVER_ENCAPSULATED_RESPONSE, response_size, response);
505 : : }
506 : :
507 [ + + ]: 53 : if (spdm_context->encap_flow_handler_callback == NULL) {
508 : : /* If ENCAP_CAP is set then the handler must also be registered. */
509 : 1 : return libspdm_generate_error_response(
510 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
511 : : }
512 : :
513 [ + + ]: 52 : if (spdm_context->response_state != LIBSPDM_RESPONSE_STATE_NORMAL) {
514 : 1 : return libspdm_responder_handle_response_state(
515 : : spdm_context,
516 : 1 : spdm_request->header.request_response_code,
517 : : response_size, response);
518 : : }
519 : :
520 [ + + ]: 51 : if (encap_context->flow_type == LIBSPDM_ENCAP_FLOW_NONE) {
521 : : /* No encapsulated flow is in progress on this channel. Note that the first
522 : : * DELIVER_ENCAPSULATED_RESPONSE after KEY_EXCHANGE_RSP with bit 2 set is legal, as
523 : : * flow_type was set to LIBSPDM_ENCAP_FLOW_SESS_MUT_AUTH at that time. */
524 : 1 : return libspdm_generate_error_response(
525 : : spdm_context,
526 : : SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
527 : : response_size, response);
528 : : }
529 : :
530 [ + + ]: 50 : if (request_size <= sizeof(spdm_deliver_encapsulated_response_request_t)) {
531 : 6 : return libspdm_generate_error_response(spdm_context,
532 : : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
533 : : response_size, response);
534 : : }
535 [ - + ]: 44 : if (spdm_request->header.spdm_version != libspdm_get_connection_version(spdm_context)) {
536 : 0 : return libspdm_generate_error_response(spdm_context,
537 : : SPDM_ERROR_CODE_VERSION_MISMATCH, 0,
538 : : response_size, response);
539 : : }
540 : :
541 : 44 : spdm_request_size = request_size;
542 : :
543 [ + + ]: 44 : if (spdm_request->header.param1 != encap_context->request_id) {
544 : 2 : return libspdm_generate_error_response(spdm_context,
545 : : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
546 : : response_size, response);
547 : : }
548 : :
549 : 42 : encap_response = spdm_request + 1;
550 : 42 : encap_response_size = spdm_request_size - sizeof(spdm_deliver_encapsulated_response_request_t);
551 : :
552 [ + + ]: 42 : if (spdm_request->header.spdm_version >= SPDM_MESSAGE_VERSION_12) {
553 : 22 : ack_header_size = sizeof(spdm_encapsulated_response_ack_response_t);
554 : : } else {
555 : 20 : ack_header_size = sizeof(spdm_message_header_t);
556 : : }
557 : :
558 [ - + ]: 42 : LIBSPDM_ASSERT(*response_size > ack_header_size);
559 : 42 : libspdm_zero_mem(response, *response_size);
560 : :
561 : 42 : spdm_response = response;
562 : 42 : spdm_response->header.spdm_version = spdm_request->header.spdm_version;
563 : 42 : spdm_response->header.request_response_code = SPDM_ENCAPSULATED_RESPONSE_ACK;
564 : 42 : spdm_response->header.param1 = 0;
565 : 42 : spdm_response->header.param2 = SPDM_ENCAPSULATED_RESPONSE_ACK_RESPONSE_PAYLOAD_TYPE_PRESENT;
566 : :
567 : 42 : encap_request_size = *response_size - ack_header_size;
568 : 42 : encap_request = (uint8_t *)spdm_response + ack_header_size;
569 [ - + ]: 42 : if (encap_response_size < sizeof(spdm_message_header_t)) {
570 : 0 : return libspdm_generate_error_response(spdm_context,
571 : : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
572 : : response_size, response);
573 : : }
574 : :
575 : 42 : libspdm_reset_message_buffer_via_request_code(spdm_context, NULL,
576 : 42 : spdm_request->header.request_response_code);
577 : :
578 : 42 : terminate_flow = false;
579 : 42 : need_continue = false;
580 : 42 : response_not_ready = false;
581 : 42 : encap_error_received = false;
582 : 42 : error_code = 0;
583 : 42 : last_request_code = encap_context->last_encap_request_header.request_response_code;
584 : : /* This is the session the flow belongs to, which is not necessarily the session the message
585 : : * arrived on. See libspdm_get_encap_session_id_via_last_request. */
586 : 42 : session_id = libspdm_get_encap_session_id_via_last_request(spdm_context);
587 : :
588 [ + + ]: 42 : if (last_request_code != 0) {
589 : : /* Process the encapsulated response from the Requester before calling the handler. */
590 : 39 : status = libspdm_dispatch_process_encap_response(
591 : : spdm_context, last_request_code,
592 : : encap_response_size, encap_response, &need_continue);
593 : :
594 [ + + + + ]: 46 : if ((session_id != NULL) &&
595 : 7 : (libspdm_get_session_info_via_session_id(spdm_context, *session_id) == NULL)) {
596 : : /* Processing the encapsulated response ended the session that the flow belongs to,
597 : : * which also discarded encap_context. There is no flow left to continue and no state
598 : : * to give the Integrator. */
599 : 1 : return libspdm_generate_error_response(
600 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
601 : : }
602 : :
603 [ + + ]: 38 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
604 : 17 : const spdm_error_response_t *encap_error = encap_response;
605 : : #if LIBSPDM_RESPOND_IF_READY_SUPPORT
606 : : const spdm_error_data_response_not_ready_t *not_ready_data;
607 : : #endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */
608 : :
609 [ + - ]: 17 : if ((encap_response_size < sizeof(spdm_error_response_t)) ||
610 [ + + ]: 17 : (encap_error->header.request_response_code != SPDM_ERROR)) {
611 : : /* The encapsulated response was not an ERROR, so processing it genuinely failed
612 : : * and there is no ErrorCode to report to the handler. A failure of the Responder's
613 : : * own state or resources, such as a payload larger than the buffer the Integrator
614 : : * supplied, is not reported as though the Requester's response were at fault. */
615 : : uint8_t process_error_code;
616 : :
617 [ + + ]: 7 : if (libspdm_is_local_process_failure(status)) {
618 : 2 : process_error_code = SPDM_ERROR_CODE_UNSPECIFIED;
619 : : } else {
620 : 5 : process_error_code = SPDM_ERROR_CODE_INVALID_RESPONSE_CODE;
621 : : }
622 : :
623 : 7 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
624 : 7 : return libspdm_generate_error_response(
625 : : spdm_context, process_error_code, 0, response_size, response);
626 : : }
627 : :
628 [ + + ]: 10 : if (encap_error->header.param1 == 0) {
629 : : /* ErrorCode 0x00 is reserved, so this ERROR is malformed. It also cannot be
630 : : * reported to the handler, as an error_code of 0 is how the absence of an
631 : : * encapsulated ERROR is conveyed. */
632 : 2 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
633 : 2 : return libspdm_generate_error_response(
634 : : spdm_context, SPDM_ERROR_CODE_INVALID_RESPONSE_CODE, 0,
635 : : response_size, response);
636 : : }
637 : :
638 : : /* The Requester delivered an encapsulated ERROR. Report its code to the handler so
639 : : * that the Integrator learns why the flow ended. */
640 : 8 : encap_error_received = true;
641 : 8 : error_code = encap_error->header.param1;
642 : :
643 [ + + ]: 8 : if (status == LIBSPDM_STATUS_NOT_READY_PEER) {
644 : 5 : response_not_ready = true;
645 : :
646 : : #if LIBSPDM_RESPOND_IF_READY_SUPPORT
647 : : /* The encapsulated request is still outstanding, so retain the flow it belongs to
648 : : * and the fields needed to reissue it with RESPOND_IF_READY once the Requester
649 : : * returns with GET_ENCAPSULATED_REQUEST. */
650 [ + + ]: 5 : if (encap_response_size <
651 : : (sizeof(spdm_error_response_t) +
652 : : sizeof(spdm_error_data_response_not_ready_t))) {
653 : 1 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
654 : 1 : return libspdm_generate_error_response(
655 : : spdm_context, SPDM_ERROR_CODE_INVALID_RESPONSE_CODE, 0,
656 : : response_size, response);
657 : : }
658 : 4 : not_ready_data = (const void *)((const uint8_t *)encap_response +
659 : : sizeof(spdm_error_response_t));
660 : :
661 : : /* The Requester echoes the request it is deferring, and the Responder knows which
662 : : * request that is. A mismatch, or a retry interval the Responder cannot honour,
663 : : * means the extended data cannot be used to reissue the request. */
664 [ + + ]: 4 : if ((not_ready_data->request_code != last_request_code) ||
665 [ + + ]: 3 : (not_ready_data->rd_tm <= 1) ||
666 [ + + ]: 2 : (not_ready_data->rd_exponent > LIBSPDM_MAX_RDT_EXPONENT)) {
667 : 3 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
668 : 3 : return libspdm_generate_error_response(
669 : : spdm_context, SPDM_ERROR_CODE_INVALID_RESPONSE_CODE, 0,
670 : : response_size, response);
671 : : }
672 : :
673 : 1 : libspdm_copy_mem(&encap_context->response_not_ready_data,
674 : : sizeof(encap_context->response_not_ready_data),
675 : : not_ready_data,
676 : : sizeof(spdm_error_data_response_not_ready_t));
677 : 1 : encap_context->response_not_ready_flow_type = encap_context->flow_type;
678 : 1 : encap_context->response_not_ready = true;
679 : : #endif /* LIBSPDM_RESPOND_IF_READY_SUPPORT */
680 : : }
681 : : }
682 : :
683 [ + + ]: 25 : if (need_continue) {
684 : : /* Build the follow-up request (next GET_CERTIFICATE chunk or VerifyNewKey)
685 : : * without invoking the handler. */
686 : 6 : status = libspdm_dispatch_encap_need_continue(
687 : : spdm_context, session_id, last_request_code,
688 : : &encap_request_size, encap_request);
689 [ + + ]: 6 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
690 : : /* The failure is local to the Responder; nothing was wrong with the Requester's
691 : : * encapsulated response. */
692 : 1 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
693 : 1 : return libspdm_generate_error_response(
694 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0,
695 : : response_size, response);
696 : : }
697 : 5 : goto set_ack_fields;
698 : : }
699 : :
700 : : #if (LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP) && (LIBSPDM_SEND_CHALLENGE_SUPPORT)
701 [ + + + + ]: 19 : if (!encap_error_received && (last_request_code == SPDM_CHALLENGE)) {
702 : : /* Basic mutual authentication concludes with the encapsulated CHALLENGE_AUTH
703 : : * response. The Responder must then terminate the encapsulated flow by clearing
704 : : * ENCAPSULATED_RESPONSE_ACK.Param2, so the Integrator's handler is not consulted
705 : : * and cannot continue the flow. */
706 : 1 : terminate_flow = true;
707 : 1 : goto set_ack_fields;
708 : : }
709 : : #endif /* (LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP) && (LIBSPDM_SEND_CHALLENGE_SUPPORT) */
710 : : }
711 : :
712 [ - + ]: 21 : LIBSPDM_ASSERT(encap_context->flow_type != LIBSPDM_ENCAP_FLOW_NONE);
713 : :
714 : : /* All response data processed; ask the Integrator what to do next. */
715 : 21 : status = ((libspdm_encap_flow_handler_func)spdm_context->encap_flow_handler_callback)(
716 : : spdm_context, session_id, encap_context->flow_type,
717 : : last_request_code, error_code, &terminate_flow, &encap_request_size, encap_request);
718 : :
719 [ + + ]: 21 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
720 : : /* The failure is local to the Responder; nothing was wrong with the Requester's
721 : : * encapsulated response. */
722 : 1 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
723 : 1 : return libspdm_generate_error_response(
724 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
725 : : }
726 : :
727 [ + + ]: 20 : if (encap_error_received) {
728 : : /* An encapsulated ERROR ends the flow, so the Integrator acknowledges it rather than
729 : : * supplying another request. */
730 [ - + ]: 4 : LIBSPDM_ASSERT(terminate_flow);
731 : 4 : terminate_flow = true;
732 : 4 : encap_request_size = 0;
733 : 4 : goto set_ack_fields;
734 : : }
735 : :
736 [ + - ]: 16 : if ((encap_request_size != 0) &&
737 [ + + ]: 16 : (((const spdm_message_header_t *)encap_request)->request_response_code == SPDM_ERROR)) {
738 : : /* Handler generated an error response instead of an encapsulated request; propagate it
739 : : * directly. This is checked before the legality test below, as SPDM_ERROR is not a
740 : : * request code and would otherwise be rejected there. */
741 : 2 : status = libspdm_propagate_encap_error(response, response_size,
742 : : encap_request, encap_request_size);
743 [ + + ]: 2 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
744 : 1 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
745 : 1 : return libspdm_generate_error_response(
746 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
747 : : }
748 : 1 : return LIBSPDM_STATUS_SUCCESS;
749 : : }
750 : :
751 [ + + + - ]: 14 : if (!terminate_flow && (encap_request_size != 0) &&
752 [ + + ]: 5 : !libspdm_is_encap_request_legal(
753 : : encap_context->flow_type, session_id != NULL,
754 : 5 : ((const spdm_message_header_t *)encap_request)->request_response_code)) {
755 : : /* The Integrator produced a request that is not permitted in this flow. */
756 : 1 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_ERROR,
757 : : "encapsulated request 0x%x is not legal in flow type %d\n",
758 : : ((const spdm_message_header_t *)encap_request)->request_response_code,
759 : : encap_context->flow_type));
760 : 1 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
761 : 1 : return libspdm_generate_error_response(
762 : : spdm_context, SPDM_ERROR_CODE_UNSPECIFIED, 0, response_size, response);
763 : : }
764 : :
765 [ + + - + ]: 13 : if (!terminate_flow && (encap_request_size != 0)) {
766 : 4 : libspdm_reset_transcript_via_encap_request(spdm_context, session_id, encap_request);
767 : : }
768 : :
769 : 9 : set_ack_fields:
770 : 23 : *response_size = ack_header_size + encap_request_size;
771 : :
772 [ + + ]: 23 : if (spdm_request->header.spdm_version >= SPDM_MESSAGE_VERSION_12) {
773 : 10 : spdm_response->ack_request_id = spdm_request->header.param1;
774 : : }
775 : :
776 [ + + + - ]: 23 : if (!terminate_flow && (encap_request_size != 0)) {
777 [ + + ]: 9 : if (encap_context->request_id == UINT8_MAX) {
778 : 1 : encap_context->request_id = 1;
779 : : } else {
780 : 8 : encap_context->request_id++;
781 : : }
782 : 9 : spdm_response->header.param1 = encap_context->request_id;
783 : : } else {
784 : : /* No further encapsulated request, so this is the final message of the flow. */
785 : 14 : spdm_response->header.param1 = 0;
786 : 14 : spdm_response->header.param2 = SPDM_ENCAPSULATED_RESPONSE_ACK_RESPONSE_PAYLOAD_TYPE_ABSENT;
787 : 14 : *response_size = ack_header_size;
788 : :
789 [ + + + - ]: 14 : if ((encap_context->flow_type == LIBSPDM_ENCAP_FLOW_SESS_MUT_AUTH) && !response_not_ready) {
790 : : /* When MutAuthRequested bit 1 or bit 2 is set, the Responder must designate the
791 : : * Requester's certificate slot in the final ENCAPSULATED_RESPONSE_ACK, as
792 : : * KEY_EXCHANGE_RSP.ReqSlotID could not convey it. */
793 : 2 : spdm_response->header.param2 =
794 : : SPDM_ENCAPSULATED_RESPONSE_ACK_RESPONSE_PAYLOAD_TYPE_REQ_SLOT_NUMBER;
795 : 2 : *response_size = ack_header_size + 1;
796 : 2 : *((uint8_t *)spdm_response + ack_header_size) = encap_context->mut_auth_req_slot_id;
797 : : }
798 : :
799 : 14 : encap_context->flow_type = LIBSPDM_ENCAP_FLOW_NONE;
800 : : }
801 : :
802 : 23 : return LIBSPDM_STATUS_SUCCESS;
803 : : }
804 : :
805 : 14 : libspdm_return_t libspdm_handle_encap_error_response_main(uint8_t error_code)
806 : : {
807 [ + + ]: 14 : if (error_code == SPDM_ERROR_CODE_RESPONSE_NOT_READY) {
808 : 5 : return LIBSPDM_STATUS_NOT_READY_PEER;
809 : : }
810 : :
811 : 9 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
812 : : }
813 : : #endif /* LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP */
|