#include "debug.h"
#include "skpm_util.h"
#include "skpm.h"
#include "crypto_module.h"

#define MAGIC_BYTES_IDENTIFIER_LEN                                  16
static uint8_t MAGIC_BYTES_IDENTIFIER[MAGIC_BYTES_IDENTIFIER_LEN]   = {0,};

#define MAGIC_BYTES_LEN                                             8
static uint8_t MAGIC_BYTES_TYPE_SERVICE_KEY[MAGIC_BYTES_LEN]        = {'S', 'E', 'R', 'V', '_', 'K', 'E', 'Y'};
static uint8_t MAGIC_BYTES_TYPE_KEY_LIST[MAGIC_BYTES_LEN]           = {'K', 'E', 'Y', '_', 'L', 'I', 'S', 'T'};
static uint8_t MAGIC_BYTES_TYPE_KEYBLOB[MAGIC_BYTES_LEN]            = {'K', 'E', 'Y', '_', 'B', 'L', 'O', 'B'};
static uint8_t MAGIC_BYTES_TYPE_ECC_KEY_INFO[MAGIC_BYTES_LEN]       = {'E', 'C', 'C', 'K', 'I', 'N', 'F', 'O'};
static uint8_t MAGIC_BYTES_TYPE_RSA_KEY_INFO[MAGIC_BYTES_LEN]       = {'R', 'S', 'A', 'K', 'I', 'N', 'F', 'O'};
static uint8_t MAGIC_BYTES_TYPE_TLS_SESSION_IFNO[MAGIC_BYTES_LEN]   = {'T', 'L', 'S', 'S', 'E', 'S', 'S', 'I'};

#ifdef USE_QSEE
#ifdef SECBOOT_OEM_SECAPP
// Seperate secure object target TA name between model sign. and chip sign.
const static int gChipSignTaListCnt = 1;
const static char gChipSignTaList[gChipSignTaListCnt][MAX_QSEE_ID_SIZE] = {
    "authnr"};
#endif

#ifdef USE_ALT_ROTHASH
#include "qsee_cfg_prop.h"
#define MAX_DISTNAME_PREFIX_SZ      128
#define MAX_TANAME_SZ               32
#define MAX_FULLNAME_SZ             160

#define ALT_ROT_DOMAIN_NAME         "alt_rot_domain_name_dot"
#define DL_ROT_DOMAIN_NAME          "dl_rot_domain_name_dot"

void get_alt_rot_distname(const char *prop_name, char *i_appname, char *o_distname) {
    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};
    if (QSEE_CFG_SUCCESS != qsee_cfg_getpropval(prop_name,
                                                strlen(prop_name) + 1, 0,
                                                (qsee_cfg_propvar_t *)&prop,
                                                sizeof(prop), &ret_size)) {
        LOGE("'alt_rot_domain_name_dot' read failed, using legacy appname");
        ret_size = strlcpy(o_distname, i_appname, MAX_TANAME_SZ);
        return;
    }
    ptr = (qsee_cfg_propvar_t *)prop;
    /* len = ret_size - sizeof(qsee_cfg_propvar_t) + padding */
    len = ret_size - sizeof(*ptr) + 2 * sizeof(ptr->val) + 1;
    if (len + 1 > sizeof(distname_prefix)) {
        LOGE("'alt_rot_domain_name_dot' len invalid, using legacy appname");
        strlcpy(o_distname, i_appname, MAX_TANAME_SZ);
        return;
    }
    /* remove the quotes only when read from devcfg */
    memcpy(distname_prefix, &ptr->val[1], len - 1);
    distname_prefix[len] = '\0';
    /* finalize fully qualified distname */
    strlcpy(o_distname, distname_prefix, MAX_DISTNAME_PREFIX_SZ);
    strlcat(o_distname, i_appname, MAX_FULLNAME_SZ);
}
#endif
#endif

#ifdef USE_BLOWFISH
#include <tees_secure_object.h>
#include <tee_internal_api.h>
#include <string.h>
const uint8_t SKPM_TID[TID_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x4b, 0x50, 0x4d};
#endif

