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_SET_CERT_CAP
10 :
11 : #if LIBSPDM_CERT_PARSE_SUPPORT
12 : /*set_cert verify cert_chain*/
13 8 : static bool libspdm_set_cert_verify_certchain(
14 : uint8_t spdm_version,
15 : const uint8_t *cert_chain, size_t cert_chain_size,
16 : uint32_t base_asym_algo, uint32_t pqc_asym_algo, uint32_t base_hash_algo,
17 : uint8_t cert_model)
18 : {
19 : const uint8_t *root_cert_buffer;
20 : size_t root_cert_buffer_size;
21 : const uint8_t *leaf_cert_buffer;
22 : size_t leaf_cert_buffer_size;
23 :
24 : /*get root cert*/
25 8 : if (!libspdm_x509_get_cert_from_cert_chain(
26 : cert_chain, cert_chain_size, 0, &root_cert_buffer,
27 : &root_cert_buffer_size)) {
28 0 : return false;
29 : }
30 :
31 : /*verify cert_chain*/
32 8 : if (!libspdm_x509_verify_cert_chain(root_cert_buffer, root_cert_buffer_size,
33 : cert_chain, cert_chain_size)) {
34 0 : return false;
35 : }
36 :
37 : /*get leaf cert*/
38 8 : if (!libspdm_x509_get_cert_from_cert_chain(
39 : cert_chain, cert_chain_size, -1, &leaf_cert_buffer,
40 : &leaf_cert_buffer_size)) {
41 0 : return false;
42 : }
43 :
44 : /*verify leaf cert*/
45 8 : if (!libspdm_x509_set_cert_certificate_check(
46 : spdm_version,
47 : leaf_cert_buffer, leaf_cert_buffer_size,
48 : base_asym_algo, pqc_asym_algo, base_hash_algo,
49 : false, cert_model)) {
50 0 : return false;
51 : }
52 :
53 8 : return true;
54 : }
55 : #endif /*LIBSPDM_CERT_PARSE_SUPPORT*/
56 :
57 14 : libspdm_return_t libspdm_get_response_set_certificate(libspdm_context_t *spdm_context,
58 : size_t request_size, const void *request,
59 : size_t *response_size, void *response)
60 : {
61 : const spdm_set_certificate_request_t *spdm_request;
62 : spdm_set_certificate_response_t *spdm_response;
63 :
64 : bool result;
65 : uint8_t spdm_version;
66 : uint8_t slot_id;
67 : bool need_reset;
68 : bool is_busy;
69 : bool erase;
70 : uint8_t set_cert_model;
71 :
72 : size_t root_cert_hash_size;
73 : const spdm_cert_chain_t *cert_chain_header;
74 : size_t cert_chain_size;
75 : const void * cert_chain;
76 : const void *full_cert_chain;
77 : size_t full_cert_chain_size;
78 : const void *old_local_cert_chain;
79 : size_t old_local_cert_chain_size;
80 :
81 : libspdm_session_info_t *session_info;
82 : libspdm_session_state_t session_state;
83 :
84 14 : spdm_request = request;
85 14 : full_cert_chain = NULL;
86 14 : full_cert_chain_size = 0;
87 :
88 : /* -=[Check Parameters Phase]=- */
89 14 : LIBSPDM_ASSERT(spdm_request->header.request_response_code == SPDM_SET_CERTIFICATE);
90 :
91 14 : spdm_version = libspdm_get_connection_version(spdm_context);
92 :
93 14 : if (spdm_version < SPDM_MESSAGE_VERSION_12) {
94 0 : return libspdm_generate_error_response(spdm_context,
95 : SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
96 : SPDM_SET_CERTIFICATE,
97 : response_size, response);
98 : }
99 :
100 14 : if (spdm_request->header.spdm_version != spdm_version) {
101 0 : return libspdm_generate_error_response(spdm_context,
102 : SPDM_ERROR_CODE_VERSION_MISMATCH, 0,
103 : response_size, response);
104 : }
105 :
106 14 : if (spdm_context->response_state != LIBSPDM_RESPONSE_STATE_NORMAL) {
107 2 : return libspdm_responder_handle_response_state(spdm_context,
108 2 : spdm_request->header.request_response_code,
109 : response_size, response);
110 : }
111 :
112 12 : if (spdm_context->connection_info.connection_state <
113 : LIBSPDM_CONNECTION_STATE_NEGOTIATED) {
114 0 : return libspdm_generate_error_response(
115 : spdm_context,
116 : SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
117 : response_size, response);
118 : }
119 :
120 12 : if (spdm_context->last_spdm_request_session_id_valid) {
121 7 : session_info = libspdm_get_session_info_via_session_id(
122 : spdm_context,
123 : spdm_context->last_spdm_request_session_id);
124 7 : if (session_info == NULL) {
125 0 : return libspdm_generate_error_response(
126 : spdm_context,
127 : SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
128 : response_size, response);
129 : }
130 7 : session_state = libspdm_secured_message_get_session_state(
131 : session_info->secured_message_context);
132 7 : if (session_state != LIBSPDM_SESSION_STATE_ESTABLISHED) {
133 0 : return libspdm_generate_error_response(
134 : spdm_context,
135 : SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
136 : response_size, response);
137 : }
138 : }
139 :
140 12 : if (!libspdm_is_capabilities_flag_supported(
141 : spdm_context, false, 0,
142 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_SET_CERT_CAP)) {
143 0 : return libspdm_generate_error_response(
144 : spdm_context, SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
145 : SPDM_SET_CERTIFICATE, response_size, response);
146 : }
147 :
148 12 : slot_id = spdm_request->header.param1 & SPDM_SET_CERTIFICATE_REQUEST_SLOT_ID_MASK;
149 12 : if (slot_id >= SPDM_MAX_SLOT_COUNT) {
150 0 : return libspdm_generate_error_response(spdm_context,
151 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
152 : response_size, response);
153 : }
154 :
155 12 : if ((!libspdm_is_in_trusted_environment(spdm_context)) && (slot_id != 0) &&
156 5 : (!spdm_context->last_spdm_request_session_id_valid)) {
157 1 : return libspdm_generate_error_response(spdm_context,
158 : SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
159 : response_size, response);
160 : }
161 :
162 11 : root_cert_hash_size = libspdm_get_hash_size(
163 : spdm_context->connection_info.algorithm.base_hash_algo);
164 :
165 11 : old_local_cert_chain =
166 11 : spdm_context->local_context.local_cert_chain_provision[slot_id];
167 11 : old_local_cert_chain_size =
168 11 : spdm_context->local_context.local_cert_chain_provision_size[slot_id];
169 :
170 11 : if (spdm_version >= SPDM_MESSAGE_VERSION_13) {
171 3 : const uint8_t key_pair_id = spdm_request->header.param2;
172 :
173 3 : set_cert_model =
174 3 : (spdm_request->header.param1 &
175 3 : SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_MASK) >>
176 : SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_OFFSET;
177 3 : erase = (spdm_request->header.param1 & SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_ERASE) != 0;
178 :
179 3 : if (spdm_context->connection_info.multi_key_conn_rsp) {
180 2 : if (key_pair_id == 0) {
181 0 : return libspdm_generate_error_response(spdm_context,
182 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
183 : response_size, response);
184 : }
185 2 : if (!erase) {
186 2 : if ((set_cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_NONE) ||
187 : (set_cert_model > SPDM_CERTIFICATE_INFO_CERT_MODEL_GENERIC_CERT)) {
188 1 : return libspdm_generate_error_response(spdm_context,
189 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
190 : response_size, response);
191 : }
192 : }
193 : } else {
194 1 : if ((key_pair_id != 0) || (set_cert_model != 0)) {
195 0 : return libspdm_generate_error_response(spdm_context,
196 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
197 : response_size, response);
198 : }
199 1 : if ((spdm_context->local_context.capability.flags &
200 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ALIAS_CERT_CAP) != 0) {
201 0 : set_cert_model = SPDM_CERTIFICATE_INFO_CERT_MODEL_ALIAS_CERT;
202 : } else {
203 1 : set_cert_model = SPDM_CERTIFICATE_INFO_CERT_MODEL_DEVICE_CERT;
204 : }
205 : }
206 : } else {
207 8 : if ((spdm_context->local_context.capability.flags &
208 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ALIAS_CERT_CAP) != 0) {
209 1 : set_cert_model = SPDM_CERTIFICATE_INFO_CERT_MODEL_ALIAS_CERT;
210 : } else {
211 7 : set_cert_model = SPDM_CERTIFICATE_INFO_CERT_MODEL_DEVICE_CERT;
212 : }
213 : }
214 :
215 10 : need_reset = libspdm_is_capabilities_flag_supported(
216 : spdm_context, false, 0,
217 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_INSTALL_RESET_CAP);
218 10 : is_busy = false;
219 :
220 10 : if ((spdm_version >= SPDM_MESSAGE_VERSION_13) && erase) {
221 : /*the CertChain field shall be absent;the value of SetCertModel shall be zero*/
222 1 : if ((request_size < sizeof(spdm_set_certificate_request_t)) ||
223 1 : ((spdm_request->header.param1 &
224 : SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_MASK) != 0)) {
225 0 : return libspdm_generate_error_response(spdm_context,
226 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
227 : response_size, response);
228 : }
229 :
230 : /* erase slot_id cert_chain*/
231 1 : result = libspdm_update_local_cert_chain(
232 : spdm_context,
233 : slot_id, 0, 0, 0, 0,
234 : old_local_cert_chain,
235 : old_local_cert_chain_size,
236 : NULL, 0,
237 : set_cert_model,
238 : &need_reset, &is_busy);
239 1 : if (!result) {
240 0 : if (is_busy) {
241 0 : return libspdm_generate_error_response(spdm_context,
242 : SPDM_ERROR_CODE_BUSY, 0,
243 : response_size, response);
244 : } else {
245 0 : return libspdm_generate_error_response(spdm_context,
246 : SPDM_ERROR_CODE_OPERATION_FAILED, 0,
247 : response_size, response);
248 : }
249 : }
250 : } else {
251 9 : if (request_size < sizeof(spdm_set_certificate_request_t) +
252 9 : sizeof(spdm_cert_chain_t) + root_cert_hash_size) {
253 1 : return libspdm_generate_error_response(spdm_context,
254 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
255 : response_size, response);
256 : }
257 :
258 : /*point to full SPDM certificate chain*/
259 8 : cert_chain = (const void*)(spdm_request + 1);
260 8 : cert_chain_header = cert_chain;
261 8 : full_cert_chain = cert_chain;
262 8 : full_cert_chain_size = cert_chain_header->length;
263 :
264 8 : if (cert_chain_header->length < sizeof(spdm_cert_chain_t) + root_cert_hash_size) {
265 0 : return libspdm_generate_error_response(spdm_context,
266 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
267 : response_size, response);
268 : }
269 8 : if (cert_chain_header->length > request_size - sizeof(spdm_set_certificate_request_t)) {
270 0 : return libspdm_generate_error_response(spdm_context,
271 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
272 : response_size, response);
273 : }
274 :
275 8 : if ((cert_chain_header->length > SPDM_MAX_CERTIFICATE_CHAIN_SIZE) &&
276 0 : (!libspdm_is_capabilities_flag_supported(
277 : spdm_context, false, 0,
278 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_LARGE_RESP_CAP))) {
279 0 : return libspdm_generate_error_response(spdm_context,
280 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
281 : response_size, response);
282 : }
283 :
284 : /*get actual cert_chain size*/
285 8 : cert_chain_size = cert_chain_header->length - sizeof(spdm_cert_chain_t) -
286 : root_cert_hash_size;
287 :
288 : /*point to actual cert_chain*/
289 8 : cert_chain = (const void*)((const uint8_t *)cert_chain
290 8 : + sizeof(spdm_cert_chain_t) + root_cert_hash_size);
291 :
292 : #if LIBSPDM_CERT_PARSE_SUPPORT
293 : /*check the cert_chain*/
294 8 : result = libspdm_set_cert_verify_certchain(spdm_version,
295 : cert_chain, cert_chain_size,
296 : spdm_context->connection_info.algorithm.base_asym_algo,
297 : spdm_context->connection_info.algorithm.pqc_asym_algo,
298 : spdm_context->connection_info.algorithm.base_hash_algo,
299 : set_cert_model);
300 8 : if (!result) {
301 0 : return libspdm_generate_error_response(spdm_context,
302 : SPDM_ERROR_CODE_UNSPECIFIED, 0,
303 : response_size, response);
304 : }
305 : #endif /*LIBSPDM_CERT_PARSE_SUPPORT*/
306 : /* set certificate to NV*/
307 8 : result = libspdm_update_local_cert_chain(
308 : spdm_context,
309 : slot_id,
310 : spdm_context->connection_info.algorithm.base_hash_algo,
311 : spdm_context->connection_info.algorithm.base_asym_algo,
312 : spdm_context->connection_info.algorithm.pqc_asym_algo,
313 : root_cert_hash_size,
314 : old_local_cert_chain,
315 : old_local_cert_chain_size,
316 : full_cert_chain,
317 : &full_cert_chain_size,
318 : set_cert_model,
319 : &need_reset, &is_busy);
320 :
321 8 : if (!result) {
322 1 : if (is_busy) {
323 1 : return libspdm_generate_error_response(spdm_context,
324 : SPDM_ERROR_CODE_BUSY, 0,
325 : response_size, response);
326 : } else {
327 0 : return libspdm_generate_error_response(spdm_context,
328 : SPDM_ERROR_CODE_UNSPECIFIED, 0,
329 : response_size, response);
330 : }
331 : }
332 : }
333 :
334 8 : LIBSPDM_ASSERT(*response_size >= sizeof(spdm_set_certificate_response_t));
335 8 : *response_size = sizeof(spdm_set_certificate_response_t);
336 8 : libspdm_zero_mem(response, *response_size);
337 8 : spdm_response = response;
338 :
339 : /*requires a reset to complete the SET_CERTIFICATE request*/
340 8 : if (libspdm_is_capabilities_flag_supported(
341 : spdm_context, false, 0,
342 1 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_INSTALL_RESET_CAP) && need_reset) {
343 1 : spdm_context->local_context.cert_slot_reset_mask |= (1 << slot_id);
344 :
345 : /*the device will reset to set cert*/
346 1 : return libspdm_generate_error_response(spdm_context,
347 : SPDM_ERROR_CODE_RESET_REQUIRED, 0,
348 : response_size, response);
349 : } else {
350 7 : spdm_response->header.spdm_version = spdm_request->header.spdm_version;
351 7 : spdm_response->header.request_response_code = SPDM_SET_CERTIFICATE_RSP;
352 7 : spdm_response->header.param1 = slot_id;
353 7 : spdm_response->header.param2 = 0;
354 : }
355 :
356 7 : return LIBSPDM_STATUS_SUCCESS;
357 : }
358 :
359 : #endif /*LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP*/
|