/* 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" // IWYU pragma: keep
#endif

#include <json.h>    // for json_object_put, json_object_t...
#include <stdlib.h>  // for free, calloc, malloc
#include <string.h>  // for memcpy, strcmp, memset, strlen
#include <strings.h> // for strcasecmp

#include "fapi_crypto.h" // for ifapi_get_hash_alg_for_size
#include "fapi_util.h"
#include "ifapi_config.h"             // for IFAPI_CONFIG
#include "ifapi_helpers.h"            // for free_string_list, ifapi_path_l...
#include "ifapi_json_serialize.h"     // for ifapi_json_IFAPI_OBJECT_serialize
#include "ifapi_keystore.h"           // for ifapi_check_provisioned
#include "ifapi_macros.h"             // for statecase, fallthrough, goto_i...
#include "ifapi_policy.h"             // for ifapi_calculate_tree
#include "ifapi_policyutil_execute.h" // for ifapi_policyutil_execute, ifap...
#include "tss2_policy.h"              // for TSS2_OBJECT
#include "util/aux_util.h"            // for goto_error

#define LOGMODULE fapi
#include "util/log.h" // for SAFE_FREE, goto_if_error, retu...

/** State machine for flushing objects.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] handle of the object to be flushed.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 */
TSS2_RC
ifapi_flush_object(FAPI_CONTEXT *context, ESYS_TR handle) {
    TSS2_RC r = TSS2_RC_SUCCESS;

    if (handle == ESYS_TR_NONE)
        return r;

    switch (context->flush_object_state) {
    statecase(context->flush_object_state, FLUSH_INIT);
        r = Esys_FlushContext_Async(context->esys, handle);
        return_if_error(r, "Flush Object");
        fallthrough;

    statecase(context->flush_object_state, WAIT_FOR_FLUSH);
        r = Esys_FlushContext_Finish(context->esys);
        if (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN)
            return TSS2_FAPI_RC_TRY_AGAIN;

        context->flush_object_state = FLUSH_INIT;
        return_if_error(r, "FlushContext");

        return TSS2_RC_SUCCESS;

    statecasedefault(context->flush_object_state);
    }
}

/** Preparation for getting a session handle.
 *
 * The corresponding async call be executed and a session secret for encryption
 * TPM2B parameters will be created.
 *
 * @param[in] esys The ESYS_CONTEXT.
 * @param[in] saltkey The key which will be used for the encryption of the session
 *            secret.
 * @param[in] profile The FAPI profile will be used to adjust the sessions symmetric
 *            parameters.
 * @param[in] hashAlg The hash algorithm used for the session.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 */
TSS2_RC
ifapi_get_session_async(ESYS_CONTEXT        *esys,
                        ESYS_TR              saltkey,
                        const IFAPI_PROFILE *profile,
                        TPMI_ALG_HASH        hashAlg) {
    TSS2_RC r;

    r = Esys_StartAuthSession_Async(esys, saltkey, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                    ESYS_TR_NONE, NULL, TPM2_SE_HMAC, &profile->session_symmetric,
                                    hashAlg);
    return_if_error(r, "Creating session.");

    return TSS2_RC_SUCCESS;
}

/**  Call for getting a session handle and adjust session parameters.
 *
 * @param[in] esys The ESYS_CONTEXT.
 * @param[out] session The session handle.
 * @param[in] flags The flags to adjust the session attributes.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 */
TSS2_RC
ifapi_get_session_finish(ESYS_CONTEXT *esys, ESYS_TR *session, TPMA_SESSION flags) {
    TSS2_RC      r;
    TPMA_SESSION sessionAttributes = 0;

    /* Check whether authorization callback is defined */

    r = Esys_StartAuthSession_Finish(esys, session);
    if (r != TSS2_RC_SUCCESS)
        return r;

    sessionAttributes |= flags;
    sessionAttributes |= TPMA_SESSION_CONTINUESESSION;

    r = Esys_TRSess_SetAttributes(esys, *session, sessionAttributes, 0xff);
    return_if_error(r, "Set session attributes.");

    return TSS2_RC_SUCCESS;
}

/** Get the digest size of the policy of a FAPI object.
 *
 * @param[in] object The object with the correspodning policy.
 *
 * @retval The size of policy digest.
 * @retval 0 if The object does not have a policy.
 */
static size_t
policy_digest_size(IFAPI_OBJECT *object) {
    switch (object->objectType) {
    case IFAPI_KEY_OBJ:
        return object->misc.key.public.publicArea.authPolicy.size;
    case IFAPI_NV_OBJ:
        return object->misc.nv.public.nvPublic.authPolicy.size;
    case IFAPI_HIERARCHY_OBJ:
        return object->misc.hierarchy.authPolicy.size;
    default:
        return 0;
    }
}

/** Initialize and expand the linked list representing a FAPI key path.
 *
 * From a passed key path the explicit key path will be determined. The
 * profile and the hierarchy will be added if necessary and the extension
 * is possible.
 *
 * @param[in]  context_profile The profile used for extension of no profile is
 *             part of the path.
 * @param[in]  ipath The implicit pathname which has to be extended.
 * @param[out] list_node1 The linked list for the passed key path without
 *             extensions.
 * @param[out] current_list_node The current node in the list list_node1,
 *             which represent the tail not processed.
 * @param[out] result The part of the new list which had been extended
 *             without the tail not processed.
 *
 * @retval TSS2_RC_SUCCESS: If the initialization was successful.
 * @retval TSS2_FAPI_RC_BAD_VALUE If an invalid path was passed.
 * @retval TSS2_FAPI_RC_MEMORY: if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
 *         or contains illegal characters.
 */

static TSS2_RC
init_explicit_key_path(const char  *context_profile,
                       const char  *ipath,
                       NODE_STR_T **list_node1,
                       NODE_STR_T **current_list_node,
                       NODE_STR_T **result) {
    *list_node1 = split_string(ipath, IFAPI_FILE_DELIM);
    NODE_STR_T *list_node = *list_node1;
    char const *profile;
    char       *hierarchy;
    TSS2_RC     r = TSS2_RC_SUCCESS;

    *result = NULL;
    if (list_node == NULL) {
        LOG_ERROR("Invalid path");
        free_string_list(*list_node1);
        return TSS2_FAPI_RC_BAD_VALUE;
    }

    /* Processing of the profile. */
    if (strncmp("P_", list_node->str, 2) == 0) {
        profile = list_node->str;
        list_node = list_node->next;
    } else {
        profile = context_profile;
    }
    /* Create the initial node of the linked list. */
    *result = init_string_list(profile);
    if (*result == NULL) {
        free_string_list(*list_node1);
        LOG_ERROR("Out of memory");
        return TSS2_FAPI_RC_MEMORY;
    }
    if (list_node == NULL) {
        /* Storage hierarchy will be used as default. */
        hierarchy = "HS";
    } else {
        if (strcmp(list_node->str, "HN") == 0) {
            hierarchy = list_node->str;
            list_node = list_node->next;
        } else if (strcmp(list_node->str, "HS") == 0 || strcmp(list_node->str, "HE") == 0
                   || strcmp(list_node->str, "HN") == 0) {
            hierarchy = list_node->str;
            list_node = list_node->next;
        } else if (strcmp(list_node->str, "EK") == 0) {
            /* The hierarchy for an endorsement key will be added. */
            hierarchy = "HE";
        } else if (list_node->str != NULL && strcmp(list_node->str, "SRK") == 0) {
            /* The storage hierachy will be added. */
            hierarchy = "HS";
        } else {
            LOG_ERROR("Hierarchy cannot be determined.");
            r = TSS2_FAPI_RC_BAD_PATH;
            goto error;
        }
    }
    /* Add the used hierarchy to the linked list. */
    if (!add_string_to_list(*result, hierarchy)) {
        LOG_ERROR("Out of memory");
        r = TSS2_FAPI_RC_MEMORY;
        goto error;
    }
    if (list_node == NULL) {
        goto_error(r, TSS2_FAPI_RC_BAD_PATH, "Explicit path can't be determined.", error);
    }

    /* Add the primary directory to the linked list. */
    if (!add_string_to_list(*result, list_node->str)) {
        LOG_ERROR("Out of memory");
        r = TSS2_FAPI_RC_MEMORY;
        goto error;
    }

    if (strcmp(hierarchy, "HS") == 0 && strcmp(list_node->str, "EK") == 0) {
        LOG_ERROR("Key EK cannot be create in the storage hierarchy.");
        r = TSS2_FAPI_RC_BAD_PATH;
        goto error;
    }

    if (strcmp(hierarchy, "HE") == 0 && strcmp(list_node->str, "SRK") == 0) {
        LOG_ERROR("Key EK cannot be create in the endorsement hierarchy.");
        r = TSS2_FAPI_RC_BAD_PATH;
        goto error;
    }

    if (strcmp(hierarchy, "HN") == 0
        && (strcmp(list_node->str, "SRK") == 0 || strcmp(list_node->str, "EK") == 0)) {
        LOG_ERROR("Key EK and SRK cannot be created in NULL hierarchy.");
        r = TSS2_FAPI_RC_BAD_PATH;
        goto error;
    }

    /* Return the rest of the path. */
    *current_list_node = list_node->next;
    return TSS2_RC_SUCCESS;

error:
    free_string_list(*result);
    *result = NULL;
    free_string_list(*list_node1);
    *list_node1 = NULL;
    return r;
}

/** Free first object of a linked list.
 *
 * Note: Referenced objects of the list have to be freed before.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 */
static TSS2_RC
pop_object_from_list(NODE_OBJECT_T **object_list) {
    return_if_null(*object_list, "Pop from list.", TSS2_FAPI_RC_BAD_REFERENCE);

    NODE_OBJECT_T *head = *object_list;
    NODE_OBJECT_T *next = head->next;
    *object_list = next;
    free(head);
    return TSS2_RC_SUCCESS;
}

/** Get relative path of a FAPI object.
 *
 * @param[in] object The internal FAPI object.
 *
 * @retval The relative path of the object.
 * @retval NULL if no path is available.
 */
const char *
ifapi_get_object_path(IFAPI_OBJECT *object) {
    if (object->rel_path)
        return object->rel_path;

    /* For hierarchies the path might not be set during reading
       from keystore. */
    if (object->objectType == IFAPI_HIERARCHY_OBJ) {
        switch (object->public.handle) {
        case ESYS_TR_RH_NULL:
            return "/HN";
        case ESYS_TR_RH_OWNER:
            return "/HS";
        case ESYS_TR_RH_ENDORSEMENT:
            return "/HE";
        case ESYS_TR_RH_LOCKOUT:
            return "/LOCKOUT";
        default:
            LOG_ERROR("This code should not be reachable");
            return NULL;
        }
    }
    return NULL;
}

/** Set authorization value for a primary key to be created.
 *
 * The callback which provides the auth value must be defined.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     object The auth value will be assigned to this object.
 * @param[in,out] inSensitive The sensitive data to store the auth value.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN If the callback for getting
 *         the auth value is not defined.
 */
TSS2_RC
ifapi_set_auth_primary(FAPI_CONTEXT          *context,
                       IFAPI_OBJECT          *object,
                       TPMS_SENSITIVE_CREATE *inSensitive) {
    TSS2_RC     r;
    const char *auth = NULL;
    const char *obj_path;

    memset(inSensitive, 0, sizeof(TPMS_SENSITIVE_CREATE));

    if (!object->misc.key.with_auth) {
        /* Auth value for esys object was already set during creation
           of primary key. */
        return TSS2_RC_SUCCESS;
    }

    obj_path = ifapi_get_object_path(object);

    /* Check whether callback is defined. */
    if (context->callbacks.auth) {
        r = context->callbacks.auth(obj_path, object->misc.key.description, &auth,
                                    context->callbacks.authData);
        return_if_error(r, "AuthCallback");
        if (auth != NULL) {
            inSensitive->userAuth.size = strlen(auth);
            memcpy(&inSensitive->userAuth.buffer[0], auth, inSensitive->userAuth.size);
        }
        object->misc.key.auth_primary_set = true;
        return TSS2_RC_SUCCESS;
    }
    SAFE_FREE(auth);
    return_error(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN, "Authorization callback not defined.");
}

/** Set authorization value for a FAPI object.
 *
 * The callback which provides the auth value must be defined.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     auth_object The auth value will be assigned to this object.
 * @param[in]     description The description will be passed to the callback
 *                which delivers the auth value.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN If the callback for getting
 *         the auth value is not defined.
 */
TSS2_RC
ifapi_set_auth(FAPI_CONTEXT *context, IFAPI_OBJECT *auth_object, const char *description) {
    TSS2_RC     r;
    const char *auth = NULL;
    TPM2B_AUTH  authValue = { .size = 0, .buffer = { 0 } };
    const char *obj_path;

    obj_path = ifapi_get_object_path(auth_object);

    /* Check whether callback is defined. */
    if (auth_object->objectType == IFAPI_KEY_OBJ && auth_object->misc.key.auth_primary_set) {
        return TSS2_RC_SUCCESS;
    }
    if (context->callbacks.auth) {
        r = context->callbacks.auth(obj_path, description, &auth, context->callbacks.authData);
        return_if_error(r, "policyAuthCallback");
        if (auth != NULL) {
            authValue.size = strlen(auth);
            if (authValue.size > sizeof(TPMU_HA)) {
                return_error2(TSS2_FAPI_RC_BAD_VALUE, "Size of auth value %" PRIu16 " > %zu",
                              authValue.size, sizeof(TPMU_HA));
            }
            memcpy(&authValue.buffer[0], auth, authValue.size);
        }

        /* Store auth value in the ESYS object. */
        r = Esys_TR_SetAuth(context->esys, auth_object->public.handle, &authValue);
        return_if_error(r, "Set auth value.");

        if (auth_object->objectType == IFAPI_NV_OBJ && object_with_auth(auth_object)
            && authValue.size == 0) {
            auth_object->auth_changed = true;
        }
        if (auth_object->objectType == IFAPI_HIERARCHY_OBJ)
            auth_object->misc.hierarchy.authorized = true;

        return TSS2_RC_SUCCESS;
    }
    SAFE_FREE(auth);
    return_error(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN, "Authorization callback not defined.");
}

/** Preparation for getting a free handle after a start handle number.
 *
 * @param[in] fctx The FAPI_CONTEXT.
 * @param[in] handle The start value for handle search.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 */
TSS2_RC
ifapi_get_free_handle_async(FAPI_CONTEXT *fctx, TPM2_HANDLE *handle) {
    TSS2_RC r = Esys_GetCapability_Async(fctx->esys, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                         TPM2_CAP_HANDLES, *handle, 1);
    return_if_error(r, "GetCapability");
    return r;
}

/** Execution of get capability until a free handle is found.
 *
 * The get capability method is called until a free handle is found
 * or the max number of trials passe to the function is exeeded.
 *
 * @param[in] fctx The FAPI_CONTEXT.
 * @param[out] handle The free handle.
 * @param[in] max The maximal number of trials.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_NV_TOO_SMALL if too many NV handles are defined.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
 *         this function needs to be called again.
 */
TSS2_RC
ifapi_get_free_handle_finish(FAPI_CONTEXT *fctx, TPM2_HANDLE *handle, TPM2_HANDLE max) {
    TPMI_YES_NO           moreData;
    TPMS_CAPABILITY_DATA *capabilityData = NULL;
    TSS2_RC               r = Esys_GetCapability_Finish(fctx->esys, &moreData, &capabilityData);

    if (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN)
        return TSS2_FAPI_RC_TRY_AGAIN;

    return_if_error(r, "GetCapability");

    if (capabilityData->data.handles.count == 0
        || capabilityData->data.handles.handle[0] != *handle) {
        SAFE_FREE(capabilityData);
        return TSS2_RC_SUCCESS;
    }
    SAFE_FREE(capabilityData);
    *handle += 1;
    if (*handle > max) {
        return_error(TSS2_FAPI_RC_NV_TOO_SMALL, "No NV index free.");
    }

    r = ifapi_get_free_handle_async(fctx, handle);
    return_if_error(r, "GetCapability");

    return TSS2_FAPI_RC_TRY_AGAIN;
}

/** Create a linked list of directories in the key store.
 *
 * If the absolute path in key store is not defined the list will
 * be extended if possible.
 *
 * @param[out] keystore The used keystore.
 * @param[in] ipath The implicit pathname, which might be extended.
 * @param[out] The linked list of directories in the explicit pathname.
 *
 * @retval TSS2_RC_SUCCESS If the keystore can be initialized.
 * @retval TSS2_FAPI_RC_IO_ERROR If the user part of the keystore can't be
 *         initialized.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated.
 * @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.
 */
static TSS2_RC
get_explicit_key_path(IFAPI_KEYSTORE *keystore, const char *ipath, NODE_STR_T **result) {
    NODE_STR_T *list_node1 = NULL;
    NODE_STR_T *list_node = NULL;

    /* Extend the first part of the list if necessary. */
    TSS2_RC r
        = init_explicit_key_path(keystore->defaultprofile, ipath, &list_node1, &list_node, result);
    goto_if_error(r, "init_explicit_key_path", error);

    /* Extend the list with the tail of the initial unmodified list. */
    while (list_node != NULL) {
        if (!add_string_to_list(*result, list_node->str)) {
            LOG_ERROR("Out of memory");
            r = TSS2_FAPI_RC_MEMORY;
            goto error;
        }
        list_node = list_node->next;
    }
    free_string_list(list_node1);
    return TSS2_RC_SUCCESS;

error:
    if (*result)
        free_string_list(*result);
    if (list_node1)
        free_string_list(list_node1);
    return r;
}

/** Prepare the creation of a primary key.
 *
 * Depending on the parameters the creation of an endorsement or storage root key
 * will be prepared.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] ktype The type of key TSS2_EK or TSS2_SRK.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_TRY_AGAIN if an I/O operation is not finished yet and
 *         this function needs to be called again.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
 *         object store.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @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_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.
 */
TSS2_RC
ifapi_init_primary_async(FAPI_CONTEXT *context, TSS2_KEY_TYPE ktype) {
    TSS2_RC      r;
    TPMS_POLICY *policy;
    IFAPI_KEY   *pkey = &context->createPrimary.pkey_object.misc.key;

    r = TSS2_RC_SUCCESS;

    if (ktype == TSS2_EK) {
        pkey->ek_profile = TPM2_YES;
        /* Values set according to EK credential profile. */
        if (context->cmd.Provision.public_templ.public.publicArea.type == TPM2_ALG_RSA) {
            if (pkey->nonce.size) {
                memcpy(context->cmd.Provision.public_templ.public.publicArea.unique.rsa.buffer,
                       &pkey->nonce.buffer[0], pkey->nonce.size);
            } else if (!(context->cmd.Provision.public_templ.unique_rsa_set
                         || context->cmd.Provision.public_templ.unique_zero)) {
                /* is needed to be backward compatible to versions where unique
                   is not set via the profile. */
                if ((context->cmd.Provision.public_templ.public.publicArea.objectAttributes
                     & TPMA_OBJECT_USERWITHAUTH))
                    context->cmd.Provision.public_templ.public.publicArea.unique.rsa.size = 0;
                else
                    context->cmd.Provision.public_templ.public.publicArea.unique.rsa.size = 256;
            }
        } else if (context->cmd.Provision.public_templ.public.publicArea.type == TPM2_ALG_ECC) {
            if (pkey->nonce.size) {
                memcpy(context->cmd.Provision.public_templ.public.publicArea.unique.ecc.x.buffer,
                       &pkey->nonce.buffer[0], pkey->nonce.size);
            } else if (!(context->cmd.Provision.public_templ.unique_ecc_set
                         || context->cmd.Provision.public_templ.unique_zero)) {
                /* is needed to be backward compatible to versions where unique
                   is not set via the profile. */
                if ((context->cmd.Provision.public_templ.public.publicArea.objectAttributes
                     & TPMA_OBJECT_USERWITHAUTH)) {
                    context->cmd.Provision.public_templ.public.publicArea.unique.ecc.x.size = 0;
                    context->cmd.Provision.public_templ.public.publicArea.unique.ecc.y.size = 0;
                } else {
                    context->cmd.Provision.public_templ.public.publicArea.unique.ecc.x.size = 32;
                    context->cmd.Provision.public_templ.public.publicArea.unique.ecc.y.size = 32;
                }
            }
        }
        policy = context->profiles.default_profile.ek_policy;
    } else if (ktype == TSS2_SRK) {
        policy = context->profiles.default_profile.srk_policy;
    } else {
        return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid key type. Only EK or SRK allowed");
    }

    if (policy) {
        /* Duplicate policy to prevent profile policy from cleanup. */
        policy = ifapi_copy_policy(policy);
        return_if_null(policy, "Out of memory.", TSS2_FAPI_RC_MEMORY);

        r = ifapi_calculate_tree(context, NULL, /**< no path needed */
                                 policy, context->profiles.default_profile.nameAlg,
                                 &context->cmd.Provision.digest_idx,
                                 &context->cmd.Provision.hash_size);
        if (r) {
            LOG_ERROR("Policy calculation");
            SAFE_FREE(policy);
            return r;
        }
        /* Check whether policy digest defined for key matches the computed policy. */
        if (context->cmd.Provision.public_templ.public.publicArea.authPolicy.size) {
            if (context->cmd.Provision.public_templ.public.publicArea.authPolicy.size
                    != context->cmd.Provision.hash_size
                || memcmp(
                       &policy->policyDigests.digests[context->policy.digest_idx].digest,
                       &context->cmd.Provision.public_templ.public.publicArea.authPolicy.buffer[0],
                       context->cmd.Provision.hash_size)
                       != 0) {
                SAFE_FREE(policy);
                return_error(TSS2_FAPI_RC_BAD_VALUE,
                             "EK Policy does not match policy defined in profile.");
            }
        }
        context->cmd.Provision.public_templ.public.publicArea.authPolicy.size
            = context->cmd.Provision.hash_size;
        memcpy(&context->cmd.Provision.public_templ.public.publicArea.authPolicy.buffer[0],
               &policy->policyDigests.digests[context->policy.digest_idx].digest,
               context->cmd.Provision.hash_size);
    }
    context->createPrimary.pkey_object.policy = policy;
    context->createPrimary.pkey_object.objectType = IFAPI_KEY_OBJ;

    memset(&context->cmd.Provision.inSensitive, 0, sizeof(TPM2B_SENSITIVE_CREATE));
    memset(&context->cmd.Provision.outsideInfo, 0, sizeof(TPM2B_DATA));
    memset(&context->cmd.Provision.creationPCR, 0, sizeof(TPML_PCR_SELECTION));

    context->primary_state = PRIMARY_AUTHORIZE_HIERARCHY;
    return r;
}

/** Finalize the creation of a primary key.
 *
 * Depending on the parameters the creation of an endorsement key or a storage root key
 * will be finalized. The created object with the all information needed by FAPI will
 * be stored in the FAPI context.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] ktype The type of key TSS2_EK or TSS2_SRK.
 * @param[in,out] hierarchy The hiearchy used for primary creation.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_TRY_AGAIN if the execution cannot be completed.
 * @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
 *         is not set.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @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_FAPI_RC_IO_ERROR if an error occured while accessing the
 *         object store.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
 *         was not successful.
 */
