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