| /* SPDX-License-Identifier: BSD-2-Clause */ |
| /******************************************************************************* |
| * Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG |
| * All rights reserved. |
| *******************************************************************************/ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #include "fapi_util.h" |
| #include "tss2_tcti.h" |
| #include "tss2_esys.h" |
| #include "tss2_fapi.h" |
| #include "fapi_int.h" |
| #include "fapi_crypto.h" |
| #include "fapi_policy.h" |
| #include "ifapi_get_intl_cert.h" |
| #include "ifapi_helpers.h" |
| |
| #define LOGMODULE fapi |
| #include "util/log.h" |
| #include "util/aux_util.h" |
| |
| #define EK_CERT_RANGE (0x01c07fff) |
| |
| |
| /** Error cleanup of provisioning |
| * |
| * The profile directory will be deleted. |
| * A persistent SRK or EK will be deleted if possible without authorization |
| * for the owner hierarchy. |
| * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated. |
| * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. |
| * @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context |
| * or contains illegal characters. |
| * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into |
| * the function. |
| * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found |
| * during authorization. |
| * @retval TSS2_FAPI_RC_IO_ERROR if an error occured while accessing the |
| * object store. |
| */ |
| void |
| error_cleanup_provisioning(FAPI_CONTEXT *context) { |
| TPM2_HANDLE dmy_handle; |
| if (context) { |
| ifapi_session_clean(context); |
| if (context->esys) { |
| IFAPI_Provision * pctx = &context->cmd.Provision; |
| if (pctx->ek_tpm_handle && pctx->ek_esys_handle) { |
| Esys_EvictControl(context->esys, ESYS_TR_RH_OWNER, |
| pctx->ek_esys_handle, |
| ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, |
| pctx->ek_tpm_handle, &dmy_handle); |
| } |
| if (pctx->srk_tpm_handle &&pctx->srk_esys_handle) { |
| Esys_EvictControl(context->esys, ESYS_TR_RH_OWNER, |
| pctx->srk_esys_handle, |
| ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, |
| pctx->srk_tpm_handle, &dmy_handle); |
| } |
| } |
| if (context->keystore.systemdir && context->config.profile_name) |
| ifapi_keystore_remove_directories(&context->keystore, |
| context->config.profile_name); |
| } |
| } |
| |
| /** One-Call function for the initial FAPI provisioning. |
| * |
| * Provisions a TSS with its TPM. This includes the setting of important passwords |
| * and policy settings as well as the readout of the EK and its certificate and |
| * the initialization of the system-wide keystore. |
| * |
| * @param[in,out] context The FAPI_CONTEXT. |
| * @param[in] authValueEh The authorization value for the endorsement |
| * hierarchy. May be NULL |
| * @param[in] authValueSh The authorization value for the storage hierarchy. |
| * Should be NULL |
| * @param[in] authValueLockout The authorization value for lockout. |
| * |
| * @retval TSS2_RC_SUCCESS: if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL. |
| * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected. |
| * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous |
| * operation already pending. |
| * @retval TSS2_FAPI_RC_NO_CERT: if no certificate was found for the computed EK. |
| * @retval TSS2_FAPI_RC_BAD_KEY: if public key of the EK does not match the |
| * configured certificate or the configured fingerprint does not match |
| * the computed EK. |
| * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for |
| * internal operations or return parameters. |
| * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its |
| * config file. |
| * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and |
| * this function needs to be called again. |
| * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into |
| * the function. |
| * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback |
| * is not set. |
| * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails. |
| * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. |
| * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest |
| * was not successful. |
| * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found |
| * during authorization. |
| * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found. |
| * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. |
| * @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context |
| * or contains illegal characters. |
| * @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned. |
| * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS if the object already exists in object store. |
| */ |
| TSS2_RC |
| Fapi_Provision( |
| FAPI_CONTEXT *context, |
| char const *authValueEh, |
| char const *authValueSh, |
| char const *authValueLockout) |
| { |
| LOG_TRACE("called for context:%p", context); |
| |
| TSS2_RC r, r2; |
| |
| /* Check for NULL parameters */ |
| check_not_null(context); |
| |
| /* Check whether TCTI and ESYS are initialized */ |
| return_if_null(context->esys, "Command can't be executed in none TPM mode.", |
| TSS2_FAPI_RC_NO_TPM); |
| |
| /* If the async state automata of FAPI shall be tested, then we must not set |
| the timeouts of ESYS to blocking mode. |
| During testing, the mssim tcti will ensure multiple re-invocations. |
| Usually however the synchronous invocations of FAPI shall instruct ESYS |
| to block until a result is available. */ |
| #ifndef TEST_FAPI_ASYNC |
| r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK); |
| return_if_error_reset_state(r, "Set Timeout to blocking"); |
| #endif /* TEST_FAPI_ASYNC */ |
| |
| r = Fapi_Provision_Async(context, authValueEh, authValueSh, authValueLockout); |
| return_if_error_reset_state(r, "Provision"); |
| |
| do { |
| /* We wait for file I/O to be ready if the FAPI state automata |
| are in a file I/O state. */ |
| r = ifapi_io_poll(&context->io); |
| return_if_error(r, "Something went wrong with IO polling"); |
| |
| /* Repeatedly call the finish function, until FAPI has transitioned |
| through all execution stages / states of this invocation. */ |
| r = Fapi_Provision_Finish(context); |
| } while (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN); |
| |
| /* Reset the ESYS timeout to non-blocking, immediate response. */ |
| r2 = Esys_SetTimeout(context->esys, 0); |
| return_if_error(r2, "Set Timeout to non-blocking"); |
| |
| return_if_error_reset_state(r, "Provision"); |
| |
| LOG_TRACE("finished"); |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /** Asynchronous function for the initial FAPI provisioning. |
| * |
| * Provisions a TSS with its TPM. This includes the setting of important passwords |
| * and policy settings as well as the readout of the EK and its certificate and |
| * the initialization of the system-wide keystore. |
| * |
| * Call Fapi_Provision_Finish to finish the execution of this command. |
| * |
| * @param[in,out] context The FAPI_CONTEXT. |
| * @param[in] authValueEh The authorization value for the endorsement |
| * hierarchy. May be NULL |
| * @param[in] authValueSh The authorization value for the storage hierarchy. |
| * Should be NULL |
| * @param[in] authValueLockout The authorization value for lockout. |
| * |
| * @retval TSS2_RC_SUCCESS: if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL. |
| * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected. |
| * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous |
| * operation already pending. |
| * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for |
| * internal operations or return parameters. |
| * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its |
| * config file. |
| * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into |
| * the function. |
| * @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context |
| * or contains illegal characters. |
| * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found |
| * during authorization. |
| */ |
| TSS2_RC |
| Fapi_Provision_Async( |
| FAPI_CONTEXT *context, |
| char const *authValueEh, |
| char const *authValueSh, |
| char const *authValueLockout) |
| { |
| char *profile_dir = NULL; |
| size_t i; |
| |
| LOG_TRACE("called for context:%p", context); |
| LOG_TRACE("authValueEh: %s", authValueEh); |
| LOG_TRACE("authValueSh: %s", authValueSh); |
| LOG_TRACE("authValueLockout: %s", authValueLockout); |
| |
| TSS2_RC r; |
| |
| /* Check for NULL parameters */ |
| check_not_null(context); |
| |
| /* Helpful alias pointers */ |
| IFAPI_Provision * command = &context->cmd.Provision; |
| |
| r = ifapi_session_init(context); |
| goto_if_error(r, "Initialize Provision", end); |
| |
| memset(&context->cmd.Provision, 0, sizeof(IFAPI_Provision)); |
| |
| /* First it will be checked whether the profile is already provisioned. */ |
| r = ifapi_asprintf(&profile_dir, "%s/%s/HS", context->keystore.systemdir, |
| context->keystore.defaultprofile); |
| goto_if_error(r, "Out of memory.", end); |
| |
| if (ifapi_io_path_exists(profile_dir)) { |
| goto_error(r, TSS2_FAPI_RC_ALREADY_PROVISIONED, |
| "Profile %s was already provisioned.", end, |
| context->keystore.defaultprofile); |
| } |
| SAFE_FREE(profile_dir); |
| |
| /* Initialize context and duplicate parameters */ |
| strdup_check(command->authValueLockout, authValueLockout, r, end); |
| strdup_check(command->authValueEh, authValueEh, r, end); |
| strdup_check(command->authValueSh, authValueSh, r, end); |
| context->ek_handle = ESYS_TR_NONE; |
| context->srk_handle = ESYS_TR_NONE; |
| command->cert_nv_idx = MIN_EK_CERT_HANDLE; |
| command->capabilityData = NULL; |
| |
| /* Set the initial state for the finish method. */ |
| context->state = PROVISION_INIT; |
| |
| /* Compute the list of all objects stored in keystore. */ |
| r = ifapi_keystore_list_all(&context->keystore, "/", &command->pathlist, |
| &command->numPaths); |
| goto_if_error(r, "get entities.", end); |
| |
| command->numHierarchyObjects = 0; |
| for (i = 0; i < command->numPaths; i++) { |
| if (ifapi_hierarchy_path_p(command->pathlist[i])) { |
| if (i != command->numHierarchyObjects) { |
| char *sav_path; |
| sav_path = command->pathlist[command->numHierarchyObjects]; |
| command->pathlist[command->numHierarchyObjects] = command->pathlist[i]; |
| command->pathlist[i] = sav_path; |
| command->numHierarchyObjects += 1; |
| } else { |
| command->numHierarchyObjects += 1; |
| } |
| } |
| } |
| |
| LOG_TRACE("finished"); |
| return TSS2_RC_SUCCESS; |
| end: |
| SAFE_FREE(profile_dir); |
| SAFE_FREE(command->authValueLockout); |
| SAFE_FREE(command->authValueEh); |
| SAFE_FREE(command->authValueSh); |
| return r; |
| } |
| |
| /** Asynchronous finish function for Fapi_Provision |
| * |
| * This function should be called after a previous Fapi_Provision_Async. |
| * |
| * @param[in,out] context The FAPI_CONTEXT |
| * |
| * @retval TSS2_RC_SUCCESS: if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL. |
| * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected. |
| * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous |
| * operation already pending. |
| * @retval TSS2_FAPI_RC_NO_CERT: if no certificate was found for the computed EK. |
| * @retval TSS2_FAPI_RC_BAD_KEY: if public key of the EK does not match the |
| * configured certificate or the configured fingerprint does not match |
| * the computed EK. |
| * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for |
| * internal operations or return parameters. |
| * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet |
| * complete. Call this function again later. |
| * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into |
| * the function. |
| * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback |
| * is not set. |
| * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails. |
| * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. |
| * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest |
| * was not successful. |
| * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found |
| * during authorization. |
| * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found. |
| * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. |
| * @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned. |
| * @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context |
| * or contains illegal characters. |
| * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS if the object already exists in object store. |
| */ |
| TSS2_RC |
| Fapi_Provision_Finish(FAPI_CONTEXT *context) |
| { |
| LOG_TRACE("called for context:%p", context); |
| |
| TSS2_RC r = TSS2_RC_SUCCESS; |
| TPM2B_NV_PUBLIC *nvPublic = NULL; |
| uint8_t *certData = NULL; |
| size_t certSize; |
| TPMI_YES_NO moreData; |
| size_t hash_size, i; |
| TPMI_ALG_HASH hash_alg; |
| TPM2B_DIGEST ek_fingerprint; |
| |
| /* Check for NULL parameters */ |
| check_not_null(context); |
| |
| /* Helpful alias pointers */ |
| IFAPI_Provision * command = &context->cmd.Provision; |
| IFAPI_OBJECT *hierarchy_hs = &command->hierarchy_hs; |
| IFAPI_OBJECT *hierarchy_he = &command->hierarchy_he; |
| IFAPI_OBJECT *hierarchy_hn = &command->hierarchy_hn; |
| IFAPI_OBJECT *hierarchy_lockout = &command->hierarchy_lockout; |
| TPMS_CAPABILITY_DATA **capabilityData = &command->capabilityData; |
| IFAPI_NV_Cmds * nvCmd = &context->nv_cmd; |
| IFAPI_OBJECT * pkeyObject = &context->createPrimary.pkey_object; |
| IFAPI_KEY * pkey = &pkeyObject->misc.key; |
| IFAPI_PROFILE * defaultProfile = &context->profiles.default_profile; |
| int curl_rc; |
| TPMA_OBJECT *attributes; |
| char *description, *path; |
| ESYS_TR auth_session; |
| |
| switch (context->state) { |
| /* Read all hierarchies from keystore. */ |
| statecase(context->state, PROVISION_GET_HIERARCHIES); |
| command->hierarchies = calloc(1, command->numHierarchyObjects * |
| sizeof(IFAPI_OBJECT)); |
| goto_if_null(command->hierarchies, "Out of memory", TSS2_FAPI_RC_MEMORY, |
| error_cleanup); |
| command->path_idx = 0; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_READ_HIERARCHIES); |
| r = ifapi_keystore_load_async(&context->keystore, &context->io, |
| command->pathlist[command->path_idx]); |
| goto_if_error2(r, "Could not open %s", error_cleanup, |
| command->pathlist[command->path_idx]); |
| fallthrough; |
| |
| statecase(context->state, PROVISION_READ_HIERARCHY); |
| path = command->pathlist[command->path_idx]; |
| if (path == NULL) { |
| goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Wrong path.", |
| error_cleanup); |
| } |
| |
| r = ifapi_keystore_load_finish(&context->keystore, &context->io, |
| &command->hierarchies[command->path_idx]); |
| return_try_again(r); |
| goto_if_error2(r, "Could not open %s", error_cleanup, path); |
| |
| /* Search for slash followed by hierarchy after profile */ |
| path = strchr(&path[1], '/'); |
| if (path == NULL) { |
| goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, |
| "Wrong path.", |
| error_cleanup); |
| } |
| |
| /* Use the first appropriate hierarchy for provisioning. The first found |
| hierarchy will be copied into the provisioning context.*/ |
| if (strcmp(path, "/HS") == 0) { |
| command->hierarchies[command->path_idx].handle = ESYS_TR_RH_OWNER; |
| if (!hierarchy_hs->handle) { |
| r = ifapi_copy_ifapi_hierarchy_object(hierarchy_hs, |
| &command-> |
| hierarchies[command->path_idx]); |
| goto_if_error(r, "Copy hierarchy", error_cleanup); |
| } |
| } else if (strcmp(path, "/HE") == 0) { |
| command->hierarchies[command->path_idx].handle = ESYS_TR_RH_ENDORSEMENT; |
| if (!hierarchy_he->handle) { |
| r = ifapi_copy_ifapi_hierarchy_object(hierarchy_he, |
| &command-> |
| hierarchies[command->path_idx]); |
| goto_if_error(r, "Copy hierarchy", error_cleanup); |
| } |
| } else if (strcmp(path, "/HN") == 0) { |
| command->hierarchies[command->path_idx].handle = ESYS_TR_RH_NULL; |
| if (!hierarchy_hn->handle) { |
| r = ifapi_copy_ifapi_hierarchy_object(hierarchy_hn, |
| &command-> |
| hierarchies[command->path_idx]); |
| goto_if_error(r, "Copy hierarchy", error_cleanup); |
| } |
| } else if (strcmp(path, "/LOCKOUT") == 0) { |
| command->hierarchies[command->path_idx].handle = ESYS_TR_RH_LOCKOUT; |
| if (!hierarchy_lockout->handle) { |
| r = ifapi_copy_ifapi_hierarchy_object(hierarchy_lockout, |
| &command-> |
| hierarchies[command->path_idx]); |
| goto_if_error(r, "Copy hierarchy", error_cleanup); |
| } |
| } else { |
| goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Invalid hierarchy list.", |
| error_cleanup); |
| } |
| |
| command->path_idx += 1; |
| if (command->path_idx < command->numHierarchyObjects) { |
| context->state = PROVISION_READ_HIERARCHIES; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } else { |
| context->state = PROVISION_PREPARE_GET_CAP_AUTH_STATE; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_INIT); |
| |
| command->root_crt = NULL; |
| /* Check whether hierarchies are stored in the current keystore. */ |
| if (command->numHierarchyObjects > 0) { |
| context->state = PROVISION_GET_HIERARCHIES; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| |
| /* Initialize the hierarchy objects. */ |
| ifapi_init_hierarchy_object(hierarchy_hs, ESYS_TR_RH_OWNER); |
| strdup_check(hierarchy_hs->misc.hierarchy.description, |
| "Owner Hierarchy", r, error_cleanup); |
| ifapi_init_hierarchy_object(hierarchy_he, ESYS_TR_RH_ENDORSEMENT); |
| strdup_check(hierarchy_he->misc.hierarchy.description, |
| "Endorsement Hierarchy", r, error_cleanup); |
| ifapi_init_hierarchy_object(hierarchy_lockout, ESYS_TR_RH_LOCKOUT); |
| strdup_check(hierarchy_lockout->misc.hierarchy.description, "Lockout Hierarchy", |
| r, error_cleanup); |
| ifapi_init_hierarchy_object(hierarchy_hn, ESYS_TR_RH_NULL); |
| strdup_check(hierarchy_hn->misc.hierarchy.description, "Null Hierarchy", |
| r, error_cleanup); |
| fallthrough; |
| |
| statecase(context->state, PROVISION_PREPARE_GET_CAP_AUTH_STATE); |
| /* The storage hierarchy will be used to read certificates from nv ram. */ |
| nvCmd->auth_object = *hierarchy_hs; |
| |
| /* Generate template for checking whether persistent SRK handle |
| already exists. */ |
| r = ifapi_set_key_flags(defaultProfile->srk_template, |
| false, &command->public_templ); |
| goto_if_error(r, "Set key flags for SRK", error_cleanup); |
| |
| r = Esys_GetCapability_Async(context->esys, |
| ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_TPM_PROPERTIES, |
| TPM2_PT_PERMANENT, 1); |
| goto_if_error(r, "Esys_GetCapability_Async", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WAIT_FOR_GET_CAP_AUTH_STATE); |
| r = Esys_GetCapability_Finish(context->esys, &moreData, capabilityData); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "GetCapablity_Finish", error_cleanup); |
| |
| if ((*capabilityData)->data.tpmProperties.tpmProperty[0].property != |
| TPM2_PT_PERMANENT) { |
| SAFE_FREE(*capabilityData); |
| goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, |
| "TPM2_PT_PERMANENT cannot be determined.", error_cleanup); |
| } |
| |
| command->auth_state = (*capabilityData)->data.tpmProperties.tpmProperty[0].value; |
| SAFE_FREE(*capabilityData); |
| |
| /* Check the TPM capabilities for the persistent handle. */ |
| if (command->public_templ.persistent_handle) { |
| r = Esys_GetCapability_Async(context->esys, |
| ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, |
| command->public_templ.persistent_handle, 1); |
| goto_if_error(r, "Esys_GetCapability_Async", error_cleanup); |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WAIT_FOR_GET_CAP0); |
| if (command->public_templ.persistent_handle) { |
| r = Esys_GetCapability_Finish(context->esys, &moreData, capabilityData); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "GetCapablity_Finish", error_cleanup); |
| |
| /* Check whether the handle already exists. */ |
| if ((*capabilityData)->data.handles.count != 0 && |
| (*capabilityData)->data.handles.handle[0] == |
| command->public_templ.persistent_handle) { |
| SAFE_FREE(*capabilityData); |
| goto_error(r, TSS2_FAPI_RC_BAD_VALUE, |
| "SRK persistent handle already defined", error_cleanup); |
| } |
| SAFE_FREE(*capabilityData); |
| } |
| |
| /* Prepare the command for reading the TPMs PCR capabilities. */ |
| r = Esys_GetCapability_Async(context->esys, |
| ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_PCRS, 0, 1); |
| goto_if_error(r, "Esys_GetCapability_Async", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WAIT_FOR_GET_CAP1); |
| r = Esys_GetCapability_Finish(context->esys, &moreData, capabilityData); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "GetCapablity_Finish", error_cleanup); |
| |
| /* Check whether the TPMs PCR capabilities are compatible with the profile. */ |
| TPML_PCR_SELECTION pcr_capability = (*capabilityData)->data.assignedPCR; |
| r = ifapi_check_profile_pcr_selection(&defaultProfile->pcr_selection, |
| &pcr_capability); |
| goto_if_error(r, "Invalid PCR selection in profile.", error_cleanup); |
| |
| SAFE_FREE(*capabilityData); |
| |
| /* Generate template for EK creation. */ |
| r = ifapi_set_key_flags(defaultProfile->ek_template, |
| context->profiles.default_profile.ek_policy ? true : false, |
| &command->public_templ); |
| goto_if_error(r, "Set key flags for SRK", error_cleanup); |
| |
| /* If neither sign nor decrypt is set both flags |
| sign_encrypt and decrypt have to be set. */ |
| attributes = &command->public_templ.public.publicArea.objectAttributes; |
| if ( !(*attributes & TPMA_OBJECT_SIGN_ENCRYPT) && |
| !(*attributes & TPMA_OBJECT_DECRYPT) ) { |
| *attributes |= TPMA_OBJECT_SIGN_ENCRYPT; |
| *attributes |= TPMA_OBJECT_DECRYPT; |
| } |
| |
| r = ifapi_merge_profile_into_template(&context->profiles.default_profile, |
| &command->public_templ); |
| goto_if_error(r, "Merging profile", error_cleanup); |
| |
| /* Clear key object for the primary to be created */ |
| memset(pkey, 0, sizeof(IFAPI_KEY)); |
| |
| /* Prepare the EK generation. */ |
| r = ifapi_init_primary_async(context, TSS2_EK); |
| goto_if_error(r, "Initialize primary", error_cleanup); |
| fallthrough; |
| statecase(context->state, PROVISION_AUTH_EK_AUTH_SENT); |
| fallthrough; |
| |
| statecase(context->state, PROVISION_AUTH_EK_NO_AUTH_SENT); |
| r = ifapi_init_primary_finish(context, TSS2_EK, hierarchy_he); |
| return_try_again(r); |
| goto_if_error(r, "Init primary finish", error_cleanup); |
| |
| /* Check whether a persistent EK handle was defined in profile. */ |
| if (command->public_templ.persistent_handle) { |
| |
| pkey->persistent_handle = command->public_templ.persistent_handle; |
| |
| /* Prepare making the EK permanent. */ |
| r = Esys_EvictControl_Async(context->esys, hierarchy_hs->handle, |
| pkeyObject->handle, ESYS_TR_PASSWORD, ESYS_TR_NONE, |
| ESYS_TR_NONE, pkey->persistent_handle); |
| goto_if_error(r, "Error Esys EvictControl", error_cleanup); |
| context->state = PROVISION_WAIT_FOR_EK_PERSISTENT; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_INIT_GET_CAP2); |
| if (context->config.ek_cert_less == TPM2_YES) { |
| /* Skip certificate validation. */ |
| context->state = PROVISION_EK_WRITE_PREPARE; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| |
| /* Check whether fingerprint for EK is defined in config file. */ |
| hash_alg = context->config.ek_fingerprint.hashAlg; |
| if (hash_alg) { |
| LOG_DEBUG("Only fingerprint check for EK."); |
| if (!(hash_size =ifapi_hash_get_digest_size(hash_alg))) { |
| goto_error(r, TSS2_ESYS_RC_NOT_IMPLEMENTED, |
| "Unsupported hash algorithm (%" PRIu16 ")", error_cleanup, |
| hash_alg); |
| } |
| r = ifapi_get_tpm_key_fingerprint(&pkeyObject->misc.key.public, hash_alg, |
| &ek_fingerprint); |
| goto_if_error_reset_state(r, "Get fingerprint of EK", error_cleanup); |
| |
| if (hash_size != ek_fingerprint.size || |
| memcmp(&context->config.ek_fingerprint.digest, &ek_fingerprint.buffer[0], |
| hash_size) != 0) { |
| goto_error(r, TSS2_FAPI_RC_BAD_KEY, |
| "Fingerprint of EK not equal to fingerprint in config file.", |
| error_cleanup); |
| } |
| /* The fingerprint was found no further certificate processing needed. */ |
| context->state = PROVISION_EK_WRITE_PREPARE; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| |
| /* Check whether EK certificate has to be retrieved */ |
| if (context->config.ek_cert_file) { |
| size_t cert_size; |
| TPM2B_PUBLIC public_key; |
| |
| /* Curl will be used to retrieve the certificate from a file or via HTTP. */ |
| curl_rc = ifapi_get_curl_buffer((unsigned char *)context->config.ek_cert_file, |
| (unsigned char **)&command->pem_cert, &cert_size); |
| if (curl_rc != 0) { |
| goto_error_reset_state(r, TSS2_FAPI_RC_NO_CERT, "Get certificate.", |
| error_cleanup); |
| } |
| |
| /* Get the public key from the certificate. */ |
| r = ifapi_get_public_from_pem_cert(command->pem_cert, &public_key); |
| goto_if_error_reset_state(r, "Get public key from pem certificate", |
| error_cleanup); |
| /* Compare public key of certificate with public data of EK */ |
| if (ifapi_cmp_public_key(&pkeyObject->misc.key.public, &public_key)) { |
| /* The retrieved certificate will be written to keystore, |
| no further certificate processing needed. */ |
| context->state = PROVISION_EK_WRITE_PREPARE; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| goto_error(r, TSS2_FAPI_RC_BAD_KEY, |
| "Public key of EK does not match certificate.", |
| error_cleanup); |
| } |
| |
| /* Prepare the search for certificates store in the NV ram of the TPM. */ |
| r = Esys_GetCapability_Async(context->esys, |
| ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, |
| MIN_EK_CERT_HANDLE, TPM2_MAX_CAP_HANDLES); |
| goto_if_error(r, "Esys_GetCapability_Async", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WAIT_FOR_GET_CAP2); |
| r = Esys_GetCapability_Finish(context->esys, &moreData, capabilityData); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "GetCapablity_Finish", error_cleanup); |
| |
| if ((*capabilityData)->data.handles.count == 0) { |
| /* No more certificate, the EK can be compared with the certificates. */ |
| Esys_Free(*capabilityData); |
| context->state = PROVISION_CHECK_FOR_VENDOR_CERT; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| command->capabilityData = *capabilityData; |
| command->cert_count = (*capabilityData)->data.handles.count; |
| |
| /* Filter out NV handles beyond the EK cert range */ |
| for (size_t i = 0; i < command->cert_count; i++) { |
| if (command->capabilityData->data.handles.handle[i] > EK_CERT_RANGE) { |
| command->cert_count = i; |
| } |
| } |
| |
| if (command->cert_count == 0) { |
| /* No certificates were found the cert range. */ |
| Esys_Free(command->capabilityData); |
| command->capabilityData = NULL; |
| context->state = PROVISION_CHECK_FOR_VENDOR_CERT; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_GET_CERT_NV); |
| |
| /* Detemine the NV index of the certifcate from the read capability data. */ |
| command->cert_nv_idx |
| = command->capabilityData->data.handles.handle[command->cert_count-1]; |
| |
| if ((command->cert_nv_idx % 2) || /**< Certificates will be stored at even address */ |
| command->cert_nv_idx == 0x01c00004 || /**< RSA template */ |
| command->cert_nv_idx == 0x01c0000c) { /**< ECC template */ |
| if (command->cert_count > 1) { |
| command->cert_count -= 1; |
| /* Check next certificate */ |
| context->state = PROVISION_GET_CERT_NV; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } else { |
| /* All certificates have been read, the EK can be written. */ |
| context->state = PROVISION_EK_WRITE_PREPARE; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| } |
| |
| /* Create esys object for the NV object of the certificate. */ |
| r = Esys_TR_FromTPMPublic_Async(context->esys, command->cert_nv_idx, |
| ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE); |
| goto_if_error_reset_state(r, "Esys_TR_FromTPMPublic_Async", error_cleanup); |
| |
| context->state = PROVISION_GET_CERT_NV_FINISH; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_GET_CERT_NV_FINISH); |
| r = Esys_TR_FromTPMPublic_Finish(context->esys, |
| &command->esys_nv_cert_handle); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "TR_FromTPMPublic_Finish", error_cleanup); |
| |
| /* Read public to get size of certificate */ |
| r = Esys_NV_ReadPublic_Async(context->esys, command->esys_nv_cert_handle, |
| ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE); |
| goto_if_error_reset_state(r, "Esys_NV_ReadPublic_Async", error_cleanup); |
| |
| context->state = PROVISION_GET_CERT_READ_PUBLIC; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_GET_CERT_READ_PUBLIC); |
| r = Esys_NV_ReadPublic_Finish(context->esys, &nvPublic, NULL); |
| return_try_again(r); |
| |
| goto_if_error(r, "Error: nv read public", error_cleanup); |
| |
| /* TPMA_NV_NO_DA is set for NV certificate */ |
| nvCmd->nv_object.misc.nv.public.nvPublic.attributes = TPMA_NV_NO_DA; |
| |
| /* Prepare context for nv read */ |
| nvCmd->data_idx = 0; |
| nvCmd->auth_index = ESYS_TR_RH_OWNER; |
| nvCmd->numBytes = nvPublic->nvPublic.dataSize; |
| nvCmd->esys_handle = command->esys_nv_cert_handle; |
| nvCmd->offset = 0; |
| command->pem_cert = NULL; |
| context->session1 = ESYS_TR_PASSWORD; |
| context->session2 = ESYS_TR_NONE; |
| nvCmd->nv_read_state = NV_READ_INIT; |
| memset(&nvCmd->nv_object, 0, sizeof(IFAPI_OBJECT)); |
| SAFE_FREE(nvPublic); |
| |
| context->state = PROVISION_READ_CERT; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_READ_CERT); |
| TPM2B_PUBLIC public_key; |
| const char * root_ca_file; |
| |
| /* The NV object of the certificate will be read asynchronous. */ |
| r = ifapi_nv_read(context, &certData, &certSize); |
| return_try_again(r); |
| goto_if_error_reset_state(r, " FAPI NV_Read", error_cleanup); |
| |
| /* Update storage hierarchy after usage by nv_read. */ |
| *hierarchy_hs = nvCmd->auth_object; |
| |
| /* For storage in key store the certificate will be converted to PEM. */ |
| r = ifapi_cert_to_pem(certData, certSize, &command->pem_cert, |
| &command->cert_key_type, &public_key); |
| SAFE_FREE(certData); |
| goto_if_error(r, "Convert certificate to pem.", error_cleanup); |
| |
| /* Check whether the EKs public key corresponds to the certificate. */ |
| if (ifapi_cmp_public_key(&pkeyObject->misc.key.public, &public_key)) { |
| context->state = PROVISION_PREPARE_READ_ROOT_CERT; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } else { |
| /* Certificate not appropriate for current EK key type */ |
| command->cert_count -= 1; |
| SAFE_FREE(command->pem_cert); |
| if (command->cert_count > 0) { |
| /* Check next certificate */ |
| context->state = PROVISION_GET_CERT_NV; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| } |
| |
| goto_error(r, TSS2_FAPI_RC_NO_CERT, "No EK certificate found.", |
| error_cleanup); |
| |
| statecase(context->state, PROVISION_PREPARE_READ_ROOT_CERT); |
| /* Prepare reading of root certificate. */ |
| root_ca_file = getenv("FAPI_TEST_ROOT_CERT"); |
| if (!root_ca_file) { |
| context->state = PROVISION_EK_CHECK_CERT; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| r = ifapi_io_read_async(&context->io, root_ca_file); |
| return_try_again(r); |
| goto_if_error2(r, "Reading certificate %s", error_cleanup, root_ca_file); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_READ_ROOT_CERT); |
| r = ifapi_io_read_finish(&context->io, (uint8_t **) &command->root_crt, NULL); |
| return_try_again(r); |
| goto_if_error(r, "Reading root certificate failed", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_EK_CHECK_CERT); |
| /* The EK certificate will be verified against the FAPI list of root certificates. */ |
| r = ifapi_verify_ek_cert(command->root_crt, command->intermed_crt, command->pem_cert); |
| SAFE_FREE(command->root_crt); |
| SAFE_FREE(command->intermed_crt); |
| goto_if_error2(r, "Verify EK certificate", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_EK_WRITE_PREPARE); |
| pkeyObject->objectType = IFAPI_KEY_OBJ; |
| pkeyObject->system = command->public_templ.system; |
| strdup_check(pkeyObject->misc.key.certificate, command->pem_cert, r, error_cleanup); |
| SAFE_FREE(command->pem_cert); |
| |
| /* Perform esys serialization if necessary */ |
| r = ifapi_esys_serialize_object(context->esys, |
| pkeyObject); |
| goto_if_error(r, "Prepare serialization", error_cleanup); |
| |
| /* Check whether object already exists in key store.*/ |
| r = ifapi_keystore_object_does_not_exist(&context->keystore, "HE/EK", pkeyObject); |
| goto_if_error_reset_state(r, "Could not write: %s", error_cleanup, "HE/EK"); |
| |
| /* Start writing the EK to the key store */ |
| r = ifapi_keystore_store_async(&context->keystore, &context->io, "HE/EK", |
| pkeyObject); |
| goto_if_error_reset_state(r, "Could not open: %s", error_cleanup, "HE/EK"); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_EK_WRITE); |
| /* Finish writing the EK to the key store */ |
| r = ifapi_keystore_store_finish(&context->keystore, &context->io); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "write_finish failed", error_cleanup); |
| |
| /* Clean objects used for EK computation */ |
| ifapi_cleanup_ifapi_object(pkeyObject); |
| memset(&command->public_templ, 0, sizeof(IFAPI_KEY_TEMPLATE)); |
| fallthrough; |
| |
| statecase(context->state, PROVISION_INIT_SRK); |
| /* Create session which will be used for SRK generation. */ |
| context->srk_handle = context->ek_handle; |
| r = ifapi_get_sessions_async(context, IFAPI_SESSION1, 0, 0); |
| goto_if_error_reset_state(r, "Create sessions", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WAIT_FOR_SRK_SESSION); |
| r = ifapi_get_sessions_finish(context, &context->profiles.default_profile, |
| context->profiles.default_profile.nameAlg); |
| return_try_again(r); |
| |
| goto_if_error_reset_state(r, " FAPI create session", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_PREPARE_LOCKOUT_PARAM); |
| /* The TPM flag lockoutAuthSet will be checked to decide whether a auth value |
| is needed to write the dictionary attack parameters. */ |
| if (!hierarchy_lockout->misc.hierarchy.authPolicy.size) { |
| if (command->auth_state & TPMA_PERMANENT_LOCKOUTAUTHSET) { |
| hierarchy_lockout->misc.hierarchy.with_auth = TPM2_YES; |
| r = ifapi_get_description(hierarchy_lockout, &description); |
| return_if_error(r, "Get description"); |
| r = ifapi_set_auth(context, hierarchy_lockout, description); |
| return_if_error(r, "Set auth value"); |
| } else { |
| hierarchy_lockout->misc.hierarchy.with_auth = TPM2_NO; |
| } |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_AUTHORIZE_LOCKOUT); |
| if (hierarchy_lockout->misc.hierarchy.with_auth == TPM2_YES || |
| hierarchy_lockout->misc.hierarchy.authPolicy.size) { |
| r = ifapi_authorize_object(context, hierarchy_lockout, &auth_session); |
| FAPI_SYNC(r, "Authorize lockout hierarchy.", error_cleanup); |
| } else { |
| auth_session = context->session1; |
| } |
| |
| /* Prepare the setting of the dictionary attack parameters. */ |
| r = Esys_DictionaryAttackParameters_Async(context->esys, ESYS_TR_RH_LOCKOUT, |
| auth_session, ESYS_TR_NONE, ESYS_TR_NONE, |
| defaultProfile->newMaxTries, defaultProfile->newRecoveryTime, |
| defaultProfile->lockoutRecovery); |
| goto_if_error(r, "Error Esys_DictionaryAttackParameters", |
| error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WRITE_LOCKOUT_PARAM); |
| r = Esys_DictionaryAttackParameters_Finish(context->esys); |
| return_try_again(r); |
| |
| goto_if_error_reset_state(r, "DictionaryAttackParameters_Finish", |
| error_cleanup); |
| |
| /* Generate template for SRK creation. */ |
| r = ifapi_set_key_flags(defaultProfile->srk_template, |
| context->profiles.default_profile.srk_policy ? true : false, |
| &command->public_templ); |
| goto_if_error(r, "Set key flags for SRK", error_cleanup); |
| |
| /* If neither sign nor decrypt is set both flags |
| sign_encrypt and decrypt have to be set. */ |
| attributes = &command->public_templ.public.publicArea.objectAttributes; |
| if ( !(*attributes & TPMA_OBJECT_SIGN_ENCRYPT) && |
| !(*attributes & TPMA_OBJECT_DECRYPT) ) { |
| *attributes |= TPMA_OBJECT_SIGN_ENCRYPT; |
| *attributes |= TPMA_OBJECT_DECRYPT; |
| } |
| |
| r = ifapi_merge_profile_into_template(&context->profiles.default_profile, |
| &command->public_templ); |
| goto_if_error(r, "Merging profile and template", error_cleanup); |
| |
| /* Clear key object for the primary to be created */ |
| memset(pkey, 0, sizeof(IFAPI_KEY)); |
| |
| /* Prepare the SRK generation. */ |
| r = ifapi_init_primary_async(context, TSS2_SRK); |
| goto_if_error(r, "Initialize primary", error_cleanup); |
| |
| context->state = PROVISION_AUTH_SRK_NO_AUTH_SENT; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_AUTH_SRK_AUTH_SENT); |
| fallthrough; |
| |
| statecase(context->state, PROVISION_AUTH_SRK_NO_AUTH_SENT); |
| r = ifapi_init_primary_finish(context, TSS2_SRK, hierarchy_hs); |
| return_try_again(r); |
| goto_if_error(r, "Init primary finish.", error_cleanup); |
| |
| /* Check whether a persistent SRK handle was defined in profile. */ |
| if (command->public_templ.persistent_handle) { |
| /* Assign found handle to object */ |
| pkey->persistent_handle = command->public_templ.persistent_handle; |
| |
| /* Prepare making the SRK permanent. */ |
| r = Esys_EvictControl_Async(context->esys, hierarchy_hs->handle, |
| pkeyObject->handle, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, |
| pkey->persistent_handle); |
| goto_if_error(r, "Error Esys EvictControl", error_cleanup); |
| |
| context->state = PROVISION_WAIT_FOR_SRK_PERSISTENT; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| |
| /* No further ESYS command is needed, the keystore object can be written. */ |
| context->state = PROVISION_SRK_WRITE_PREPARE; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_SRK_WRITE_PREPARE); |
| pkeyObject->objectType = IFAPI_KEY_OBJ; |
| pkeyObject->system = command->public_templ.system; |
| |
| /* Perform esys serialization if necessary */ |
| r = ifapi_esys_serialize_object(context->esys, pkeyObject); |
| goto_if_error(r, "Prepare serialization", error_cleanup); |
| |
| /* Check whether object already exists in key store.*/ |
| r = ifapi_keystore_object_does_not_exist(&context->keystore, "HS/SRK", pkeyObject); |
| goto_if_error_reset_state(r, "Could not write: %s", error_cleanup, "HS/SRK"); |
| |
| /* Start writing the SRK to the key store */ |
| r = ifapi_keystore_store_async(&context->keystore, &context->io, "HS/SRK", |
| pkeyObject); |
| goto_if_error_reset_state(r, "Could not open: %s", error_cleanup, "HS/SRK"); |
| context->state = PROVISION_SRK_WRITE; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_SRK_WRITE); |
| /* Finish writing the SRK to the key store */ |
| r = ifapi_keystore_store_finish(&context->keystore, &context->io); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "write_finish failed", error_cleanup); |
| |
| /* Clean objects used for SRK computation */ |
| ifapi_cleanup_ifapi_object(pkeyObject); |
| memset(&command->public_templ, 0, sizeof(IFAPI_KEY_TEMPLATE)); |
| fallthrough; |
| |
| statecase(context->state, PROVISION_CLEAN_EK_SESSION) |
| /* Cleanup the session used for SRK generation. */ |
| r = ifapi_cleanup_session(context); |
| try_again_or_error_goto(r, "Cleanup", error_cleanup); |
| |
| /* Create session which will be used for parameter encryption. */ |
| r = ifapi_get_sessions_async(context, IFAPI_SESSION1, 0, 0); |
| goto_if_error_reset_state(r, "Create sessions", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WAIT_FOR_EK_SESSION); |
| r = ifapi_get_sessions_finish(context, &context->profiles.default_profile, |
| context->profiles.default_profile.nameAlg); |
| return_try_again(r); |
| |
| goto_if_error_reset_state(r, " FAPI create session", error_cleanup); |
| |
| /* |
| * Adaption of the lockout hierarchy to the passed parameters |
| * and the current profile. |
| */ |
| if (!command->authValueLockout || |
| strcmp(command->authValueLockout, "") == 0) { |
| context->state = PROVISION_LOCKOUT_CHANGE_POLICY; |
| /* Auth value of lockout hierarchy will not be changed. */ |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| |
| if (strlen(command->authValueLockout) > sizeof(TPMU_HA)) { |
| goto_error(r, TSS2_FAPI_RC_BAD_VALUE, |
| "Password too long.", error_cleanup); |
| } |
| /* Copy auth value into context. */ |
| memcpy(&command->hierarchy_auth.buffer[0], |
| command->authValueLockout, strlen(command->authValueLockout)); |
| command->hierarchy_auth.size = strlen(command->authValueLockout); |
| context->hierarchy_policy_state = HIERARCHY_CHANGE_POLICY_INIT; |
| if (defaultProfile->lockout_policy) { |
| command->hierarchy_policy = |
| ifapi_copy_policy(defaultProfile->lockout_policy); |
| goto_if_null2(command->hierarchy_policy, "Out of memory.", r, |
| TSS2_FAPI_RC_MEMORY, error_cleanup); |
| } else { |
| command->hierarchy_policy = NULL; |
| } |
| context->state = PROVISION_CHANGE_LOCKOUT_AUTH; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| |
| statecase(context->state, PROVISION_WAIT_FOR_SRK_PERSISTENT); |
| r = Esys_EvictControl_Finish(context->esys, &pkeyObject->handle); |
| return_try_again(r); |
| goto_if_error(r, "Evict control failed", error_cleanup); |
| |
| /* The SRK was made persistent and can be written to key store. */ |
| command->srk_tpm_handle = pkeyObject->misc.key.persistent_handle; |
| command->srk_esys_handle = pkeyObject->handle; |
| context->state = PROVISION_SRK_WRITE_PREPARE; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| |
| statecase(context->state, PROVISION_WAIT_FOR_EK_PERSISTENT); |
| r = Esys_EvictControl_Finish(context->esys, &pkeyObject->handle); |
| return_try_again(r); |
| |
| /* Retry with authorization callback after trial with null auth */ |
| if (number_rc(r) == TPM2_RC_BAD_AUTH && |
| hierarchy_hs->misc.hierarchy.with_auth == TPM2_NO) { |
| char* description; |
| r = ifapi_get_description(hierarchy_hs, &description); |
| return_if_error(r, "Get description"); |
| |
| r = ifapi_set_auth(context, hierarchy_hs, "CreatePrimary"); |
| SAFE_FREE(description); |
| goto_if_error_reset_state(r, "Create EK", error_cleanup); |
| |
| hierarchy_hs->misc.hierarchy.with_auth = TPM2_YES; |
| context->state = PROVISION_WAIT_FOR_SRK_PERSISTENT; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| goto_if_error(r, "Evict control failed", error_cleanup); |
| |
| command->ek_tpm_handle = pkeyObject->misc.key.persistent_handle; |
| command->ek_esys_handle = pkeyObject->handle; |
| |
| context->state = PROVISION_INIT_GET_CAP2; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| |
| statecase(context->state, PROVISION_CHANGE_LOCKOUT_AUTH); |
| |
| /* If a lockout auth value is provided by profile, the auth value |
| if the hierarchy will be changed asynchronous. */ |
| r = ifapi_change_auth_hierarchy(context, ESYS_TR_RH_LOCKOUT, |
| &command->hierarchy_lockout, &command->hierarchy_auth); |
| return_try_again(r); |
| goto_if_error(r, "Change auth hierarchy.", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_LOCKOUT_CHANGE_POLICY); |
| /* If a lockout policy is provided by profile, the policy will be |
| assigned to lockout hierarchy asynchronous. */ |
| r = ifapi_change_policy_hierarchy(context, ESYS_TR_RH_LOCKOUT, |
| hierarchy_lockout, |
| command->hierarchy_policy); |
| return_try_again(r); |
| goto_if_error(r, "Change policy LOCKOUT", error_cleanup); |
| |
| /* Check whether object already exists in key store.*/ |
| r = ifapi_keystore_object_does_not_exist(&context->keystore, "/LOCKOUT", |
| &command->hierarchy_lockout); |
| goto_if_error_reset_state(r, "Could not write: %s", error_cleanup, "/LOCKOUT"); |
| |
| /* Start writing the lockout hierarchy object to the key store */ |
| r = ifapi_keystore_store_async(&context->keystore, &context->io, "/LOCKOUT", |
| &command->hierarchy_lockout); |
| goto_if_error_reset_state(r, "Could not open: %s", |
| error_cleanup, "/LOCKOUT"); |
| |
| context->state = PROVISION_WRITE_LOCKOUT; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WRITE_LOCKOUT); |
| /* Finish writing the lockout hierarchy to the key store */ |
| r = ifapi_keystore_store_finish(&context->keystore, &context->io); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "write_finish failed", error_cleanup); |
| |
| /* Write all lockout hierarchies. */ |
| command->hierarchy = hierarchy_lockout; |
| command->path_idx = 0; |
| if (command->numHierarchyObjects) { |
| context->state = PROVISION_WRITE_HIERARCHIES; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_CHANGE_EH_CHECK); |
| context->hierarchy_policy_state = HIERARCHY_CHANGE_POLICY_INIT; |
| if (defaultProfile->eh_policy) { |
| command->hierarchy_policy = ifapi_copy_policy(defaultProfile->eh_policy); |
| goto_if_null2(command->hierarchy_policy, "Out of memory", r, |
| TSS2_FAPI_RC_MEMORY, error_cleanup); |
| } else { |
| command->hierarchy_policy = NULL; |
| } |
| if (command->authValueEh) { |
| context->state = PROVISION_CHANGE_EH_AUTH; |
| /* Save auth value of endorsement hierarchy in context. */ |
| memcpy(&command->hierarchy_auth.buffer[0], command->authValueEh, |
| strlen(command->authValueEh)); |
| command->hierarchy_auth.size = strlen(command->authValueEh); |
| } else { |
| /* Auth value of lockout hierarchy will not be changed. */ |
| context->state = PROVISION_EH_CHANGE_POLICY; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| context->state = PROVISION_CHANGE_EH_AUTH; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_CHANGE_EH_AUTH); |
| /* If an endorsement hierarchy auth value is provided by profile, |
| the auth value will be changed asynchronous. */ |
| r = ifapi_change_auth_hierarchy(context, ESYS_TR_RH_ENDORSEMENT, |
| &command->hierarchy_he, &command->hierarchy_auth); |
| return_try_again(r); |
| goto_if_error(r, "Change auth hierarchy.", error_cleanup); |
| fallthrough; |
| |
| statecase(context->state, PROVISION_EH_CHANGE_POLICY); |
| /* If an endorsement hierarchy policy is provided by profile, |
| the policy will be assigned asynchronous. */ |
| r = ifapi_change_policy_hierarchy(context, ESYS_TR_RH_ENDORSEMENT, |
| hierarchy_he, |
| command->hierarchy_policy); |
| return_try_again(r); |
| goto_if_error(r, "Change policy EH", error_cleanup); |
| |
| /* Check whether object already exists in key store.*/ |
| r = ifapi_keystore_object_does_not_exist(&context->keystore, "/HE", |
| &command->hierarchy_he); |
| goto_if_error_reset_state(r, "Could not write: %s", error_cleanup, "/HE"); |
| |
| /* Start writing the endorsement hierarchy object to the key store */ |
| r = ifapi_keystore_store_async(&context->keystore, &context->io, "/HE", |
| hierarchy_he); |
| goto_if_error_reset_state(r, "Could not open: %s", error_cleanup, "/HE"); |
| |
| context->state = PROVISION_WRITE_EH; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WRITE_EH); |
| /* Finish writing the endorsement hierarchy to the key store */ |
| r = ifapi_keystore_store_finish(&context->keystore, &context->io); |
| return_try_again(r); |
| return_if_error_reset_state(r, "write_finish failed"); |
| |
| /* Write all endorsement hierarchies. */ |
| command->hierarchy = hierarchy_he; |
| command->path_idx = 0; |
| if (command->numHierarchyObjects) { |
| context->state = PROVISION_WRITE_HIERARCHIES; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_CHANGE_SH_CHECK); |
| context->hierarchy_policy_state = HIERARCHY_CHANGE_POLICY_INIT; |
| if (defaultProfile->sh_policy) { |
| command->hierarchy_policy = ifapi_copy_policy(defaultProfile->sh_policy); |
| goto_if_null2(command->hierarchy_policy, "Out of memory", r, |
| TSS2_FAPI_RC_MEMORY, error_cleanup); |
| } else { |
| command->hierarchy_policy = NULL; |
| } |
| if (command->authValueSh) { |
| context->state = PROVISION_CHANGE_SH_AUTH; |
| /* Save auth value of owner hierarchy in context. */ |
| memcpy(&command->hierarchy_auth.buffer[0], command->authValueSh, |
| strlen(command->authValueSh)); |
| command->hierarchy_auth.size = strlen(command->authValueSh); |
| context->state = PROVISION_CHANGE_SH_AUTH; |
| } else { |
| /* Auth value of owner hierarchy will not be changed. */ |
| context->state = PROVISION_SH_CHANGE_POLICY; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| context->state = PROVISION_CHANGE_SH_AUTH; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_CHANGE_SH_AUTH); |
| /* If an owner hierarchy auth value is provided by profile, |
| the auth value will be changed asynchronous. */ |
| context->hierarchy_policy_state = HIERARCHY_CHANGE_POLICY_INIT; |
| r = ifapi_change_auth_hierarchy(context, ESYS_TR_RH_OWNER, |
| &command->hierarchy_hs, &command->hierarchy_auth); |
| return_try_again(r); |
| goto_if_error(r, "Change auth hierarchy.", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_SH_CHANGE_POLICY); |
| /* If an owner hierarchy policy is provided by profile, |
| the policy will be assigned asynchronous. */ |
| r = ifapi_change_policy_hierarchy(context, ESYS_TR_RH_OWNER, |
| hierarchy_hs, |
| command->hierarchy_policy); |
| return_try_again(r); |
| goto_if_error(r, "Change policy SH", error_cleanup); |
| |
| /* Check whether object already exists in key store.*/ |
| r = ifapi_keystore_object_does_not_exist(&context->keystore, "/HS", |
| hierarchy_hs); |
| goto_if_error_reset_state(r, "Could not write: %s", error_cleanup, "/HS"); |
| |
| /* Start writing the owner hierarchy object to the key store */ |
| r = ifapi_keystore_store_async(&context->keystore, &context->io, "/HS", |
| &command->hierarchy_hs); |
| goto_if_error_reset_state(r, "Could not open: %s", error_cleanup, "/HS"); |
| context->state = PROVISION_WRITE_SH; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WRITE_SH); |
| /* The onwer hierarchy object will be written to key store. */ |
| r = ifapi_keystore_store_finish(&context->keystore, &context->io); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "write_finish failed", error_cleanup); |
| |
| /* Write all storage hierarchies. */ |
| command->hierarchy = hierarchy_hs; |
| command->path_idx = 0; |
| if (command->numHierarchyObjects) { |
| context->state = PROVISION_WRITE_HIERARCHIES; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_PREPARE_NULL); |
| command->hierarchy = hierarchy_hn; |
| /* Start writing the null hierarchy object to the key store */ |
| r = ifapi_keystore_store_async(&context->keystore, &context->io, "/HN", |
| &command->hierarchy_hn); |
| goto_if_error_reset_state(r, "Could not open: %s", error_cleanup, "/HN"); |
| context->state = PROVISION_WRITE_NULL; |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WRITE_NULL); |
| /* The null hierarchy object will be written to key store. */ |
| r = ifapi_keystore_store_finish(&context->keystore, &context->io); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "write_finish failed", error_cleanup); |
| |
| command->hierarchy = hierarchy_hn; |
| command->path_idx = 0; |
| if (command->numHierarchyObjects) { |
| context->state = PROVISION_WRITE_HIERARCHIES; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_FINISHED); |
| if (context->srk_handle != ESYS_TR_NONE) { |
| /* Prepare the flushing of a non persistent SRK. */ |
| r = Esys_FlushContext_Async(context->esys, context->srk_handle); |
| goto_if_error(r, "Flush SRK", error_cleanup); |
| } |
| fallthrough; |
| |
| /* Flush the SRK if not persistent */ |
| statecase(context->state, PROVISION_FLUSH_SRK); |
| if (context->srk_handle != ESYS_TR_NONE) { |
| r = Esys_FlushContext_Finish(context->esys); |
| try_again_or_error_goto(r, "Flush SRK", error_cleanup); |
| |
| context->srk_handle = ESYS_TR_NONE; |
| |
| } |
| if (context->ek_handle != ESYS_TR_NONE) { |
| /* Prepare the flushing of a non persistent EK. */ |
| r = Esys_FlushContext_Async(context->esys, context->ek_handle); |
| goto_if_error(r, "Flush EK", error_cleanup); |
| } |
| fallthrough; |
| |
| /* Flush the EK if not persistent */ |
| statecase(context->state, PROVISION_FLUSH_EK); |
| if (context->ek_handle != ESYS_TR_NONE) { |
| r = Esys_FlushContext_Finish(context->esys); |
| try_again_or_error_goto(r, "Flush EK", error_cleanup); |
| |
| context->ek_handle = ESYS_TR_NONE; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_CLEAN_SRK_SESSION); |
| /* Cleanup the session used for parameter encryption */ |
| r = ifapi_cleanup_session(context); |
| try_again_or_error_goto(r, "Cleanup", error_cleanup); |
| |
| context->state =_FAPI_STATE_INIT; |
| break; |
| |
| statecase(context->state, PROVISION_WRITE_HIERARCHIES); |
| /* Write the current hierarchy to all hierarchies if this type which |
| exist in the keystore. If all files are written the provisioning |
| is continued at the appropriate next state. */ |
| if (command->path_idx == command->numHierarchyObjects) { |
| if (command->hierarchy->handle == ESYS_TR_RH_OWNER) |
| context->state = PROVISION_PREPARE_NULL; |
| if (command->hierarchy->handle == ESYS_TR_RH_NULL) |
| context->state = PROVISION_FINISHED; |
| else if (command->hierarchy->handle == ESYS_TR_RH_ENDORSEMENT) |
| context->state = PROVISION_CHANGE_SH_CHECK; |
| else if (command->hierarchy->handle == ESYS_TR_RH_LOCKOUT) |
| context->state = PROVISION_CHANGE_EH_CHECK; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| if (command->hierarchies[command->path_idx].handle == command->hierarchy->handle) { |
| r = ifapi_keystore_store_async(&context->keystore, &context->io, |
| command->pathlist[command->path_idx], |
| command->hierarchy); |
| goto_if_error_reset_state(r, "Could not open: %s", error_cleanup, |
| command->pathlist[command->path_idx]); |
| } else { |
| command->path_idx +=1; |
| context->state = PROVISION_WRITE_HIERARCHIES; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| fallthrough; |
| |
| statecase(context->state, PROVISION_WRITE_HIERARCHY); |
| /* Finish writing the hierarchy to the key store */ |
| r = ifapi_keystore_store_finish(&context->keystore, &context->io); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "write_finish failed", error_cleanup); |
| |
| command->path_idx +=1; |
| context->state = PROVISION_WRITE_HIERARCHIES; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| |
| /* |
| * The vendor ID stored in the TPM has to be read to check whether |
| * a special certificate handling is needed. |
| */ |
| statecase(context->state, PROVISION_CHECK_FOR_VENDOR_CERT); |
| /* Prepare reading the vendor ID */ |
| r = Esys_GetCapability_Async(context->esys, |
| ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, |
| TPM2_CAP_TPM_PROPERTIES, TPM2_PT_MANUFACTURER, 1); |
| goto_if_error(r, "Esys_GetCapability_Async", error_cleanup); |
| |
| fallthrough; |
| |
| statecase(context->state, PROVISION_GET_VENDOR); |
| /* Get the vendor ID asynchronous. */ |
| r = Esys_GetCapability_Finish(context->esys, &moreData, capabilityData); |
| return_try_again(r); |
| goto_if_error_reset_state(r, "GetCapablity_Finish", error_cleanup); |
| |
| if ((*capabilityData)->data.tpmProperties.tpmProperty[0].value == VENDOR_INTC) { |
| /* Get INTEL certificate for EK public hash via web */ |
| uint8_t *cert_buffer = NULL; |
| size_t cert_size; |
| TPM2B_PUBLIC public; |
| r = ifapi_get_intl_ek_certificate(context, &pkey->public, &cert_buffer, |
| &cert_size); |
| goto_if_error_reset_state(r, "Get certificates", error_cleanup); |
| |
| r = ifapi_cert_to_pem(cert_buffer, cert_size, &command->pem_cert, |
| NULL, &public); |
| SAFE_FREE(cert_buffer); |
| goto_if_error_reset_state(r, "Convert certificate buffer to PEM.", |
| error_cleanup); |
| } else { |
| /* No certificate was stored in the TPM and ek_cert_less was not set.*/ |
| goto_error(r, TSS2_FAPI_RC_NO_CERT, |
| "No EK certifcate found for current crypto profile found. " |
| "You may want to switch the profile in fapi-config or " |
| "set the ek_cert_less or ek_cert_file options in fapi-config. " |
| "See also https://tpm2-software.github.io/2020/07/22/Fapi_Crypto_Profiles.html", |
| error_cleanup); |
| } |
| |
| SAFE_FREE(*capabilityData); |
| context->state = PROVISION_EK_WRITE_PREPARE; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| |
| statecasedefault(context->state); |
| } |
| |
| error_cleanup: |
| /* Primaries might not have been flushed in error cases */ |
| if (r) |
| error_cleanup_provisioning(context); |
| ifapi_cleanup_ifapi_object(pkeyObject); |
| ifapi_cleanup_ifapi_object(hierarchy_hs); |
| ifapi_cleanup_ifapi_object(hierarchy_he); |
| ifapi_cleanup_ifapi_object(hierarchy_hn); |
| ifapi_cleanup_ifapi_object(hierarchy_lockout); |
| for (size_t i = 0; i < command->numPaths; i++) { |
| SAFE_FREE(command->pathlist[i]); |
| } |
| SAFE_FREE(command->pathlist); |
| ifapi_primary_clean(context); |
| ifapi_primary_clean(context); |
| SAFE_FREE(command->root_crt); |
| SAFE_FREE(*capabilityData); |
| SAFE_FREE(command->authValueLockout); |
| SAFE_FREE(command->authValueEh); |
| SAFE_FREE(command->authValueSh); |
| SAFE_FREE(command->pem_cert); |
| SAFE_FREE(certData); |
| SAFE_FREE(nvPublic); |
| if (command->numHierarchyObjects > 0) { |
| for (i = 0; i < command->numHierarchyObjects; i++) { |
| ifapi_cleanup_ifapi_object(&command->hierarchies[i]); |
| } |
| SAFE_FREE(command->hierarchies); |
| } |
| if (command->pathlist) { |
| for (size_t i = 0; i < command->numPaths; i++) { |
| SAFE_FREE(command->pathlist[i]); |
| } |
| SAFE_FREE(command->pathlist); |
| } |
| LOG_TRACE("finished"); |
| return r; |
| } |