TSS2_RC
ifapi_init_primary_finish(FAPI_CONTEXT *context, TSS2_KEY_TYPE ktype, IFAPI_OBJECT *hierarchy) {
    TSS2_RC              r;
    ESYS_TR              primaryHandle;
    TPM2B_PUBLIC        *outPublic = NULL;
    TPM2B_CREATION_DATA *creationData = NULL;
    TPM2B_DIGEST        *creationHash = NULL;
    TPMT_TK_CREATION    *creationTicket = NULL;
    IFAPI_KEY           *pkey = &context->createPrimary.pkey_object.misc.key;
    NODE_STR_T          *k_sub_path = NULL;
    ESYS_TR              auth_session;

    switch (context->primary_state) {
    statecase(context->primary_state, PRIMARY_AUTHORIZE_HIERARCHY);
        if (hierarchy->misc.hierarchy.with_auth == TPM2_YES || policy_digest_size(hierarchy)) {
            r = ifapi_authorize_object(context, hierarchy, &auth_session);
            FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);
        } else {
            auth_session = context->session1;
        }
        r = Esys_CreatePrimary_Async(
            context->esys, hierarchy->public.handle,
            (auth_session == ESYS_TR_NONE) ? ESYS_TR_PASSWORD : auth_session,
            ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE, &context->cmd.Provision.inSensitive,
            &context->cmd.Provision.public_templ.public, &context->cmd.Provision.outsideInfo,
            &context->cmd.Provision.creationPCR);
        goto_if_error_reset_state(r, "CreatePrimary", error_cleanup);

        pkey->unique_init_set = TPM2_YES;
        pkey->unique_init = context->cmd.Provision.public_templ.public.publicArea.unique;

        fallthrough;

    statecase(context->primary_state, PRIMARY_WAIT_FOR_PRIMARY);
        r = Esys_CreatePrimary_Finish(context->esys, &primaryHandle, &outPublic, &creationData,
                                      &creationHash, &creationTicket);
        if (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN)
            return TSS2_FAPI_RC_TRY_AGAIN;

        goto_if_error_reset_state(r, "FAPI Provision", error_cleanup);

        /* Set EK or SRK handle in context. */
        if (ktype == TSS2_EK) {
            context->ek_handle = primaryHandle;
        } else if (ktype == TSS2_SRK) {
            context->srk_handle = primaryHandle;
        } else {
            return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid key type. Only EK or SRK allowed");
        }

        /* Prepare serialization of pkey to key store. */

        SAFE_FREE(pkey->serialization.buffer);
        r = Esys_TR_Serialize(context->esys, primaryHandle, &pkey->serialization.buffer,
                              &pkey->serialization.size);
        goto_if_error(r, "Error serialize esys object", error_cleanup);

        r = ifapi_get_name(&outPublic->publicArea, &pkey->name);
        goto_if_error(r, "Get primary name", error_cleanup);

        pkey->public = *outPublic;
        pkey->policyInstance = NULL;
        pkey->creationData = *creationData;
        pkey->creationHash = *creationHash;
        pkey->creationTicket = *creationTicket;
        pkey->description = NULL;
        pkey->certificate = NULL;

        /* Cleanup unused information */
        SAFE_FREE(outPublic);
        SAFE_FREE(creationData);
        SAFE_FREE(creationHash);
        SAFE_FREE(creationTicket);

        if (pkey->public.publicArea.type == TPM2_ALG_RSA)
            pkey->signing_scheme = context->profiles.default_profile.rsa_signing_scheme;
        else
            pkey->signing_scheme = context->profiles.default_profile.ecc_signing_scheme;
        context->createPrimary.pkey_object.public.handle = primaryHandle;
        SAFE_FREE(pkey->serialization.buffer);
        return TSS2_RC_SUCCESS;

    statecasedefault(context->primary_state);
    }

error_cleanup:
    SAFE_FREE(outPublic);
    SAFE_FREE(creationData);
    SAFE_FREE(creationHash);
    SAFE_FREE(creationTicket);
    ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
    free_string_list(k_sub_path);
    SAFE_FREE(pkey->serialization.buffer);
    ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
    return r;
}

/** Prepare the loading of a primary key from key store.
 *
 * The asynchronous loading or the key from keystore will be prepared and
 * the path will be stored in the FAPI context.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] path The FAPI path of the primary key.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
 * @retval TSS2_FAPI_RC_IO_ERROR if an I/O error was encountered.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if the file does not exist.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
 * @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.
 */
TSS2_RC
ifapi_load_primary_async(FAPI_CONTEXT *context, char *path) {

    TSS2_RC r;

    memset(&context->createPrimary.pkey_object, 0, sizeof(IFAPI_OBJECT));
    context->createPrimary.path = path;
    r = ifapi_keystore_load_async(&context->keystore, &context->io, path);
    return_if_error2(r, "Could not open: %s", path);
    context->primary_state = PRIMARY_READ_KEY;
    return TSS2_RC_SUCCESS;
}

/** State machine to finalize the loading of a primary key from key store.
 *
 * The asynchronous loading or the key from keystore will be finalized.
 * Afterwards the hierarchy object, which will be used for authorization will
 * be loaded and the ESAPI functions for primary generation will be called
 * if the primary is not persistent.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[out] handle The object handle of the primary key.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if the hierarchy file does not exist.
 * @retval TSS2_FAPI_RC_IO_ERROR if an I/O error was encountered.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @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_POLICY_UNKNOWN if policy search for a certain policy digest
 *         was not successful.
 * @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.
 */
TSS2_RC
ifapi_load_primary_finish(FAPI_CONTEXT *context, ESYS_TR *handle) {
    TSS2_RC       r;
    IFAPI_OBJECT *hierarchy = &context->createPrimary.hierarchy;

    TPM2B_PUBLIC          *outPublic = NULL;
    TPM2B_CREATION_DATA   *creationData = NULL;
    TPM2B_DIGEST          *creationHash = NULL;
    TPMT_TK_CREATION      *creationTicket = NULL;
    IFAPI_OBJECT          *pkey_object = &context->createPrimary.pkey_object;
    IFAPI_KEY             *pkey = &context->createPrimary.pkey_object.misc.key;
    TPMS_CAPABILITY_DATA **capabilityData = &context->createPrimary.capabilityData;
    TPMI_YES_NO            moreData;
    ESYS_TR                auth_session = ESYS_TR_NONE; /* Initialized due to scanbuild */

    LOG_TRACE("call");

    switch (context->primary_state) {
    statecase(context->primary_state, PRIMARY_READ_KEY);
        /* Read the primary key from keystore. */
        r = ifapi_keystore_load_finish(&context->keystore, &context->io, pkey_object);
        return_try_again(r);
        return_if_error(r, "read_finish failed");

        r = ifapi_initialize_object(context->esys, pkey_object);
        goto_if_error_reset_state(r, "Initialize key object", error_cleanup);

        /* Check whether a persistent key was loaded.
           In this case the handle has already been set. */
        if (pkey_object->public.handle != ESYS_TR_NONE) {
            if (pkey->creationTicket.hierarchy == TPM2_RH_ENDORSEMENT
                && strcmp("/EK", &pkey_object->rel_path[strlen(pkey_object->rel_path) - 3]) == 0) {
                context->ek_persistent = true;
            } else if (strcmp("/SRK", &pkey_object->rel_path[strlen(pkey_object->rel_path) - 4])
                       == 0) {
                context->srk_persistent = true;
            }
            /* It has to be checked whether the persistent handle exists. */
            context->primary_state = PRIMARY_VERIFY_PERSISTENT;
            return TSS2_FAPI_RC_TRY_AGAIN;
        } else {
            if (pkey->creationTicket.hierarchy == TPM2_RH_ENDORSEMENT) {
                context->ek_persistent = false;
            } else {
                context->srk_persistent = false;
            }
        }
        fallthrough;

    statecase(context->primary_state, PRIMARY_READ_HIERARCHY);
        /* The hierarchy object used for auth_session will be loaded from key store. */
        if (pkey->creationTicket.hierarchy == TPM2_RH_ENDORSEMENT) {
            r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HE");
            return_if_error2(r, "Could not open hierarchy /HE");
        } else if (pkey->creationTicket.hierarchy == TPM2_RH_NULL) {
            r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HN");
            return_if_error2(r, "Could not open hierarchy /HN");
        } else {
            r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HS");
            return_if_error2(r, "Could not open hierarchy /HS");
        }
        fallthrough;

    statecase(context->primary_state, PRIMARY_READ_HIERARCHY_FINISH);
        r = ifapi_keystore_load_finish(&context->keystore, &context->io, hierarchy);
        return_try_again(r);
        return_if_error(r, "read_finish failed");

        r = ifapi_initialize_object(context->esys, hierarchy);
        goto_if_error_reset_state(r, "Initialize hierarchy object", error_cleanup);

        if (pkey->creationTicket.hierarchy == TPM2_RH_ENDORSEMENT) {
            hierarchy->public.handle = ESYS_TR_RH_ENDORSEMENT;
        } else if (pkey->creationTicket.hierarchy == TPM2_RH_NULL) {
            hierarchy->public.handle = ESYS_TR_RH_NULL;
        } else {
            hierarchy->public.handle = ESYS_TR_RH_OWNER;
        }
        fallthrough;

    statecase(context->primary_state, PRIMARY_AUTHORIZE_HIERARCHY);
        /* The asynchronous authorization of the hierarchy needed for primary. */
        r = ifapi_authorize_object(context, hierarchy, &auth_session);
        FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);

        memset(&context->createPrimary.inSensitive, 0, sizeof(TPM2B_SENSITIVE_CREATE));
        memset(&context->createPrimary.outsideInfo, 0, sizeof(TPM2B_DATA));
        memset(&context->createPrimary.creationPCR, 0, sizeof(TPML_PCR_SELECTION));
        fallthrough;

    statecase(context->primary_state, PRIMARY_GET_AUTH_VALUE);
        /* Get the auth value to be stored in inSensitive */
        r = ifapi_set_auth_primary(context, pkey_object,
                                   &context->createPrimary.inSensitive.sensitive);
        return_try_again(r);
        goto_if_error_reset_state(r, "Get auth value for primary", error_cleanup);

        /* Prepare primary creation. */
        TPM2B_PUBLIC public = pkey->public;

        memset(&public.publicArea.unique, 0, sizeof(TPMU_PUBLIC_ID));

        if (pkey->unique_init_set == TPM2_YES) {
            public.publicArea.unique = pkey->unique_init;
        } else {
            /* is needed to be backward compatible to versions where unique
               is not set via the profile and stored in the keystore */
            if (hierarchy->public.handle == ESYS_TR_RH_ENDORSEMENT && pkey->ek_profile) {
                /* Values set according to EK credential profile. */
                if (public.publicArea.type == TPM2_ALG_RSA) {
                    if (pkey->nonce.size) {
                        memcpy(public.publicArea.unique.rsa.buffer, &pkey->nonce.buffer[0],
                               pkey->nonce.size);
                    }
                    if ((public.publicArea.objectAttributes & TPMA_OBJECT_USERWITHAUTH))
                    public.publicArea.unique.rsa.size = 0;
                    else public.publicArea.unique.rsa.size = 256;
                } else if (public.publicArea.type == TPM2_ALG_ECC) {
                    if (pkey->nonce.size) {
                        memcpy(public.publicArea.unique.ecc.x.buffer, &pkey->nonce.buffer[0],
                               pkey->nonce.size);
                    }
                    if ((public.publicArea.objectAttributes & TPMA_OBJECT_USERWITHAUTH)) {
                        public.publicArea.unique.ecc.x.size = 0;
                        public.publicArea.unique.ecc.y.size = 0;
                    } else {
                        public.publicArea.unique.ecc.x.size = 32;
                        public.publicArea.unique.ecc.y.size = 32;
                    }
                }
            }
        }

        r = Esys_CreatePrimary_Async(
            context->esys, hierarchy->public.handle, auth_session,
            ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE, &context->createPrimary.inSensitive,
            &public, &context->createPrimary.outsideInfo, &context->createPrimary.creationPCR);
        return_if_error(r, "CreatePrimary");
        fallthrough;

    statecase(context->primary_state, PRIMARY_HAUTH_SENT);
        if (context->createPrimary.handle) {
            *handle = context->createPrimary.handle;
            context->primary_state = PRIMARY_CREATED;
            return TSS2_FAPI_RC_TRY_AGAIN;
        } else {
            r = Esys_CreatePrimary_Finish(context->esys, &pkey_object->public.handle, &outPublic,
                                          &creationData, &creationHash, &creationTicket);
            return_try_again(r);
            goto_if_error_reset_state(r, "FAPI regenerate primary", error_cleanup);
        }
        *handle = pkey_object->public.handle;
        context->primary_state = PRIMARY_INIT;

        /* Check whether the public key corresponds to key in key store. */
        if (!ifapi_cmp_public_key(outPublic, &pkey_object->misc.key.public)) {
            goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
                       "Public key for %s was not created correctly.", error_cleanup,
                       pkey_object->rel_path);
        }

        break;

    statecase(context->primary_state, PRIMARY_VERIFY_PERSISTENT);
        /* Check the TPM capabilities for the persistent handle. */
        r = Esys_GetCapability_Async(context->esys, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                     TPM2_CAP_HANDLES, pkey_object->misc.key.persistent_handle, 1);
        goto_if_error(r, "Esys_GetCapability_Async", error_cleanup);
        fallthrough;

    statecase(context->primary_state, PRIMARY_GET_CAP);
        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 persistent handle exists. */
        if ((*capabilityData)->data.handles.count != 0
            && (*capabilityData)->data.handles.handle[0]
                   == pkey_object->misc.key.persistent_handle) {
            /* Persistent handle found. */
            SAFE_FREE(*capabilityData);
            *handle = pkey_object->public.handle;
            break;
        }
        goto_error(r, TSS2_FAPI_RC_KEY_NOT_FOUND,
                   "The persistent handle 0x%x does not exist. "
                   "The TPM state and the keystore state do not match.",
                   error_cleanup, pkey_object->misc.key.persistent_handle);

    statecasedefault(context->primary_state);
    }
    SAFE_FREE(outPublic);
    SAFE_FREE(creationData);
    SAFE_FREE(creationHash);
    SAFE_FREE(creationTicket);
    ifapi_cleanup_ifapi_object(&context->createPrimary.hierarchy);
    return TSS2_RC_SUCCESS;

error_cleanup:
    SAFE_FREE(*capabilityData);
    SAFE_FREE(outPublic);
    SAFE_FREE(creationData);
    SAFE_FREE(creationHash);
    SAFE_FREE(creationTicket);
    ifapi_cleanup_ifapi_object(&context->createPrimary.hierarchy);
    ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
    return r;
}

/** Prepare session for FAPI command execution.
 *
 * It will be checked whether the context of FAPI and ESAPI is initialized
 * and whether no other FAPI command session is running.
 * Also some handle variables in the context are initialized.
 *
 * @param[in] context The FAPI_CONTEXT.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE if the context is not initialized.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE If a FAPI command session is active.
 * @retval TSS2_FAPI_RC_NO_TPM if the ESAPI context is not initialized.
 */
TSS2_RC
ifapi_session_init(FAPI_CONTEXT *context) {
    LOG_TRACE("call");
    return_if_null(context, "No context", TSS2_FAPI_RC_BAD_REFERENCE);

    return_if_null(context->esys, "No context", TSS2_FAPI_RC_NO_TPM);

    if (context->state != FAPI_STATE_INIT) {
        return_error(TSS2_FAPI_RC_BAD_SEQUENCE, "Invalid State");
    }

    context->session1 = ESYS_TR_NONE;
    context->session2 = ESYS_TR_NONE;
    context->srk_handle = ESYS_TR_NONE;
    return TSS2_RC_SUCCESS;
}

/** Prepare session for FAPI command execution without TPM.
 *
 * It will be checked whether the context of FAPI is initialized
 * and whether no other FAPI command session is running.
 * Also some handle variables in the context are initialized.
 *
 * @param[in] context The FAPI_CONTEXT.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE if the context is not initialized.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE If a FAPI command session is active.
 */
TSS2_RC
ifapi_non_tpm_mode_init(FAPI_CONTEXT *context) {
    LOG_TRACE("call");
    return_if_null(context, "No context", TSS2_FAPI_RC_BAD_REFERENCE);

    if (context->state != FAPI_STATE_INIT) {
        return_error(TSS2_FAPI_RC_BAD_SEQUENCE, "Invalid State");
    }

    context->session1 = ESYS_TR_NONE;
    context->session2 = ESYS_TR_NONE;
    context->srk_handle = ESYS_TR_NONE;
    return TSS2_RC_SUCCESS;
}

/** Cleanup FAPI sessions in error cases.
 *
 * The uses sessions and the SRK (if not persistent) will be flushed
 * non asynchronous in error cases.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 */
void
ifapi_session_clean(FAPI_CONTEXT *context) {
    TPM2_HANDLE           tpm_policy_handle;
    TPMI_YES_NO           moreData;
    TPMS_CAPABILITY_DATA *capabilityData = NULL;
    bool                  handle_found = false;

    if (context->session1 != ESYS_TR_NONE && context->session1 != ESYS_TR_PASSWORD) {
        if (context->session1 == context->session2) {
            context->session2 = ESYS_TR_NONE;
        }
        if (Esys_FlushContext(context->esys, context->session1) != TSS2_RC_SUCCESS) {
            LOG_ERROR("Cleanup session failed.");
        }
        context->session1 = ESYS_TR_NONE;
    }
    if (context->session2 != ESYS_TR_NONE) {
        if (Esys_FlushContext(context->esys, context->session2) != TSS2_RC_SUCCESS) {
            LOG_ERROR("Cleanup session failed.");
        }
        context->session2 = ESYS_TR_NONE;
    }

    if (context->policy_session && context->policy_session != ESYS_TR_NONE) {
        if (Esys_TR_GetTpmHandle(context->esys, context->policy_session, &tpm_policy_handle)) {
            LOG_ERROR("Cleanup policy_session could not get TPM handle.");
        } else {
            /* Check whether policy session still exists. */
            if (Esys_GetCapability(context->esys, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                   TPM2_CAP_HANDLES, TPM2_LOADED_SESSION_FIRST,
                                   TPM2_MAX_CAP_HANDLES, &moreData, &capabilityData)) {
                LOG_ERROR("Cleanup policy_session could not check TPM handle.");
            } else {
                /* Loop is used because usage of tpm_policy_handle instead of
                   TPM2_LOADED_SESSION_FIRST for get capability did not work. If the handle is not
                   found and moreData is set the flush will also be executed. */
                for (uint32_t i = 0; i < capabilityData->data.handles.count; i++) {
                    if (capabilityData->data.handles.handle[i] == tpm_policy_handle) {
                        handle_found = true;
                    }
                }
                if (handle_found || moreData) {
                    SAFE_FREE(capabilityData);
                    if (Esys_FlushContext(context->esys, context->policy_session)
                        != TSS2_RC_SUCCESS) {
                        LOG_ERROR("Cleanup policy_session failed.");
                    }
                } else {
                    SAFE_FREE(capabilityData);
                }
            }
        }
        context->policy_session = ESYS_TR_NONE;
    }
    if (!context->srk_persistent && context->srk_handle != ESYS_TR_NONE) {
        if (Esys_FlushContext(context->esys, context->srk_handle) != TSS2_RC_SUCCESS) {
            LOG_ERROR("Cleanup Policy Session  failed.");
        }
        context->srk_handle = ESYS_TR_NONE;
    }
    context->srk_persistent = false;
}

/** State machine for asynchronous cleanup of a FAPI session.
 *
 * Used sessions and the SRK will be flushed.
 *
 * @param[in] context The FAPI_CONTEXT storing the used handles.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 */
TSS2_RC
ifapi_cleanup_session(FAPI_CONTEXT *context) {
    TSS2_RC r;

    /* Policy sessions were closed after successful execution. */

    switch (context->cleanup_state) {
    statecase(context->cleanup_state, CLEANUP_INIT);
        if (context->session1 != ESYS_TR_NONE) {
            if (context->session1 == context->session2) {
                context->session2 = ESYS_TR_NONE;
            }
            r = Esys_FlushContext_Async(context->esys, context->session1);
            try_again_or_error_goto(r, "Flush session.", error);
        }
        fallthrough;

    statecase(context->cleanup_state, CLEANUP_SESSION1);
        if (context->session1 != ESYS_TR_NONE) {
            r = Esys_FlushContext_Finish(context->esys);
            try_again_or_error_goto(r, "Flush session.", error);
        }
        context->session1 = ESYS_TR_NONE;

        if (context->session2 != ESYS_TR_NONE) {
            r = Esys_FlushContext_Async(context->esys, context->session2);
            try_again_or_error_goto(r, "Flush session.", error);
        }
        fallthrough;

    statecase(context->cleanup_state, CLEANUP_SESSION2);
        if (context->session2 != ESYS_TR_NONE) {
            r = Esys_FlushContext_Finish(context->esys);
            try_again_or_error_goto(r, "Flush session.", error);
        }
        context->session2 = ESYS_TR_NONE;

        if (!context->srk_persistent && context->srk_handle != ESYS_TR_NONE) {
            r = Esys_FlushContext_Async(context->esys, context->srk_handle);
            try_again_or_error_goto(r, "Flush SRK.", error);
        }
        fallthrough;

    statecase(context->cleanup_state, CLEANUP_SRK);
        if (!context->srk_persistent && context->srk_handle != ESYS_TR_NONE) {
            r = Esys_FlushContext_Finish(context->esys);
            try_again_or_error_goto(r, "Flush SRK.", error);

            context->srk_handle = ESYS_TR_NONE;
            context->srk_persistent = false;
        }
        context->cleanup_state = CLEANUP_INIT;
        return TSS2_RC_SUCCESS;

    statecasedefault(context->state);
    }
error:
    context->cleanup_state = CLEANUP_INIT;
    return r;
}

/** Cleanup primary keys in error cases (non asynchronous).
 *
 * @param[in] context The FAPI_CONTEXT storing the used handles.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 */
void
ifapi_primary_clean(FAPI_CONTEXT *context) {
    if (!context->srk_persistent && context->srk_handle != ESYS_TR_NONE) {
        if (Esys_FlushContext(context->esys, context->srk_handle) != TSS2_RC_SUCCESS) {
            LOG_ERROR("Cleanup session failed.");
        }
        context->srk_handle = ESYS_TR_NONE;
    }
    if (!context->ek_persistent && context->ek_handle != ESYS_TR_NONE) {
        if (Esys_FlushContext(context->esys, context->ek_handle) != TSS2_RC_SUCCESS) {
            LOG_ERROR("Cleanup EK failed.");
        }
        context->ek_handle = ESYS_TR_NONE;
    }
    context->srk_persistent = false;
}

/** Prepare the session creation of a FAPI command.
 *
 * The initial state of the state machine for session creation will be determined.
 * Depending of the session_flags creation of a primary for the encryption of
 * the session secret can be adjusted.
 * The session passed session attributes will be used for the ESAPI command
 * Esys_TRSess_SetAttributes.
 *
 * @param[in] context The FAPI_CONTEXT storing the used handles.
 * @param[in] session_flags The flags to adjust used session and encryption
 *            key. With IFAPI_SESSION1 and IFAPI_SESSION2 the session creation
 *            for sesion1 and session2 can be activated, IFAPI_SESSION_GEN_SRK
 *            triggers the creation of the primary for session secret encryption.
 * @param[in] attribute_flags1 The attributes used for session1.
 * @param[in] attribute_flags2 The attributes used for session2.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if the hierarchy file or the primary key file
 *         does not exist.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
 *         of the primary.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
 *         object store.
 * @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
 * @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.
 */
TSS2_RC
ifapi_get_sessions_async(FAPI_CONTEXT      *context,
                         IFAPI_SESSION_TYPE session_flags,
                         TPMA_SESSION       attribute_flags1,
                         TPMA_SESSION       attribute_flags2) {
    TSS2_RC r;

    LOG_TRACE("call");
    context->session_flags = session_flags;
    context->session1_attribute_flags = attribute_flags1;
    context->session2_attribute_flags = attribute_flags2;
    char *file = NULL;

    if (session_flags & IFAPI_SESSION_USE_SRK) {
        context->session_state = SESSION_CREATE_SESSION;
        return TSS2_RC_SUCCESS;
    } else if (!(session_flags & IFAPI_SESSION_GEN_SRK)) {
        context->srk_handle = ESYS_TR_NONE;
        context->session_state = SESSION_CREATE_SESSION;
        return TSS2_RC_SUCCESS;
    }

    context->primary_state = PRIMARY_INIT;
    r = ifapi_asprintf(&file, "%s%s", context->config.profile_name, IFAPI_SRK_KEY_PATH);
    goto_if_error(r, "Error ifapi_asprintf", error_cleanup);

    r = ifapi_load_primary_async(context, file);
    return_if_error_reset_state(r, "Load EK");
    free(file);

    context->session_state = SESSION_WAIT_FOR_PRIMARY;
    return TSS2_RC_SUCCESS;

error_cleanup:
    SAFE_FREE(file);
    return r;
}

