/*
 * =====================================================================================
 *
 *       Filename:  satsServiceJni.c
 *
 *    Description:  JNI for SATS service for installation of device keys.
 *
 *        Version:  1.0
 *        Created:  03/13/2017 03:26:51 PM
 *       Compiler:  armcc
 *
 *         Author:  Dongwook Shim (), dw.shim@samsung.com
 *        Company:  Samsung Electronics
 *
 *        Copyright (c) 2017 by Samsung Electronics, All rights reserved.
 *
 * =====================================================================================
 */

#include <string.h>
#include <errno.h>
#include "drkServices.h"

#include <vendor/samsung/hardware/security/drk/2.0/ISehDrk.h>
#include <vendor/samsung/hardware/security/drk/2.0/types.h>
#include <SehDrk.h>

#include "Log.h"
#include "Tlv.h"
#include "systemConfig.h"
#include "msgHandler.h"

#ifndef USE_RELEASE
extern int32_t selftestProvServiceBlob(uint8_t *serviceName, int keytype, uint8_t *tlv, uint32_t tlvLen, uint8_t *serviceCert,
                                       uint32_t *serviceCertLen);
#endif // End of USE_RELEASE

using namespace vendor::samsung::hardware::security::drk;
using ::vendor::samsung::hardware::security::drk::V2_0::ISehDrk;
using ::vendor::samsung::hardware::security::drk::V2_0::SehCommand;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;

int32_t VFI_appendVndFile(char *filename, uint8_t *buf, uint32_t size);

/**********************************************************************************************
 *
 * Utils Function Implementation Part
 *
 * *******************************************************************************************/
static int32_t checkVndAlive(const Return<void>& ret)
{
    if (ret.isDeadObject()) {
        return ERR_VND_DEAD_OBJECT;
    }

    if (!ret.isOk()) {
        return ERR_VND_DEAD_OBJECT;
    }

    return NOT_ERROR;
}

static hidl_vec<uint8_t> byte2vec(const uint8_t *data, uint32_t length)
{
    FILE *fp = NULL;
    hidl_vec<uint8_t> result;
    char package[PACKAGE_NAME_LEN + 1] = {0};
    char filePath[MAX_FILE_PATH_LEN + 1] = {0};

    snprintf(filePath, MAX_FILE_PATH_LEN, "/proc/%d/cmdline", getpid());
    if ((fp = fopen(filePath, "rb")) != NULL) {
        if(fread(package, 1, PACKAGE_NAME_LEN, fp) == 0){
            LOGMI("Failed in reading package length");
        }
        fclose(fp);

        if (length < (MAX_BODY_SIZE - PACKAGE_NAME_LEN)) {
            result.resize(length + PACKAGE_NAME_LEN);
            memcpy(result.data(), package, PACKAGE_NAME_LEN);
            if (data != NULL && length > 0) {
                memcpy(result.data() + PACKAGE_NAME_LEN, data, length);
            }
        }
    } else {
        LOGME("%s / reason : %s", (char *)filePath, strerror(errno));
    }

    return result;
}

static int32_t vec2byte(const hidl_vec<uint8_t>& hv, uint8_t **out, uint32_t *length)
{
    if (out == NULL || length == NULL) {
        LOGME("Invalid input parameter, blob = NULL");
        return -1;
    }

    *length = (uint32_t)hv.size();
    if (*length > MAX_BODY_SIZE || *length == 0) {
        LOGME("Response buffer size is %u", *length);
        *out = NULL;
        return -2;
    }

    *out = (uint8_t *)calloc(*length, sizeof(uint8_t));
    if (*out == NULL) {
        LOGME("malloc was failed.");
        return -3;
    }

    memcpy((void *)*out, (void *)hv.data(), hv.size());
    return 0;
}