#ifdef USE_MOBICORE
#include "tlStd.h"
#include "TlApi/TlApi.h"

// Constants
const uint8_t SKPM_TID[TID_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2F};
#endif

#ifdef USE_BLOWFISH
int32_t wrap_object(const uint8_t *tID, const uint8_t *inKeyBuffer, uint32_t inkeyBufferLen, uint32_t *maxBufferLen, void *outWrapObj) {
    uint32_t ret = 0;
    uint32_t outPutSize;
    SO_AccessControlInfoType wrap_uuid;

    memset(&wrap_uuid, 0x00, sizeof(SO_AccessControlInfoType));
    memcpy(&wrap_uuid.ta_id, tID, sizeof(TEEC_UUID));
#ifdef USE_TEEGRIS_V4
    memcpy(&wrap_uuid.auth_id, "samsung_ta", strlen("samsung_ta"));
#endif
    wrap_uuid.access_flags = DELEGATED_TA_ID_AC;

#ifdef USE_TEEGRIS
    outPutSize = SO_OUT_BUF_SIZE(inkeyBufferLen, 1);
//    LOGI("outPutSize : %u", outPutSize);
    if (outPutSize > *maxBufferLen) {
        LOGE("ouput data is over the buffer.");
        return RET_ERR_BUFFER_NOT_ENOUGH;
    }
#endif

    ret = TEES_WrapSecureObject((void *)inKeyBuffer, inkeyBufferLen, outWrapObj, maxBufferLen, &wrap_uuid);
    if (TEE_SUCCESS != ret) {
        LOGE("TEES_WrapSecureObject: 0x%08X", ret);
        return RET_ERR_TZ;
    }

    return RET_SUCCESS;
}

int32_t unwrap_object(const uint8_t *sourceTid, uint8_t *inBuffer, uint32_t inBufferLen, uint8_t *outBuffer, uint32_t *outBufferLen) {
    uint32_t ret = 0;

#ifdef USE_TEEGRIS
    SO_AccessControlInfoType wrap_uuid;

    memset(&wrap_uuid, 0x00, sizeof(SO_AccessControlInfoType));
    memcpy(&wrap_uuid.ta_id, sourceTid, sizeof(TEEC_UUID));
    memcpy(&wrap_uuid.auth_id, "samsung_ta", strlen("samsung_ta"));
#ifdef USE_TEEGRIS_V4
    wrap_uuid.access_flags = DELEGATED_TA_ID_AC;
#endif

    ret = TEES_CheckSecureObjectCreator(inBuffer, inBufferLen, &wrap_uuid);
    if (TEE_SUCCESS != ret) {
        LOGE("TEES_CheckSecureObjectCreator: 0x%08X", ret);
        return RET_ERR_TZ;
    }
#endif

    ret = TEES_UnwrapSecureObject(inBuffer, inBufferLen, outBuffer, outBufferLen);
    if (TEE_SUCCESS != ret) {
        LOGE("TEES_UnwrapSecureObject: 0x%08X", ret);
        return RET_ERR_TZ;
    }

    return 0;
}
#endif

#ifdef USE_MOBICORE
int32_t wrap_object(const uint8_t *tID, const uint8_t *inKeyBuffer, uint32_t inkeyBufferLen, uint32_t *maxBufferLen, void *outWrapObj) {
    uint32_t ret = 0;
    tlApiSpTrustletId_t wrap_uuid;

    wrap_uuid.spid = MC_SPID_SYSTEM;
    memcpy(&(wrap_uuid.uuid), tID, sizeof(mcUuid_t));
    //MC_SO_LIFETIME_PERMANENT
    //MC_SO_LIFETIME_POWERCYCLE
    ret = tlApiWrapObject((void *)inKeyBuffer, 0, inkeyBufferLen, outWrapObj, maxBufferLen,
                    MC_SO_CONTEXT_TLT, MC_SO_LIFETIME_PERMANENT, &wrap_uuid, TLAPI_WRAP_DEFAULT);

    if (TLAPI_OK != ret) {
        LOGE("tlApiWrapObject: 0x%08X", ret);
        return RET_ERR_TZ;
    }

    return RET_SUCCESS;
}