/** State machine for the session creation of a FAPI command.
 *
 * The sessions needed for a FAPI command will be created. If needed also the
 * primary key for session encryption will be created.
 *
 * @param[in] context The FAPI_CONTEXT storing the used handles.
 * @param[in] profile The FAPI profile will be used to adjust session parameters.
 * @param[in] hash_alg The hash algorithm used for the session.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_IO_ERROR if an I/O error was encountered.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @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_KEY_NOT_FOUND if a key was not found.
 * @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_POLICY_UNKNOWN if policy search for a certain policy digest
 *         was not successful.
 * @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.
 */
TSS2_RC
ifapi_get_sessions_finish(FAPI_CONTEXT        *context,
                          const IFAPI_PROFILE *profile,
                          TPMI_ALG_HASH        hash_alg) {
    TSS2_RC r;

    switch (context->session_state) {
    statecase(context->session_state, SESSION_WAIT_FOR_PRIMARY);
        LOG_TRACE("**STATE** SESSION_WAIT_FOR_PRIMARY");
        r = ifapi_load_primary_finish(context, &context->srk_handle);
        return_try_again(r);

        ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
        return_if_error(r, "Load primary.");
        fallthrough;

    statecase(context->session_state, SESSION_CREATE_SESSION);
        LOG_TRACE("**STATE** SESSION_CREATE_SESSION");
        if (!(context->session_flags & IFAPI_SESSION1)) {
            LOG_TRACE("finished");
            return TSS2_RC_SUCCESS;
        }

        /* Initializing the first session for the caller */

        r = ifapi_get_session_async(context->esys, context->srk_handle, profile, hash_alg);
        return_if_error_reset_state(r, "Create FAPI session async");
        fallthrough;

    statecase(context->session_state, SESSION_WAIT_FOR_SESSION1);
        LOG_TRACE("**STATE** SESSION_WAIT_FOR_SESSION1");
        r = ifapi_get_session_finish(context->esys, &context->session1,
                                     context->session1_attribute_flags);
        return_try_again(r);
        return_if_error_reset_state(r, "Create FAPI session finish");

        if (!(context->session_flags & IFAPI_SESSION2)) {
            LOG_TRACE("finished");
            return TSS2_RC_SUCCESS;
        }

        /* Initializing the second session for the caller */

        r = ifapi_get_session_async(context->esys, context->srk_handle, profile, profile->nameAlg);
        return_if_error_reset_state(r, "Create FAPI session async");
        fallthrough;

    statecase(context->session_state, SESSION_WAIT_FOR_SESSION2);
        LOG_TRACE("**STATE** SESSION_WAIT_FOR_SESSION2");
        r = ifapi_get_session_finish(context->esys, &context->session2,
                                     context->session2_attribute_flags);
        return_try_again(r);

        return_if_error_reset_state(r, "Create FAPI session finish");
        break;

    statecasedefault(context->session_state);
    }

    return TSS2_RC_SUCCESS;
}

/** Merge profile already stored in FAPI context into a NV object template.
 *
 * The defaults for NV creation which are stored in the FAPI default profile
 * will be merged in the passed templates default values.
 *
 * @param[in] context The FAPI_CONTEXT with the default profile.
 * @param[in] template The template with the default values for the NV object.
 *
 * @retval TSS2_RC_SUCCESS on success.
 */
TSS2_RC
ifapi_merge_profile_into_nv_template(FAPI_CONTEXT *context, IFAPI_NV_TEMPLATE *template) {
    const TPMA_NV        extend_mask = TPM2_NT_EXTEND << TPMA_NV_TPM2_NT_SHIFT;
    const TPMA_NV        counter_mask = TPM2_NT_COUNTER << TPMA_NV_TPM2_NT_SHIFT;
    const TPMA_NV        bitfield_mask = TPM2_NT_BITS << TPMA_NV_TPM2_NT_SHIFT;
    const IFAPI_PROFILE *profile = &context->profiles.default_profile;
    size_t               hash_size;

    template->public.nameAlg = profile->nameAlg;
    if ((template->public.attributes & extend_mask) == extend_mask) {
        /* The size of the NV ram to be extended must be read from the
           profile */
        hash_size = ifapi_hash_get_digest_size(profile->nameAlg);
        template->public.dataSize = hash_size;
    } else if ((template->public.attributes & counter_mask) == counter_mask
               || (template->public.attributes & bitfield_mask) == bitfield_mask) {
        /* For bit fields and counters only size 8 is possible */
        template->public.dataSize = 8;
    } else {
        /* For normal NV ram the passed size will be used. */
        template->public.dataSize = context->nv_cmd.numBytes;
    }

    return TSS2_RC_SUCCESS;
}

/** Merge profile already stored in FAPI context into a key template.
 *
 * The defaults for key creation which are stored in the FAPI default profile
 * will be merged in the passed templates default values.
 *
 * @param[in] profile The profile which will be used to adjust the template.
 * @param[in] template The template with the default values for the key object.
 *
 * @retval TSS2_RC_SUCCESS on success.
 */
TSS2_RC
ifapi_merge_profile_into_template(const IFAPI_PROFILE *profile, IFAPI_KEY_TEMPLATE *template) {
    /* Merge profile parameters */
    template->public.publicArea.type = profile->type;
    template->public.publicArea.nameAlg = profile->nameAlg;
    if (profile->type == TPM2_ALG_RSA) {
        template->public.publicArea.parameters.rsaDetail.keyBits = profile->keyBits;
        template->public.publicArea.parameters.rsaDetail.exponent = profile->exponent;
    } else if (profile->type == TPM2_ALG_ECC) {
        template->public.publicArea.parameters.eccDetail.curveID = profile->curveID;
        template->public.publicArea.parameters.eccDetail.kdf.scheme = TPM2_ALG_NULL;
    }

    /* Set remaining parameters depending on key type */
    if (template->public.publicArea.objectAttributes & TPMA_OBJECT_RESTRICTED) {
        if (template->public.publicArea.objectAttributes & TPMA_OBJECT_DECRYPT) {
            template->public.publicArea.parameters.asymDetail.symmetric = profile->sym_parameters;
        } else {
            template->public.publicArea.parameters.asymDetail.symmetric.algorithm = TPM2_ALG_NULL;
        }
        if (profile->type == TPM2_ALG_RSA) {
            if (template->unique_ecc_set) {
                return_error(TSS2_FAPI_RC_BAD_VALUE, "ECC unique was set for an RSA key.");
            }
            if (template->unique_zero) {
                template->public.publicArea.unique.rsa.size = template->unique_zero;
            }
            if (template->public.publicArea.objectAttributes & TPMA_OBJECT_SIGN_ENCRYPT) {
                template->public.publicArea.parameters.rsaDetail.scheme.scheme
                    = profile->rsa_signing_scheme.scheme;
                memcpy(&template->public.publicArea.parameters.rsaDetail.scheme.details,
                       &profile->rsa_signing_scheme.details, sizeof(TPMU_ASYM_SCHEME));
            } else {
                template->public.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL;
            }
        } else if (profile->type == TPM2_ALG_ECC) {
            if (template->unique_rsa_set) {
                return_error(TSS2_FAPI_RC_BAD_VALUE, "RSA unique was set for an ECC key.");
            }
            if (template->unique_zero) {
                template->public.publicArea.unique.ecc.x.size = template->unique_zero;
                template->public.publicArea.unique.ecc.y.size = template->unique_zero;
            }
            if (template->public.publicArea.objectAttributes & TPMA_OBJECT_SIGN_ENCRYPT) {
                template->public.publicArea.parameters.eccDetail.scheme.scheme
                    = profile->ecc_signing_scheme.scheme;
                memcpy(&template->public.publicArea.parameters.eccDetail.scheme.details,
                       &profile->ecc_signing_scheme.details, sizeof(TPMU_ASYM_SCHEME));
            } else {
                template->public.publicArea.parameters.eccDetail.scheme.scheme = TPM2_ALG_NULL;
            }
        } else {
            template->public.publicArea.parameters.asymDetail.scheme.scheme = TPM2_ALG_NULL;
        }
    } else {
        /* Non restricted key */
        template->public.publicArea.parameters.asymDetail.symmetric.algorithm = TPM2_ALG_NULL;
        template->public.publicArea.parameters.asymDetail.scheme.scheme = TPM2_ALG_NULL;
    }
    return TSS2_RC_SUCCESS;
}

/** Convert absolute path to FAPI path which can be used as parameter for FAPI commands.
 *
 * Function converts the absolute path to a FAPI path.
 *
 * @param[in] keystore The used keystore.
 * @param[out] path FAPI key path.
 */
static void
full_path_to_fapi_path(IFAPI_KEYSTORE *keystore, char *path) {
    unsigned int       start_pos, end_pos, i;
    const unsigned int path_length = strlen(path);
    size_t             keystore_length = strlen(keystore->userdir);
    char               fapi_path_delim;

    start_pos = 0;

    /* Check key store part of the path */
    if (strncmp(&path[0], keystore->userdir, keystore_length) == 0) {
        start_pos = strlen(keystore->userdir);
    } else {
        keystore_length = strlen(keystore->systemdir);
        if (strncmp(&path[0], keystore->systemdir, keystore_length) == 0)
            start_pos = strlen(keystore->systemdir);
    }
    if (!start_pos)
        return;

    /* Shift FAPI path to the beginning. */
    end_pos = path_length - start_pos;
    memmove(&path[0], &path[start_pos], end_pos);
    size_t ip = 0;
    size_t lp = strlen(path);

    /* Special handling for // */
    while (ip < lp) {
        if (strncmp(&path[ip], "//", 2) == 0) {
            memmove(&path[ip], &path[ip + 1], lp - ip);
            lp -= 1;
        } else {
            ip += 1;
        }
    }

    /* Special handling for policy path were the name of the object file
       is part of the path. */
    if (ifapi_path_type_p(path, IFAPI_POLICY_PATH))
        fapi_path_delim = '.';
    else
        fapi_path_delim = IFAPI_FILE_DELIM_CHAR;

    for (i = end_pos - 1; i > 0; i--) {
        if (path[i] == fapi_path_delim) {
            path[i] = '\0';
            break;
        }
    }
}

/** Asynchronous preparation for loading a key and parent keys.
 *
 * The key loading is prepared. The pathname will be extended if possible and
 * a linked list with the directories will be created.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     keyPath the key path without the parent directories
 *                of the key store. (e.g. HE/EK, HS/SRK/mykey)
 *
 * @retval TSS2_RC_SUCCESS If the preparation is successful.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
 * @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
ifapi_load_keys_async(FAPI_CONTEXT *context, char const *keyPath) {
    TSS2_RC     r;
    NODE_STR_T *path_list;
    size_t      path_length;
    char       *fapi_key_path = NULL;

    LOG_TRACE("Load key: %s", keyPath);
    fapi_key_path = strdup(keyPath);
    check_oom(fapi_key_path);
    full_path_to_fapi_path(&context->keystore, fapi_key_path);
    r = get_explicit_key_path(&context->keystore, fapi_key_path, &path_list);
    SAFE_FREE(fapi_key_path);
    return_if_error(r, "Compute explicit path.");

    context->loadKey.path_list = path_list;
    path_length = ifapi_path_length(path_list);
    r = ifapi_load_key_async(context, path_length);
    goto_if_error(r, "Load key async.", error);

    return TSS2_RC_SUCCESS;

error:
    free_string_list(context->loadKey.path_list);
    return r;
}

/** Asynchronous preparation for loading of the parent keys.
 *
 * The key loading is prepared. The pathname will be extended if possible and
 * a linked list with the directories will be created.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     keyPath the key path without the parent directories
 *                of the key store. (e.g. HE/EK, HS/SRK/mykey)
 *
 * @retval TSS2_RC_SUCCESS If the preparation is successful.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
 * @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
ifapi_load_parent_keys_async(FAPI_CONTEXT *context, char const *keyPath) {
    TSS2_RC     r;
    NODE_STR_T *path_list;
    size_t      path_length;
    char       *fapi_key_path = NULL;

    LOG_TRACE("Load key: %s", keyPath);
    fapi_key_path = strdup(keyPath);
    check_oom(fapi_key_path);
    full_path_to_fapi_path(&context->keystore, fapi_key_path);
    r = get_explicit_key_path(&context->keystore, fapi_key_path, &path_list);
    SAFE_FREE(fapi_key_path);
    goto_if_error(r, "Compute explicit path.", error);

    context->loadKey.path_list = path_list;
    path_length = ifapi_path_length(path_list);
    r = ifapi_load_key_async(context, path_length - 1);
    return_if_error(r, "Load key async.");

    return TSS2_RC_SUCCESS;

error:
    free_string_list(context->loadKey.path_list);
    return r;
}

/** Asynchronous finish function for loading a key.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     flush_parent If false the parent of the key to be loaded
 *                will not be flushed.
 * @param[out]    handle The ESYS handle of the key.
 * @param[out]    key_object The object which will be used for the
 *                authorization of the loaded key.
 * @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_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @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_FAPI_RC_IO_ERROR if an error occurred while accessing the
 *         object store.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @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_POLICY_UNKNOWN if policy search for a certain policy digest
 *         was not successful.
 * @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.
 */
TSS2_RC
ifapi_load_keys_finish(FAPI_CONTEXT  *context,
                       bool           flush_parent,
                       ESYS_TR       *handle,
                       IFAPI_OBJECT **key_object) {
    TSS2_RC r;

    r = ifapi_load_key_finish(context, flush_parent);
    if (r == TSS2_FAPI_RC_TRY_AGAIN)
        return r;

    goto_if_error(r, "Load keys", error);

    *handle = context->loadKey.auth_object.public.handle;
    /* The current authorization object is the last key loaded and
       will be used. */
    *key_object = &context->loadKey.auth_object;
    free_string_list(context->loadKey.path_list);
    return TSS2_RC_SUCCESS;

error:
    free_string_list(context->loadKey.path_list);
    return r;
}

/** Initialize state machine for loading a key.
 *
 * @param[in,out] context for storing all state information.
 * @param[in] position the position of the key in path list stored in
 *            context->loadKey.path_list.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
 */
TSS2_RC
ifapi_load_key_async(FAPI_CONTEXT *context, size_t position) {
    context->loadKey.state = LOAD_KEY_GET_PATH;
    context->loadKey.position = position;
    context->loadKey.key_list = NULL;
    context->loadKey.parent_handle = ESYS_TR_NONE;

    return TSS2_RC_SUCCESS;
}

/** State machine for loading a key.
 *
 * A stack with all sup keys will be created and decremented during
 * the loading auf all keys.
 * The object of the loaded key will be stored in:
 * context->loadKey.auth_object
 *
 * @param[in,out] context for storing all state information.
 * @param[in]     flush_parent If flush_parent is false parent is
                  only flushed if a new parent is available.
 *
 * @retval TSS2_RC_SUCCESS If the loading of the key was successful.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
 *         not covered by other return codes.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
 * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
 *         store.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If an object needed for loading or
 *         authentication was not found.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
 *         not successful.
 * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for loading
 *         fails.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
 *         is not defined.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @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.
 */
TSS2_RC
ifapi_load_key_finish(FAPI_CONTEXT *context, bool flush_parent) {
    TSS2_RC       r;
    NODE_STR_T   *path_list = context->loadKey.path_list;
    size_t       *position = &context->loadKey.position;
    IFAPI_OBJECT *key_object = NULL;
    IFAPI_KEY    *key = NULL;
    ESYS_TR       auth_session;

    switch (context->loadKey.state) {
    statecase(context->loadKey.state, LOAD_KEY_GET_PATH);
        context->loadKey.key_path = NULL;
        /* Compute path name of key to be loaded. */
        r = ifapi_path_string_n(&context->loadKey.key_path, NULL, path_list, NULL, *position);
        LOG_TRACE("Load path %s", context->loadKey.key_path);
        return_if_error(r, "Compute key path.");

        context->loadKey.key_object = ifapi_allocate_object(context);
        goto_if_null2(context->loadKey.key_object, "Allocating key", r, TSS2_FAPI_RC_MEMORY,
                      error_cleanup);

        goto_if_null2(context->loadKey.key_path, "Invalid path", r, TSS2_FAPI_RC_GENERAL_FAILURE,
                      error_cleanup); /**< to avoid scan-build errors. */

        /* Prepare key loading. */
        r = ifapi_keystore_load_async(&context->keystore, &context->io, context->loadKey.key_path);
        return_if_error2(r, "Could not open: %s", context->loadKey.key_path);
        fallthrough;

    statecase(context->loadKey.state, LOAD_KEY_READ_KEY);
        goto_if_null2(context->loadKey.key_path, "Invalid path", r, TSS2_FAPI_RC_GENERAL_FAILURE,
                      error_cleanup); /**< to avoid scan-build errors. */

        key = &context->loadKey.key_object->misc.key;
        memset(&context->loadKey.key_object->misc.key, 0, sizeof(IFAPI_KEY));

        r = ifapi_keystore_load_finish(&context->keystore, &context->io,
                                       context->loadKey.key_object);
        if (r != TSS2_RC_SUCCESS) {
            ifapi_cleanup_ifapi_object(context->loadKey.key_object);
        }
        return_try_again(r);
        return_if_error(r, "read_finish failed");

        if (context->loadKey.key_object->objectType != IFAPI_KEY_OBJ)
            goto_error(r, TSS2_FAPI_RC_BAD_PATH, "%s is no key", error_cleanup,
                       context->loadKey.key_path);

        r = ifapi_initialize_object(context->esys, context->loadKey.key_object);
        goto_if_error_reset_state(r, "Initialize key object", error_cleanup);

        SAFE_FREE(context->loadKey.key_path);
        context->loadKey.handle = context->loadKey.key_object->public.handle;
        if (context->loadKey.handle != ESYS_TR_NONE) {
            /* Persistent key could be desearialized keys can be loaded */
            r = ifapi_copy_ifapi_key_object(&context->loadKey.auth_object,
                                            context->loadKey.key_object);
            goto_if_error(r, "Could not copy key object", error_cleanup);
            ifapi_cleanup_ifapi_object(context->loadKey.key_object);
            context->loadKey.state = LOAD_KEY_LOAD_KEY;

            return TSS2_FAPI_RC_TRY_AGAIN;
        }

        if (key->private.size == 0) {
            /* Create a deep copy of the primary key */
            r = ifapi_copy_ifapi_key_object(&context->createPrimary.pkey_object,
                                            context->loadKey.key_object);
            goto_if_error(r, "Could not copy primary key", error_cleanup);

            ifapi_cleanup_ifapi_key(key);
            context->primary_state = PRIMARY_READ_HIERARCHY;
            context->loadKey.state = LOAD_KEY_WAIT_FOR_PRIMARY;
            return TSS2_FAPI_RC_TRY_AGAIN;
        }
        IFAPI_OBJECT *copyToPush = calloc(1, sizeof(IFAPI_OBJECT));
        goto_if_null(copyToPush, "Out of memory", TSS2_FAPI_RC_MEMORY, error_cleanup);
        r = ifapi_copy_ifapi_key_object(copyToPush, context->loadKey.key_object);
        if (r) {
            free(copyToPush);
            LOG_ERROR("Could not create a copy to push");
            goto error_cleanup;
        }
        /* Add object to the list of keys to be loaded. */
        r = push_object_to_list(copyToPush, &context->loadKey.key_list);
        if (r) {
            free(copyToPush);
            LOG_ERROR("Out of memory");
            goto error_cleanup;
        }

        ifapi_cleanup_ifapi_object(context->loadKey.key_object);

        *position -= 1;
        context->loadKey.state = LOAD_KEY_GET_PATH;
        return TSS2_FAPI_RC_TRY_AGAIN;

    statecase(context->loadKey.state, LOAD_KEY_LOAD_KEY);
        if (!(context->loadKey.key_list)) {
            LOG_TRACE("All keys loaded.");
            return TSS2_RC_SUCCESS;
        }

        /* if flush_parent is false parent is only flushed if a new parent
           is available */
        if (!flush_parent && context->loadKey.parent_handle != ESYS_TR_NONE) {
            r = Esys_FlushContext(context->esys, context->loadKey.parent_handle);
            goto_if_error_reset_state(r, "Flush object", error_cleanup);
        }
        fallthrough;

    statecase(context->loadKey.state, LOAD_KEY_AUTHORIZE);
        key_object = context->loadKey.key_list->object;
        key = &key_object->misc.key;
        r = ifapi_authorize_object(context, &context->loadKey.auth_object, &auth_session);
        FAPI_SYNC(r, "Authorize key.", error_cleanup);

        /* Store parent handle in context for usage in ChangeAuth if not persistent */
        context->loadKey.parent_handle = context->loadKey.handle;
        if (context->loadKey.auth_object.misc.key.persistent_handle)
            context->loadKey.parent_handle_persistent = true;
        else
            context->loadKey.parent_handle_persistent = false;

        TPM2B_PRIVATE private;

        private.size = key->private.size;
        memcpy(&private.buffer[0], key->private.buffer, key->private.size);

        r = Esys_Load_Async(context->esys, context->loadKey.handle, auth_session,
                            ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE, &private,
                            &key->public);
        goto_if_error(r, "Load async", error_cleanup);
        fallthrough;

    statecase(context->loadKey.state, LOAD_KEY_AUTH);
        r = Esys_Load_Finish(context->esys, &context->loadKey.handle);
        return_try_again(r);
        goto_if_error_reset_state(r, "Load", error_cleanup);

        /* The current parent is flushed if not prohibited by flush parent */
        context->session2 = ESYS_TR_NONE;
        if (flush_parent && context->loadKey.auth_object.objectType == IFAPI_KEY_OBJ
            && !context->loadKey.auth_object.misc.key.persistent_handle) {
            r = Esys_FlushContext(context->esys, context->loadKey.auth_object.public.handle);
            goto_if_error_reset_state(r, "Flush object", error_cleanup);
        }
        LOG_TRACE("New key used as auth object.");
        ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
        r = ifapi_copy_ifapi_key_object(&context->loadKey.auth_object,
                                        context->loadKey.key_list->object);
        goto_if_error(r, "Could not copy loaded key", error_cleanup);
        context->loadKey.auth_object.public.handle = context->loadKey.handle;
        IFAPI_OBJECT *top_obj = context->loadKey.key_list->object;
        ifapi_cleanup_ifapi_object(top_obj);
        SAFE_FREE(context->loadKey.key_list->object);
        r = pop_object_from_list(&context->loadKey.key_list);
        goto_if_error_reset_state(r, "Pop key failed.", error_cleanup);

        context->loadKey.state = LOAD_KEY_LOAD_KEY;
        return TSS2_FAPI_RC_TRY_AGAIN;

    statecase(context->loadKey.state, LOAD_KEY_WAIT_FOR_PRIMARY);
        r = ifapi_load_primary_finish(context, &context->loadKey.handle);
        return_try_again(r);
        goto_if_error(r, "CreatePrimary", error_cleanup);

        /* Save the primary object for authorization */
        r = ifapi_copy_ifapi_key_object(&context->loadKey.auth_object,
                                        &context->createPrimary.pkey_object);
        goto_if_error(r, "Could not copy primary key", error_cleanup);

        ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);

        if (context->loadKey.key_list) {
            context->loadKey.state = LOAD_KEY_LOAD_KEY;
            return TSS2_FAPI_RC_TRY_AGAIN;
        } else {
            LOG_TRACE("success");
            ifapi_cleanup_ifapi_object(context->loadKey.key_object);
            return TSS2_RC_SUCCESS;
        }
        break;

    statecasedefault(context->loadKey.state);
    }

error_cleanup:
    if (context->loadKey.handle && context->loadKey.handle != ESYS_TR_NONE
        && context->loadKey.key_object->misc.key.persistent_handle) {
        Esys_FlushContext(context->esys, context->loadKey.handle);
    }
    ifapi_free_object_list(context->loadKey.key_list);
    ifapi_cleanup_ifapi_object(context->loadKey.key_object);
    SAFE_FREE(context->loadKey.key_path);
    return r;
}

