Line data Source code
1 : /**
2 : * Copyright Notice:
3 : * Copyright 2021-2025 DMTF. All rights reserved.
4 : * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md
5 : **/
6 :
7 : #include "internal/libspdm_requester_lib.h"
8 : #include "internal/libspdm_secured_message_lib.h"
9 :
10 : #if LIBSPDM_ENABLE_CAPABILITY_PSK_CAP
11 :
12 : #pragma pack(1)
13 : typedef struct {
14 : spdm_message_header_t header;
15 : uint16_t opaque_length;
16 : uint8_t opaque_data[SPDM_MAX_OPAQUE_DATA_SIZE];
17 : uint8_t verify_data[LIBSPDM_MAX_HASH_SIZE];
18 : } libspdm_psk_finish_request_mine_t;
19 :
20 : typedef struct {
21 : spdm_message_header_t header;
22 : uint16_t opaque_length;
23 : uint8_t opaque_data[SPDM_MAX_OPAQUE_DATA_SIZE];
24 : } libspdm_psk_finish_response_max_t;
25 : #pragma pack()
26 :
27 : /**
28 : * This function generates the PSK finish HMAC based upon TH.
29 : *
30 : * @param spdm_context A pointer to the SPDM context.
31 : * @param session_info The session info of an SPDM session.
32 : * @param hmac The buffer to store the finish HMAC.
33 : *
34 : * @retval true PSK finish HMAC is generated.
35 : * @retval false PSK finish HMAC is not generated.
36 : **/
37 32 : bool libspdm_generate_psk_exchange_req_hmac(libspdm_context_t *spdm_context,
38 : libspdm_session_info_t *session_info,
39 : void *hmac)
40 : {
41 : size_t hash_size;
42 : uint8_t calc_hmac_data[LIBSPDM_MAX_HASH_SIZE];
43 : bool result;
44 : #if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
45 : uint8_t *th_curr_data;
46 : size_t th_curr_data_size;
47 : libspdm_th_managed_buffer_t th_curr;
48 : uint8_t hash_data[LIBSPDM_MAX_HASH_SIZE];
49 : #endif
50 :
51 32 : hash_size = libspdm_get_hash_size(spdm_context->connection_info.algorithm.base_hash_algo);
52 :
53 : #if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
54 : result = libspdm_calculate_th_for_finish(spdm_context, session_info, NULL,
55 : 0, NULL, 0, &th_curr);
56 : if (!result) {
57 : return false;
58 : }
59 : th_curr_data = libspdm_get_managed_buffer(&th_curr);
60 : th_curr_data_size = libspdm_get_managed_buffer_size(&th_curr);
61 :
62 : result = libspdm_hash_all (spdm_context->connection_info.algorithm.base_hash_algo,
63 : th_curr_data, th_curr_data_size, hash_data);
64 : if (!result) {
65 : return false;
66 : }
67 :
68 : result = libspdm_hmac_all_with_request_finished_key(
69 : session_info->secured_message_context, hash_data,
70 : hash_size, calc_hmac_data);
71 : if (!result) {
72 : return false;
73 : }
74 : #else
75 32 : result = libspdm_calculate_th_hmac_for_finish_req(
76 : spdm_context, session_info, &hash_size, calc_hmac_data);
77 32 : if (!result) {
78 0 : return false;
79 : }
80 : #endif
81 32 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "th_curr hmac - "));
82 32 : LIBSPDM_INTERNAL_DUMP_DATA(calc_hmac_data, hash_size);
83 32 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "\n"));
84 :
85 32 : libspdm_copy_mem(hmac, hash_size, calc_hmac_data, hash_size);
86 :
87 32 : return true;
88 : }
89 :
90 : /**
91 : * This function sends PSK_FINISH and receives PSK_FINISH_RSP for SPDM PSK finish.
92 : *
93 : * @param spdm_context A pointer to the SPDM context.
94 : * @param session_id session_id to the PSK_FINISH request.
95 : *
96 : * @retval RETURN_SUCCESS The PSK_FINISH is sent and the PSK_FINISH_RSP is received.
97 : * @retval RETURN_DEVICE_ERROR A device error occurs when communicates with the device.
98 : **/
99 35 : static libspdm_return_t libspdm_try_send_receive_psk_finish(
100 : libspdm_context_t *spdm_context,
101 : uint32_t session_id,
102 : const void *requester_opaque_data,
103 : size_t requester_opaque_data_size,
104 : void *responder_opaque_data,
105 : size_t *responder_opaque_data_size)
106 : {
107 : libspdm_return_t status;
108 : libspdm_psk_finish_request_mine_t *spdm_request;
109 : size_t spdm_request_size;
110 : size_t hmac_size;
111 : libspdm_psk_finish_response_max_t *spdm_response;
112 : size_t spdm_response_size;
113 : libspdm_session_info_t *session_info;
114 : uint8_t th2_hash_data[LIBSPDM_MAX_HASH_SIZE];
115 : libspdm_session_state_t session_state;
116 : bool result;
117 : uint8_t *message;
118 : size_t message_size;
119 : size_t transport_header_size;
120 : uint8_t *ptr;
121 : size_t opaque_data_entry_size;
122 : size_t opaque_data_size;
123 :
124 35 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_11) {
125 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
126 : }
127 :
128 35 : if (!libspdm_is_capabilities_flag_supported(
129 : spdm_context, true,
130 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_PSK_CAP,
131 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_PSK_CAP_RESPONDER_WITH_CONTEXT)) {
132 1 : status = LIBSPDM_STATUS_UNSUPPORTED_CAP;
133 1 : goto error;
134 : }
135 :
136 34 : if (spdm_context->connection_info.connection_state <
137 : LIBSPDM_CONNECTION_STATE_NEGOTIATED) {
138 1 : status = LIBSPDM_STATUS_INVALID_STATE_LOCAL;
139 1 : goto error;
140 : }
141 :
142 : session_info =
143 33 : libspdm_get_session_info_via_session_id(spdm_context, session_id);
144 33 : if (session_info == NULL) {
145 0 : LIBSPDM_ASSERT(false);
146 0 : status = LIBSPDM_STATUS_INVALID_STATE_LOCAL;
147 0 : goto error;
148 : }
149 33 : session_state = libspdm_secured_message_get_session_state(
150 : session_info->secured_message_context);
151 33 : if (session_state != LIBSPDM_SESSION_STATE_HANDSHAKING) {
152 1 : status = LIBSPDM_STATUS_INVALID_STATE_LOCAL;
153 1 : goto error;
154 : }
155 :
156 32 : transport_header_size = spdm_context->local_context.capability.transport_header_size;
157 32 : status = libspdm_acquire_sender_buffer (spdm_context, &message_size, (void **)&message);
158 32 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
159 0 : return status;
160 : }
161 32 : LIBSPDM_ASSERT (message_size >= transport_header_size +
162 : spdm_context->local_context.capability.transport_tail_size);
163 32 : spdm_request = (void *)(message + transport_header_size);
164 32 : spdm_request_size = message_size - transport_header_size -
165 32 : spdm_context->local_context.capability.transport_tail_size;
166 :
167 32 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_request->header));
168 32 : spdm_request->header.spdm_version = libspdm_get_connection_version (spdm_context);
169 32 : spdm_request->header.request_response_code = SPDM_PSK_FINISH;
170 32 : spdm_request->header.param1 = 0;
171 32 : spdm_request->header.param2 = 0;
172 :
173 32 : ptr = (uint8_t *)spdm_request + sizeof(spdm_psk_finish_request_t);
174 32 : if (libspdm_get_connection_version(spdm_context) >= SPDM_MESSAGE_VERSION_14) {
175 1 : if (requester_opaque_data != NULL) {
176 0 : LIBSPDM_ASSERT(requester_opaque_data_size <= SPDM_MAX_OPAQUE_DATA_SIZE);
177 0 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_psk_finish_request_t) +
178 : sizeof(uint16_t) + requester_opaque_data_size);
179 :
180 0 : libspdm_write_uint16(ptr, (uint16_t)requester_opaque_data_size);
181 0 : ptr += sizeof(uint16_t);
182 :
183 0 : libspdm_copy_mem(ptr,
184 : (spdm_request_size - (sizeof(spdm_psk_finish_request_t) +
185 : sizeof(uint16_t))),
186 : requester_opaque_data, requester_opaque_data_size);
187 0 : opaque_data_size = requester_opaque_data_size;
188 : } else {
189 1 : opaque_data_size = 0;
190 1 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_psk_finish_request_t) +
191 : sizeof(uint16_t) + opaque_data_size);
192 :
193 1 : libspdm_write_uint16(ptr, (uint16_t)opaque_data_size);
194 1 : ptr += sizeof(uint16_t);
195 : }
196 1 : ptr += opaque_data_size;
197 1 : opaque_data_entry_size = sizeof(uint16_t) + opaque_data_size;
198 : } else {
199 31 : opaque_data_entry_size = 0;
200 : }
201 :
202 32 : hmac_size = libspdm_get_hash_size(
203 : spdm_context->connection_info.algorithm.base_hash_algo);
204 32 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_request->header) +
205 : opaque_data_entry_size + hmac_size);
206 32 : spdm_request_size = sizeof(spdm_psk_finish_request_t) + opaque_data_entry_size + hmac_size;
207 :
208 32 : status = libspdm_append_message_f(spdm_context, session_info, true, (uint8_t *)spdm_request,
209 : spdm_request_size - hmac_size);
210 32 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
211 0 : libspdm_release_sender_buffer (spdm_context);
212 0 : goto error;
213 : }
214 :
215 32 : result = libspdm_generate_psk_exchange_req_hmac(spdm_context, session_info,
216 : ptr);
217 32 : if (!result) {
218 0 : libspdm_release_sender_buffer (spdm_context);
219 0 : status = LIBSPDM_STATUS_CRYPTO_ERROR;
220 0 : goto error;
221 : }
222 :
223 32 : status = libspdm_append_message_f(spdm_context, session_info, true,
224 : (uint8_t *)spdm_request +
225 32 : spdm_request_size - hmac_size,
226 : hmac_size);
227 32 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
228 0 : libspdm_release_sender_buffer (spdm_context);
229 0 : goto error;
230 : }
231 :
232 32 : status = libspdm_send_spdm_request(spdm_context, &session_id,
233 : spdm_request_size, spdm_request);
234 32 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
235 1 : libspdm_release_sender_buffer (spdm_context);
236 1 : goto error;
237 : }
238 :
239 31 : libspdm_reset_message_buffer_via_request_code(spdm_context, session_info,
240 : SPDM_PSK_FINISH);
241 :
242 31 : libspdm_release_sender_buffer (spdm_context);
243 31 : spdm_request = (void *)spdm_context->last_spdm_request;
244 :
245 : /* receive */
246 :
247 31 : status = libspdm_acquire_receiver_buffer (spdm_context, &message_size, (void **)&message);
248 31 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
249 0 : goto error;
250 : }
251 31 : LIBSPDM_ASSERT (message_size >= transport_header_size);
252 31 : spdm_response = (void *)(message);
253 31 : spdm_response_size = message_size;
254 :
255 31 : status = libspdm_receive_spdm_response(
256 : spdm_context, &session_id, &spdm_response_size, (void **)&spdm_response);
257 31 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
258 0 : goto receive_done;
259 : }
260 31 : if (spdm_response_size < sizeof(spdm_message_header_t)) {
261 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
262 0 : goto receive_done;
263 : }
264 31 : if (spdm_response->header.request_response_code == SPDM_ERROR) {
265 25 : if (spdm_response->header.param1 == SPDM_ERROR_CODE_DECRYPT_ERROR) {
266 2 : status = LIBSPDM_STATUS_SESSION_MSG_ERROR;
267 2 : goto receive_done;
268 : }
269 23 : status = libspdm_handle_error_response_main(
270 : spdm_context, &session_id,
271 : &spdm_response_size, (void **)&spdm_response,
272 : SPDM_PSK_FINISH, SPDM_PSK_FINISH_RSP);
273 23 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
274 22 : goto receive_done;
275 : }
276 6 : } else if (spdm_response->header.request_response_code !=
277 : SPDM_PSK_FINISH_RSP) {
278 1 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
279 1 : goto receive_done;
280 : }
281 6 : if (spdm_response->header.spdm_version != spdm_request->header.spdm_version) {
282 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
283 0 : goto receive_done;
284 : }
285 :
286 6 : ptr = (uint8_t *)spdm_response + sizeof(spdm_psk_finish_response_t);
287 :
288 6 : if (libspdm_get_connection_version(spdm_context) >= SPDM_MESSAGE_VERSION_14) {
289 1 : if (spdm_response_size < sizeof(spdm_psk_finish_response_t) + sizeof(uint16_t)) {
290 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
291 0 : goto receive_done;
292 : }
293 1 : opaque_data_size = libspdm_read_uint16((const uint8_t *)ptr);
294 1 : ptr += sizeof(uint16_t);
295 1 : if (opaque_data_size > SPDM_MAX_OPAQUE_DATA_SIZE) {
296 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
297 0 : goto receive_done;
298 : }
299 : /* this message can only be in secured session
300 : * thus don't need to consider transport layer padding, just check its exact size */
301 1 : if (spdm_response_size != sizeof(spdm_psk_finish_response_t) + sizeof(uint16_t) +
302 : opaque_data_size) {
303 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
304 0 : goto receive_done;
305 : }
306 :
307 1 : if ((responder_opaque_data != NULL) && (responder_opaque_data_size != NULL)) {
308 0 : if (opaque_data_size >= *responder_opaque_data_size) {
309 0 : status = LIBSPDM_STATUS_BUFFER_TOO_SMALL;
310 0 : goto receive_done;
311 : }
312 0 : libspdm_copy_mem(responder_opaque_data, *responder_opaque_data_size,
313 : ptr, opaque_data_size);
314 0 : *responder_opaque_data_size = opaque_data_size;
315 : }
316 :
317 1 : ptr += opaque_data_size;
318 1 : opaque_data_entry_size = sizeof(uint16_t) + opaque_data_size;
319 : } else {
320 : /* this message can only be in secured session
321 : * thus don't need to consider transport layer padding, just check its exact size */
322 5 : if (spdm_response_size != sizeof(spdm_psk_finish_response_t)) {
323 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
324 0 : goto receive_done;
325 : }
326 5 : if ((responder_opaque_data != NULL) && (responder_opaque_data_size != NULL)) {
327 0 : *responder_opaque_data_size = 0;
328 : }
329 5 : opaque_data_entry_size = 0;
330 : }
331 6 : spdm_response_size = sizeof(spdm_finish_response_t) + opaque_data_entry_size;
332 :
333 6 : status = libspdm_append_message_f(spdm_context, session_info, true, spdm_response,
334 : spdm_response_size);
335 6 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
336 0 : goto receive_done;
337 : }
338 :
339 6 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "libspdm_generate_session_data_key[%x]\n", session_id));
340 6 : result = libspdm_calculate_th2_hash(spdm_context, session_info, true,
341 : th2_hash_data);
342 6 : if (!result) {
343 0 : status = LIBSPDM_STATUS_CRYPTO_ERROR;
344 0 : goto receive_done;
345 : }
346 6 : result = libspdm_generate_session_data_key(
347 : session_info->secured_message_context, th2_hash_data);
348 6 : if (!result) {
349 0 : status = LIBSPDM_STATUS_CRYPTO_ERROR;
350 0 : goto receive_done;
351 : }
352 :
353 6 : libspdm_secured_message_set_session_state(
354 : session_info->secured_message_context,
355 : LIBSPDM_SESSION_STATE_ESTABLISHED);
356 :
357 : /* -=[Log Message Phase]=- */
358 : #if LIBSPDM_ENABLE_MSG_LOG
359 6 : libspdm_append_msg_log(spdm_context, spdm_response, spdm_response_size);
360 : #endif /* LIBSPDM_ENABLE_MSG_LOG */
361 :
362 6 : libspdm_release_receiver_buffer (spdm_context);
363 6 : return LIBSPDM_STATUS_SUCCESS;
364 :
365 25 : receive_done:
366 25 : libspdm_release_receiver_buffer (spdm_context);
367 29 : error:
368 29 : if (LIBSPDM_STATUS_BUSY_PEER != status) {
369 27 : libspdm_free_session_id(spdm_context, session_id);
370 : }
371 29 : return status;
372 : }
373 :
374 34 : libspdm_return_t libspdm_send_receive_psk_finish(libspdm_context_t *spdm_context,
375 : uint32_t session_id)
376 : {
377 : size_t retry;
378 : uint64_t retry_delay_time;
379 : libspdm_return_t status;
380 :
381 34 : spdm_context->crypto_request = true;
382 34 : retry = spdm_context->retry_times;
383 34 : retry_delay_time = spdm_context->retry_delay_time;
384 : do {
385 35 : status = libspdm_try_send_receive_psk_finish(spdm_context,
386 : session_id,
387 : NULL, 0, NULL, NULL);
388 35 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
389 33 : return status;
390 : }
391 :
392 2 : libspdm_sleep(retry_delay_time);
393 2 : } while (retry-- != 0);
394 :
395 1 : return status;
396 : }
397 :
398 0 : libspdm_return_t libspdm_send_receive_psk_finish_ex(
399 : libspdm_context_t *spdm_context,
400 : uint32_t session_id,
401 : const void *requester_opaque_data,
402 : size_t requester_opaque_data_size,
403 : void *responder_opaque_data,
404 : size_t *responder_opaque_data_size)
405 : {
406 : size_t retry;
407 : uint64_t retry_delay_time;
408 : libspdm_return_t status;
409 :
410 0 : spdm_context->crypto_request = true;
411 0 : retry = spdm_context->retry_times;
412 0 : retry_delay_time = spdm_context->retry_delay_time;
413 : do {
414 0 : status = libspdm_try_send_receive_psk_finish(spdm_context,
415 : session_id,
416 : requester_opaque_data,
417 : requester_opaque_data_size,
418 : responder_opaque_data,
419 : responder_opaque_data_size);
420 0 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
421 0 : return status;
422 : }
423 :
424 0 : libspdm_sleep(retry_delay_time);
425 0 : } while (retry-- != 0);
426 :
427 0 : return status;
428 : }
429 :
430 : #endif /* LIBSPDM_ENABLE_CAPABILITY_PSK_CAP*/
|