/*
 * app_cipher.c (copied from QSEE_TZ_Vendor.c and QSEE_TZ_aes.c (tz_platform) to remove the code dependency in TIMA path)
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include <secrsa_err.h> // E_SECMATH_SUCCESS, E_SECMATH_FAILURE
#include <secrsa_padding.h> // S_BIGINT_POS
#include <qsee_cipher.h>
#include <qsee_hash.h> // qsee_hash, QSEE_RSA_PAD_PKCS1_PSS, QSEE_SHA256_HASH_SZ, QSEE_HASH_IDX_SHA256
#include <qsee_kdf.h> // qsee_kdf
#include <qsee_log.h>
#include <qsee_prng.h>
#include <qsee_rsa.h> // QSEE_RSA_KEY, QSEE_RSA_PSS_PAD_INFO, qsee_rsa_sign_hash

#include "app_cipher.h"

static uint8_t wrapped_message[MAX_WRAPPED_MESSAGE_SIZE] = { 0 };

QSEE_CIPHER_MODE_ET qsee_cipher_mode_convert(
    CIPHER_MODE_ET cipherMode
)
{
    switch (cipherMode) {
        case CIPHER_MODE_ECB:
            return QSEE_CIPHER_MODE_ECB;
            break;
        case CIPHER_MODE_CBC:
            return QSEE_CIPHER_MODE_CBC;
            break;
        case CIPHER_MODE_CTR:
            return QSEE_CIPHER_MODE_CTR;
            break;
        case CIPHER_MODE_XTS:
            return QSEE_CIPHER_MODE_XTS;
            break;
        case CIPHER_MODE_CCM:
            return QSEE_CIPHER_MODE_CCM;
            break;
        case CIPHER_MODE_CTS:
            return QSEE_CIPHER_MODE_CTS;
            break;
        default:
            return (QSEE_CIPHER_MODE_INVALID);
    }
}

QSEE_CIPHER_PAD_ET qsee_cipher_pad_convert(
    CIPHER_PAD_ET cipherPad
)
{
    switch (cipherPad) {
        case CIPHER_PAD_ISO10126:
            return QSEE_CIPHER_PAD_ISO10126;
            break;
        case CIPHER_PAD_PKCS7:
            return QSEE_CIPHER_PAD_PKCS7;
            break;
        case CIPHER_PAD_NO_PAD:
            return QSEE_CIPHER_PAD_NO_PAD;
            break;
        default:
            return (QSEE_CIPHER_PAD_INVALID);
    }
}

uint32_t TZ_derive_key_aes(
    uint8_t *salt,
    uint32_t saltLen,
    uint8_t *encKey,
    uint32_t encKeyLen
)
{
    char key_context[] = {"HW Crypto AES key derived from SHK"};
    int ret = TZ_API_OK;

    if (salt == NULL || saltLen == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s invalid salt", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (encKey == NULL || encKeyLen == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s invalid key buffer", __FUNCTION__);
        return TZ_API_ERROR;
    }

    ret = qsee_kdf(NULL, 32 /* Could be any value. it is ignored as 1st param is NULL */,
                   (void*)salt, saltLen,
                   (void*)key_context, strlen(key_context),
                   encKey, encKeyLen);
    if (ret == -1) {
        qsee_log(QSEE_LOG_MSG_ERROR, "qsee_kdf() null pointers present where not allowed");
        ret = TZ_API_ERROR;
    } else if (ret == -2) {
        qsee_log(QSEE_LOG_MSG_ERROR, "qsee_kdf() invalid values or overflow");
        ret = TZ_API_ERROR;
    }

    return (uint32_t) ret;
}

/**
 * @Generate random data.
 *
 * @param[in, out] randomBuffer:  The output random data buffer
 * @param[in, out] randomLen: The length of random data to generate.
 * The number of random data bytes generated will be returned in randomLen.
 * */
uint32_t TZ_gen_prng_data(
    uint8_t *randomBuffer,
    uint32_t *randomLen
)
{
    uint32_t ret = TZ_API_OK;

    if (randomBuffer == NULL) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s invalid input buffer", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (randomLen == NULL || *randomLen == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s invalid randomLen", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (*randomLen > MAX_PRNG_LENGTH) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s randomLen too large", __FUNCTION__);
        return TZ_API_ERROR;
    }

    qsee_log(QSEE_LOG_MSG_DEBUG, "%s random buffer length: %d", __FUNCTION__, *randomLen);

    ret = qsee_prng_getdata(randomBuffer, *randomLen);

    if (ret == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: the random data generated has length = 0", __FUNCTION__);
        return TZ_API_ERROR;
    }

    *randomLen = ret;

    return TZ_API_OK;
}

