/*
 * =====================================================================================
 *
 *       Filename:  teeCmdExecuter.c
 *
 *    Description:  Parsing data and execute command.
 *
 *        Version:  1.0
 *        Created:  03/28/2017 03:40:48 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 "commonConfig.h"
#include "TLV.h"
#include "cryptoPlatform.h"
#include "log.h"
#include "certGenerator.h"
#include "PlatformConfig.h"

int32_t taCmdExecute(uint32_t cmdId, const uint8_t *inData, uint32_t inDataLen,
                     uint8_t *outData, uint32_t *outDataLen)
{
    int32_t ret = NOT_ERROR;
    uint8_t *pWrappedKey = NULL, *pKeyInfo = NULL, *pAttrs = NULL;
    uint16_t wrappedKeyLen = 0, keyInfoLen = 0, tidLen = 0, attrsLen = 0;
    uint8_t *pModelName = NULL, *pHuid = NULL, *pPreGenKeyPair = NULL, *pCert = NULL, *pImei = NULL;
    uint16_t modelNameLen = 0, huidLen = 0, preGenKeyPairLen = 0, certLen = 0, imeiLen = 0;
    KeyType_t keyType = KEY_TYPE_NONE;
#if (defined USE_QSEE) || (defined USE_MOBICORE)
    uint32_t wrappedKeyLenV1 = 0;
#endif  // End of USE_QSEE || USE_MOBICORE
#if (defined USE_QSEE)
    Tlv_t *pTlv = NULL;
    uint32_t dataLen = 0;
    uint32_t pos = 0;
#endif  // End of USE_QSEE
#if (defined USE_MOBICORE)
    uint32_t keyInfoLenV1 = 0, tidLenV1 = 0, attrsLenV1 = 0;
#endif  // End of USE_MOBICORE

    if(inData == NULL || inDataLen > MAX_SKM_BUF_SIZE || outData == NULL || outDataLen == NULL) {
        LOGE("%s : Invalid argument.", __func__);
        return ERR_TA_INVALID_ARGUMENT;
    }

    memset(outData, 0, *outDataLen);
    initAttrSubst();

    switch(cmdId) {
    /**************************************************************
     *
     * New skm commands.
     *
     **************************************************************/
    case CMD_GENERATE_SERVICE_KEY :
        if((ret = tlvGet(inData, inDataLen, TLV_KEY_INFO, &keyInfoLen, &pKeyInfo)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        if(keyInfoLen != sizeof(ServiceKeyInfo_t)) {
            LOGE("Invalid key info size. %d", keyInfoLen);
            return ERR_TA_INVALID_BLOB;
        }

        if((ret = tlvGet(inData, inDataLen, TLV_WRAPPED_KEY, &wrappedKeyLen, &pWrappedKey)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        if(wrappedKeyLen > MAX_ENCRYPTED_BLOB_SIZE) {
            LOGE("Encrypted blob size(%d) is invalid.", wrappedKeyLen);
            return ERR_TA_INVALID_ARGUMENT;
        }

        if((ret = tlvGet(inData, inDataLen, TLV_ATTRS, &attrsLen, &pAttrs)) != NOT_ERROR) {
            LOGI("ATTRS value is not exist. %d", ret);
        }

        if(pAttrs != NULL && attrsLen > MAX_SKM_BUF_SIZE - sizeof(struct KeyInfo) - wrappedKeyLen - tidLen) {
            LOGE("Attr size(%d) is invalid.", attrsLen);
            return ERR_TA_INVALID_ARGUMENT;
        }

        return generateServiceKey(pWrappedKey, wrappedKeyLen, outData, outDataLen, (ServiceKeyInfo_t *)pKeyInfo, pAttrs, attrsLen);

    case CMD_VERIFY_DRK_KEY :
    case CMD_GET_DEV_INFO :
        if((ret = tlvGet(inData, inDataLen, TLV_WRAPPED_KEY, &wrappedKeyLen, &pWrappedKey)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        return verifyDeviceRootKey(pWrappedKey, wrappedKeyLen,outData, outDataLen);

    case CMD_GET_DRK_UID :
        if((ret = tlvGet(inData, inDataLen, TLV_WRAPPED_KEY, &wrappedKeyLen, &pWrappedKey)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        return readDrkCertificateUID(outData, outDataLen, pWrappedKey, wrappedKeyLen);

    case CMD_IS_SUPPORTED_DRK_V2 :
        LOGI("This TA supports DRK v2.");
        *outDataLen = 0;
        return NOT_ERROR;    // Always return true;

    case CMD_GET_DRK_CERT :
        if((ret = tlvGet(inData, inDataLen, TLV_WRAPPED_KEY, &wrappedKeyLen, &pWrappedKey)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        return getDrkCertificate(outData, outDataLen, pWrappedKey, wrappedKeyLen);

 #if (defined USE_QSEE) && !(defined USE_QSEE_PROV_SFS)

    case CMD_INSTALL_DRK_KEY :
        if((ret = tlvGet(inData, inDataLen, TLV_WRAPPED_KEY, &wrappedKeyLen, &pWrappedKey)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        return installDrkV1(pWrappedKey, wrappedKeyLen, outData, outDataLen);
#endif  // End of USE_QSEE && !USE_QSEE_PROV_SFS

    case CMD_MAKE_DRK_CSR :
        if((ret = tlvGet(inData, inDataLen, TLV_MODEL_NAME, &modelNameLen, &pModelName)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        if((ret = tlvGet(inData, inDataLen, TLV_HUID, &huidLen, &pHuid)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        if((ret = tlvGet(inData, inDataLen, TLV_IMEI, &imeiLen, &pImei)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        return generateCertificateSigningRequest(pModelName, modelNameLen, pHuid, huidLen,
                pPreGenKeyPair, preGenKeyPairLen, pImei, imeiLen, outData, outDataLen);

    case CMD_INSTALL_DRK_CERT :
        if((ret = tlvGet(inData, inDataLen, TLV_CERT, &certLen, &pCert)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        return installCertificate((const uint8_t *)pCert, certLen, outData, outDataLen);

    case CMD_READ_DRK_FROM_HWVAULT :
        return readDrkFromHwvault(outData, outDataLen);

        /**************************************************************
         *
         * Supported old skm commands for compatibility with DM-verity.
         *
         **************************************************************/
#if !(defined USE_TIZEN)    // Block old commands in Tizen.

    case CMD_GENERATE_RSA_SERVICE_KEY_V1 :
    case CMD_GENERATE_RSA_SERVICE_KEY_V0 :
        keyType = KEY_TYPE_RSA;

    case CMD_GENERATE_EC_SERVICE_KEY_V1 :
        if(keyType == KEY_TYPE_NONE) {
            keyType = KEY_TYPE_EC;
        }

#if (defined USE_QSEE)
        memcpy(&dataLen, inData + inDataLen - sizeof(uint32_t), sizeof(dataLen));

        if(dataLen > inDataLen) {
            LOGE("In data length is not proper - %d %d.", dataLen, inDataLen);
            return ERR_TA_BUFFER_OVERFLOW;
        }

        pTlv = (Tlv_t *)inData;

        if(pTlv->tag == KEYBLOB_TAG_TA_NAME) {
            if((tidLen = pTlv->dataLen) > MAX_TID_SIZE) {
                LOGE("Invalid TID length: %d", tidLen);
                return ERR_TA_INVALID_ARGUMENT;
            }

            pTlv = (Tlv_t *)(pTlv->data + pTlv->dataLen);
            pos += tidLen;
        }

        if(pTlv->tag == KEYBLOB_TAG_ATTRS) {
            attrsLen = pTlv->dataLen;
            pAttrs = pTlv->data;

            if((dataLen > attrsLen) && (pos < dataLen - attrsLen)) {
                pTlv = (Tlv_t *)(pTlv->data + pTlv->dataLen);
                pos += attrsLen;
            } else {
                LOGE("Inserted data is too big - %d %d", tidLen, attrsLen);
                return ERR_TA_BUFFER_OVERFLOW;
            }
        }

        if((dataLen > sizeof(struct KeyInfo)) && (pos < dataLen - sizeof(struct KeyInfo))) {
            pKeyInfo = (uint8_t *)pTlv;
            pos += sizeof(struct KeyInfo);

            memcpy(&wrappedKeyLenV1, inData + inDataLen - sizeof(uint32_t) * 2, sizeof(wrappedKeyLenV1));

            if((dataLen > wrappedKeyLenV1) && (pos < dataLen - wrappedKeyLenV1)) {
                pWrappedKey = pKeyInfo + sizeof(struct KeyInfo);

                LOGD("dataLen = %d, tidLen = %d, attrsLen = %d, drkKeyLen = %d", dataLen, tidLen, attrsLen, wrappedKeyLenV1);

                ret = generateServiceKeyV1((struct KeyInfo *)pKeyInfo, pWrappedKey, wrappedKeyLenV1,
                                           outData, outDataLen, pAttrs, attrsLen, keyType);
            } else {
                LOGE("Invalid blob length : %d %d %d", tidLen, attrsLen, dataLen);
                return ERR_TA_INVALID_BLOB;
            }
        } else {
            LOGE("Invalid blob length : %d %d %d", tidLen, attrsLen, dataLen);
            return ERR_TA_INVALID_BLOB;
        }

#elif (defined USE_MOBICORE)
        memcpy(&keyInfoLenV1, inData, sizeof(keyInfoLenV1));

        if(keyInfoLenV1 > sizeof(struct KeyInfo)) {
            LOGE("KeyInfo size(%d) is invalid.", keyInfoLenV1);
            return ERR_TA_INVALID_ARGUMENT;
        }

        pKeyInfo = (uint8_t *)inData + sizeof(keyInfoLenV1);
        memcpy(&wrappedKeyLenV1, pKeyInfo + keyInfoLenV1, sizeof(wrappedKeyLenV1));

        if(wrappedKeyLenV1 > MAX_ENCRYPTED_BLOB_SIZE) {
            LOGE("Encrypted blob size(%d) is invalid.", wrappedKeyLenV1);
            return ERR_TA_INVALID_ARGUMENT;
        }

        pWrappedKey = pKeyInfo + keyInfoLenV1 + sizeof(wrappedKeyLenV1);
        memcpy(&tidLenV1, pWrappedKey + wrappedKeyLenV1, sizeof(tidLenV1));

        if(tidLenV1 > MAX_TID_SIZE) {
            LOGE("TID size(%d) is invalid.", tidLenV1);
            return ERR_TA_INVALID_ARGUMENT;
        }

        memcpy(&attrsLenV1, pWrappedKey + wrappedKeyLenV1 + sizeof(tidLenV1) + tidLenV1, sizeof(attrsLenV1));

        if(attrsLenV1 > MAX_SKM_BUF_SIZE - sizeof(struct KeyInfo) - wrappedKeyLenV1 - tidLenV1) {
            LOGE("Attr size(%d) is invalid.", attrsLenV1);
            return ERR_TA_INVALID_ARGUMENT;
        }

        if(attrsLenV1 > 0) {
            pAttrs = pWrappedKey + wrappedKeyLenV1 + sizeof(tidLenV1) + tidLenV1 + sizeof(attrsLenV1);
        }

        if((ret = generateServiceKeyV1((struct KeyInfo *)pKeyInfo, pWrappedKey, wrappedKeyLenV1,
                                       outData + sizeof(uint32_t), outDataLen, pAttrs, attrsLenV1, keyType)) == NOT_ERROR) {
            memcpy(outData, outDataLen, sizeof(uint32_t));
            *outDataLen += sizeof(uint32_t);
        }

#elif (defined USE_BLOWFISH)

        if((ret = tlvGet(inData, inDataLen, TLV_KEY_INFO, &keyInfoLen, &pKeyInfo)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        if((ret = tlvGet(inData, inDataLen, TLV_WRAPPED_KEY, &wrappedKeyLen, &pWrappedKey)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        if(wrappedKeyLen > MAX_ENCRYPTED_BLOB_SIZE) {
            LOGE("Encrypted blob size(%d) is invalid.", wrappedKeyLen);
            return ERR_TA_INVALID_ARGUMENT;
        }

        if((ret = tlvGet(inData, inDataLen, TLV_ATTRS, &attrsLen, &pAttrs)) != NOT_ERROR) {
            LOGI("ATTRS value is not exist.", ret);
        }

        if(pAttrs != NULL && attrsLen > MAX_SKM_BUF_SIZE - sizeof(struct KeyInfo) - wrappedKeyLen - tidLen) {
            LOGE("Attr size(%d) is invalid.", attrsLen);
            return ERR_TA_INVALID_ARGUMENT;
        }

        ret = generateServiceKeyV1((struct KeyInfo *)pKeyInfo, pWrappedKey, wrappedKeyLen, outData, outDataLen, pAttrs,
                                   attrsLen, keyType);
#else
        ret = ERR_TA_UNSUPPORTED_CMD;
#endif  // End of USE_QSEE | USE_MOBICORE | USE_BLOWFISH
        return ret;

    case CMD_VERIFY_RSA_DRK_V1 :
#if (defined USE_QSEE)
        pWrappedKey = (uint8_t *)inData;
        wrappedKeyLenV1 = inDataLen;

        ret = verifyDeviceRootKey(pWrappedKey, wrappedKeyLenV1,outData, outDataLen);
#elif (defined USE_MOBICORE)
        memcpy(&wrappedKeyLenV1, inData + sizeof(uint32_t) + sizeof(struct KeyInfo), sizeof(wrappedKeyLenV1));
        pWrappedKey = (uint8_t *)(inData + sizeof(uint32_t) * 2 + sizeof(struct KeyInfo));

        ret = verifyDeviceRootKey(pWrappedKey, wrappedKeyLenV1,outData, outDataLen);
#elif (defined USE_BLOWFISH)

        if((ret = tlvGet(inData, inDataLen, TLV_WRAPPED_KEY, &wrappedKeyLen, &pWrappedKey)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        ret = verifyDeviceRootKey(pWrappedKey, wrappedKeyLen,outData, outDataLen);
#else
        ret = ERR_TA_UNSUPPORTED_CMD;
#endif  // End of USE_QSEE | USE_MOBICORE | USE_BLOWFISH
        *outDataLen = 0;
        return ret;

    case CMD_GET_DRK_UID_V1 :
        memset(outData, 0, *outDataLen);
#if (defined USE_QSEE)
        return readDrkCertificateUID(outData, outDataLen, (uint8_t *)inData, inDataLen);
#elif (defined USE_MOBICORE)
        memcpy(&wrappedKeyLenV1, inData + sizeof(uint32_t) + sizeof(struct KeyInfo), sizeof(wrappedKeyLenV1));
        pWrappedKey = (uint8_t *)(inData + sizeof(uint32_t) * 2 + sizeof(struct KeyInfo));
        *outDataLen -= sizeof(uint32_t);

        if((ret = readDrkCertificateUID(outData + sizeof(uint32_t), outDataLen,
                                        pWrappedKey, wrappedKeyLenV1)) == NOT_ERROR) {
            *outDataLen = strlen((char *)outData + sizeof(uint32_t));
            memcpy(outData, outDataLen, sizeof(uint32_t));
            *outDataLen += sizeof(uint32_t);
        }

        return ret;
#elif (defined USE_BLOWFISH)

        if((ret = tlvGet(inData, inDataLen, TLV_WRAPPED_KEY, &wrappedKeyLen, &pWrappedKey)) != NOT_ERROR) {
            LOGE("Failed to get blob with error %d.", ret);
            return ERR_TA_BASE + ret;
        }

        return readDrkCertificateUID(outData, outDataLen, pWrappedKey, wrappedKeyLen);
#else
        return ERR_TA_UNSUPPORTED_CMD;
#endif  // End of USE_QSEE | USE_MOBICORE | USE_BLOWFISH

#if (defined USE_QSEE)

    case CMD_SHARE_SERVICE_KEY_V1 :
    case CMD_SHARE_SERVICE_KEY_V0 :
        pKeyInfo = (uint8_t *)inData;
#if !(defined USE_QSEE_SFS)
        memcpy(&wrappedKeyLenV1, inData + sizeof(struct KeyInfo), sizeof(wrappedKeyLenV1));

        if(wrappedKeyLenV1 > 0) {
            pWrappedKey = (uint8_t *)inData + sizeof(struct KeyInfo) + sizeof(wrappedKeyLenV1);
        } else {
            pWrappedKey = NULL;
        }

#endif  // End of !USE_QSEE_SFS
        return getSharedServiceKey((struct KeyInfo *)pKeyInfo, pWrappedKey, wrappedKeyLenV1, outData, outDataLen);
#endif  // End of USE_QSEE
#endif  // End of !USE_TIZEN

    default :
        LOGE("Not supported command : 0x%X", cmdId);
        return ERR_TA_UNSUPPORTED_CMD;
    }
}
