// Random Data Generation Function

#include "crypto_rand.h"

#ifndef BORING_SSL
#include <openssl/opensslconf.h>
#include <unistd.h>
#endif

#include <tee_internal_api.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <openssl/crypto.h> // needed for FIPS symbols declaration
#include <openssl/rand.h>
#include <openssl/err.h>

#include <gpapi_log.h>
#include <misc_defs.h>

/* Currently requested random buffer size is 32 bytes */
#define PRNG_RANDOM_BUF_SIZE	32

extern int init_crypto(void);

 /*
 * @return  (-1) - fail, 0 - success
 */
#if 0
static int get_time_for_entropy(struct timespec *ts)
{
	struct timespec  local_ts = { 0,0 };
	struct timespec  local_ts1 = { 0,0 };

	// currently secure kernel implements two time source, so try to use them
	if(clock_gettime(CLOCK_MONOTONIC, &local_ts) != 0 &&
		clock_gettime(CLOCK_REE, &local_ts1 ) != 0	)
	{
		MB_LOGE("Couldn't get monotonic and ree time.\n");
		return -1;
	}

	MB_LOGI("local_ts - %"PRId64", %ld\n", local_ts.tv_sec, local_ts.tv_nsec );
	MB_LOGI("local_ts1 - %"PRId64", %ld\n", local_ts1.tv_sec, local_ts1.tv_nsec );

	local_ts.tv_sec += local_ts1.tv_sec;
	local_ts.tv_nsec += local_ts1.tv_nsec;

	*ts = local_ts;

	return 0;
}
#endif

/**
 * @fn      int _generate_random_hw( void* randomBuffer, size_t randomBufferLen )
 * @param   randomBuffer a pointer to the buffer.
 * @param   randomBufferLen size of the buffer
 *
 * @return  0 - fail, 1 - success
 */
static int _generate_random_hw( void* randomBuffer, size_t randomBufferLen )
{

	(void) randomBuffer;
	(void) randomBufferLen;
	MB_LOGE("Warning! CRYPO DRIVER for random generation is disabled.");
	return 0;
}

/*
 * @return  0 - fail, 1 - success
 */
static int crypto_core_entropy_source_sw( void* randomBuffer, size_t randomBufferLen )
{
#if 0
	struct timespec ts;
	size_t const sizNSec = sizeof(ts.tv_nsec);

	if( !randomBuffer || 0 == randomBufferLen )
	{
		return 0;
	}

	if( randomBufferLen <= sizNSec )
	{
		// prepare entropy from the current time
		if( get_time_for_entropy(&ts) != 0 )
		{
			return 0;
		}
		ts.tv_nsec ^= ts.tv_sec;
		TEE_MemMove( randomBuffer, &ts.tv_nsec, randomBufferLen );
		return 0;
	}
	else
	{
		size_t sizCounter = 0;
		// round down buffer size by sizNSec size to prevent buffer overflow
		size_t const sizRoundBuffLen = (randomBufferLen / sizNSec) * sizNSec;

		for( sizCounter = 0; sizCounter < sizRoundBuffLen; sizCounter += sizNSec )
		{
			// prepare entropy from the current time
			if( get_time_for_entropy(&ts) != 0 )
			{
				return 0;
			}
			ts.tv_nsec ^= ts.tv_sec;
			TEE_MemMove( randomBuffer + sizCounter, &ts.tv_nsec, sizNSec );
		}
	}
	return 1;
#else
	(void) randomBuffer;
	(void) randomBufferLen;
        return 0;
#endif
}


//#include <TlApiCrypto.h>

/* XXX: MobiCore PRNG is not secure (for MobiCore V2.0 or lower) */
// int32_t getRandBlock(uint8_t *buf, uint32_t len)
// {
//     if (0 != tlApiRandomGenerateData(2, buf, &len))
//     {
//         MB_LOGE("ERROR: tlApiRandomGenerateData()");
//         return 0;
//     }
//     return 1;
// }

int CryptoCoreEntropySource( void* randomBuffer, int randomBufferLen )
{
	MB_LOGE("%s() enter\n", __FUNCTION__);
	if( 0 == _generate_random_hw( randomBuffer, randomBufferLen ) )
	{
		MB_LOGI("_generate_random_hw() failed. Generating with SW implementation\n");
		if(0 == crypto_core_entropy_source_sw( randomBuffer, randomBufferLen ))
		{
			MB_LOGE("_generate_random_hw() and crypto_core_entropy_source_sw() are failed.\n");
			return 0;
		}
	}
	return 1;
}

void TEE_GenerateRandom(void* randomBuffer, uint32_t randomBufferLen)
{
	if( NULL == randomBuffer )
	{
		MB_LOGE("NULL == randomBuffer\n");
		TEE_Panic(ID_TEE_GenerateRandom);
	}

	if( 0 == randomBufferLen )
	{
		MB_LOGE("randomBuffer = 0\n");
		return;
	}

	int res = RAND_bytes( randomBuffer, randomBufferLen );
	if( 0 >= res )
	{
		MB_LOGE("RAND_bytes() failed! Res = %d \n", res);
		PRINT_OSSL_ERROR_AND_PANIC(ID_TEE_GenerateRandom);
	}
}

