#include <string.h>
#include <stdio.h>
#include <openssl/sha.h>
#include <sys/system_properties.h>
#include "version.h"
#include "Log.h"
#include "File.h"
#include "Tlv.h"
#include "B64Coder.h"
#include "ril/rilCommands.h"
#include "DeviceInfo.h"

#define MODEL_MANUFACTURER_PART "SAMSUNG-"
#define TOTAL_MATCH_VAL  3
#define CMP_UNIT_SIZE    44
#define CP_HASH          26
#define AP_HASH          CP_HASH + CMP_UNIT_SIZE + 1
#define MAX_HUID_LEN     115

//UFS_CID_PATH is removed from Android P
//Reason    : The path of unique number must be hidden.
//Requester : grant.jung    System R&D Group 1, jkchoi90      System R&D Team
extern "C" int getUfsCidPath(uint8_t *out, uint32_t *outLen);

//UFS_CID_PATH is removed from Android P
//Reason    : It is impossible to use previous UN path becase of GKI(Generic Kernel Interface)
//Requester : grant.jung    System R&D Group 1
extern "C" int getUfsCidPathV2(uint8_t *out, uint32_t *outLen);

namespace vendor   {
namespace samsung  {
namespace hardware {
namespace security {
namespace drk      {


int32_t DeviceInfo::matchUidString(uint8_t *uid, uint32_t uidLen, uint8_t *p1,  uint32_t p1Len)
{
    int32_t    ret    = NOT_ERROR;
    uint8_t   *pB64   = NULL;
    int32_t    b64Len = 0;

    if (uid == NULL || p1 == NULL) {
        LOGME("Invalid argument in matchUidString");
        return ERR_INVALID_ARGUMENT;
    }

    if ((pB64 = B64Coder::B64Encoding(p1, p1Len, B64_SAFE_ALPHABET)) == NULL) {
        LOGME("Failed to make b64");
        ret = ERR_BASE64_CODING_FAILED;
        goto end;
    }
    b64Len = strlen((char *)pB64);

    if (memcmp(uid, pB64, uidLen) == 0) {
        ret = DEVICE_STATUS_IS_VALID;
    } else {
        ret = DEVICE_STATUS_IS_INVALID;
    }

end:
    if (pB64 != NULL) {
        memset(pB64, 0, b64Len);
        free(pB64); pB64 = NULL;
    }
    return ret;
}

int32_t DeviceInfo::matchAPUidString(uint8_t *uid, uint32_t uidLen, uint8_t *p1,  uint32_t p1Len,
                                     uint8_t *p2, uint32_t p2Len)
{
    int32_t     ret = NOT_ERROR;
    SHA256_CTX  sha256ctx;
    uint8_t     sha256Ap[SHA256_DIGEST_LENGTH] = {0};

    if (uid == NULL || p1 == NULL || p2 == NULL) {
        LOGME("Invalid argument in matchAPUidString");
        return ERR_INVALID_ARGUMENT;
    }

    memset(sha256Ap, 0, sizeof(sha256Ap));

    SHA256_Init(&sha256ctx);
    SHA256_Update(&sha256ctx, p1, p1Len);
    SHA256_Update(&sha256ctx, p2, p2Len);
    SHA256_Final(sha256Ap, &sha256ctx);

    ret = matchUidString(uid, uidLen, sha256Ap, sizeof(sha256Ap));

    memset(&sha256ctx, 0, sizeof(sha256ctx));
    memset(sha256Ap, 0, sizeof(sha256Ap));

    return ret;
}

int32_t DeviceInfo::getSystemInfo(SYSTEM_ID_TYPE systemIdType, Bytes& out)
{
    int32_t  ret = NOT_ERROR;
    File     lcSysFile;
    Bytes    lcSysFileBuf;

    /* modem hash */
    char     rilResponseData[MAX_DRK_CP_INFO_DATA_BUF_LEN];
    int             rilResponseDataLen = 0;

    /* UN path */
    char            lcUfsUnPath[MAX_FILE_PATH_LEN] = {0};
    uint32_t        lcUfsUnPathLen = MAX_FILE_PATH_LEN;
    /* New UN path */
    char            lcUfsUnPathV2[MAX_FILE_PATH_LEN] = {0};
    uint32_t        lcUfsUnPathV2Len = MAX_FILE_PATH_LEN;

    uint32_t        limitSize = 0,
                    offset = 0;

    SHA256_CTX      sha256;
    uint8_t         sha256Mac[SHA256_DIGEST_LENGTH],
                    sha256Cid[SHA256_DIGEST_LENGTH],
                    sha256Out[SHA256_DIGEST_LENGTH];


    if ((ret = getUfsCidPath((uint8_t *)lcUfsUnPath, &lcUfsUnPathLen)) != NOT_ERROR) {
        LOGME("Failed to get the ufs un path. %d", ret);
        return ret;
    }

    if ((ret = getUfsCidPathV2((uint8_t *)lcUfsUnPathV2, &lcUfsUnPathV2Len)) != NOT_ERROR) {
        LOGME("Failed to get the new ufs un path. %d", ret);
        return ret;
    }

    switch (systemIdType) {
        case SYSTEM_ID_MODEM :
        case SYSTEM_ID_IMEI :
            if (isNoImeiSupportedModel()) {
                LOGMI("No IMEI supported model !!!");

                ret = lcSysFile.openFile((char *)WIFI_MAC_PATH, READ);
                if (ret != NOT_ERROR) {
                    LOGME("Failed to read MAC - %d", ret);
                    return ERR_FILE_READ_FAILED;
                }

                ret = lcSysFile.readFile(lcSysFileBuf);
                if ((ret != NOT_ERROR) || lcSysFileBuf.length() == 0) {
                    LOGME("Failed to read CID - %d / length %d", ret, lcSysFileBuf.length());
                    return ERR_FILE_READ_FAILED;
                }

                if (lcSysFileBuf[lcSysFileBuf.length() - 1] == 0xA) {
                    lcSysFileBuf.delAtEnd();
                }

                lcSysFileBuf.lowerCase();

                if (systemIdType == SYSTEM_ID_IMEI) {
                    out = lcSysFileBuf;
                } else {
                    bzero(sha256Mac, sizeof(sha256Mac));
                    bzero(sha256Cid, sizeof(sha256Cid));
                    bzero(sha256Out, sizeof(sha256Out));

                    SHA256((uint8_t *)lcSysFileBuf, lcSysFileBuf.length(), sha256Mac);
                    lcSysFileBuf.empty();
                    lcSysFile.closeFile();

                    if (access(lcUfsUnPath, F_OK) == 0) {
                        LOGMD("UFS v1 type !!!");
                        ret = lcSysFile.openFile((char *)lcUfsUnPath, READ);
                    } else if (access(lcUfsUnPathV2, F_OK) == 0) {
                        LOGMD("UFS v2 type !!!");
                        ret = lcSysFile.openFile((char *)lcUfsUnPathV2, READ);
                    } else if (access(EMMC_CID_PATH, F_OK) == 0) {
                        LOGMD("EMMC v1 type !!!");
                        ret = lcSysFile.openFile((char *)EMMC_CID_PATH, READ);
                    } else if (access(EMMC_CID_PATH_V2, F_OK) == 0) {
                        LOGMD("EMMC v2 type !!!");
                        ret = lcSysFile.openFile((char *)EMMC_CID_PATH_V2, READ);
                    } else {
                        LOGME("Not supported Storage type.");
                        return ERR_NOT_IMPLEMENTED;
                    }

                    if (ret != NOT_ERROR) {
                        LOGME("Failed to read SN - %d", ret);
                        return ERR_FILE_READ_FAILED;
                    }

                    ret = lcSysFile.readFile(lcSysFileBuf);
                    if ((ret != NOT_ERROR) || lcSysFileBuf.length() == 0) {
                        LOGME("Failed to read SN - %d / length %d", ret, lcSysFileBuf.length());
                        return ERR_FILE_READ_FAILED;
                    }

                    if (lcSysFileBuf[lcSysFileBuf.length() - 1] == 0xA) {
                        lcSysFileBuf.delAtEnd();
                    }

                    SHA256((uint8_t *)lcSysFileBuf, lcSysFileBuf.length(), sha256Cid);
                    lcSysFileBuf.empty();

                    // Make modem hash = H(H(CID) + H(MAC)).
                    SHA256_Init(&sha256);
                    SHA256_Update(&sha256, sha256Cid, sizeof(sha256Cid));
                    SHA256_Update(&sha256, sha256Mac, sizeof(sha256Mac));
                    SHA256_Final(sha256Out, &sha256);

                    // set value into output parameter.
                    out.set(sha256Out, SHA256_DIGEST_LENGTH);

                    bzero(sha256Mac, sizeof(sha256Mac));
                    bzero(sha256Cid, sizeof(sha256Cid));
                    bzero(sha256Out, sizeof(sha256Out));
                }
            } else {
                LOGMI("IMEI supported model !!!");
                if (systemIdType == SYSTEM_ID_MODEM) {
                    limitSize = SHA256_DIGEST_LENGTH;
                    offset = IPC_IMEI_LEN * 2;
                } else {
                    limitSize = IPC_IMEI_LEN * 2;
                    offset = 0;
                }

                bzero(rilResponseData, sizeof(rilResponseData));

                if ((rilResponseDataLen = getModemInfo(rilResponseData, sizeof(rilResponseData)))
                    != MAX_DRK_CP_INFO_DATA_BUF_LEN) {
                    LOGME("Failed to getModemInfo, ret : %d", rilResponseDataLen);
                    return ERR_RILD_REQUEST_HOOK_ERROR;
                }

                ret = out.set((uint8_t *)(rilResponseData + offset), limitSize);
                bzero(rilResponseData, sizeof(rilResponseData));

                if (ret != 0) {
                    LOGME("Failed to read MAC - %d", ret);
                    return ERR_NOT_ENOUGH_MEMORY;
                }
            }
            return NOT_ERROR;

        case SYSTEM_ID_SN :
            if (access(lcUfsUnPath, F_OK) == 0) {
                LOGMD("UFS v1 type !!!");
                ret = lcSysFile.openFile((char *)lcUfsUnPath, READ);
            } else if (access(lcUfsUnPathV2, F_OK) == 0) {
                LOGMD("UFS v2 type !!!");
                ret = lcSysFile.openFile((char *)lcUfsUnPathV2, READ);
            } else if (access(EMMC_CID_PATH, F_OK) == 0) {
                LOGMD("EMMC v1 type !!!");
                ret = lcSysFile.openFile((char *)EMMC_CID_PATH, READ);
            } else if (access(EMMC_CID_PATH_V2, F_OK) == 0) {
                LOGMD("EMMC v2 type !!!");
                ret = lcSysFile.openFile((char *)EMMC_CID_PATH_V2, READ);
            } else {
                LOGME("Not supported Storage type.");
                return ERR_NOT_IMPLEMENTED;
            }

            if (ret != NOT_ERROR) {
                LOGME("Failed to read SN - %d", ret);
                return ERR_FILE_READ_FAILED;
            }

            ret = lcSysFile.readFile(lcSysFileBuf);
            if ((ret != NOT_ERROR) || lcSysFileBuf.length() == 0) {
                LOGME("Failed to read SN - %d / length %d", ret, lcSysFileBuf.length());
                return ERR_FILE_READ_FAILED;
            }

            if (lcSysFileBuf[lcSysFileBuf.length() - 1] == 0xA) {
                lcSysFileBuf.delAtEnd();
            }

            out = lcSysFileBuf;
            return NOT_ERROR;
        default :
            LOGME("Invalid system ID %d", systemIdType);
            return ERR_NOT_IMPLEMENTED;
    }
}

int32_t DeviceInfo::getSystemProperty(uint8_t *name, Bytes& out)
{
    int32_t ret = NOT_ERROR;
    int     bufLen = 0;
    char    buf[PROP_VALUE_MAX + 1] = {0};

    if ((bufLen = __system_property_get((char *)name, (char *)buf)) <= 0) {
        return ERR_PLATFORM_API_OPERATION_FAILED;
    }
    LOGMD("Property (%s): '%s'", name, buf);
    ret = out.set((uint8_t *)buf, bufLen);
    if (ret != 0) {
        LOGMD("It can't set out parameter : %d", ret);
    }
    bzero(buf, sizeof(buf));
    return ret;
}

bool DeviceInfo::isNoImeiSupportedModel()
{
    int32_t ret = NOT_ERROR;
    Bytes   value;

    if ((ret = getSystemProperty((uint8_t *)PROPERTY_CARRIER, value)) != NOT_ERROR) {
        LOGME("get carrier info (%d).", ret);
        return false;
    }

    if (value.length() != strlen(PROPERTY_VALUE_WIFI_ONLY)) {
        LOGME("Carrier length is not matched.");
        return false;
    }

    if (!strncmp((char *)value, PROPERTY_VALUE_WIFI_ONLY, strlen(PROPERTY_VALUE_WIFI_ONLY))) {
        return true;
    }

    return false;
}

int32_t DeviceInfo::gatherDevInfos(Bytes& in)
{
    int32_t ret = NOT_ERROR;
    Bytes   lcDevInfo;
    TLV     lcTzDevInfo;

    memset(devInfo, 0x0, sizeof(devInfo));

    /* GET MODEM */
    if ((getSystemInfo(SYSTEM_ID_MODEM, lcDevInfo)) != NOT_ERROR) {
        LOGME("Failed to get system id[%d].", SYSTEM_ID_MODEM);
    } else  {
        if (lcDevInfo.length() != 0 && lcDevInfo.length() < MAX_SYSTEM_ID_LEN) {
            memcpy(devInfo[SYSTEM_ID_MODEM].systemInfo, (uint8_t *)lcDevInfo, lcDevInfo.length());
            devInfo[SYSTEM_ID_MODEM].systemInfoLen = lcDevInfo.length();
        }
    }
    lcDevInfo.empty();

    /* GET IMEI */
    if ((getSystemInfo(SYSTEM_ID_IMEI, lcDevInfo)) != NOT_ERROR) {
        LOGME("Failed to get system id[%d].", SYSTEM_ID_IMEI);
    } else {
        if (lcDevInfo.length() != 0) {
            SHA256((uint8_t *)lcDevInfo, lcDevInfo.length(), devInfo[SYSTEM_ID_IMEI].systemInfo);
            devInfo[SYSTEM_ID_IMEI].systemInfoLen = SHA256_DIGEST_LENGTH;
        }
    }
    lcDevInfo.empty();

    /* GET  SN */
    if ((getSystemInfo(SYSTEM_ID_SN, lcDevInfo)) != NOT_ERROR) {
        LOGME("Failed to get system id[%d].", SYSTEM_ID_SN);
    } else {
        if (lcDevInfo.length() != 0) {
            SHA256((uint8_t *)lcDevInfo, lcDevInfo.length(), devInfo[SYSTEM_ID_SN].systemInfo);
            devInfo[SYSTEM_ID_SN].systemInfoLen = SHA256_DIGEST_LENGTH;
        }
    }
    lcDevInfo.empty();

    if (in.length() > 0) {
        ret = lcTzDevInfo.decode(in);
        if (ret != NOT_ERROR) {
            goto end;
        }

        /* GET MODEL NAME */
        ret = lcTzDevInfo.get(TLV_MODEL_NAME, lcDevInfo);
        if (ret != NOT_ERROR) {
            if ((ret = getModelName(lcDevInfo)) != NOT_ERROR) {
                LOGME("Failed to getModel in getDeviceInformations : %d", ret);
                goto end;
            }
        }

        if (lcDevInfo.length() != 0 && lcDevInfo.length() < MAX_SYSTEM_ID_LEN) {
            memcpy(devInfo[SYSTEM_ID_MODEL].systemInfo, (uint8_t *)lcDevInfo, lcDevInfo.length());
            devInfo[SYSTEM_ID_MODEL].systemInfoLen = lcDevInfo.length();
            lcDevInfo.empty();
        }

        /* GET UID  */
        ret = lcTzDevInfo.get(TLV_HUID, lcDevInfo);
        if (ret == NOT_ERROR) {
            if (lcDevInfo.length() != 0 && lcDevInfo.length() < MAX_SYSTEM_ID_LEN) {
                memcpy(devInfo[SYSTEM_ID_UID].systemInfo, (uint8_t *)lcDevInfo, lcDevInfo.length());
                devInfo[SYSTEM_ID_UID].systemInfoLen = lcDevInfo.length();
                lcDevInfo.empty();
            }
        } else {
            LOGME("Failed to get uid from tlv : %d", ret);
        }
    } else {
        LOGMD("Skip to get SYSTEM_ID_UID & SYSTEM_ID_MODEL");
        ret = NOT_ERROR;
    }

end:
    return ret;
}

int32_t DeviceInfo::isMatchedDevInfo()
{
    int32_t resultMatch = DEVICE_STATUS_IS_INVALID,
            isMatchedCP = DEVICE_STATUS_IS_INVALID,
            isMatchedAP = DEVICE_STATUS_IS_INVALID;
    Bytes   lcDevInfo;


    if (strncmp((char *)devInfo[SYSTEM_ID_UID].systemInfo, UID_PREFIX_DRK, strlen(UID_PREFIX_DRK)) == 0) {
        //DRK V2
        //Compare CP
        isMatchedCP = matchUidString(devInfo[SYSTEM_ID_UID].systemInfo + CP_HASH, CMP_UNIT_SIZE, devInfo[SYSTEM_ID_MODEM].systemInfo, devInfo[SYSTEM_ID_MODEM].systemInfoLen);

        //Compare AP
        isMatchedAP = matchAPUidString(devInfo[SYSTEM_ID_UID].systemInfo + AP_HASH, CMP_UNIT_SIZE,
                                       devInfo[SYSTEM_ID_IMEI].systemInfo, devInfo[SYSTEM_ID_IMEI].systemInfoLen, devInfo[SYSTEM_ID_SN].systemInfo, devInfo[SYSTEM_ID_SN].systemInfoLen);

        if ((isMatchedCP == DEVICE_STATUS_IS_VALID) && (isMatchedAP == DEVICE_STATUS_IS_VALID)) {
            resultMatch = DEVICE_STATUS_IS_VALID;
        } else {
            LOGME("Device info isn't matched %d %d", isMatchedCP, isMatchedAP);
            resultMatch = DEVICE_STATUS_IS_INVALID;
        }
    } else {
        //DRK V1 or NA(getDeviceInfo)
        resultMatch = DEVICE_STATUS_IS_DRK_V1;
    }
    return resultMatch;
}

int32_t DeviceInfo::getModelName(Bytes& out)
{
    int32_t  ret = NOT_ERROR;
    Bytes    modelName;
    char     buf[PROP_VALUE_MAX + 1] = {0};
    char    *cbuf = NULL;
    uint32_t bufLen = 0;

    if ((ret = getSystemProperty((uint8_t *)PROPERTY_MODEL, modelName)) != NOT_ERROR) {
        if ((ret = getSystemProperty((uint8_t *)PROPERTY_BASE_MODEL, modelName)) != NOT_ERROR) {
            return ret;
        }
    }

    bufLen = (modelName.length() > PROP_VALUE_MAX) ? PROP_VALUE_MAX : modelName.length();
    memcpy(buf, (char *)modelName, bufLen);

    if (strncmp((char *)buf, MODEL_MANUFACTURER_PART, sizeof(MODEL_MANUFACTURER_PART) - 1) == 0) {
        cbuf = buf + (sizeof(MODEL_MANUFACTURER_PART) - 1);
        bufLen -= (uint32_t)sizeof(MODEL_MANUFACTURER_PART) - 1;
    } else {
        cbuf = buf;
    }

    ret = out.set((uint8_t *)cbuf, bufLen);
    bzero(buf, sizeof(buf));

    if (ret != 0) {
        return ERR_BUFFER_OVERFLOW;
    }
    return NOT_ERROR;
}

int32_t DeviceInfo::getSerialNo(Bytes& out)
{
    int32_t  ret = NOT_ERROR;
    Bytes    serialNum;
    uint8_t  sha1Digest[SHA_DIGEST_LENGTH] = {0};
    uint8_t *p64 = NULL;

    if ((ret = getSystemInfo(SYSTEM_ID_SN, serialNum)) != NOT_ERROR) {
        LOGME("Failed to get SN with error %d.", ret);
        return ret;
    }

    SHA1((uint8_t *)serialNum, serialNum.length(), sha1Digest);

    if ((p64 = B64Coder::B64Encoding(sha1Digest, (uint32_t)sizeof(sha1Digest), B64_BASIC)) == NULL) {
        LOGME("Failed to encode b64.");
        return ERR_BASE64_CODING_FAILED;
    }

    memset(sha1Digest, 0x0, SHA_DIGEST_LENGTH);

    ret = out.set(p64, (uint32_t)strlen((char *)p64));
    if (p64) {
        memset(p64, 0x0, strlen((char *)p64));
        free(p64);
    }

    if (ret != 0) {
        return ERR_BUFFER_OVERFLOW;
    }
    return NOT_ERROR;
}

int32_t DeviceInfo::MakeTobeSignedCSR(int prefix, Bytes& in, Bytes& out)
{
    int32_t ret = NOT_ERROR, huidLen = 0, isSAKv1 = false;
    uint8_t sha256Imei[SHA256_DIGEST_LENGTH]  = {0},
            sha256Cid[SHA256_DIGEST_LENGTH]   = {0},
            sha256Digest[SHA256_DIGEST_LENGTH] = {0},
            huid[MAX_HUID_LEN + 1]            = {0};
    SHA256_CTX sha256;
    Bytes   branchId, timestamp, modemHash, imei, cid, modelName, value;
    TLV     lcDevInfo, lcCsr(TLV_START);
    uint8_t *pB64Modem = NULL, *pB64Ap = NULL;
    char *   pValue = NULL;

    if (in.length() == 0) {
        LOGME("Input is invalid.");
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = lcDevInfo.decode(in)) != NOT_ERROR) {
        LOGME("Failed to tlv decode with error %d.", ret);
        return ret;
    }

