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_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 33 : 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 33 : 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 33 : result = libspdm_calculate_th_hmac_for_finish_req(
76 : spdm_context, session_info, &hash_size, calc_hmac_data);
77 33 : if (!result) {
78 0 : return false;
79 : }
80 : #endif
81 33 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "th_curr hmac - "));
82 33 : LIBSPDM_INTERNAL_DUMP_DATA(calc_hmac_data, hash_size);
83 33 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "\n"));
84 :
85 33 : libspdm_copy_mem(hmac, hash_size, calc_hmac_data, hash_size);
86 :
87 33 : 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 36 : static libspdm_return_t libspdm_try_send_receive_psk_finish(
97 : libspdm_context_t *spdm_context,
98 : uint32_t session_id,
99 : const void *requester_opaque_data,
100 : size_t requester_opaque_data_size,
101 : void *responder_opaque_data,
102 : size_t *responder_opaque_data_size)
103 : {
104 : libspdm_return_t status;
105 : libspdm_psk_finish_request_mine_t *spdm_request;
106 : size_t spdm_request_size;
107 : size_t hmac_size;
108 : libspdm_psk_finish_response_max_t *spdm_response;
109 : size_t spdm_response_size;
110 : libspdm_session_info_t *session_info;
111 : uint8_t th2_hash_data[LIBSPDM_MAX_HASH_SIZE];
112 : libspdm_session_state_t session_state;
113 : bool result;
114 : uint8_t *message;
115 : size_t message_size;
116 : size_t transport_header_size;
117 : uint8_t *ptr;
118 : size_t opaque_data_entry_size;
119 : size_t opaque_data_size;
120 :
121 36 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_11) {
122 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
123 : }
124 :
125 36 : if (!libspdm_is_capabilities_flag_supported(
126 : spdm_context, true,
127 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_PSK_CAP,
128 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_PSK_CAP_RESPONDER_WITH_CONTEXT)) {
129 1 : status = LIBSPDM_STATUS_UNSUPPORTED_CAP;
130 1 : goto error;
131 : }
132 :
133 35 : if (spdm_context->connection_info.connection_state <
134 : LIBSPDM_CONNECTION_STATE_NEGOTIATED) {
135 1 : status = LIBSPDM_STATUS_INVALID_STATE_LOCAL;
136 1 : goto error;
137 : }
138 :
139 : session_info =
140 34 : libspdm_get_session_info_via_session_id(spdm_context, session_id);
141 34 : if (session_info == NULL) {
142 0 : LIBSPDM_ASSERT(false);
143 0 : status = LIBSPDM_STATUS_INVALID_STATE_LOCAL;
144 0 : goto error;
145 : }
146 34 : session_state = libspdm_secured_message_get_session_state(
147 : session_info->secured_message_context);
148 34 : if (session_state != LIBSPDM_SESSION_STATE_HANDSHAKING) {
149 1 : status = LIBSPDM_STATUS_INVALID_STATE_LOCAL;
150 1 : goto error;
151 : }
152 :
153 33 : transport_header_size = spdm_context->local_context.capability.transport_header_size;
154 33 : status = libspdm_acquire_sender_buffer (spdm_context, &message_size, (void **)&message);
155 33 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
156 0 : return status;
157 : }
158 33 : LIBSPDM_ASSERT (message_size >= transport_header_size +
159 : spdm_context->local_context.capability.transport_tail_size);
160 33 : spdm_request = (void *)(message + transport_header_size);
161 33 : spdm_request_size = message_size - transport_header_size -
162 33 : spdm_context->local_context.capability.transport_tail_size;
163 :
164 33 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_request->header));
165 33 : spdm_request->header.spdm_version = libspdm_get_connection_version (spdm_context);
166 33 : spdm_request->header.request_response_code = SPDM_PSK_FINISH;
167 33 : spdm_request->header.param1 = 0;
168 33 : spdm_request->header.param2 = 0;
169 :
170 33 : ptr = (uint8_t *)spdm_request + sizeof(spdm_psk_finish_request_t);
171 33 : if (libspdm_get_connection_version(spdm_context) >= SPDM_MESSAGE_VERSION_14) {
172 2 : if (requester_opaque_data != NULL) {
173 0 : LIBSPDM_ASSERT(requester_opaque_data_size <= SPDM_MAX_OPAQUE_DATA_SIZE);
174 0 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_psk_finish_request_t) +
175 : sizeof(uint16_t) + requester_opaque_data_size);
176 :
177 0 : libspdm_write_uint16(ptr, (uint16_t)requester_opaque_data_size);
178 0 : ptr += sizeof(uint16_t);
179 :
180 0 : libspdm_copy_mem(ptr,
181 : (spdm_request_size - (sizeof(spdm_psk_finish_request_t) +
182 : sizeof(uint16_t))),
183 : requester_opaque_data, requester_opaque_data_size);
184 0 : opaque_data_size = requester_opaque_data_size;
185 : } else {
186 2 : opaque_data_size = 0;
187 2 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_psk_finish_request_t) +
188 : sizeof(uint16_t) + opaque_data_size);
189 :
190 2 : libspdm_write_uint16(ptr, (uint16_t)opaque_data_size);
191 2 : ptr += sizeof(uint16_t);
192 : }
193 2 : ptr += opaque_data_size;
194 2 : opaque_data_entry_size = sizeof(uint16_t) + opaque_data_size;
195 : } else {
196 31 : opaque_data_entry_size = 0;
197 : }
198 :
199 33 : hmac_size = libspdm_get_hash_size(
200 : spdm_context->connection_info.algorithm.base_hash_algo);
201 33 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_request->header) +
202 : opaque_data_entry_size + hmac_size);
203 33 : spdm_request_size = sizeof(spdm_psk_finish_request_t) + opaque_data_entry_size + hmac_size;
204 :
205 33 : status = libspdm_append_message_f(spdm_context, session_info, true, (uint8_t *)spdm_request,
206 : spdm_request_size - hmac_size);
207 33 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
208 0 : libspdm_release_sender_buffer (spdm_context);
209 0 : goto error;
210 : }
211 :
212 33 : result = libspdm_generate_psk_exchange_req_hmac(spdm_context, session_info,
213 : ptr);
214 33 : if (!result) {
215 0 : libspdm_release_sender_buffer (spdm_context);
216 0 : status = LIBSPDM_STATUS_CRYPTO_ERROR;
217 0 : goto error;
218 : }
219 :
220 33 : status = libspdm_append_message_f(spdm_context, session_info, true,
221 : (uint8_t *)spdm_request +
222 33 : spdm_request_size - hmac_size,
223 : hmac_size);
224 33 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
225 0 : libspdm_release_sender_buffer (spdm_context);
226 0 : goto error;
227 : }
228 :
229 33 : status = libspdm_send_spdm_request(spdm_context, &session_id,
230 : spdm_request_size, spdm_request);
231 33 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
232 1 : libspdm_release_sender_buffer (spdm_context);
233 1 : goto error;
234 : }
235 :
236 32 : libspdm_reset_message_buffer_via_request_code(spdm_context, session_info,
237 : SPDM_PSK_FINISH);
238 :
239 32 : libspdm_release_sender_buffer (spdm_context);
240 32 : spdm_request = (void *)spdm_context->last_spdm_request;
241 :
242 : /* receive */
243 :
244 32 : status = libspdm_acquire_receiver_buffer (spdm_context, &message_size, (void **)&message);
245 32 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
246 0 : goto error;
247 : }
248 32 : LIBSPDM_ASSERT (message_size >= transport_header_size);
249 32 : spdm_response = (void *)(message);
250 32 : spdm_response_size = message_size;
251 :
252 32 : status = libspdm_receive_spdm_response(
253 : spdm_context, &session_id, &spdm_response_size, (void **)&spdm_response);
254 32 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
255 0 : goto receive_done;
256 : }
257 32 : if (spdm_response_size < sizeof(spdm_message_header_t)) {
258 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
259 0 : goto receive_done;
260 : }
261 32 : if (spdm_response->header.request_response_code == SPDM_ERROR) {
262 25 : if (spdm_response->header.param1 == SPDM_ERROR_CODE_DECRYPT_ERROR) {
263 2 : status = LIBSPDM_STATUS_SESSION_MSG_ERROR;
264 2 : goto receive_done;
265 : }
266 23 : status = libspdm_handle_error_response_main(
267 : spdm_context, &session_id,
268 : &spdm_response_size, (void **)&spdm_response,
269 : SPDM_PSK_FINISH, SPDM_PSK_FINISH_RSP);
270 23 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
271 22 : goto receive_done;
272 : }
273 7 : } else if (spdm_response->header.request_response_code !=
274 : SPDM_PSK_FINISH_RSP) {
275 1 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
276 1 : goto receive_done;
277 : }
278 7 : if (spdm_response->header.spdm_version != spdm_request->header.spdm_version) {
279 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
280 0 : goto receive_done;
281 : }
282 :
283 7 : ptr = (uint8_t *)spdm_response + sizeof(spdm_psk_finish_response_t);
284 :
285 7 : if (libspdm_get_connection_version(spdm_context) >= SPDM_MESSAGE_VERSION_14) {
286 2 : if (spdm_response_size < sizeof(spdm_psk_finish_response_t) + sizeof(uint16_t)) {
287 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
288 0 : goto receive_done;
289 : }
290 2 : opaque_data_size = libspdm_read_uint16((const uint8_t *)ptr);
291 2 : ptr += sizeof(uint16_t);
292 2 : if (opaque_data_size > SPDM_MAX_OPAQUE_DATA_SIZE) {
293 1 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
294 1 : goto receive_done;
295 : }
296 :
297 : /* this message can only be in secured session
298 : * thus don't need to consider transport layer padding, just check its exact size */
299 1 : if (spdm_response_size != sizeof(spdm_psk_finish_response_t) + sizeof(uint16_t) +
300 : opaque_data_size) {
301 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
302 0 : goto receive_done;
303 : }
304 :
305 1 : if ((responder_opaque_data != NULL) && (responder_opaque_data_size != NULL)) {
306 0 : if (opaque_data_size > *responder_opaque_data_size) {
307 0 : status = LIBSPDM_STATUS_BUFFER_TOO_SMALL;
308 0 : goto receive_done;
309 : }
310 0 : libspdm_copy_mem(responder_opaque_data, *responder_opaque_data_size,
311 : ptr, opaque_data_size);
312 0 : *responder_opaque_data_size = opaque_data_size;
313 : }
314 :
315 1 : ptr += opaque_data_size;
316 1 : opaque_data_entry_size = sizeof(uint16_t) + opaque_data_size;
317 : } else {
318 : /* this message can only be in secured session
319 : * thus don't need to consider transport layer padding, just check its exact size */
320 5 : if (spdm_response_size != sizeof(spdm_psk_finish_response_t)) {
321 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
322 0 : goto receive_done;
323 : }
324 5 : if ((responder_opaque_data != NULL) && (responder_opaque_data_size != NULL)) {
325 0 : *responder_opaque_data_size = 0;
326 : }
327 5 : opaque_data_entry_size = 0;
328 : }
329 6 : spdm_response_size = sizeof(spdm_psk_finish_response_t) + opaque_data_entry_size;
330 :
331 6 : status = libspdm_append_message_f(spdm_context, session_info, true, spdm_response,
332 : spdm_response_size);
333 6 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
334 0 : goto receive_done;
335 : }
336 :
337 6 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "libspdm_generate_session_data_key[%x]\n", session_id));
338 6 : result = libspdm_calculate_th2_hash(spdm_context, session_info, true,
339 : th2_hash_data);
340 6 : if (!result) {
341 0 : status = LIBSPDM_STATUS_CRYPTO_ERROR;
342 0 : goto receive_done;
343 : }
344 6 : result = libspdm_generate_session_data_key(
345 : session_info->secured_message_context, th2_hash_data);
346 6 : if (!result) {
347 0 : status = LIBSPDM_STATUS_CRYPTO_ERROR;
348 0 : goto receive_done;
349 : }
350 :
351 6 : libspdm_secured_message_set_session_state(
352 : session_info->secured_message_context,
353 : LIBSPDM_SESSION_STATE_ESTABLISHED);
354 :
355 : /* -=[Log Message Phase]=- */
356 : #if LIBSPDM_ENABLE_MSG_LOG
357 6 : libspdm_append_msg_log(spdm_context, spdm_response, spdm_response_size);
358 : #endif /* LIBSPDM_ENABLE_MSG_LOG */
359 :
360 6 : libspdm_release_receiver_buffer (spdm_context);
361 6 : return LIBSPDM_STATUS_SUCCESS;
362 :
363 26 : receive_done:
364 26 : libspdm_release_receiver_buffer (spdm_context);
365 30 : error:
366 30 : if (LIBSPDM_STATUS_BUSY_PEER != status) {
367 28 : libspdm_free_session_id(spdm_context, session_id);
368 : }
369 30 : return status;
370 : }
371 :
372 35 : libspdm_return_t libspdm_send_receive_psk_finish(libspdm_context_t *spdm_context,
373 : uint32_t session_id)
374 : {
375 : size_t retry;
376 : uint64_t retry_delay_time;
377 : libspdm_return_t status;
378 :
379 35 : spdm_context->crypto_request = true;
380 35 : retry = spdm_context->retry_times;
381 35 : retry_delay_time = spdm_context->retry_delay_time;
382 : do {
383 36 : status = libspdm_try_send_receive_psk_finish(spdm_context,
384 : session_id,
385 : NULL, 0, NULL, NULL);
386 36 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
387 34 : return status;
388 : }
389 :
390 2 : libspdm_sleep(retry_delay_time);
391 2 : } while (retry-- != 0);
392 :
393 1 : return status;
394 : }
395 :
396 0 : libspdm_return_t libspdm_send_receive_psk_finish_ex(
397 : libspdm_context_t *spdm_context,
398 : uint32_t session_id,
399 : const void *requester_opaque_data,
400 : size_t requester_opaque_data_size,
401 : void *responder_opaque_data,
402 : size_t *responder_opaque_data_size)
403 : {
404 : size_t retry;
405 : uint64_t retry_delay_time;
406 : libspdm_return_t status;
407 :
408 0 : spdm_context->crypto_request = true;
409 0 : retry = spdm_context->retry_times;
410 0 : retry_delay_time = spdm_context->retry_delay_time;
411 : do {
412 0 : status = libspdm_try_send_receive_psk_finish(spdm_context,
413 : session_id,
414 : requester_opaque_data,
415 : requester_opaque_data_size,
416 : responder_opaque_data,
417 : responder_opaque_data_size);
418 0 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
419 0 : return status;
420 : }
421 :
422 0 : libspdm_sleep(retry_delay_time);
423 0 : } while (retry-- != 0);
424 :
425 0 : return status;
426 : }
427 :
428 : #endif /* LIBSPDM_ENABLE_CAPABILITY_PSK_CAP*/
|