/** Get the name alg corresponding to a FAPI object.
 *
 * @param[in] context The context with the default profile.
 * @param[in] object The object to be checked.
 * @retval TPMI_ALG_HASH The hash algorithm.
 * @retval 0 If no name alg can be assigned to the object.
 */
static size_t
get_name_alg(FAPI_CONTEXT *context, IFAPI_OBJECT *object) {
    switch (object->objectType) {
    case IFAPI_KEY_OBJ:
        return object->misc.key.public.publicArea.nameAlg;
    case IFAPI_NV_OBJ:
        return object->misc.nv.public.nvPublic.nameAlg;
    case IFAPI_HIERARCHY_OBJ:
        return context->profiles.default_profile.nameAlg;
    default:
        return 0;
    }
}

/** State machine to authorize a key, a NV object of a hierarchy.
 *
 * @param[in,out] context for storing all state information.
 * @param[in] object The FAPI object.
 * @param[out] session The session which can be used for object authorization.
 *
 * @retval TSS2_RC_SUCCESS If the authorization is successful
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
 * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
 *         store.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy for a certain path was not found.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
 *         not successful.
 * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for the policy
 *         execution fails.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
           is not defined.
 * @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_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 */
TSS2_RC
ifapi_authorize_object(FAPI_CONTEXT *context, IFAPI_OBJECT *object, ESYS_TR *session) {
    TSS2_RC     r;
    TPMI_YES_NO auth_required;

    LOG_DEBUG("Authorize object: %x", object->public.handle);
    switch (object->authorization_state) {
    statecase(object->authorization_state, AUTH_INIT)
        LOG_TRACE("**STATE** AUTH_INIT");
        if (!policy_digest_size(object)) {
            if (object_with_auth(object)) {
                /* Check whether hierarchy was already authorized. */
                if (object->objectType != IFAPI_HIERARCHY_OBJ
                    || !object->misc.hierarchy.authorized) {
                    char *description = NULL;
                    r = ifapi_get_description(object, &description);
                    return_if_error(r, "Get description");

                    r = ifapi_set_auth(context, object, description);
                    SAFE_FREE(description);
                    return_if_error(r, "Set auth value");
                }
            }
        }
        if (!policy_digest_size(object)) {
            /* No policy session needed current fapi session can be used */
            if (context->session1 && context->session1 != ESYS_TR_NONE)
                *session = context->session1;
            else
                /* Use password session if session1 had not been created */
                *session = ESYS_TR_PASSWORD;
            context->session2 = ESYS_TR_NONE;
            break;
        }

        /* Save current object to be authorized in context. */
        context->current_auth_object = object;
        r = ifapi_policyutil_execute_prepare(context, get_name_alg(context, object),
                                             object->policy);
        return_if_error(r, "Prepare policy execution.");

        /* Next state will switch from prev context to next context. */
        context->policy.util_current_policy = context->policy.util_current_policy->prev;
        object->authorization_state = AUTH_EXEC_POLICY;

        /* Use session for parameter encryption */
        if (context->session1 != ESYS_TR_PASSWORD) {
            context->session2 = context->session1;
        }
        fallthrough;

    statecase(object->authorization_state, AUTH_EXEC_POLICY)
        *session = ESYS_TR_NONE;
        r = ifapi_policyutil_execute(context, session);
        if (r == TSS2_FAPI_RC_TRY_AGAIN)
            return r;

        return_if_error(r, "Execute policy.");

        context->policy_session = *session;
        r = Esys_TRSess_GetAuthRequired(context->esys, *session, &auth_required);
        return_if_error(r, "GetAuthRequired");

        /* Check whether PolicyCommand requiring authorization was executed */
        if (auth_required == TPM2_YES) {
            char *description;
            r = ifapi_get_description(object, &description);
            return_if_error(r, "Get description");
            r = ifapi_set_auth(context, object, description);
            SAFE_FREE(description);
            goto_if_error(r, "Set auth value", error);
        }
        /* Clear continue session flag, so policy session will be flushed after authorization */
        r = Esys_TRSess_SetAttributes(context->esys, *session, 0, TPMA_SESSION_CONTINUESESSION);
        goto_if_error(r, "Esys_TRSess_SetAttributes", error);
        break;

    general_failure(object->authorization_state)
    }

    object->authorization_state = AUTH_INIT;
    return TSS2_RC_SUCCESS;

error:
    /* No policy call was executed session can be flushed */
    Esys_FlushContext(context->esys, *session);
    *session = ESYS_TR_NONE;
    return r;
}

/** State machine to write data to the NV ram of the TPM.
 *
 * The NV object will be read from object store and the data will be
 * written by one, or more than one if necessary, ESAPI calls to the NV ram of
 * the TPM.
 * The sub context nv_cmd will be prepared:
 * - data The buffer for the data which has to be written
 * - offset The current offset for writing
 * - numBytes The number of bytes which have to be written.
 *
 * @param[in,out] context for storing all state information.
 * @param[in] nvPath The fapi path of the NV object.
 * @param[in] param_offset The offset in the NV memory (will be stored in context).
 * @param[in] data The pointer to the data to be written.
 * @param[in] size The number of bytes to be written.
 *
 * @retval TSS2_RC_SUCCESS If data can be written.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
 +         not covered by other return codes.
 * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the object
 *         store.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND The nv object or an object needed for
 *         authentication was not found.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
 *          not successful.
 * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for the
 *         command execution fails.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
 *         is not defined.
 * @retval TSS2_FAPI_RC_BAD_PATH if a path is used in inappropriate context
 *         or contains illegal characters.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
 */
TSS2_RC
ifapi_nv_write(FAPI_CONTEXT  *context,
               char          *nvPath,
               size_t         param_offset,
               uint8_t const *data,
               size_t         size) {
    TSS2_RC              r = TSS2_RC_SUCCESS;
    ESYS_TR              auth_index;
    ESYS_TR              nv_index = context->nv_cmd.esys_handle;
    IFAPI_OBJECT        *object = &context->nv_cmd.nv_object;
    IFAPI_OBJECT        *auth_object = &context->nv_cmd.auth_object;
    TPM2B_MAX_NV_BUFFER *aux_data = (TPM2B_MAX_NV_BUFFER *)&context->aux_data;
    char                *nv_file_name = NULL;
    ESYS_TR              auth_session;

    switch (context->nv_cmd.nv_write_state) {
    statecase(context->nv_cmd.nv_write_state, NV2_WRITE_INIT);
        memset(&context->nv_cmd.nv_object, 0, sizeof(IFAPI_OBJECT));
        context->nv_cmd.nvPath = nvPath;
        context->nv_cmd.offset = param_offset;
        context->nv_cmd.numBytes = size;
        context->nv_cmd.data = data;
        if (context->nv_cmd.numBytes > context->nv_buffer_max)
            aux_data->size = context->nv_buffer_max;
        else
            aux_data->size = context->nv_cmd.numBytes;
        context->nv_cmd.data_idx = 0;

        /* Use calloc to ensure zero padding for write buffer. */
        context->nv_cmd.write_data = calloc(1, size);
        goto_if_null2(context->nv_cmd.write_data, "Out of memory.", r, TSS2_FAPI_RC_MEMORY,
                      error_cleanup);
        memcpy(context->nv_cmd.write_data, data, size);
        memcpy(&aux_data->buffer[0], &context->nv_cmd.data[0], aux_data->size);

        /* Prepare reading of the key from keystore. */
        r = ifapi_keystore_load_async(&context->keystore, &context->io, context->nv_cmd.nvPath);
        goto_if_error2(r, "Could not open: %s", error_cleanup, context->nv_cmd.nvPath);
        fallthrough;

    statecase(context->nv_cmd.nv_write_state, NV2_WRITE_READ);
        r = ifapi_keystore_load_finish(&context->keystore, &context->io, object);
        return_try_again(r);
        goto_if_error(r, "read_finish failed", error_cleanup);

        if (object->objectType != IFAPI_NV_OBJ)
            goto_error(r, TSS2_FAPI_RC_BAD_PATH, "%s is no NV object.", error_cleanup,
                       context->nv_cmd.nvPath);

        r = ifapi_initialize_object(context->esys, object);
        goto_if_error_reset_state(r, "Initialize NV object", error_cleanup);

        /* Store object info in context */
        nv_index = context->nv_cmd.nv_object.public.handle;
        context->nv_cmd.esys_handle = nv_index;
        context->nv_cmd.nv_obj = object->misc.nv;

        /* Check whether the file in object store can be updated if necessary */
        if (!(context->nv_cmd.nv_object.misc.nv.public.nvPublic.attributes & TPMA_NV_WRITTEN)) {
            r = ifapi_keystore_check_writeable(&context->keystore, nvPath);
            goto_if_error_reset_state(r, "Check whether update object store is possible.",
                                      error_cleanup);
        }

        /* Determine the object which will be uses for authorization. */
        if (object->misc.nv.public.nvPublic.attributes & TPMA_NV_PPWRITE) {
            ifapi_init_hierarchy_object(auth_object, ESYS_TR_RH_PLATFORM);
            auth_index = ESYS_TR_RH_PLATFORM;
        } else {
            if (object->misc.nv.public.nvPublic.attributes & TPMA_NV_OWNERWRITE) {
                ifapi_init_hierarchy_object(auth_object, ESYS_TR_RH_OWNER);
                auth_index = ESYS_TR_RH_OWNER;
            } else {
                auth_index = nv_index;
            }
            *auth_object = *object;
        }
        context->nv_cmd.auth_index = auth_index;

        /* Get A session for authorizing the NV write operation. */
        r = ifapi_get_sessions_async(context, IFAPI_SESSION_GEN_SRK | IFAPI_SESSION1,
                                     TPMA_SESSION_DECRYPT, 0);
        goto_if_error(r, "Create sessions", error_cleanup);

        fallthrough;

    statecase(context->nv_cmd.nv_write_state, NV2_WRITE_WAIT_FOR_SESSSION);
        r = ifapi_get_sessions_finish(context, &context->profiles.default_profile,
                                      object->misc.nv.public.nvPublic.nameAlg);
        return_try_again(r);
        goto_if_error_reset_state(r, " FAPI create session", error_cleanup);

        fallthrough;

    statecase(context->nv_cmd.nv_write_state, NV2_WRITE_AUTHORIZE);
        r = ifapi_authorize_object(context, auth_object, &auth_session);
        FAPI_SYNC(r, "Authorize NV object.", error_cleanup);

        context->nv_cmd.auth_session = auth_session;

        /* Prepare the writing to NV ram. */
        r = Esys_NV_Write_Async(context->esys, context->nv_cmd.auth_index, nv_index, auth_session,
                                ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE, aux_data,
                                context->nv_cmd.data_idx);
        goto_if_error_reset_state(r, " Fapi_NvWrite_Async", error_cleanup);

        if (!(object->misc.nv.public.nvPublic.attributes & TPMA_NV_NO_DA))
            context->nv_cmd.nv_write_state = NV2_WRITE_AUTH_SENT;
        else
            context->nv_cmd.nv_write_state = NV2_WRITE_NULL_AUTH_SENT;

        context->nv_cmd.bytesRequested = aux_data->size;

        fallthrough;

    case NV2_WRITE_AUTH_SENT:
    case NV2_WRITE_NULL_AUTH_SENT:
        r = Esys_NV_Write_Finish(context->esys);
        return_try_again(r);

        if (number_rc(r) == TPM2_RC_BAD_AUTH) {
            if (context->nv_cmd.nv_write_state == NV2_WRITE_NULL_AUTH_SENT) {
                IFAPI_OBJECT *auth_object2 = &context->nv_cmd.auth_object;
                char         *description;
                r = ifapi_get_description(auth_object2, &description);
                goto_if_error(r, "Get description", error_cleanup);
                r = ifapi_set_auth(context, auth_object2, description);
                SAFE_FREE(description);
                goto_if_error_reset_state(r, " Fapi_NvWrite_Finish", error_cleanup);

                /* Prepare the writing to NV ram. */
                r = Esys_NV_Write_Async(context->esys, context->nv_cmd.auth_index, nv_index,
                                        context->nv_cmd.auth_session,
                                        ENC_SESSION_IF_POLICY(context->nv_cmd.auth_session),
                                        ESYS_TR_NONE, aux_data, context->nv_cmd.data_idx);
                goto_if_error_reset_state(r, "FAPI NV_Write_Async", error_cleanup);

                context->nv_cmd.nv_write_state = NV2_WRITE_AUTH_SENT;
                return TSS2_FAPI_RC_TRY_AGAIN;
            }
        }
        goto_if_error_reset_state(r, "FAPI NV_Write_Finish", error_cleanup);

        context->nv_cmd.numBytes -= context->nv_cmd.bytesRequested;

        if (context->nv_cmd.numBytes > 0) {
            /* Increment data idx with number of transmitted bytes. */
            context->nv_cmd.data_idx += aux_data->size;
            if (context->nv_cmd.numBytes > context->nv_buffer_max)
                aux_data->size = context->nv_buffer_max;
            else
                aux_data->size = context->nv_cmd.numBytes;
            memcpy(&aux_data->buffer[0], &context->nv_cmd.write_data[context->nv_cmd.data_idx],
                   aux_data->size);

        statecase(context->nv_cmd.nv_write_state, NV2_WRITE_AUTHORIZE2);
            r = ifapi_authorize_object(context, auth_object, &auth_session);
            FAPI_SYNC(r, "Authorize NV object.", error_cleanup);

            /* Prepare the writing to NV ram */
            r = Esys_NV_Write_Async(context->esys, context->nv_cmd.auth_index, nv_index,
                                    auth_session, ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE,
                                    aux_data, context->nv_cmd.data_idx);
            goto_if_error_reset_state(r, "FAPI NV_Write", error_cleanup);

            context->nv_cmd.bytesRequested = aux_data->size;
            context->nv_cmd.nv_write_state = NV2_WRITE_AUTH_SENT;
            return TSS2_FAPI_RC_TRY_AGAIN;
        }
        if (auth_object->auth_changed) {
            /* Auth for automatically added NV objects which hav no
               auth value has to be updated.
            */
            context->nv_cmd.nv_object.misc.nv.with_auth = TPM2_NO;
        }

        if (context->nv_cmd.nv_object.misc.nv.public.nvPublic.attributes & TPMA_NV_WRITTEN
            && !auth_object->auth_changed) {
            LOG_DEBUG("success");
            r = TSS2_RC_SUCCESS;
            break;
        }
        fallthrough;

    statecase(context->nv_cmd.nv_write_state, NV2_WRITE_WRITE_PREPARE);
        /* Set written bit in keystore */
        context->nv_cmd.nv_object.misc.nv.public.nvPublic.attributes |= TPMA_NV_WRITTEN;
        /* Perform esys serialization if necessary */
        r = ifapi_esys_serialize_object(context->esys, &context->nv_cmd.nv_object);
        goto_if_error(r, "Prepare serialization", error_cleanup);

        /* Start writing the NV object to the key store */
        r = ifapi_keystore_store_async(&context->keystore, &context->io, context->nv_cmd.nvPath,
                                       &context->nv_cmd.nv_object);
        goto_if_error_reset_state(r, "Could not open: %s", error_cleanup, context->nv_cmd.nvPath);
        context->nv_cmd.nv_write_state = NV2_WRITE_WRITE;
        fallthrough;

    statecase(context->nv_cmd.nv_write_state, NV2_WRITE_WRITE);
        /* Finish writing the NV object to the key store */
        r = ifapi_keystore_store_finish(&context->io);
        return_try_again(r);
        return_if_error_reset_state(r, "write_finish failed");

        LOG_DEBUG("success");
        r = TSS2_RC_SUCCESS;
        break;

    statecasedefault(context->nv_cmd.nv_write_state);
    }

error_cleanup:
    context->session2 = ESYS_TR_NONE;
    SAFE_FREE(nv_file_name);
    SAFE_FREE(context->nv_cmd.write_data);
    context->nv_cmd.nv_write_state = NV2_WRITE_INIT;
    return r;
}

/** State machine to read data from the NV ram of the TPM.
 *
 * The state machine can bes used to read NV data for a given ESAPI
 * object or for a TPM NV index. If TPM NV index is used a ESAPI object
 * will be created if the NV index exists. If not the size 0 will be
 * returned.
 * If a TPM handle is used the initial stat NV_READ_CHECK_HANDLE has
 * to be set: context->nv_cmd.nv_read_state.
 * Context nv_cmd has to be prepared before the call of this function:
 * With an TPM handle:
 * - tpm_handle The ESAPI handle of the authorization object.
 * With an ESYS handle:
 * - auth_index The ESAPI handle of the authorization object.
 * - numBytes The number of bytes which should be read.
 * - esys_handle The ESAPI handle of the NV object.
 *
 * @param[in,out] context for storing all state information.
 * @param[out] data the data fetched from TPM.
 * @param[in,out] size The number of bytes requested and fetched.
 *                will be 0 if a TPM handle is used but the NV index
 *                does not exist.
 *
 * @retval TSS2_RC_SUCCESS If the data was read successfully.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
 +         not covered by other return codes.
 * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the object
 *         store.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy for a certain path was not found.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
 *         not successful.
 * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for the
 *         execution fails.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
 *         is not defined.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 */
TSS2_RC
ifapi_nv_read(FAPI_CONTEXT *context, uint8_t **data, size_t *size) {
    TSS2_RC               r;
    UINT16                aux_size;
    TPM2B_MAX_NV_BUFFER  *aux_data;
    UINT16                bytesRequested = context->nv_cmd.bytesRequested;
    size_t               *numBytes = &context->nv_cmd.numBytes;
    ESYS_TR               nv_index = context->nv_cmd.esys_handle;
    IFAPI_OBJECT         *auth_object = &context->nv_cmd.auth_object;
    ESYS_TR               session;
    TPMS_CAPABILITY_DATA *capabilityData = NULL;
    TPM2B_NV_PUBLIC      *nvPublic = NULL;
    TPMI_YES_NO           moreData;

    switch (context->nv_cmd.nv_read_state) {
    statecase(context->nv_cmd.nv_read_state, NV_READ_INIT);
        LOG_TRACE("NV_READ_INIT");
        context->nv_cmd.rdata = NULL;
        fallthrough;

    statecase(context->nv_cmd.nv_read_state, NV_READ_AUTHORIZE);
        LOG_TRACE("NV_READ_AUTHORIZE");
        r = ifapi_authorize_object(context, auth_object, &session);
        FAPI_SYNC(r, "Authorize NV object.", error_cleanup);

        if (*numBytes > context->nv_buffer_max)
            aux_size = context->nv_buffer_max;
        else
            aux_size = *numBytes;

        /* Prepare the reading from NV ram. */

        r = Esys_NV_Read_Async(context->esys, context->nv_cmd.auth_index, nv_index, session,
                               ENC_SESSION_IF_POLICY(session), ESYS_TR_NONE, aux_size, 0);
        goto_if_error_reset_state(r, " Fapi_NvRead_Async", error_cleanup);

        context->nv_cmd.nv_read_state = NV_READ_AUTH_SENT;
        context->nv_cmd.bytesRequested = aux_size;

        return TSS2_FAPI_RC_TRY_AGAIN;

    statecase(context->nv_cmd.nv_read_state, NV_READ_AUTH_SENT);
        LOG_TRACE("NV_READ_NULL_AUTH_SENT");
        if (context->nv_cmd.rdata == NULL) {
            /* Allocate the data buffer if not already initialized. */
            LOG_TRACE("Allocate %zu bytes", *numBytes);
            context->nv_cmd.rdata = malloc(*numBytes);
        }
        *data = context->nv_cmd.rdata;
        goto_if_null(*data, "Malloc failed", TSS2_FAPI_RC_MEMORY, error_cleanup);

        r = Esys_NV_Read_Finish(context->esys, &aux_data);

        if (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN)
            return TSS2_FAPI_RC_TRY_AGAIN;

        if (context->nv_cmd.auth_index == ESYS_TR_RH_OWNER && number_rc(r) == TPM2_RC_BAD_AUTH
            && auth_object->misc.hierarchy.with_auth == TPM2_NO) {
            /* NULL auth failed, password was used for owner hierarchy, try again. */
            auth_object->misc.hierarchy.with_auth = TPM2_YES;
            context->nv_cmd.nv_read_state = NV_READ_AUTHORIZE;

            return TSS2_FAPI_RC_TRY_AGAIN;
        }

        goto_if_error_reset_state(r, "FAPI NV_Read_Finish", error_cleanup);

        if (aux_data->size < bytesRequested)
            *numBytes = 0;
        else
            *numBytes -= aux_data->size;
        memcpy(*data + context->nv_cmd.data_idx, &aux_data->buffer[0], aux_data->size);
        context->nv_cmd.data_idx += aux_data->size;
        free(aux_data);
        if (*numBytes > 0) {
        statecase(context->nv_cmd.nv_read_state, NV_READ_AUTHORIZE2);
            r = ifapi_authorize_object(context, auth_object, &session);
            FAPI_SYNC(r, "Authorize NV object.", error_cleanup);

            /* The reading of the NV data is not completed. The next
            reading will be prepared. */
            if (*numBytes > context->nv_buffer_max)
                aux_size = context->nv_buffer_max;
            else
                aux_size = *numBytes;

            r = Esys_NV_Read_Async(context->esys, context->nv_cmd.auth_index, nv_index, session,
                                   ENC_SESSION_IF_POLICY(session), ESYS_TR_NONE, aux_size,
                                   context->nv_cmd.data_idx);
            goto_if_error_reset_state(r, "FAPI NV_Read", error_cleanup);
            context->nv_cmd.bytesRequested = aux_size;
            context->nv_cmd.nv_read_state = NV_READ_AUTH_SENT;
            return TSS2_FAPI_RC_TRY_AGAIN;
        } else {
            *size = context->nv_cmd.data_idx;
            LOG_DEBUG("success");
            r = TSS2_RC_SUCCESS;

            if (auth_object->auth_changed) {
                /* Auth for automatically added NV objects which hav no
                   auth value has to be updated.
                */
                context->nv_cmd.nv_object.misc.nv.with_auth = TPM2_NO;
                /* Start writing the NV object to the key store */
                r = ifapi_keystore_store_async(&context->keystore, &context->io,
                                               context->nv_cmd.nvPath, &context->nv_cmd.nv_object);
                goto_if_error_reset_state(r, "Could not open: %sh", error_cleanup,
                                          context->nv_cmd.nvPath);
                context->nv_cmd.nv_read_state = NV_READ_WRITE_CHANGED_OBJECT;
                return TSS2_FAPI_RC_TRY_AGAIN;
            } else {
                break;
            }
        }
    statecase(context->nv_cmd.nv_read_state, NV_READ_CHECK_HANDLE);
        context->nv_cmd.data_idx = 0;
        context->nv_cmd.auth_index = ESYS_TR_RH_OWNER;
        context->nv_cmd.offset = 0;
        r = Esys_GetCapability_Async(context->esys, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                     TPM2_CAP_HANDLES, context->nv_cmd.tpm_handle, 1);
        goto_if_error(r, "Esys_GetCapability_Async", error_cleanup);

        fallthrough;

    statecase(context->nv_cmd.nv_read_state, NV_READ_GET_CAPABILITY);
        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
            || capabilityData->data.handles.handle[0] != context->nv_cmd.tpm_handle) {
            *size = 0;
            break;
        }
        SAFE_FREE(capabilityData);
        r = Esys_TR_FromTPMPublic_Async(context->esys, context->nv_cmd.tpm_handle, ESYS_TR_NONE,
                                        ESYS_TR_NONE, ESYS_TR_NONE);
        goto_if_error(r, "Esys_TR_FromTPMPublic_Async", error_cleanup);

        fallthrough;

    statecase(context->nv_cmd.nv_read_state, NV_READ_GET_ESYS_HANDLE);
        r = Esys_TR_FromTPMPublic_Finish(context->esys, &context->nv_cmd.esys_handle);
        return_try_again(r);

        goto_if_error(r, "Esys_TR_FromTPMPublic_Finish", error_cleanup);

        r = Esys_NV_ReadPublic_Async(context->esys, context->nv_cmd.esys_handle, ESYS_TR_NONE,
                                     ESYS_TR_NONE, ESYS_TR_NONE);
        goto_if_error(r, "Esys_NV_ReadPublic_Async", error_cleanup);

        fallthrough;

    statecase(context->nv_cmd.nv_read_state, NV_READ_GET_NV_PUBLIC);
        r = Esys_NV_ReadPublic_Finish(context->esys, &nvPublic, NULL);
        return_try_again(r);
        goto_if_error(r, "Error: nv read public", error_cleanup);

        context->nv_cmd.numBytes = nvPublic->nvPublic.dataSize;
        SAFE_FREE(nvPublic);
        context->nv_cmd.nv_read_state = NV_READ_INIT;
        return TSS2_FAPI_RC_TRY_AGAIN;

    statecase(context->nv_cmd.nv_read_state, NV_READ_WRITE_CHANGED_OBJECT)
    /* Finish writing the NV object to the key store */
        r = ifapi_keystore_store_finish(&context->io);
        return_try_again(r);
        return_if_error_reset_state(r, "write_finish failed");
        break;

    statecasedefault(context->nv_cmd.nv_read_state);
    }