uint32_t TZ_SHA256_digest(
    uint8_t *messageData,
    uint32_t messageLen,
    uint8_t *digest,
    uint32_t *pDigestLen
)
{
    int qcRet = 0;

    qsee_log(QSEE_LOG_MSG_DEBUG, "%s: generate digest", __FUNCTION__);

    if (messageData == NULL || messageLen == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid input data buffer", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (digest == NULL || pDigestLen == NULL || *pDigestLen == 0 || *pDigestLen < SHA256_DIGEST_LENGTH) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid output digest data buffer", __FUNCTION__);
        return TZ_API_ERROR;
    }

    *pDigestLen = QSEE_SHA256_HASH_SZ;

    // Calculate HASH
    qcRet = qsee_hash(QSEE_HASH_SHA256, messageData, messageLen, digest, *pDigestLen);

    if (qcRet != 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_hash() returns an error = %d", __FUNCTION__, qcRet);
        return TZ_API_ERROR;
    }

    qsee_log(QSEE_LOG_MSG_DEBUG, "%s: qsee_hash() returns OK", __FUNCTION__);
    return TZ_API_OK;
}

uint32_t TZ_sign_CKM_SHA256_RSA_PKCS_PSS(
    QSEE_RSA_KEY *rsa_key,
    uint8_t *messageData,
    uint32_t messageLen,
    uint8_t *signature,
    int *pSigLen
)
{
    uint32_t ret = TZ_API_OK;
    QSEE_RSA_PSS_PAD_INFO pss_pad_info;
    uint8 a_hash[QSEE_SHA256_HASH_SZ];
    pss_pad_info.hashidx = QSEE_HASH_IDX_SHA256;
    pss_pad_info.saltlen = 32;

    // Compute hash for message result
    if (qsee_hash(QSEE_HASH_SHA256, messageData, messageLen, a_hash, QSEE_SHA256_HASH_SZ) != E_SECMATH_SUCCESS) {
        qsee_log(QSEE_LOG_MSG_DEBUG, "%s: Failed to compute hash!", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_rsa_sign_hash(
            rsa_key,
            QSEE_RSA_PAD_PKCS1_PSS,
            &pss_pad_info,
            QSEE_HASH_IDX_SHA256,
            (unsigned char *)a_hash,
            QSEE_SHA256_HASH_SZ,
            signature,
            pSigLen)) {
        qsee_log(QSEE_LOG_MSG_DEBUG, "%s: Failed to sign: ret = %d", __FUNCTION__, ret);
        ret = TZ_API_ERROR;
        goto exit;
    }

exit:
    return ret;
}

// The plaintext buffer must be large enough for padding
uint32_t TZ_aes_encrypt_with_parameters(
    uint8_t *encKey,
    uint32_t encKeyLen,
    uint8_t *plaintext,
    uint32_t plaintextLen,
    uint8_t *ciphertext,
    uint32_t *pCiphertextLen,
    CIPHER_MODE_ET cipherMode,
    CIPHER_PAD_ET cipherPad,
    uint8_t *aes_iv,
    uint32_t aes_iv_size
)
{
    uint32_t ret = TZ_API_OK;
    qsee_cipher_ctx *ctx = 0;
    uint8_t padLen = 0;
    uint8_t *pointer = NULL;
    uint32_t i = 0;

    QSEE_CIPHER_MODE_ET qseeCipherMode = qsee_cipher_mode_convert(cipherMode);
    QSEE_CIPHER_PAD_ET qseeCipherPad = qsee_cipher_pad_convert(cipherPad);

    if (encKey == NULL || encKeyLen == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid input encrypt key", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (encKeyLen != AES256_KEY_SIZE) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: should use 256-bit aes encryption key", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (plaintext == NULL || plaintextLen == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid input plaintext", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (ciphertext == NULL || pCiphertextLen == NULL) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid input ciphertext", __FUNCTION__);
        return TZ_API_ERROR;
    }

    // guarding against integer overflow in upcoming addition
    if (plaintextLen > 0xffffffff - EXTRA_AES_BUFFER_SPACE) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: exceedingly large plaintextLen = %u", __FUNCTION__, plaintextLen);
        return TZ_API_ERROR;
    }

    if (*pCiphertextLen < (plaintextLen + EXTRA_AES_BUFFER_SPACE)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: insufficient ciphertext buffer (ciphertext buffer needs to be at least %d larger than the plaintext length)", __FUNCTION__, EXTRA_AES_BUFFER_SPACE);
        return TZ_API_ERROR;
    }

    if (plaintextLen % AES_BLOCK_SIZE == 0) {
        padLen = AES_BLOCK_SIZE;
    } else {
        padLen = AES_BLOCK_SIZE - plaintextLen % AES_BLOCK_SIZE;
    }
    pointer = plaintext + plaintextLen;
    for (i=0; i< padLen; i++) {
        *(pointer + i) = padLen;
    }

    if (qsee_cipher_init(QSEE_CIPHER_ALGO_AES_256, &ctx) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_init API failed", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_KEY, encKey, encKeyLen) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_set_parm API failed to set encryption key", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_MODE, &qseeCipherMode, sizeof(qseeCipherMode)) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_set_parm API failed to set encryption mode", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_PAD, &qseeCipherPad, sizeof(qseeCipherPad)) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_set_parm API failed to set padding mode", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_IV, aes_iv, aes_iv_size) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_set_parm API failed to set QSEE_CIPHER_PARAM_IV", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_cipher_encrypt(ctx, plaintext, plaintextLen, ciphertext, (uint32_t *)pCiphertextLen) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_encrypt API failed", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

