1 /* SPDX-License-Identifier: BSD-3-Clause */
2
3 #ifdef HAVE_CONFIG_H
4 #include <config.h>
5 #endif
6
7 #include <stdbool.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include <curl/curl.h>
13 #include <openssl/buffer.h>
14 #include <openssl/evp.h>
15 #include <openssl/sha.h>
16 #include <json-c/json.h>
17
18 #include "fapi_crypto.h"
19 #include "ifapi_helpers.h"
20
21 #define LOGMODULE fapi
22 #include "util/log.h"
23 #include "util/aux_util.h"
24
25 typedef struct tpm_getekcertificate_ctx tpm_getekcertificate_ctx;
26 struct tpm_getekcertificate_ctx {
27 char *ec_cert_path;
28 FILE *ec_cert_file_handle;
29 char *ek_server_addr;
30 unsigned int SSL_NO_VERIFY;
31 char *ek_path;
32 bool verbose;
33 bool is_tpm2_device_active;
34 TPM2B_PUBLIC *out_public;
35 };
36
37 static tpm_getekcertificate_ctx ctx = {
38 .is_tpm2_device_active = true,
39 };
40
41 /** Compute the SHA256 hash from the public key of an EK.
42 *
43 * @param[in] ek_public The public information of the EK.
44 * @retval unsigned_char* The hash value.
45 * @retval NULL If the computation of the hash fails.
46 */
hash_ek_public(TPM2B_PUBLIC * ek_public)47 static unsigned char *hash_ek_public(TPM2B_PUBLIC *ek_public) {
48
49 unsigned char *hash = (unsigned char *)malloc(SHA256_DIGEST_LENGTH);
50 if (!hash) {
51 LOG_ERROR("OOM");
52 return NULL;
53 }
54
55 SHA256_CTX sha256;
56 int is_success = SHA256_Init(&sha256);
57 if (!is_success) {
58 LOG_ERROR("SHA256_Init failed");
59 goto err;
60 }
61
62 switch (ek_public->publicArea.type) {
63 case TPM2_ALG_RSA:
64 /* Add public key to the hash. */
65 is_success = SHA256_Update(&sha256,
66 ek_public->publicArea.unique.rsa.buffer,
67 ek_public->publicArea.unique.rsa.size);
68 if (!is_success) {
69 LOG_ERROR("SHA256_Update failed");
70 goto err;
71 }
72
73 /* Add exponent to the hash. */
74 if (ek_public->publicArea.parameters.rsaDetail.exponent != 0) {
75 LOG_ERROR("non-default exponents unsupported");
76 goto err;
77 }
78 /* Exponent 65537 will be added. */
79 BYTE buf[3] = { 0x1, 0x00, 0x01 };
80 is_success = SHA256_Update(&sha256, buf, sizeof(buf));
81 if (!is_success) {
82 LOG_ERROR("SHA256_Update failed");
83 goto err;
84 }
85 break;
86
87 case TPM2_ALG_ECC:
88 is_success = SHA256_Update(&sha256,
89 ek_public->publicArea.unique.ecc.x.buffer,
90 ek_public->publicArea.unique.ecc.x.size);
91 if (!is_success) {
92 LOG_ERROR("SHA256_Update failed");
93 goto err;
94 }
95
96 /* Add public key to the hash. */
97 is_success = SHA256_Update(&sha256,
98 ek_public->publicArea.unique.ecc.y.buffer,
99 ek_public->publicArea.unique.ecc.y.size);
100 if (!is_success) {
101 LOG_ERROR("SHA256_Update failed");
102 goto err;
103 }
104 break;
105
106 default:
107 LOG_ERROR("unsupported EK algorithm");
108 goto err;
109 }
110
111 is_success = SHA256_Final(hash, &sha256);
112 if (!is_success) {
113 LOG_ERROR("SHA256_Final failed");
114 goto err;
115 }
116
117 LOG_TRACE("public-key-hash:");
118 LOG_TRACE(" sha256: ");
119 LOGBLOB_TRACE(&hash[0], SHA256_DIGEST_LENGTH, "Hash");
120 return hash;
121 err:
122 free(hash);
123 return NULL;
124 }
125
126 /** Calculate the base64 encoding of the hash of the Endorsement Public Key.
127 *
128 * @param[in] buffer The hash of the endorsement public key.
129 * @retval char* The base64 encoded string.
130 * @retval NULL if the encoding fails.
131 */
132 static char *
base64_encode(const unsigned char * buffer)133 base64_encode(const unsigned char* buffer)
134 {
135 BIO *bio, *b64;
136 BUF_MEM *buffer_pointer;
137
138 LOG_INFO("Calculating the base64_encode of the hash of the Endorsement"
139 "Public Key:");
140
141 if (buffer == NULL) {
142 LOG_ERROR("hash_ek_public returned null");
143 return NULL;
144 }
145
146 b64 = BIO_new(BIO_f_base64());
147 bio = BIO_new(BIO_s_mem());
148 bio = BIO_push(b64, bio);
149 BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
150 BIO_write(bio, buffer, SHA256_DIGEST_LENGTH);
151 (void)(BIO_flush(bio));
152 BIO_get_mem_ptr(bio, &buffer_pointer);
153
154 /* these are not NULL terminated */
155 char *b64text = buffer_pointer->data;
156 size_t len = buffer_pointer->length;
157
158 size_t i;
159 for (i = 0; i < len; i++) {
160 if (b64text[i] == '+') {
161 b64text[i] = '-';
162 }
163 if (b64text[i] == '/') {
164 b64text[i] = '_';
165 }
166 }
167
168 char *final_string = NULL;
169
170 CURL *curl = curl_easy_init();
171 if (curl) {
172 char *output = curl_easy_escape(curl, b64text, len);
173 if (output) {
174 final_string = strdup(output);
175 curl_free(output);
176 }
177 }
178 curl_easy_cleanup(curl);
179 curl_global_cleanup();
180 BIO_free_all(bio);
181
182 /* format to a proper NULL terminated string */
183 return final_string;
184 }
185
186 /** Decode a base64 encoded certificate into binary form.
187 *
188 * @param[in] buffer The base64 encoded certificate.
189 * @param[in] len The length of the encoded certificate.
190 * @param[out] new_len The lenght of the binary certificate.
191 * @retval char* The binary data of the certificate.
192 * @retval NULL if the decoding fails.
193 */
194 static char *
base64_decode(unsigned char * buffer,size_t len,size_t * new_len)195 base64_decode(unsigned char* buffer, size_t len, size_t *new_len)
196 {
197 size_t i, unescape_len, r;
198 char *binary_data = NULL, *unescaped_string = NULL;
199
200 LOG_INFO("Decoding the base64 encoded cert into binary form");
201
202 if (buffer == NULL) {
203 LOG_ERROR("Cert buffer is null");
204 return NULL;
205 }
206
207 for (i = 0; i < len; i++) {
208 if (buffer[i] == '-') {
209 buffer[i] = '+';
210 }
211 if (buffer[i] == '_') {
212 buffer[i] = '/';
213 }
214 }
215
216 CURL *curl = curl_easy_init();
217 if (curl) {
218 /* Convert URL encoded string to a "plain string" */
219 char *output = curl_easy_unescape(curl, (char *)buffer,
220 len, (int *)&unescape_len);
221 if (output) {
222 unescaped_string = strdup(output);
223 curl_free(output);
224 }
225 }
226 curl_easy_cleanup(curl);
227 curl_global_cleanup();
228 if (unescaped_string == NULL)
229 return NULL;
230
231 binary_data = calloc(1, unescape_len);
232 if (binary_data == NULL) {
233 free (unescaped_string);
234 return NULL;
235 }
236
237 BIO *bio, *b64;
238 bio = BIO_new_mem_buf(unescaped_string, -1);
239 b64 = BIO_new(BIO_f_base64());
240 bio = BIO_push(b64, bio);
241 BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
242
243 if ((r = BIO_read(bio, binary_data, unescape_len)) <= 0) {
244 LOG_ERROR("BIO_read base64 encoded cert failed");
245 free(binary_data);
246 binary_data = NULL;
247 }
248 *new_len = r;
249
250 free (unescaped_string);
251 BIO_free_all(bio);
252 return binary_data;
253 }
254
255 /** Get endorsement certificate from the WEB.
256 *
257 * The base64 encoded public endorsement key will be added to the INTEL
258 * server address and used as URL to retrieve the certificate.
259 * The certificate will be retrieved via curl.
260 *
261 * @param[in] b64h The base64 encoded public key.
262 * @param[out] buffer The json encoded certificate.
263 * @param[out] cert_size The size of the certificate.
264 */
retrieve_endorsement_certificate(char * b64h,unsigned char ** buffer,size_t * cert_size)265 int retrieve_endorsement_certificate(char *b64h, unsigned char ** buffer,
266 size_t *cert_size) {
267 int ret = -1;
268
269 size_t len = 1 + strlen(b64h) + strlen(ctx.ek_server_addr);
270 char *weblink = (char *) malloc(len);
271
272 if (!weblink) {
273 LOG_ERROR("oom");
274 return ret;
275 }
276
277 snprintf(weblink, len, "%s%s", ctx.ek_server_addr, b64h);
278
279 CURLcode rc = ifapi_get_curl_buffer((unsigned char *)weblink,
280 buffer, cert_size);
281 free(weblink);
282 return rc;
283 }
284
285 /** Get INTEL certificate for EK
286 *
287 * Using the base64 encoded public endorsement key the JSON encoded certificate
288 * will be downloaded.
289 * The JSON certificate will be parsed and the base64 encoded certificate
290 * will be converted into binary format.
291 *
292 *
293 * @param[in] context The FAPI context with the configuration data.
294 * @param[in] ek_public The out public data of the EK.
295 * @param[out] cert_buffer the der encoded certificate.
296 * @param[out] cert_size The size of the certificate buffer.
297 *
298 * @retval TSS2_RC_SUCCESS on success.
299 * @retval TSS2_FAPI_RC_NO_CERT If an error did occur during certificate downloading.
300 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occured.
301 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
302 */
303 TSS2_RC
ifapi_get_intl_ek_certificate(FAPI_CONTEXT * context,TPM2B_PUBLIC * ek_public,unsigned char ** cert_buffer,size_t * cert_size)304 ifapi_get_intl_ek_certificate(FAPI_CONTEXT *context, TPM2B_PUBLIC *ek_public,
305 unsigned char ** cert_buffer, size_t *cert_size)
306 {
307 int rc = 1;
308 unsigned char *hash = hash_ek_public(ek_public);
309 char *cert_ptr;
310 char *cert_start = NULL, *cert_bin = NULL;
311 char *b64 = base64_encode(hash);
312
313 if (!b64) {
314 LOG_ERROR("base64_encode returned null");
315 goto out;
316 }
317 if (context->config.intel_cert_service)
318 ctx.ek_server_addr = context->config.intel_cert_service;
319 else
320 ctx.ek_server_addr = "https://ekop.intel.com/ekcertservice/";
321
322 LOG_INFO("%s", b64);
323
324 /* Download the JSON encoded certificate. */
325 rc = retrieve_endorsement_certificate(b64, cert_buffer, cert_size);
326 free(b64);
327 goto_if_error(rc, "Retrieve endorsement certificate", out);
328 cert_ptr = (char *)*cert_buffer;
329 LOGBLOB_DEBUG((uint8_t *)cert_ptr, *cert_size, "%s", "Certificate");
330
331 /* Parse certificate data out of the json structure */
332 struct json_object *jso_cert, *jso = json_tokener_parse(cert_ptr);
333 if (jso == NULL)
334 goto_error(rc, TSS2_FAPI_RC_GENERAL_FAILURE,
335 "Failed to parse EK cert data", out_free_json);
336
337 if (!json_object_object_get_ex(jso, "certificate", &jso_cert))
338 goto_error(rc, TSS2_FAPI_RC_GENERAL_FAILURE,
339 "Could not find cert object", out_free_json);
340
341 if (!json_object_is_type(jso_cert, json_type_string))
342 goto_error(rc, TSS2_FAPI_RC_GENERAL_FAILURE,
343 "Invalid EK cert data", out_free_json);
344
345 cert_start = strdup(json_object_get_string(jso_cert));
346 if (!cert_start) {
347 SAFE_FREE(cert_ptr);
348 goto_error(rc, TSS2_FAPI_RC_MEMORY,
349 "Failed to duplicate cert", out_free_json);
350 }
351
352 *cert_size = strlen(cert_start);
353
354 /* Base64 decode buffer into binary PEM format */
355 cert_bin = base64_decode((unsigned char *)cert_start,
356 *cert_size, cert_size);
357 SAFE_FREE(cert_ptr);
358 SAFE_FREE(cert_start);
359
360 if (cert_bin == NULL) {
361 goto_error(rc, TSS2_FAPI_RC_GENERAL_FAILURE,
362 "Invalid EK cert data", out_free_json);
363 }
364 LOG_DEBUG("Binary cert size %zu", *cert_size);
365 *cert_buffer = (unsigned char *)cert_bin;
366
367 out_free_json:
368 json_object_put(jso);
369
370 out:
371 /* In some case this call was necessary after curl usage */
372 OpenSSL_add_all_algorithms();
373
374 free(hash);
375 if (rc == 0) {
376 return TSS2_RC_SUCCESS;
377 } else {
378 LOG_ERROR("Get INTEL EK certificate.");
379 return TSS2_FAPI_RC_NO_CERT;
380 }
381 }
382