/* SPDX-License-Identifier: BSD-2-Clause */
/*******************************************************************************
 * Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG
 * All rights reserved.
 *******************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <stdlib.h>

#include "fapi_util.h"
#include "fapi_policy.h"
#include "ifapi_helpers.h"
#include "fapi_crypto.h"
#include "ifapi_policy_instantiate.h"
#include "ifapi_policyutil_execute.h"
#include "ifapi_policy_execute.h"
#include "ifapi_policy_callbacks.h"
#include "tss2_mu.h"

#define LOGMODULE fapi
#include "util/log.h"
#include "util/aux_util.h"

/** Determine the auth object of a NV index.
 *
 * The auth object is determined depending on the object flags.
 *
 * @param[in]  nv_object The internal FAPI object representing the NV index.
 * @param[out] nv_index The ESYS handle of the NV index.
 * @param[out] auth_object The internal FAPI auth object.
 * @param[out] auth_index The ESYS handle of the auth object.
 * @retval TSS2_RC_SUCCESS on success.
 */
static void
get_nv_auth_object(
    IFAPI_OBJECT *nv_object,
    ESYS_TR nv_index,
    IFAPI_OBJECT *auth_object,
    ESYS_TR *auth_index)
{
    if (nv_object->misc.nv.public.nvPublic.attributes & TPMA_NV_PPREAD) {
        ifapi_init_hierarchy_object(auth_object, ESYS_TR_RH_PLATFORM);
        *auth_index = ESYS_TR_RH_PLATFORM;
    } else {
        if (nv_object->misc.nv.public.nvPublic.attributes & TPMA_NV_OWNERREAD) {
            ifapi_init_hierarchy_object(auth_object, ESYS_TR_RH_OWNER);
            *auth_index = ESYS_TR_RH_OWNER;
        } else {
            *auth_index = nv_index;
            *auth_object = *nv_object;
        }
    }
}

/** Get public data of a key from keystore.
 *
 * @param[in] path The relative path of the key.
 * @param[out] public The caller allocated public structure.
 * @param[in,out] ctx The context to access io and keystore module and to store
 *                      the io state.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be loaded.
 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
 *         internal operations or return parameters.
 * @retval TSS2_FAPI_RC_BAD_TEMPLATE If the loaded template is not
 *         appropriate for this operation.
 * @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.
 * @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_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 */
TSS2_RC
ifapi_get_key_public(
    const char *path,
    TPMT_PUBLIC *public,
    void *ctx)
{
    TSS2_RC r = TSS2_RC_SUCCESS;
    IFAPI_OBJECT object;
    FAPI_CONTEXT *context = ctx;

    switch (context->io_state) {
    statecase(context->io_state, IO_INIT)
        /* Prepare the loading of the object. */
        r = ifapi_keystore_load_async(&context->keystore, &context->io, path);
        return_if_error2(r, "Could not open: %s", path);
        fallthrough;

    statecase(context->io_state, IO_ACTIVE)
        /* Finalize or retry the reading and check the object type */
        r = ifapi_keystore_load_finish(&context->keystore, &context->io,
                                       &object);
        return_try_again(r);
        return_if_error(r, "read_finish failed");

        switch (object.objectType) {
        case IFAPI_KEY_OBJ:
            *public = object.misc.key.public.publicArea;
            break;
        case IFAPI_EXT_PUB_KEY_OBJ:
            *public = object.misc.ext_pub_key.public.publicArea;
            break;
        default:
            goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Object %s is not a key.",
                       cleanup, path);
        }
        break;

    statecasedefault_error(context->state, r, cleanup);
    }

cleanup:
    context->io_state = IO_INIT;
    ifapi_cleanup_ifapi_object(&object);
    return r;
}

/** Get TPM name of an object from  key keystore.
 *
 * @param[in] path The relative path of the object.
 * @param[out] name The caller allocate public structure.
 * @param[in,out] ctx The context to access io and keystore module and to store
 *                the io state.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be loaded.
 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
 *         internal operations or return parameters.
 * @retval TSS2_FAPI_RC_BAD_TEMPLATE If the loaded template is not
 *         appropriate for this operation.
 * @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.
 * @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_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 */
TSS2_RC
ifapi_get_object_name(
    const char *path,
    TPM2B_NAME *name,
    void *ctx)
{
    TSS2_RC r = TSS2_RC_SUCCESS;
    IFAPI_OBJECT object;
    FAPI_CONTEXT *context = ctx;

    switch (context->io_state) {
    statecase(context->io_state, IO_INIT)
        /* Prepare the loading of the object. */
        r = ifapi_keystore_load_async(&context->keystore, &context->io, path);
        return_if_error2(r, "Could not open: %s", path);
        fallthrough;

    statecase(context->io_state, IO_ACTIVE)
        /* Finalize or retry the reading and check the object type */
        r = ifapi_keystore_load_finish(&context->keystore, &context->io,
                                       &object);
        return_try_again(r);
        return_if_error(r, "read_finish failed");

        switch (object.objectType) {
        case IFAPI_KEY_OBJ:
            r = ifapi_get_name(&object.misc.key.public.publicArea,
                               (TPM2B_NAME *)name);
            break;
        case IFAPI_EXT_PUB_KEY_OBJ:
            r = ifapi_get_name(&object.misc.ext_pub_key.public.publicArea,
                               (TPM2B_NAME *)name);
            break;
        case IFAPI_NV_OBJ:
            r = ifapi_nv_get_name(&object.misc.nv.public, name);
            break;
        default:
            goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid object %s.",
                       cleanup, path);
        }
        goto_if_error(r, "Get object name.", cleanup);
        break;

    statecasedefault(context->state);
    }

