/**
 * \file deleg_wrap_unwrap.c
 * \brief Delegated wrap unwrap functionality.
 * \author Sergey Sidorov (s.sidorov@samsung.com)
 * \author Vladyslav Figol (v.figol@samsung.com)
 * \version 0.1
 * \date Created June 11, 2015
 * \par In Samsung Ukraine R&D Center (SURC) under a contract between
 * \par LLC "Samsung Electronics Ukraine Company" (Kyiv, Ukraine) and
 * \par "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
 * \par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
 */

#include "wsm_log.h"
#include "wsm_types.h"

#if defined(MOBICORE)

    #include "tlStd.h"
    #include "TlApi/TlApi.h"

    #define  UNWRAP_FLAGS (TLAPI_UNWRAP_PERMIT_DELEGATED |  \
                           TLAPI_UNWRAP_PERMIT_CONTEXT_TL | \
                           TLAPI_UNWRAP_PERMIT_CONTEXT_SP | \
                           TLAPI_UNWRAP_PERMIT_CONTEXT_DEVICE)

// WSM MC TA name
static const uint8_t SRC_TA_NAME[] =
{ 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30 };

#elif defined(QSEE)
    #include "com_dtypes.h"
    #include "qsee_message.h"

#if defined(qsee_sm8150)
    #define QSEE_OEM_TA_HASH "9361A53CBE05BDDEEEF7DDCD9E0D2AD472FF0A1AEE5F3EE4C7E416162273D237"
#elif defined(qsee_sm6150)
    #define QSEE_OEM_TA_HASH "FEF4EFBC5D6689C2939E7C410088053F102F5FB4D7B7319C3144BD79E5464654"
#elif defined(qsee_sdm710)
    #define QSEE_OEM_TA_HASH "C6A1B1F001AA41325C471DEFBB767B962D381CF0FE7224E73EC4305C0C848736"
#elif defined(qsee_sm8250)
    #define QSEE_OEM_TA_HASH "288717EFA81760C347CDB3A0CA23723C92AC2EE97AD36A7BB9EE3EEE76678BEA" // SM8250_LA10
#elif defined(qsee_sm7250)
    #define QSEE_OEM_TA_HASH "5F9C8380E961E155915D29DE377BBED27FDA4E89F64C7BBB403CF3B8070C3BB0"
#elif defined(qsee_sm7125)
    #define QSEE_OEM_TA_HASH "CFB65A86E7474BFBC56E4073CD90B00894FA7658A5DD3F09C6C9802EBF4B2BF5"
#elif defined(qsee_sm7225)
    #define QSEE_OEM_TA_HASH "1719D77DE33667DC1D31FE8C99668CB2CCE585C44C6EDADBA57573052430BEE4"
#else
    #define QSEE_OEM_TA_HASH ""
#endif /* if defined(qsee_sm8150) */

#if defined(USE_ALT_ROT_NAME)
    #include "qsee_cfg_prop.h"
#endif /* if defined(USE_ALT_ROT_NAME) */

    #define SRC_TA_NAME     "wsm"

//  Must leave at least 144 extra bytes for the header and MAC,
//  as per the API spec.
//  > Checked on SM-R730S device. 116 is enough.
    #define HEADER_AND_MAC_LEN 144
    #define MAX_DISTNAME_PREFIX_SZ 128
    #define MAX_TANAME_SZ 32
    #define MAX_FULLNAME_SZ 160

    #ifndef QSEE_OEM_TA_HASH
    #define QSEE_OEM_TA_HASH "nope"
    #endif /* ifndef QSEE_OEM_TA_HASH */