error_cleanup:
    context->session2 = ESYS_TR_NONE;
    SAFE_FREE(capabilityData);
    context->nv_cmd.nv_read_state = NV_READ_INIT;
    return r;
}

#define min(X, Y) ((X) > (Y)) ? (Y) : X

/** State machine to retrieve random data from TPM.
 *
 * If the buffer size exceeds the maximum size, several ESAPI calls are made.
 *
 * @param[in,out] context for storing all state information.
 * @param[in] numBytes Number of random bytes to be computed.
 * @param[out] data The random data.
 *
 * @retval TSS2_RC_SUCCESS If random data can be computed.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 */
TSS2_RC
ifapi_get_random(FAPI_CONTEXT *context, size_t numBytes, uint8_t **data) {
    TSS2_RC       r;
    TPM2B_DIGEST *aux_data = NULL;

    switch (context->get_random_state) {
    statecase(context->get_random_state, GET_RANDOM_INIT);
        context->get_random.numBytes = numBytes;
        context->get_random.data = calloc(1, context->get_random.numBytes);
        context->get_random.idx = 0;
        return_if_null(context->get_random.data, "FAPI out of memory.", TSS2_FAPI_RC_MEMORY);

        /* Prepare the creation of random data. */
        r = Esys_GetRandom_Async(context->esys, context->session1, ESYS_TR_NONE, ESYS_TR_NONE,
                                 min(context->get_random.numBytes, sizeof(TPMU_HA)));
        goto_if_error_reset_state(r, "FAPI GetRandom", error_cleanup);
        fallthrough;

    statecase(context->get_random_state, GET_RANDOM_SENT);
        r = Esys_GetRandom_Finish(context->esys, &aux_data);
        return_try_again(r);
        goto_if_error_reset_state(r, "FAPI GetRandom_Finish", error_cleanup);

        if (aux_data->size > context->get_random.numBytes) {
            goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "TPM returned too many bytes", error_cleanup);
        }

        /* Save created random data. */
        memcpy(context->get_random.data + context->get_random.idx, &aux_data->buffer[0],
               aux_data->size);
        context->get_random.numBytes -= aux_data->size;
        context->get_random.idx += aux_data->size;
        Esys_Free(aux_data);
        aux_data = NULL;
        if (context->get_random.numBytes > 0) {

            /* Continue creaion of random data if needed. */
            r = Esys_GetRandom_Async(context->esys, context->session1, ESYS_TR_NONE, ESYS_TR_NONE,
                                     min(context->get_random.numBytes, sizeof(TPMU_HA)));
            goto_if_error_reset_state(r, "FAPI GetRandom", error_cleanup);

            return TSS2_FAPI_RC_TRY_AGAIN;
        }
        break;

    statecasedefault(context->get_random_state);
    }

    *data = context->get_random.data;

    LOG_DEBUG("success");
    context->get_random_state = GET_RANDOM_INIT;
    return TSS2_RC_SUCCESS;

error_cleanup:
    if (aux_data)
        Esys_Free(aux_data);
    context->get_random_state = GET_RANDOM_INIT;
    if (context->get_random.data != NULL)
        SAFE_FREE(context->get_random.data);
    return r;
}

/** Load a key and initialize profile and session for ESAPI execution.
 *
 * This state machine prepares the session for key loading. Some
 * session related parameters will be taken from profile.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     keyPath the key path without the parent directories
 *                of the key store. (e.g. HE/EK, HS/SRK/mykey)
 * @param[out]    key_object The callee allocated internal representation
 *                of a key object.
 *
 * @retval TSS2_RC_SUCCESS If the key was loaded successfully.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
 *         not covered by other return codes.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
 * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the object
 *         store.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy or key was not found.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
 *         not successful.
 * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for policy
 *         execution fails.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
           is not defined.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @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.
 */
TSS2_RC
ifapi_load_key(FAPI_CONTEXT *context, char const *keyPath, IFAPI_OBJECT **key_object) {
    TSS2_RC              r;
    const IFAPI_PROFILE *profile;

    return_if_null(keyPath, "Bad reference for key path.", TSS2_FAPI_RC_BAD_REFERENCE);

    switch (context->loadKey.prepare_state) {
    statecase(context->loadKey.prepare_state, PREPARE_LOAD_KEY_INIT);
        context->loadKey.path = keyPath;

        /* Prepare the session creation. */
        r = ifapi_get_sessions_async(context, IFAPI_SESSION_GEN_SRK | IFAPI_SESSION1,
                                     TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT, 0);
        goto_if_error_reset_state(r, "Create sessions", error_cleanup);
        fallthrough;

    statecase(context->loadKey.prepare_state, PREPARE_LOAD_KEY_WAIT_FOR_SESSION);
        r = ifapi_profiles_get(&context->profiles, context->loadKey.path, &profile);
        goto_if_error_reset_state(r, "Reading profile data", error_cleanup);

        r = ifapi_get_sessions_finish(context, profile, profile->nameAlg);
        return_try_again(r);
        goto_if_error_reset_state(r, " FAPI create session", error_cleanup);

        /* Prepare the key loading. */
        r = ifapi_load_keys_async(context, context->loadKey.path);
        goto_if_error(r, "Load keys.", error_cleanup);
        fallthrough;

    statecase(context->loadKey.prepare_state, PREPARE_LOAD_KEY_WAIT_FOR_KEY);
        r = ifapi_load_keys_finish(context, IFAPI_FLUSH_PARENT, &context->loadKey.handle,
                                   key_object);
        return_try_again(r);
        goto_if_error_reset_state(r, " Load key.", error_cleanup);

        break;

    statecase(context->loadKey.prepare_state, PREPARE_LOAD_KEY_INIT_KEY);
        context->loadKey.path = keyPath;
        r = ifapi_load_keys_async(context, context->loadKey.path);
        goto_if_error(r, "Load keys.", error_cleanup);

        context->loadKey.prepare_state = PREPARE_LOAD_KEY_WAIT_FOR_KEY;

        return TSS2_FAPI_RC_TRY_AGAIN;

    statecasedefault(context->loadKey.prepare_state);
    }

error_cleanup:
    context->loadKey.prepare_state = PREPARE_LOAD_KEY_INIT;
    return r;
}

/** State machine for signing operation.
 *
 * The key used for signing will be authorized and the signing of the passed data
 * will be executed.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     sig_key_object The Fapi key object which will be used to
 *                sign the passed digest.
 * @param[in]     padding is the padding algorithm used. Possible values are RSA_SSA,
 *                RSA_PPSS (case insensitive). padding MAY be NULL.
 * @param[in]     digest is the data to be signed, already hashed.
 *                digest MUST NOT be NULL.
 * @param[out]    tpm_signature returns the signature in binary form (DER format).
 *                tpm_signature MUST NOT be NULL (callee-allocated).
 * @param[out]    publicKey is the public key of the signing key in PEM format.
 *                publicKey is callee allocated and MAY be NULL.
 * @param[out]    certificate is the certificate associated with the signing key
 *                in PEM format. certificate MAY be NULL.
 *
 * @retval TSS2_RC_SUCCESS If the signing was successful.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
 *         not covered by other return codes.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
 * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
 *         store.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy for a certain path was not found.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
 *         not successful.
 * @retval TSS2_FAPI_RC_BAD_TEMPLATE In a invalid policy is loaded during execution.
 * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for policy
 *         execution fails.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
 *         is not defined.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 */
TSS2_RC
ifapi_key_sign(FAPI_CONTEXT      *context,
               IFAPI_OBJECT      *sig_key_object,
               char const        *padding,
               TPM2B_DIGEST      *digest,
               TPMT_TK_HASHCHECK *validation,
               TPMT_SIGNATURE   **tpm_signature,
               char             **publicKey,
               char             **certificate) {
    TSS2_RC         r;
    TPMT_SIG_SCHEME sig_scheme;
    ESYS_TR         session;

    TPMT_TK_HASHCHECK hash_validation = {
        .tag = TPM2_ST_HASHCHECK,
        .hierarchy = TPM2_RH_OWNER,
    };
    if (validation) {
        hash_validation = *validation;
    } else {
        memset(&hash_validation.digest, 0, sizeof(TPM2B_DIGEST));
    }

    switch (context->Key_Sign.state) {
    statecase(context->Key_Sign.state, SIGN_INIT);
        sig_key_object = context->Key_Sign.key_object;
        context->Key_Sign.handle = sig_key_object->public.handle;

        r = ifapi_authorize_object(context, sig_key_object, &session);
        return_try_again(r);
        goto_if_error(r, "Authorize signing key", cleanup);

        r = ifapi_get_sig_scheme(context, sig_key_object, padding, digest, &sig_scheme);
        goto_if_error(r, "Get signature scheme", cleanup);

        /* Prepare the signing operation. */
        r = Esys_Sign_Async(context->esys, context->Key_Sign.handle, session,
                            ENC_SESSION_IF_POLICY(session), ESYS_TR_NONE, digest, &sig_scheme,
                            &hash_validation);
        goto_if_error(r, "Error: Sign", cleanup);
        fallthrough;

    statecase(context->Key_Sign.state, SIGN_AUTH_SENT);
        context->Key_Sign.signature = NULL;
        r = Esys_Sign_Finish(context->esys, &context->Key_Sign.signature);
        return_try_again(r);
        context->session2 = ESYS_TR_NONE;
        goto_if_error(r, "Error: Sign", cleanup);

        /* Prepare the flushing of the signing key. */
        if (!sig_key_object->misc.key.persistent_handle) {
            r = Esys_FlushContext_Async(context->esys, context->Key_Sign.handle);
            goto_if_error(r, "Error: FlushContext", cleanup);
        }
        fallthrough;

    statecase(context->Key_Sign.state, SIGN_WAIT_FOR_FLUSH);
        if (!sig_key_object->misc.key.persistent_handle) {
            r = Esys_FlushContext_Finish(context->esys);
            return_try_again(r);
            goto_if_error(r, "Error: Sign", cleanup);
        }

        int pem_size;
        if (publicKey) {
            /* Convert internal key object to PEM format. */
            r = ifapi_pub_pem_key_from_tpm(&sig_key_object->misc.key.public, publicKey, &pem_size);
            goto_if_error(r, "Conversion pub key to PEM failed", cleanup);
        }
        context->Key_Sign.handle = ESYS_TR_NONE;
        *tpm_signature = context->Key_Sign.signature;
        if (certificate) {
            if (context->Key_Sign.key_object->misc.key.certificate) {
                *certificate = strdup(context->Key_Sign.key_object->misc.key.certificate);
                goto_if_null(*certificate, "Out of memory.", TSS2_FAPI_RC_MEMORY, cleanup);
            } else {
                strdup_check(*certificate, "", r, cleanup);
            }
        }
        LOG_TRACE("success");
        r = TSS2_RC_SUCCESS;
        break;

    statecasedefault(context->Key_Sign.state);
    }

cleanup:
    if (context->Key_Sign.handle != ESYS_TR_NONE)
        Esys_FlushContext(context->esys, context->Key_Sign.handle);
    ifapi_cleanup_ifapi_object(context->Key_Sign.key_object);
    context->Key_Sign.state = SIGN_INIT;

    return r;
}

/** Get json encoding for FAPI object.
 *
 * A json representation which can be used for exporting of a FAPI object will
 * be created.
 *
 * @param[in]   context The FAPI_CONTEXT.
 * @param[in]   object The object to be serialized.
 * @param[out]  json_string The json string created by the deserialzation
 *              function (callee-allocated).
 *
 * @retval TSS2_RC_SUCCESS If the serialization was successful.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during
 *         serialization.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE If a NULL pointer was passed for
 *         the object.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 */
TSS2_RC
ifapi_get_json(FAPI_CONTEXT *context, IFAPI_OBJECT *object, char **json_string) {
    TSS2_RC      r = TSS2_RC_SUCCESS;
    json_object *jso = NULL;

    /* Perform esys serialization if necessary */
    r = ifapi_esys_serialize_object(context->esys, object);
    goto_if_error(r, "Prepare serialization", cleanup);

    r = ifapi_json_IFAPI_OBJECT_serialize(object, &jso);
    return_if_error(r, "Serialize duplication object");

    *json_string = strdup(json_object_to_json_string_ext(jso, JSON_C_TO_STRING_PRETTY));
    goto_if_null2(*json_string, "Converting json to string", r, TSS2_FAPI_RC_MEMORY, cleanup);

cleanup:
    if (jso)
        json_object_put(jso);
    return r;
}

/** Serialize persistent objects into buffer of keystore object.
 *
 * NV objects and persistent keys will serialized via the ESYS API to
 * enable reconstruction durinng loading from keystore.
 *
 * @param[in]     ectx The ESAPI context.
 * @param[in,out] object  The nv object or the key.
 * @retval TSS2_RC_SUCCESS if the function call was a success.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occured.
 */
TSS2_RC
ifapi_esys_serialize_object(ESYS_CONTEXT *ectx, IFAPI_OBJECT *object) {
    TSS2_RC    r = TSS2_RC_SUCCESS;
    IFAPI_KEY *key_object = NULL;
    IFAPI_NV  *nv_object;

    switch (object->objectType) {
    case IFAPI_NV_OBJ:
        nv_object = &object->misc.nv;
        if (nv_object->serialization.buffer != NULL) {
            /* Cleanup old buffer */
            Fapi_Free(nv_object->serialization.buffer);
            nv_object->serialization.buffer = NULL;
        }
        r = Esys_TR_Serialize(ectx, object->public.handle, &nv_object->serialization.buffer,
                              &nv_object->serialization.size);
        return_if_error(r, "Error serialize esys object");
        break;

    case IFAPI_KEY_OBJ:
        key_object = &object->misc.key;
        key_object->serialization.size = 0;
        if (key_object->serialization.buffer != NULL) {
            /* Cleanup old buffer */
            Fapi_Free(key_object->serialization.buffer);
            key_object->serialization.buffer = NULL;
        }
        if (object->public.handle != ESYS_TR_NONE && key_object->persistent_handle) {
            key_object->serialization.buffer = NULL;
            r = Esys_TR_Serialize(ectx, object->public.handle, &key_object->serialization.buffer,
                                  &key_object->serialization.size);
            return_if_error(r, "Error serialize esys object");
        }
        break;

    default:
        /* Nothing to be done */
        break;
    }
    return TSS2_RC_SUCCESS;
}

/** Initialize the part of an IFAPI_OBJECT  which is not serialized.
 *
 * For persistent objects the correspodning ESYS object will be created.
 *
 * @param[in,out] ectx The ESYS context.
 * @param[out] object the deserialzed binary object.
 * @retval TSS2_RC_SUCCESS if the function call was a success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if the json object can't be deserialized.
 */
TSS2_RC
ifapi_initialize_object(ESYS_CONTEXT *ectx, IFAPI_OBJECT *object) {
    TSS2_RC r = TSS2_RC_SUCCESS;
    ESYS_TR handle;

    switch (object->objectType) {
    case IFAPI_NV_OBJ:
        if (object->misc.nv.serialization.size > 0) {
            r = Esys_TR_Deserialize(ectx, &object->misc.nv.serialization.buffer[0],
                                    object->misc.nv.serialization.size, &handle);
            goto_if_error(r, "Error dserialize esys object", cleanup);
        } else {
            handle = ESYS_TR_NONE;
        }
        object->authorization_state = AUTH_INIT;
        object->public.handle = handle;
        break;

    case IFAPI_KEY_OBJ:
        if (object->misc.key.serialization.size > 0) {
            r = Esys_TR_Deserialize(ectx, &object->misc.key.serialization.buffer[0],
                                    object->misc.key.serialization.size, &handle);
            goto_if_error(r, "Error deserialize esys object", cleanup);
        } else {
            handle = ESYS_TR_NONE;
        }
        object->authorization_state = AUTH_INIT;
        object->public.handle = handle;
        break;

    default:
        object->authorization_state = AUTH_INIT;
        break;
    }

    return r;

cleanup:
    SAFE_FREE(object->policy);
    return r;
}

/** Prepare key creation with an auth value.
 *
 * The auth value will be copied int the FAPI context for later use in key creation.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     keyPath the key path without the parent directories
 *                of the key store. (e.g. HE/EK, HS/SRK/mykey)
 * @param[in]     policyPath identifies the policy to be associated with the new key.
 *                policyPath MAY be NULL. If policyPath is NULL then no policy will
 *                be associated with the key.
 * @param[in]     authValue The authentication value of the key.
 *
 * @retval TSS2_RC_SUCCESS If the preparation was successful.
 * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS If the object with does already exist in
 *         keystore.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
 *         config file.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @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
ifapi_key_create_prepare_auth(FAPI_CONTEXT *context,
                              char const   *keyPath,
                              char const   *policyPath,
                              char const   *authValue) {
    TSS2_RC r;

    memset(&context->cmd.Key_Create.inSensitive, 0, sizeof(TPM2B_SENSITIVE_CREATE));
    if (authValue) {
        /* Copy the auth value */
        if (strlen(authValue) > sizeof(TPMU_HA)) {
            return_error(TSS2_FAPI_RC_BAD_VALUE, "Password too long.");
        }
        memcpy(&context->cmd.Key_Create.inSensitive.sensitive.userAuth.buffer, authValue,
               strlen(authValue));
        context->cmd.Key_Create.inSensitive.sensitive.userAuth.size = strlen(authValue);
    }
    context->cmd.Key_Create.gen_sensitive_random = false;
    context->cmd.Key_Create.inSensitive.sensitive.data.size = 0;
    r = ifapi_key_create_prepare(context, keyPath, policyPath);
    return r;
}

/** Prepare key creation with an auth value and sensitive data.
 *
 * The auth value and the sensitive data will be copied int the FAPI context
 * for later use in key creation.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     keyPath the key path without the parent directories
 *                of the key store. (e.g. HE/EK, HS/SRK/mykey)
 * @param[in]     policyPath identifies the policy to be associated with the new key.
 *                policyPath MAY be NULL. If policyPath is NULL then no policy will
 *                be associated with the key.
 * @param[in]     dataSize The size of the sensitive data.
 * @param[in]     authValue The authentication value of the key.
 * @param[in]     data The sensitive data.
 *
 * @retval TSS2_RC_SUCCESS If the preparation was successful.
 * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS If the object with does already exist in
 *         keystore.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
 *         config file.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @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
ifapi_key_create_prepare_sensitive(FAPI_CONTEXT  *context,
                                   char const    *keyPath,
                                   char const    *policyPath,
                                   size_t         dataSize,
                                   char const    *authValue,
                                   uint8_t const *data) {
    TSS2_RC r;

    memset(&context->cmd.Key_Create.inSensitive, 0, sizeof(TPM2B_SENSITIVE_CREATE));
    if (dataSize > sizeof(context->cmd.Key_Create.inSensitive.sensitive.data.buffer)
        || dataSize == 0) {
        return_error(TSS2_FAPI_RC_BAD_VALUE, "Data too big or equal zero.");
    }
    if (data) {
        /* Copy the sensitive data */
        context->cmd.Key_Create.gen_sensitive_random = false;
        memcpy(&context->cmd.Key_Create.inSensitive.sensitive.data.buffer, data, dataSize);
    } else {
        context->cmd.Key_Create.gen_sensitive_random = true;
    }
    context->cmd.Key_Create.inSensitive.sensitive.data.size = dataSize;
    if (authValue) {
        /* Copy the auth value. */
        if (strlen(authValue) > sizeof(TPMU_HA)) {
            return_error(TSS2_FAPI_RC_BAD_VALUE, "Password too long.");
        }
        memcpy(&context->cmd.Key_Create.inSensitive.sensitive.userAuth.buffer, authValue,
               strlen(authValue));
        context->cmd.Key_Create.inSensitive.sensitive.userAuth.size = strlen(authValue);
    }
    r = ifapi_key_create_prepare(context, keyPath, policyPath);
    return r;
}

/** Prepare key creation if possible.
 *
 * It will be checked whether the object already exists in key store and the FAPI context
 * will be initialized appropriate for key creation.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     keyPath the key path without the parent directories
 *                of the key store. (e.g. HE/EK, HS/SRK/mykey)
 * @param[in]     policyPath identifies the policy to be associated with the new key.
 *                policyPath MAY be NULL. If policyPath is NULL then no policy will
 *                be associated with the key.
 *
 * @retval TSS2_RC_SUCCESS If the preparation was successful.
 * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS If the object with does already exist in
 *         keystore.
 * @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_NO_TPM if FAPI was initialized in no-TPM-mode via its
 *         config file.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @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
ifapi_key_create_prepare(FAPI_CONTEXT *context, char const *keyPath, char const *policyPath) {
    TSS2_RC       r;
    IFAPI_OBJECT *object = &context->cmd.Key_Create.object;
    NODE_STR_T   *path_list = NULL;

    LOG_TRACE("call");
    r = ifapi_session_init(context);
    return_if_error(r, "Initialize Key_Create");

    /* First check whether an existing object would be overwritten */
    r = ifapi_keystore_check_overwrite(&context->keystore, keyPath);
    return_if_error2(r, "Check overwrite %s", keyPath);

    context->srk_handle = 0;

    /* Clear the memory used for the the new key object */
    memset(&context->cmd.Key_Create.outsideInfo, 0, sizeof(TPM2B_DATA));
    memset(&context->cmd.Key_Create.creationPCR, 0, sizeof(TPML_PCR_SELECTION));
    memset(object, 0, sizeof(IFAPI_OBJECT));

    strdup_check(context->cmd.Key_Create.policyPath, policyPath, r, error);
    strdup_check(context->cmd.Key_Create.keyPath, keyPath, r, error);
    r = get_explicit_key_path(&context->keystore, keyPath, &path_list);
    return_if_error(r, "Compute explicit path.");

    context->loadKey.path_list = path_list;
    char *file;
    r = ifapi_path_string(&file, NULL, path_list, NULL);
    goto_if_error(r, "Compute explicit path.", error);

    LOG_DEBUG("Explicit key path: %s", file);

    free(file);

    context->cmd.Key_Create.state = KEY_CREATE_INIT;

    return TSS2_RC_SUCCESS;

error:
    free_string_list(path_list);
    return r;
}

/** State machine for key creation.
 *
 * The function for the preparation of the key have to called before the state machine can
 * be activated. The linked list for the used directories must be available in the
 * FAPI context.
 * It will be checked whether the object already exists in key store and the FAPI context
 * will be initialized appropriate for key creation.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in] template The template which defines the key attributes and whether the
 *            key will be persistent.
 *
 * @retval TSS2_RC_SUCCESS If the key could be generated.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
 *         not covered by other return codes.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
 * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
 *         store.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If an object needed for creation or
           authentication was not found.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
 *         not successful.
 * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for creation
 *         fails.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
 *         is not defined.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
 *         or contains illegal characters.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS if the object already exists in object store.
 */