cleanup:
    ifapi_cleanup_ifapi_object(&object);
    return r;
}

/** Get public data of a NV object from keystore.
 *
 * @param[in] path The relative path of the NV object.
 * @param[out] nv_public The caller allocated public structure.
 * @param[in,out] ctx The context to access io and keystore module and to store
 *                the io state.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be loaded.
 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
 *         internal operations or return parameters.
 * @retval TSS2_FAPI_RC_BAD_TEMPLATE If the loaded template is not
 *         appropriate for this operation.
 * @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.
 * @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_GENERAL_FAILURE if an internal error occurred.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
 */
TSS2_RC
ifapi_get_nv_public(
    const char *path,
    TPM2B_NV_PUBLIC *nv_public,
    void *ctx)
{
    TSS2_RC r = TSS2_RC_SUCCESS;
    IFAPI_OBJECT object;
    FAPI_CONTEXT *context = ctx;

    switch (context->io_state) {
    statecase(context->io_state, IO_INIT)
        /* Prepare the loading of the NV object. */
        r = ifapi_keystore_load_async(&context->keystore, &context->io, path);
        return_if_error2(r, "Could not open: %s", path);
        fallthrough;

    statecase(context->io_state, IO_ACTIVE)
        /* Finalize or retry the reading and check the object type */
        r = ifapi_keystore_load_finish(&context->keystore, &context->io,
                                       &object);
        return_try_again(r);
        return_if_error(r, "read_finish failed");

        if (object.objectType != IFAPI_NV_OBJ) {
            goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Object %s is not a key.",
                       cleanup, path);
        }

        *nv_public = object.misc.nv.public;
        context->io_state = IO_INIT;
        break;

    statecasedefault(context->state);
    }

cleanup:
    ifapi_cleanup_ifapi_object(&object);
    return r;
}

/** Read values of PCR registers and clear selection.
 *
 * @param[in,out] pcr_select The registers to be read (bank selection from profile).
 * @param[in,out] pcr_selection The registers to be read (with bank selection).
 * @param[out] pcr_values The callee-allocated public structure.
 * @param[in,out] ctx The context to access io and keystore module and to store
 *                the io state.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if the input parameters had inappropriate values.
 * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
 *         complete. Call this function again later.
 * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
 *         operation already pending.
 */
TSS2_RC
ifapi_read_pcr(
    TPMS_PCR_SELECT *pcr_select,
    TPML_PCR_SELECTION *pcr_selection,
    TPML_PCRVALUES **pcr_values,
    void *ctx)
{
    TSS2_RC r = TSS2_RC_SUCCESS;
    FAPI_CONTEXT *context = ctx;
    UINT32 update_counter;
    TPML_PCR_SELECTION *out_selection = NULL;
    TPML_PCR_SELECTION *profile_selection;
    TPML_DIGEST *pcr_digests = NULL;
    size_t i, pcr, n_pcrs = 0, i_pcr;

    switch (context->io_state) {
    statecase(context->io_state, IO_INIT)
        if (pcr_select->sizeofSelect) {
            if (pcr_selection->count) {
                /* If pcr_select is used pcr_selection can't be initialized */
                return_error(TSS2_FAPI_RC_BAD_VALUE,
                             "Policy PCR: pcr_selection can't be used if pcr_selection is used.");
            }
            /* Determine hash alg */
            profile_selection = &context->profiles.default_profile.pcr_selection;
            for (i = 0; i < profile_selection->count; i++) {
                for (pcr = 0; pcr < TPM2_MAX_PCRS; pcr++) {
                    uint8_t byte_idx = pcr / 8;
                    uint8_t flag = 1 << (pcr % 8);
                    /* Check whether PCR is used. */
                    if (flag & profile_selection->pcrSelections[i].pcrSelect[byte_idx] &&
                        flag & pcr_select->pcrSelect[byte_idx]) {
                        pcr_selection->pcrSelections[0].hash = profile_selection->pcrSelections[i].hash;
                    }
                }
            }
            if (!pcr_selection->pcrSelections[0].hash) {
                /* hash for current pcr_select can't be determined */
                return_error(TSS2_FAPI_RC_BAD_VALUE,
                             "Policy PCR: pcr_select does not match profile.");
            }
            /* Only one bank will be used. The hash alg from profile will be used */
            pcr_selection->count = 1;
            pcr_selection->pcrSelections[0].sizeofSelect = pcr_select->sizeofSelect;
            for (i = 0; i < pcr_select->sizeofSelect; i++)
                pcr_selection->pcrSelections[0].pcrSelect[i] = pcr_select->pcrSelect[i];
        }

        /* Prepare the PCR Reading. */
        r = Esys_PCR_Read_Async(context->esys,
                                ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
                                pcr_selection);
        return_if_error(r, "PCR Read");
        fallthrough;

    statecase(context->io_state, IO_ACTIVE)
        /* Finalize or retry the reading and check the object type */
        r = Esys_PCR_Read_Finish(context->esys,
                                 &update_counter,
                                 &out_selection,
                                 &pcr_digests);

        if ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN)
            return TSS2_FAPI_RC_TRY_AGAIN;

        return_if_error(r, "PCR_Read_Finish");

        /* Count pcrs */
        for (i = 0; i < out_selection->count; i++) {
            for (pcr = 0; pcr < TPM2_MAX_PCRS; pcr++) {
                uint8_t byte_idx = pcr / 8;
                uint8_t flag = 1 << (pcr % 8);
                /* Check whether PCR is used. */
                if (flag & out_selection->pcrSelections[i].pcrSelect[byte_idx])
                    n_pcrs += 1;
            }
        }

        *pcr_values = calloc(1, sizeof(TPML_PCRVALUES) + n_pcrs* sizeof(TPMS_PCRVALUE));
        goto_if_null2(*pcr_values, "Out of memory.", r, TSS2_FAPI_RC_MEMORY, cleanup);

        /* Initialize digest list with pcr values from TPM */
        i_pcr = 0;
        for (i = 0; i < out_selection->count; i++) {
            for (pcr = 0; pcr < TPM2_MAX_PCRS; pcr++) {
                uint8_t byte_idx = pcr / 8;
                uint8_t flag = 1 << (pcr % 8);
                /* Check whether PCR is used. */
                if (flag & out_selection->pcrSelections[i].pcrSelect[byte_idx]) {
                    (*pcr_values)->pcrs[i_pcr].pcr = pcr;
                    (*pcr_values)->pcrs[i_pcr].hashAlg = out_selection->pcrSelections[i].hash;
                    memcpy(&(*pcr_values)->pcrs[i_pcr].digest,
                           &pcr_digests->digests[i_pcr].buffer[0],
                           pcr_digests->digests[i_pcr].size);
                    i_pcr += 1;
                }
            }
        }

        context->io_state = IO_INIT;
        break;

    statecasedefault(context->state);
    }