void get_alt_rot_distname(char *i_appname, char *o_distname)
{
#if defined(USE_ALT_ROT_NAME)

    const char *prop_name = "alt_rot_domain_name_dot";
    uint32_t ret_size = 0;
    size_t len = 0;
    qsee_cfg_propvar_t *ptr = NULL;

    uint32_t prop[2 + (MAX_DISTNAME_PREFIX_SZ / sizeof(uint32_t))] = { 0 };
    char distname_prefix[MAX_DISTNAME_PREFIX_SZ] = { 0 };

    // Possible return code of qsee_cfg_getpropval:
    // QSEE_CFG_SUCCESS                   = 0x00,
    // QSEE_CFG_INVALID_INPUT             = 0x01,
    // QSEE_CFG_BUFFER_TOO_SMALL          = 0x02,
    // QSEE_CFG_ERROR_GET_PROP_HDLSTR     = 0x03,
    // QSEE_CFG_ERROR_GET_PROP_VALUE      = 0x04,
    // QSEE_CFG_ERROR_MEMSCPY             = 0x05,
    // QSEE_CFG_UNKNOWN_TYPE              = 0x06,

    // first try: getting officially
    uint32_t getp_ret = qsee_cfg_getpropval(prop_name, strlen(prop_name) + 1,
                                            0, (qsee_cfg_propvar_t *)&prop, sizeof(prop),
                                            &ret_size);
    if (QSEE_CFG_SUCCESS != getp_ret)
    {
        WSM_LOG_EN(LOG_TAG, "[alt name] = %d\n", getp_ret);

        // second try: getting alt name from LOCAL SDK
        WSM_LOG(err_level_info, LOG_TAG, "[%s] getting alt name from LOCAL SDK hash \n", __func__);

        const char QSEE_OEM_TA_VALUE[] = "alt."QSEE_OEM_TA_HASH ".";
        strlcpy(o_distname, QSEE_OEM_TA_VALUE, MAX_DISTNAME_PREFIX_SZ);
        strlcat(o_distname, i_appname, MAX_FULLNAME_SZ);

        return;
    }

    ptr = (qsee_cfg_propvar_t *)prop;
    len = ret_size - sizeof(*ptr) + 2 * sizeof(ptr->val) + 1;

    if (len > sizeof(distname_prefix))
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_WRAP_DELEGATED,
                  "[%s] QSEE Wrap alt_rot_domain_name_dot len invalid\n", __func__);
        strlcpy(o_distname, i_appname, MAX_TANAME_SZ);
        return;
    }

    memcpy(distname_prefix, &ptr->val[1], len - 1);
    distname_prefix[len - 1] = '\0';

    strlcpy(o_distname, distname_prefix, MAX_DISTNAME_PREFIX_SZ);
    strlcat(o_distname, i_appname, MAX_FULLNAME_SZ);
#else
    strlcat(o_distname, i_appname, MAX_FULLNAME_SZ);
#endif /* if defined(USE_ALT_ROT_NAME) */
}

#elif defined(GP_API)

    #include <tee_internal_api.h>
    #include <tees_secure_object.h>
    #define TA_PROP_GROUP_ID  "samsung_ta"

#elif defined(WSM_HOST)

// Do nothing here for WSM_HOST, but avoid warning

#else


    #error Error: target TZ not DEFINED !!!

#endif /* if defined(MOBICORE) */

#if defined(GP_API)

#include <stdio.h>

#include <stdlib.h>


///////////////////////////////////////////////////////////////////////////////////
//
// TODO: Below code was added temporary to parse TEEC_UUID since BF SDK sscanf()
//       defined in it's stdio.h not visible. It will be removed when BF SDK will
//       be updated
//
//       Below implementation was copied 'as is' since BF SDK do not provide stroul(), etc.
//
///////////////////////////////////////////////////////////////////////////////////

#include <limits.h>
#include <errno.h>

static inline int isdigit(int c)
{
    return c >= '0' && c <= '9';
}

static inline int isxdigit(int c)
{
    return isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}