#define UNWRAP_FLAGS ( TLAPI_UNWRAP_PERMIT_DELEGATED | \
                       TLAPI_UNWRAP_PERMIT_CONTEXT_TL | \
                       TLAPI_UNWRAP_PERMIT_CONTEXT_SP | \
                       TLAPI_UNWRAP_PERMIT_CONTEXT_DEVICE )

int32_t unwrap_object(uint8_t *inBuffer, uint32_t *inBufferLen) {
    uint32_t ret = 0;
    uint32_t maxBufferLen = *inBufferLen;

    ret = tlApiUnwrapObject(inBuffer, *inBufferLen, inBuffer, &maxBufferLen, UNWRAP_FLAGS);
    if (TLAPI_OK != ret) {
        LOGE("tlApiUnwrapObject: 0x%08X", ret);
        return RET_ERR_TZ;
    }
    *inBufferLen = maxBufferLen;
    return 0;
}
#endif

STATUS wrap_secure_object(const uint8_t *appId, const uint8_t *input, uint32_t input_size, uint8_t *output, uint32_t *output_size) {
    STATUS ret = STATUS_SUCCESS;

#if defined USE_MOBICORE || defined USE_BLOWFISH
    //hex_print_tag_debug("Input", (uint8_t*)input, input_size);

    if (wrap_object(appId, input, input_size, output_size, output) == 0) {
        //hex_print_tag_debug("Wrapped", output, *output_size);
    } else {
        LOGE("Wrapped data failed !!");
        ret = STATUS_FAILED;
    }

#endif
#ifdef USE_QSEE
    int qsee_ret;

    if (*output_size < input_size + 144) {
        LOGE("Output buffer is not enough !!");
        return STATUS_FAILED;
    }

#ifdef SECBOOT_OEM_SECAPP
    int i, chipSigned = 0;

    for (i = 0; i < gChipSignTaListCnt; i++) {
        if (strncmp((char*)appId, gChipSignTaList[i], strlen(gChipSignTaList[i])) == 0) {
            chipSigned = 1;
        }
    }

    if (chipSigned) {
#ifdef USE_ALT_ROTHASH
        char rotHashAppId[MAX_DISTNAME_PREFIX_SZ + MAX_TANAME_SZ + 1] = {0};
        get_alt_rot_distname(DL_ROT_DOMAIN_NAME, (char*)appId, rotHashAppId);
        LOGI("Use ALTROTHASH AppId : %s", rotHashAppId);
        qsee_ret = qsee_encapsulate_inter_app_message((char*)rotHashAppId, (uint8_t *)input, input_size, output, output_size);
#else // USE_ALT_ROTHASH
        char chipSignedAppId[QSEE_MESSAGE_APP_NAME_MAX_LEN] = SECBOOT_OEM_SECAPP;
        strcat(chipSignedAppId, (char*)appId);
        LOGI("Use chipSignedAppId : %s", chipSignedAppId);
        qsee_ret = qsee_encapsulate_inter_app_message((char*)chipSignedAppId, (uint8_t *)input, input_size, output, output_size);
#endif // USE_ALT_ROTHASH
    } else {
#ifdef USE_ALT_ROTHASH
        char rotHashAppId[MAX_DISTNAME_PREFIX_SZ + MAX_TANAME_SZ + 1] = {0};
        get_alt_rot_distname(ALT_ROT_DOMAIN_NAME, (char*)appId, rotHashAppId);
        LOGI("Use ALTROTHASH AppId : %s", rotHashAppId);
        //LOGI("Use rotHashAppId %s", (char*)rotHashAppId);
        qsee_ret = qsee_encapsulate_inter_app_message((char*)rotHashAppId, (uint8_t *)input, input_size, output, output_size);
#else // USE_ALT_ROTHASH
        qsee_ret = qsee_encapsulate_inter_app_message((char*)appId, (uint8_t *)input, input_size, output, output_size);
#endif // USE_ALT_ROTHASH
    }
#else // SECBOOT_OEM_SECAPP
    qsee_ret = qsee_encapsulate_inter_app_message((char*)appId, (uint8_t *)input, input_size, output, output_size);
#endif // SECBOOT_OEM_SECAPP

    if (qsee_ret != 0) {
        LOGE("Wrapped data failed !!, ret = %x", qsee_ret);
        ret = STATUS_FAILED;
    }
#endif

    return ret;
}