cleanup:
    SAFE_FREE(out_selection);
    SAFE_FREE(pcr_digests);
    return r;
}

/** Callback for authorization of objects used by policy.
 *
 * @param[in] name The name of the object to be authorized.
 * @param[in] object_handle The ESYS handle of the used object.
 * @param[in] auth_handle will be used for object authorization. For
              keys it will we equal to the object handle.
 * @param[out] authSession The session used for object authorization.
 * @param[in,out] userdata The Fapi context which will be used for keystore
 *                access, and storing the policy execution state.
 *                the io state.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or policy is NULL.
 * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed.
 * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
 *         complete. Call this function again later.
 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
 *         operation already pending.
 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy was not found.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND If a key was not found.
 * @retval TSS2_FAPI_RC_IO_ERROR If an IO error occurred during reading
 *         a policy or a key.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an error in an used library
 *         occurred.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
 *         is not set.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
 *         was not successful.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 */
TSS2_RC
ifapi_policyeval_cbauth(
    TPM2B_NAME *name,
    ESYS_TR *object_handle,
    ESYS_TR *auth_handle,
    ESYS_TR *authSession,
    void *userdata)
{
    TSS2_RC r;
    FAPI_CONTEXT *fapi_ctx = userdata;
    IFAPI_POLICY_EXEC_CTX *current_policy;
    IFAPI_POLICY_EXEC_CB_CTX *cb_ctx;
    bool next_case;

    return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE);
    return_if_null(fapi_ctx->policy.policyutil_stack, "Policy not initialized.",
                   TSS2_FAPI_RC_BAD_REFERENCE);

    if (fapi_ctx->policy.util_current_policy) {
        /* Use the current policy in the policy stack. */
        current_policy = fapi_ctx->policy.util_current_policy->pol_exec_ctx;
    } else {
        /* Start with the bottom of the policy stack */
        current_policy = fapi_ctx->policy.policyutil_stack->pol_exec_ctx;
    }
    cb_ctx = current_policy->app_data;

    do {
        next_case = false;
        switch (cb_ctx->cb_state) {
        statecase(cb_ctx->cb_state, POL_CB_EXECUTE_INIT);
            cb_ctx->auth_index = ESYS_TR_NONE;
            /* Search object with name in keystore. */
            r = ifapi_keystore_search_obj(&fapi_ctx->keystore, &fapi_ctx->io,
                                          name,
                                          &cb_ctx->object_path);
            FAPI_SYNC(r, "Search Object", cleanup);

            r = ifapi_keystore_load_async(&fapi_ctx->keystore, &fapi_ctx->io,
                                          cb_ctx->object_path);
            return_if_error2(r, "Could not open: %s", cb_ctx->object_path);
            SAFE_FREE(cb_ctx->object_path);
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_READ_OBJECT);
            /* Get object from file */
            r = ifapi_keystore_load_finish(&fapi_ctx->keystore, &fapi_ctx->io,
                                           &cb_ctx->object);
            return_try_again(r);
            return_if_error(r, "read_finish failed");

            r = ifapi_initialize_object(fapi_ctx->esys, &cb_ctx->object);
            goto_if_error(r, "Initialize NV object", cleanup);

            if (cb_ctx->object.objectType == IFAPI_NV_OBJ) {
                /* NV Authorization */

                cb_ctx->nv_index = cb_ctx->object.handle;

                /* Determine the object used for authorization. */
                get_nv_auth_object(&cb_ctx->object,
                                   cb_ctx->object.handle,
                                   &cb_ctx->auth_object,
                                   &cb_ctx->auth_index);

                goto_if_error(r, "PolicySecret set authorization", cleanup);
                cb_ctx->cb_state = POL_CB_AUTHORIZE_OBJECT;

                cb_ctx->auth_object_ptr = &cb_ctx->auth_object;
                next_case = true;
                break;
            } else if (cb_ctx->object.objectType == IFAPI_HIERARCHY_OBJ) {
                cb_ctx->cb_state = POL_CB_AUTHORIZE_OBJECT;
                next_case = true;
                break;
            } else {
                cb_ctx->key_handle = cb_ctx->object.handle;
                cb_ctx->cb_state = POL_CB_LOAD_KEY;
            }
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_LOAD_KEY);
            /* Key loading and authorization */
            r = ifapi_load_key(fapi_ctx, cb_ctx->object_path,
                               &cb_ctx->auth_object_ptr);
            FAPI_SYNC(r, "Fapi load key.", cleanup);

            cb_ctx->object = *cb_ctx->key_object_ptr;
            SAFE_FREE(cb_ctx->key_object_ptr);
            cb_ctx->auth_object_ptr = &cb_ctx->object;
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_AUTHORIZE_OBJECT);
            r = ifapi_authorize_object(fapi_ctx, cb_ctx->auth_object_ptr, authSession);
            return_try_again(r);
            goto_if_error(r, "Authorize  object.", cleanup);

            cb_ctx->cb_state = POL_CB_EXECUTE_INIT;
            break;
            /* FALLTHRU */

        statecasedefault(cb_ctx->cb_state);
        }
    } while (next_case);
    *object_handle = cb_ctx->object.handle;
    if (cb_ctx->object.objectType == IFAPI_NV_OBJ)
        *auth_handle = cb_ctx->auth_index;
    else
        *auth_handle = cb_ctx->object.handle;

    if (current_policy->policySessionSav != ESYS_TR_NONE)
        fapi_ctx->policy.session = current_policy->policySessionSav;