static int strtoul_safe(const char *str, char **endptr, int base, unsigned long *out_val)
{
    if (!out_val)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_NULL_POINTER, "%s(): Error: invalid input parameter (NULL).\n",
                  __func__);
        return WSM_RET_E_NULL_POINTER;
    }

    char *eptr;

    errno = 0;
    unsigned long val = strtoul(str, &eptr, base);

    if (errno == EINVAL && val == 0)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_LENGTH, "%s(): Error: no conversion could be performed. "
                                             "errno=%d, ret val=%lu\n", __func__, errno, val);
        return WSM_RET_E_LENGTH;
    }

    if (errno == EINVAL)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_EINVAL,
                  "%s(): Error: base is not supported. errno=%d, base=%d\n",
                  __func__, errno, base);
        return WSM_RET_E_EINVAL;
    }

    if (errno == ERANGE && val == ULONG_MAX)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_MAX_LIMIT_REACHED,
                  "%s(): Error: value is outside the range of representable values. errno=%d, ret val=%lu\n",
                  __func__, errno, val);
        return WSM_RET_E_MAX_LIMIT_REACHED;
    }

    if (errno != 0)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_UNKNOWN, "%s(): Error: unknown. errno=%d\n", __func__, errno);
        return WSM_RET_E_UNKNOWN;
    }

    if (eptr == str)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_STRING_CONVERTION, "%s(): Error: no conversion is performed\n",
                  __func__);
        return -1;
    }

    /* If we got here, strtoul() successfully parsed a number */
    if (endptr != NULL)
    {
        *endptr = eptr;
    }

    *out_val = val;

    return WSM_RET_SUC;
}

static int string_to_uuid(const char *in, TEEC_UUID *uu)
{
    WSM_LOG(err_level_info, LOG_TAG, "[%s] Entry \n", __func__);

    enum { CLOCK_SEQ_AND_NODE_SZ = 8 };
    enum {
        TIMELOW_DASH_IDX  = 8,
        TIMEMID_DASH_IDX  = 13,
        TIMEHI_DASH_IDX   = 18,
        CLOCKSEQ_DASH_IDX = 23,
    };
    int c;
    int ret;
    const char *substr;

    if (in == NULL)
    {
        return -1;
    }

    if (uu == NULL)
    {
        return -1;
    }

    if (strlen(in) != (UUID_STRING_LEN - 1))
    {
        return -1;
    }

    /* Full checking of input string:
     * - 'in' string should consist of hex values from 0x0 to 0xf
     * - dashes should be on the proper places in the 'in' string */
    for (size_t i = 0; in[i] != '\0'; ++i)
    {
        c = in[i];
        if (i == TIMELOW_DASH_IDX
            || i == TIMEMID_DASH_IDX
            || i == TIMEHI_DASH_IDX
            || i == CLOCKSEQ_DASH_IDX)
        {
            if (c == '-')
            {
                continue;
            }
            else
            {
                return -1;
            }
        }
        if (!isxdigit(c))
        {
            return -1;
        }
    }

    unsigned long val;
    ret = strtoul_safe(in, NULL, 16, &val);
    if (WSM_RET_SUC != ret)
    {
        return -1;
    }
    uu->timeLow = val;

    ret = strtoul_safe(in + TIMELOW_DASH_IDX + 1, NULL, 16, &val);
    if (WSM_RET_SUC != ret)
    {
        return -1;
    }
    uu->timeMid = val;

    ret = strtoul_safe(in + TIMEMID_DASH_IDX + 1, NULL, 16, &val);
    if (WSM_RET_SUC != ret)
    {
        return -1;
    }
    uu->timeHiAndVersion = val;

    substr = in + TIMEHI_DASH_IDX + 1;
    char buf[3] = { 0 };

    for (size_t i = 0; i < CLOCK_SEQ_AND_NODE_SZ; ++i, substr++)
    {
        unsigned long tmp_val;
        if (i == 2 && *substr == '-')
        {
            /* step over last dash */
            substr++;
        }
        buf[0] = *substr;
        substr++;
        buf[1] = *substr;

        ret = strtoul_safe(buf, NULL, 16, &tmp_val);
        if (WSM_RET_SUC != ret)
        {
            return -1;
        }
        uu->clockSeqAndNode[i] = tmp_val;
    }

    return 0;
}

#if 0

///////////////////////////////////////////////////////////////////////////////////
//
// TODO: Below code will be uncommented when BF SDK will add visibility to sscanf()
//       defined in it's stdio.h
//
///////////////////////////////////////////////////////////////////////////////////


#define UUID_SSCANF_RES_SUCCESS 11