    if ((ret = lcDevInfo.get(TLV_TIMESTAMP, timestamp)) != NOT_ERROR) {
        LOGME("Failed to get value with error %d (ts)", ret);
        return ret;
    }

    if ((ret = lcDevInfo.get(TLV_BRANCHID, branchId)) != NOT_ERROR) {
        LOGME("Failed to get value with error %d (bid).", ret);
        return ret;
    }

    if ((ret = getSystemInfo(SYSTEM_ID_MODEM, modemHash)) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_MODEM_HASH with error %d.", ret);
        return ret;
    }

    if ((ret = getSystemInfo(SYSTEM_ID_IMEI, imei)) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_IMEI with error %d.", ret);
        return ret;
    }

    if ((ret = getSystemInfo(SYSTEM_ID_SN, cid)) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_SN with error %d.", ret);
        return ret;
    }

    if ((ret = getModelName(modelName)) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_SN with error %d.", ret);
        return ret;
    }

    if ((pB64Modem = B64Coder::B64Encoding((uint8_t *)modemHash, modemHash.length(), B64_SAFE_ALPHABET)) == NULL) {
        ret = ERR_BASE64_CODING_FAILED;
        goto end;
    }

    SHA256((uint8_t *)imei, imei.length(), sha256Imei);
    SHA256((uint8_t *)cid, cid.length(), sha256Cid);

