/*!
 * In Samsung Ukraine R&D Center (SRK) under a contract between
 * LLC "Samsung Electronics Ukraine Company" (Kyiv, Ukraine)
 * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
 * Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved.
 *
 * Created on: Feb 27, 2018
 * Author: Pavlo Marusyk <p.marusik@samsung.com>
 */

#include "tz_proca_handler.h"
#include "wsm_log.h"

#if (TEEGRIS_SDK_VER_MAJOR >= 4)
    #include "tees_extension.h"
#endif /* if (TEEGRIS_SDK_VER_MAJOR >= 4) */

#if (PROCA_FEATURE_ENABLED == WSM_OPTION_ENABLED)
#if defined(BEFORE_QUEEN_BRANCH_SETUP)
static const char *process_name = "/vendor/bin/vendor.samsung.security.wsm@1.0-service";
#else
static const char *process_name = "/vendor/bin/vendor.samsung.hardware.security.wsm@1.0-service";
#endif /* if defined(BEFORE_QUEEN_BRANCH_SETUP) */
#endif /* if (PROCA_FEATURE_ENABLED == WSM_OPTION_ENABLED) */

static return_t proca_tz_auth_with_rules()
{
#if (PROCA_FEATURE_ENABLED == WSM_OPTION_ENABLED)
#if (TEEGRIS_SDK_VER_MAJOR >= 4)
    PaTzResult pa_result;
    PaHandler pa_handler;
    TEE_Result tee_res;
    struct TEES_ClientCredentials creds;

    if ((tee_res = TEES_GetClientCredentials(&creds)) != TEE_SUCCESS)
    {
        WSM_LOG_E(LOG_TAG, tee_res,
                  "PROCA: OpenSession: TEES_GetClientCredentials() failed with %#x\n", __func__,
                  tee_res);
        return tee_res;
    }
    int pid = creds.pid;

    PaTzRules rules = PaTzRulesCreate();
    pa_result = PaTzRulesAddProcessName(rules, process_name);
    if (PA_TZ_SUCCESS != pa_result)
    {
        PaTzRulesDestroy(rules);
        WSM_LOG_E(LOG_TAG, WSM_ERR_PROCA_AUTH_SWD,
                  "[%s], PROCA: rules add fail\n", __func__);
        return get_proca_error(pa_result);
    }

    pa_result = PaTzHandlerCreateFromPid(pid, &pa_handler);
    if (PA_TZ_SUCCESS != pa_result)
    {
        PaTzRulesDestroy(rules);
        WSM_LOG_E(LOG_TAG, WSM_ERR_PROCA_AUTH_SWD,
                  "[%s], PROCA: handler create fail\n", __func__);
        return get_proca_error(pa_result);
    }

    pa_result = PaTzAuthenticateWithRules(pa_handler, rules, NULL);
    if (PA_TZ_PROCA_NOT_SUPPORTED == pa_result)
    {
        PaTzRulesDestroy(rules);
        WSM_LOG_E(LOG_TAG, WSM_ERR_PROCA_AUTH_SWD,
                  "[%s], PROCA: WSM DAEMON TZ authentication for TEE skip\n", __func__);
        return get_proca_error(pa_result);
    }
    else if (PA_TZ_SUCCESS != pa_result)
    {
        PaTzRulesDestroy(rules);
        WSM_LOG_E(LOG_TAG, WSM_ERR_PROCA_AUTH_SWD,
                  "[%s], PROCA: WSM DAEMON Authentication fail (0x%08x)\n", __func__, pa_result);
        return get_proca_error(pa_result);
    }

    PaTzRulesDestroy(rules);
    WSM_LOG(err_level_info, LOG_TAG, "PROCA: WSM DAEMON TZ authentication for TEE successful\n");

#endif /* if (TEEGRIS_SDK_VER_MAJOR >= 4) */
#endif /* if (PROCA_FEATURE_ENABLED == WSM_OPTION_ENABLED) */
    return WSM_RET_SUC;
}

static return_t proca_tz_auth_by_name()
{
    return_t res = WSM_RET_SUC;

#if (PROCA_FEATURE_ENABLED == WSM_OPTION_ENABLED)
    PaTzResult pa_result;
    PaHandler pa_handler;

    pa_result = PaTzHandlerCreateFromProcessName(process_name, &pa_handler);
    if (PA_TZ_SUCCESS != pa_result)
    {
        WSM_LOG_E(LOG_TAG, WSM_ERR_PROCA_AUTH_SWD,
                  "[%s], PROCA: tz handler create fail\n", __func__);
        return get_proca_error(pa_result);
    }

    pa_result = PaTzAuthenticate(pa_handler);
    if (PA_TZ_PROCA_NOT_SUPPORTED == pa_result)
    {
        WSM_LOG(err_level_info, LOG_TAG, "PROCA: WSM DAEMON TZ authentication skip\n");
    }
    else
    {
        if (PA_TZ_SUCCESS != pa_result)
        {
            // Authentication is failed
            WSM_LOG_E(LOG_TAG, WSM_ERR_PROCA_AUTH_SWD,
                      "[%s], PROCA: WSM DAEMON Authentication fail (0x%08x)\n", __func__,
                      pa_result);
            res = get_proca_error(pa_result);
        }
        else
        {
            WSM_LOG(err_level_info, LOG_TAG, "PROCA: WSM DAEMON TZ authentication successful\n");
        }
    }
#endif /* if (PROCA_FEATURE_ENABLED == WSM_OPTION_ENABLED) */

    return res;
}