static bool string_to_uuid(const char *buff, TEEC_UUID *uuid)
{
    WSM_LOG(err_level_info, LOG_TAG, "[%s] Entry \n", __func__);

    if ((NULL == buff) || (NULL == uuid))
    {
        WSM_LOG(err_level_info, LOG_TAG, "[%s] Bad parameters \n", __func__);
        return false;
    }

    // ffffffff-0000-0000-0000-000000000030

    // ffffffff timeLow
    // 0000     timeMid
    // 0000     timeHiAndVersion
    // 00       clockSeqAndNode[0]
    // 00       clockSeqAndNode[1]
    // 00       clockSeqAndNode[2]
    // 00       clockSeqAndNode[3]
    // 00       clockSeqAndNode[4]
    // 00       clockSeqAndNode[5]
    // 00       clockSeqAndNode[6]
    // 30       clockSeqAndNode[7]

    const char uuid_format[]
        = "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx";

    int res = sscanf(buff,
                     uuid_format,
                     &uuid->timeLow,
                     &uuid->timeMid,
                     &uuid->timeHiAndVersion,
                     &uuid->clockSeqAndNode[0],
                     &uuid->clockSeqAndNode[1],
                     &uuid->clockSeqAndNode[2],
                     &uuid->clockSeqAndNode[3],
                     &uuid->clockSeqAndNode[4],
                     &uuid->clockSeqAndNode[5],
                     &uuid->clockSeqAndNode[6],
                     &uuid->clockSeqAndNode[7]);

    return UUID_SSCANF_RES_SUCCESS == res;
}

#endif /* if 0 */

#endif // GP_API

return_t wsm_delegatedWrap(uint8_t *plain_data, uint32_t plain_len, uint8_t *ta_name,
                           uint32_t ta_name_len, uint8_t *wrapped_data, uint32_t *wrapped_data_len)
{
    return_t ret = WSM_RET_SUC;

    WSM_LOG(err_level_info, LOG_TAG, "[%s] Entry \n", __func__);
    WSM_LOG_HEX(err_level_debug, LOG_TAG, "Target TA = ", (const char *)ta_name, ta_name_len);

    ASSERT(plain_data);
    ASSERT(wrapped_data);
    ASSERT(wrapped_data_len);
    ASSERT(plain_len);
    ASSERT(ta_name);
    ASSERT(ta_name_len < TA_NAME_LEN);
    ASSERT(ta_name_len != 0);

    WSM_LOG(err_level_debug, LOG_TAG, "[%s] Wrap input %p %d %p %d\n",
            __func__,
            plain_data,
            plain_len,
            wrapped_data,
            *wrapped_data_len);
    WSM_LOG_HEX(err_level_debug, LOG_TAG, "Plain data = ", (const char *)&plain_data[0], plain_len);

    ta_name[ta_name_len] = '\0'; // terminate array by 0

#if defined(MOBICORE)

    size_t              soLength = MC_SO_SIZE(0, plain_len);
    uint32_t            temp_len = 0;
    uint8_t             *temp_wrapped = 0;
    tlApiSpTrustletId_t consumer;

    memset(&consumer, 0x00, sizeof(consumer));

    int sscanf_result = sscanf((char *)ta_name,
                               "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%hhx",
                               &consumer.uuid.value[0],  &consumer.uuid.value[1],
                               &consumer.uuid.value[2],  &consumer.uuid.value[3],
                               &consumer.uuid.value[4],  &consumer.uuid.value[5],
                               &consumer.uuid.value[6],  &consumer.uuid.value[7],
                               &consumer.uuid.value[8],  &consumer.uuid.value[9],
                               &consumer.uuid.value[10], &consumer.uuid.value[11],
                               &consumer.uuid.value[12], &consumer.uuid.value[13],
                               &consumer.uuid.value[14], &consumer.uuid.value[15]);

    WSM_LOG_HEX(err_level_debug, LOG_TAG, "delegatedWrap UUID:", (const char *)consumer.uuid.value,
                sizeof(mcUuid_t));

    if (sscanf_result != UUID_LENGTH)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_LENGTH_MISMATCH, "[%s] sscanf recognition error %d\n",
                  __func__, ret);
        return WSM_RET_E_GENERAL_ERROR;
    }

    consumer.spid =  MC_SPID_SYSTEM;

    temp_wrapped = (uint8_t *)((((int)wrapped_data + 3) / 4) * 4); // get aligned dst pointer
    temp_len = *wrapped_data_len - (uint32_t)(temp_wrapped - wrapped_data); // get aligned available dst buffer len

    if (soLength > temp_len)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_LENGTH_MISMATCH,
                  "[%s] Output buffer doesnt have enough length \n",
                  __func__);
        return WSM_RET_E_BAD_PARAM;
    }

    int wr = tlApiWrapObject(plain_data,
                             0,
                             plain_len,
                             temp_wrapped,
                             &temp_len,
                             MC_SO_CONTEXT_TLT,
                             MC_SO_LIFETIME_PERMANENT,
                             &consumer,
                             TLAPI_WRAP_DEFAULT);
    if (wr != TLAPI_OK)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_WRAP, "[%s] MC Wrap error %d\n", __func__, wr);
        return WSM_RET_E_WRAP_DELEGATED;
    }

    memmove(wrapped_data, temp_wrapped, temp_len);
    *wrapped_data_len = soLength;

    ret = WSM_RET_SUC;