cleanup:
    ifapi_cleanup_ifapi_object(&cb_ctx->object);
    if (current_policy->policySessionSav
        && current_policy->policySessionSav != ESYS_TR_NONE)
        fapi_ctx->policy.session = current_policy->policySessionSav;
    return r;
}

/** Callback for branch selection of policy or.
 *
 * @param[in]  branches The list of policy branches.
 * @param[out] branch_idx The index of the selcted branch.
 * @param[in,out] userdata The Fapi context which will be used for keystore
 *                access, and storing the policy execution state.
 *                the io state.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if no branch selection callback is
 *         defined. This callback will be needed of or policies which have to be
 *         executed.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the computed branch index
 *         delivered by the callback does not identify a branch.
 */
TSS2_RC
ifapi_branch_selection(
    TPML_POLICYBRANCHES *branches,
    size_t *branch_idx,
    void *userdata)
{
    TSS2_RC r;
    FAPI_CONTEXT *fapi_ctx = userdata;
    size_t i;
    const char *names[8];

    return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE);

    if (!fapi_ctx->callbacks.branch) {
        return_error(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN,
                     "No branch selection callback");
    }
    for (i = 0; i < branches->count; i++)
        names[i] = branches->authorizations[i].name;

    r = fapi_ctx->callbacks.branch(fapi_ctx, "PolicyOR",
                                   &names[0],
                                   branches->count,
                                   branch_idx,
                                   fapi_ctx->callbacks.branchData);
    return_if_error(r, "policyBranchSelectionCallback");

    if (*branch_idx >= branches->count) {
        return_error2(TSS2_FAPI_RC_AUTHORIZATION_FAILED, "Invalid branch number.");
    }
    return TSS2_RC_SUCCESS;
}

/** Callback for policy action.
 *
 * @param[in] action The name of the policy action.
 * @param[in,out] userdata The Fapi context which will be used for keystore
 *                access, and storing the policy execution state.
 *                the io state.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN If the callback for branch selection is
 *         not defined. This callback will be needed of or policies have to be
 *         executed.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE If no user data is passed.
 */
TSS2_RC
ifapi_policy_action(
    const char *action,
    void *userdata)
{
    TSS2_RC r;
    FAPI_CONTEXT *fapi_ctx = userdata;
    return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE);

    if (!fapi_ctx->callbacks.action) {
        return_error(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN,
                     "No action callback");
    }
    r = fapi_ctx->callbacks.action(fapi_ctx, action,
                                   fapi_ctx->callbacks.actionData);
    return_if_error(r, "ifapi_policy_action callback");

    return TSS2_RC_SUCCESS;
}

/** Callback for signing a byte buffer.
 *
 * @param[in] key_pem The pem key used for signing operation.
 * @param[in] public_key_hint A human readable hint to denote which public
 *            key to use.
 * @param[in] key_pem_hash_alg The hash alg used for digest computation.
 * @param[in] buffer the byte array to be signed.
 * @param[in] buffer_size The size of the buffer to be signed.
 * @param[out] signature The signature in DER format.
 * @param[out] signature_size The size of the signature.
 * @param[in] userdata The user context to retrieve the signing function.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN If the callback for signing is
 *         not defined.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE If no user data is passed.
 * @retval TSS2_FAPI_RC_TRY_AGAIN: if the callback operation is not yet
 *         complete. Call this function again later.
 */
TSS2_RC
ifapi_sign_buffer(
    char *key_pem,
    char *public_key_hint,
    TPMI_ALG_HASH key_pem_hash_alg,
    uint8_t *buffer,
    size_t buffer_size,
    uint8_t **signature,
    size_t *signature_size,
    void *userdata)
{
    TSS2_RC r;
    FAPI_CONTEXT *fapi_ctx = userdata;

    return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE);

    if (!fapi_ctx->callbacks.sign) {
        return_error2(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN,
                      "No signature callback.");
    }
    r = fapi_ctx->callbacks.sign(fapi_ctx, "PolicySigned", key_pem,
                                 public_key_hint ? public_key_hint : "",
                                 key_pem_hash_alg,
                                 buffer, buffer_size,
                                 signature, signature_size,
                                 fapi_ctx->callbacks.signData);
    try_again_or_error(r, "Execute policy signature callback.");

    return TSS2_RC_SUCCESS;
}

/**  Check whether public data of key is assigned to policy.
 *
 * It will be checked whether policy was authorized by abort key with public
 * data of type TPMT_PUBLIC.
 *
 * @param[in] policy The policy to be checked.
 * @param[in] publicVoid The public information of the key.
 * @param[in] nameAlgVoid Not used for this compare function.
 * @param[out] equal Switch whether check was successful.
 */