static void updateCertificateIssuedLog(char *serviceName, int32_t retCode)
{
    time_t      lc_log_time_t;
    struct tm   lc_log_tm;
    char        lc_log_buf[UNIT_LOG_MSG_LEN + 1] = {0};
    int         lc_len = 0;

    if (serviceName == NULL) {
        return;
    }

    lc_log_time_t = time(NULL);
    lc_log_tm = *localtime(&lc_log_time_t);

    if (retCode == RET_OK) {
        lc_len = snprintf(lc_log_buf, UNIT_LOG_MSG_LEN, "%02d-%02d %02d:%02d:%02d ServiceName : %s, Success.\n",
                          lc_log_tm.tm_mon += 1,  lc_log_tm.tm_mday,
                          lc_log_tm.tm_hour, lc_log_tm.tm_min, lc_log_tm.tm_sec, serviceName);
    } else {
        lc_len = snprintf(lc_log_buf, UNIT_LOG_MSG_LEN, "%02d-%02d %02d:%02d:%02d ServiceName : %s, Failure. (%X)\n",
                          lc_log_tm.tm_mon += 1,  lc_log_tm.tm_mday,
                          lc_log_tm.tm_hour, lc_log_tm.tm_min, lc_log_tm.tm_sec, serviceName, retCode);
    }

    VFI_appendVndFile((char *)"issued.log", (uint8_t *)lc_log_buf, lc_len);
    return;
}

/**********************************************************************************************
 *
 * Internal Function Implementation Part
 *
 * *******************************************************************************************/
static int32_t DRKAPI_process_cmd(int32_t cmd, uint8_t *in, uint32_t in_len, uint8_t **out, uint32_t *out_len)
{
    int32_t result = 0,
            lcResult = 0,
            retry_count = 0;
    const int32_t RETRY_COUNT = 6;
    int32_t retry_time[RETRY_COUNT] = {1, 1, 2, 3, 5, 0};

    SehCommand req_data;
    SehCommand out_data;

    android::sp<ISehDrk> servcli = ISehDrk::getService();
    if (servcli == nullptr) {
        LOGME("[WARN] the instance of vendor drk service cannot be gotten.");
        return -1;
    }

    memset(&req_data, 0x0, sizeof(SehCommand));
    memset(&out_data, 0x0, sizeof(SehCommand));

    req_data.id   = cmd;
    req_data.data = byte2vec(in, in_len);

    do {
        lcResult = checkVndAlive(servcli->communicate(req_data, [&](int32_t rv, const SehCommand & rep) {
            result   = rv;
            out_data = rep;
        }));

        if(lcResult == NOT_ERROR && result == ERR_LOCK_TO_HOLD) {
            LOGME("%s Service is busy. sleep %d sec ", __func__, retry_time[retry_count]);
            memset(&out_data, 0x0, sizeof(SehCommand));
            sleep(retry_time[retry_count++]);
        } else {
            break;
        }
    } while(retry_count < RETRY_COUNT);

    if (lcResult == NOT_ERROR) {
        if (out != NULL && out_len != NULL) {
            vec2byte(out_data.data, out, out_len);
        }
    } else {
        result = lcResult;
    }
    return result;
}

static int32_t DRKAPI_process_cmd_1way(int32_t cmd, uint8_t *in, uint32_t in_len)
{
    return DRKAPI_process_cmd(cmd, in, in_len, NULL, NULL);
}

static int32_t DRKAPI_process_gen_csr(int32_t cmd, uint8_t *timestamp, uint32_t timestamp_len, uint8_t *branchId, uint32_t branchId_len, uint8_t *out, uint32_t *out_len)
{
    int32_t   ret = NOT_ERROR;
    uint32_t  lcOutLen = 0;
    uint8_t  *lcOut = NULL;
    TLV       lcTlv(TLV_START);
    Bytes     lcSysInfo;

    if (timestamp == NULL || branchId == NULL || out == NULL || out_len == NULL) {
        ret = ERR_COMMON_INVALID_ARGUMENT;
        goto end;
    }

    if ((ret = lcTlv.add(TLV_TIMESTAMP, timestamp, timestamp_len)) != NOT_ERROR) {
        LOGME("To add ts into tlv is failed. (%d)", ret);
        goto end;
    }

    if ((ret = lcTlv.add(TLV_BRANCHID, branchId, branchId_len)) != NOT_ERROR) {
        LOGME("To add bid into tlv is failed. (%d)", ret);
        goto end;
    }

    if ((ret = lcTlv.encode(lcSysInfo)) != NOT_ERROR) {
        LOGME("To encode tlv is failed. (%d)", ret);
        goto end;
    }

    if ((ret = DRKAPI_process_cmd(cmd, (uint8_t *)lcSysInfo, lcSysInfo.length(),
                                  &lcOut, &lcOutLen)) != NOT_ERROR) {
        LOGME("The vnd process return error. (%d)", ret);
        goto end;
    }

    if (*out_len > lcOutLen) {
        memcpy(out, lcOut, lcOutLen);
        *out_len = lcOutLen;
    }

end:
    if (lcOut) {
        memset(lcOut, 0x0, lcOutLen);
        free(lcOut);
    }
    return ret;
}