#endif /* if defined(MOBICORE) */

#if defined(QSEE)

    if (plain_len + HEADER_AND_MAC_LEN > *wrapped_data_len)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_LENGTH_MISMATCH,
                  "[%s]  wrapped_data_len is too small. Must be at least plain_len + 144 (HEADER_AND_MAC_LEN).\nplain_len = %d, wrapped_data_len = %d\n", __func__, plain_len,
                  *wrapped_data_len);
        return WSM_RET_E_TZ_RELATED;
    }

    // Generate TA name OEM TA or TA Fuse depend of device model
    char oem_ta_name[256];
    const size_t oem_ta_name_size = sizeof(oem_ta_name);
    memset(oem_ta_name, 0, oem_ta_name_size);

    get_alt_rot_distname((char *)ta_name, oem_ta_name);

    WSM_LOG(err_level_info, LOG_TAG, "[%s] Target TA(OEM/FUSE) = |%s|\n", __func__, oem_ta_name);

    // TODO: here warning, only for qsee_msm8x26
    // Warning:  #167-D: argument of type "uint32" is incompatible with parameter of type "uint32 *" (uint32)wrapped_data_len);
    int qsee_res = qsee_encapsulate_inter_app_message((char *)oem_ta_name,
                                                      plain_data,
                                                      plain_len,
                                                      wrapped_data,
                                                      wrapped_data_len);
    if (qsee_res != 0)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_WRAP_DELEGATED, "[%s] QSEE Wrap error %d\n",
                  __func__, qsee_res);
        return WSM_RET_E_WRAP_DELEGATED;
    }

    ret = WSM_RET_SUC;
#endif /* if defined(QSEE) */

