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