#include <openssl/sha.h>
#include "DeviceInfo.h"
#include "ProvCommand.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       {

static bool __isMatchedSHA256(const uint8_t *data, size_t dataLen, const uint8_t *hash)
{
    uint8_t sha256Digest[SHA256_DIGEST_LENGTH] = {0};

    if (data == NULL || dataLen == 0) {
        return false;
    }

    SHA256(data, dataLen, sha256Digest);

    if (memcmp(sha256Digest, hash, sizeof(sha256Digest)) == 0) {
        memset(sha256Digest, 0x0, SHA256_DIGEST_LENGTH);
        return true;
    } else {
        memset(sha256Digest, 0x0, SHA256_DIGEST_LENGTH);
        return false;
    }
}

int32_t  ProvCommand::makeSignedCSR(Bytes& in, Bytes& out)
{
    int32_t    ret = NOT_ERROR;
    DeviceInfo lcDI;
    Bytes      lcCsr;

    if ((ret = lcDI.MakeTobeSignedCSR(DRK_PREFIX, in, lcCsr)) != NOT_ERROR) {
        LOGME("To make CSR is failed with ret : %d.", ret);
        goto end;
    }

    if ((ret = CommandWithoutWrappedKey(CMD_MAKE_DRK_CSR, lcCsr, out)) != NOT_ERROR) {
        LOGME("To sign CSR is failed with ret : %d.", ret);
        goto end;
    }
end:
    return ret;
}

int32_t ProvCommand::checkKeyBlobFormat(uint8_t *keyBlob, uint32_t keyBlobLen, Bytes& out)
{
    int32_t  ret = NOT_ERROR;
    uint8_t *lcData = NULL;
    uint32_t lcDataLen = 0,
             pos = 0;

    if (keyBlob == NULL || keyBlobLen < (SHA256_DIGEST_LENGTH + KEYBLOB_HEADER_LEN)) {
        LOGME("Null pointer delievered.");
        return ERR_INVALID_ARGUMENT;
    }

    //version of KeyBlob
    if (keyBlob[pos++] != KEYBLOB_VERSION) {
        LOGME("Version is unsupported. 0x%X", keyBlob[pos - 1]);
        ret = ERR_INVALID_BLOB;
        goto end;
    }

    if (keyBlob[pos++] != KEYBLOB_KEYMASTER) {
        LOGME("Wrong key blob type: 0x%X", keyBlob[pos - 1]);
        ret = ERR_INVALID_BLOB;
        goto end;
    }

    lcDataLen = GET_UINT16(keyBlob, pos);
    pos += 2;

    if (keyBlobLen - (SHA256_DIGEST_LENGTH + KEYBLOB_HEADER_LEN) < lcDataLen) {
        LOGME("Wrong key blob length: %d, %d", keyBlobLen, lcDataLen);
        ret = ERR_INVALID_BLOB;
        goto end;
    }
    pos += lcDataLen;

    if (!__isMatchedSHA256(keyBlob, lcDataLen + KEYBLOB_HEADER_LEN, (uint8_t *)(keyBlob + pos))) {
        LOGME("Hash verification is failed.");
        ret = ERR_HASH_VERIFICATION_FAILED;
        goto end;
    }

    lcData = (uint8_t *)(keyBlob + KEYBLOB_HEADER_LEN);
    ret = out.set(lcData, lcDataLen);
end:
    return ret;
}