static int32_t DRKAPI_process_gen_servkey(uint8_t *serviceName, int32_t keyType, uint8_t *tlv, uint32_t tlv_len, uint8_t *out, uint32_t *out_len)
{
    int32_t   ret = NOT_ERROR;
    uint32_t  lcOutLen = 0;
    uint8_t  *lcOut = NULL;
    TLV       lcTlv(TLV_START);
    Bytes     lcReqInfo;

    ServiceKeyInfo_t serviceKeyInfo;

    memset(&serviceKeyInfo, 0, sizeof(serviceKeyInfo));

    if (serviceName == NULL || out == NULL || out_len == NULL) {
        ret = ERR_COMMON_INVALID_ARGUMENT;
        goto end;
    }

    switch (keyType) {
        case KEY_TYPE_RSA :
            serviceKeyInfo.keyType = keyType;
            if (!strncmp((char *)serviceName, (char *)MCPAY_SERV_NAME, strlen((char *)MCPAY_SERV_NAME))) {
                serviceKeyInfo.keyLength = RSA_BIT_SIZE_MASTER_CARD;
            } else {
                serviceKeyInfo.keyLength = RSA_BIT_SIZE_DEFAULT;
            }
            break;
        case KEY_TYPE_EC :
            serviceKeyInfo.keyType = keyType;
            serviceKeyInfo.keyLength = EC_BIT_SIZE_DEFAULT;
            break;
        default :
            LOGME("Unsupported key type %d.", keyType);
            return ERR_INVALID_ARGUMENT;
    }

    snprintf(serviceKeyInfo.serviceName, MAX_SERVICE_NAME + 1, "%s", serviceName);
    if ((ret = lcTlv.add(TLV_KEY_INFO, (uint8_t *)&serviceKeyInfo, (uint32_t)sizeof(serviceKeyInfo))) != NOT_ERROR) {
        LOGME("To add ts into tlv is failed. k(%d)", ret);
        goto end;
    }

    if (tlv != NULL) {
        if ((ret = lcTlv.add(TLV_ATTRS, tlv, tlv_len)) != NOT_ERROR) {
            LOGME("To add bid into tlv is failed. t(%d, %d)", ret, tlv_len);
            goto end;
        }
    }

    if ((ret = lcTlv.encode(lcReqInfo)) != NOT_ERROR) {
        LOGME("To encode tlv is failed. (%d)", ret);
        goto end;
    }

    ret = DRKAPI_process_cmd(CMD_GENERATE_SERVICE_KEY, (uint8_t *)lcReqInfo, lcReqInfo.length(), &lcOut, &lcOutLen);
    updateCertificateIssuedLog((char *)serviceName, ret);
    if (ret != NOT_ERROR) {
        LOGME("The vnd process return error. (%d)", ret);
        goto end;
    }

    if (*out_len > lcOutLen) {
        memcpy(out, lcOut, lcOutLen);
        *out_len = lcOutLen;
    }

end:
    if (lcOut) {
        memset(lcOut, 0x0, lcOutLen);
        free(lcOut);
    }
    return ret;
}


