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