#if defined(USE_BLOWFISH) || defined(USE_QSEE)
STATUS unwrap_secure_object(const uint8_t *sourceTid, const uint8_t *input, uint32_t input_size, uint8_t *output, uint32_t *output_size) {
    STATUS ret = STATUS_SUCCESS;

#ifdef USE_BLOWFISH
    uint8_t temp_out[MAX_SECURE_OBJECT_SIZE] = {0, };
    uint32_t temp_out_size = sizeof(temp_out);

    //hex_print_tag_debug("Wrapped", (uint8_t*)temp_in, input_size);
    if (unwrap_object((uint8_t *)sourceTid, (uint8_t *)input, input_size, temp_out, &temp_out_size) == 0) {
        //hex_print_tag_debug("Unwrapped", (uint8_t*)temp_in, temp_in_size);
        if (temp_out_size > *output_size) {
            LOGE("ouput data is over the buffer.");
            return STATUS_FAILED;
        }

        memcpy(output, temp_out, temp_out_size);
        *output_size = temp_out_size;
    } else {
        LOGE("Unwrapped data failed !!");
        ret = STATUS_FAILED;
    }
#endif
#ifdef USE_QSEE
    char appName[QSEE_MESSAGE_APP_NAME_MAX_LEN] = "";
    int qsee_ret;

    //hex_print_tag_debug("Wrapped", (uint8_t*)input, input_size);
    qsee_ret = qsee_decapsulate_inter_app_message(appName, (uint8_t *)input, input_size, output, output_size);
    if (qsee_ret == 0) {
        LOGD("sourceTid : %s", (char*)sourceTid);
        LOGD("appName : %s", appName);

#ifdef USE_ALT_ROTHASH
        char rotHashAppId[MAX_DISTNAME_PREFIX_SZ + MAX_TANAME_SZ + 1] = {0};
        get_alt_rot_distname(ALT_ROT_DOMAIN_NAME, (char*)sourceTid, rotHashAppId);
        LOGI("Use ALTROTHASH AppId : %s", rotHashAppId);

        if (strncmp(appName, rotHashAppId, strlen(appName)) != 0) {
#else
        if (strncmp(appName, (char*)sourceTid, strlen(appName)) != 0) {
#endif
            LOGE("appName is not matched : %s", appName);
            return STATUS_FAILED;    
        }
        //hex_print_tag_debug("Unwrapped", (uint8_t*)output, *output_size);
    } else {
        LOGE("Unwrapped data failed !!, ret = %x", qsee_ret);
        ret = STATUS_FAILED;
    }
#endif
    return ret;
    
}
#else
STATUS unwrap_secure_object(const uint8_t *input, uint32_t input_size, uint8_t *output, uint32_t *output_size) {
    STATUS ret = STATUS_SUCCESS;

    uint8_t temp_in[MAX_SECURE_OBJECT_SIZE];
    uint32_t temp_in_size = input_size;

    if (input_size > MAX_SECURE_OBJECT_SIZE) {
        LOGE("input data is over the buffer.");
        return STATUS_FAILED;
    }
    memcpy(temp_in, input, input_size);
    //hex_print_tag_debug("Wrapped", (uint8_t*)temp_in, input_size);
    if (unwrap_object(temp_in, &temp_in_size) == 0) {
        //hex_print_tag_debug("Unwrapped", (uint8_t*)temp_in, temp_in_size);
        if (temp_in_size > *output_size) {
            LOGE("ouput data is over the buffer.");
            return STATUS_FAILED;
        }

        memcpy(output, temp_in, temp_in_size);
        *output_size = temp_in_size;
    } else {
        LOGE("Unwrapped data failed !!");
        ret = STATUS_FAILED;
    }

    return ret;
}
#endif

STATUS wrapInternalData(uint8_t objectType, uint8_t* input, uint32_t inputSize,
                         uint8_t* output, uint32_t *outputSize) {
    STATUS ret = RET_SUCCESS;

    uint8_t magicNumber[MAGIC_BYTES_LEN] = {0,};
    uint8_t tempInput[MAX_SECURE_OBJECT_SIZE];
    uint32_t tempInputSize = 0;

#ifdef USE_QSEE
    uint8_t wrapKey[AES_256_KEY_SIZE];
    uint8_t iv[AES_BLOCK_SIZE] = {0,};
    uint8_t temp[MAX_SECURE_OBJECT_SIZE] = {0, };
    uint8_t hash[SHA256_DIGEST_SIZE] = {0,};
    uint32_t offset = 0;
#endif

    LOGD("wrapInternalData started");

    if (inputSize + MAGIC_BYTES_IDENTIFIER_LEN + MAGIC_BYTES_LEN > MAX_SECURE_OBJECT_SIZE) {
        LOGE("Buffer is not enough");
        ret = STATUS_FAILED;
        goto error;
    }

    if (objectType == SECURE_OBJECT_TYPE_SERVICE_KEY) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_SERVICE_KEY, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_KEY_LIST) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_KEY_LIST, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_KEYBLOB) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_KEYBLOB, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_ECC_KEY_INFO) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_ECC_KEY_INFO, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_RSA_KEY_INFO) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_RSA_KEY_INFO, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_TLS_SESSION_INFO) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_TLS_SESSION_IFNO, MAGIC_BYTES_LEN);
    } else {
        LOGE("Not supported key type");
        ret = STATUS_FAILED;
        goto error;
    }

    memcpy(tempInput, MAGIC_BYTES_IDENTIFIER, MAGIC_BYTES_IDENTIFIER_LEN);
    tempInputSize += MAGIC_BYTES_IDENTIFIER_LEN;
    memcpy(tempInput + tempInputSize, magicNumber, MAGIC_BYTES_LEN);
    tempInputSize += MAGIC_BYTES_LEN;
    memcpy(tempInput + tempInputSize, input, inputSize);
    tempInputSize += inputSize;