    SHA256_Init(&sha256);
    SHA256_Update(&sha256, sha256Imei, sizeof(sha256Imei));
    SHA256_Update(&sha256, sha256Cid, sizeof(sha256Cid));
    SHA256_Final(sha256Digest, &sha256);

    if ((pB64Ap = B64Coder::B64Encoding(sha256Digest, SHA256_DIGEST_LENGTH, B64_SAFE_ALPHABET))  == NULL) {
        ret = ERR_BASE64_CODING_FAILED;
        goto end;
    }

    if (prefix == SAK_PREFIX) {
        //PLM P201215-05239
        //DRK service need to check sak versioin for special case (convert process | BULK)
        if (getSystemProperty((uint8_t *)PROPERTY_KEYSTORE_KEYTYPE, value) == NOT_ERROR) {
            pValue = strtok(value, ",");
            while(pValue != NULL){
                if(strncmp(pValue, "sak", sizeof("sak")) == 0) {
                    LOGMD("SAK v1 type");
                    isSAKv1 = true;
                    break;
                }
                pValue = strtok(NULL, ",");
            }
        }

        huidLen = snprintf((char *)huid, sizeof(huid), "%s:%s:%s:%s:%s", (isSAKv1)?"SAK_V1":"SAK_V2",
                       (char *)timestamp, (char *)branchId, pB64Modem, pB64Ap);
    } else {
        huidLen = snprintf((char *)huid, sizeof(huid), "DRK_V2:%s:%s:%s:%s", (char *)timestamp,
                       (char *)branchId, pB64Modem, pB64Ap);
    }
    if ((ret = lcCsr.add(TLV_MODEL_NAME, (uint8_t *)modelName, modelName.length())) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_MODEM_HASH with error %d.", ret);
        goto end;
    }