#if defined(GP_API)

    SO_AccessControlInfoType ac_info;

    TEE_MemFill(&ac_info, 0, sizeof ac_info);

    ac_info.access_flags = DELEGATED_TA_ID_AC; // 0x4 - wrap walid for only for specific TA defined in ta_id field
    #if TEEGRIS_SDK_VER_MAJOR >= 4
    // http://mosaic.sec.samsung.net/kms/comty.do?comtyId=3012074&menuId=3012077&postId=369221257&page=view&type=LIST
    // DELEGATED_TA_ID_AC 할 때 target의 authority 추가 필요
    // You should designate authority and uuid together, if object is created by DELEGATED_TA_ID_AC.
    // SO_AccessControlInfoType ac_info;
    // TEEC_UUID uuid = (TEEC_UUID)TA_PROP_UUID_SECURE_OBJECT_2;
    // memset(&ac_info, 0, sizeof ac_info);
    // memcpy(&ac_info.ta_id, &uuid, sizeof(TEEC_UUID));
    // memcpy(&ac_info.auth_id, samsung_ta, strlen(samsung_ta));
    // ac_info.access_flags = DELEGATED_TA_ID_AC;

    TEE_MemMove(&ac_info.auth_id, TA_PROP_GROUP_ID, strlen(TA_PROP_GROUP_ID));
    #endif /* if TEEGRIS_SDK_VER_MAJOR >= 4 */

    if ((UUID_STRING_LEN - 1) != ta_name_len)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_LENGTH_MISMATCH, "[%s] Wrong TA name len \n", __func__);
        return WSM_RET_E_BAD_PARAM;
    }

    // TODO update after sscanf will be avaliable in BF SDK
    // if ( !string_to_uuid((const char *)ta_name, (TEEC_UUID *)&ac_info.ta_id) )
    if (-1 == string_to_uuid((const char *)ta_name, (TEEC_UUID *)&ac_info.ta_id))
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_CONVERTION, "[%s] string_to_uuid FAIL \n", __func__);
        return WSM_RET_E_BAD_PARAM;
    }


    TEE_Result  res = TEES_WrapSecureObject(plain_data,
                                            plain_len,
                                            wrapped_data,
                                            wrapped_data_len,
                                            &ac_info);
    if (res != TEE_SUCCESS)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_WRAP_DELEGATED, "[%s] BF Wrap error %d\n", __func__, res);
        return WSM_RET_E_WRAP_DELEGATED;
    }

    ret = WSM_RET_SUC;

#endif /* if defined(GP_API) */

    WSM_LOG_HEX(err_level_debug, LOG_TAG, " wrapped data =  ", (const char *)wrapped_data,
                *wrapped_data_len);

    return ret;
}

return_t wsm_delegatedUnWrap(uint8_t *wrapped_data, uint32_t wrapped_data_len, uint8_t *plain_data,
                             uint32_t *plain_len, uint8_t *ta_name, uint32_t ta_name_len)
{
    return_t ret = WSM_RET_E_GENERAL_ERROR;

    WSM_LOG(err_level_info, LOG_TAG, "[%s] Entry \n", __func__);
    WSM_LOG_HEX(err_level_debug, LOG_TAG, " wrapped data to be unwrapped =  ",
                (const char *)wrapped_data, wrapped_data_len);

    ASSERT(plain_data);
    ASSERT(wrapped_data);
    ASSERT(wrapped_data_len);
    ASSERT(plain_len);
    ASSERT(ta_name_len < TA_NAME_LEN);

    ta_name[ta_name_len] = '\0'; // terminate array by 0
#if defined(MOBICORE)

    if (MC_SO_SIZE(0, *plain_len) < wrapped_data_len)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_LENGTH_MISMATCH,
                  "[%s] Output buffer doesnt have enough length \n", __func__);
        return WSM_RET_E_GENERAL_ERROR;
    }

    mcSoHeader_t *hdr = (mcSoHeader_t *)wrapped_data;
    if (sizeof(SRC_TA_NAME) != UUID_LENGTH ||
        memcmp(SRC_TA_NAME, hdr->producer.uuid.value, UUID_LENGTH))
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_UNWRAP, "[%s] Wrong producer name \n", __func__);
        return WSM_RET_E_GENERAL_ERROR;
    }

    int uwr = tlApiUnwrapObject(wrapped_data,
                                wrapped_data_len,
                                plain_data,
                                plain_len,
                                UNWRAP_FLAGS);
    if (uwr != TLAPI_OK)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_UNWRAP_DELEGATED, "[%s] MC UnWrap error %d\n", __func__, uwr);
        return WSM_RET_E_UNWRAP_DELEGATED;
    }

    ret = WSM_RET_SUC;
#endif /* if defined(MOBICORE) */