#if defined USE_MOBICORE || defined USE_BLOWFISH
    ret = wrap_secure_object(SKPM_TID, tempInput, tempInputSize, output, outputSize);
    if (ret != STATUS_SUCCESS) {
        LOGE("wrap_secure_object failed");
        goto error;
    }
#endif

#ifdef USE_QSEE
    crypto_gen_random(iv, AES_BLOCK_SIZE);
    hex_print_tag_debug("iv", iv, AES_BLOCK_SIZE);
    crypto_get_tz_encryption_key(wrapKey);
    hex_print_tag_debug("wrapKey", wrapKey, AES_256_KEY_SIZE);

    hex_print_tag_debug("tempInput", tempInput, tempInputSize);
    crypto_sha(MD_TYPE_SHA256, hash, tempInput, tempInputSize, NULL);
    hex_print_tag_debug("hash", hash, SHA256_DIGEST_SIZE);

    /* Encrypt plain data */
    ret = crypto_aes_cbc_encrypt(tempInput, tempInputSize, output, outputSize, iv, wrapKey, AES_256_KEY_SIZE);
    secure_memclear(wrapKey, sizeof(wrapKey));
    if (ret != STATUS_SUCCESS) {
        LOGE("crypto_aes_cbc_encrypt failed");
        goto error;
    }

    hex_print_tag_debug("out", output, *outputSize);
    if (MAX_SECURE_OBJECT_SIZE - *outputSize > AES_BLOCK_SIZE + AES_BLOCK_SIZE + AES_BLOCK_SIZE + SHA256_DIGEST_SIZE) {
        hex_print_tag_debug("iv", iv, AES_BLOCK_SIZE);
        memset(temp, 0x00, AES_BLOCK_SIZE);
        offset += AES_BLOCK_SIZE;
        memcpy(temp + offset, iv, AES_BLOCK_SIZE);
        offset += AES_BLOCK_SIZE;
        memset(temp + offset, 0x00, AES_BLOCK_SIZE);
        offset += AES_BLOCK_SIZE;
        memcpy(temp + offset, hash, SHA256_DIGEST_SIZE);
        offset += SHA256_DIGEST_SIZE;
        memcpy(temp + offset, output, *outputSize);
        offset += *outputSize;
        *outputSize = offset;
        memcpy(output, temp, *outputSize);
        hex_print_tag_debug("out with iv", output, *outputSize);
    } else {
        LOGE("Input data is over the buffer.");
        ret = STATUS_FAILED;
        goto error;
    }