TSS2_RC
ifapi_key_create(FAPI_CONTEXT *context, IFAPI_KEY_TEMPLATE *template) {
    TSS2_RC              r;
    size_t               path_length;
    NODE_STR_T          *path_list = context->loadKey.path_list;
    TPM2B_PUBLIC        *outPublic = NULL;
    TPM2B_PRIVATE       *outPrivate = NULL;
    TPM2B_CREATION_DATA *creationData = NULL;
    TPM2B_DIGEST        *creationHash = NULL;
    TPMT_TK_CREATION    *creationTicket = NULL;
    IFAPI_OBJECT        *object = &context->cmd.Key_Create.object;
    IFAPI_OBJECT        *hierarchy = &context->cmd.Key_Create.hierarchy;
    ESYS_TR              auth_session;
    uint8_t             *random_data = NULL;

    LOG_TRACE("call");

    switch (context->cmd.Key_Create.state) {
    statecase(context->cmd.Key_Create.state, KEY_CREATE_INIT);
        context->cmd.Key_Create.public_templ = *template;
        context->loadKey.auth_object.public.handle = ESYS_TR_NONE;

        /* Profile name is first element of the explicit path list */
        char *profile_name = context->loadKey.path_list->str;
        r = ifapi_profiles_get(&context->profiles, profile_name, &context->cmd.Key_Create.profile);
        goto_if_error_reset_state(r, "Retrieving profile data", error_cleanup);

        if (context->cmd.Key_Create.inSensitive.sensitive.data.size > 0) {
            /* A keyed hash object sealing sensitive data will be created */
            context->cmd.Key_Create.public_templ.public.publicArea.type = TPM2_ALG_KEYEDHASH;
            context->cmd.Key_Create.public_templ.public.publicArea.nameAlg
                = context->cmd.Key_Create.profile->nameAlg;
            if (context->cmd.Key_Create.public_templ.public.publicArea.objectAttributes
                & TPMA_OBJECT_SIGN_ENCRYPT) {
                TPMS_KEYEDHASH_PARMS *details;
                details = &context->cmd.Key_Create.public_templ.public.publicArea.parameters
                               .keyedHashDetail;
                details->scheme.scheme = TPM2_ALG_HMAC;
                details->scheme.details.hmac.hashAlg = context->cmd.Key_Create.profile->nameAlg;
            } else {
                context->cmd.Key_Create.public_templ.public.publicArea.parameters.keyedHashDetail
                    .scheme.scheme
                    = TPM2_ALG_NULL;
            }
        } else {
            r = ifapi_merge_profile_into_template(context->cmd.Key_Create.profile,
                                                  &context->cmd.Key_Create.public_templ);
            goto_if_error_reset_state(r, "Merge profile", error_cleanup);
        }

        if (context->cmd.Key_Create.policyPath
            && strcmp(context->cmd.Key_Create.policyPath, "") != 0)
            context->cmd.Key_Create.state = KEY_CREATE_CALCULATE_POLICY;
        /* else jump over to KEY_CREATE_WAIT_FOR_SESSION below */
        /* FALLTHRU */

    case KEY_CREATE_CALCULATE_POLICY:
        if (context->cmd.Key_Create.state == KEY_CREATE_CALCULATE_POLICY) {
            r = ifapi_calculate_tree(context, context->cmd.Key_Create.policyPath,
                                     &context->policy.policy,
                                     context->cmd.Key_Create.public_templ.public.publicArea.nameAlg,
                                     &context->policy.digest_idx, &context->policy.hash_size);
            return_try_again(r);
            goto_if_error2(r, "Calculate policy tree %s", error_cleanup,
                           context->cmd.Key_Create.policyPath);

            /* Store the calculated policy in the key object */
            object->policy = calloc(1, sizeof(TPMS_POLICY));
            return_if_null(object->policy, "Out of memory", TSS2_FAPI_RC_MEMORY);
            *(object->policy) = context->policy.policy;

            context->cmd.Key_Create.public_templ.public.publicArea.authPolicy.size
                = context->policy.hash_size;
            memcpy(&context->cmd.Key_Create.public_templ.public.publicArea.authPolicy.buffer[0],
                   &context->policy.policy.policyDigests.digests[context->policy.digest_idx].digest,
                   context->policy.hash_size);
        }
        r = ifapi_get_sessions_async(context, IFAPI_SESSION_GEN_SRK | IFAPI_SESSION1,
                                     TPMA_SESSION_ENCRYPT | TPMA_SESSION_DECRYPT, 0);
        goto_if_error_reset_state(r, "Create sessions", error_cleanup);
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_SESSION);
        LOG_TRACE("KEY_CREATE_WAIT_FOR_SESSION");
        r = ifapi_get_sessions_finish(context, context->cmd.Key_Create.profile,
                                      context->cmd.Key_Create.profile->nameAlg);
        return_try_again(r);
        goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_RANDOM);
        if (context->cmd.Key_Create.gen_sensitive_random) {
            r = ifapi_get_random(context, context->cmd.Key_Create.inSensitive.sensitive.data.size,
                                 &random_data);
            return_try_again(r);
            goto_if_error_reset_state(r, "FAPI GetRandom", error_cleanup);

            /* Copy the sensitive data */
            memcpy(&context->cmd.Key_Create.inSensitive.sensitive.data.buffer, random_data,
                   context->cmd.Key_Create.inSensitive.sensitive.data.size);
            SAFE_FREE(random_data);
        }
        path_length = ifapi_path_length(path_list);
        if (path_length == 0) {
            goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Invalid path list", error_cleanup);
        }
        r = ifapi_load_key_async(context, path_length - 1);
        goto_if_error(r, "LoadKey async", error_cleanup);
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_PARENT);
        LOG_TRACE("KEY_CREATE_WAIT_FOR_PARENT");
        r = ifapi_load_key_finish(context, IFAPI_FLUSH_PARENT);
        return_try_again(r);
        goto_if_error(r, "LoadKey finish", error_cleanup);
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_AUTHORIZATION);
        r = ifapi_authorize_object(context, &context->loadKey.auth_object, &auth_session);
        FAPI_SYNC(r, "Authorize key.", error_cleanup);

        r = Esys_Create_Async(
            context->esys, context->loadKey.auth_object.public.handle, auth_session,
            ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE, &context->cmd.Key_Create.inSensitive,
            &context->cmd.Key_Create.public_templ.public, &context->cmd.Key_Create.outsideInfo,
            &context->cmd.Key_Create.creationPCR);
        goto_if_error(r, "Create_Async", error_cleanup);
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_AUTH_SENT);
        r = Esys_Create_Finish(context->esys, &outPrivate, &outPublic, &creationData, &creationHash,
                               &creationTicket);
        try_again_or_error_goto(r, "Key create finish", error_cleanup);

        context->session2 = ESYS_TR_NONE;

        /* Prepare object for serialization */
        object->system = context->cmd.Key_Create.public_templ.system;
        object->objectType = IFAPI_KEY_OBJ;
        object->misc.key.public = *outPublic;
        object->misc.key.private.size = outPrivate->size;
        object->misc.key.private.buffer = calloc(1, outPrivate->size);
        goto_if_null2(object->misc.key.private.buffer, "Out of memory.", r, TSS2_FAPI_RC_MEMORY,
                      error_cleanup);

        object->misc.key.private.buffer
            = memcpy(&object->misc.key.private.buffer[0], &outPrivate->buffer[0], outPrivate->size);
        object->misc.key.policyInstance = NULL;
        object->misc.key.creationData = *creationData;
        object->misc.key.creationHash = *creationHash;
        object->misc.key.creationTicket = *creationTicket;
        object->misc.key.description = NULL;
        object->misc.key.certificate = NULL;
        SAFE_FREE(creationData);
        SAFE_FREE(creationTicket);
        SAFE_FREE(creationHash);
        /* unique_init is only needed for primary keys. */
        object->misc.key.unique_init_set = TPM2_NO;
        if (context->cmd.Key_Create.inSensitive.sensitive.userAuth.size > 0)
            object->misc.key.with_auth = TPM2_YES;
        else
            object->misc.key.with_auth = TPM2_NO;
        ;
        r = ifapi_get_name(&outPublic->publicArea, &object->misc.key.name);
        goto_if_error(r, "Get key name", error_cleanup);

        SAFE_FREE(outPrivate);
        SAFE_FREE(outPublic);

        if (object->misc.key.public.publicArea.type == TPM2_ALG_RSA)
            object->misc.key.signing_scheme = context->cmd.Key_Create.profile->rsa_signing_scheme;
        else
            object->misc.key.signing_scheme = context->cmd.Key_Create.profile->ecc_signing_scheme;

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_LOAD_AUTHORIZATION);
        if (template->persistent_handle) {
            r = ifapi_authorize_object(context, &context->loadKey.auth_object, &auth_session);
            FAPI_SYNC(r, "Authorize key.", error_cleanup);

            TPM2B_PRIVATE private;
            private.size = object->misc.key.private.size;
            memcpy(&private.buffer[0], &object->misc.key.private.buffer[0], private.size);

            r = Esys_Load_Async(context->esys, context->loadKey.handle, auth_session,
                                ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE, &private,
                                &object->misc.key.public);
            goto_if_error(r, "Load key.", error_cleanup);
        }
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_KEY);
        if (template->persistent_handle) {
            r = Esys_Load_Finish(context->esys, &context->loadKey.handle);
            return_try_again(r);
            goto_if_error_reset_state(r, "Load", error_cleanup);
        }
        context->session2 = ESYS_TR_NONE;
        /* Prepare Flushing of key used for authorization */
        if (!context->loadKey.auth_object.misc.key.persistent_handle) {
            r = Esys_FlushContext_Async(context->esys, context->loadKey.auth_object.public.handle);
            goto_if_error(r, "Flush parent", error_cleanup);
        }
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_FLUSH1);
        if (!context->loadKey.auth_object.misc.key.persistent_handle) {
            r = Esys_FlushContext_Finish(context->esys);
            try_again_or_error_goto(r, "Flush context", error_cleanup);

            ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
        }
        if (template->persistent_handle) {
            r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HS");
            return_if_error2(r, "Could not open hierarchy /HS");
        }
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_HIERARCHY);
        if (template->persistent_handle) {
            r = ifapi_keystore_load_finish(&context->keystore, &context->io, hierarchy);
            return_try_again(r);
            return_if_error(r, "read_finish failed");
            r = ifapi_initialize_object(context->esys, hierarchy);
            goto_if_error_reset_state(r, "Initialize hierarchy object", error_cleanup);

            hierarchy->public.handle = ESYS_TR_RH_OWNER;
        }
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_AUTHORIZE_HIERARCHY);
        if (template->persistent_handle) {
            r = ifapi_authorize_object(context, hierarchy, &auth_session);
            FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);

            object->misc.key.persistent_handle = template->persistent_handle;

            /* Prepare making the loaded key permanent. */
            r = Esys_EvictControl_Async(context->esys, hierarchy->public.handle,
                                        context->loadKey.handle, auth_session, ESYS_TR_NONE,
                                        ESYS_TR_NONE, object->misc.key.persistent_handle);
            goto_if_error(r, "Error Esys EvictControl", error_cleanup);

            ifapi_cleanup_ifapi_object(hierarchy);
        }
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_EVICT_CONTROL);
        if (template->persistent_handle) {
            /* Prepare making the loaded key permanent. */
            r = Esys_EvictControl_Finish(context->esys, &object->public.handle);
            return_try_again(r);
            goto_if_error(r, "Evict control failed", error_cleanup);
        }

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_FLUSH2);
        /* Flush the key which was evicted. */
        if (template->persistent_handle) {
            r = ifapi_flush_object(context, context->loadKey.handle);
            return_try_again(r);
            goto_if_error(r, "Flush key", error_cleanup);

            context->loadKey.handle = ESYS_TR_NONE;
        }

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WRITE_PREPARE);
        if (template->persistent_handle) {
            /* Compute the serialization, which will be used for the
               reconstruction of the key object. */
            SAFE_FREE(object->misc.key.serialization.buffer);
            r = Esys_TR_Serialize(context->esys, object->public.handle,
                                  &object->misc.key.serialization.buffer,
                                  &object->misc.key.serialization.size);
            goto_if_error(r, "Serialize object", error_cleanup);
        }

        /* Perform esys serialization if necessary */
        r = ifapi_esys_serialize_object(context->esys, object);
        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,
                                                 context->cmd.Key_Create.keyPath, object);
        goto_if_error_reset_state(r, "Could not write: %s", error_cleanup,
                                  context->cmd.Key_Create.keyPath);

        /* Start writing the object to the key store */
        r = ifapi_keystore_store_async(&context->keystore, &context->io,
                                       context->cmd.Key_Create.keyPath, object);
        goto_if_error_reset_state(r, "Could not open: %s", error_cleanup,
                                  context->cmd.Key_Create.keyPath);
        ifapi_cleanup_ifapi_object(object);
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_WRITE);
        /* Finish writing the key to the key store */
        r = ifapi_keystore_store_finish(&context->io);
        return_try_again(r);
        return_if_error_reset_state(r, "write_finish failed");

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_CLEANUP);
        r = ifapi_cleanup_session(context);
        try_again_or_error_goto(r, "Cleanup", error_cleanup);

        context->cmd.Key_Create.state = KEY_CREATE_INIT;
        r = TSS2_RC_SUCCESS;
        break;

    statecasedefault(context->cmd.Key_Create.state);
    }

cleanup:
    free_string_list(context->loadKey.path_list);
    SAFE_FREE(outPublic);
    SAFE_FREE(outPrivate);
    SAFE_FREE(creationData);
    SAFE_FREE(creationHash);
    SAFE_FREE(creationTicket);
    SAFE_FREE(context->cmd.Key_Create.policyPath);
    SAFE_FREE(context->cmd.Key_Create.keyPath);
    SAFE_FREE(random_data);
    ifapi_cleanup_ifapi_object(object);
    ifapi_session_clean(context);
    if (template->persistent_handle)
        ifapi_cleanup_ifapi_object(hierarchy);
    return r;

error_cleanup:
    if (template->persistent_handle)
        ifapi_cleanup_ifapi_object(hierarchy);
    if (context->loadKey.auth_object.public.handle != ESYS_TR_NONE
        && !context->loadKey.auth_object.misc.key.persistent_handle) {
        Esys_FlushContext(context->esys, context->loadKey.auth_object.public.handle);
    }
    goto cleanup;
}

/** Get signature scheme for key.
 *
 * If padding is passed the scheme will be derived from paddint otherwise
 * the scheme form object will be used.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] object The internal FAPI object of the key.
 * @param[in] padding The strings RSA_SSA or RSA_PSS will be converted
 *            into the TSS constants used for the signing scheme.
 * @param[in] digest The digest size will be used to determine the hashalg
 *            for the signature scheme.
 * @param[out] sig_scheme The computed signature scheme.
 *
 * @retval TSS2_FAPI_RC_BAD_VALUE If the digest size is not appropriate.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 */
TSS2_RC
ifapi_get_sig_scheme(FAPI_CONTEXT    *context,
                     IFAPI_OBJECT    *object,
                     char const      *padding,
                     TPM2B_DIGEST    *digest,
                     TPMT_SIG_SCHEME *sig_scheme) {
    TPMI_ALG_HASH hash_alg;
    TSS2_RC       r;

    /* Get hash algorithm from digest size */
    r = ifapi_get_hash_alg_for_size(digest->size, &hash_alg);
    return_if_error2(r, "Invalid digest size");

    if (digest->size == TPM2_SM3_256_DIGEST_SIZE
        && object->misc.key.signing_scheme.details.any.hashAlg == TPM2_ALG_SM3_256) {
        hash_alg = TPM2_ALG_SM3_256;
    }

    if (padding) {
        /* Use scheme object from context */
        if (strcasecmp("RSA_SSA", padding) == 0) {
            context->Key_Sign.scheme.scheme = TPM2_ALG_RSASSA;
            context->Key_Sign.scheme.details.rsassa.hashAlg = hash_alg;
        }
        if (strcasecmp("RSA_PSS", padding) == 0) {
            context->Key_Sign.scheme.scheme = TPM2_ALG_RSAPSS;
            context->Key_Sign.scheme.details.rsapss.hashAlg = hash_alg;
        }
        *sig_scheme = context->Key_Sign.scheme;
        return TSS2_RC_SUCCESS;
    } else {
        /* Use scheme defined for object */
        *sig_scheme = object->misc.key.signing_scheme;
        sig_scheme->details.any.hashAlg = hash_alg;
        return TSS2_RC_SUCCESS;
    }
}

/** State machine for changing the hierarchy authorization.
 *
 * First it will be tried to set the auth value of the hierarchy with a
 * "null" authorization. If this trial is not successful it will be tried to
 * authorize the hierarchy via a callback.
 * If an not null auth value is passed with_auth is set to yes for the
 * object otherwise to no. So for later authorizations it will be clear
 * whether null authorization is possible or not.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] handle The ESAPI handle of the hierarchy.
 * @param[in,out] hierarchy_object The internal FAPI representation of a
 *                hierarchy.
 * @param[in] newAuthValue The new authorization for the hierarchy.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
 *         is not set.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occured.
 * @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_FAPI_RC_IO_ERROR if an error occured while accessing the
 *         object store.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
 *         was not successful.
 */
TSS2_RC
ifapi_change_auth_hierarchy(FAPI_CONTEXT *context,
                            ESYS_TR       handle,
                            IFAPI_OBJECT *hierarchy_object,
                            TPM2B_AUTH   *newAuthValue) {
    TSS2_RC r;
    ESYS_TR auth_session;

    switch (context->hierarchy_state) {
    statecase(context->hierarchy_state, HIERARCHY_CHANGE_AUTH_INIT);
        if (hierarchy_object->misc.hierarchy.with_auth == TPM2_YES
            || policy_digest_size(hierarchy_object)) {
            r = ifapi_authorize_object(context, hierarchy_object, &auth_session);
            FAPI_SYNC(r, "Authorize hierarchy.", error);
        } else {
            auth_session = context->session1;
        }

        r = Esys_HierarchyChangeAuth_Async(
            context->esys, handle,
            (auth_session && auth_session != ESYS_TR_NONE) ? auth_session : ESYS_TR_PASSWORD,
            ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE, newAuthValue);
        return_if_error(r, "HierarchyChangeAuth");
        fallthrough;

    statecase(context->hierarchy_state, HIERARCHY_CHANGE_AUTH_NULL_AUTH_SENT);
        r = Esys_HierarchyChangeAuth_Finish(context->esys);
        return_try_again(r);

        if (number_rc(r) == TPM2_RC_BAD_AUTH
            && hierarchy_object->misc.hierarchy.with_auth == TPM2_NO) {

            /* Retry after NULL authorization was not successful */
            char *description;
            r = ifapi_get_description(hierarchy_object, &description);
            return_if_error(r, "Get description");

            r = ifapi_set_auth(context, hierarchy_object, description);
            SAFE_FREE(description);
            return_if_error(r, "HierarchyChangeAuth");

            r = Esys_HierarchyChangeAuth_Async(
                context->esys, handle,
                (context->session1 && context->session1 != ESYS_TR_NONE) ? context->session1
                                                                         : ESYS_TR_PASSWORD,
                ESYS_TR_NONE, ESYS_TR_NONE, newAuthValue);
            return_if_error(r, "HierarchyChangeAuth");
            return TSS2_FAPI_RC_TRY_AGAIN;
        }
        return_if_error(r, "HierarchyChangeAuth");

        if (newAuthValue->size > 0)
            hierarchy_object->misc.hierarchy.with_auth = TPM2_YES;
        else
            hierarchy_object->misc.hierarchy.with_auth = TPM2_NO;

        context->hierarchy_state = HIERARCHY_CHANGE_AUTH_INIT;
        return r;

    statecasedefault(context->hierarchy_state);
    }
error:
    context->hierarchy_state = HIERARCHY_CHANGE_AUTH_INIT;
    return r;
}

/** State machine for changing the policy of a hierarchy.
 *
 * Based on a passed policy the policy digest will be computed.
 * First it will be tried to set the policy of the hierarchy with a
 * "null" authorization. If this trial is not successful it will be tried to
 * authorize the hierarchy via a callback.
 * If an not null auth value is passed with_auth is set to yes for the
 * object otherwise to no. So for later authorizations it will be clear
 * whether null authorization is possible or not.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] handle The ESAPI handle of the hierarchy.
 * @param[in,out] hierarchy_object The internal FAPI representation of a
 *                hierarchy.
 * @param[in] policy The new policy assigned to the hierarchy.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
 *         not covered by other return codes.
 * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during policy calculation.
 * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
 *         store.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If an object needed for policy calculation was
 *         not found.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
 *         not successful.
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @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_NOT_PROVISIONED FAPI was not provisioned.
 * @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
 *         or contains illegal characters.
 */
TSS2_RC
ifapi_change_policy_hierarchy(FAPI_CONTEXT *context,
                              ESYS_TR       handle,
                              IFAPI_OBJECT *hierarchy_object,
                              TPMS_POLICY  *policy) {
    TSS2_RC r;
    ESYS_TR auth_session;

    switch (context->hierarchy_policy_state) {
    statecase(context->hierarchy_policy_state, HIERARCHY_CHANGE_POLICY_INIT);
        if ((!policy || !policy->policy) && !hierarchy_object->policy) {
            /* No policy will be used for hierarchy */
            return TSS2_RC_SUCCESS;
        }
        fallthrough;

    statecase(context->hierarchy_policy_state, HIERARCHY_CHANGE_POLICY_AUTHORIZE);
        if (hierarchy_object->misc.hierarchy.with_auth == TPM2_YES
            || policy_digest_size(hierarchy_object)) {
            r = ifapi_authorize_object(context, hierarchy_object, &auth_session);
            FAPI_SYNC(r, "Authorize hierarchy.", error);
        } else {
            auth_session = context->session1;
        }

        if (policy) {
            context->policy.state = POLICY_INIT;

            /* Calculate the policy digest which will be used as hierarchy policy. */
            r = ifapi_calculate_tree(context, NULL, /**< no path needed */
                                     policy, context->profiles.default_profile.nameAlg,
                                     &context->cmd.Provision.digest_idx,
                                     &context->cmd.Provision.hash_size);
            goto_if_error(r, "Policy calculation", error);

            /* Policy data will be stored in the provisioning context. */
            context->cmd.Provision.policy_digest.size = context->cmd.Provision.hash_size;
            memcpy(&context->cmd.Provision.policy_digest.buffer[0],
                   &policy->policyDigests.digests[context->cmd.Provision.digest_idx].digest,
                   context->cmd.Provision.hash_size);

            hierarchy_object->policy = policy;
            hierarchy_object->misc.hierarchy.authPolicy = context->cmd.Provision.policy_digest;
        } else {
            /* No policy will be used for this hierarchy. */
            context->cmd.Provision.policy_digest.size = 0;
            ifapi_cleanup_policy(hierarchy_object->policy);
            SAFE_FREE(hierarchy_object->policy);
            hierarchy_object->policy = NULL;
            hierarchy_object->misc.hierarchy.with_auth = TPM2_NO;
            hierarchy_object->misc.hierarchy.authPolicy.size = 0;
        }

        /* Prepare the setting of the policy. */
        r = Esys_SetPrimaryPolicy_Async(
            context->esys, handle,
            (auth_session && auth_session != ESYS_TR_NONE) ? auth_session : ESYS_TR_PASSWORD,
            ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE,
            &context->cmd.Provision.policy_digest,
            context->cmd.Provision.policy_digest.size ? context->profiles.default_profile.nameAlg
                                                      : TPM2_ALG_NULL);
        return_if_error(r, "Esys_SetPrimaryPolicy_Async");
        fallthrough;

    statecase(context->hierarchy_policy_state, HIERARCHY_CHANGE_POLICY_NULL_AUTH_SENT);
        r = Esys_SetPrimaryPolicy_Finish(context->esys);
        return_try_again(r);
        if (number_rc(r) == TPM2_RC_BAD_AUTH
            && hierarchy_object->misc.hierarchy.with_auth == TPM2_NO) {
            /* Retry after NULL authorization was not successful */
            char *description;
            r = ifapi_get_description(hierarchy_object, &description);
            return_if_error(r, "Get description");

            r = ifapi_set_auth(context, hierarchy_object, description);
            SAFE_FREE(description);
            return_if_error(r, "HierarchyChangePolicy");

            r = Esys_SetPrimaryPolicy_Async(
                context->esys, handle,
                (context->session1 && context->session1 != ESYS_TR_NONE) ? context->session1
                                                                         : ESYS_TR_PASSWORD,
                ESYS_TR_NONE, ESYS_TR_NONE, &context->cmd.Provision.policy_digest,
                context->profiles.default_profile.nameAlg);
            return_if_error(r, "Esys_SetPrimaryPolicy_Async");
            return TSS2_FAPI_RC_TRY_AGAIN;
        }
        return_if_error(r, "Set primary policy");
        break;

    statecasedefault(context->hierarchy_policy_state);
    }

error:
    return r;
}