static TSS2_RC
equal_policy_authorization(
    TPMS_POLICY *policy,
    void *publicVoid,
    void *nameAlgVoid,
    bool *equal)
{
    TPMT_PUBLIC *public = publicVoid;
    (void)nameAlgVoid;
    size_t i;
    TPML_POLICYAUTHORIZATIONS *authorizations = policy->policyAuthorizations;

    *equal = false;
    if (authorizations) {
        for (i = 0; i < authorizations->count; i++) {
            if (ifapi_TPMT_PUBLIC_cmp
                (public, &authorizations->authorizations[i].key)) {
                *equal = true;
                return TSS2_RC_SUCCESS;
            }
        }
    }
    return TSS2_RC_SUCCESS;
}

/** Check whether policy digest can be found in policy.
 *
 * It will be tested whether the policy has been instatiated with the
 * passed digest.
 *
 * @param[in] policy The policy to be checked.
 * @param[in] authPolicyVoid The digest to be searched.
 * @param[in] nameAlgVoid The hash algorithm used for the digest computation.
 * @param[out] equal Switch whether check was successful.
 */
static TSS2_RC
compare_policy_digest(
    TPMS_POLICY *policy,
    void *authPolicyVoid,
    void *nameAlgVoid,
    bool *equal)
{
    TPM2B_DIGEST *authPolicy = authPolicyVoid;
    TPMI_ALG_HASH *hash_alg_ptr = nameAlgVoid;
    TPMI_ALG_HASH hash_alg = *hash_alg_ptr;
    size_t i;
    TPML_DIGEST_VALUES *digest_values;

    *equal = false;

    digest_values = &policy->policyDigests;

    if (digest_values) {
        for (i = 0; i < digest_values->count; i++) {
            if (digest_values->digests[i].hashAlg == hash_alg) {
                if (memcmp(&digest_values->digests[i].digest,
                           &authPolicy->buffer[0],
                           authPolicy->size))
                    continue;
                *equal = true;
                return TSS2_RC_SUCCESS;
            }
        }
    }
    return TSS2_RC_SUCCESS;
}

/** Search a policy file which fulfills a certain predicate.
 *
 * @param[in] context The context for storing the state information of the search
              process and the keystore paths.
 * @param[in] compare The function which will be used for comparison.
 * @param[in] all_objects Switch which determines wheter all policies fulfilling the
 *            the condition will be returned or only the first policy.
 * @param[in] object1 The first object used for comparison.
 * @param[in] object2 The second object used for comparison.
 * @param[out] policy_found The linked list with the policies fulfilling the condition.
 *
 * @retval TSS2_RC_SUCCESS on success.
 * @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_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_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_REFERENCE a invalid null pointer is passed.
 */
static TSS2_RC
search_policy(
    FAPI_CONTEXT *context,
    Policy_Compare_Object compare,
    bool all_objects,
    void *object1,
    void *object2,
    struct POLICY_LIST **policy_found)
{
    TSS2_RC r = TSS2_RC_SUCCESS;
    char *path;
    TPMS_POLICY policy = { 0 };
    bool found;
    struct POLICY_LIST *policy_object = NULL;
    struct POLICY_LIST *second;

    switch (context->fsearch.state) {
    case FSEARCH_INIT:
        LOG_DEBUG("** STATE ** FSEARCH_INIT");
        memset(&context->fsearch, 0, sizeof(IFAPI_FILE_SEARCH_CTX));
        /* Get the list of all files. */
        r = ifapi_keystore_list_all(&context->keystore, IFAPI_POLICY_DIR, &context->fsearch.pathlist,
                                    &context->fsearch.numPaths);
        return_if_error(r, "get entities.");
        context->fsearch.path_idx = context->fsearch.numPaths;

        context->fsearch.state = FSEARCH_OBJECT;
    /* FALLTHRU */

    case FSEARCH_OBJECT:
        LOG_DEBUG("** STATE ** FSEARCH_OBJECT");

        /* Test whether all files have been checked. */
        if (context->fsearch.path_idx == 0) {
            if (*policy_found) {
                context->fsearch.state = FSEARCH_INIT;
                for (size_t i = 0; i < context->fsearch.numPaths; i++) {
                    SAFE_FREE(context->fsearch.pathlist[i]);
                }
                SAFE_FREE(context->fsearch.pathlist);
                return TSS2_RC_SUCCESS;
            }
            goto_error(r, TSS2_FAPI_RC_POLICY_UNKNOWN, "Policy not found.", cleanup);
        }
        context->fsearch.path_idx -= 1;
        path = context->fsearch.pathlist[context->fsearch.path_idx];
        context->fsearch.current_path = path;
        LOG_DEBUG("Check file: %s %zu", path, context->fsearch.path_idx);

        /* Prepare policy loading. */
        r = ifapi_policy_store_load_async(&context->pstore, &context->io, path);
        goto_if_error2(r, "Can't open: %s", cleanup, path);

        context->fsearch.state = FSEARCH_READ;
    /* FALLTHRU */

    case FSEARCH_READ:
        LOG_DEBUG("** STATE ** FSEARCH_READ");
        /* Finalize policy loading if possible. */
        r = ifapi_policy_store_load_finish(&context->pstore, &context->io, &policy);
        return_try_again(r);
        goto_if_error(r, "read_finish failed", cleanup);

        /* Call the passed compare function. */
        r = compare(&policy, object1, object2, &found);
        if (found) {
            LOG_DEBUG("compare true  %s",
                      context->fsearch.pathlist[context->fsearch.path_idx]);
        } else {
            LOG_DEBUG("compare false  %s",
                      context->fsearch.pathlist[context->fsearch.path_idx]);
        }
        goto_if_error(r, "Invalid cipher object.", cleanup);

        if (!found) {
            if (!all_objects && context->fsearch.path_idx == 0) {
                /* All files checked, but no policy found. */
                context->fsearch.state = FSEARCH_INIT;
                ifapi_cleanup_policy(&policy);
                return TSS2_BASE_RC_POLICY_UNKNOWN;
            } else {
                /* Continue search. */
                context->fsearch.state = FSEARCH_OBJECT;
                ifapi_cleanup_policy(&policy);
                return TSS2_FAPI_RC_TRY_AGAIN;
            }
        }
        /* Extend linked list.*/
        policy_object = calloc(sizeof(struct POLICY_LIST), 1);
        return_if_null(policy_object, "Out of memory.", TSS2_FAPI_RC_MEMORY);

        strdup_check(policy_object->path, context->fsearch.current_path, r, cleanup);
        policy_object->policy = policy;
        if (*policy_found != NULL) {
            second = *policy_found;
            policy_object->next = second;
        }
        *policy_found = policy_object;

        if (context->fsearch.path_idx == 0) {
            context->fsearch.state = FSEARCH_INIT;
            /* Cleanup list of all paths. */
            for (size_t i = 0; i < context->fsearch.numPaths; i++) {
                SAFE_FREE(context->fsearch.pathlist[i]);
            }
            SAFE_FREE(context->fsearch.pathlist);
            return TSS2_RC_SUCCESS;
        }

        if (all_objects) {
            context->fsearch.state = FSEARCH_OBJECT;
            return TSS2_FAPI_RC_TRY_AGAIN;
        }

        break;

    default:
        context->state = _FAPI_STATE_INTERNALERROR;
        goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid state for load key.", cleanup);
    }
    context->fsearch.state = FSEARCH_INIT;
    for (size_t i = 0; i < context->fsearch.numPaths; i++) {
        SAFE_FREE(context->fsearch.pathlist[i]);
    }
    SAFE_FREE(context->fsearch.pathlist);
    return TSS2_RC_SUCCESS;
cleanup:
    SAFE_FREE(policy_object);
    ifapi_cleanup_policy(&policy);
    for (size_t i = 0; i < context->fsearch.numPaths; i++) {
        SAFE_FREE(context->fsearch.pathlist[i]);
    }
    SAFE_FREE(context->fsearch.pathlist);
    context->fsearch.state = FSEARCH_INIT;
    return r;
}

