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