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_requester_lib.h"
8 :
9 : #if LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP
10 : /**
11 : * This function sends SET_CERTIFICATE
12 : * to set certificate from the device.
13 : *
14 : * @param context A pointer to the SPDM context.
15 : * @param session_id Indicates if it is a secured message protected via SPDM session.
16 : * If session_id is NULL, it is a normal message.
17 : * If session_id is NOT NULL, it is a secured message.
18 : * @param slot_id The number of slot for the certificate chain.
19 : * @param cert_chain The pointer for the certificate chain to set.
20 : * The cert chain is a full SPDM certificate chain, including Length and Root Cert Hash.
21 : * @param cert_chain_size The size of the certificate chain to set.
22 : * @param request_attribute Set certificate request attributes. This field is only used for SPDM 1.3 and above.
23 : * And the bit[0~3] of request_attribute must be 0.
24 : * @param key_pair_id The value of this field shall be the unique key pair number identifying the desired
25 : * asymmetric key pair to associate with SlotID
26 : **/
27 8 : static libspdm_return_t libspdm_try_set_certificate(libspdm_context_t *spdm_context,
28 : const uint32_t *session_id, uint8_t slot_id,
29 : void *cert_chain, size_t cert_chain_size,
30 : uint8_t request_attribute,
31 : uint8_t key_pair_id)
32 : {
33 : libspdm_return_t status;
34 : spdm_set_certificate_request_t *spdm_request;
35 : size_t spdm_request_size;
36 : spdm_set_certificate_response_t *spdm_response;
37 : size_t spdm_response_size;
38 : size_t transport_header_size;
39 : uint8_t *message;
40 : size_t message_size;
41 : libspdm_session_info_t *session_info;
42 : libspdm_session_state_t session_state;
43 :
44 8 : LIBSPDM_ASSERT(slot_id < SPDM_MAX_SLOT_COUNT);
45 :
46 8 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_12) {
47 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
48 : }
49 8 : if (libspdm_get_connection_version (spdm_context) < SPDM_MESSAGE_VERSION_13) {
50 5 : if ((cert_chain == NULL) || (cert_chain_size == 0)) {
51 1 : return LIBSPDM_STATUS_INVALID_PARAMETER;
52 : }
53 : }
54 7 : if (libspdm_get_connection_version (spdm_context) >= SPDM_MESSAGE_VERSION_13) {
55 3 : const uint8_t set_cert_model =
56 3 : (request_attribute & SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_MASK) >>
57 : SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_OFFSET;
58 :
59 : /* Bit[0~3] of request_attribute must be 0 since this value is provided by the slot_id
60 : * parameter. */
61 3 : if ((request_attribute & SPDM_SET_CERTIFICATE_REQUEST_SLOT_ID_MASK) != 0) {
62 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
63 : }
64 :
65 : /* SET_CERT_CAP for a 1.2 Responder is not checked because it was not defined
66 : * in SPDM 1.2.0. */
67 3 : if (!libspdm_is_capabilities_flag_supported(
68 : spdm_context, true, 0,
69 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_SET_CERT_CAP)) {
70 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
71 : }
72 3 : if (spdm_context->connection_info.multi_key_conn_rsp) {
73 2 : if (key_pair_id == 0) {
74 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
75 : }
76 2 : if ((request_attribute & SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_ERASE) == 0) {
77 2 : if ((set_cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_NONE) ||
78 : (set_cert_model > SPDM_CERTIFICATE_INFO_CERT_MODEL_GENERIC_CERT)) {
79 1 : return LIBSPDM_STATUS_INVALID_PARAMETER;
80 : }
81 : }
82 : } else {
83 1 : if ((key_pair_id != 0) || (set_cert_model != 0)) {
84 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
85 : }
86 : }
87 : }
88 :
89 6 : if (spdm_context->connection_info.connection_state < LIBSPDM_CONNECTION_STATE_NEGOTIATED) {
90 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
91 : }
92 :
93 6 : if (session_id != NULL) {
94 1 : session_info = libspdm_get_session_info_via_session_id(
95 : spdm_context, *session_id);
96 1 : if (session_info == NULL) {
97 0 : LIBSPDM_ASSERT(false);
98 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
99 : }
100 1 : session_state = libspdm_secured_message_get_session_state(
101 : session_info->secured_message_context);
102 1 : if (session_state != LIBSPDM_SESSION_STATE_ESTABLISHED) {
103 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
104 : }
105 : }
106 :
107 6 : transport_header_size = spdm_context->local_context.capability.transport_header_size;
108 6 : status = libspdm_acquire_sender_buffer (spdm_context, &message_size, (void **)&message);
109 6 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
110 0 : return status;
111 : }
112 6 : LIBSPDM_ASSERT (message_size >= transport_header_size +
113 : spdm_context->local_context.capability.transport_tail_size);
114 6 : spdm_request = (void *)(message + transport_header_size);
115 6 : spdm_request_size = message_size - transport_header_size -
116 6 : spdm_context->local_context.capability.transport_tail_size;
117 :
118 6 : LIBSPDM_ASSERT(spdm_request_size >= sizeof(spdm_set_certificate_request_t));
119 6 : spdm_request->header.spdm_version = libspdm_get_connection_version (spdm_context);
120 6 : spdm_request->header.request_response_code = SPDM_SET_CERTIFICATE;
121 6 : spdm_request->header.param1 = slot_id & SPDM_SET_CERTIFICATE_REQUEST_SLOT_ID_MASK;
122 6 : spdm_request->header.param2 = 0;
123 :
124 6 : if (spdm_request->header.spdm_version >= SPDM_MESSAGE_VERSION_13) {
125 2 : if ((request_attribute & SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_ERASE) != 0) {
126 : /*the CertChain field shall be absent*/
127 1 : cert_chain_size = 0;
128 : /*the value of SetCertModel shall be zero*/
129 1 : spdm_request->header.param1 &= ~SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_CERT_MODEL_MASK;
130 : /*set Erase bit */
131 1 : spdm_request->header.param1 |= SPDM_SET_CERTIFICATE_REQUEST_ATTRIBUTES_ERASE;
132 : } else {
133 1 : spdm_request->header.param1 |= request_attribute;
134 : }
135 :
136 2 : if (spdm_context->connection_info.multi_key_conn_rsp) {
137 1 : spdm_request->header.param2 = key_pair_id;
138 : }
139 : }
140 :
141 6 : if ((libspdm_get_connection_version (spdm_context) < SPDM_MESSAGE_VERSION_13) ||
142 : (cert_chain_size != 0)) {
143 5 : if (cert_chain == NULL) {
144 0 : libspdm_release_sender_buffer (spdm_context);
145 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
146 : }
147 :
148 5 : LIBSPDM_ASSERT(spdm_request_size >=
149 : sizeof(spdm_set_certificate_request_t) + cert_chain_size);
150 5 : libspdm_copy_mem(spdm_request + 1,
151 : spdm_request_size - sizeof(spdm_set_certificate_request_t),
152 : (uint8_t *)cert_chain, cert_chain_size);
153 : }
154 :
155 6 : spdm_request_size = sizeof(spdm_set_certificate_request_t) + cert_chain_size;
156 :
157 6 : status = libspdm_send_spdm_request(spdm_context, session_id, spdm_request_size, spdm_request);
158 6 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
159 1 : libspdm_release_sender_buffer (spdm_context);
160 1 : return status;
161 : }
162 5 : libspdm_release_sender_buffer (spdm_context);
163 5 : spdm_request = (void *)spdm_context->last_spdm_request;
164 :
165 : /* receive */
166 5 : status = libspdm_acquire_receiver_buffer (spdm_context, &message_size, (void **)&message);
167 5 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
168 0 : return status;
169 : }
170 5 : LIBSPDM_ASSERT (message_size >= transport_header_size);
171 5 : spdm_response = (void *)(message);
172 5 : spdm_response_size = message_size;
173 :
174 5 : status = libspdm_receive_spdm_response(spdm_context, session_id,
175 : &spdm_response_size, (void **)&spdm_response);
176 :
177 5 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
178 0 : goto receive_done;
179 : }
180 5 : if (spdm_response_size < sizeof(spdm_message_header_t)) {
181 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
182 0 : goto receive_done;
183 : }
184 5 : if (spdm_response->header.request_response_code == SPDM_ERROR) {
185 1 : status = libspdm_handle_error_response_main(
186 : spdm_context, NULL,
187 : &spdm_response_size,
188 : (void **)&spdm_response, SPDM_SET_CERTIFICATE, SPDM_SET_CERTIFICATE_RSP);
189 1 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
190 1 : goto receive_done;
191 : }
192 4 : } else if (spdm_response->header.request_response_code != SPDM_SET_CERTIFICATE_RSP) {
193 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
194 0 : goto receive_done;
195 : }
196 4 : if (spdm_response->header.spdm_version != spdm_request->header.spdm_version) {
197 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
198 0 : goto receive_done;
199 : }
200 4 : if ((spdm_response->header.param1 & SPDM_CERTIFICATE_RESPONSE_SLOT_ID_MASK) != slot_id) {
201 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
202 0 : goto receive_done;
203 : }
204 :
205 : /* -=[Log Message Phase]=- */
206 : #if LIBSPDM_ENABLE_MSG_LOG
207 4 : libspdm_append_msg_log(spdm_context, spdm_response, spdm_response_size);
208 : #endif /* LIBSPDM_ENABLE_MSG_LOG */
209 :
210 4 : status = LIBSPDM_STATUS_SUCCESS;
211 :
212 5 : receive_done:
213 5 : libspdm_release_receiver_buffer (spdm_context);
214 5 : return status;
215 : }
216 :
217 5 : libspdm_return_t libspdm_set_certificate(void *spdm_context,
218 : const uint32_t *session_id, uint8_t slot_id,
219 : void *cert_chain, size_t cert_chain_size)
220 : {
221 : libspdm_context_t *context;
222 : size_t retry;
223 : uint64_t retry_delay_time;
224 : libspdm_return_t status;
225 :
226 5 : context = spdm_context;
227 5 : context->crypto_request = true;
228 5 : retry = context->retry_times;
229 5 : retry_delay_time = context->retry_delay_time;
230 : do {
231 5 : status = libspdm_try_set_certificate(context, session_id, slot_id,
232 : cert_chain, cert_chain_size, 0, 0);
233 5 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
234 5 : return status;
235 : }
236 :
237 0 : libspdm_sleep(retry_delay_time);
238 0 : } while (retry-- != 0);
239 :
240 0 : return status;
241 : }
242 :
243 3 : libspdm_return_t libspdm_set_certificate_ex(void *spdm_context,
244 : const uint32_t *session_id, uint8_t slot_id,
245 : void *cert_chain, size_t cert_chain_size,
246 : uint8_t request_attribute,
247 : uint8_t key_pair_id)
248 : {
249 : libspdm_context_t *context;
250 : size_t retry;
251 : uint64_t retry_delay_time;
252 : libspdm_return_t status;
253 :
254 3 : context = spdm_context;
255 3 : context->crypto_request = true;
256 3 : retry = context->retry_times;
257 3 : retry_delay_time = context->retry_delay_time;
258 : do {
259 3 : status = libspdm_try_set_certificate(context, session_id, slot_id,
260 : cert_chain, cert_chain_size,
261 : request_attribute, key_pair_id);
262 3 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
263 3 : return status;
264 : }
265 :
266 0 : libspdm_sleep(retry_delay_time);
267 0 : } while (retry-- != 0);
268 :
269 0 : return status;
270 : }
271 :
272 : #endif /*LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP*/
|