/** Get policy digest for a certain hash alg.
 *
 * @param[in]  policy The policy with the digest list.
 * @param[in]  hashAlg The hash algorithm used for the digest computation.
 * @param[out] digest The digest matching hashAlg.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 */
static TSS2_RC
get_policy_digest(TPMS_POLICY *policy,
                  TPMI_ALG_HASH hashAlg,
                  TPM2B_DIGEST *digest)
{
    size_t i;

    if (!(digest->size = ifapi_hash_get_digest_size(hashAlg))) {
        return_error2(TSS2_FAPI_RC_BAD_VALUE,
                      "Unsupported hash algorithm (%" PRIu16 ")", hashAlg);
    }

    for (i = 0; i < policy->policyDigests.count; i++) {
        if (policy->policyDigests.digests[i].hashAlg == hashAlg) {
            memcpy(&digest->buffer[0],
                   &policy->policyDigests.digests[i].digest, digest->size);
            return TSS2_RC_SUCCESS;
        }
    }
    return TSS2_FAPI_RC_GENERAL_FAILURE;
}

/** Get policy authorization for a certain public key
 *
 * @param[in]  policy The policy with the authorization.
 * @param[in]  public The public data of the key.
 * @param[out] signature The signature found in the authorization list.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 */
static TSS2_RC
get_policy_signature(
    TPMS_POLICY *policy,
    TPMT_PUBLIC *public,
    TPMT_SIGNATURE *signature)
{
    size_t i;

    for (i = 0; i < policy->policyAuthorizations->count; i++) {
        if (ifapi_TPMT_PUBLIC_cmp(public,
                                  &policy->policyAuthorizations->authorizations[i].key)) {
            *signature = policy->policyAuthorizations->authorizations[i].signature;
            return TSS2_RC_SUCCESS;
        }
    }
    /* Appropriate authorization should always exist */
    return TSS2_FAPI_RC_GENERAL_FAILURE;
}

/** Cleanup a linked list of policies.
 *
 * @param[in] The linked list.
 */
static void
cleanup_policy_list(struct POLICY_LIST * list) {
    if (list) {
        struct POLICY_LIST * branch = list;
        while (branch) {
            struct POLICY_LIST *next = branch->next;
            /* Cleanup the policy stored in the list. */
            ifapi_cleanup_policy(&branch->policy);
            SAFE_FREE(branch->path);
            SAFE_FREE(branch);
            branch = next;
        }
    }
}

/** Callback for retrieving, selecting and execute a authorized policy.
 *
 * All policies authorized by a certain key will be retrieved and one policy
 * will be selected via a branch selection callback.
 *
 * @param[in] key_public the public data of the key which was used for policy
 *            authorization.
 * @param[in] hash_alg The hash algorithm used for policy computation.
 * @param[out] digest The policy digest of the authorized policy.
 * @param[out] signature The signature produced during policy authorization.
 * @param[in] userdata The user context to retrieve the policy.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_MEMORY: if it's not possible to allocate enough memory.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE If no user data id passed or context stack
 *         is not initialized.
 * @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 policy
 *         execution fails.
 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
 *         is not set.
 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
 *         the function.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @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.
 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
 */