/** Allocate ifapi object and store the result in a linked list.
 *
 * Allocated ifapi objects will be recorded in the context.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 *
 * @retval The allocated ifapi object.
 * @retval NULL if the object cannot be allocated.
 */
IFAPI_OBJECT
*ifapi_allocate_object(FAPI_CONTEXT *context) {
    NODE_OBJECT_T *node = calloc(1, sizeof(NODE_OBJECT_T));
    if (!node)
        return NULL;

    node->object = calloc(1, sizeof(IFAPI_OBJECT));
    if (!node->object) {
        free(node);
        return NULL;
    }
    node->next = context->object_list;
    context->object_list = node;
    return (IFAPI_OBJECT *)node->object;
}

/** Free all ifapi objects stored in the context.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 */
void
ifapi_free_objects(FAPI_CONTEXT *context) {
    NODE_OBJECT_T *free_node;
    NODE_OBJECT_T *node = context->object_list;
    while (node) {
        free(node->object);
        free_node = node;
        node = node->next;
        free(free_node);
    }
}

// NOLINTBEGIN(bugprone-macro-parentheses)
#define ADD_CAPABILITY_INFO(capability, field, subfield, max_count, property_count)                \
    if (context->cmd.GetInfo.fetched_data->data.capability.count                                   \
        > (max_count) - (property_count)) {                                                        \
        context->cmd.GetInfo.fetched_data->data.capability.count = (max_count) - (property_count); \
    }                                                                                              \
                                                                                                   \
    memmove(&context->cmd.GetInfo.capability_data->data.capability.field[property_count],          \
            context->cmd.GetInfo.fetched_data->data.capability.field,                              \
            context->cmd.GetInfo.fetched_data->data.capability.count                               \
                * sizeof(context->cmd.GetInfo.fetched_data->data.capability.field[0]));            \
    (property_count) += context->cmd.GetInfo.fetched_data->data.capability.count;                  \
                                                                                                   \
    context->cmd.GetInfo.capability_data->data.capability.count = property_count;                  \
                                                                                                   \
    if (more_data && (property_count) < count                                                      \
        && context->cmd.GetInfo.fetched_data->data.capability.count) {                             \
        context->cmd.GetInfo.property = context->cmd.GetInfo.capability_data->data.capability      \
                                            .field[(property_count) - 1] subfield                  \
                                        + 1;                                                       \
    } else {                                                                                       \
        more_data = false;                                                                         \
    }
// NOLINTEND(bugprone-macro-parentheses)

/** Prepare the receiving of capability data.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 *
 * @retval TSS2_RC_SUCCESS.
 */
TPM2_RC
ifapi_capability_init(FAPI_CONTEXT *context) {
    context->cmd.GetInfo.capability_data = NULL;
    context->cmd.GetInfo.fetched_data = NULL;

    return TSS2_RC_SUCCESS;
}

/** State machine for receiving TPM capability information.
 *
 * The state machine shares the state with the FAPI function Fapi_GetInfo.
 * context->state == GET_INFO_GET_CAP_MORE signals that more capability data can
 * be retrieved.
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     capability The capability to be retrieved.
 * @param[in]     count The maximal number of items that should be retrieved.
 * @param[out]    capability_data The retrieved capability information.
 *
 * @retval TSS2_RC_SUCCESS If all capability data is retrieved.
 * @retval TSS2_FAPI_RC_TRY_AGAIN if more capability data is available.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 */
TPM2_RC
ifapi_capability_get(FAPI_CONTEXT          *context,
                     TPM2_CAP               capability,
                     UINT32                 count,
                     TPMS_CAPABILITY_DATA **capability_data) {

    TPMI_YES_NO   more_data;
    TSS2_RC       r = TSS2_RC_SUCCESS;
    ESYS_CONTEXT *ectx = context->esys;

    LOG_TRACE("Current state is: %i", context->state);

    switch (context->state) {
    statecase(context->state, GET_INFO_GET_CAP);
        /* fetch capability info */
        context->cmd.GetInfo.fetched_data = NULL;
        context->cmd.GetInfo.capability_data = NULL;
        fallthrough;

    statecase(context->state, GET_INFO_GET_CAP_MORE);
        r = Esys_GetCapability_Async(ectx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, capability,
                                     context->cmd.GetInfo.property,
                                     count - context->cmd.GetInfo.property_count);
        goto_if_error(r, "Error GetCapability", error_cleanup);
        fallthrough;

    statecase(context->state, GET_INFO_WAIT_FOR_CAP);
        r = Esys_GetCapability_Finish(ectx, &more_data, &context->cmd.GetInfo.fetched_data);
        return_try_again(r);
        goto_if_error(r, "Error GetCapability", error_cleanup);

        LOG_TRACE("GetCapability: capability: 0x%x, property: 0x%x", capability,
                  context->cmd.GetInfo.property);

        if (context->cmd.GetInfo.fetched_data->capability != capability) {
            goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
                       "TPM returned different capability than requested: 0x%x != 0x%x",
                       error_cleanup, context->cmd.GetInfo.fetched_data->capability, capability);
        }

        if (context->cmd.GetInfo.capability_data == NULL) {
            /* reuse the TPM's result structure */
            context->cmd.GetInfo.capability_data = context->cmd.GetInfo.fetched_data;

            if (!more_data) {
                /* there won't be another iteration of the loop, just return the result unmodified
                 */
                *capability_data = context->cmd.GetInfo.capability_data;
                return TPM2_RC_SUCCESS;
            }
        }

        /* append the TPM's results to the initial structure, as long as there is still space left
         */
        switch (capability) {
        case TPM2_CAP_ALGS:
            ADD_CAPABILITY_INFO(algorithms, algProperties, .alg, TPM2_MAX_CAP_ALGS,
                                context->cmd.GetInfo.property_count);
            break;
        case TPM2_CAP_HANDLES:
            ADD_CAPABILITY_INFO(handles, handle, , TPM2_MAX_CAP_HANDLES,
                                context->cmd.GetInfo.property_count);
            break;
        case TPM2_CAP_COMMANDS:
            ADD_CAPABILITY_INFO(command, commandAttributes, , TPM2_MAX_CAP_CC,
                                context->cmd.GetInfo.property_count);
            /* workaround because tpm2-tss does not implement attribute commandIndex for TPMA_CC */
            context->cmd.GetInfo.property &= TPMA_CC_COMMANDINDEX_MASK;
            break;
        case TPM2_CAP_PP_COMMANDS:
            ADD_CAPABILITY_INFO(ppCommands, commandCodes, , TPM2_MAX_CAP_CC,
                                context->cmd.GetInfo.property_count);
            break;
        case TPM2_CAP_AUDIT_COMMANDS:
            ADD_CAPABILITY_INFO(auditCommands, commandCodes, , TPM2_MAX_CAP_CC,
                                context->cmd.GetInfo.property_count);
            break;
        case TPM2_CAP_PCRS:
            ADD_CAPABILITY_INFO(assignedPCR, pcrSelections, .hash, TPM2_NUM_PCR_BANKS,
                                context->cmd.GetInfo.property_count);
            break;
        case TPM2_CAP_TPM_PROPERTIES:
            ADD_CAPABILITY_INFO(tpmProperties, tpmProperty, .property, TPM2_MAX_TPM_PROPERTIES,
                                context->cmd.GetInfo.property_count);
            break;
        case TPM2_CAP_PCR_PROPERTIES:
            ADD_CAPABILITY_INFO(pcrProperties, pcrProperty, .tag, TPM2_MAX_PCR_PROPERTIES,
                                context->cmd.GetInfo.property_count);
            break;
        case TPM2_CAP_ECC_CURVES:
            ADD_CAPABILITY_INFO(eccCurves, eccCurves, , TPM2_MAX_ECC_CURVES,
                                context->cmd.GetInfo.property_count);
            break;
        case TPM2_CAP_VENDOR_PROPERTY:
            /* We will skip over VENDOR_PROPERTY capabilities on FAPI level */
            break;
        default:
            LOG_ERROR("Unsupported capability: 0x%x\n", capability);
            if (context->cmd.GetInfo.fetched_data != context->cmd.GetInfo.capability_data) {
                free(context->cmd.GetInfo.fetched_data);
            }
            free(context->cmd.GetInfo.capability_data);
            *capability_data = NULL;
            return TSS2_FAPI_RC_BAD_VALUE;
        }

        if (context->cmd.GetInfo.fetched_data != context->cmd.GetInfo.capability_data) {
            free(context->cmd.GetInfo.fetched_data);
        }
        *capability_data = context->cmd.GetInfo.capability_data;
        break;

    statecasedefault(context->state);
    }
    if (more_data) {
        context->state = GET_INFO_GET_CAP_MORE;
        return TSS2_FAPI_RC_TRY_AGAIN;
    } else {
        context->state = FAPI_STATE_INIT;
        return TSS2_RC_SUCCESS;
    }

error_cleanup:
    context->state = FAPI_STATE_INIT;
    SAFE_FREE(context->cmd.GetInfo.capability_data);
    SAFE_FREE(context->cmd.GetInfo.fetched_data);
    return r;
}

/** Get certificates stored in NV ram.
 *
 * The NV handles in the certificate range are determined. The corresponding
 * certificates are appended to an unit8_t array.
 *
 * @param[in,out] context The FAPI_CONTEXT. The sub context for NV reading
 *                will be used.
 * @param[in] min_handle The first possible handle in the handle range.
 * @param[in] max_handle Maximal handle to filter out the handles not in the
 *            handle range for certificates.
 * @param[out] certs The byte array of the concatenated certificates
 * @param[out] cert_list_size The size of the cert array.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
 *
 * @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_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @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_IO_ERROR if an error occurred while accessing the
 *         object store.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
 *         was not successful.
 * @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @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.
 */
TSS2_RC
ifapi_get_certificates(FAPI_CONTEXT *context,
                       UINT32        min_handle,
                       UINT32        max_handle,
                       uint8_t     **certs,
                       size_t       *cert_list_size) {
    TSS2_RC     r;
    TPMI_YES_NO moreData;
    uint8_t    *cert_data = NULL;
    size_t      cert_size;
    bool        provisioned;

    context->cmd.Provision.cert_nv_idx = MIN_EK_CERT_HANDLE;

    switch (context->get_cert_state) {
    statecase(context->get_cert_state, GET_CERT_INIT);
        context->cmd.Provision.certs = NULL;
        context->cmd.Provision.cert_list_size = 0;
        context->cmd.Provision.capabilityData = NULL;
        context->cmd.Provision.cert_idx = 0;
        /* Prepare the reading of the capability handles in the certificate range */
        r = Esys_GetCapability_Async(context->esys, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                     TPM2_CAP_HANDLES, min_handle, TPM2_MAX_CAP_HANDLES);
        goto_if_error(r, "Esys_GetCapability_Async", error);
        fallthrough;

    statecase(context->get_cert_state, GET_CERT_WAIT_FOR_GET_CAP);
        r = Esys_GetCapability_Finish(context->esys, &moreData,
                                      &context->cmd.Provision.capabilityData);
        try_again_or_error(r, "GetCapablity_Finish");

        if (!context->cmd.Provision.capabilityData
            || context->cmd.Provision.capabilityData->data.handles.count == 0) {
            *certs = NULL;
            *cert_list_size = 0;
            SAFE_FREE(context->cmd.Provision.capabilityData);
            return TSS2_RC_SUCCESS;
        }
        context->cmd.Provision.cert_count
            = context->cmd.Provision.capabilityData->data.handles.count;

        /* Filter out NV handles beyond the EK cert range */
        for (size_t i = 0; i < context->cmd.Provision.cert_count; i++) {
            if (context->cmd.Provision.capabilityData->data.handles.handle[i] > max_handle) {
                context->cmd.Provision.cert_count = i;
                break;
            }
        }
        fallthrough;

    statecase(context->get_cert_state, GET_CERT_GET_CERT_NV);
        goto_if_null(context->cmd.Provision.capabilityData, "capabilityData is null",
                     TSS2_FAPI_RC_MEMORY, error);
        context->cmd.Provision.cert_nv_idx = context->cmd.Provision.capabilityData->data.handles
                                                 .handle[context->cmd.Provision.cert_idx];

        ifapi_init_hierarchy_object(&context->nv_cmd.auth_object, TPM2_RH_OWNER);

        r = Esys_TR_FromTPMPublic_Async(context->esys, context->cmd.Provision.cert_nv_idx,
                                        ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE);
        goto_if_error_reset_state(r, "Esys_TR_FromTPMPublic_Async", error);
        fallthrough;

    statecase(context->get_cert_state, GET_CERT_GET_CERT_NV_FINISH);
        r = Esys_TR_FromTPMPublic_Finish(context->esys,
                                         &context->cmd.Provision.esys_nv_cert_handle);
        try_again_or_error_goto(r, "TR_FromTPMPublic_Finish", error);

        /* Read public to get size of certificate */
        r = Esys_NV_ReadPublic_Async(context->esys, context->cmd.Provision.esys_nv_cert_handle,
                                     ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE);
        goto_if_error_reset_state(r, "Esys_NV_ReadPublic_Async", error);
        fallthrough;

    statecase(context->get_cert_state, GET_CERT_GET_CERT_READ_PUBLIC);
        r = Esys_NV_ReadPublic_Finish(context->esys, &context->cmd.Provision.nvPublic, NULL);
        try_again_or_error_goto(r, "Error: nv read public", error);

        /* TPMA_NV_NO_DA is set for NV certificate */
        context->nv_cmd.nv_object.misc.nv.public.nvPublic.attributes = TPMA_NV_NO_DA;

        r = ifapi_check_provisioned(&context->keystore, "/HS", &provisioned);
        goto_if_error_reset_state(r, "Provisioning check failed", error);

        if (provisioned) {
            /* Use information from keystore if available. */
            r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HS");
            goto_if_error_reset_state(r, "Could not open hierarchy /HS", error);
        }

        fallthrough;

    statecase(context->get_cert_state, GET_CERT_GET_CERT_READ_HIERARCHY);
        r = ifapi_check_provisioned(&context->keystore, "/HS", &provisioned);
        goto_if_error_reset_state(r, "Provisioning check failed", error);

        if (provisioned) {
            /* Use information from keystore if available. */
            r = ifapi_keystore_load_finish(&context->keystore, &context->io,
                                           &context->nv_cmd.auth_object);
            try_again_or_error_goto(r, "read_finish failed", error);

            r = ifapi_initialize_object(context->esys, &context->nv_cmd.auth_object);
            goto_if_error_reset_state(r, "Initialize hierarchy object", error);
        }

        /* Prepare context for nv read */

        context->nv_cmd.auth_object.public.handle = ESYS_TR_RH_OWNER;
        context->nv_cmd.data_idx = 0;
        context->nv_cmd.auth_index = ESYS_TR_RH_OWNER;
        context->nv_cmd.numBytes = context->cmd.Provision.nvPublic->nvPublic.dataSize;
        context->nv_cmd.esys_handle = context->cmd.Provision.esys_nv_cert_handle;
        context->nv_cmd.offset = 0;
        context->session1 = ESYS_TR_PASSWORD;
        context->session2 = ESYS_TR_NONE;
        context->nv_cmd.nv_read_state = NV_READ_INIT;
        memset(&context->nv_cmd.nv_object, 0, sizeof(IFAPI_OBJECT));
        SAFE_FREE(context->cmd.Provision.nvPublic);
        fallthrough;

    statecase(context->get_cert_state, GET_CERT_READ_CERT);
        r = ifapi_nv_read(context, &cert_data, &cert_size);
        try_again_or_error_goto(r, " FAPI NV_Read", error);

        goto_if_null(cert_data, "Failed to read certificate", TSS2_FAPI_RC_GENERAL_FAILURE, error);

        context->cmd.Provision.cert_idx += 1;

        /* Append cert */

        if (context->cmd.Provision.certs) {
            uint8_t *new_certs = realloc(context->cmd.Provision.certs,
                                         context->cmd.Provision.cert_list_size + cert_size);
            goto_if_null(new_certs, "Out of memory", TSS2_FAPI_RC_MEMORY, error);
            memcpy(new_certs + context->cmd.Provision.cert_list_size, cert_data, cert_size);
            free(cert_data);
            context->cmd.Provision.certs = new_certs;
            context->cmd.Provision.cert_list_size += cert_size;
        } else {
            context->cmd.Provision.certs = cert_data;
            context->cmd.Provision.cert_list_size = cert_size;
        }
        ifapi_cleanup_ifapi_object(&context->nv_cmd.auth_object);

        if (context->cmd.Provision.cert_idx == context->cmd.Provision.cert_count) {
            /* All certificates have been read */
            SAFE_FREE(context->cmd.Provision.capabilityData);
            *certs = context->cmd.Provision.certs;
            *cert_list_size = context->cmd.Provision.cert_list_size;
            return TSS2_RC_SUCCESS;
        } else {
            context->get_cert_state = GET_CERT_GET_CERT_NV;
            return TSS2_FAPI_RC_TRY_AGAIN;
        }
        break;

    statecasedefault(context->get_cert_state);
    }

error:
    SAFE_FREE(context->cmd.Provision.nvPublic);
    SAFE_FREE(context->cmd.Provision.capabilityData);
    ifapi_cleanup_ifapi_object(&context->nv_cmd.auth_object);
    if (r) {
        SAFE_FREE(context->cmd.Provision.certs);
    }
    return r;
}

/** Get description of an internal FAPI object.
 *
 * @param[in] object The object with the description.
 * @param[out] description The callee allocated description.
 *
 * @retval TSS2_RC_SUCCESS If a copy of the description can be returned
 *         or if no description exists.
 * @retval TSS2_FAPI_RC_MEMORY in the copy cannot be allocated.
 */
TSS2_RC
ifapi_get_description(IFAPI_OBJECT *object, char **description) {
    char *obj_description = NULL;

    switch (object->objectType) {
    case IFAPI_KEY_OBJ:
        obj_description = object->misc.key.description;
        break;
    case IFAPI_NV_OBJ:
        obj_description = object->misc.nv.description;
        break;
    case IFAPI_HIERARCHY_OBJ:
        if (object->misc.hierarchy.description)
            obj_description = object->misc.hierarchy.description;
        else if (object->public.handle == ESYS_TR_RH_OWNER)
            obj_description = "Owner Hierarchy";
        else if (object->public.handle == ESYS_TR_RH_ENDORSEMENT)
            obj_description = "Endorsement Hierarchy";
        else if (object->public.handle == ESYS_TR_RH_LOCKOUT)
            obj_description = "Lockout Hierarchy";
        else if (object->public.handle == ESYS_TR_RH_NULL)
            obj_description = "Null Hierarchy";
        else
            obj_description = "Hierarchy";
        break;
    default:
        *description = strdup("");
        check_oom(*description);
        return TSS2_RC_SUCCESS;
    }
    if (obj_description) {
        *description = strdup(obj_description);
        check_oom(*description);
    } else {
        *description = strdup("");
        check_oom(*description);
    }
    return TSS2_RC_SUCCESS;
}

/** Set description of an internal FAPI object.
 *
 * @param[in,out] object The object with the description.
 * @param[in] description The description char strint or NULL.
 */
void
ifapi_set_description(IFAPI_OBJECT *object, char *description) {
    switch (object->objectType) {
    case IFAPI_KEY_OBJ:
        SAFE_FREE(object->misc.key.description);
        object->misc.key.description = description;
        break;
    case IFAPI_NV_OBJ:
        SAFE_FREE(object->misc.nv.description);
        object->misc.nv.description = description;
        break;
    case IFAPI_HIERARCHY_OBJ:
        SAFE_FREE(object->misc.hierarchy.description);
        object->misc.hierarchy.description = description;
        break;
    default:
        LOG_WARNING("Description can't be set");
        break;
    }
}

/** Determine key properties (primary, null hierarchy).
 *
 * It will be checked whether a path is the path of a primary key,
 * and whether it's a key in null hiearchy
 *
 * @param[in,out] context The FAPI_CONTEXT.
 * @param[in]     key_path the key path.
 * @param[out]    is_primary if key path is the path of a primary.
 * @param[out]    in_null_hierarchy if key is a null hierarchy key.
 *
 * @retval TSS2_RC_SUCCESS If the preparation is successful.
 * @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
 * @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
ifapi_get_key_properties(FAPI_CONTEXT *context,
                         char const   *key_path,
                         bool         *is_primary,
                         bool         *in_null_hierarchy) {
    TSS2_RC     r;
    NODE_STR_T *path_list = NULL;
    size_t      path_length;
    const char *hierarchy;

    LOG_TRACE("Check primary: %s", key_path);

    *is_primary = false;
    *in_null_hierarchy = false;

    r = get_explicit_key_path(&context->keystore, key_path, &path_list);
    return_if_error(r, "Compute explicit path.");

    path_length = ifapi_path_length(path_list);

    if (path_length == 3) {
        if (strncmp("P_", path_list->str, 2) == 0) {
            hierarchy = path_list->next->str;
            if (strcmp(hierarchy, "HS") == 0 || strcmp(hierarchy, "HE") == 0
                || strcmp(hierarchy, "HN") == 0) {
                *is_primary = true;
            }
        }
    }
    if (path_length >= 3) {
        hierarchy = path_list->next->str;
        if (strcmp(hierarchy, "HN") == 0)
            *in_null_hierarchy = true;
    }
    free_string_list(path_list);
    return TSS2_RC_SUCCESS;
}

/** Creation of a primary key.
 *
 * Depending on the flags stored in the context the creation of a primary
 * key will be prepared.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] template The template which defines the key attributes and whether the
 *            key will be persistent.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_TRY_AGAIN if an I/O operation is not finished yet and
 *         this function needs to be called again.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
 *         object store.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @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_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_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_POLICY_UNKNOWN if policy search for a certain policy digest
 *         was not successful.
 * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS if the object already exists in object store.
 */
