/* SPDX-License-Identifier: BSD-2-Clause */ /******************************************************************************* * Copyright 2017, Fraunhofer SIT sponsored by Infineon Technologies AG * All rights reserved. *******************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "tss2_esys.h" #include "tss2_fapi.h" #include "test-fapi.h" #define LOGDEFAULT LOGLEVEL_INFO #define LOGMODULE test #include "util/log.h" #include "util/aux_util.h" #ifndef FAPI_PROFILE #define FAPI_PROFILE "P_ECC" #endif /* FAPI_PROFILE */ char *fapi_profile = NULL; char *tmpdir = NULL; char *config = NULL; char *config_path = NULL; char *config_env = NULL; char *remove_cmd = NULL; char *system_dir = NULL; FAPI_CONTEXT *global_fapi_context = NULL; bool file_exists (char *path) { struct stat buffer; return (stat (path, &buffer) == 0); } /* Determine integer number from json object. */ static int64_t get_number(json_object *jso) { const char* token; int itoken = 0; int pos = 0; int64_t num; token = json_object_get_string(jso); if (strncmp(token, "0x", 2) == 0) { itoken = 2; sscanf(&token[itoken], "%"PRIx64"%n", &num, &pos); } else { sscanf(&token[itoken], "%"PRId64"%n", &num, &pos); } return num; } /* Determin number of fields in a json objecd. */ size_t nmb_of_fields(json_object *jso) { size_t n = 0; json_object_object_foreach(jso, key, val) { UNUSED(val); UNUSED(key); n++; } return n; } /* Compare two json objects. * * Only strings, integers, array and json objects are supported. */ bool cmp_jso(json_object *jso1, json_object *jso2) { enum json_type type1, type2; size_t i, size; type1 = json_object_get_type(jso1); type2 = json_object_get_type(jso2); if (type1 != type2) { return false; } if (type1 == json_type_object) { if (nmb_of_fields(jso1) != nmb_of_fields(jso2)) { return false; } json_object_object_foreach(jso1, key1, jso_sub1) { json_object *jso_sub2; if (!json_object_object_get_ex(jso2, key1, &jso_sub2)) { return false; } if (!cmp_jso(jso_sub1, jso_sub2)) { return false; } } return true; } else if (type1 == json_type_int) { return (get_number(jso1) == get_number(jso2)); } else if (type1 == json_type_array) { size = json_object_array_length(jso1); /* Cast to size_t due to change in json-c API. older versions use result type int */ if (size != (size_t)json_object_array_length(jso2)) { return false; } for (i = 0; i < size; i++) { if (!cmp_jso(json_object_array_get_idx(jso1, i), json_object_array_get_idx(jso2, i))) { return false; } } return true; } else if (type1 == json_type_string) { return (strcmp(json_object_get_string(jso1), json_object_get_string(jso2)) == 0); } else { return false; } } /* Compare two delimter sparated token lists. */ bool cmp_strtokens(char* string1, char *string2, char *delimiter) { bool found = false; char *token1 = NULL; char *token2 = NULL; char *end_token1; char *end_token2; char *string2_copy; string1 = strdup(string1); ASSERT(string1); token1 = strtok_r(string1, delimiter, &end_token1); while(token1 != NULL) { found = false; string2_copy = strdup(string2); ASSERT(string2_copy); token2 = strtok_r(string2_copy, delimiter, &end_token2); while (token2 != NULL) { if (strcmp(token1, token2) == 0) { found = true; break; } token2 = strtok_r(NULL, delimiter, &end_token2); } free(string2_copy); if (!found) { break; } token1 = strtok_r(NULL, delimiter, &end_token1); } free(string1); return found; error: SAFE_FREE(string1); return false; } TSS2_RC pcr_reset(FAPI_CONTEXT *context, UINT32 pcr) { TSS2_RC r; TSS2_TCTI_CONTEXT *tcti; ESYS_CONTEXT *esys; r = Fapi_GetTcti(context, &tcti); goto_if_error(r, "Error Fapi_GetTcti", error); r = Esys_Initialize(&esys, tcti, NULL); goto_if_error(r, "Error Fapi_GetTcti", error); r = Esys_PCR_Reset(esys, pcr, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE); Esys_Finalize(&esys); goto_if_error(r, "Error Eys_PCR_Reset", error); error: return r; } int init_fapi(char *profile, FAPI_CONTEXT **fapi_context) { TSS2_RC rc; int ret, size; SAFE_FREE(config); SAFE_FREE(config_path); SAFE_FREE(config_env); SAFE_FREE(remove_cmd); SAFE_FREE(system_dir); FILE *config_file; fapi_profile = profile; /* First we construct a fapi config file */ #if defined(FAPI_NONTPM) size = asprintf(&config, "{\n" " \"profile_name\": \"%s\",\n" " \"profile_dir\": \"" TOP_SOURCEDIR "/test/data/fapi/\",\n" " \"user_dir\": \"%s/user/dir\",\n" " \"system_dir\": \"%s/system_dir\",\n" " \"system_pcrs\" : [],\n" " \"log_dir\" : \"%s\",\n" " \"tcti\": \"none\",\n" "}\n", profile, tmpdir, tmpdir, tmpdir); #elif defined(FAPI_TEST_FINGERPRINT) size = asprintf(&config, "{\n" " \"profile_name\": \"%s\",\n" " \"profile_dir\": \"" TOP_SOURCEDIR "/test/data/fapi/\",\n" " \"user_dir\": \"%s/user/dir\",\n" " \"system_dir\": \"%s/system_dir\",\n" " \"system_pcrs\" : [],\n" " \"log_dir\" : \"%s\",\n" " \"tcti\": \"%s\",\n" #if defined(FAPI_TEST_EK_CERT_LESS) " \"ek_cert_less\": \"yes\",\n" #else " \"ek_fingerprint\": %s,\n" #endif "}\n", profile, tmpdir, tmpdir, tmpdir, getenv("TPM20TEST_TCTI") #if !defined(FAPI_TEST_EK_CERT_LESS) , getenv("FAPI_TEST_FINGERPRINT") #endif ); #elif defined(FAPI_TEST_CERTIFICATE) size = asprintf(&config, "{\n" " \"profile_name\": \"%s\",\n" " \"profile_dir\": \"" TOP_SOURCEDIR "/test/data/fapi/\",\n" " \"user_dir\": \"%s/user/dir\",\n" " \"system_dir\": \"%s/system_dir\",\n" " \"system_pcrs\" : [],\n" " \"log_dir\" : \"%s\",\n" " \"tcti\": \"%s\",\n" #if defined(FAPI_TEST_EK_CERT_LESS) " \"ek_cert_less\": \"yes\",\n" #else " \"ek_cert_file\": \"%s\",\n" #endif "}\n", profile, tmpdir, tmpdir, tmpdir, getenv("TPM20TEST_TCTI") #if !defined(FAPI_TEST_EK_CERT_LESS) , getenv("FAPI_TEST_CERTIFICATE") #endif ); #elif defined(FAPI_TEST_FINGERPRINT_ECC) size = asprintf(&config, "{\n" " \"profile_name\": \"%s\",\n" " \"profile_dir\": \"" TOP_SOURCEDIR "/test/data/fapi/\",\n" " \"user_dir\": \"%s/user/dir\",\n" " \"system_dir\": \"%s/system_dir\",\n" " \"system_pcrs\" : [],\n" " \"log_dir\" : \"%s\",\n" " \"tcti\": \"%s\",\n" #if defined(FAPI_TEST_EK_CERT_LESS) " \"ek_cert_less\": \"yes\",\n" #else " \"ek_fingerprint\": %s,\n" #endif "}\n", profile, tmpdir, tmpdir, tmpdir, getenv("TPM20TEST_TCTI") #if !defined(FAPI_TEST_EK_CERT_LESS) , getenv("FAPI_TEST_FINGERPRINT_ECC") #endif ); #elif defined(FAPI_TEST_CERTIFICATE_ECC) size = asprintf(&config, "{\n" " \"profile_name\": \"%s\",\n" " \"profile_dir\": \"" TOP_SOURCEDIR "/test/data/fapi/\",\n" " \"user_dir\": \"%s/user/dir\",\n" " \"system_dir\": \"%s/system_dir\",\n" " \"system_pcrs\" : [],\n" " \"log_dir\" : \"%s\",\n" " \"tcti\": \"%s\",\n" #if defined(FAPI_TEST_EK_CERT_LESS) " \"ek_cert_less\": \"yes\",\n" #else " \"ek_cert_file\": \"%s\",\n" #endif "}\n", profile, tmpdir, tmpdir, tmpdir, getenv("TPM20TEST_TCTI") #if defined(FAPI_TEST_EK_CERT_LESS) #else , getenv("FAPI_TEST_CERTIFICATE_ECC") #endif ); #else /* FAPI_NONTPM */ size = asprintf(&config, "{\n" " \"profile_name\": \"%s\",\n" " \"profile_dir\": \"" TOP_SOURCEDIR "/test/data/fapi/\",\n" " \"user_dir\": \"%s/user/dir\",\n" " \"system_dir\": \"%s/system_dir\",\n" " \"system_pcrs\" : [],\n" " \"log_dir\" : \"%s\",\n" " \"tcti\": \"%s\",\n" #if defined(FAPI_TEST_EK_CERT_LESS) " \"ek_cert_less\": \"yes\",\n" #endif "}\n", profile, tmpdir, tmpdir, tmpdir, getenv("TPM20TEST_TCTI")); #endif /* FAPI_NONTPM */ if (size < 0) { LOG_ERROR("Out of memory"); ret = EXIT_ERROR; goto error; } size = asprintf(&system_dir, "%s/system_dir/", tmpdir); if (size < 0) { LOG_ERROR("Out of memory"); ret = EXIT_ERROR; goto error; } if (!file_exists(system_dir)) { int rc_mkdir = mkdir(system_dir, 0777); if (rc_mkdir != 0) { LOG_ERROR("mkdir not possible: %i %s", rc_mkdir, system_dir); ret = EXIT_ERROR; goto error; } } if (size < 0) { LOG_ERROR("Out of memory"); ret = EXIT_ERROR; goto error; } LOG_INFO("Using config:\n%s", config); /* We construct the path for the config file */ size = asprintf(&config_path, "%s/fapi-config.json", tmpdir); if (size < 0) { LOG_ERROR("Out of memory"); ret = EXIT_ERROR; goto error; } /* We write the config file to disk */ config_file = fopen(config_path, "w"); if (!config_file) { LOG_ERROR("Opening config file for writing"); perror(config_path); ret = EXIT_ERROR; goto error; } size = fprintf(config_file, "%s", config); fclose(config_file); if (size < 0) { LOG_ERROR("Writing config file"); perror(config_path); ret = EXIT_ERROR; goto error; } /* We set the environment variable for FAPI to consume the config file */ size = asprintf(&config_env, "TSS2_FAPICONF=%s", config_path); if (size < 0) { LOG_ERROR("Out of memory"); ret = EXIT_ERROR; goto error; } putenv(config_env); /*********** * Call FAPI ***********/ rc = Fapi_Initialize(fapi_context, NULL); if (rc != TSS2_RC_SUCCESS) { LOG_ERROR("Esys_Initialize FAILED! Response Code : 0x%x", rc); ret = EXIT_FAILURE; goto error; } global_fapi_context = *fapi_context; return 0; error: Fapi_Finalize(fapi_context); if (system_dir) free(system_dir); if (config) free(config); if (config_path) free(config_path); if (config_env) free(config_env); if (remove_cmd) free(remove_cmd); return ret; } /** * This program is a template for integration tests (ones that use the TCTI, * the ESAPI, and FAPI contexts / API directly). It does nothing more than * parsing command line options that allow the caller (likely a script) * to specifywhich TCTI to use for the test using getenv("TPM20TEST_TCTI"). */ int main(int argc, char *argv[]) { int ret, size; char *config = NULL; char *config_path = NULL; char *config_env = NULL; char *remove_cmd = NULL; char *system_dir = NULL; char template[] = "/tmp/fapi_tmpdir.XXXXXX"; tmpdir = mkdtemp(template); if (!tmpdir) { LOG_ERROR("No temp dir created"); return EXIT_ERROR; } ret = init_fapi(FAPI_PROFILE, &global_fapi_context); if (ret) goto error; ret = test_invoke_fapi(global_fapi_context); LOG_INFO("Test returned %i", ret); if (ret) goto error; size = asprintf(&remove_cmd, "rm -r -f %s", tmpdir); if (size < 0) { LOG_ERROR("Out of memory"); ret = EXIT_ERROR; goto error; } if (system(remove_cmd) != 0) { LOG_ERROR("Directory %s can't be deleted.", tmpdir); ret = EXIT_ERROR; goto error; } error: Fapi_Finalize(&global_fapi_context); if (system_dir) free(system_dir); if (config) free(config); if (config_path) free(config_path); if (config_env) free(config_env); if (remove_cmd) free(remove_cmd); return ret; }