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 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 8 : if (spdm_version == SPDM_MESSAGE_VERSION_12) {
45 7 : const bool is_device_cert_model =
46 : (cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_DEVICE_CERT);
47 :
48 : /*verify leaf cert*/
49 7 : 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 8 : return true;
64 : }
65 : #endif /*LIBSPDM_CERT_PARSE_SUPPORT*/
66 :
67 14 : 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 14 : spdm_request = request;
91 :
92 : /* -=[Check Parameters Phase]=- */
93 14 : LIBSPDM_ASSERT(spdm_request->header.request_response_code == SPDM_SET_CERTIFICATE);
94 :
95 14 : spdm_version = libspdm_get_connection_version(spdm_context);
96 :
97 14 : 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 14 : 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 14 : 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 12 : 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 12 : if (spdm_context->last_spdm_request_session_id_valid) {
125 7 : session_info = libspdm_get_session_info_via_session_id(
126 : spdm_context,
127 : spdm_context->last_spdm_request_session_id);
128 7 : 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 7 : session_state = libspdm_secured_message_get_session_state(
135 : session_info->secured_message_context);
136 7 : 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 12 : 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 12 : slot_id = spdm_request->header.param1 & SPDM_SET_CERTIFICATE_REQUEST_SLOT_ID_MASK;
153 12 : 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 12 : if ((!libspdm_is_in_trusted_environment(spdm_context)) && (slot_id != 0) &&
160 5 : (!spdm_context->last_spdm_request_session_id_valid)) {
161 1 : return libspdm_generate_error_response(spdm_context,
162 : SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
163 : response_size, response);
164 : }
165 :
166 11 : root_cert_hash_size = libspdm_get_hash_size(
167 : spdm_context->connection_info.algorithm.base_hash_algo);
168 :
169 11 : if (spdm_version >= SPDM_MESSAGE_VERSION_13) {
170 3 : const uint8_t key_pair_id = spdm_request->header.param2;
171 :
172 3 : set_cert_model =
173 3 : (spdm_request->header.param1 &
174 3 : SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_MASK) >>
175 : SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_OFFSET;
176 3 : erase = (spdm_request->header.param1 & SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_ERASE) != 0;
177 :
178 3 : if (spdm_context->connection_info.multi_key_conn_rsp) {
179 2 : if (key_pair_id == 0) {
180 0 : return libspdm_generate_error_response(spdm_context,
181 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
182 : response_size, response);
183 : }
184 2 : if (!erase) {
185 2 : if ((set_cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_NONE) ||
186 : (set_cert_model > SPDM_CERTIFICATE_INFO_CERT_MODEL_GENERIC_CERT)) {
187 1 : return libspdm_generate_error_response(spdm_context,
188 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
189 : response_size, response);
190 : }
191 : }
192 : } else {
193 1 : if ((key_pair_id != 0) || (set_cert_model != 0)) {
194 0 : return libspdm_generate_error_response(spdm_context,
195 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
196 : response_size, response);
197 : }
198 1 : if ((spdm_context->local_context.capability.flags &
199 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ALIAS_CERT_CAP) != 0) {
200 0 : set_cert_model = SPDM_CERTIFICATE_INFO_CERT_MODEL_ALIAS_CERT;
201 : } else {
202 1 : set_cert_model = SPDM_CERTIFICATE_INFO_CERT_MODEL_DEVICE_CERT;
203 : }
204 : }
205 : } else {
206 8 : if ((spdm_context->local_context.capability.flags &
207 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ALIAS_CERT_CAP) != 0) {
208 1 : set_cert_model = SPDM_CERTIFICATE_INFO_CERT_MODEL_ALIAS_CERT;
209 : } else {
210 7 : set_cert_model = SPDM_CERTIFICATE_INFO_CERT_MODEL_DEVICE_CERT;
211 : }
212 : }
213 :
214 10 : need_reset = libspdm_is_capabilities_flag_supported(
215 : spdm_context, false, 0,
216 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_INSTALL_RESET_CAP);
217 10 : is_busy = false;
218 :
219 10 : if ((spdm_version >= SPDM_MESSAGE_VERSION_13) && erase) {
220 : /*the CertChain field shall be absent;the value of SetCertModel shall be zero*/
221 1 : if ((request_size < sizeof(spdm_set_certificate_request_t)) ||
222 1 : ((spdm_request->header.param1 &
223 : SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_MASK) != 0)) {
224 0 : return libspdm_generate_error_response(spdm_context,
225 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
226 : response_size, response);
227 : }
228 :
229 : /* erase slot_id cert_chain*/
230 1 : result = libspdm_write_certificate_to_nvm(
231 : spdm_context,
232 : slot_id, NULL, 0, 0, 0, 0,
233 : &need_reset, &is_busy);
234 1 : if (!result) {
235 0 : if (is_busy) {
236 0 : return libspdm_generate_error_response(spdm_context,
237 : SPDM_ERROR_CODE_BUSY, 0,
238 : response_size, response);
239 : } else {
240 0 : return libspdm_generate_error_response(spdm_context,
241 : SPDM_ERROR_CODE_OPERATION_FAILED, 0,
242 : response_size, response);
243 : }
244 : }
245 : } else {
246 9 : if (request_size < sizeof(spdm_set_certificate_request_t) +
247 9 : sizeof(spdm_cert_chain_t) + root_cert_hash_size) {
248 1 : return libspdm_generate_error_response(spdm_context,
249 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
250 : response_size, response);
251 : }
252 :
253 : /*point to full SPDM certificate chain*/
254 8 : cert_chain = (const void*)(spdm_request + 1);
255 8 : cert_chain_header = cert_chain;
256 :
257 8 : if (cert_chain_header->length < sizeof(spdm_cert_chain_t) + root_cert_hash_size) {
258 0 : return libspdm_generate_error_response(spdm_context,
259 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
260 : response_size, response);
261 : }
262 8 : if (cert_chain_header->length > request_size - sizeof(spdm_set_certificate_request_t)) {
263 0 : return libspdm_generate_error_response(spdm_context,
264 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
265 : response_size, response);
266 : }
267 :
268 8 : if ((cert_chain_header->length > SPDM_MAX_CERTIFICATE_CHAIN_SIZE) &&
269 0 : (!libspdm_is_capabilities_flag_supported(
270 : spdm_context, false, 0,
271 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_LARGE_RESP_CAP))) {
272 0 : return libspdm_generate_error_response(spdm_context,
273 : SPDM_ERROR_CODE_INVALID_REQUEST, 0,
274 : response_size, response);
275 : }
276 :
277 : /*get actual cert_chain size*/
278 8 : cert_chain_size = cert_chain_header->length - sizeof(spdm_cert_chain_t) -
279 : root_cert_hash_size;
280 :
281 : /*point to actual cert_chain*/
282 8 : cert_chain = (const void*)((const uint8_t *)cert_chain
283 8 : + sizeof(spdm_cert_chain_t) + root_cert_hash_size);
284 :
285 : #if LIBSPDM_CERT_PARSE_SUPPORT
286 : /*check the cert_chain*/
287 8 : result = libspdm_set_cert_verify_certchain(spdm_version,
288 : cert_chain, cert_chain_size,
289 : spdm_context->connection_info.algorithm.base_asym_algo,
290 : spdm_context->connection_info.algorithm.pqc_asym_algo,
291 : spdm_context->connection_info.algorithm.base_hash_algo,
292 : set_cert_model);
293 8 : if (!result) {
294 0 : return libspdm_generate_error_response(spdm_context,
295 : SPDM_ERROR_CODE_UNSPECIFIED, 0,
296 : response_size, response);
297 : }
298 : #endif /*LIBSPDM_CERT_PARSE_SUPPORT*/
299 :
300 : /* set certificate to NV*/
301 8 : result = libspdm_write_certificate_to_nvm(
302 : spdm_context,
303 : slot_id, cert_chain,
304 : cert_chain_size,
305 : spdm_context->connection_info.algorithm.base_hash_algo,
306 : spdm_context->connection_info.algorithm.base_asym_algo,
307 : spdm_context->connection_info.algorithm.pqc_asym_algo,
308 : &need_reset, &is_busy);
309 :
310 8 : if (!result) {
311 1 : if (is_busy) {
312 1 : return libspdm_generate_error_response(spdm_context,
313 : SPDM_ERROR_CODE_BUSY, 0,
314 : response_size, response);
315 : } else {
316 0 : return libspdm_generate_error_response(spdm_context,
317 : SPDM_ERROR_CODE_UNSPECIFIED, 0,
318 : response_size, response);
319 : }
320 : }
321 : }
322 :
323 8 : LIBSPDM_ASSERT(*response_size >= sizeof(spdm_set_certificate_response_t));
324 8 : *response_size = sizeof(spdm_set_certificate_response_t);
325 8 : libspdm_zero_mem(response, *response_size);
326 8 : spdm_response = response;
327 :
328 : /*requires a reset to complete the SET_CERTIFICATE request*/
329 8 : if (libspdm_is_capabilities_flag_supported(
330 : spdm_context, false, 0,
331 1 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_INSTALL_RESET_CAP) && need_reset) {
332 1 : spdm_context->local_context.cert_slot_reset_mask |= (1 << slot_id);
333 :
334 : /*the device will reset to set cert*/
335 1 : return libspdm_generate_error_response(spdm_context,
336 : SPDM_ERROR_CODE_RESET_REQUIRED, 0,
337 : response_size, response);
338 : } else {
339 7 : spdm_response->header.spdm_version = spdm_request->header.spdm_version;
340 7 : spdm_response->header.request_response_code = SPDM_SET_CERTIFICATE_RSP;
341 7 : spdm_response->header.param1 = slot_id;
342 7 : spdm_response->header.param2 = 0;
343 : }
344 :
345 7 : return LIBSPDM_STATUS_SUCCESS;
346 : }
347 :
348 : #endif /*LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP*/
|