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