#endif

    hex_print_tag_debug("wrapped", output, *outputSize);

error:
    LOGD("wrapInternalData end, ret : %x", ret);

    return ret;
}


#if defined(MSM8996) || defined(MSM8998) || defined(MSM8952) || \
    defined(MSM8956) || defined(MSM8917) || defined(SDM450) || \
    defined(SDM660) || defined(SDM660_SPF21) || defined(SDM670) || \
    defined(SDM670_SPF20) || defined(SDM710) || defined(SDM710_SPF20) || \
    defined(SDM845) || defined(SM6150) || defined(SM7150) || \
    defined(SM6150_SPF20) || defined(SM7150_SPF20) || \
    defined(SM8150) || defined(SM8150_FUSION) || defined(SM8150_FUSION_LA20)|| \
    defined(MT6737T) || defined(MT6757) || defined(EXYNOS7270) || \
    defined(EXYNOS7420) || defined(EXYNOS7570) || defined(EXYNOS7580) || \
    defined(EXYNOS7870) || defined(EXYNOS7880) || defined(EXYNOS7885) || \
    defined(EXYNOS7904) || defined(EXYNOS8890) || defined(EXYNOS8890_310B) || \
    defined(EXYNOS8895) || defined(EXYNOS9110) || defined(EXYNOS9610) || \
    defined(EXYNOS9810) || defined(EXYNOS9820)
#define CHECK_UNWRAPP_CONDITION
#endif

