/**
 * \file CryptoPlatformMobiCore.c
 * \brief MobiCore related high level crypto functions.
 * \author Oleksandr Gabrilchuk (o.gabrilchuk@samsung.com)
 * \version 0.1
 * \date Created Jun 7, 2013
 * \par In Samsung Ukraine R&D Center (SURC) under a contract between
 * \par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * \par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * \par Copyright: (c) Samsung Electronics Co, Ltd 2012. All rights reserved.
 **/

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "CryptoPlatform.h"
#include "CommLayerData.h"
#include "mobicore_utils.h"

#include "tlStd.h"
#include "tlapi_secdrv.h"
#include "TlApi/TlApi.h"
#include "log.h"
#ifdef USE_FCDRV
#include "fcdrv_hw_hal.h"
#include "tlapi_fcdrv.h"
#endif

uint8_t UUID_PROV_DAP[MAX_UUID_SIZE_16] = {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17};
uint8_t UUID_PROV_DRK[MAX_UUID_SIZE_16] = {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d};

int32_t getSHA1Digest(uint8_t* data, uint32_t len, uint8_t* digest)
{
	uint32_t stSessionHandle = 0;
	uint32_t tmpDigestLen = 20;
	uint8_t tmp_digest[SHA1_DIGEST_LEN] = {0};
	tlApiResult_t tlRes = 0;

	tlRes = tlApiMessageDigestInit(&stSessionHandle, TLAPI_ALG_SHA1);
	if (tlRes != TLAPI_OK) {
		LOGE("tlApiMessageDigestInit failed");
		return PLATFORM_INTERNAL_ERROR;
	}

	tlRes = tlApiMessageDigestDoFinal(stSessionHandle, data, len, tmp_digest, &tmpDigestLen);
	if (tlRes != TLAPI_OK) {
		LOGE("tlApiMessageDigestDoFinal failed");
		return PLATFORM_INTERNAL_ERROR;
	}
	if (tmpDigestLen > SHA1_DIGEST_LEN) {
		LOGE("Digest Length [%d] is bigger than SHA1_DIGEST_LEN", tmpDigestLen);
		return WRONG_DATA;
	}
	memcpy(digest, tmp_digest, tmpDigestLen);
	memset(tmp_digest, 0, sizeof(tmp_digest));

	return NO_ERROR;
}

int32_t getSHA256Digest(uint8_t* data, uint32_t len, uint8_t* digest)
{
	uint32_t stSessionHandle = 0;
	uint32_t tmpDigestLen = SHA256_DIGEST_LEN;
	uint8_t tmp_digest[SHA256_DIGEST_LEN] = {0};
	tlApiResult_t tlRes = 0;

	tlRes = tlApiMessageDigestInit(&stSessionHandle, TLAPI_ALG_SHA256);
	if (tlRes != TLAPI_OK) {
		LOGE("tlApiMessageDigestInit failed");
		return PLATFORM_INTERNAL_ERROR;
	}

	tlRes = tlApiMessageDigestDoFinal(stSessionHandle, data, len, tmp_digest, &tmpDigestLen);
	if (tlRes != TLAPI_OK) {
		LOGE("tlApiMessageDigestDoFinal failed");
		return PLATFORM_INTERNAL_ERROR;
	}

	memcpy(digest, tmp_digest, SHA256_DIGEST_LEN);
	memset(tmp_digest, 0, sizeof(tmp_digest));

	return NO_ERROR;
}

/* XXX: MobiCore PRNG is not secure (for MobiCore V2.0 or lower) */
int32_t getRandBlock(uint8_t *buf, uint32_t len)
{
	if (TLAPI_OK != tlApiRandomGenerateData(TLAPI_ALG_SECURE_RANDOM, buf, &len)) {
		LOGE("ERROR: tlApiRandomGenerateData()");
		return PLATFORM_INTERNAL_ERROR;
	}
	return NO_ERROR;
}

int32_t loadPcr(uint8_t* wrapped, uint32_t wrappedLen, uint8_t* keyBlob, uint32_t* keyBlobLen)
{

	if (NO_ERROR != unwrap(wrapped, wrappedLen, keyBlob, keyBlobLen, UUID_PROV_DAP)) {
		LOGE("unwrap failed");
		return WRAPPING_API_ERROR;
	}

	return NO_ERROR;
}

int32_t loadMLDAPKeyBlob(uint8_t* wrapped, uint32_t wrappedLen, KeyInfo_t* keyInfo, uint8_t* keyBlob, uint32_t* keyBlobLen)
{
	(void)keyInfo;

	if (NO_ERROR != unwrap(wrapped, wrappedLen, keyBlob, keyBlobLen, UUID_PROV_DAP)) {
		LOGE("unwrap failed");
		return WRAPPING_API_ERROR;
	}

	return NO_ERROR;
}

int32_t loadSKMKeyBlob(uint8_t* wrapped, uint32_t wrappedLen, KeyInfo_t* keyInfo, uint8_t* keyBlob, uint32_t* keyBlobLen)
{
	(void)keyInfo;

	if (NO_ERROR != unwrap(wrapped, wrappedLen, keyBlob, keyBlobLen, UUID_PROV_DRK)) {
		LOGE("unwrap failed");
		return WRAPPING_API_ERROR;
	}

	return NO_ERROR;
}

int32_t saveKeyBlob(uint8_t* keyBlob, uint32_t keyBlobLen, KeyInfo_t* keyInfo, uint8_t* wrappedOut, uint32_t* wrappedOutLen, uint8_t* TID, uint32_t TIDLen)
{
	(void)keyInfo;

	if (NO_ERROR != wrap(keyBlob, keyBlobLen, wrappedOut, wrappedOutLen, TID, TIDLen)) {
		LOGE("wrap failed");
		return WRAPPING_API_ERROR;
	}

	return NO_ERROR;
}

/* Get OEM flag for integrity check */
int32_t getOemFlag(void)
{
	int32_t  res = PLATFORM_INTERNAL_ERROR;
	uint32_t value = 0;
	tlApiResult_t ret_dr;

#ifdef USE_FCDRV
	uint32_t index = 3;
	ret_dr = tlApiFcSecGetOemFlag(index, &value);
#else
	ret_dr = tlApiSecGetOemFlag(&value);
#endif

	if (ret_dr != TLAPI_OK) {
		LOGE("sec_get_oem_flag error. [return value] = %d", ret_dr);
		res = GET_OEM_FLAG_ERROR;
		goto cleanup;
	}

	// Integrity Check Failed State
	if (value)
		res = INTEGRITY_ERROR;
	else
		res = NO_ERROR;

cleanup:
	return res;
}