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_common_lib.h"
8 : #include "internal/libspdm_secured_message_lib.h"
9 :
10 189 : size_t libspdm_get_opaque_data_version_selection_data_size(const libspdm_context_t *spdm_context)
11 : {
12 : size_t size;
13 :
14 189 : if (spdm_context->local_context.secured_message_version.secured_message_version_count == 0) {
15 0 : return 0;
16 : }
17 :
18 189 : if (libspdm_get_connection_version (spdm_context) >= SPDM_MESSAGE_VERSION_12) {
19 28 : size = sizeof(spdm_general_opaque_data_table_header_t) +
20 : sizeof(secured_message_opaque_element_table_header_t) +
21 : sizeof(secured_message_opaque_element_version_selection_t);
22 : } else {
23 161 : size = sizeof(secured_message_general_opaque_data_table_header_t) +
24 : sizeof(secured_message_opaque_element_table_header_t) +
25 : sizeof(secured_message_opaque_element_version_selection_t);
26 : }
27 : /* Add Padding*/
28 189 : return (size + 3) & ~3;
29 : }
30 :
31 410 : size_t libspdm_get_opaque_data_supported_version_data_size(libspdm_context_t *spdm_context)
32 : {
33 : size_t size;
34 :
35 410 : if (spdm_context->local_context.secured_message_version.secured_message_version_count == 0) {
36 0 : return 0;
37 : }
38 :
39 410 : if (libspdm_get_connection_version (spdm_context) >= SPDM_MESSAGE_VERSION_12) {
40 48 : size = sizeof(spdm_general_opaque_data_table_header_t) +
41 : sizeof(secured_message_opaque_element_table_header_t) +
42 : sizeof(secured_message_opaque_element_supported_version_t) +
43 48 : sizeof(spdm_version_number_t) *
44 48 : spdm_context->local_context.secured_message_version.secured_message_version_count;
45 : } else {
46 362 : size = sizeof(secured_message_general_opaque_data_table_header_t) +
47 : sizeof(secured_message_opaque_element_table_header_t) +
48 : sizeof(secured_message_opaque_element_supported_version_t) +
49 362 : sizeof(spdm_version_number_t) *
50 362 : spdm_context->local_context.secured_message_version.secured_message_version_count;
51 : }
52 : /* Add Padding*/
53 410 : return (size + 3) & ~3;
54 : }
55 :
56 26 : size_t libspdm_get_untrusted_opaque_data_supported_version_data_size(
57 : libspdm_context_t *spdm_context, uint8_t version_count)
58 : {
59 : size_t size;
60 :
61 26 : if (libspdm_get_connection_version (spdm_context) >= SPDM_MESSAGE_VERSION_12) {
62 8 : size = sizeof(spdm_general_opaque_data_table_header_t) +
63 : sizeof(secured_message_opaque_element_table_header_t) +
64 : sizeof(secured_message_opaque_element_supported_version_t) +
65 8 : sizeof(spdm_version_number_t) * version_count;
66 : } else {
67 18 : size = sizeof(secured_message_general_opaque_data_table_header_t) +
68 : sizeof(secured_message_opaque_element_table_header_t) +
69 : sizeof(secured_message_opaque_element_supported_version_t) +
70 18 : sizeof(spdm_version_number_t) * version_count;
71 : }
72 : /* Add Padding*/
73 26 : return (size + 3) & ~3;
74 : }
75 :
76 189 : bool libspdm_get_element_from_opaque_data_with_element_id (libspdm_context_t *spdm_context,
77 : size_t data_in_size, const void *data_in,
78 : uint8_t element_id, uint8_t element_index,
79 : uint8_t *total_matched_element_cnt,
80 : const void **get_element_ptr, size_t *get_element_len)
81 : {
82 : const secured_message_general_opaque_data_table_header_t *general_opaque_data_table_header;
83 : const spdm_general_opaque_data_table_header_t *spdm_general_opaque_data_table_header;
84 : const opaque_element_table_header_t *opaque_element_table_header;
85 : uint16_t opaque_element_data_len;
86 :
87 : bool result;
88 : uint8_t element_num;
89 : uint8_t index;
90 : uint8_t matched_element_cnt;
91 : size_t data_element_size;
92 : size_t current_element_len;
93 : size_t total_element_len;
94 :
95 : /*check parameter in*/
96 189 : if (element_id > SPDM_REGISTRY_ID_MAX) {
97 0 : return false;
98 : }
99 189 : if ((data_in_size == 0) || (data_in == NULL)) {
100 0 : return false;
101 : }
102 :
103 189 : if (libspdm_get_connection_version (spdm_context) >= SPDM_MESSAGE_VERSION_12) {
104 72 : spdm_general_opaque_data_table_header = data_in;
105 72 : if (data_in_size < sizeof(spdm_general_opaque_data_table_header_t)) {
106 0 : return false;
107 : }
108 72 : if (spdm_general_opaque_data_table_header->total_elements < 1) {
109 0 : return false;
110 : }
111 72 : opaque_element_table_header = (const void *)(spdm_general_opaque_data_table_header + 1);
112 :
113 72 : element_num = spdm_general_opaque_data_table_header->total_elements;
114 :
115 72 : data_element_size = data_in_size - sizeof(spdm_general_opaque_data_table_header_t);
116 : } else {
117 117 : general_opaque_data_table_header = data_in;
118 117 : if (data_in_size < sizeof(secured_message_general_opaque_data_table_header_t)) {
119 0 : return false;
120 : }
121 117 : if ((general_opaque_data_table_header->spec_id != SECURED_MESSAGE_OPAQUE_DATA_SPEC_ID) ||
122 116 : (general_opaque_data_table_header->opaque_version != SECURED_MESSAGE_OPAQUE_VERSION) ||
123 116 : (general_opaque_data_table_header->total_elements < 1)) {
124 1 : return false;
125 : }
126 116 : opaque_element_table_header = (const void *)(general_opaque_data_table_header + 1);
127 :
128 116 : element_num = general_opaque_data_table_header->total_elements;
129 :
130 116 : data_element_size = data_in_size -
131 : sizeof(secured_message_general_opaque_data_table_header_t);
132 : }
133 :
134 188 : total_element_len = 0;
135 188 : result = false;
136 :
137 : /* find the Nth element with specific element_id, N = element_index. */
138 188 : matched_element_cnt = 0;
139 517 : for (index = 0; index < element_num; index++) {
140 : /*ensure the opaque_element_table_header is valid*/
141 333 : if (total_element_len + sizeof(opaque_element_table_header_t) >
142 : data_element_size) {
143 0 : return false;
144 : }
145 :
146 : /*check element header id*/
147 333 : if ((opaque_element_table_header->id > SPDM_REGISTRY_ID_MAX)) {
148 4 : return false;
149 : }
150 :
151 329 : if ((total_element_len + sizeof(opaque_element_table_header_t) +
152 329 : opaque_element_table_header->vendor_len + 2) > data_element_size) {
153 0 : return false;
154 : }
155 :
156 329 : opaque_element_data_len = libspdm_read_uint16(
157 329 : (const uint8_t *)opaque_element_table_header + sizeof(opaque_element_table_header_t) +
158 329 : opaque_element_table_header->vendor_len);
159 :
160 329 : current_element_len = sizeof(opaque_element_table_header_t) +
161 329 : opaque_element_table_header->vendor_len + 2 + opaque_element_data_len;
162 : /* Add Padding*/
163 329 : current_element_len = (current_element_len + 3) & ~3;
164 :
165 329 : total_element_len += current_element_len;
166 :
167 329 : if (data_element_size < total_element_len) {
168 0 : return false;
169 : }
170 :
171 329 : if (opaque_element_table_header->id == element_id) {
172 : /*get element by element id*/
173 222 : if (matched_element_cnt == element_index) {
174 188 : *get_element_ptr = opaque_element_table_header;
175 188 : *get_element_len = current_element_len;
176 188 : result = true;
177 : }
178 222 : matched_element_cnt += 1;
179 : }
180 :
181 : /*move to next element*/
182 329 : opaque_element_table_header = (const opaque_element_table_header_t *)
183 : ((const uint8_t *)opaque_element_table_header +
184 : current_element_len);
185 : }
186 :
187 : /*ensure data size is right*/
188 184 : if (data_element_size != total_element_len) {
189 0 : return false;
190 : }
191 :
192 184 : *total_matched_element_cnt = matched_element_cnt;
193 184 : return result;
194 : }
195 :
196 92 : bool libspdm_get_sm_data_element_from_opaque_data (libspdm_context_t *spdm_context,
197 : size_t data_in_size, const void *data_in,
198 : uint8_t sm_data_id,
199 : const void **get_element_ptr, size_t *get_element_len)
200 : {
201 : const opaque_element_table_header_t *opaque_element_table_header;
202 : size_t opaque_element_len;
203 : const secured_message_opaque_element_table_header_t *secured_message_element_table_header;
204 : const secured_message_opaque_element_header_t *secured_message_element_header;
205 : bool result;
206 : uint8_t element_index;
207 : uint8_t element_num;
208 : uint8_t total_matched_element_cnt;
209 :
210 : /*check parameter in*/
211 92 : if ((data_in_size == 0) || (data_in == NULL)) {
212 0 : return false;
213 : }
214 :
215 : /*get the total matched element count*/
216 92 : result = libspdm_get_element_from_opaque_data_with_element_id(
217 : spdm_context, data_in_size, data_in,
218 : SPDM_REGISTRY_ID_DMTF, 0, &total_matched_element_cnt,
219 : (const void **) &opaque_element_table_header, &opaque_element_len);
220 92 : if (!result) {
221 5 : return false;
222 : }
223 :
224 87 : element_num = total_matched_element_cnt;
225 108 : for (element_index = 0; element_index < element_num; element_index++) {
226 : /*get element by element id*/
227 97 : result = libspdm_get_element_from_opaque_data_with_element_id(
228 : spdm_context, data_in_size, data_in,
229 : SPDM_REGISTRY_ID_DMTF, element_index, &total_matched_element_cnt,
230 : (const void **) &opaque_element_table_header, &opaque_element_len);
231 97 : if (!result) {
232 0 : return false;
233 : }
234 :
235 97 : secured_message_element_table_header = (const void *)opaque_element_table_header;
236 97 : if (secured_message_element_table_header->vendor_len == 0) {
237 97 : secured_message_element_header =
238 : (const void *)(secured_message_element_table_header + 1);
239 97 : if ((const uint8_t *)secured_message_element_header +
240 : sizeof(secured_message_opaque_element_header_t) >
241 97 : (const uint8_t *)data_in + data_in_size) {
242 0 : return false;
243 : }
244 :
245 97 : if ((secured_message_element_header->sm_data_id == sm_data_id) &&
246 76 : (secured_message_element_header->sm_data_version ==
247 : SECURED_MESSAGE_OPAQUE_ELEMENT_SMDATA_DATA_VERSION)) {
248 76 : *get_element_ptr = opaque_element_table_header;
249 76 : *get_element_len = opaque_element_len;
250 76 : return true;
251 : }
252 : }
253 : }
254 :
255 11 : return false;
256 : }
257 :
258 76 : bool libspdm_process_general_opaque_data_check(libspdm_context_t *spdm_context,
259 : size_t data_in_size,
260 : const void *data_in)
261 : {
262 : const spdm_general_opaque_data_table_header_t
263 : *spdm_general_opaque_data_table_header;
264 : const opaque_element_table_header_t
265 : *opaque_element_table_header;
266 : uint8_t element_num;
267 : uint8_t element_index;
268 : uint16_t opaque_element_data_len;
269 : size_t data_element_size;
270 : size_t current_element_len;
271 : size_t total_element_len;
272 76 : uint8_t zero_padding[4] = {0};
273 :
274 76 : total_element_len = 0;
275 :
276 76 : LIBSPDM_ASSERT(data_in_size <= SPDM_MAX_OPAQUE_DATA_SIZE);
277 :
278 76 : if (libspdm_get_connection_version(spdm_context) >= SPDM_MESSAGE_VERSION_12) {
279 19 : if ((spdm_context->connection_info.algorithm.other_params_support &
280 : SPDM_ALGORITHMS_OPAQUE_DATA_FORMAT_MASK) == SPDM_ALGORITHMS_OPAQUE_DATA_FORMAT_1) {
281 : /* Check byte alignment */
282 15 : if ((data_in_size & 3) != 0) {
283 0 : return false;
284 : }
285 :
286 15 : spdm_general_opaque_data_table_header = data_in;
287 15 : if (data_in_size < sizeof(spdm_general_opaque_data_table_header_t)) {
288 0 : return false;
289 : }
290 15 : if (spdm_general_opaque_data_table_header->total_elements < 1) {
291 0 : return false;
292 : }
293 15 : opaque_element_table_header = (const void *)(spdm_general_opaque_data_table_header + 1);
294 :
295 15 : element_num = spdm_general_opaque_data_table_header->total_elements;
296 :
297 15 : data_element_size = data_in_size - sizeof(spdm_general_opaque_data_table_header_t);
298 :
299 33 : for (element_index = 0; element_index < element_num; element_index++) {
300 : /*ensure the opaque_element_table_header is valid*/
301 19 : if (total_element_len + sizeof(opaque_element_table_header_t) +
302 : sizeof(opaque_element_data_len) >
303 : data_element_size) {
304 0 : return false;
305 : }
306 :
307 : /*check element header id*/
308 19 : if (opaque_element_table_header->id > SPDM_REGISTRY_ID_MAX) {
309 0 : return false;
310 : }
311 :
312 : /*ensure the vendor_id and opaque_element_data_len field are within the buffer*/
313 19 : if (total_element_len + sizeof(opaque_element_table_header_t) +
314 19 : opaque_element_table_header->vendor_len +
315 : sizeof(opaque_element_data_len) >
316 : data_element_size) {
317 0 : return false;
318 : }
319 :
320 19 : opaque_element_data_len = libspdm_read_uint16(
321 19 : (const uint8_t *)(opaque_element_table_header + 1) +
322 19 : opaque_element_table_header->vendor_len);
323 :
324 19 : current_element_len = sizeof(opaque_element_table_header_t) +
325 19 : opaque_element_table_header->vendor_len +
326 19 : sizeof(opaque_element_data_len) +
327 : opaque_element_data_len;
328 :
329 : /*ensure the element with padding is within the buffer before reading it*/
330 19 : if (total_element_len + ((current_element_len + 3) & ~3) >
331 : data_element_size) {
332 0 : return false;
333 : }
334 :
335 19 : if ((current_element_len & 3) != 0) {
336 13 : if (!libspdm_consttime_is_mem_equal(zero_padding,
337 : (uint8_t *)(size_t)
338 : (opaque_element_table_header) +
339 : current_element_len,
340 13 : 4 - (current_element_len & 3))) {
341 1 : return false;
342 : }
343 : }
344 : /* Add Padding*/
345 18 : current_element_len = (current_element_len + 3) & ~3;
346 :
347 18 : total_element_len += current_element_len;
348 :
349 : /*move to next element*/
350 18 : opaque_element_table_header =
351 : (const opaque_element_table_header_t *)
352 : ((const uint8_t *)opaque_element_table_header +
353 : current_element_len);
354 : }
355 : }
356 : }
357 :
358 75 : return true;
359 : }
360 :
361 : /**
362 : * Process opaque data version selection.
363 : *
364 : * This function should be called in KEY_EXCHANGE/PSK_EXCHANGE response parsing in requester.
365 : *
366 : * @param data_in_size size in bytes of the data_in.
367 : * @param data_in A pointer to the buffer to store the opaque data version selection.
368 : **/
369 44 : libspdm_return_t libspdm_process_opaque_data_version_selection_data(
370 : libspdm_context_t *spdm_context, size_t data_in_size, void *data_in,
371 : spdm_version_number_t *secured_message_version)
372 : {
373 : const secured_message_opaque_element_table_header_t *opaque_element_table_header;
374 : const secured_message_opaque_element_version_selection_t *opaque_element_version_section;
375 :
376 : bool result;
377 : uint8_t secured_message_version_index;
378 : const void *get_element_ptr;
379 : size_t get_element_len;
380 :
381 44 : result = false;
382 44 : get_element_ptr = NULL;
383 :
384 44 : if (spdm_context->local_context.secured_message_version.secured_message_version_count == 0) {
385 0 : return LIBSPDM_STATUS_SUCCESS;
386 : }
387 :
388 44 : result = libspdm_get_sm_data_element_from_opaque_data(
389 : spdm_context, data_in_size, data_in,
390 : SECURED_MESSAGE_OPAQUE_ELEMENT_SMDATA_ID_VERSION_SELECTION,
391 : &get_element_ptr, &get_element_len);
392 44 : if ((!result) || (get_element_ptr == NULL)) {
393 3 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO,"get element error!\n"));
394 3 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
395 : }
396 :
397 41 : opaque_element_table_header = (const secured_message_opaque_element_table_header_t*)
398 : get_element_ptr;
399 :
400 : /* Check for selection version data. */
401 41 : if ((opaque_element_table_header->vendor_len != 0) ||
402 41 : (opaque_element_table_header->opaque_element_data_len !=
403 : sizeof(secured_message_opaque_element_version_selection_t))) {
404 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
405 : }
406 41 : opaque_element_version_section = (const void *)(opaque_element_table_header + 1);
407 :
408 41 : if ((const uint8_t *)opaque_element_version_section +
409 : sizeof(secured_message_opaque_element_version_selection_t) >
410 41 : (const uint8_t *)opaque_element_table_header + get_element_len) {
411 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
412 : }
413 :
414 41 : for (secured_message_version_index = 0;
415 46 : secured_message_version_index <
416 46 : spdm_context->local_context.secured_message_version.secured_message_version_count;
417 5 : secured_message_version_index++) {
418 92 : if (libspdm_get_version_from_version_number(opaque_element_version_section->
419 : selected_version)
420 : ==
421 46 : libspdm_get_version_from_version_number(
422 46 : spdm_context->local_context.secured_message_version.secured_message_version[
423 : secured_message_version_index])) {
424 41 : libspdm_copy_mem(secured_message_version,
425 : sizeof(spdm_version_number_t),
426 41 : &(opaque_element_version_section->selected_version),
427 : sizeof(spdm_version_number_t));
428 41 : return LIBSPDM_STATUS_SUCCESS;
429 : }
430 : }
431 :
432 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
433 : }
434 :
435 : /* Return the highest secured message version this endpoint offers in its local supported list, in
436 : * version-number form (e.g. SECURED_SPDM_VERSION_13 << SPDM_VERSION_NUMBER_SHIFT_BIT), or 0 if the
437 : * list is empty. The Requester uses this as the "version" to gate the AEADlimitOE element in its
438 : * request, since no version has been negotiated yet at that point. */
439 134 : spdm_version_number_t libspdm_local_max_secured_message_version(
440 : const libspdm_context_t *spdm_context)
441 : {
442 : uint8_t index;
443 : spdm_version_number_t max_version;
444 :
445 134 : max_version = 0;
446 134 : for (index = 0;
447 277 : index < spdm_context->local_context.secured_message_version.secured_message_version_count;
448 143 : index++) {
449 143 : spdm_version_number_t version =
450 143 : spdm_context->local_context.secured_message_version.secured_message_version[index];
451 286 : if (libspdm_get_version_from_version_number(version) >
452 143 : libspdm_get_version_from_version_number(max_version)) {
453 143 : max_version = version;
454 : }
455 : }
456 134 : return max_version;
457 : }
458 :
459 : /* DSP0277 1.3: derive the AeadLimitExponent that this endpoint advertises from its single source of
460 : * truth, max_spdm_session_sequence_number. That value is the maximum sequence number that IS allowed
461 : * (seq > max triggers overflow), i.e. AeadLimit - 1, so the AEAD limit is
462 : * AeadLimit = max + 1 = 2 ^ exponent (see libspdm_apply_aead_limit_to_session). The advertised
463 : * exponent is therefore floor(log2(max + 1)): the largest e such that 2 ^ e <= max + 1. The all-ones
464 : * cap maps to 64 (the spec default, since 2^64 is not representable). A cap that is not of the form
465 : * 2 ^ e - 1 rounds the exponent down to the nearest representable AEAD limit, which is <= the
466 : * configured cap (the safe direction). */
467 39 : static uint8_t libspdm_aead_limit_exponent_from_max_sequence_number(uint64_t max_sequence_number)
468 : {
469 : uint8_t exponent;
470 : uint64_t aead_limit;
471 :
472 : /* All-ones is the maximum allowed value for the default AeadLimit of 2^64, which is not
473 : * representable in a uint64_t; adding 1 would overflow, so handle it directly. */
474 39 : if (max_sequence_number >= LIBSPDM_MAX_SPDM_SESSION_SEQUENCE_NUMBER) {
475 30 : return SECURED_MESSAGE_AEAD_LIMIT_EXPONENT_MAX;
476 : }
477 :
478 9 : aead_limit = max_sequence_number + 1;
479 9 : exponent = 0;
480 230 : while (aead_limit > 1) {
481 221 : aead_limit >>= 1;
482 221 : exponent++;
483 : }
484 9 : return exponent;
485 : }
486 :
487 325 : size_t libspdm_get_opaque_data_aead_limit_element_size(const libspdm_context_t *spdm_context,
488 : spdm_version_number_t secured_message_version)
489 : {
490 : size_t size;
491 :
492 : /* The AEADlimitOE element is only defined for secured message version 1.3 and later. It must
493 : * not be appended to opaque data for an older version. */
494 325 : if (libspdm_get_version_from_version_number(secured_message_version) < SECURED_SPDM_VERSION_13) {
495 285 : return 0;
496 : }
497 :
498 40 : size = sizeof(secured_message_opaque_element_table_header_t) +
499 : sizeof(secured_message_opaque_element_aead_limit_t);
500 : /* Add Padding*/
501 40 : return (size + 3) & ~3;
502 : }
503 :
504 : /* Walk a well-formed opaque data blob (general opaque data table header followed by total_elements
505 : * elements) and return its current length in bytes. data_out_capacity bounds the walk. Returns
506 : * false if the blob does not fit within data_out_capacity. */
507 162 : static bool libspdm_get_opaque_data_current_size(libspdm_context_t *spdm_context,
508 : const void *data_out, size_t data_out_capacity,
509 : size_t *current_size)
510 : {
511 : const secured_message_general_opaque_data_table_header_t *general_opaque_data_table_header;
512 : const spdm_general_opaque_data_table_header_t *spdm_general_opaque_data_table_header;
513 : const opaque_element_table_header_t *opaque_element_table_header;
514 : uint8_t element_num;
515 : uint8_t index;
516 : uint16_t opaque_element_data_len;
517 : size_t total_size;
518 : size_t current_element_len;
519 :
520 162 : if (libspdm_get_connection_version(spdm_context) >= SPDM_MESSAGE_VERSION_12) {
521 18 : if (data_out_capacity < sizeof(spdm_general_opaque_data_table_header_t)) {
522 0 : return false;
523 : }
524 18 : spdm_general_opaque_data_table_header = data_out;
525 18 : element_num = spdm_general_opaque_data_table_header->total_elements;
526 18 : opaque_element_table_header = (const void *)(spdm_general_opaque_data_table_header + 1);
527 18 : total_size = sizeof(spdm_general_opaque_data_table_header_t);
528 : } else {
529 144 : if (data_out_capacity < sizeof(secured_message_general_opaque_data_table_header_t)) {
530 0 : return false;
531 : }
532 144 : general_opaque_data_table_header = data_out;
533 144 : element_num = general_opaque_data_table_header->total_elements;
534 144 : opaque_element_table_header = (const void *)(general_opaque_data_table_header + 1);
535 144 : total_size = sizeof(secured_message_general_opaque_data_table_header_t);
536 : }
537 :
538 324 : for (index = 0; index < element_num; index++) {
539 162 : if (total_size + sizeof(opaque_element_table_header_t) +
540 162 : opaque_element_table_header->vendor_len + sizeof(uint16_t) > data_out_capacity) {
541 0 : return false;
542 : }
543 162 : opaque_element_data_len = libspdm_read_uint16(
544 162 : (const uint8_t *)opaque_element_table_header + sizeof(opaque_element_table_header_t) +
545 162 : opaque_element_table_header->vendor_len);
546 162 : current_element_len = sizeof(opaque_element_table_header_t) +
547 162 : opaque_element_table_header->vendor_len + sizeof(uint16_t) +
548 : opaque_element_data_len;
549 : /* Add Padding*/
550 162 : current_element_len = (current_element_len + 3) & ~3;
551 162 : if (total_size + current_element_len > data_out_capacity) {
552 0 : return false;
553 : }
554 162 : total_size += current_element_len;
555 162 : opaque_element_table_header = (const opaque_element_table_header_t *)
556 : ((const uint8_t *)opaque_element_table_header +
557 : current_element_len);
558 : }
559 :
560 162 : *current_size = total_size;
561 162 : return true;
562 : }
563 :
564 162 : void libspdm_build_opaque_data_aead_limit_element(libspdm_context_t *spdm_context,
565 : spdm_version_number_t secured_message_version,
566 : size_t *data_out_size, void *data_out)
567 : {
568 : size_t data_out_capacity;
569 : size_t existing_size;
570 : size_t element_size;
571 : secured_message_opaque_element_table_header_t *opaque_element_table_header;
572 : secured_message_opaque_element_aead_limit_t *opaque_element_aead_limit;
573 : uint8_t *element_ptr;
574 : bool result;
575 :
576 : /* data_out already holds a blob built by the supported-version / version-selection builder.
577 : * *data_out_size is the capacity of data_out on input. Parse the blob to find where it ends;
578 : * that is the append offset. */
579 162 : data_out_capacity = *data_out_size;
580 162 : result = libspdm_get_opaque_data_current_size(spdm_context, data_out, data_out_capacity,
581 : &existing_size);
582 162 : LIBSPDM_ASSERT(result);
583 162 : if (!result) {
584 141 : return;
585 : }
586 :
587 : /* The size helper enforces the secured message version 1.3 gate; for an older version it
588 : * returns 0 and nothing is appended. */
589 162 : element_size = libspdm_get_opaque_data_aead_limit_element_size(spdm_context,
590 : secured_message_version);
591 162 : if (element_size == 0) {
592 141 : *data_out_size = existing_size;
593 141 : return;
594 : }
595 :
596 21 : LIBSPDM_ASSERT(existing_size + element_size <= data_out_capacity);
597 :
598 21 : if (libspdm_get_connection_version(spdm_context) >= SPDM_MESSAGE_VERSION_12) {
599 : spdm_general_opaque_data_table_header_t *spdm_general_opaque_data_table_header;
600 10 : spdm_general_opaque_data_table_header = data_out;
601 10 : spdm_general_opaque_data_table_header->total_elements++;
602 : } else {
603 : secured_message_general_opaque_data_table_header_t *general_opaque_data_table_header;
604 11 : general_opaque_data_table_header = data_out;
605 11 : general_opaque_data_table_header->total_elements++;
606 : }
607 :
608 21 : element_ptr = (uint8_t *)data_out + existing_size;
609 21 : opaque_element_table_header = (void *)element_ptr;
610 21 : opaque_element_table_header->id = SPDM_REGISTRY_ID_DMTF;
611 21 : opaque_element_table_header->vendor_len = 0;
612 21 : opaque_element_table_header->opaque_element_data_len =
613 : sizeof(secured_message_opaque_element_aead_limit_t);
614 :
615 21 : opaque_element_aead_limit = (void *)(opaque_element_table_header + 1);
616 21 : opaque_element_aead_limit->sm_data_version =
617 : SECURED_MESSAGE_OPAQUE_ELEMENT_SMDATA_DATA_VERSION;
618 21 : opaque_element_aead_limit->sm_data_id =
619 : SECURED_MESSAGE_OPAQUE_ELEMENT_SMDATA_ID_AEAD_LIMIT;
620 21 : opaque_element_aead_limit->aead_limit_exponent =
621 21 : libspdm_aead_limit_exponent_from_max_sequence_number(
622 : spdm_context->max_spdm_session_sequence_number);
623 :
624 : /* Zero the padding bytes between the element and the new end. */
625 21 : libspdm_zero_mem(element_ptr + sizeof(secured_message_opaque_element_table_header_t) +
626 : sizeof(secured_message_opaque_element_aead_limit_t),
627 : element_size - (sizeof(secured_message_opaque_element_table_header_t) +
628 : sizeof(secured_message_opaque_element_aead_limit_t)));
629 :
630 21 : *data_out_size = existing_size + element_size;
631 : }
632 :
633 71 : libspdm_return_t libspdm_process_opaque_data_aead_limit(libspdm_context_t *spdm_context,
634 : spdm_version_number_t
635 : secured_message_version,
636 : size_t data_in_size, const void *data_in,
637 : uint8_t *aead_limit_exponent)
638 : {
639 : bool result;
640 : const void *get_element_ptr;
641 : size_t get_element_len;
642 : const secured_message_opaque_element_table_header_t *opaque_element_table_header;
643 : const secured_message_opaque_element_aead_limit_t *opaque_element_aead_limit;
644 :
645 : /* Default per DSP0277 1.3: when the AEAD limit element is absent, the exponent is 64. */
646 71 : *aead_limit_exponent = SECURED_MESSAGE_AEAD_LIMIT_EXPONENT_DEFAULT;
647 :
648 : /* The AEADlimitOE element is only defined for secured message version 1.3 and later. For an
649 : * older negotiated version, ignore the element entirely (keep the default exponent) even if a
650 : * peer erroneously included it. */
651 71 : if (libspdm_get_version_from_version_number(secured_message_version) <
652 : SECURED_SPDM_VERSION_13) {
653 51 : return LIBSPDM_STATUS_SUCCESS;
654 : }
655 :
656 20 : get_element_ptr = NULL;
657 20 : result = libspdm_get_sm_data_element_from_opaque_data(
658 : spdm_context, data_in_size, data_in,
659 : SECURED_MESSAGE_OPAQUE_ELEMENT_SMDATA_ID_AEAD_LIMIT,
660 : &get_element_ptr, &get_element_len);
661 20 : if ((!result) || (get_element_ptr == NULL)) {
662 : /* Absent is allowed: keep the default exponent. */
663 11 : return LIBSPDM_STATUS_SUCCESS;
664 : }
665 :
666 9 : opaque_element_table_header = (const secured_message_opaque_element_table_header_t *)
667 : get_element_ptr;
668 9 : if ((opaque_element_table_header->vendor_len != 0) ||
669 9 : (opaque_element_table_header->opaque_element_data_len !=
670 : sizeof(secured_message_opaque_element_aead_limit_t))) {
671 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
672 : }
673 :
674 9 : opaque_element_aead_limit = (const void *)(opaque_element_table_header + 1);
675 9 : if ((const uint8_t *)opaque_element_aead_limit +
676 : sizeof(secured_message_opaque_element_aead_limit_t) >
677 9 : (const uint8_t *)opaque_element_table_header + get_element_len) {
678 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
679 : }
680 :
681 : /* AeadLimitExponent shall be <= 64. */
682 9 : if (opaque_element_aead_limit->aead_limit_exponent >
683 : SECURED_MESSAGE_AEAD_LIMIT_EXPONENT_MAX) {
684 1 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
685 : }
686 :
687 8 : *aead_limit_exponent = opaque_element_aead_limit->aead_limit_exponent;
688 8 : return LIBSPDM_STATUS_SUCCESS;
689 : }
690 :
691 18 : void libspdm_apply_aead_limit_to_session(libspdm_context_t *spdm_context,
692 : void *session_info_p,
693 : uint8_t peer_aead_limit_exponent)
694 : {
695 : libspdm_session_info_t *session_info;
696 : uint8_t local_aead_limit_exponent;
697 : uint64_t local_max_sequence_number;
698 : uint64_t peer_max_sequence_number;
699 : uint64_t effective_max;
700 :
701 18 : session_info = session_info_p;
702 :
703 : /* The caller invokes this only when the session negotiated secured message version >= 1.3.
704 : *
705 : * The DSP0277 AeadLimit is the first sequence number that is NOT allowed, so the maximum
706 : * allowed sequence number is AeadLimit - 1 = 2 ^ AeadLimitExponent - 1. Fold together this
707 : * endpoint's advertised limit and the peer's advertised limit; the effective cap is the
708 : * smaller of the two, so neither side's limit is ever raised by the other.
709 : *
710 : * This endpoint's contribution must be the SAME rounded-down limit it advertised on the wire,
711 : * not the raw configured max_spdm_session_sequence_number. When the configured cap is not of
712 : * the form 2 ^ e - 1 the advertised exponent is floor(log2(cap + 1)); enforcing the raw cap
713 : * here would let this side send past the limit the peer derives from that exponent. At
714 : * exponent 64 the maximum allowed value is 2^64 - 1, i.e. LIBSPDM_MAX_SPDM_SESSION_SEQUENCE_NUMBER
715 : * (all-ones), which is also the default. */
716 18 : local_aead_limit_exponent = libspdm_aead_limit_exponent_from_max_sequence_number(
717 : spdm_context->max_spdm_session_sequence_number);
718 18 : if (local_aead_limit_exponent >= 64) {
719 14 : local_max_sequence_number = LIBSPDM_MAX_SPDM_SESSION_SEQUENCE_NUMBER;
720 : } else {
721 4 : local_max_sequence_number = ((uint64_t)1 << local_aead_limit_exponent) - 1;
722 : }
723 :
724 18 : if (peer_aead_limit_exponent >= 64) {
725 12 : peer_max_sequence_number = LIBSPDM_MAX_SPDM_SESSION_SEQUENCE_NUMBER;
726 : } else {
727 6 : peer_max_sequence_number = ((uint64_t)1 << peer_aead_limit_exponent) - 1;
728 : }
729 :
730 18 : effective_max = local_max_sequence_number;
731 18 : if (peer_max_sequence_number < effective_max) {
732 4 : effective_max = peer_max_sequence_number;
733 : }
734 :
735 18 : libspdm_secured_message_set_max_spdm_session_sequence_number(
736 : session_info->secured_message_context, effective_max);
737 18 : }
|