#if defined(QSEE)

    uint8_t src_app_name[256] = { 0 };

    if ((strlen(SRC_TA_NAME) + 1) > sizeof(src_app_name))
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_LENGTH_MISMATCH, "[%s] App name is too long \n", __func__);
        return WSM_RET_E_GENERAL_ERROR;
    }

    // Generate TA name OEM TA or TA Fuse depend of device model
    char oem_ta_name[256];
    const size_t oem_ta_name_size = sizeof(oem_ta_name);
    memset(oem_ta_name, 0, oem_ta_name_size);

    get_alt_rot_distname((char *)ta_name, oem_ta_name);

    WSM_LOG(err_level_info, LOG_TAG, "[%s] Target TA(OEM/FUSE) = |%s|\n", __func__, oem_ta_name);

    int qsee_res = qsee_decapsulate_inter_app_message((char *)src_app_name,
                                                      wrapped_data,
                                                      wrapped_data_len,
                                                      plain_data,
                                                      plain_len);
    if (qsee_res != 0)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_WRAP_DELEGATED, "[%s] QSEE UnWrap error %d\n", __func__, ret);
        return WSM_RET_E_UNWRAP_DELEGATED;
    }

    int cmp_res = memcmp(src_app_name, oem_ta_name, strlen(oem_ta_name));
    if (cmp_res != 0)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_COMPARE, "[%s] src_app_name %s check error %d\n", __func__,
                  src_app_name, ret);
        return WSM_RET_E_GENERAL_ERROR;
    }

    ret = WSM_RET_SUC;

#endif /* if defined(QSEE) */

#if defined(GP_API)
    SO_AccessControlInfoType ac_info;

    memset(&ac_info, 0, sizeof ac_info);

    if ((UUID_STRING_LEN - 1) != ta_name_len)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_LENGTH_MISMATCH, "[%s] Wrong TA name len \n", __func__);
        return WSM_RET_E_BAD_PARAM;
    }

    if (-1 == string_to_uuid((const char *)ta_name, (TEEC_UUID *)&ac_info.ta_id))
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_CONVERTION, "[%s] string_to_uuid FAIL \n", __func__);
        return WSM_RET_E_BAD_PARAM;
    }

    ac_info.access_flags = DELEGATED_TA_ID_AC;

    #if TEEGRIS_SDK_VER_MAJOR >= 4
    // http://mosaic.sec.samsung.net/kms/comty.do?comtyId=3012074&menuId=3012077&postId=369221257&page=view&type=LIST
    // DELEGATED_TA_ID_AC 할 때 target의 authority 추가 필요
    // You should designate authority and uuid together, if object is created by DELEGATED_TA_ID_AC.
    // SO_AccessControlInfoType ac_info;
    // TEEC_UUID uuid = (TEEC_UUID)TA_PROP_UUID_SECURE_OBJECT_2;
    // memset(&ac_info, 0, sizeof ac_info);
    // memcpy(&ac_info.ta_id, &uuid, sizeof(TEEC_UUID));
    // memcpy(&ac_info.auth_id, samsung_ta, strlen(samsung_ta));
    // ac_info.access_flags = DELEGATED_TA_ID_AC;

    TEE_MemMove(&ac_info.auth_id, TA_PROP_GROUP_ID, strlen(TA_PROP_GROUP_ID));
    #endif /* if TEEGRIS_SDK_VER_MAJOR >= 4 */

    TEE_Result  res = TEES_CheckSecureObjectCreator(wrapped_data, wrapped_data_len, &ac_info);
    if (res != TEE_SUCCESS)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_WRAP_DELEGATED, "[%s] BF Unwrap check so error %d\n", __func__,
                  res);
        return WSM_RET_E_UNWRAP_DELEGATED;
    }

    res = TEES_UnwrapSecureObject(wrapped_data,
                                  wrapped_data_len,
                                  plain_data,
                                  plain_len);
    if (res != TEE_SUCCESS)
    {
        WSM_LOG_E(LOG_TAG, WSM_RET_E_WRAP_DELEGATED, "[%s] BF Unwrap error %d\n", __func__, res);
        return WSM_RET_E_UNWRAP_DELEGATED;
    }

    ret = WSM_RET_SUC;

#endif /* if defined(GP_API) */

    // Printed only 32 bytes for information
    size_t hex_buffer_len;
    (void)hex_buffer_len; // to prevent build issue in RELEASE mode

    hex_buffer_len = (32 > *plain_len) ? *plain_len : 32;
    WSM_LOG_HEX(err_level_debug, LOG_TAG, " unwrapped data = ", (const char *)&plain_data[0],
                hex_buffer_len);

    return ret;
}
