Branch data 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_responder_lib.h"
8 : :
9 : : #if (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && (LIBSPDM_SEND_GET_CERTIFICATE_SUPPORT)
10 : :
11 : 13 : libspdm_return_t libspdm_get_encap_request_get_certificate(void *context,
12 : : const uint32_t *session_id,
13 : : uint8_t req_slot_id,
14 : : size_t cert_chain_size,
15 : : void *cert_chain,
16 : : size_t *encap_request_size,
17 : : void *encap_request)
18 : : {
19 : : libspdm_context_t *spdm_context;
20 : : libspdm_encap_context_t *encap_context;
21 : :
22 : 13 : spdm_context = context;
23 : :
24 [ + + + + ]: 13 : if ((cert_chain == NULL) || (cert_chain_size == 0)) {
25 : 2 : return LIBSPDM_STATUS_INVALID_PARAMETER;
26 : : }
27 : :
28 [ + + ]: 11 : if (req_slot_id >= SPDM_MAX_SLOT_COUNT) {
29 : : /* The slot indexes per-slot state of SPDM_MAX_SLOT_COUNT entries, in which the
30 : : * Requester's certificate chain is recorded once retrieved. GET_CERTIFICATE has no slot
31 : : * value that designates a provisioned public key. */
32 : 3 : return LIBSPDM_STATUS_INVALID_PARAMETER;
33 : : }
34 : :
35 : 8 : encap_context = libspdm_get_encap_context(spdm_context, session_id);
36 [ - + ]: 8 : if (encap_context == NULL) {
37 : : /* session_id does not refer to an existing session. */
38 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
39 : : }
40 : :
41 : : /* This starts a fresh retrieval of the Requester's certificate chain. libspdm issues the
42 : : * remaining requests itself, accumulating into this buffer. */
43 : 8 : encap_context->payload_buffer = cert_chain;
44 : 8 : encap_context->payload_buffer_max_size = cert_chain_size;
45 : 8 : encap_context->payload_buffer_size = 0;
46 : :
47 : 8 : return libspdm_get_encap_request_get_certificate_continue(
48 : : spdm_context, session_id, req_slot_id, encap_request_size, encap_request);
49 : : }
50 : :
51 : 12 : libspdm_return_t libspdm_get_encap_request_get_certificate_continue(
52 : : libspdm_context_t *context,
53 : : const uint32_t *session_id,
54 : : uint8_t req_slot_id,
55 : : size_t *encap_request_size,
56 : : void *encap_request)
57 : : {
58 : : libspdm_encap_context_t *encap_context;
59 : : libspdm_context_t *spdm_context;
60 : : spdm_get_certificate_large_request_t *spdm_request;
61 : : libspdm_return_t status;
62 : : uint32_t req_msg_length;
63 : : uint32_t req_msg_offset;
64 : : bool use_large_cert_chain;
65 : : uint32_t req_msg_header_size;
66 : : uint32_t rsp_msg_header_size;
67 : :
68 : 12 : spdm_context = context;
69 : :
70 : 12 : encap_context = libspdm_get_encap_context(spdm_context, session_id);
71 [ - + ]: 12 : if (encap_context == NULL) {
72 : : /* session_id does not refer to an existing session. */
73 : 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
74 : : }
75 : :
76 : 12 : encap_context->last_encap_request_size = 0;
77 : :
78 [ + + ]: 12 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_11) {
79 : 1 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
80 : : }
81 : :
82 [ - + ]: 11 : if (!libspdm_is_capabilities_flag_supported(
83 : : spdm_context, false,
84 : : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CERT_CAP, 0)) {
85 : 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
86 : : }
87 : :
88 [ + + + - ]: 13 : if ((libspdm_get_connection_version (spdm_context) >= SPDM_MESSAGE_VERSION_14) &&
89 : 2 : libspdm_is_capabilities_flag_supported(
90 : : spdm_context, false,
91 : : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_LARGE_RESP_CAP, 0)) {
92 : 2 : use_large_cert_chain = true;
93 : 2 : encap_context->use_large_cert_chain = true;
94 : : } else {
95 : 9 : use_large_cert_chain = false;
96 : 9 : encap_context->use_large_cert_chain = false;
97 : : }
98 : :
99 [ + + ]: 11 : if (use_large_cert_chain) {
100 : 2 : req_msg_header_size = sizeof(spdm_get_certificate_large_request_t);
101 : 2 : rsp_msg_header_size = sizeof(spdm_certificate_large_response_t);
102 : : } else {
103 : 9 : req_msg_header_size = sizeof(spdm_get_certificate_request_t);
104 : 9 : rsp_msg_header_size = sizeof(spdm_certificate_response_t);
105 : : }
106 : :
107 [ - + ]: 11 : LIBSPDM_ASSERT(*encap_request_size >= req_msg_header_size);
108 : 11 : *encap_request_size = req_msg_header_size;
109 : :
110 : 11 : spdm_request = encap_request;
111 : :
112 : 11 : spdm_request->header.spdm_version = libspdm_get_connection_version (spdm_context);
113 : 11 : spdm_request->header.request_response_code = SPDM_GET_CERTIFICATE;
114 : 11 : spdm_request->header.param1 = req_slot_id;
115 : 11 : spdm_request->header.param2 = 0;
116 : 11 : req_msg_offset = (uint32_t)encap_context->payload_buffer_size;
117 : :
118 : : /* certificate response is encapsulate in deliver encapsulated response msg */
119 : 11 : req_msg_length = spdm_context->local_context.capability.max_spdm_msg_size -
120 : 11 : sizeof(spdm_deliver_encapsulated_response_request_t) -
121 : : rsp_msg_header_size;
122 : :
123 [ + + ]: 11 : if (use_large_cert_chain) {
124 : 2 : spdm_request->header.param1 |= SPDM_GET_CERTIFICATE_REQUEST_LARGE_CERT_CHAIN;
125 : 2 : spdm_request->offset = 0;
126 : 2 : spdm_request->length = 0;
127 : 2 : spdm_request->large_offset = req_msg_offset;
128 : 2 : spdm_request->large_length = req_msg_length;
129 : : } else {
130 : 9 : req_msg_length = LIBSPDM_MIN(req_msg_length, SPDM_MAX_CERTIFICATE_CHAIN_SIZE);
131 : 9 : spdm_request->offset = (uint16_t)req_msg_offset;
132 : 9 : spdm_request->length = (uint16_t)req_msg_length;
133 : : }
134 : 11 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "request (offset 0x%x, size 0x%x):\n",
135 : : req_msg_offset, req_msg_length));
136 : :
137 : : /* Cache data*/
138 : 11 : status = libspdm_append_message_mut_b(spdm_context, spdm_request, *encap_request_size);
139 [ - + ]: 11 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
140 : 0 : return LIBSPDM_STATUS_BUFFER_FULL;
141 : : }
142 : :
143 : 11 : encap_context->req_slot_id = req_slot_id;
144 : 11 : libspdm_copy_mem(&encap_context->last_encap_request_header,
145 : : sizeof(encap_context->last_encap_request_header),
146 : 11 : &spdm_request->header, sizeof(spdm_message_header_t));
147 : 11 : encap_context->last_encap_request_size = *encap_request_size;
148 : :
149 : 11 : return LIBSPDM_STATUS_SUCCESS;
150 : : }
151 : :
152 : 26 : libspdm_return_t libspdm_process_encap_response_certificate(
153 : : libspdm_context_t *spdm_context, size_t encap_response_size,
154 : : const void *encap_response, bool *need_continue)
155 : : {
156 : : libspdm_encap_context_t *encap_context;
157 : : const spdm_certificate_large_response_t *spdm_response;
158 : : size_t spdm_response_size;
159 : : bool result;
160 : : libspdm_return_t status;
161 : : uint32_t request_offset;
162 : : uint8_t slot_id;
163 : : uint8_t *cert_chain_buffer;
164 : : size_t cert_chain_buffer_size;
165 : : size_t cert_chain_buffer_max_size;
166 : : uint8_t cert_model;
167 : : uint32_t rsp_msg_portion_length;
168 : : uint32_t rsp_msg_remainder_length;
169 : : bool use_large_cert_chain;
170 : : uint32_t rsp_msg_header_size;
171 : : uint32_t max_cert_chain_size;
172 : : uint32_t req_msg_length;
173 : :
174 : 26 : spdm_response = encap_response;
175 : 26 : spdm_response_size = encap_response_size;
176 : :
177 [ - + ]: 26 : if (spdm_response_size < sizeof(spdm_message_header_t)) {
178 : 0 : return LIBSPDM_STATUS_INVALID_MSG_SIZE;
179 : : }
180 [ - + ]: 26 : if (spdm_response->header.spdm_version != libspdm_get_connection_version (spdm_context)) {
181 : 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
182 : : }
183 [ + + ]: 26 : if (spdm_response->header.request_response_code == SPDM_ERROR) {
184 : 2 : status = libspdm_handle_encap_error_response_main(spdm_response->header.param1);
185 [ + - ]: 2 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
186 : 2 : return status;
187 : : }
188 [ - + ]: 24 : } else if (spdm_response->header.request_response_code != SPDM_CERTIFICATE) {
189 : 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
190 : : }
191 : 24 : encap_context = libspdm_get_encap_context_via_last_request(spdm_context);
192 : :
193 : 24 : cert_chain_buffer = (uint8_t *)encap_context->payload_buffer;
194 : 24 : cert_chain_buffer_size = encap_context->payload_buffer_size;
195 : 24 : cert_chain_buffer_max_size = encap_context->payload_buffer_max_size;
196 : :
197 : 24 : use_large_cert_chain = encap_context->use_large_cert_chain;
198 [ + + ]: 24 : if (use_large_cert_chain) {
199 [ + + ]: 3 : if ((spdm_response->header.param1 & SPDM_CERTIFICATE_RESPONSE_LARGE_CERT_CHAIN) == 0) {
200 : 1 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
201 : : }
202 : : } else {
203 [ + + ]: 21 : if ((spdm_response->header.spdm_version >= SPDM_MESSAGE_VERSION_14) &&
204 [ + - ]: 1 : ((spdm_response->header.param1 & SPDM_CERTIFICATE_RESPONSE_LARGE_CERT_CHAIN) != 0)) {
205 : 1 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
206 : : }
207 : : }
208 : :
209 [ + + ]: 22 : if (use_large_cert_chain) {
210 : 2 : max_cert_chain_size = SPDM_MAX_CERTIFICATE_CHAIN_SIZE_14;
211 : 2 : rsp_msg_header_size = sizeof(spdm_certificate_large_response_t);
212 : : } else {
213 : 20 : max_cert_chain_size = SPDM_MAX_CERTIFICATE_CHAIN_SIZE;
214 : 20 : rsp_msg_header_size = sizeof(spdm_certificate_response_t);
215 : : }
216 : :
217 : : /* certificate response is encapsulate in deliver encapsulated response msg */
218 : 22 : req_msg_length = spdm_context->local_context.capability.max_spdm_msg_size -
219 : 22 : sizeof(spdm_deliver_encapsulated_response_request_t) -
220 : : rsp_msg_header_size;
221 : :
222 [ + + ]: 22 : if (!use_large_cert_chain) {
223 : 20 : req_msg_length = LIBSPDM_MIN(req_msg_length, SPDM_MAX_CERTIFICATE_CHAIN_SIZE);
224 : : }
225 : :
226 [ - + ]: 22 : if (encap_response_size < rsp_msg_header_size) {
227 : 0 : return LIBSPDM_STATUS_INVALID_MSG_SIZE;
228 : : }
229 [ + + ]: 22 : if (use_large_cert_chain) {
230 : 2 : rsp_msg_portion_length = spdm_response->large_portion_length;
231 : 2 : rsp_msg_remainder_length = spdm_response->large_remainder_length;
232 : : } else {
233 : 20 : rsp_msg_portion_length = spdm_response->portion_length;
234 : 20 : rsp_msg_remainder_length = spdm_response->remainder_length;
235 : : }
236 : :
237 [ + + + + ]: 22 : if ((rsp_msg_portion_length > req_msg_length) ||
238 : : (rsp_msg_portion_length == 0)) {
239 : 3 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
240 : : }
241 : :
242 : 19 : request_offset = (uint32_t)cert_chain_buffer_size;
243 : :
244 [ - + ]: 19 : if (rsp_msg_portion_length > max_cert_chain_size - request_offset) {
245 : 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
246 : : }
247 [ - + ]: 19 : if (rsp_msg_remainder_length > max_cert_chain_size - request_offset - rsp_msg_portion_length) {
248 : 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
249 : : }
250 [ + + ]: 19 : if (request_offset == 0) {
251 : 13 : encap_context->cert_chain_total_len = rsp_msg_portion_length + rsp_msg_remainder_length;
252 : 6 : } else if (encap_context->cert_chain_total_len !=
253 [ + + ]: 6 : request_offset + rsp_msg_portion_length + rsp_msg_remainder_length) {
254 : 1 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
255 : : }
256 : 18 : slot_id = encap_context->req_slot_id;
257 [ - + ]: 18 : if ((spdm_response->header.param1 & SPDM_CERTIFICATE_RESPONSE_SLOT_ID_MASK) != slot_id) {
258 : 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
259 : : }
260 [ + + ]: 18 : if (spdm_response->header.spdm_version >= SPDM_MESSAGE_VERSION_13) {
261 : 9 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "cert_info - 0x%02x\n",
262 : : spdm_response->header.param2));
263 : 9 : cert_model = spdm_response->header.param2 &
264 : : SPDM_CERTIFICATE_RESPONSE_ATTRIBUTES_CERTIFICATE_INFO_MASK;
265 [ + + ]: 9 : if (spdm_context->connection_info.multi_key_conn_req) {
266 [ - + ]: 5 : if (cert_model > SPDM_CERTIFICATE_INFO_CERT_MODEL_GENERIC_CERT) {
267 : 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
268 : : }
269 [ + + + + ]: 5 : if ((slot_id == 0) &&
270 : : (cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_GENERIC_CERT)) {
271 : 1 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
272 : : }
273 [ + + ]: 4 : if ((cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_NONE) &&
274 [ + - ]: 1 : (spdm_response->portion_length != 0)) {
275 : 1 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
276 : : }
277 : : } else {
278 [ - + ]: 4 : if (cert_model != SPDM_CERTIFICATE_INFO_CERT_MODEL_NONE) {
279 : 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
280 : : }
281 : : }
282 [ + - ]: 7 : if (spdm_context->connection_info.peer_cert_info[slot_id] ==
283 : : SPDM_CERTIFICATE_INFO_CERT_MODEL_NONE) {
284 : 7 : spdm_context->connection_info.peer_cert_info[slot_id] = cert_model;
285 [ # # ]: 0 : } else if (spdm_context->connection_info.peer_cert_info[slot_id] != cert_model) {
286 : 0 : return LIBSPDM_STATUS_INVALID_MSG_FIELD;
287 : : }
288 : : }
289 : :
290 [ - + ]: 16 : if (spdm_response_size < rsp_msg_header_size + rsp_msg_portion_length) {
291 : 0 : return LIBSPDM_STATUS_INVALID_MSG_SIZE;
292 : : }
293 : 16 : spdm_response_size = rsp_msg_header_size + rsp_msg_portion_length;
294 : :
295 : : /* Cache data*/
296 : :
297 : 16 : status = libspdm_append_message_mut_b(spdm_context, spdm_response, spdm_response_size);
298 [ - + ]: 16 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
299 : 0 : return LIBSPDM_STATUS_BUFFER_FULL;
300 : : }
301 : :
302 : 16 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "Certificate (offset 0x%x, size 0x%x):\n",
303 : : request_offset, rsp_msg_portion_length));
304 : 16 : LIBSPDM_INTERNAL_DUMP_HEX((const uint8_t *)spdm_response + rsp_msg_header_size,
305 : : rsp_msg_portion_length);
306 : :
307 [ + + ]: 16 : if (cert_chain_buffer_size + rsp_msg_portion_length > cert_chain_buffer_max_size) {
308 : 2 : LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "cert_chain buffer too small\n"));
309 : 2 : return LIBSPDM_STATUS_BUFFER_TOO_SMALL;
310 : : }
311 : :
312 : 14 : libspdm_copy_mem(cert_chain_buffer + cert_chain_buffer_size,
313 : : cert_chain_buffer_max_size - cert_chain_buffer_size,
314 : : (const uint8_t *)spdm_response + rsp_msg_header_size,
315 : : rsp_msg_portion_length);
316 : :
317 : 14 : cert_chain_buffer_size += rsp_msg_portion_length;
318 : 14 : encap_context->payload_buffer_size = cert_chain_buffer_size;
319 : :
320 [ + + ]: 14 : if (rsp_msg_remainder_length != 0) {
321 : 10 : *need_continue = true;
322 : :
323 : 10 : return LIBSPDM_STATUS_SUCCESS;
324 : : }
325 : :
326 : 4 : *need_continue = false;
327 : :
328 [ + + ]: 4 : if (spdm_context->local_context.verify_peer_spdm_cert_chain != NULL) {
329 : 1 : result = spdm_context->local_context.verify_peer_spdm_cert_chain (
330 : 1 : spdm_context, encap_context->req_slot_id,
331 : : cert_chain_buffer_size, cert_chain_buffer, NULL, NULL);
332 [ - + ]: 1 : if (!result) {
333 : 0 : return LIBSPDM_STATUS_VERIF_FAIL;
334 : : }
335 : : } else {
336 : 3 : result = libspdm_verify_peer_cert_chain_buffer_integrity(
337 : : spdm_context, cert_chain_buffer, cert_chain_buffer_size);
338 [ + + ]: 3 : if (!result) {
339 : 1 : return LIBSPDM_STATUS_VERIF_FAIL;
340 : : }
341 : :
342 : : /*verify peer cert chain authority*/
343 : 2 : result = libspdm_verify_peer_cert_chain_buffer_authority(
344 : : spdm_context, cert_chain_buffer, cert_chain_buffer_size, NULL, NULL);
345 [ - + ]: 2 : if (!result) {
346 : 0 : status = LIBSPDM_STATUS_VERIF_NO_AUTHORITY;
347 : : }
348 : : }
349 : :
350 : 3 : slot_id = encap_context->req_slot_id;
351 [ - + ]: 3 : LIBSPDM_ASSERT(slot_id < SPDM_MAX_SLOT_COUNT);
352 : :
353 : : #if LIBSPDM_RECORD_TRANSCRIPT_DATA_SUPPORT
354 : : spdm_context->connection_info.peer_used_cert_chain[slot_id].buffer_size =
355 : : cert_chain_buffer_size;
356 : :
357 : : libspdm_copy_mem(spdm_context->connection_info.peer_used_cert_chain[slot_id].buffer,
358 : : sizeof(spdm_context->connection_info.peer_used_cert_chain[slot_id].buffer),
359 : : cert_chain_buffer, cert_chain_buffer_size);
360 : : #else
361 : 3 : result = libspdm_hash_all(
362 : : spdm_context->connection_info.algorithm.base_hash_algo,
363 : : cert_chain_buffer, cert_chain_buffer_size,
364 : 3 : spdm_context->connection_info.peer_used_cert_chain[slot_id].buffer_hash);
365 [ - + ]: 3 : if (!result) {
366 : 0 : return LIBSPDM_STATUS_CRYPTO_ERROR;
367 : : }
368 : 6 : spdm_context->connection_info.peer_used_cert_chain[slot_id].buffer_hash_size =
369 : 3 : libspdm_get_hash_size(spdm_context->connection_info.algorithm.base_hash_algo);
370 : :
371 : 3 : libspdm_free_peer_leaf_cert_public_key(spdm_context, slot_id);
372 [ - + ]: 3 : if (spdm_context->connection_info.algorithm.req_pqc_asym_alg != 0) {
373 : 0 : result = libspdm_get_pqc_leaf_cert_public_key_from_cert_chain(
374 : : spdm_context->connection_info.algorithm.base_hash_algo,
375 : : spdm_context->connection_info.algorithm.req_pqc_asym_alg,
376 : : cert_chain_buffer, cert_chain_buffer_size,
377 : 0 : &spdm_context->connection_info.peer_used_cert_chain[slot_id].leaf_cert_public_key);
378 : : } else {
379 : 3 : result = libspdm_get_leaf_cert_public_key_from_cert_chain(
380 : : spdm_context->connection_info.algorithm.base_hash_algo,
381 : 3 : spdm_context->connection_info.algorithm.req_base_asym_alg,
382 : : cert_chain_buffer, cert_chain_buffer_size,
383 : 3 : &spdm_context->connection_info.peer_used_cert_chain[slot_id].leaf_cert_public_key);
384 : : }
385 [ - + ]: 3 : if (!result) {
386 : 0 : return LIBSPDM_STATUS_INVALID_CERT;
387 : : }
388 : : #endif
389 [ + - ]: 3 : if (status != LIBSPDM_STATUS_VERIF_NO_AUTHORITY) {
390 : 3 : return LIBSPDM_STATUS_SUCCESS;
391 : : } else {
392 : 0 : return LIBSPDM_STATUS_VERIF_NO_AUTHORITY;
393 : : }
394 : : }
395 : :
396 : : #endif /* (LIBSPDM_ENABLE_CAPABILITY_ENCAP_CAP) && (...) */
|