exit:
    if (ctx) {
        if (qsee_cipher_free_ctx(ctx) < 0) {
            qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_free_ctx API failed", __FUNCTION__);
            ret = TZ_API_ERROR;
        }
        ctx = 0;
    } 
    return ret;
}

uint32_t TZ_aes_cbc_256_encrypt_with_iv(
    uint8_t *encKey,
    uint32_t encKeyLen,
    uint8_t *plaintext,
    uint32_t plaintextLen,
    uint8_t *ciphertext,
    uint32_t *pCiphertextLen,
    uint8_t *iv,
    uint32_t ivLen,
    bool pad_cipher
)
{
    if ((NULL == encKey) || (NULL == plaintext) || (NULL == ciphertext) || (NULL == pCiphertextLen) || (NULL == iv)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid input", __FUNCTION__);
        return TZ_API_ERROR;
    }

    CIPHER_MODE_ET cipherMode = CIPHER_MODE_CBC;
    CIPHER_PAD_ET cipherPad;

    if (pad_cipher) {
        cipherPad = CIPHER_PAD_PKCS7;
    } else {
        cipherPad = CIPHER_PAD_NO_PAD;
    }

    return (TZ_aes_encrypt_with_parameters(encKey, encKeyLen, plaintext, plaintextLen, ciphertext, pCiphertextLen,
                                           cipherMode, cipherPad, iv, ivLen));
}

/* 
 * It encrypts the data with random IV and stores the IV header in the wrapped data.
 * TZ_wrap_persist_data
 */
