/*
 * =====================================================================================
 *
 *       Filename:  mcCryptoApi.c
 *
 *    Description:  APIs combined with T-base APIs. 
 *
 *        Version:  1.0
 *        Created:  04/27/2017 08:53:14 AM
 *       Compiler:  armcc
 *
 *         Author:  Dongwook Shim (), dw.shim@samsung.com
 *        Company:  Samsung Electronics
 *
 *        Copyright (c) 2017 by Samsung Electronics, All rights reserved. 
 *
 * =====================================================================================
 */

#include "commonConfig.h"
#include "log.h"
#include "teeCryptoApi.h"

#include "tlStd.h"
#include "TlApi/TlApi.h"
#include "TlApi/TlApiCrypto.h"

#define UNWRAP_FLAGS ( TLAPI_UNWRAP_PERMIT_DELEGATED | TLAPI_UNWRAP_PERMIT_CONTEXT_TL | \
		TLAPI_UNWRAP_PERMIT_CONTEXT_SP | TLAPI_UNWRAP_PERMIT_CONTEXT_DEVICE )

int32_t createSecureObject(const uint8_t *inData, const uint32_t inDataLen, 
		uint8_t *outData, uint32_t *outDataLen, const uint8_t *targetUid, const uint32_t targetUidLen)
{
	int32_t ret = NOT_ERROR;
	tlApiSpTrustletId_t consumerTid = {MC_SPID_SYSTEM, MC_UUID_RESERVED_DEFINE};

	if(inData == NULL || outData == NULL || outDataLen == NULL ||
			targetUid == NULL || targetUidLen != sizeof(consumerTid.uuid.value))
	{
		LOGE("%s : Invalid argument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	memcpy(consumerTid.uuid.value, targetUid, sizeof(consumerTid.uuid.value));
	*outData = 0xfe;

	if((ret = tlApiWrapObject((void *)inData, 0, inDataLen, outData, outDataLen, MC_SO_CONTEXT_TLT,
					MC_SO_LIFETIME_PERMANENT, &consumerTid, TLAPI_WRAP_DEFAULT)) != TLAPI_OK)
	{
		LOGE("Failed to create secure object with error 0x%X.", ret);
		ret = ERR_TA_BASE - ret;
	}

	return ret;
}

int32_t openSecureObject(const uint8_t *inData, const uint32_t inDataLen,
		uint8_t *outData, uint32_t *outDataLen, const uint8_t *targetUid, const uint32_t targetUidLen)
{
	int32_t ret = NOT_ERROR;
	mcSoHeader_t *hdr = (mcSoHeader_t *)inData;

	if(inData == NULL || outData == NULL || outDataLen == NULL ||
			targetUid == NULL || sizeof(hdr->producer.uuid.value) != targetUidLen)
	{
		LOGE("%s : Invalid argument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	if(memcmp(targetUid, hdr->producer.uuid.value, targetUidLen))
	{
		// Check whether SKM TA's secure object to be compatiable with DRK v2.
		if(targetUid[targetUidLen - 1] != 0xc || hdr->producer.uuid.value[sizeof(hdr->producer.uuid.value) - 1] != 0xd)
		{
			LOGE("Unsupported source TA1 : 0x%X.", targetUid[targetUidLen - 1]);
			LOGE("Unsupported source TA : 0x%X.", hdr->producer.uuid.value[sizeof(hdr->producer.uuid.value) - 1]);
			return ERR_TA_INVALID_BLOB;
		}
	}

	if((ret = tlApiUnwrapObject((void *)inData, inDataLen, outData, outDataLen, UNWRAP_FLAGS)) != TLAPI_OK)
	{
		LOGE("Failed to open secure object with error 0x%X.", ret);
		ret = ERR_TA_BASE - ret;
	}

	return ret;
}

int32_t getRandBlock(uint8_t *randomBuffer, uint32_t randomLen)
{
	int32_t ret = NOT_ERROR;
	size_t rndLen = randomLen;

	if(randomBuffer == NULL || randomLen == 0)
	{
		LOGE("%s : Invalid argument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	if((ret = tlApiRandomGenerateData(TLAPI_ALG_SECURE_RANDOM, randomBuffer, &rndLen)) != TLAPI_OK)
	{
		LOGE("Failed to generate random with error 0x%X.", ret);
		return ERR_TA_BASE - ret;
	}

	if(randomLen != rndLen)
	{
		LOGE("Request size(%d) and generated size(%zu) is not matched.", randomLen, rndLen);
		return ERR_TA_GEN_RANDOM_FAILED;
	}

	return rndLen;
}

int32_t getShaDigest(const uint8_t *msg, size_t msgLen, uint8_t *digest, size_t digestLen, DigestAlgo_t algo)
{
	int32_t ret = NOT_ERROR;
	tlApiCrSession_t sessionHandle;
	tlApiMdAlg_t tlApiHashAlgo;
	size_t reqDigestLen = digestLen;

	if(msg == NULL || msgLen == 0 || digest == NULL)
	{
		LOGE("%s : Invalid argument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	switch(algo)
	{
		case  ALGO_SHA1 :
			if(digestLen != TLAPI_ALG_HASH_SHA1_SIZE)
			{
				LOGE("Digest buffer is not matched - %zu.", digestLen);
				return ERR_TA_INVALID_ARGUMENT;
			}
			tlApiHashAlgo = TLAPI_ALG_SHA1;
			break;

		case ALGO_SHA256 :
			if(digestLen != TLAPI_ALG_HASH_SHA256_SIZE)
			{
				LOGE("Digest buffer is not matched - %zu.", digestLen);
				return ERR_TA_INVALID_ARGUMENT;
			}
			tlApiHashAlgo = TLAPI_ALG_SHA256;
			break;

		default :
			LOGE("Not supported algothim : %d.", algo);
			return ERR_TA_INVALID_ARGUMENT;
	}

	if((ret = tlApiMessageDigestInit(&sessionHandle, tlApiHashAlgo)) != TLAPI_OK)
	{
		LOGE("Failed to init digest with error 0x%X.", ret);
		return ERR_TA_BASE - ret;
	}

	if((ret = tlApiMessageDigestDoFinal(sessionHandle, msg, msgLen, digest, &reqDigestLen)) != TLAPI_OK)
	{
		LOGE("Failed to doFinal digest with error 0x%X.", ret);
		return ERR_TA_BASE - ret;
	}

	if(reqDigestLen != digestLen)
	{
		LOGE("Digest size is not matched. %zu.", reqDigestLen);
		return ERR_TA_GEN_HASH_FAILED;
	}

	return ret;
}

int32_t aes256CbcEncrypt(const uint8_t *inData, uint32_t inDataLen, uint8_t *iv,
	uint8_t *outData, uint32_t *outDataLen, const uint8_t *key, const uint32_t keyLen, AesEncryptMode_t mode)
{
	int32_t ret = NOT_ERROR;
	tlApiCrSession_t sessionHandle;
	tlApiSymKey_t symKey;
	tlApiKey_t tlKey;
	tlApiCipherMode_t cipherMode;

	if(inData == NULL || iv == NULL || outData == NULL || outDataLen == NULL || key == NULL)
	{
		LOGE("%s : Invalid argument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	switch(mode)
	{
		case AES_MODE_ENCRYPT :
			cipherMode = TLAPI_MODE_ENCRYPT;
			break;

		case AES_MODE_DECRYPT :
			cipherMode = TLAPI_MODE_DECRYPT;
			break;

		default :
			LOGE("Unsupported mode - %d.", mode);
			return ERR_TA_INVALID_ARGUMENT;
	}

	memset(&symKey, 0, sizeof(symKey));
	memset(&tlKey, 0, sizeof(tlKey));

	symKey.key = (uint8_t *)key;
	symKey.len = keyLen;
	tlKey.symKey = &symKey;

	if((ret = tlApiCipherInitWithData(&sessionHandle, TLAPI_ALG_AES_256_CBC_PKCS7, cipherMode,
					&tlKey, iv, IV_SIZE)) != TLAPI_OK)
	{
		LOGE("Failed to cipherInit with error %X.", ret);
		return ERR_TA_BASE - ret;
	}

	if((ret = tlApiCipherDoFinal(sessionHandle, inData, inDataLen, outData, outDataLen)) != TLAPI_OK)
	{
		LOGE("Failed to cipherDoFinal with error %X.", ret);
		return ERR_TA_BASE - ret;
	}

	return ret;
}