TSS2_RC
ifapi_exec_auth_policy(
    TPMT_PUBLIC *key_public,
    TPMI_ALG_HASH hash_alg,
    TPM2B_DIGEST *digest,
    TPMT_SIGNATURE *signature,
    void *userdata)
{
    TSS2_RC r;
    FAPI_CONTEXT *fapi_ctx = userdata;
    IFAPI_POLICY_EXEC_CTX *current_policy;
    IFAPI_POLICY_EXEC_CB_CTX *cb_ctx;
    size_t n, i;
    struct POLICY_LIST *branch;
    const char **names = NULL;
    size_t branch_idx;
    bool policy_set = false;

    return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE);
    return_if_null(fapi_ctx->policy.policyutil_stack, "Policy not initialized.",
                   TSS2_FAPI_RC_BAD_REFERENCE);

    if (fapi_ctx->policy.util_current_policy) {
        /* Use the current policy in the policy stack. */
        current_policy = fapi_ctx->policy.util_current_policy->pol_exec_ctx;
    } else {
        /* Start with the bottom of the policy stack */
        current_policy = fapi_ctx->policy.policyutil_stack->pol_exec_ctx;
    }
    cb_ctx = current_policy->app_data;

    switch (cb_ctx->cb_state) {
        statecase(cb_ctx->cb_state, POL_CB_EXECUTE_INIT)
            current_policy->object_handle = ESYS_TR_NONE;
            current_policy->policy_list = NULL;
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_SEARCH_POLICY)
            r = search_policy(fapi_ctx,
                              equal_policy_authorization, true,
                              key_public, NULL,
                              &current_policy->policy_list);
            FAPI_SYNC(r, "Search policy", cleanup);

            if (current_policy->policy_list->next) {
                /* More than one policy has to be selected via
                   callback */
                if (!fapi_ctx->callbacks.branch) {
                    return_error(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN,
                                 "No branch selection callback");
                }
                n = 1;

                /* Count policies */
                for (branch = current_policy->policy_list; branch->next;
                     branch = branch->next)
                    n += 1;
                names = malloc(sizeof(char *) * n);
                return_if_null(names, "Out of memory.", TSS2_FAPI_RC_MEMORY);
                i = 0;
                branch = current_policy->policy_list;
                /* Compute name list for slectiion callback. */
                do {
                    names[i] = branch->path;
                    i += 1;
                    branch = branch->next;
                } while (branch);

                /* Policy selection */
                r = fapi_ctx->callbacks.branch(fapi_ctx, "PolicyAuthorize",
                                               &names[0], n, &branch_idx,
                                               fapi_ctx->callbacks.branchData);
                goto_if_error(r, "policyBranchSelectionCallback", cleanup);

                if (branch_idx >= n) {
                    goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid branch number.",
                               cleanup);
                }
                /* Get policy from policy list */
                n = 0;
                branch = current_policy->policy_list;
                do {
                    if (n == branch_idx) {
                        cb_ctx->policy = &branch->policy;
                        policy_set = true;
                        break;
                    }
                    n += 1;
                    branch = branch->next;
                }  while (branch);

            } else {
                /* Only one policy found. */
                cb_ctx->policy = &current_policy->policy_list->policy;
                policy_set = true;
            }
            if (!policy_set) {
                goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Policy could not be set.",
                           cleanup);
            }
            /* Prepare policy execution */
            r = ifapi_policyutil_execute_prepare(fapi_ctx, current_policy->hash_alg,
                                                 cb_ctx->policy);
            /* Next state will switch from prev context to next context. */
            goto_if_error(r, "Prepare policy execution.", cleanup);
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_EXECUTE_SUB_POLICY)
            ESYS_TR session = current_policy->session;
            r = ifapi_policyutil_execute(fapi_ctx, &session);
            if (r == TSS2_FAPI_RC_TRY_AGAIN){
                SAFE_FREE(names);
                return r;
            }

            goto_if_error(r, "Execute policy.", cleanup);

            r = get_policy_signature(cb_ctx->policy, key_public,
                                     signature);
            goto_if_error(r, "Get authorization", cleanup);

            r = get_policy_digest(cb_ctx->policy, hash_alg, digest);
            goto_if_error(r, "Get authorization", cleanup);
            cb_ctx->cb_state = POL_CB_EXECUTE_INIT;
            break;

        statecasedefault_error(cb_ctx->state, r, cleanup);
    }
cleanup:
    SAFE_FREE(names);
    cleanup_policy_list(current_policy->policy_list);
    return r;
}

/** Callback for executing a policy identified by a digest stored in a nv object.
 *
 * @param[in] nv_public the public data of the nv object which stores the digest
 *            of the authorized policy.
 * @param[in] hash_alg The hash algorithm used for policy computation.
 * @param[in] userdata The user context to retrieve the policy.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_MEMORY: if it's not possible to allocate enough memory.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE If no user data id passed or context stack
 *         is not initialized.
 * @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 policy
 *         execution fails.
 * @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.
 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
 * @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_ESYS_RC_* possible error codes of ESAPI.
 */
