/*
 * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Created in Samsung Ukraine R&D Center (SRK) under a contract between
 * LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine)
 * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
 *
 * Created on: Dec 19, 2017
 * Author: Kostiantyn Volobuiev <k.volobuyev@samsung.com>
 * Author: Viktor Kopp (v.kopp@samsung.com)
 * Author: Andrey Orlenko (a.orlenko@samsung.com)
 * Brief: Module that responds for miscellaneous operations.
 */

#include "TzwMisc.h"

#include "TigerLogging.h"
#include "TigerMacros.h"
#include "TigerTci.h"

#if defined(TZ_MODEL_QCOM)
    #include <qsee_heap.h>
    #include <qsee_comstr.h>
    #include <qsee_message.h>
    #include <qsee_ecc.h>
    #include <stdlib.h>
#elif defined(TZ_MODEL_Kinibi)
    #include "TlApi/TlApiCrypto.h"
    #include "TlApi/TlApiSecurity.h"

static TEE_Result digestSHA1Of(const uint8_t *buffer, uint32_t len_of_buffer,
                            uint8_t *digest, uint32_t *len_of_digest) {
    TEE_Result nResult = TEE_SUCCESS;
    uint8_t *pData = (uint8_t*)buffer;
    size_t nDataLen = len_of_buffer;
    size_t nBlockLen = 128;
    size_t nBlockCount;
    size_t nLastBlockLen;

    TEE_OperationHandle operationHandle = 0;

    nResult = TEE_AllocateOperation(&operationHandle, TEE_ALG_SHA1, TEE_MODE_DIGEST, 0);
    if (TEE_SUCCESS != nResult){
        LOG_E("TEE_AllocateOperation failed")
        return nResult;
    }

    for (nBlockCount = 0; nBlockCount < (nDataLen / nBlockLen); nBlockCount++) {
        TEE_DigestUpdate(operationHandle, pData, nBlockLen);

        pData = (void *)((uint8_t *)pData + nBlockLen);
    }
    nLastBlockLen = (nDataLen % nBlockLen);

    if (nLastBlockLen != 0) {
        TEE_DigestUpdate(operationHandle, pData, nLastBlockLen);
    }

    nResult = TEE_DigestDoFinal(operationHandle, NULL, 0, digest, len_of_digest);
    if (TEE_SUCCESS != nResult){
        LOG_E("TEE_DigestDoFinal failed, nResult: 0x%08x", nResult)
    }

    TEE_FreeOperation(operationHandle);

    return nResult;
}

#endif

#define QSEE_TA_ALIAS_MAX_LENGTH (32)

int tzwGenerateRandom(void *randomBuffer, uint32_t randomBufferLen) {
#if defined(TIGER_TZ_MODEL_BLOWFISH)
   TEE_GenerateRandom(randomBuffer, randomBufferLen);
   return 0;
#elif defined(TZ_MODEL_QCOM)
   return qsee_get_random_bytes(randomBuffer, randomBufferLen);
#elif defined(TZ_MODEL_Kinibi)
    uint32_t buffLen = randomBufferLen;
    LOG_D("randomBufferLen[in]:%" PRIu32, randomBufferLen);
    tlApiResult_t ret = tlApiRandomGenerateData(TLAPI_ALG_PSEUDO_RANDOM, randomBuffer, &buffLen);
    LOG_D("randomBufferLen[out]:%" PRIu32, buffLen);
    if(TLAPI_OK != ret){
        LOG_E("fail to tlApiRandomGenerateData(): %d", ret);
    }
    return ret;
#endif
}

TEE_Result tzwUnwrapData(const char* sourceAppName,
                         const uint8_t* wrappedData, uint32_t const wrappedDataLen,
                         uint8_t* plainData, uint32_t* plainDataLen) {
    LOG_FUNC_BEGIN;

    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(NULL != wrappedData);
    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(NULL != plainData);
    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(0 != wrappedDataLen);
    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(NULL != plainDataLen);
    TIGER_CHECK_FUNCTION_ARGUMENT_RETURN(strlen(sourceAppName) <= QSEE_TA_ALIAS_MAX_LENGTH);
    uint8_t digest[20];
    uint32_t len = 20;

    LOG_D("Trying unwrap data for '%s', size: %d", sourceAppName, wrappedDataLen);

#ifdef __DEV_DEBUG__
    logRawByteArrayHex(wrappedData, wrappedDataLen, "DRK raw blob");
#endif
    digestSHA1Of(wrappedData, wrappedDataLen, digest, &len);
    logByteArrayHex(digest, len, "digest(SHA1) of DRK raw blob");

#if defined(TZ_MODEL_BLOWFISH)
    TEE_Result result = TEES_UnwrapSecureObject((const unsigned char*) wrappedData,
                                              wrappedDataLen,
                                              plainData,
                                              plainDataLen);
    if (TEE_SUCCESS != result) {
        LOG_E("Couldn't unwrap!, TEES_UnWrapSecureObject() error:%x.", result);
        return result;
    }
#elif defined(TZ_MODEL_QCOM)
    char appName[QSEE_TA_ALIAS_MAX_LENGTH] = { 0 };
    int ret = qsee_decapsulate_inter_app_message(appName,
                                                 (uint8_t*) wrappedData, wrappedDataLen,
                                                 plainData, plainDataLen);
    if (QSEE_MESSAGE_SUCESS != ret) {
        LOG_E("Failed to unwrap(), %x", ret);
        *plainDataLen = 0;
        return TEE_ERROR_BAD_FORMAT;
    }

    if (0 != strncmp(appName, sourceAppName, strlen(sourceAppName))) {
        LOG_E("Failed to unwrap, key not from %s", sourceAppName);
        *plainDataLen = 0;
        return TEE_ERROR_BAD_FORMAT;
    }
#elif defined(TZ_MODEL_Kinibi)

    tlApiResult_t ret = tlApiUnwrapObject((void*)wrappedData, wrappedDataLen,
                            plainData, plainDataLen,
                            TLAPI_UNWRAP_PERMIT_CONTEXT_TL|MC_SO_CONTEXT_TLT);
    if (TLAPI_OK != ret) {
        LOG_E("Unwrap DRK blob error. [return value] = 0x%08x\n", ret);
        return TEE_ERROR_BAD_FORMAT;
    }

#else

#error ("TZ MODEL is not defined yet")
    return TEE_ERROR_BAD_STATE;
#endif

    LOG_D("Unwrapped successfully! %d bytes", *plainDataLen);
    LOG_FUNC_END;
    return TEE_SUCCESS;
}