STATUS unwrapInternalData(uint8_t objectType, uint8_t* input, uint32_t inputSize,
                            uint8_t* output, uint32_t *outputSize) {
    STATUS ret = RET_SUCCESS;
    uint8_t magicNumber[MAGIC_BYTES_LEN] = {0,};
    uint8_t tempOutput[MAX_SECURE_OBJECT_SIZE];
    uint32_t tempOutSize = sizeof(tempOutput);

#ifdef USE_QSEE
    uint8_t wrapKey[AES_256_KEY_SIZE];
    uint8_t iv[AES_BLOCK_SIZE] = {0,};
    uint8_t temp[MAX_SECURE_OBJECT_SIZE] = {0, };
    uint8_t hash[SHA256_DIGEST_SIZE] = {0,};
    uint8_t calHash[SHA256_DIGEST_SIZE] = {0,};
    uint32_t offset = 0;
#endif

    LOGD("unwrapInternalData started");

    hex_print_tag_debug("Input", input, inputSize);
#ifdef USE_MOBICORE
    ret = unwrap_secure_object(input, inputSize, tempOutput, &tempOutSize);
    if (ret != STATUS_SUCCESS) {
        LOGE("unwrap_secure_object failed");
        goto error;
    }
#endif
#ifdef USE_BLOWFISH
    ret = unwrap_secure_object(SKPM_TID, input, inputSize, tempOutput, &tempOutSize);
    if (ret != STATUS_SUCCESS) {
        LOGE("unwrap_secure_object failed");
        goto error;
    }
#endif
#ifdef USE_QSEE
    crypto_get_tz_encryption_key(wrapKey);
    hex_print_tag_debug("wrapKey", wrapKey, AES_256_KEY_SIZE);

#ifdef CHECK_UNWRAPP_CONDITION
    if (memcmp(iv, input, AES_BLOCK_SIZE) == 0) {
#endif
        offset += AES_BLOCK_SIZE;
        if (inputSize < offset + AES_BLOCK_SIZE) {
            LOGE("Wrapped key data is not correct");
            ret = STATUS_FAILED;
            goto error;
        }
        memcpy(iv, input + offset, AES_BLOCK_SIZE);
        hex_print_tag_debug("iv", iv, AES_BLOCK_SIZE);
        offset += AES_BLOCK_SIZE;
#ifdef CHECK_UNWRAPP_CONDITION
        if (memcmp(hash, input + offset, AES_BLOCK_SIZE) == 0) {
#endif
            if (inputSize < offset + AES_BLOCK_SIZE) {
                LOGE("Wrapped key data is not correct");
                ret = STATUS_FAILED;
                goto error;
            }            
            offset += AES_BLOCK_SIZE;
            memcpy(hash, input + offset, SHA256_DIGEST_SIZE);
            hex_print_tag_debug("hash", hash, SHA256_DIGEST_SIZE);
            offset += SHA256_DIGEST_SIZE;
#ifdef CHECK_UNWRAPP_CONDITION
        }
#endif
        inputSize -= offset;
        if (inputSize > sizeof(temp)) {
            LOGE("Not enough buffer");
            ret = STATUS_FAILED;
            goto error;
        }
        memcpy(temp, input + offset, inputSize);
        hex_print_tag_debug("Input without iv", temp, inputSize);
#ifdef CHECK_UNWRAPP_CONDITION
    } else {
        if (inputSize > sizeof(temp)) {
            LOGE("Not enough buffer");
            ret = STATUS_FAILED;
            goto error;
        }
        memcpy(temp, input, inputSize);
    }
#endif

    ret = crypto_aes_cbc_decrypt(temp, inputSize, tempOutput, &tempOutSize, iv, wrapKey, AES_256_KEY_SIZE);
    secure_memclear(wrapKey, sizeof(wrapKey));
    if (ret != STATUS_SUCCESS) {
        LOGE("crypto_aes_cbc_decrypt failed");
        goto error;
    }

#ifdef CHECK_UNWRAPP_CONDITION
    if (memcmp(hash, calHash, SHA256_DIGEST_SIZE) != 0) {
#endif
        crypto_sha(MD_TYPE_SHA256, calHash, tempOutput, tempOutSize, NULL);

        hex_print_tag_debug("hash", hash, SHA256_DIGEST_SIZE);
        hex_print_tag_debug("calHash", calHash, SHA256_DIGEST_SIZE);
        if (memcmp(hash, calHash, SHA256_DIGEST_SIZE) != 0) {
            LOGE("Data is corrupted");
            ret = STATUS_FAILED;
            goto error;
        }
#ifdef CHECK_UNWRAPP_CONDITION
    }
#endif

#endif

    hex_print_tag_debug("tempOutput", tempOutput, tempOutSize);

    if (objectType == SECURE_OBJECT_TYPE_SERVICE_KEY) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_SERVICE_KEY, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_KEY_LIST) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_KEY_LIST, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_KEYBLOB) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_KEYBLOB, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_ECC_KEY_INFO) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_ECC_KEY_INFO, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_RSA_KEY_INFO) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_RSA_KEY_INFO, MAGIC_BYTES_LEN);
    } else if (objectType == SECURE_OBJECT_TYPE_TLS_SESSION_INFO) {
        memcpy(magicNumber, MAGIC_BYTES_TYPE_TLS_SESSION_IFNO, MAGIC_BYTES_LEN);
    } else {
        LOGE("Not supported key type");
        ret = STATUS_FAILED;
        goto error;
    }

#ifdef CHECK_UNWRAPP_CONDITION
    if (memcmp(tempOutput, MAGIC_BYTES_IDENTIFIER, MAGIC_BYTES_IDENTIFIER_LEN) == 0) {
#endif
        if (memcmp(tempOutput + MAGIC_BYTES_IDENTIFIER_LEN, magicNumber, MAGIC_BYTES_LEN) != 0) {
            LOGE("Not correct so type");
            ret = STATUS_FAILED;
            goto error;
        }
        if (tempOutSize > *outputSize) {
            LOGE("Not enough buffer");
            ret = STATUS_FAILED;
            goto error;
        }
        memcpy(output, tempOutput + MAGIC_BYTES_IDENTIFIER_LEN + MAGIC_BYTES_LEN, tempOutSize - (MAGIC_BYTES_IDENTIFIER_LEN + MAGIC_BYTES_LEN));
        *outputSize = tempOutSize - MAGIC_BYTES_IDENTIFIER_LEN - MAGIC_BYTES_LEN;
#ifdef CHECK_UNWRAPP_CONDITION
    } else {
        if (tempOutSize > *outputSize) {
            LOGE("Not enough buffer");
            ret = STATUS_FAILED;
            goto error;
        }
        memcpy(output, tempOutput, tempOutSize);
        *outputSize = tempOutSize;
    }