int32_t  ProvCommand::GenerateEncryptedCSR(int32_t cmd, Bytes& in, Bytes& out)
{
    int32_t ret = NOT_ERROR,
            tzCmd = 0;
    TLV     lcTlv(TLV_START);
    Bytes   lcCsr, lcSignedCsr, lcOut;

    if (cmd == CMD_MAKE_DRK_CSR) {
        if ((ret = makeSignedCSR(in, lcCsr)) != NOT_ERROR) {
            LOGME("To make signed CSR is failed with ret : %d.", ret);
            goto end;
        }
        tzCmd = CMD_ENCRYPT_DRK_CSR;
    } else {
        lcCsr = in;
        tzCmd = CMD_ENCRYPT_SAK_CSR;
    }

    if ((ret = lcTlv.add(TLV_CSR, (uint8_t *)lcCsr, lcCsr.length())) != NOT_ERROR) {
        LOGME("To make the request of encryption is failed with ret : %d.", ret);
        goto end;
    }

    if ((ret = lcTlv.encode(lcSignedCsr)) != NOT_ERROR) {
        LOGME("To encode TLV for encryption is failed with ret : %d.", ret);
        goto end;
    }

    if ((ret = CommandWithoutWrappedKey(tzCmd, lcSignedCsr, lcOut)) != NOT_ERROR) {
        LOGME("To encrypt CSR is failed with ret : %d.", ret);
        goto end;
    }

    if ((ret = lcOut.b64Encode(BASE64_BASIC, out)) != NOT_ERROR) {
        LOGME("To encode base64 of CSR is failed with ret : %d.", ret);
    }
end:
    return ret;
}

int32_t  ProvCommand::GenerateSignedCSR(Bytes& in, Bytes& out)
{
    int32_t    ret = NOT_ERROR;
    DeviceInfo lcDI;

    if ((ret = lcDI.MakeTobeSignedCSR(SAK_PREFIX, in, out)) != NOT_ERROR) {
        LOGME("To make signed CSR is failed with ret : %d.", ret);
        goto end;
    }

    if ((ret = CommandWithoutWrappedKey(CMD_MAKE_TBS_CSR, in, out)) != NOT_ERROR) {
        LOGME("To encrypt CSR is failed with ret : %d.", ret);
    }
end:
    return ret;
}

int32_t  ProvCommand::InstallBoundKey(Bytes& in)
{
    int32_t ret = NOT_ERROR;
    Bytes   lcIn, lcOut;
    TLV     lcTlv;
    File    drkFile;
#if defined(DRKFEATURE_HWVAULT_ENABLE) || defined(VAULTKEEPER_ENABLED)
    int32_t lcrv = NOT_ERROR;
    TLV     vaultTlv;
    Bytes   drkData;
#endif

    /* Convert drkBlob to Bytes */
    if ((ret = lcTlv.add(TLV_CERT, in)) != NOT_ERROR) {
        LOGME("lcTlv.add is failed. %d.", ret);
        goto end;
    }

    if ((ret = lcTlv.encode(lcIn)) != NOT_ERROR) {
        LOGME("lcTlv.encode is failed. %d.", ret);
        goto end;
    }

    if ((ret = CommandWithoutWrappedKey(CMD_INSTALL_DRK_CERT, lcIn, lcOut)) != NOT_ERROR) {
        LOGME("To check support drk v2 is failed with ret : %d.", ret);
        goto end;
    }

    if ((ret = drkFile.openFile((char *)DRK_SAVE_FILE_PATH, WRITE)) == NOT_ERROR) {
        if ((ret = drkFile.writeFile((uint8_t *)lcOut, lcOut.length())) == NOT_ERROR) {
            LOGMD("DRK Cert is saved [len %d]", lcOut.length());
#if defined(DRKFEATURE_HWVAULT_ENABLE)
            hidl_vec<uint8_t> type, blob;
            const char *DRK_SLOT_NAME = "drk";

            vaultTlv.add(TLV_SIGNATURE, (uint8_t *)HWVAULT_DRK_MAGIC_CODE, (uint32_t)strlen(HWVAULT_DRK_MAGIC_CODE));
            vaultTlv.add(TLV_ENCRYPTED_DATA_BLOB, (uint8_t *)lcOut, lcOut.length());
            vaultTlv.encode(drkData);

            type.setToExternal((uint8_t *)DRK_SLOT_NAME, strlen(DRK_SLOT_NAME));
            blob.setToExternal((uint8_t *)drkData, drkData.length());
            if((lcrv = hermes_put_persistent_cred(type, blob)) != NOT_ERROR) {
                LOGMD("[WARN] DRK Cert Backup isn't saved [ret %d : len %d]", lcrv, drkData.length());
            } else {
                LOGMD("DRK Cert Backup is saved [len %d]", drkData.length());
            }
#elif defined(VAULTKEEPER_ENABLED)
            if (DeviceInfo::GetFeatureSet(VK_FEATURE) == NOT_ERROR) {
                VaultKeeperManager *drkFilebak = VaultKeeperManager::getInstance();

                vaultTlv.add(TLV_SIGNATURE, (uint8_t *)VK_DRK_MAGIC_CODE, (uint32_t)strlen(VK_DRK_MAGIC_CODE));
                vaultTlv.add(TLV_ENCRYPTED_DATA_BLOB, (uint8_t *)lcOut, lcOut.length());
                vaultTlv.encode(drkData);
                lcrv = drkFilebak->write(VaultKeeperManager::TYPE_VAULT_DATA_SHELTERED, (char *)drkData, (int)drkData.length());
                if (lcrv != HIDL_SUCCESS) {
                    LOGMD("[WARN] DRK Cert Backup isn't saved [ret %d : len %d]", lcrv, drkData.length());
                } else {
                    LOGMD("DRK Cert Backup is saved [len %d]", drkData.length());
                }
            } else {
                LOGMD("Skip to save Cert.");
            }
#else
            LOGMD("Skip to save DRK Cert for Backup.");
#endif
        }
    }
end:
    if (ret == NOT_ERROR) {
        DRKLog::InitIssuedLog();
    } else {
        LOGME("Caution !!! DRK Cert isn't saved [%d]", ret);
    }
    SAVEISSUELOG(ret);
    return ret;
}

