Line data Source code
1 : /**
2 : * Copyright Notice:
3 : * Copyright 2024 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 <stdarg.h>
8 : #include <stddef.h>
9 : #include <setjmp.h>
10 : #include <stdint.h>
11 : #include <stdlib.h>
12 : #include <stdio.h>
13 : #include <assert.h>
14 : #include <string.h>
15 :
16 : #include <base.h>
17 : #if defined(_WIN32) || (defined(__clang__) && (defined (LIBSPDM_CPU_AARCH64) || \
18 : defined(LIBSPDM_CPU_ARM)))
19 : #else
20 : #include <fcntl.h>
21 : #include <unistd.h>
22 : #include <sys/stat.h>
23 : #endif
24 : #include "library/memlib.h"
25 : #include "internal/libspdm_device_secret_lib.h"
26 : #include "internal/libspdm_common_lib.h"
27 :
28 : bool g_in_trusted_environment = false;
29 : bool g_set_cert_is_busy = false;
30 :
31 : #if LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP
32 12 : bool libspdm_is_in_trusted_environment(void *spdm_context)
33 : {
34 12 : return g_in_trusted_environment;
35 : }
36 :
37 14 : static bool libspdm_write_certificate_to_nvm(
38 : void *spdm_context,
39 : uint8_t slot_id, const void * cert_chain,
40 : size_t cert_chain_size,
41 : uint32_t base_hash_algo, uint32_t base_asym_algo, uint32_t pqc_asym_algo,
42 : bool *need_reset, bool *is_busy)
43 : {
44 14 : if (g_set_cert_is_busy) {
45 1 : *is_busy = true;
46 :
47 1 : return false;
48 : } else {
49 : #if defined(_WIN32) || (defined(__clang__) && (defined (LIBSPDM_CPU_AARCH64) || \
50 : defined(LIBSPDM_CPU_ARM)))
51 : FILE *fp_out;
52 : #else
53 : int64_t fp_out;
54 : #endif
55 :
56 13 : char file_name[] = "slot_id_0_cert_chain.der";
57 : /*change the file name, for example: slot_id_1_cert_chain.der*/
58 13 : file_name[8] = (char)(slot_id+'0');
59 :
60 : /*check the input parameter*/
61 13 : if ((cert_chain == NULL) ^ (cert_chain_size == 0) ) {
62 0 : return false;
63 : }
64 :
65 : #if defined(_WIN32) || (defined(__clang__) && (defined (LIBSPDM_CPU_AARCH64) || \
66 : defined(LIBSPDM_CPU_ARM)))
67 : if ((fp_out = fopen(file_name, "w+b")) == NULL) {
68 : printf("Unable to open file %s\n", file_name);
69 : return false;
70 : }
71 :
72 : if (cert_chain != NULL) {
73 : if ((fwrite(cert_chain, 1, cert_chain_size, fp_out)) != cert_chain_size) {
74 : printf("Write output file error %s\n", file_name);
75 : fclose(fp_out);
76 : return false;
77 : }
78 : }
79 :
80 : fclose(fp_out);
81 : #else
82 13 : if (cert_chain != NULL) {
83 12 : if ((fp_out = open(file_name, O_WRONLY | O_CREAT, S_IRWXU)) == -1) {
84 0 : printf("Unable to open file %s\n", file_name);
85 0 : return false;
86 : }
87 :
88 12 : if ((write(fp_out, cert_chain, cert_chain_size)) != cert_chain_size) {
89 0 : printf("Write output file error %s\n", file_name);
90 0 : close(fp_out);
91 0 : return false;
92 : }
93 : } else {
94 1 : if ((fp_out = open(file_name, O_WRONLY | O_TRUNC)) == -1) {
95 0 : printf("Unable to open file %s\n", file_name);
96 0 : return false;
97 : }
98 : }
99 :
100 13 : close(fp_out);
101 : #endif
102 :
103 13 : return true;
104 : }
105 : }
106 :
107 1 : uint32_t libspdm_get_cert_chain_slot_storage_size(
108 : void *spdm_context, uint8_t slot_id)
109 : {
110 1 : return SPDM_MAX_CERTIFICATE_CHAIN_SIZE_14;
111 : }
112 :
113 15 : bool libspdm_update_local_cert_chain(
114 : void *spdm_context,
115 : uint8_t slot_id,
116 : uint32_t base_hash_algo,
117 : uint32_t base_asym_algo,
118 : uint32_t pqc_asym_algo,
119 : size_t hash_size,
120 : const void *old_cert_chain,
121 : size_t old_cert_chain_size,
122 : const void *cert_chain,
123 : size_t *cert_chain_size,
124 : uint8_t cert_model,
125 : bool *need_reset,
126 : bool *is_busy)
127 : {
128 : libspdm_data_parameter_t parameter;
129 : libspdm_return_t status;
130 : size_t header_size;
131 : uint8_t *new_buffer;
132 : bool result;
133 : const uint8_t *new_chain_bytes;
134 :
135 15 : if (slot_id >= SPDM_MAX_SLOT_COUNT) {
136 1 : return false;
137 : }
138 :
139 14 : if (cert_chain != NULL) {
140 13 : result = libspdm_write_certificate_to_nvm(
141 : spdm_context,
142 : slot_id,
143 13 : (const uint8_t *)cert_chain + sizeof(spdm_cert_chain_t) + hash_size,
144 13 : *cert_chain_size - (sizeof(spdm_cert_chain_t) + hash_size),
145 : base_hash_algo,
146 : base_asym_algo,
147 : pqc_asym_algo,
148 : need_reset,
149 : is_busy);
150 : } else {
151 1 : result = libspdm_write_certificate_to_nvm(
152 : spdm_context,
153 : slot_id,
154 : NULL,
155 : 0,
156 : base_hash_algo,
157 : base_asym_algo,
158 : pqc_asym_algo,
159 : need_reset,
160 : is_busy);
161 : }
162 :
163 14 : if (!result) {
164 1 : return result;
165 : }
166 :
167 13 : if (cert_chain == NULL || cert_chain_size == NULL || *cert_chain_size == 0) {
168 1 : new_buffer = NULL;
169 1 : goto set_cert;
170 : }
171 :
172 12 : header_size = sizeof(spdm_cert_chain_t) + hash_size;
173 12 : if (*cert_chain_size < header_size) {
174 1 : return false;
175 : }
176 :
177 11 : new_chain_bytes = (const uint8_t *)cert_chain;
178 :
179 11 : if (cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_DEVICE_CERT ||
180 : cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_GENERIC_CERT) {
181 : /* Device/generic certificate model: replace the entire local chain.*/
182 :
183 8 : new_buffer = (uint8_t *)malloc(*cert_chain_size);
184 8 : if (new_buffer == NULL) {
185 0 : return false;
186 : }
187 :
188 8 : libspdm_copy_mem(new_buffer, *cert_chain_size,
189 : new_chain_bytes, *cert_chain_size);
190 3 : } else if (cert_model == SPDM_CERTIFICATE_INFO_CERT_MODEL_ALIAS_CERT) {
191 : /* Alias Cert Model, the new `cert_chain` shall contain a partial
192 : * certificate chain from the root CA to the Device Certificate CA.
193 : *
194 : * We need to update the old root CA to the Device Certificate CA
195 : * and leave the other certs as they are. Then update the hash and
196 : * the size.
197 : */
198 2 : const uint8_t *new_certs = new_chain_bytes + header_size;
199 2 : size_t new_certs_size = *cert_chain_size - header_size;
200 2 : const uint8_t *old_certs = (const uint8_t *)old_cert_chain + header_size;
201 2 : size_t old_certs_size = old_cert_chain_size - header_size;
202 2 : size_t old_offset = 0;
203 2 : int32_t cert_index = 0;
204 2 : size_t mutable_cert_offset = 0;
205 : spdm_cert_chain_t *new_cert_chain;
206 : const uint8_t *root_cert;
207 : size_t root_cert_len;
208 :
209 : /* First, find the offset of the Device Certificate CA in the old
210 : * certs. This should contain the Hardware identity OID.
211 : */
212 6 : while (old_offset < old_certs_size) {
213 6 : const uint8_t *cert = NULL;
214 6 : size_t cert_size = 0;
215 :
216 6 : if (!libspdm_x509_get_cert_from_cert_chain(
217 : old_certs, old_certs_size, cert_index,
218 : &cert, &cert_size)) {
219 0 : return false;
220 : }
221 :
222 6 : mutable_cert_offset += cert_size;
223 :
224 6 : if (libspdm_contains_hardware_id_oid(cert, cert_size)) {
225 : /* We found the Device Certificate CA */
226 2 : break;
227 : }
228 :
229 4 : cert_index++;
230 : }
231 :
232 : /* Now Allocate enough memory for the new cert chain and the old
233 : * mutable certs
234 : */
235 2 : size_t new_cert_chain_size = (old_certs_size - mutable_cert_offset) + *cert_chain_size;
236 2 : new_buffer = (uint8_t *)malloc(new_cert_chain_size);
237 2 : if (new_buffer == NULL) {
238 0 : return false;
239 : }
240 :
241 :
242 : /* Copy the new immutable certificates */
243 2 : libspdm_copy_mem(new_buffer + header_size, new_cert_chain_size - header_size,
244 : new_certs, new_certs_size);
245 :
246 : /* Copy the old mutable certificates */
247 2 : libspdm_copy_mem(new_buffer + header_size + new_certs_size, new_cert_chain_size - header_size - new_certs_size,
248 2 : old_certs + mutable_cert_offset, old_certs_size - mutable_cert_offset);
249 :
250 : /* Update the size */
251 2 : *cert_chain_size = new_cert_chain_size;
252 :
253 2 : new_cert_chain = (spdm_cert_chain_t*)new_buffer;
254 2 : new_cert_chain->length = (uint32_t)new_cert_chain_size;
255 :
256 : /* Get Root Certificate*/
257 2 : status = libspdm_x509_get_cert_from_cert_chain(new_buffer + header_size, new_cert_chain_size - header_size, 0,
258 : &root_cert,
259 : &root_cert_len);
260 2 : if (!status) {
261 0 : free(new_buffer);
262 0 : return false;
263 : }
264 :
265 : /* Update the certificate hash */
266 2 : libspdm_hash_all(
267 : base_hash_algo,
268 : root_cert,
269 : root_cert_len,
270 : new_buffer + 4);
271 : } else {
272 1 : return false;
273 : }
274 :
275 11 : set_cert:
276 :
277 11 : libspdm_zero_mem(¶meter, sizeof(parameter));
278 11 : parameter.location = LIBSPDM_DATA_LOCATION_LOCAL;
279 11 : parameter.additional_data[0] = slot_id;
280 11 : if (cert_chain_size == NULL) {
281 1 : status = libspdm_set_data(spdm_context, LIBSPDM_DATA_LOCAL_PUBLIC_CERT_CHAIN,
282 : ¶meter, new_buffer, 0);
283 : } else {
284 10 : status = libspdm_set_data(spdm_context, LIBSPDM_DATA_LOCAL_PUBLIC_CERT_CHAIN,
285 : ¶meter, new_buffer, *cert_chain_size);
286 : }
287 11 : if (status != LIBSPDM_STATUS_SUCCESS) {
288 0 : free(new_buffer);
289 0 : return false;
290 : }
291 :
292 : /* `old_cert_chain` can be freed at this point. We can't
293 : * free() the const version supplied to this function and
294 : * stored in libspdm, so implementations need to keep track
295 : * of the buffer manually, probably by storing it in
296 : * `app_context_data_ptr`.
297 : */
298 :
299 11 : return true;
300 : }
301 : #endif /* LIBSPDM_ENABLE_CAPABILITY_SET_CERT_CAP */
|