TSS2_RC
ifapi_exec_auth_nv_policy(
    TPM2B_NV_PUBLIC *nv_public,
    TPMI_ALG_HASH hash_alg,
    void *userdata)
{
    TSS2_RC r;
    TPM2B_MAX_NV_BUFFER *aux_data;
    FAPI_CONTEXT *fapi_ctx = userdata;
    IFAPI_POLICY_EXEC_CTX *current_policy;
    IFAPI_POLICY_EXEC_CB_CTX *cb_ctx;
    char *nv_path = NULL;
    ESYS_CONTEXT *esys_ctx;
    size_t digest_size, offset;
    TPMT_HA nv_policy;

    return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE);
    return_if_null(fapi_ctx->policy.policyutil_stack, "Policy not initialized.",
                   TSS2_FAPI_RC_BAD_REFERENCE);

    if (fapi_ctx->policy.util_current_policy) {
        /* Use the current policy in the policy stack. */
        current_policy = fapi_ctx->policy.util_current_policy->pol_exec_ctx;
    } else {
        /* Start with the bottom of the policy stack */
        current_policy = fapi_ctx->policy.policyutil_stack->pol_exec_ctx;
    }
    cb_ctx = current_policy->app_data;
    esys_ctx = fapi_ctx->esys;

    if (!(digest_size = ifapi_hash_get_digest_size(hash_alg))) {
        return_error2(TSS2_FAPI_RC_BAD_VALUE,
                      "Unsupported hash algorithm (%" PRIu16 ")", hash_alg);
    }

    switch (cb_ctx->cb_state) {
        statecase(cb_ctx->cb_state, POL_CB_EXECUTE_INIT)
            /* Search a NV object with a certain NV indext stored in nv_public. */
            r = ifapi_keystore_search_nv_obj(&fapi_ctx->keystore, &fapi_ctx->io,
                                             nv_public, &nv_path);
            FAPI_SYNC(r, "Search Object", cleanup);

            r = ifapi_keystore_load_async(&fapi_ctx->keystore, &fapi_ctx->io, nv_path);
            SAFE_FREE(nv_path);
            return_if_error2(r, "Could not open: %s", nv_path);

            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_NV_READ)
            /* Get object from file */
            r = ifapi_keystore_load_finish(&fapi_ctx->keystore, &fapi_ctx->io,
                                           &cb_ctx->object);
            return_try_again(r);
            return_if_error(r, "read_finish failed");

            r = ifapi_initialize_object(esys_ctx, &cb_ctx->object);
            goto_if_error(r, "Initialize NV object", cleanup);

            current_policy->nv_index = cb_ctx->object.handle;
            ifapi_cleanup_ifapi_object(&cb_ctx->object);
            get_nv_auth_object(&cb_ctx->object,
                               current_policy->nv_index,
                               &current_policy->auth_object,
                               &current_policy->auth_handle);
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_AUTHORIZE_OBJECT)
            /* Authorize the NV object with the corresponding auth object. */
            r = ifapi_authorize_object(fapi_ctx, &cb_ctx->auth_object, &cb_ctx->session);
            return_try_again(r);
            goto_if_error(r, "Authorize  object.", cleanup);

            /* Prepare the reading of the NV index from TPM. */
            r = Esys_NV_Read_Async(esys_ctx,
                            current_policy->auth_handle, current_policy->nv_index,
                            cb_ctx->session, ESYS_TR_NONE, ESYS_TR_NONE,
                            sizeof(TPMI_ALG_HASH) + digest_size, 0);
            goto_if_error(r, "Unmarshal policy", cleanup);
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_READ_NV_POLICY)
            /* Finalize the reading. */
            r = Esys_NV_Read_Finish(esys_ctx, &aux_data);
            try_again_or_error_goto(r, "NV read", cleanup);

            offset = 0;
            r = Tss2_MU_TPMT_HA_Unmarshal(&aux_data->buffer[0], aux_data->size,
                                          &offset, &nv_policy);
            Esys_Free(aux_data);
            goto_if_error(r, "Unmarshal policy", cleanup);

            cb_ctx->policy_digest.size = digest_size;
            memcpy(&cb_ctx->policy_digest.buffer[0], &nv_policy.digest, digest_size);
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_SEARCH_POLICY)
            /* Search policy appropriate in object store */
            r = search_policy(fapi_ctx, compare_policy_digest, false,
                              &cb_ctx->policy_digest, &hash_alg,
                              &current_policy->policy_list);
            FAPI_SYNC(r, "Search policy", cleanup);

            if (!current_policy->policy_list) {
                goto_error(r, TSS2_FAPI_RC_POLICY_UNKNOWN, "Policy not found", cleanup);
            }
            /* Prepare policy execution */
            r = ifapi_policyutil_execute_prepare(fapi_ctx, current_policy->hash_alg,
                                                 &current_policy->policy_list->policy);
            return_if_error(r, "Prepare policy execution.");
            fallthrough;

        statecase(cb_ctx->cb_state, POL_CB_EXECUTE_SUB_POLICY)
            ESYS_TR session = current_policy->session;
            r = ifapi_policyutil_execute(fapi_ctx, &session);
            if (r == TSS2_FAPI_RC_TRY_AGAIN)
                return r;

            goto_if_error(r, "Execute policy.", cleanup);
            cb_ctx->cb_state = POL_CB_EXECUTE_INIT;
            break;

        statecasedefault_error(cb_ctx->state, r, cleanup);
    }
cleanup:
    cleanup_policy_list(current_policy->policy_list);
    SAFE_FREE(nv_path);
    return r;

}

/** Callback for getting the name of a key to be duplicated.
 *
 * @param[out] name the name of the object to be duplicated.
 * @param[in] userdata The user context to retrieve the key.
 * @retval TSS2_RC_SUCCESS on success.
 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if the context is not passed or the
 *         object to be duplicated is not set.
 */
TSS2_RC
ifapi_get_duplicate_name(
    TPM2B_NAME *name,
    void *userdata)
{
    FAPI_CONTEXT *fapi_ctx = userdata;

    return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE);
    return_if_null(fapi_ctx->duplicate_key, "Object for duplication no set.",
                   TSS2_FAPI_RC_BAD_REFERENCE);
    *name = fapi_ctx->duplicate_key->misc.key.name;
    return TSS2_RC_SUCCESS;
}