static int32_t DRKAPI_process_install(int32_t keyType, uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len)
{
    int32_t   ret = NOT_ERROR;
    uint8_t  *pKeyBlob = NULL;
    uint32_t  keyBlobLen = 0;
    Bytes     temp, lcCert;

    if (in == NULL || in_len == 0 || (in_len % 4) != 0) {
        ret = ERR_INVALID_ARGUMENT;
        goto end;
    }
    temp.set(in, in_len);
    if (temp.b64Decode(BASE64_BASIC, lcCert) != NOT_ERROR) {
        ret = ERR_BASE64_CODING_FAILED;
        goto end;
    }

    switch (keyType) {
        case KEY_GOOGLE_ATTESTATION :   // Base64 format.
        case KEY_STRONGBOX_ATTESTATION :
            if ((ret = DRKAPI_process_cmd(CMD_UNWRAP_GAK_BLOB, (uint8_t *)lcCert, lcCert.length(), &pKeyBlob, &keyBlobLen)) != NOT_ERROR) {
                goto end;
            }

            if (out != NULL && out_len != NULL && *out_len > keyBlobLen) {
                memcpy(out, pKeyBlob, keyBlobLen);
                *out_len = keyBlobLen;
            } else {
                ret = ERR_BUFFER_OVERFLOW;
            }
            break;
        case KEY_DRK_V2 :
            ret = DRKAPI_process_cmd_1way(CMD_INSTALL_DRK_CERT, (uint8_t *)lcCert, lcCert.length());
            break;
        default :
            LOGME("Unsupported key type(%d).", keyType);
            ret = ERR_INVALID_ARGUMENT;
            break;
    }

end :
    if (pKeyBlob != NULL) {
        memset(pKeyBlob, 0, keyBlobLen);
        free(pKeyBlob);
    }
    return ret;
}

#ifndef USE_RELEASE
int32_t DRKAPI_process_cmd_wrap(int32_t cmd, uint8_t *in, uint32_t in_len, uint8_t **out, uint32_t *out_len)
{
    return DRKAPI_process_cmd(cmd, in, in_len, out, out_len);
}
#endif

/**********************************************************************************************
 *
 * VND FILE Function Implementation Part
 *
 * *******************************************************************************************/