int32_t  ProvCommand::UnwrapKeyBlob(int32_t cmd, Bytes& in, Bytes& out)
{
    int32_t ret = NOT_ERROR;

    Bytes lcIn, lcKeyBlob;
    TLV lcTlv;

    if ((ret = checkKeyBlobFormat((uint8_t *)in, in.length(), lcKeyBlob)) != NOT_ERROR) {
        LOGME("Checking KeyBlobl Format is failed with ret : %d.", ret);
        goto end;
    }

    if ((ret = lcTlv.add(TLV_WRAPPED_KEY, lcKeyBlob)) != NOT_ERROR) {
        LOGME("lcTlv.add is failed. %d.", ret);
        goto end;
    }

    if ((ret = lcTlv.encode(lcIn)) != NOT_ERROR) {
        LOGME("lcTlv.encode is failed. %d.", ret);
        goto end;
    }

    if ((ret = CommandWithoutWrappedKey(cmd, lcIn, out)) != NOT_ERROR) {
        LOGME("To check support drk v2 is failed with ret : %d.", ret);
        goto end;
    }

end:
    return ret;
}

int32_t  ProvCommand::IsSupportDRKv2()
{
    int32_t ret = NOT_ERROR;
    Bytes lcIn, lcOut;
    if ((ret = CommandWithoutWrappedKey(CMD_IS_SUPPORTED_DRK_V2, lcIn, lcOut)) != NOT_ERROR) {
        LOGME("To check DRK version is failed with ret : %d.", ret);
    }

    return ret;
}

#ifndef USE_RELEASE
int32_t  ProvCommand::VerifyDRKforSelfTest(Bytes& in, Bytes& out)
{
    int32_t ret = NOT_ERROR;

    if ((ret = CommandWithoutWrappedKey(CMD_VERIFY_SERVICE_KEY, in, out)) != NOT_ERROR) {
        LOGME("%s failed with ret : %d.", __func__, ret);
    }

    return ret;
}
#endif
}  // namespace drk
}  // namespace security
}  // namespace hardware
}  // namespace samsung
}  // namespace vendor