return_t proca_tz_auth(void)
{
    return_t res_rules = WSM_RET_SUC;
    return_t res_by_name = WSM_RET_SUC;

    res_rules = proca_tz_auth_with_rules();

    res_by_name = proca_tz_auth_by_name();

    return (res_rules == WSM_RET_SUC) ? res_by_name : res_rules;
}

return_t get_proca_error(PaTzResult proca_error)
{
    (void)proca_error;

    return_t res = WSM_RET_SUC;
#if (PROCA_FEATURE_ENABLED == WSM_OPTION_ENABLED)

    switch (proca_error)
    {
        case PA_TZ_GENERAL_ERROR:
            res = WSM_ERR_PROCA_GENERAL_ERROR;
            break;

        case PA_TZ_INVALID_HANDLER:
            res = WSM_ERR_PROCA_INVALID_HANDLER;
            break;

        case PA_TZ_DRIVER_COMM_ERROR:
            res = WSM_ERR_PROCA_DRIVER_COMM_ERROR;
            break;

        case PA_TZ_MEMORY_PERM_ERROR:
            res = WSM_ERR_PROCA_MEMORY_PERM_ERROR;
            break;

        case PA_TZ_VIRT_TO_PHYS_ERROR:
            res = WSM_ERR_PROCA_VIRT_TO_PHYS_ERROR;
            break;

        case PA_TZ_AUTHENTICATION_FAILED:
            res = WSM_ERR_PROCA_AUTHENTICATION_FAILED;
            break;

        case PA_TZ_AF_TASK_IS_NOT_FOUND:
            res = WSM_ERR_PROCA_AF_TASK_IS_NOT_FOUND;
            break;

        case PA_TZ_AF_INTEGRITY_IS_NONE:
            res = WSM_ERR_PROCA_AF_INTEGRITY_IS_NONE;
            break;

        case PA_TZ_AF_INTEGRITY_IS_NOT_READY:
            res = WSM_ERR_PROCA_AF_INTEGRITY_IS_NOT_READY;
            break;

        case PA_TZ_AF_CERTIFICATE_IS_ABSENT:
            res = WSM_ERR_PROCA_AF_CERTIFICATE_IS_ABSENT;
            break;

        case PA_TZ_AF_CERTIFICATE_IS_INCORRECT:
            res = WSM_ERR_PROCA_AF_CERTIFICATE_IS_INCORRECT;
            break;

        case PA_TZ_AF_CERTIFICATE_IS_NOT_MATCH:
            res = WSM_ERR_PROCA_AF_CERTIFICATE_IS_NOT_MATCH;
            break;

        case PA_TZ_AF_APPNAME_IS_INCORRECT:
            res = WSM_ERR_PROCA_AF_APPNAME_IS_INCORRECT;
            break;

        case PA_TZ_AF_TASK_HAS_DUPLICATE:
            res = WSM_ERR_PROCA_AF_TASK_HAS_DUPLICATE;
            break;

        case PA_TZ_ENCODE_AUTHENTICATION_FAILED:
            res = WSM_ERR_PROCA_ENCODE_AUTHENTICATION_FAILED;
            break;

        case PA_TZ_DECODE_AUTHENTICATION_FAILED:
            res = WSM_ERR_PROCA_DECODE_AUTHENTICATION_FAILED;
            break;

        case PA_TZ_NON_SUPPORTED:
            res = WSM_ERR_PROCA_NON_SUPPORTED;
            break;

        case PA_TZ_CALLER_IS_FORBIDEN:
            res = WSM_ERR_PROCA_CALLER_IS_FORBIDEN;
            break;

        case PA_TZ_INCOMPATIBLE_RULES:
            res = WSM_ERR_PROCA_INCOMPATIBLE_RULES;
            break;

        case PA_TZ_CRYPTO_ERRORS:
            res = WSM_ERR_PROCA_CRYPTO_ERRORS;
            break;

        case PA_TZ_SINGATURE_VALIDATION_FAILURE:
            res = WSM_ERR_PROCA_SINGATURE_VALIDATION_FAILURE;
            break;

        default:
            res = WSM_ERR_PROCA_NOT_DEFINED;
            break;
    }
#endif /* if (PROCA_FEATURE_ENABLED == WSM_OPTION_ENABLED) */
    return res;
}