uint32_t TZ_wrap_persist_data_aes_cbc_256(
    uint8_t *appName,
    uint32_t appNameLen,
    uint8_t *data,
    uint32_t datalen,
    uint8_t *wrapped_data,
    uint32_t *wrapped_datalen
)
{
    uint32_t ret = TZ_API_OK;
    persist_data_header_t header = { 0 };
    uint32_t headerLen = sizeof(header);
    uint8_t keyBuffer[AES256_KEY_SIZE] = { 0 };
    uint32_t keyLen = sizeof(keyBuffer);
    uint8_t digest[SHA256_DIGEST_LENGTH] = { 0 };
    uint32_t digestLen = sizeof(digest);

    if ((appName == NULL) || (data == NULL) || (wrapped_data == NULL) || (datalen == 0)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s invalid input buffer", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    /*
     * MAX_WRAPPED_MESSAGE size is quite small so when we will add together
     * a few integers bounded below it uint32_t overflow is not possible
     */
    if ((appNameLen > MAX_FULLNAME_SIZE) || (datalen > ICCC_MAX_DATA_BUF) || (*wrapped_datalen > MAX_WRAPPED_DATA_LEN)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s input buffers too long", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (*wrapped_datalen < (datalen + SHA256_DIGEST_LENGTH + AES_BLOCK_SIZE + sizeof(persist_data_header_t))) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s insufficient wrap data buffer", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    /* Derive key using app name as salt */
    ret = TZ_derive_key_aes(appName, appNameLen, keyBuffer, keyLen);
    if (ret != TZ_API_OK) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: TZ_derive_key_aes returns an error", __FUNCTION__);
        goto exit;
    }

    /* Generate IV */
    header.iv_len = AES_IV_SIZE;
    ret = TZ_gen_prng_data(header.iv, &header.iv_len);
    if (ret != TZ_API_OK) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: TZ_gen_prng_data returns an error", __FUNCTION__);
        goto exit;
    }

    header.version = PERSIST_DATA_HEADER_VERSION;
    header.magic = PERSIST_DATA_HEADER_MAGIC;
    memcpy(wrapped_data, &header, headerLen);

    ret = TZ_SHA256_digest(data, datalen, digest, &digestLen);
    if (ret != TZ_API_OK) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: TZ_SHA256_digest() returns an error", __FUNCTION__);
        goto exit;
    }

    if (MAX_WRAPPED_MESSAGE_SIZE < datalen + digestLen) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s input data length exceeds MAX_WRAPPED_MESSAGE_SIZE", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    memcpy(wrapped_message, data, datalen);
    memcpy(wrapped_message + datalen, digest, digestLen);

    *wrapped_datalen -= headerLen;
    ret = TZ_aes_cbc_256_encrypt_with_iv(keyBuffer, keyLen, wrapped_message, 
                                         datalen+digestLen, wrapped_data+headerLen, wrapped_datalen, header.iv, header.iv_len, true);
    *wrapped_datalen += headerLen;
    memset(wrapped_message, 0, datalen + digestLen);

    if (ret != TZ_API_OK) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: TZ_aes_encrypt() returns an error", __FUNCTION__);
        goto exit;
    }

exit:
    memset(keyBuffer, 0, keyLen);
    return ret;
}

uint32_t TZ_aes_decrypt_with_parameters(
    uint8_t *encKey,
    uint32_t encKeyLen,
    uint8_t *ciphertext,
    uint32_t ciphertextLen,
    uint8_t *plaintext,
    uint32_t *pPlaintextLen,
    CIPHER_MODE_ET cipherMode,
    CIPHER_PAD_ET cipherPad,
    uint8_t *aes_iv,
    uint32_t aes_iv_size
)
{
    uint32_t ret = TZ_API_OK;
    qsee_cipher_ctx *ctx = 0;

    QSEE_CIPHER_MODE_ET qseeCipherMode = qsee_cipher_mode_convert(cipherMode);
    QSEE_CIPHER_PAD_ET qseeCipherPad = qsee_cipher_pad_convert(cipherPad);

    uint32_t decryptLen =0;

    if (encKey == NULL || encKeyLen == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid input encrypt key", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (encKeyLen != AES256_KEY_SIZE) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: should use 256-bit AES encryption key", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (ciphertext == NULL || ciphertextLen == 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid input ciphertext", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (plaintext == NULL || pPlaintextLen == NULL) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid plaintext buffer", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (*pPlaintextLen < ciphertextLen) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: insufficient plaintext buffer", __FUNCTION__);
        return TZ_API_ERROR;
    }
    decryptLen =*pPlaintextLen;

    if (qsee_cipher_init(QSEE_CIPHER_ALGO_AES_256, &ctx) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_init API failed", __FUNCTION__);
        return TZ_API_ERROR;
    }

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_KEY, encKey, encKeyLen) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_set_parm API failed to set encryption key", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_MODE, &qseeCipherMode, sizeof(qseeCipherMode)) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_set_parm API failed to set encryption mode", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_PAD, &qseeCipherPad, sizeof(qseeCipherPad)) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_set_parm API failed to set padding mode", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_IV, aes_iv, QSEE_AES256_IV_SIZE) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_set_parm API failed to set QSEE_CIPHER_PARAM_IV", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    if (qsee_cipher_decrypt(ctx, ciphertext, ciphertextLen, plaintext, (uint32_t *)&decryptLen) < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: qsee_cipher_decrypt API failed", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    qsee_log(QSEE_LOG_MSG_DEBUG, "%s: ciphertextLen = %d, decryptLen = %d", __FUNCTION__, ciphertextLen, decryptLen);
    *pPlaintextLen = decryptLen;

exit:
    if (ctx) {
        if (qsee_cipher_free_ctx(ctx) < 0) {
            qsee_log(QSEE_LOG_MSG_ERROR,"%s: qsee_cipher_free_ctx API failed", __FUNCTION__);
            ret = TZ_API_ERROR;
        }
        ctx = 0;
    }

    return ret;
}