int32_t VFI_readVndFile(char *filename, uint8_t **buf)
{
    const int32_t     command = CMD_VND_READFILE;
    int32_t           ret     = NOT_ERROR;
    FILEBLOB          fblob;
    Bytes             lcData;
    uint8_t           *out     = NULL;
    uint32_t           out_len = 0;

    LOGMI("%s start", __func__);
    if (filename == NULL || buf == NULL) {
        LOGME("%s Invalid argument.", __func__);
        ret = ERR_COMMON_INVALID_ARGUMENT;
        goto err;
    }

    ret = fblob.name.set((uint8_t *)filename, (uint32_t)strlen(filename));
    if (ret != NOT_ERROR) {
        LOGME("Bytes assign was failed. (%d)", ret);
        goto err;
    }

    ret = msg_encode_cli(command, (void *)&fblob, lcData);
    if (ret != NOT_ERROR) {
        LOGME("encoding was failed. (%d)", ret);
        ret = ERR_TLV_ENCODE_FAILED;
        goto err;
    }
    ret = DRKAPI_process_cmd(command, (uint8_t *)lcData, lcData.length(), &out, &out_len);
    if (ret != NOT_ERROR) {
        LOGME("process_cmd was failed. (%d)", ret);
        goto err;
    }

    lcData.empty();
    ret = lcData.set(out, out_len);
    if (ret != NOT_ERROR) {
        LOGME("output Bytes assign was failed. (%d)", ret);
        goto err;
    }
    ret = msg_decode_cli(command, lcData, (void *)&fblob);
    if (ret != NOT_ERROR) {
        LOGME("decoding was failed. (%d)", ret);
        ret = ERR_TLV_DECODE_FAILED;
        goto err;
    }

    if ((*buf = (uint8_t *)calloc(fblob.data.length() + 1, sizeof(uint8_t))) != NULL) {
        memcpy(*buf, (uint8_t *)fblob.data, fblob.data.length());
        ret = fblob.data.length();
    } else {
        LOGME("Failed to calloc buffer. : %s", strerror(errno));
        ret = ERR_COMMON_MALLOC_FAILED;
    }

err:
    if (out) {
        memset(out, 0x0, out_len);
        free(out);
        out = NULL;
        out_len = 0;
    }
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

int32_t VFI_writeVndFile(char *filename, uint8_t *buf, uint32_t size)
{
    const int32_t     command = CMD_VND_SAVEFILE;
    int32_t   ret = NOT_ERROR;

    FILEBLOB fblob;
    Bytes    req;

    LOGMI("%s start", __func__);
    if (filename == NULL || buf == NULL) {
        LOGME("%s Invalid argument.", __func__);
        ret = ERR_COMMON_INVALID_ARGUMENT;
        goto err;
    }
    ret = fblob.name.set((uint8_t *)filename, (uint32_t)strlen(filename));
    if (ret != NOT_ERROR) {
        LOGME("Bytes assign was failed. (%d)", ret);
        goto err;
    }
    ret = fblob.data.set(buf, size);
    if (ret != NOT_ERROR) {
        LOGME("Bytes assign was failed. (%d)", ret);
        goto err;
    }
    ret = msg_encode_cli(command, (void *)&fblob, req);
    if (ret != NOT_ERROR) {
        LOGME("encoding was failed. (%d)", ret);
        ret = ERR_TLV_ENCODE_FAILED;
        goto err;
    }
    ret = DRKAPI_process_cmd_1way(command, (uint8_t *)req, req.length());
    if (ret != NOT_ERROR) {
        LOGME("process_cmd was failed. (%d)", ret);
        goto err;
    }
err:
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

int32_t VFI_appendVndFile(char *filename, uint8_t *buf, uint32_t size)
{
    const int32_t     command = CMD_VND_APPENDFILE;
    int32_t   ret = NOT_ERROR;

    FILEBLOB fblob;
    Bytes    req;

    LOGMI("%s start", __func__);
    if (filename == NULL || buf == NULL) {
        LOGME("%s Invalid argument.", __func__);
        ret = ERR_COMMON_INVALID_ARGUMENT;
        goto err;
    }
    ret = fblob.name.set((uint8_t *)filename, (uint32_t)strlen(filename));
    if (ret != NOT_ERROR) {
        LOGME("Bytes assign was failed. (%d)", ret);
        goto err;
    }
    ret = fblob.data.set(buf, size);
    if (ret != NOT_ERROR) {
        LOGME("Bytes assign was failed. (%d)", ret);
        goto err;
    }
    ret = msg_encode_cli(command, (void *)&fblob, req);
    if (ret != NOT_ERROR) {
        LOGME("encoding was failed. (%d)", ret);
        ret = ERR_TLV_ENCODE_FAILED;
        goto err;
    }
    ret = DRKAPI_process_cmd_1way(command, (uint8_t *)req, req.length());
    if (ret != NOT_ERROR) {
        LOGME("process_cmd was failed. (%d)", ret);
        goto err;
    }
err:
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}


int32_t VFI_processPdpCmd(int32_t commandId, uint8_t *buf, uint32_t *bufLen)
{
    int32_t   ret = NOT_ERROR;
    uint8_t  *out = NULL;
    uint32_t  outLen = 0;

    LOGMI("%s start", __func__);

    ret = DRKAPI_process_cmd(commandId, NULL, 0, &out, &outLen);
    if (ret != NOT_ERROR) {
        LOGME("process_cmd was failed. (%d)", ret);
        goto err;
    }

    if (outLen > 0) {
        if (buf != NULL && bufLen != NULL) {
            if (*bufLen > outLen) {
                memcpy(buf, out, outLen);
                *bufLen = outLen;
            } else {
                *bufLen = 0;
                LOGME("Failed to copy buffer.");
                ret =  ERR_BUFFER_OVERFLOW;
            }
        } else {
            LOGME("%s Invalid output argument.", __func__);
            ret = ERR_COMMON_INVALID_ARGUMENT;
        }
    } else {
        if (bufLen != NULL) {
            *bufLen = 0;
        }
    }

err:
    if (out) {
        memset(out, 0x0, outLen);
        free(out);
        out = NULL;
        outLen = 0;
    }
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

/**********************************************************************************************
 *
 * Key Install Function Implementation Part
 *
 * *******************************************************************************************/

int32_t KFI_isSupportedDrkV2()
{
    LOGMI("%s start", __func__);
    return DRKAPI_process_cmd_1way(CMD_IS_SUPPORTED_DRK_V2, NULL, 0);
}

int32_t KFI_isExistDRK(int32_t keyType)
{
    LOGMI("%s start", __func__);
    if (keyType != KEY_TYPE_RSA && keyType != KEY_TYPE_EC) {
        LOGME("%s Invalid argument. (keyType : %d)", __func__, keyType);
        return ERR_INVALID_ARGUMENT;
    }
    return DRKAPI_process_cmd_1way(CMD_VERIFY_DRK_KEY, NULL, 0);
}

int32_t KFI_readDrkUID(int32_t keyType, uint8_t *out, uint32_t *out_len)
{
    int32_t  ret = NOT_ERROR;
    uint8_t *drkUid = NULL;
    uint32_t drkUidLen = 0;

    LOGMI("%s start", __func__);
    if (keyType != KEY_TYPE_RSA && keyType != KEY_TYPE_EC) {
        LOGME("%s Invalid argument. (keyType : %d)", __func__, keyType);
        return ERR_INVALID_ARGUMENT;
    }

    if (out == NULL || out_len == NULL) {
        LOGME("%s Invalid argument.", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = DRKAPI_process_cmd(CMD_GET_DRK_UID, NULL, 0, &drkUid, &drkUidLen)) == NOT_ERROR) {
        if (*out_len > drkUidLen) {
            *out_len = drkUidLen;
            memcpy(out, drkUid, drkUidLen);
        } else {
            ret = ERR_BUFFER_OVERFLOW;
        }
    }

    if (drkUid) {
        memset(drkUid, 0x0, drkUidLen);
        free(drkUid);
    }
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

int32_t KFI_generateCertificateSigningRequest(uint8_t *timeStamp, uint8_t *branchId, uint8_t *out, uint32_t *out_len)
{
    int32_t    ret = NOT_ERROR;
    uint8_t    csrData[CSR_DATA_MAX_SIZE] = {0};
    uint32_t   csrDataLen = CSR_DATA_MAX_SIZE, timeStampLen = 0, branchIdLen = 0;

    LOGMI("%s start", __func__);
    if (timeStamp == NULL || branchId == NULL || out == NULL || out_len == NULL || *out_len == 0) {
        LOGME("%s Invalid argument.", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    timeStampLen = (uint32_t)strlen((char *)timeStamp);
    branchIdLen  = (uint32_t)strlen((char *)branchId);
    if (timeStampLen != CSR_TIMESTAMP_LEN || branchIdLen != CSR_BRANCH_ID_LEN) {
        LOGME("%s Invalid argument. about lengths", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = DRKAPI_process_gen_csr(CMD_MAKE_DRK_CSR, timeStamp, timeStampLen, branchId, branchIdLen,
                                          (uint8_t *)csrData, &csrDataLen)) == NOT_ERROR) {
        if (*out_len > csrDataLen) {
            *out_len = csrDataLen;
            memcpy(out, csrData, csrDataLen);
        } else {
            ret = ERR_BUFFER_OVERFLOW;
        }
    }
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

int32_t KFI_generateEncryptedCSR(uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len)
{
    int32_t  ret = NOT_ERROR;
    uint8_t *ecsr = NULL;
    uint32_t ecsrLen = 0;

    LOGMI("%s start", __func__);
    if (in == NULL || in_len == 0 || out == NULL) {
        LOGME("%s Invalid argument.", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = DRKAPI_process_cmd(CMD_ENCRYPT_SAK_CSR, in, in_len, &ecsr, &ecsrLen)) == NOT_ERROR) {
        if (*out_len > ecsrLen) {
            *out_len = ecsrLen;
            memcpy(out, ecsr, ecsrLen);
        } else {
            ret = ERR_BUFFER_OVERFLOW;
        }
    }

    if (ecsr) {
        memset(ecsr, 0x0, ecsrLen);
        free(ecsr);
    }
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}


int32_t KFI_generateTobesignedCSR(uint8_t *timeStamp, uint8_t *branchId, uint8_t *model, uint32_t *model_len, uint8_t *huid, uint32_t *huid_len, uint8_t *num, uint32_t *num_len)
{
    int32_t    ret = NOT_ERROR;
    uint8_t    csrData[CSR_DATA_MAX_SIZE] = {0};
    uint32_t   csrDataLen = CSR_DATA_MAX_SIZE, timeStampLen = 0, branchIdLen = 0;
    TLV        lcTlv;

    LOGMI("%s start", __func__);
    if (timeStamp == NULL || branchId == NULL) {
        LOGME("%s Invalid argument.", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    timeStampLen = (uint32_t)strlen((char *)timeStamp);
    branchIdLen  = (uint32_t)strlen((char *)branchId);
    if (timeStampLen != CSR_TIMESTAMP_LEN || branchIdLen != CSR_BRANCH_ID_LEN) {
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = DRKAPI_process_gen_csr(CMD_MAKE_TBS_CSR, timeStamp, timeStampLen, branchId, branchIdLen,
                                          (uint8_t *)csrData, &csrDataLen)) != NOT_ERROR) {
        goto end;
    }

    if ((ret = lcTlv.decode(csrData, csrDataLen)) != NOT_ERROR) {
        goto end;
    }

    if ((ret = lcTlv.get(TLV_MODEL_NAME, model, model_len)) != NOT_ERROR) {
        goto end;
    }

    if ((ret = lcTlv.get(TLV_HUID, huid, huid_len)) != NOT_ERROR) {
        goto end;
    }

    if ((ret = lcTlv.get(TLV_IMEI, num, num_len)) != NOT_ERROR) {
        goto end;
    }
end:
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

int32_t KFI_installDeviceUnBoundCertificate(int32_t keyType, uint8_t *cert, uint32_t cert_len, uint8_t *unwrapBlob, uint32_t *unwrapBlobLen)
{
    LOGMI("%s start", __func__);
    return DRKAPI_process_install(keyType, cert, cert_len, unwrapBlob, unwrapBlobLen);
}

int32_t KFI_installDeviceBoundCertificate(int32_t keyType, uint8_t *cert, uint32_t cert_len)
{
    LOGMI("%s start", __func__);
    return DRKAPI_process_install(keyType, cert, cert_len, NULL, NULL);
}

int32_t KFI_setPdpData()
{
    LOGMI("%s start", __func__);
    return DRKAPI_process_cmd_1way(CMD_VND_SET_PDP_DATA, NULL, 0);
}

int32_t KFI_checkPdpStatus()
{
    LOGMI("%s start", __func__);
    return DRKAPI_process_cmd_1way(CMD_VND_CHECK_PDP_DATA, NULL, 0);
}

/**********************************************************************************************
 *
 * Service Function Implementation Part
 *
 * *******************************************************************************************/
int32_t  SFI_generateServiceKey(uint8_t *serviceName, int32_t keyType, uint8_t *tlv, uint32_t tlv_len, uint8_t *out, uint32_t *out_len)
{
    LOGMI("%s start", __func__);
    if (keyType != KEY_TYPE_RSA && keyType != KEY_TYPE_EC) {
        LOGMI("%s error. keyType :%d", __func__, keyType);
        return ERR_INVALID_ARGUMENT;
    }

    return DRKAPI_process_gen_servkey(serviceName, keyType, tlv, tlv_len, out, out_len);
}

void SFI_releaseSession()
{
    LOGMI("%s start", __func__);
    DRKAPI_process_cmd_1way(CMD_RELEASE_GEN_SERV_KEY, NULL, 0);
    return;
}

int32_t SFI_getDrkCertificate(int32_t keyType, uint8_t *out, uint32_t *out_len)
{
    int32_t  ret = NOT_ERROR;
    uint8_t *drkCert = NULL;
    uint32_t drkCertLen = 0;

    LOGMI("%s start", __func__);
    if (keyType != KEY_TYPE_RSA && keyType != KEY_TYPE_EC) {
        LOGME("%s Invalid argument.(keyType : %d)", __func__, keyType);
        return ERR_INVALID_ARGUMENT;
    }

    if (out == NULL || out_len == NULL) {
        LOGME("%s Invalid argument.", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = DRKAPI_process_cmd(CMD_GET_DRK_CERT, NULL, 0, &drkCert, &drkCertLen)) == NOT_ERROR) {
        if (*out_len > drkCertLen) {
            *out_len = drkCertLen;
            memcpy(out, drkCert, drkCertLen);
        } else {
            ret = ERR_BUFFER_OVERFLOW;
        }
    }
    if (drkCert) {
        memset(drkCert, 0x0, drkCertLen);
        free(drkCert);
    }
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

int32_t SFI_getDeviceInformation(uint8_t *out, uint32_t *out_len)
{
    int32_t  ret = NOT_ERROR;
    uint8_t *devInfo = NULL;
    uint32_t devInfoLen = 0;

    LOGMI("%s start", __func__);
    if (out == NULL || out_len == NULL) {
        LOGME("%s Invalid argument.", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = DRKAPI_process_cmd(CMD_GET_DEV_INFO, NULL, 0, &devInfo, &devInfoLen)) == NOT_ERROR) {
        if (*out_len > devInfoLen) {
            *out_len = devInfoLen;
            memcpy(out, devInfo, devInfoLen);
        } else {
            ret = ERR_BUFFER_OVERFLOW;
        }
    }

    if (devInfo) {
        memset(devInfo, 0x0, devInfoLen);
        free(devInfo);
    }
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

int32_t SFI_getBigDataMessage(uint8_t *serviceName, uint8_t *bigData, uint32_t *bigData_len)
{
    int32_t  ret = NOT_ERROR;
    uint8_t *bdMsg = NULL;
    uint32_t bdMsgLen = 0;

    LOGMI("%s start", __func__);
    if (serviceName == NULL || bigData == NULL || bigData_len == NULL) {
        LOGME("%s Invalid argument.", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = DRKAPI_process_cmd(CMD_GET_BIG_DATA, serviceName, strlen((char *)serviceName), &bdMsg, &bdMsgLen)) == NOT_ERROR) {
        if (*bigData_len > bdMsgLen) {
            *bigData_len = bdMsgLen;
            memcpy(bigData, bdMsg, bdMsgLen);
        } else {
            ret = ERR_BUFFER_OVERFLOW;
        }
    }
    if (bdMsg) {
        memset(bdMsg, 0x0, bdMsgLen);
        free(bdMsg);
    }
    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

int32_t SFI_getPdpData(uint8_t *pdpData, uint32_t *pdpData_len)
{
    int32_t  ret = NOT_ERROR;
    uint8_t *pdMsg = NULL;
    uint32_t pdMsgLen = 0;

    LOGMI("%s start", __func__);

    if (pdpData == NULL || pdpData_len == NULL) {
        LOGME("%s Invalid argument.", __func__);
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = DRKAPI_process_cmd(CMD_VND_GET_PDP_DATA, NULL, 0, &pdMsg, &pdMsgLen)) == NOT_ERROR) {
        if (pdMsg != NULL && *pdpData_len > pdMsgLen) {
            *pdpData_len = pdMsgLen;
            memcpy(pdpData, pdMsg, pdMsgLen);
        } else {
            ret = ERR_BUFFER_OVERFLOW;
        }
    }

    if (pdMsg) {
        memset(pdMsg, 0x0, pdMsgLen);
        free(pdMsg);
    }

    LOGMI("%s end (%d)", __func__, ret);
    return ret;
}

NATIVE_FUNC_LIST gProvFunctionList = {
    KFI_isSupportedDrkV2,
    KFI_generateCertificateSigningRequest,
    KFI_generateTobesignedCSR,
    KFI_generateEncryptedCSR,
    KFI_installDeviceUnBoundCertificate,
    KFI_installDeviceBoundCertificate,
    KFI_setPdpData,
    KFI_checkPdpStatus,
    KFI_isExistDRK,
    KFI_readDrkUID,
    SFI_getDrkCertificate,
    SFI_getDeviceInformation,
    SFI_releaseSession,
    SFI_generateServiceKey,
    SFI_getBigDataMessage,
    SFI_getPdpData,
    VFI_readVndFile,
    VFI_writeVndFile,
    VFI_appendVndFile,
    VFI_processPdpCmd,
#ifdef USE_RELEASE
    NULL,
#else
    selftestProvServiceBlob,
#endif
};

#pragma GCC visibility push(default)

NATIVE_FUNC_LIST *getFunctionList()
{
    return &gProvFunctionList;
}
#pragma GCC visibility pop
// End of PROV function list