    if ((ret = lcCsr.add(TLV_HUID, (uint8_t *)huid, huidLen)) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_MODEM_HASH with error %d.", ret);
        goto end;
    }

    if ((ret = lcCsr.add(TLV_IMEI, (uint8_t *)imei, imei.length())) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_MODEM_HASH with error %d.", ret);
        goto end;
    }

    ret = NOT_ERROR;

end:
    if (pB64Modem) {
        memset(pB64Modem, 0x0, strlen((char *)pB64Modem));
        free(pB64Modem);
        pB64Modem = NULL;
    }

    if (pB64Ap) {
        memset(pB64Ap, 0x0, strlen((char *)pB64Ap));
        free(pB64Ap);
        pB64Ap = NULL;
    }

    if (ret == NOT_ERROR) {
        ret = lcCsr.encode(out);
    }

    bzero(sha256Imei, sizeof(sha256Imei));
    bzero(sha256Cid, sizeof(sha256Cid));
    bzero(sha256Digest, sizeof(sha256Digest));
    bzero(huid, sizeof(huid));
    return ret;
}

int32_t DeviceInfo::MakeBigDataMsg(Bytes& serviceName, Bytes& in, Bytes& out)
{
    int32_t   ret = NOT_ERROR, pos = 0;
    char      message[MAX_BIGDATA_SIZE + 1] = {0};
    uint32_t  messageLen = MAX_BIGDATA_SIZE;
    char      prefix[CSR_PREFIX_LEN + 1] = {0},
              isdate[CSR_TIMESTAMP_LEN + 1] = {0},
              branid[CSR_BRANCH_ID_LEN + 1] = {0};


    ret = gatherDevInfos(in);
    if (ret == NOT_ERROR) {
        messageLen = snprintf(message, MAX_BIGDATA_SIZE,
                                "SDAK=\"MatchDevInfo\":\"%d\",\"ModelInfo\":\"%s\",\"ServiceName\":\"%s\",",
                              isMatchedDevInfo(), devInfo[SYSTEM_ID_MODEL].systemInfo, (char *)serviceName);

        memcpy(prefix, devInfo[SYSTEM_ID_UID].systemInfo + pos, CSR_PREFIX_LEN);
        pos += (CSR_PREFIX_LEN+1);

        memcpy(isdate, devInfo[SYSTEM_ID_UID].systemInfo + pos, CSR_TIMESTAMP_LEN);
        pos += (CSR_TIMESTAMP_LEN+1);

        if (strncmp((char *)prefix, "DRK_V2", CSR_PREFIX_LEN) == 0) {
            memcpy(branid, devInfo[SYSTEM_ID_UID].systemInfo + pos, CSR_BRANCH_ID_LEN);
        } else {
            pos += 3;
            memcpy(branid, devInfo[SYSTEM_ID_UID].systemInfo + pos, CSR_BRANCH_ID_LEN - 1);
        }
        messageLen += snprintf(message + messageLen, MAX_BIGDATA_SIZE - messageLen,
                                "\"PrefixInfo\":\"%s\",\"IssuedDate\":\"%s\",\"BranchInfo\":\"%s\"\n",
                              prefix, isdate, branid);

        ret = out.set((uint8_t *)message, messageLen);
    }
    return ret;
}

int32_t DeviceInfo::MakeDeviceInfoMsg(Bytes& in, Bytes& out)
{
    int32_t ret = NOT_ERROR;
    TLV     lcDevInfo;
    int     i = 0;
    ret = gatherDevInfos(in);
    if (ret == NOT_ERROR) {
        devInfo[SYSTEM_ID_STATUS].systemInfoLen = snprintf((char *)devInfo[SYSTEM_ID_STATUS].systemInfo, sizeof(devInfo[SYSTEM_ID_STATUS].systemInfo),
                                                           "%d", isMatchedDevInfo());
        for (i = 0; i < 4; i++) {
            lcDevInfo.add((TLVTAG)(100 + (1 << i)), devInfo[i].systemInfo, devInfo[i].systemInfoLen);
        }
        lcDevInfo.add(TLV_DI_UID, devInfo[SYSTEM_ID_UID].systemInfo, devInfo[SYSTEM_ID_UID].systemInfoLen);

        ret = lcDevInfo.encode(out);
    }
    return ret;
}

int32_t  DeviceInfo::MakeServiceKeyInfo(Bytes& in)
{
    int32_t ret = NOT_ERROR;
    Bytes   lcModel,
            lcSerial;
    ServiceKeyInfo_t *lcski = NULL;

    if (in.length() != (uint32_t)sizeof(ServiceKeyInfo_t)) {
        LOGME("the size of struct is differ from buffer. (%d:%d)", in.length(), (uint32_t)sizeof(ServiceKeyInfo_t));
        return ERR_INVALID_ARGUMENT;
    }

    lcski = (ServiceKeyInfo_t *)((uint8_t *)in);
    if (lcski == NULL) {
        LOGME("struct is null object.");
        return ERR_INVALID_ARGUMENT;
    }

    if ((ret = getModelName(lcModel)) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_SN with error %d.", ret);
        return ret;
    }

    if ((ret = getSerialNo(lcSerial)) != NOT_ERROR) {
        LOGME("Failed to get SYSTEM_ID_SN with error %d.", ret);
        return ret;
    }

    snprintf(lcski->model, MAX_MODEL_SIZE - 1, "%s", (char *)lcModel);
    snprintf(lcski->serialNo, MAX_SERIALNO_SIZE - 1, "%s", (char *)lcSerial);

    return NOT_ERROR;
}

int32_t DeviceInfo::GetFeatureSet(int32_t mode)
{
    int32_t ret = NOT_ERROR,
            bufLen = 0;
    char    buf[PROP_VALUE_MAX + 1] = {0};

    switch (mode) {
        case VK_FEATURE:
            if ((bufLen = __system_property_get(PROP_VK, buf)) <= 0 || buf[0] != '1') {
                LOGMD("Skip write drk to Vaultkeeper");
                ret = ERR_SYSCALL_FAILED;
            }
            break;
        default:
            LOGMI("======== [ Ver : %s ] ========", COMMON_VERSION);
            ret = ERR_SYSCALL_FAILED;
        break;
    }

    memset(buf, 0, sizeof(buf));
    return ret;
}

int32_t  DeviceInfo::checkValidationCertwithDeviceInfo(Bytes& in)
{
    int32_t ret = NOT_ERROR;

    if (in.length() == 0) {
        LOGME("Invalid argument in checkValidationCertwithDeviceInfo");
        return ERR_INVALID_ARGUMENT;
    }

    ret = gatherDevInfos(in);
    if (ret == NOT_ERROR) {
        if(isMatchedDevInfo() != DEVICE_STATUS_IS_VALID){
            ret = ERR_NOT_MATCHED;
        }
    }
    return ret;
}

}  // namespace drk
}  // namespace security
}  // namespace hardware
}  // namespace samsung
}  // namespace vendor