uint32_t TZ_aes_cbc_256_decrypt_with_iv(
    uint8_t *encKey,
    uint32_t encKeyLen,
    uint8_t *ciphertext,
    uint32_t ciphertextLen,
    uint8_t *plaintext,
    uint32_t *pPlaintextLen,
    uint8_t *iv,
    uint32_t ivLen
)
{
    if ((NULL == encKey) || (NULL == ciphertext) || (NULL == plaintext) || (NULL == pPlaintextLen) || (NULL == iv)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: invalid input", __FUNCTION__);
        return TZ_API_ERROR;
    }

    CIPHER_MODE_ET cipherMode = CIPHER_MODE_CBC;
    CIPHER_PAD_ET cipherPad = CIPHER_PAD_PKCS7;

    return (TZ_aes_decrypt_with_parameters(encKey, encKeyLen, ciphertext, ciphertextLen, plaintext, pPlaintextLen,
                                           cipherMode, cipherPad, iv, ivLen));
}

/* 
 * It extracts IV header from the wrapped data and decrypts the wrapped data with the IV.
 * TZ_unwrap_persist_data
 */
uint32_t TZ_unwrap_persist_data_aes_cbc_256(
    uint8_t *appName,
    uint32_t appNameLen,
    uint8_t *wrapped_data,
    uint32_t wrapped_datalen,
    uint8_t *data,
    uint32_t *datalen)
{
    uint32_t ret = TZ_API_OK;
    persist_data_header_t header = { 0 };
    uint32_t headerLen = sizeof(header);
    uint8_t keyBuffer[AES256_KEY_SIZE] = { 0 };
    uint32_t keyLen = sizeof(keyBuffer);
    uint8_t digest[SHA256_DIGEST_LENGTH] = { 0 };
    uint32_t digestLen = sizeof(digest);

    if ((appName==NULL) || (data == NULL) || (wrapped_data == NULL) || (wrapped_datalen == 0)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s invalid input buffer", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    memcpy(&header, (persist_data_header_t *)wrapped_data, headerLen);
    if (header.version != PERSIST_DATA_HEADER_VERSION || header.magic != PERSIST_DATA_HEADER_MAGIC) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s Invalid persist data header version", __FUNCTION__);
        ret = TZ_API_ERROR_VERSION_MISMATCH;
        goto exit;
    }

    /* Derive key using app name as salt */
    ret = TZ_derive_key_aes(appName, appNameLen, keyBuffer, keyLen);
    if (ret != TZ_API_OK) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: TZ_derive_key_aes returns an error", __FUNCTION__);
        goto exit;
    }

    ret = TZ_aes_cbc_256_decrypt_with_iv(keyBuffer, keyLen, wrapped_data + headerLen, wrapped_datalen - headerLen, data, datalen, header.iv, header.iv_len);
    if (ret != TZ_API_OK) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: TZ_aes_decrypt() returns an error", __FUNCTION__);
        goto exit;
    }

    if (*datalen < SHA256_DIGEST_LENGTH) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: the length of unwrapped data is invalid", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    }

    ret = TZ_SHA256_digest(data, *datalen - SHA256_DIGEST_LENGTH, digest, &digestLen);
    if (ret != TZ_API_OK) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: TZ_SHA256_digest() returns an error", __FUNCTION__);
        goto exit;
    }

    if (0 != memcmp(digest, data+*datalen-digestLen, digestLen)) {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s: the unwrapped data has integrity error", __FUNCTION__);
        ret = TZ_API_ERROR;
        goto exit;
    } 
    *datalen = *datalen - digestLen;

exit:
    memset(keyBuffer, 0 , keyLen);
    return ret;
}