TSS2_RC
ifapi_create_primary(FAPI_CONTEXT *context, IFAPI_KEY_TEMPLATE *template) {
    TSS2_RC              r;
    ESYS_TR              auth_session;
    TPM2B_PUBLIC        *outPublic = NULL;
    TPM2B_CREATION_DATA *creationData = NULL;
    TPM2B_DIGEST        *creationHash = NULL;
    TPMT_TK_CREATION    *creationTicket = NULL;
    IFAPI_OBJECT        *object = &context->cmd.Key_Create.object;
    IFAPI_OBJECT        *hierarchy = &context->cmd.Key_Create.hierarchy;
    IFAPI_KEY           *pkey = &context->cmd.Key_Create.object.misc.key;
    const char          *hierarchy_path;
    const char          *profile_name;

    /* Compute policy */

    switch (context->cmd.Key_Create.state) {
    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_INIT);
        profile_name = context->loadKey.path_list->str;
        r = ifapi_profiles_get(&context->profiles, profile_name, &context->cmd.Key_Create.profile);
        goto_if_error_reset_state(r, "Retrieving profile data", error_cleanup);

        context->cmd.Key_Create.public_templ = *template;
        r = ifapi_merge_profile_into_template(context->cmd.Key_Create.profile,
                                              &context->cmd.Key_Create.public_templ);
        goto_if_error_reset_state(r, "Merge profile", error_cleanup);

        /* Persistent keys are not allowd in the NULL hierarchy. */

        if (template->persistent_handle) {
            hierarchy_path = context->loadKey.path_list->next->str;
            if (strcmp(hierarchy_path, "HN") == 0)
                goto_error_reset_state(r, TSS2_FAPI_RC_BAD_VALUE,
                                       "Persistent keys are not possible in the NULL "
                                       "hierarchy.",
                                       error_cleanup);
        }

        if (context->cmd.Key_Create.policyPath
            && strcmp(context->cmd.Key_Create.policyPath, "") != 0)
            context->cmd.Key_Create.state = KEY_CREATE_PRIMARY_CALCULATE_POLICY;
        /* else jump over to KEY_CREATE_PRIMARY_WAIT_FOR_SESSION below */
    /* FALLTHRU */
    case KEY_CREATE_PRIMARY_CALCULATE_POLICY:
        if (context->cmd.Key_Create.state == KEY_CREATE_PRIMARY_CALCULATE_POLICY) {
            r = ifapi_calculate_tree(context, context->cmd.Key_Create.policyPath,
                                     &context->policy.policy,
                                     context->cmd.Key_Create.public_templ.public.publicArea.nameAlg,
                                     &context->policy.digest_idx, &context->policy.hash_size);
            return_try_again(r);
            goto_if_error2(r, "Calculate policy tree %s", error_cleanup,
                           context->cmd.Key_Create.policyPath);

            /* Store the calculated policy in the key object */
            object->policy = calloc(1, sizeof(TPMS_POLICY));
            return_if_null(object->policy, "Out of memory", TSS2_FAPI_RC_MEMORY);
            *(object->policy) = context->policy.policy;

            context->cmd.Key_Create.public_templ.public.publicArea.authPolicy.size
                = context->policy.hash_size;
            memcpy(&context->cmd.Key_Create.public_templ.public.publicArea.authPolicy.buffer[0],
                   &context->policy.policy.policyDigests.digests[context->policy.digest_idx].digest,
                   context->policy.hash_size);
        }

        r = ifapi_get_sessions_async(context, IFAPI_SESSION_GEN_SRK | IFAPI_SESSION1,
                                     TPMA_SESSION_ENCRYPT | TPMA_SESSION_DECRYPT, 0);
        goto_if_error_reset_state(r, "Create sessions", error_cleanup);

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_SESSION);
        r = ifapi_get_sessions_finish(context, &context->profiles.default_profile,
                                      context->profiles.default_profile.nameAlg);
        return_try_again(r);
        goto_if_error(r, "Create FAPI session.", error_cleanup);

        hierarchy_path = context->loadKey.path_list->next->str;

        r = ifapi_keystore_load_async(&context->keystore, &context->io, hierarchy_path);
        free_string_list(context->loadKey.path_list);
        context->loadKey.path_list = NULL;
        return_if_error2(r, "Could not open hierarchy /%s", hierarchy_path);

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_HIERARCHY);
        r = ifapi_keystore_load_finish(&context->keystore, &context->io, hierarchy);
        return_try_again(r);
        return_if_error(r, "read_finish failed");

        r = ifapi_initialize_object(context->esys, hierarchy);
        goto_if_error_reset_state(r, "Initialize hierarchy object", error_cleanup);

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_AUTHORIZE1);
        r = ifapi_authorize_object(context, hierarchy, &auth_session);
        FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);

        r = Esys_CreatePrimary_Async(
            context->esys, hierarchy->public.handle, auth_session,
            ENC_SESSION_IF_POLICY(auth_session), ESYS_TR_NONE, &context->cmd.Key_Create.inSensitive,
            &context->cmd.Key_Create.public_templ.public, &context->cmd.Key_Create.outsideInfo,
            &context->cmd.Key_Create.creationPCR);
        goto_if_error_reset_state(r, "Prepare create primary", error_cleanup);

        object->misc.key.unique_init_set = TPM2_YES;
        object->misc.key.unique_init
            = context->cmd.Key_Create.public_templ.public.publicArea.unique;

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_PRIMARY);
        r = Esys_CreatePrimary_Finish(context->esys, &context->cmd.Key_Create.handle, &outPublic,
                                      &creationData, &creationHash, &creationTicket);
        try_again_or_error_goto(r, "Create primary.", error_cleanup);

        /* Prepare object for serialization */
        context->session2 = ESYS_TR_NONE;
        object->system = context->cmd.Key_Create.public_templ.system;
        object->objectType = IFAPI_KEY_OBJ;
        object->misc.key.public = *outPublic;
        object->misc.key.private.size = 0;
        object->misc.key.private.buffer = NULL;
        object->misc.key.policyInstance = NULL;
        object->misc.key.creationData = *creationData;
        object->misc.key.creationHash = *creationHash;
        object->misc.key.creationTicket = *creationTicket;
        object->misc.key.description = NULL;
        object->misc.key.certificate = NULL;
        object->misc.key.reset_count = context->init_time.clockInfo.resetCount;

        SAFE_FREE(pkey->serialization.buffer);
        r = Esys_TR_Serialize(context->esys, context->cmd.Key_Create.handle,
                              &pkey->serialization.buffer, &pkey->serialization.size);
        goto_if_error(r, "Error serialize esys object", error_cleanup);

        SAFE_FREE(creationData);
        SAFE_FREE(creationTicket);
        SAFE_FREE(creationHash);
        if (context->cmd.Key_Create.inSensitive.sensitive.userAuth.size > 0)
            object->misc.key.with_auth = TPM2_YES;
        else
            object->misc.key.with_auth = TPM2_NO;
        ;
        r = ifapi_get_name(&outPublic->publicArea, &object->misc.key.name);
        goto_if_error(r, "Get key name", error_cleanup);

        if (object->misc.key.public.publicArea.type == TPM2_ALG_RSA)
            object->misc.key.signing_scheme = context->cmd.Key_Create.profile->rsa_signing_scheme;
        else
            object->misc.key.signing_scheme = context->cmd.Key_Create.profile->ecc_signing_scheme;
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_AUTHORIZE2);
        if (template->persistent_handle) {
            object->misc.key.persistent_handle = template->persistent_handle;
            r = ifapi_authorize_object(context, hierarchy, &auth_session);
            FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);

            /* Prepare making the created primary permanent. */
            r = Esys_EvictControl_Async(context->esys, hierarchy->public.handle,
                                        context->cmd.Key_Create.handle, auth_session, ESYS_TR_NONE,
                                        ESYS_TR_NONE, object->misc.key.persistent_handle);
            goto_if_error(r, "Error Esys EvictControl", error_cleanup);
        }
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_EVICT_CONTROL);
        if (template->persistent_handle) {
            /* Prepare making the loaded key permanent. */
            r = Esys_EvictControl_Finish(context->esys, &object->public.handle);
            return_try_again(r);
            goto_if_error(r, "Evict control failed", error_cleanup);
        }

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_FLUSH);
        /* Flush the primary key. */
        r = ifapi_flush_object(context, context->cmd.Key_Create.handle);
        return_try_again(r);
        goto_if_error(r, "Flush key", error_cleanup);

        context->cmd.Key_Create.handle = ESYS_TR_NONE;

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WRITE_PREPARE);
        SAFE_FREE(outPublic);

        /* Perform esys serialization */
        r = ifapi_esys_serialize_object(context->esys, object);
        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,
                                                 context->cmd.Key_Create.keyPath, object);
        goto_if_error_reset_state(r, "Could not write: %s", error_cleanup,
                                  context->cmd.Key_Create.keyPath);

        /* Start writing the object to the key store */
        r = ifapi_keystore_store_async(&context->keystore, &context->io,
                                       context->cmd.Key_Create.keyPath, object);
        goto_if_error_reset_state(r, "Could not open: %s", error_cleanup,
                                  context->cmd.Key_Create.keyPath);
        ifapi_cleanup_ifapi_object(object);
        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WRITE);
        /* Finish writing the key to the key store */
        r = ifapi_keystore_store_finish(&context->io);
        return_try_again(r);
        return_if_error_reset_state(r, "write_finish failed");

        fallthrough;

    statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_CLEANUP);
        r = ifapi_cleanup_session(context);
        try_again_or_error_goto(r, "Cleanup", error_cleanup);

        context->cmd.Key_Create.state = KEY_CREATE_INIT;
        break;

    statecasedefault(context->cmd.Key_Create.state);
    }
    free_string_list(context->loadKey.path_list);
    ifapi_cleanup_ifapi_object(hierarchy);
    SAFE_FREE(outPublic);
    SAFE_FREE(creationData);
    SAFE_FREE(creationHash);
    SAFE_FREE(creationTicket);
    SAFE_FREE(context->cmd.Key_Create.policyPath);
    SAFE_FREE(context->cmd.Key_Create.keyPath);
    ifapi_cleanup_ifapi_object(object);
    ifapi_session_clean(context);
    return TSS2_RC_SUCCESS;

error_cleanup:
    SAFE_FREE(outPublic);
    SAFE_FREE(creationData);
    SAFE_FREE(creationHash);
    SAFE_FREE(creationTicket);
    SAFE_FREE(context->cmd.Key_Create.policyPath);
    SAFE_FREE(context->cmd.Key_Create.keyPath);
    free_string_list(context->loadKey.path_list);
    ifapi_cleanup_ifapi_object(hierarchy);
    return r;
}

static const struct {
    char  *name;
    UINT32 from;
    UINT32 to;
} nv_tab[] = { { "TPM", 0x01000000, 0x013fffff },
               { "Platform", 0x01400000, 0x017fffff },
               { "Owner", 0x01800000, 0x01bfffff },
               { "Endorsement_Certificate", 0x01c00000, 0x01c07fff },
               { "Platform_Certificate", 0x01c08000, 0x01c0ffff },
               { "Component_OEM", 0x01c10000, 0x01c1ffff },
               { "TPM_OEM", 0x01c20000, 0x01c2ffff },
               { "Platform_OEM", 0x01c30000, 0x01c3ffff },
               { "PC-Client", 0x01c40000, 0x01c4ffff },
               { "Server", 0x01c50000, 0x01c5ffff },
               { "Virtualized_Platform", 0x01c60000, 0x01c6ffff },
               { "MPWG", 0x01c70000, 0x01c7ffff },
               { "Embedded", 0x01c80000, 0x01c8ffff } };

/** Creation of objects for existing nv indexes in keystore.
 *
 * For every TPM NV index which is currently not stored in the keystore
 * a FAPI object in the appropriate sub directory will be created.
 * e.g. a certificate stored at 0x1c00002 will be stored at:
 * /nv/Endorsement_Certificate/1c00002
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] ctx The context of the asynchronous state machine
 *            which creates the nv keystore objects.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_TRY_AGAIN if an I/O operation is not finished yet and
 *         this function needs to be called again.
 * @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
 *         object store.
 */
TSS2_RC
ifapi_create_nv_objects(FAPI_CONTEXT *fapi_ctx, IFAPI_CREATE_NV *ctx) {
    /* Check for NULL parameters */
    check_not_null(ctx);
    TPMS_CAPABILITY_DATA **capability = &ctx->capability;
    TPM2B_NV_PUBLIC       *existing_nv_public = NULL;
    IFAPI_OBJECT           object;
    size_t                 i;
    TSS2_RC                r;

    switch (ctx->state) {
    statecase(ctx->state, CREATE_NV_CHECK_NV_OBJECTS_INIT);
        /* Compute the list of all nv objects stored in keystore. */
        r = ifapi_keystore_list_all(&fapi_ctx->keystore, "/nv", &ctx->pathlist, &ctx->numPaths);
        goto_if_error2(r, "Could not create list of nv objects", cleanup_return);

        ctx->nv_idx_list = calloc(ctx->numPaths, sizeof(TPMI_RH_NV_INDEX));
        ctx->path_idx = 0;
        fallthrough;

    statecase(ctx->state, CREATE_NV_CHECK_NV_OBJECTS);
        if (ctx->path_idx == ctx->numPaths) {
            /* All nv files were read. */
            ctx->state = CREATE_NV_GET_TPM_NV_HANDLES;
            ctx->more_data = TPM2_YES;
            ctx->nv_index = TPM2_NV_INDEX_FIRST;
            return TSS2_FAPI_RC_TRY_AGAIN;
        }
        r = ifapi_keystore_load_async(&fapi_ctx->keystore, &fapi_ctx->io,
                                      ctx->pathlist[ctx->path_idx]);
        goto_if_error2(r, "Could not open %s", cleanup_return, ctx->pathlist[ctx->path_idx]);
        fallthrough;

    statecase(ctx->state, CREATE_NV_READ_NV_OBJECT);
        r = ifapi_keystore_load_finish(&fapi_ctx->keystore, &fapi_ctx->io, &object);
        return_try_again(r);
        goto_if_error2(r, "Could not open %s", cleanup_return, ctx->pathlist[ctx->path_idx]);
        ctx->nv_idx_list[ctx->path_idx] = object.misc.nv.public.nvPublic.nvIndex;
        ifapi_cleanup_ifapi_object(&object);
        ctx->path_idx += 1;
        ctx->state = CREATE_NV_CHECK_NV_OBJECTS;
        return TSS2_FAPI_RC_TRY_AGAIN;

    statecase(ctx->state, CREATE_NV_GET_TPM_NV_HANDLES);
        /* Get the list of all nv indexes */
        r = Esys_GetCapability_Async(fapi_ctx->esys, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                     TPM2_CAP_HANDLES, ctx->nv_index, TPM2_MAX_CAP_HANDLES);
        goto_if_error2(r, "GetCapability_Async", cleanup_return);

        fallthrough;

    statecase(ctx->state, CREATE_NV_GET_TPM_NV_HANDLES2);
        r = Esys_GetCapability_Finish(fapi_ctx->esys, &ctx->more_data, capability);
        return_try_again(r);
        goto_if_error2(r, "GetCapability_Finish", cleanup_return);

        ctx->nv_cap_idx = 0;
        fallthrough;

    statecase(ctx->state, CREATE_NV_CHECK_NV_INDEX);
        /* Async loop over all nv indexes */
        if (ctx->nv_cap_idx == (*capability)->data.handles.count) {
            /* All indexes from get capability processed. */
            if (0 && ctx->more_data) {
                /* Further call of get capability needed. */
                ctx->state = CREATE_NV_GET_TPM_NV_HANDLES;
                /* Define property for get capability. */
                ctx->nv_index
                    = (*capability)->data.handles.handle[(*capability)->data.handles.count - 1] + 1;
                SAFE_FREE(*capability);
                return TSS2_FAPI_RC_TRY_AGAIN;
            } else {
                break;
            }
        }

        if ((*capability)->data.handles.handle[ctx->nv_cap_idx] > TPM2_NV_INDEX_LAST) {
            break;
        }

        /* Check whether nv index already exists in keystore. */
        for (i = 0; i < ctx->numPaths; i++) {
            if (ctx->nv_idx_list[i] == (*capability)->data.handles.handle[ctx->nv_cap_idx]) {
                ctx->nv_cap_idx++;
                return TSS2_FAPI_RC_TRY_AGAIN;
            }
        }

        ctx->nv_index = (*capability)->data.handles.handle[ctx->nv_cap_idx];

        r = Esys_TR_FromTPMPublic_Async(fapi_ctx->esys, ctx->nv_index, ESYS_TR_NONE, ESYS_TR_NONE,
                                        ESYS_TR_NONE);
        return_if_error(r, "Esys_TR_FromTPMPublic_Async");

        fallthrough;

    statecase(ctx->state, CREATE_NV_NV_GET_ESYS_HANDLE);
        r = Esys_TR_FromTPMPublic_Finish(fapi_ctx->esys, &ctx->esys_nv_handle);
        return_try_again(r);

        return_if_error(r, "Esys_TR_FromTPMPublic_Finish");

        /* Read public from the existing nv object */
        r = Esys_NV_ReadPublic_Async(fapi_ctx->esys, ctx->esys_nv_handle, ESYS_TR_NONE,
                                     ESYS_TR_NONE, ESYS_TR_NONE);
        return_if_error(r, "Esys_NV_ReadPublic_Async");
        fallthrough;

    statecase(ctx->state, CREATE_NV_NV_WAIT_FOR_READ_PUBLIC);
        r = Esys_NV_ReadPublic_Finish(fapi_ctx->esys, &existing_nv_public, NULL);
        return_try_again(r);
        return_if_error(r, "Error: nv read public");

        ctx->path = NULL;

        for (i = 0; i < ARRAY_LEN(nv_tab); i++) {
            if (ctx->nv_index >= nv_tab[i].from && ctx->nv_index <= nv_tab[i].to) {
                r = ifapi_asprintf(&ctx->path, "/nv/%s/%x", nv_tab[i].name, ctx->nv_index);
                return_if_error(r, "Can't create pathname for nv index.");
            }
        }

        if (!ctx->path) {
            LOG_WARNING("NV path can't be determined for handel 0x%x", ctx->nv_index);
            ctx->nv_cap_idx++;
            SAFE_FREE(existing_nv_public);
            ctx->state = CREATE_NV_CHECK_NV_INDEX;
            return TSS2_FAPI_RC_TRY_AGAIN;
        }

        if (existing_nv_public->nvPublic.authPolicy.size > 0) {
            /* NV objects with existing policy will not be automatically added. */
            ctx->nv_cap_idx++;
            SAFE_FREE(existing_nv_public);
            ctx->state = CREATE_NV_CHECK_NV_INDEX;
            return TSS2_FAPI_RC_TRY_AGAIN;
        }

        memset(&ctx->nv_object, 0, sizeof(IFAPI_OBJECT));
        ctx->nv_object.objectType = IFAPI_NV_OBJ;
        if (existing_nv_public->nvPublic.attributes & TPMA_NV_PLATFORMCREATE) {
            ctx->nv_object.misc.nv.hierarchy = ESYS_TR_RH_PLATFORM;
        } else {
            ctx->nv_object.misc.nv.hierarchy = ESYS_TR_RH_OWNER;
        }
        ctx->nv_object.system = TPM2_YES;

        if (ctx->nv_index >= 0x01c00000 && ctx->nv_index <= 0x01c07fff) {
            ctx->nv_object.misc.nv.with_auth = TPM2_NO;
        } else {
            ctx->nv_object.misc.nv.with_auth = TPM2_YES;
        }
        ctx->nv_object.misc.nv.public = *existing_nv_public;
        ctx->nv_object.public.handle = ctx->esys_nv_handle;
        SAFE_FREE(existing_nv_public);
        /* Perform esys serialization  */
        r = ifapi_esys_serialize_object(fapi_ctx->esys, &ctx->nv_object);
        goto_if_error(r, "Prepare serialization", cleanup_return);

        /* Check whether object already exists in key store.*/
        r = ifapi_keystore_object_does_not_exist(&fapi_ctx->keystore, ctx->path, &ctx->nv_object);
        goto_if_error2(r, "Could not write: %sh", cleanup_return, ctx->path);

        /* Start writing the NV object to the key store */
        r = ifapi_keystore_store_async(&fapi_ctx->keystore, &fapi_ctx->io, ctx->path,
                                       &ctx->nv_object);
        goto_if_error2(r, "Could not open: %sh", cleanup_return, ctx->path);

        fallthrough;

    statecase(ctx->state, CREATE_NV_WRITE);
        SAFE_FREE(ctx->path);
        /* Finish writing the NV object to the key store */
        r = ifapi_keystore_store_finish(&fapi_ctx->io);
        return_try_again(r);
        goto_if_error(r, "write_finish failed", cleanup_return);

        ifapi_cleanup_ifapi_object(&ctx->nv_object);

        ctx->nv_cap_idx++;
        ctx->state = CREATE_NV_CHECK_NV_INDEX;
        return TSS2_FAPI_RC_TRY_AGAIN;

    statecasedefault(ctx->state);
    }
    ctx->state = CREATE_NV_CHECK_NV_OBJECTS_INIT;
    SAFE_FREE(*capability);
    for (i = 0; i < ctx->numPaths; i++) {
        SAFE_FREE(ctx->pathlist[i]);
    }
    SAFE_FREE(ctx->pathlist);
    SAFE_FREE(ctx->nv_idx_list);
    LOG_TRACE("finished");
    return TSS2_RC_SUCCESS;

cleanup_return:
    /* Cleanup any intermediate results and state stored in the context. */
    for (i = 0; i < ctx->numPaths; i++) {
        SAFE_FREE(ctx->pathlist[i]);
    }
    ifapi_cleanup_ifapi_object(&ctx->nv_object);
    SAFE_FREE(ctx->pathlist);
    SAFE_FREE(ctx->nv_idx_list);
    SAFE_FREE(ctx->path);

    return r;
}

/** Check whether an NV index exist on TPM and in keystore.
 *
 *  It will be checked whether an NV index exists in the TPM.
 *  An error will be returned if the index already exists in
 *  the keystore.
 *
 * @param[in] context The FAPI_CONTEXT.
 * @param[in] template The template which defines the key attributes and whether the
 *            key will be persistent.
 * @param[out] The boolean which defines whether the index exists.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if the index already exists.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 * @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_TRY_AGAIN if an I/O operation is not finished yet and
 *         this function needs to be called again.
 * @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
 *         object store.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @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
ifapi_check_existing_nv(FAPI_CONTEXT     *context,
                        TPMI_RH_NV_INDEX  nv_index,
                        bool             *nv_exists,
                        ESYS_TR          *esys_nv_handle,
                        TPM2B_NV_PUBLIC **nvPublic) {
    TSS2_RC r;
    *nv_exists = false;
    TPMI_YES_NO            moreData;
    TPMS_CAPABILITY_DATA **capabilityData = &context->nv_cmd.capability;

    switch (context->nv_cmd.nv_check) {
    statecase(context->nv_cmd.nv_check, CHECK_NV_INIT);
        *capabilityData = NULL;
        r = Esys_GetCapability_Async(context->esys, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                     TPM2_CAP_HANDLES, nv_index, 1);
        return_if_error(r, "Esys_GetCapability_Async");
        fallthrough;
    statecase(context->nv_cmd.nv_check, CHECK_NV_WAIT_FOR_GET_CAP);
        r = Esys_GetCapability_Finish(context->esys, &moreData, capabilityData);
        return_try_again(r);
        return_if_error_reset_state(r, "GetCapablity_Finish");
        if ((*capabilityData)->data.handles.count == 0
            || (*capabilityData)->data.handles.handle[0] != nv_index) {
            context->nv_cmd.nv_check = CHECK_NV_INIT;
            SAFE_FREE(*capabilityData);
            *nv_exists = false;
            break;
        }
        SAFE_FREE(*capabilityData);

        r = Esys_TR_FromTPMPublic_Async(context->esys, nv_index, ESYS_TR_NONE, ESYS_TR_NONE,
                                        ESYS_TR_NONE);
        return_if_error(r, "Esys_TR_FromTPMPublic_Async");

        fallthrough;

    statecase(context->nv_cmd.nv_check, CHECK_NV_GET_ESYS_HANDLE);
        r = Esys_TR_FromTPMPublic_Finish(context->esys, esys_nv_handle);
        return_try_again(r);

        return_if_error(r, "Esys_TR_FromTPMPublic_Finish");

        /* Read public from the existing nv object */
        r = Esys_NV_ReadPublic_Async(context->esys, *esys_nv_handle, ESYS_TR_NONE, ESYS_TR_NONE,
                                     ESYS_TR_NONE);
        return_if_error(r, "Esys_NV_ReadPublic_Async");
        fallthrough;

    statecase(context->nv_cmd.nv_check, CHECK_NV_WAIT_FOR_READ_PUBLIC);
        r = Esys_NV_ReadPublic_Finish(context->esys, nvPublic, NULL);
        return_try_again(r);
        return_if_error(r, "Error: nv read public");
        *nv_exists = true;
        break;
    statecasedefault(context->cmd.Key_Create.state);
    }

    return TSS2_RC_SUCCESS;
}
