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_KEY_EX_CAP) || (LIBSPDM_ENABLE_CAPABILITY_PSK_CAP)
11 :
12 : #pragma pack(1)
13 : typedef struct {
14 : spdm_message_header_t header;
15 : uint8_t dummy_data[sizeof(spdm_error_data_response_not_ready_t)];
16 : } libspdm_key_update_response_mine_t;
17 : #pragma pack()
18 :
19 : /**
20 : * This function sends KEY_UPDATE
21 : * to update keys for an SPDM Session.
22 : *
23 : * After keys are updated, this function also uses VERIFY_NEW_KEY to verify the key.
24 : *
25 : * @param spdm_context A pointer to the SPDM context.
26 : * @param session_id The session ID of the session.
27 : * @param single_direction true means the operation is UPDATE_KEY.
28 : * false means the operation is UPDATE_ALL_KEYS.
29 : * @param key_updated true means the operation is to verify key(s).
30 : * false means the operation is to update and verify key(s).
31 : **/
32 92 : static libspdm_return_t libspdm_try_key_update(libspdm_context_t *spdm_context,
33 : uint32_t session_id,
34 : bool single_direction, bool *key_updated)
35 : {
36 : libspdm_return_t status;
37 : bool result;
38 : spdm_key_update_request_t *spdm_request;
39 : size_t spdm_request_size;
40 : libspdm_key_update_response_mine_t *spdm_response;
41 : size_t spdm_response_size;
42 : libspdm_session_info_t *session_info;
43 : libspdm_session_state_t session_state;
44 : uint8_t *message;
45 : size_t message_size;
46 : size_t transport_header_size;
47 :
48 92 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_11) {
49 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
50 : }
51 :
52 92 : if (!libspdm_is_capabilities_flag_supported(
53 : spdm_context, true,
54 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_KEY_UPD_CAP,
55 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_KEY_UPD_CAP)) {
56 1 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
57 : }
58 :
59 91 : if (spdm_context->connection_info.connection_state <
60 : LIBSPDM_CONNECTION_STATE_NEGOTIATED) {
61 1 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
62 : }
63 : session_info =
64 90 : libspdm_get_session_info_via_session_id(spdm_context, session_id);
65 90 : if (session_info == NULL) {
66 0 : LIBSPDM_ASSERT(false);
67 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
68 : }
69 90 : session_state = libspdm_secured_message_get_session_state(
70 : session_info->secured_message_context);
71 90 : if (session_state != LIBSPDM_SESSION_STATE_ESTABLISHED) {
72 1 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
73 : }
74 :
75 89 : libspdm_reset_message_buffer_via_request_code(spdm_context, session_info, SPDM_KEY_UPDATE);
76 :
77 89 : if (!(*key_updated)) {
78 :
79 : /* Update key*/
80 :
81 85 : transport_header_size = spdm_context->local_context.capability.transport_header_size;
82 85 : status = libspdm_acquire_sender_buffer (spdm_context, &message_size, (void **)&message);
83 85 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
84 0 : return status;
85 : }
86 85 : LIBSPDM_ASSERT (message_size >= transport_header_size +
87 : spdm_context->local_context.capability.transport_tail_size);
88 85 : spdm_request = (void *)(message + transport_header_size);
89 85 : spdm_request_size = message_size - transport_header_size -
90 85 : spdm_context->local_context.capability.transport_tail_size;
91 :
92 85 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_key_update_request_t));
93 85 : spdm_request->header.spdm_version = libspdm_get_connection_version (spdm_context);
94 85 : spdm_request->header.request_response_code = SPDM_KEY_UPDATE;
95 85 : if (single_direction) {
96 57 : spdm_request->header.param1 = SPDM_KEY_UPDATE_OPERATIONS_TABLE_UPDATE_KEY;
97 : } else {
98 28 : spdm_request->header.param1 = SPDM_KEY_UPDATE_OPERATIONS_TABLE_UPDATE_ALL_KEYS;
99 : }
100 85 : spdm_request->header.param2 = 0;
101 :
102 85 : if (!libspdm_get_random_number(sizeof(spdm_request->header.param2),
103 : &spdm_request->header.param2)) {
104 0 : libspdm_release_sender_buffer (spdm_context);
105 0 : return LIBSPDM_STATUS_LOW_ENTROPY;
106 : }
107 :
108 85 : spdm_request_size = sizeof(spdm_key_update_request_t);
109 :
110 : /* If updating both, create new responder key*/
111 85 : if (!single_direction) {
112 28 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
113 : "libspdm_create_update_session_data_key[%x] Responder\n",
114 : session_id));
115 28 : result = libspdm_create_update_session_data_key(
116 : session_info->secured_message_context,
117 : LIBSPDM_KEY_UPDATE_ACTION_RESPONDER);
118 28 : if (!result) {
119 0 : libspdm_release_sender_buffer (spdm_context);
120 0 : return LIBSPDM_STATUS_CRYPTO_ERROR;
121 : }
122 : }
123 :
124 85 : status = libspdm_send_spdm_request(spdm_context, &session_id,
125 : spdm_request_size, spdm_request);
126 85 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
127 1 : libspdm_release_sender_buffer (spdm_context);
128 1 : return status;
129 : }
130 :
131 84 : libspdm_release_sender_buffer (spdm_context);
132 84 : spdm_request = (void *)spdm_context->last_spdm_request;
133 :
134 : /* receive */
135 :
136 84 : status = libspdm_acquire_receiver_buffer (spdm_context, &message_size, (void **)&message);
137 84 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
138 0 : return status;
139 : }
140 84 : LIBSPDM_ASSERT (message_size >= transport_header_size);
141 84 : spdm_response = (void *)(message);
142 84 : spdm_response_size = message_size;
143 :
144 84 : status = libspdm_receive_spdm_response(
145 : spdm_context, &session_id, &spdm_response_size, (void **)&spdm_response);
146 :
147 84 : if (!LIBSPDM_STATUS_IS_ERROR(status)) {
148 84 : if (spdm_response->header.request_response_code == SPDM_ERROR) {
149 50 : status = libspdm_handle_error_response_main(
150 : spdm_context, &session_id,
151 : &spdm_response_size, (void **)&spdm_response,
152 : SPDM_KEY_UPDATE, SPDM_KEY_UPDATE_ACK);
153 34 : } else if (spdm_response_size != sizeof(spdm_key_update_response_t)) {
154 : /* this message can only be in secured session thus
155 : * don't need to consider transport layer padding, just check its exact size */
156 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
157 34 : } else if (spdm_response->header.spdm_version != spdm_request->header.spdm_version) {
158 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
159 34 : } else if ((spdm_response->header.request_response_code !=
160 33 : SPDM_KEY_UPDATE_ACK) ||
161 33 : (spdm_response->header.param1 != spdm_request->header.param1) ||
162 32 : (spdm_response->header.param2 != spdm_request->header.param2)) {
163 3 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
164 : }
165 : }
166 :
167 84 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
168 51 : if (!single_direction) {
169 25 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
170 : "libspdm_activate_update_session_data_key[%x] Responder old\n",
171 : session_id));
172 25 : result = libspdm_activate_update_session_data_key(
173 : session_info->secured_message_context,
174 : LIBSPDM_KEY_UPDATE_ACTION_RESPONDER, false);
175 25 : if (!result) {
176 0 : libspdm_release_receiver_buffer (spdm_context);
177 0 : return LIBSPDM_STATUS_CRYPTO_ERROR;
178 : }
179 : }
180 51 : libspdm_release_receiver_buffer (spdm_context);
181 51 : return status;
182 : }
183 :
184 33 : if (!single_direction) {
185 3 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
186 : "libspdm_activate_update_session_data_key[%x] Responder new\n",
187 : session_id));
188 3 : result = libspdm_activate_update_session_data_key(
189 : session_info->secured_message_context,
190 : LIBSPDM_KEY_UPDATE_ACTION_RESPONDER, true);
191 3 : if (!result) {
192 0 : libspdm_release_receiver_buffer (spdm_context);
193 0 : return LIBSPDM_STATUS_CRYPTO_ERROR;
194 : }
195 : }
196 :
197 33 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
198 : "libspdm_create_update_session_data_key[%x] Requester\n",
199 : session_id));
200 33 : result = libspdm_create_update_session_data_key(
201 : session_info->secured_message_context,
202 : LIBSPDM_KEY_UPDATE_ACTION_REQUESTER);
203 33 : if (!result) {
204 0 : libspdm_release_receiver_buffer (spdm_context);
205 0 : return LIBSPDM_STATUS_CRYPTO_ERROR;
206 : }
207 33 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,
208 : "libspdm_activate_update_session_data_key[%x] Requester new\n",
209 : session_id));
210 33 : result = libspdm_activate_update_session_data_key(
211 : session_info->secured_message_context,
212 : LIBSPDM_KEY_UPDATE_ACTION_REQUESTER, true);
213 33 : if (!result) {
214 0 : libspdm_release_receiver_buffer (spdm_context);
215 0 : return LIBSPDM_STATUS_CRYPTO_ERROR;
216 : }
217 :
218 : /* -=[Log Message Phase]=- */
219 : #if LIBSPDM_ENABLE_MSG_LOG
220 33 : libspdm_append_msg_log(spdm_context, spdm_response, spdm_response_size);
221 : #endif /* LIBSPDM_ENABLE_MSG_LOG */
222 :
223 33 : libspdm_release_receiver_buffer (spdm_context);
224 : }
225 :
226 37 : *key_updated = true;
227 :
228 :
229 : /* Verify key*/
230 :
231 37 : transport_header_size = spdm_context->local_context.capability.transport_header_size;
232 37 : status = libspdm_acquire_sender_buffer (spdm_context, &message_size, (void **)&message);
233 37 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
234 0 : return status;
235 : }
236 37 : LIBSPDM_ASSERT (message_size >= transport_header_size +
237 : spdm_context->local_context.capability.transport_tail_size);
238 37 : spdm_request = (void *)(message + transport_header_size);
239 37 : spdm_request_size = message_size - transport_header_size -
240 37 : spdm_context->local_context.capability.transport_tail_size;
241 :
242 37 : LIBSPDM_ASSERT (spdm_request_size >= sizeof(spdm_key_update_request_t));
243 37 : spdm_request->header.spdm_version = libspdm_get_connection_version (spdm_context);
244 37 : spdm_request->header.request_response_code = SPDM_KEY_UPDATE;
245 37 : spdm_request->header.param1 = SPDM_KEY_UPDATE_OPERATIONS_TABLE_VERIFY_NEW_KEY;
246 37 : spdm_request->header.param2 = 1;
247 37 : if (!libspdm_get_random_number(sizeof(spdm_request->header.param2),
248 : &spdm_request->header.param2)) {
249 0 : libspdm_release_sender_buffer (spdm_context);
250 0 : return LIBSPDM_STATUS_LOW_ENTROPY;
251 : }
252 37 : spdm_request_size = sizeof(spdm_key_update_request_t);
253 :
254 37 : status = libspdm_send_spdm_request(spdm_context, &session_id,
255 : spdm_request_size, spdm_request);
256 37 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
257 0 : libspdm_release_sender_buffer (spdm_context);
258 0 : return status;
259 : }
260 37 : libspdm_release_sender_buffer (spdm_context);
261 37 : spdm_request = (void *)spdm_context->last_spdm_request;
262 :
263 : /* receive */
264 :
265 37 : status = libspdm_acquire_receiver_buffer (spdm_context, &message_size, (void **)&message);
266 37 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
267 0 : return status;
268 : }
269 37 : LIBSPDM_ASSERT (message_size >= transport_header_size);
270 37 : spdm_response = (void *)(message);
271 37 : spdm_response_size = message_size;
272 :
273 37 : status = libspdm_receive_spdm_response(
274 : spdm_context, &session_id, &spdm_response_size, (void **)&spdm_response);
275 37 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
276 0 : libspdm_release_receiver_buffer (spdm_context);
277 0 : return status;
278 : }
279 :
280 37 : if (spdm_response->header.request_response_code == SPDM_ERROR) {
281 26 : status = libspdm_handle_error_response_main(
282 : spdm_context, &session_id,
283 : &spdm_response_size, (void **)&spdm_response,
284 : SPDM_KEY_UPDATE, SPDM_KEY_UPDATE_ACK);
285 26 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
286 25 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmVerifyKey[%x] Failed\n", session_id));
287 25 : libspdm_release_receiver_buffer (spdm_context);
288 25 : return status;
289 : }
290 : }
291 :
292 : /* this message can only be in secured session
293 : * thus don't need to consider transport layer padding, just check its exact size */
294 12 : if (spdm_response_size != sizeof(spdm_key_update_response_t)) {
295 0 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmVerifyKey[%x] Failed\n", session_id));
296 0 : libspdm_release_receiver_buffer (spdm_context);
297 0 : return LIBSPDM_STATUS_INVALID_MSG_SIZE;
298 : }
299 :
300 12 : if (spdm_response->header.spdm_version != spdm_request->header.spdm_version) {
301 0 : libspdm_release_receiver_buffer (spdm_context);
302 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
303 : }
304 :
305 12 : if ((spdm_response->header.request_response_code !=
306 11 : SPDM_KEY_UPDATE_ACK) ||
307 11 : (spdm_response->header.param1 != spdm_request->header.param1) ||
308 10 : (spdm_response->header.param2 != spdm_request->header.param2)) {
309 3 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmVerifyKey[%x] Failed\n", session_id));
310 3 : libspdm_release_receiver_buffer (spdm_context);
311 3 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
312 : }
313 9 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "SpdmVerifyKey[%x] Success\n", session_id));
314 :
315 : /* -=[Log Message Phase]=- */
316 : #if LIBSPDM_ENABLE_MSG_LOG
317 9 : libspdm_append_msg_log(spdm_context, spdm_response, spdm_response_size);
318 : #endif /* LIBSPDM_ENABLE_MSG_LOG */
319 :
320 9 : libspdm_release_receiver_buffer (spdm_context);
321 9 : return LIBSPDM_STATUS_SUCCESS;
322 : }
323 :
324 83 : libspdm_return_t libspdm_key_update(void *spdm_context, uint32_t session_id,
325 : bool single_direction)
326 : {
327 : libspdm_context_t *context;
328 : size_t retry;
329 : uint64_t retry_delay_time;
330 : libspdm_return_t status;
331 : bool key_updated;
332 :
333 83 : context = spdm_context;
334 83 : key_updated = false;
335 83 : context->crypto_request = true;
336 83 : retry = context->retry_times;
337 83 : retry_delay_time = context->retry_delay_time;
338 : do {
339 92 : status = libspdm_try_key_update(spdm_context, session_id,
340 : single_direction, &key_updated);
341 92 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
342 80 : return status;
343 : }
344 :
345 12 : libspdm_sleep(retry_delay_time);
346 12 : } while (retry-- != 0);
347 :
348 3 : return status;
349 : }
350 :
351 : #endif /* (LIBSPDM_ENABLE_CAPABILITY_KEY_EX_CAP) || (LIBSPDM_ENABLE_CAPABILITY_PSK_CAP) */
|