#include <sys/system_properties.h>
#include "BaseTzCommand.h"


#if defined(DRKFEATURE_HWVAULT_ENABLE)
#include "hermes_cred.h"
#elif defined(VAULTKEEPER_ENABLED)
#include "VKManagerAPI.h"
#endif

namespace vendor   {
namespace samsung  {
namespace hardware {
namespace security {
namespace drk      {


#define MODE_DEFAULT        0
#define MODE_RESTORE_DRK    1

int32_t    BaseTzCommand::getDeviceRootKey(Bytes& out, int32_t mode)
{
    int32_t   ret = NOT_ERROR;
    Bytes     drkFile;
    File      lcFile;

#if defined(DRKFEATURE_HWVAULT_ENABLE)
    Bytes     lcOut;
    TLV       lcTlv;
#elif defined(VAULTKEEPER_ENABLED)
    char      vkDrkFile[MAX_VK_DRK_LEN] = {0};
    int       vkDrkFileLen = 0;
    TLV       vkTlv;

    char      lcProp[PROP_VALUE_MAX] = {0,};
    int       lcPropLen = 0;
    VaultKeeperManager *drkFilebak = NULL;

    memset(vkDrkFile, 0x0, sizeof(vkDrkFile));
#endif

    if (mode == MODE_RESTORE_DRK) {
#if defined(DRKFEATURE_HWVAULT_ENABLE)
        ret = CommandWithoutWrappedKey(CMD_READ_DRK_FROM_HWVAULT, lcOut, lcOut);
        if(ret != NOT_ERROR) {
            LOGME("DRK wasn't read from Hwvault ret %d", ret);
            ret = ERR_DRK_IS_NOT_EXIST;
            goto end;
        }

        LOGMD("DRK file in HV length=%d", lcOut.length());

        lcTlv.decode((uint8_t *)lcOut, lcOut.length());
        lcTlv.get(TLV_SIGNATURE, drkFile);
        if (drkFile.length() != (uint32_t)strlen(HWVAULT_DRK_MAGIC_CODE) ||
                strncmp((char *)drkFile, (char *)HWVAULT_DRK_MAGIC_CODE, strlen(HWVAULT_DRK_MAGIC_CODE))) {
            LOGME("Invalid magic code(hv)");
            ret = ERR_DRK_IS_NOT_EXIST;
            goto end;
        }

        drkFile.empty();
        if((ret = lcTlv.get(TLV_ENCRYPTED_DATA_BLOB, drkFile)) != NOT_ERROR) {
            LOGME("Invalid drk blob %d", drkFile.length());
            ret = ERR_DRK_IS_NOT_EXIST;
            goto end;
        }
#elif defined(VAULTKEEPER_ENABLED)
        LOGMD("DRK wasn't read from local. %d", ret);
        if ((lcPropLen = __system_property_get(PROP_VK, lcProp)) <= 0 || lcProp[0] != '1') {
            LOGMI("Skip read drk from Vaultkeeper %d", lcPropLen);
            ret = ERR_DRK_IS_NOT_EXIST;
            goto end;
        } else {
            drkFilebak = VaultKeeperManager::getInstance();
            ret = drkFilebak->read(VaultKeeperManager::TYPE_VAULT_DATA_SHELTERED, vkDrkFile, &vkDrkFileLen);
            if (ret != HIDL_SUCCESS) {
                LOGME("DRK wasn't read from Vaultkeeper ret %d", ret);
                ret = ERR_DRK_IS_NOT_EXIST;
                goto end;
            }
        }

        LOGMD("DRK file in VK length=%d", vkDrkFileLen);

        vkTlv.decode((uint8_t *)vkDrkFile, vkDrkFileLen);
        vkTlv.get(TLV_SIGNATURE, drkFile);

        if (drkFile.length() != (uint32_t)strlen(VK_DRK_MAGIC_CODE) ||
            strncmp((char *)drkFile, (char *)VK_DRK_MAGIC_CODE, strlen(VK_DRK_MAGIC_CODE))) {
            LOGME("Invalid magic code(vk)");
            ret = ERR_DRK_IS_NOT_EXIST;
            goto end;
        }
        drkFile.empty();

        vkTlv.get(TLV_ENCRYPTED_DATA_BLOB, drkFile);
#else
        ret = ERR_DRK_IS_NOT_EXIST;
#endif
    } else {
        ret = lcFile.openFile((char *)DRK_SAVE_FILE_PATH, READ);
        if (ret != NOT_ERROR) {
            LOGME("opening file was error %d", ret);
            ret = ERR_DRK_IS_NOT_EXIST;
            goto end;
        } else {
            ret = lcFile.readFile(drkFile);
            if (ret != NOT_ERROR) {
                LOGME("reading file was error %d", ret);
                ret = ERR_DRK_IS_NOT_EXIST;
                goto end;
            }
        }
    }

end:
    if (ret == NOT_ERROR) {
        LOGMI("DRK file exists. %d", drkFile.length());
        out = drkFile;
    } else {
        LOGME("Failed to get DRK with error %d", ret);
        DRKLog::PrintIssuedLog();
    }

    return ret;
}

int32_t BaseTzCommand::CommandWithWrappedKey(uint32_t cmdId, Bytes& in, Bytes& out)
{
    int32_t  ret = NOT_ERROR, mode = MODE_DEFAULT;
    TLV      lcTlv;
    Bytes    tlvBlob, drkBlob;
    File     lcFile;

    TA_MODE openMode = NONE_MODE;
    TA_MODE closeMode = NONE_MODE;

    switch (cmdId) {
        case CMD_GENERATE_SERVICE_KEY:
            openMode = SKM_MODE;
            closeMode = NONE_MODE;
            break;
        case CMD_VERIFY_BACKUP_KEY:
            openMode = SKM_MODE;
            closeMode = SKM_MODE;
            mode = MODE_RESTORE_DRK;
            cmdId = CMD_VERIFY_DRK_KEY;
            break;
#ifndef USE_RELEASE
        case CMD_GENERATE_PROV_SERVICE_KEY:
            openMode = SKM_PROV_MODE;
            closeMode = NONE_MODE;
            cmdId = CMD_GENERATE_SERVICE_KEY;
            break;
#endif
        default :
            if (cmdId & CMD_PROV) {
                openMode = PROV_MODE;
                closeMode = PROV_MODE;
            } else {
                openMode = SKM_MODE;
                closeMode = SKM_MODE;
            }
            break;
    }

    if (in.length() > 0) {
        if ((ret = lcTlv.decode(in)) != NOT_ERROR) {
            LOGME("inTlv.decode is failed. %d.", ret);
            goto err;
        }
    }

    /* Read DRK */
    if ((ret = getDeviceRootKey(drkBlob, mode)) != NOT_ERROR) {
        LOGME("GetDeviceRootKey is failed. %d.", ret);
        goto err;
    }

    /* Convert drkBlob to Bytes */
    if ((ret = lcTlv.add(TLV_WRAPPED_KEY, drkBlob)) != NOT_ERROR) {
        LOGME("inTlv.add is failed. %d.", ret);
        goto err;
    }

    if ((ret = lcTlv.encode(tlvBlob)) != NOT_ERROR) {
        LOGME("inTlv.encode is failed. %d.", ret);
        goto err;
    }

    /* load TA */
    if ((ret = mTzCom.TzOpen(openMode)) != NOT_ERROR) {
        LOGME("TzOpen is failed. %d.", ret);
        goto err;
    }

    if ((ret = mTzCom.TzSendCmd(cmdId, tlvBlob, out)) != NOT_ERROR) {
        LOGME("TzSendCmd error %d.", ret);
        goto err;
    }

    /* Save DRK */
    //DRK blob was verified.
    if(mode == MODE_RESTORE_DRK) {
        if(lcFile.openFile((char *)DRK_SAVE_FILE_PATH, WRITE) == NOT_ERROR) {
            if(lcFile.writeFile(drkBlob) == NOT_ERROR) {
                LOGMI("DRK blob was overwritten successfully");
                goto end;
            }
        }
        LOGMI("Skip overwrite key blob.");
    }

    goto end;
err:
    closeMode = openMode;
    out.empty();
end:
    mTzCom.TzClose(closeMode);
    return ret;
}

int32_t BaseTzCommand::CommandWithoutWrappedKey(uint32_t cmdId, Bytes& in, Bytes& out)
{
    int ret = NOT_ERROR;

    TA_MODE openMode = NONE_MODE;
    TA_MODE closeMode = NONE_MODE;

    switch (cmdId) {
        case CMD_MAKE_DRK_CSR : // In case of making DRK's CSR, both TA should be stay loaded.
            openMode = SKM_PROV_MODE;
            closeMode = NONE_MODE;
            break;
        case CMD_ENCRYPT_DRK_CSR: // In case of encryption DRK&SKA's CSR, we don't need to load both TA. And we should unload both TA after it.
            openMode = NONE_MODE;
            closeMode = NONE_MODE;
            break;
        case CMD_ENCRYPT_SAK_CSR:
            openMode = NONE_MODE;
            closeMode = PROV_MODE;
            break;
        case CMD_INSTALL_DRK_CERT:
            openMode  = NONE_MODE;
            closeMode = SKM_PROV_MODE;
            break;
        case CMD_MAKE_TBS_CSR:
            openMode  = PROV_MODE;
            closeMode = NONE_MODE;
            break;
        case CMD_UNWRAP_GAK_BLOB:
            openMode  = PROV_MODE;
            closeMode = NONE_MODE;
            break;
#ifndef USE_RELEASE
        case CMD_VERIFY_SERVICE_KEY:
            openMode  = NONE_MODE;
            closeMode = SKM_PROV_MODE;
            break;
#endif
        default :
            if (cmdId & CMD_PROV) {
                openMode = PROV_MODE;
                closeMode = PROV_MODE;
            } else {
                openMode = SKM_MODE;
                closeMode = SKM_MODE;
            }
            break;
    }

    if ((ret = mTzCom.TzOpen(openMode)) != NOT_ERROR) {
        LOGME("TzOpen is failed. %d.", ret);
        goto err;
    }

    if (cmdId != CMD_MAKE_TBS_CSR) {
        if ((ret = mTzCom.TzSendCmd(cmdId, in, out)) != NOT_ERROR) {
            LOGME("TzSendCmd error %d.", ret);
            goto err;
        }
    }

    goto end;
err:
    closeMode = openMode;

end:
    mTzCom.TzClose(closeMode);
    return ret;
}

void    BaseTzCommand::ReleaseTzSession()
{
    mTzCom.TzClose(SKM_PROV_MODE);
}

}  // namespace drk
}  // namespace security
}  // namespace hardware
}  // namespace samsung
}  // namespace vendor