#endif
    hex_print_tag_debug("unwrapped", output, *outputSize);

error:
    LOGD("unwrapInternalData end, ret : %x", ret);

    return ret;
}

void secure_memclear(void * secure_data, size_t size) {
    volatile uint8_t * temp = secure_data;

    if ((NULL == secure_data) || (0 == size) || (SIZE_MAX < size)) {
        LOGE("incorrect input parameters");
        return;
    }

    while ( size-- ) {
        *temp++ = 0;
    }
}

#ifndef SUPPORT_GUARDIAN_M
void *tz_malloc(uint32_t size) {
#ifdef USE_MOBICORE
    return tlApiMalloc(size, 0);
#endif
#ifdef USE_QSEE
    return qsee_malloc(size);
#endif
#ifdef USE_BLOWFISH
    return TEE_Malloc(size, 0);
#endif
}

void tz_free(void *data) {
#ifdef USE_MOBICORE
    tlApiFree(data);
#endif
#ifdef USE_QSEE
    qsee_free(data);
#endif
#ifdef USE_BLOWFISH
    TEE_Free(data);
#endif
    data = NULL;
}
#endif

int32_t parseTlvData(uint8_t *tlvData, uint32_t leftBuffSize, uint8_t *data, uint32_t dataBuffSize, uint32_t *dataSize) {
    uint32_t total_len = 0;

    if (total_len + 1 > leftBuffSize) {
        LOGE("Over the Buffer size");
        return RET_ERR_WRONG_INPUT_FORMAT;
    }

    if (tlvData[total_len] == 0x82) {
        total_len++;
        if (total_len + 2 > leftBuffSize) {
            LOGE("Over the Buffer size");
            return RET_ERR_WRONG_INPUT_FORMAT;
        }
        *dataSize = tlvData[total_len++];
        *dataSize = *dataSize << 8;
        *dataSize |= tlvData[total_len++];
        if (dataBuffSize < *dataSize) {
            LOGE("Buffer is not enough");
            return RET_ERR_BUFFER_NOT_ENOUGH;
        }
        if (total_len + *dataSize > leftBuffSize) {
            LOGE("Over the Buffer size");
            return RET_ERR_WRONG_INPUT_FORMAT;
        }
        memcpy(data, tlvData + total_len, *dataSize);
        total_len += *dataSize;
    } else if (tlvData[total_len] == 0x81) {
        total_len++;
        if (total_len + 1 > leftBuffSize) {
            LOGE("Over the Buffer size");
            return RET_ERR_WRONG_INPUT_FORMAT;
        }
        *dataSize = tlvData[total_len++];
        if (dataBuffSize < *dataSize) {
            LOGE("Buffer is not enough");
            return RET_ERR_BUFFER_NOT_ENOUGH;
        }
        if (total_len + *dataSize > leftBuffSize) {
            LOGE("Over the Buffer size");
            return RET_ERR_WRONG_INPUT_FORMAT;
        }
        memcpy(data, tlvData + total_len, *dataSize);
        total_len += *dataSize;
    } else {
        *dataSize = tlvData[total_len++];
        if (dataBuffSize < *dataSize) {
            LOGE("Buffer is not enough");
            return RET_ERR_BUFFER_NOT_ENOUGH;
        }
        if (total_len + *dataSize > leftBuffSize) {
            LOGE("Over the Buffer size");
            return RET_ERR_WRONG_INPUT_FORMAT;
        }
        memcpy(data, tlvData + total_len, *dataSize);
        total_len += *dataSize;
    }

    return total_len;
}

