/*
								+----------------------------------+
								|                                  |
								| ***  Random generator model  *** |
								|                                  |
								|   Copyright © -tHE SWINe- 2007   |
								|                                  |
								|           RandSafe.cpp           |
								|                                  |
								+----------------------------------+
*/

/**
 *	@file RandSafe.cpp
 *	@brief cryptographically safe random generator
 *	@date 2007
 *	@author -tHE SWINe-
 */

/*
 *								=== ~platform utils ===
 */

#include "NewFix.h"
#include "CallStack.h"
#include "RandSafe.h"

#if defined(_WIN32) || defined(_WIN64)
#define NOMINMAX
#include <windows.h>
#include <wincrypt.h>

static class CWinCrypt {
protected:
	HCRYPTPROV m_h_crypt;

public:
	CWinCrypt()
	{
		CryptAcquireContext(&m_h_crypt, 0, 0, PROV_RSA_FULL, CRYPT_SILENT);
	}

	~CWinCrypt()
	{
		CryptReleaseContext(m_h_crypt, 0);
	}

	HCRYPTPROV h_Crypt()
	{
		return m_h_crypt;
	}
} uberlame_win_crypt_initializer; // win32 random number generator initializer

#else // _WIN32 || _WIN64

#ifdef HAVE_RANDOM_H
#include <random.h> // unix random generator (don't have?)
#endif // HAVE_RANDOM_H

#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#endif // _WIN32 || _WIN64

template <class CType>
static bool GenRand(const CType &r_n_random)
{
#if defined(_WIN32) || defined(_WIN64)
	return CryptGenRandom(uberlame_win_crypt_initializer.h_Crypt(), sizeof(CType), (BYTE*)&r_n_random) != 0;
#else // _WIN32 || _WIN64
#if defined(HAVE_RANDOM_H)
	return !get_random_bytes(&r_n_random, sizeof(CType));
#elif 1 // disable if your distribution does not have "/dev/random
	static const char *p_s_randpath = "/dev/random";
	int fp = open(p_s_randpath, O_RDONLY);
	if(fp < 0)
		return false;
	// open as read only

	uint32_t r_n_random;
	if(read(fp, &r_n_random, sizeof(CType)) < 0) {
		close(fp);
		return false;
	}
	// get random data

	return true;
#else // HAVE_RANDOM_H
#error "fatal error: no suitable secure random number generator implementation"
	return false; // t_odo - replace by a secure random number generator
#endif // HAVE_RANDOM_H
#endif // _WIN32 || _WIN64
}

/*
 *								=== ~platform utils ===
 */

/*
 *								=== CSafeRandom ===
 */

uint32_t CSafeRandom<true>::n_Rand32() // throw(std::runtime_error)
{
	uint32_t n;
	if(!GenRand(n))
		throw std::runtime_error("CSafeRandom::GenRand() failed");
	return n;
}

uint64_t CSafeRandom<true>::n_Rand64() // throw(std::runtime_error)
{
	uint64_t n;
	if(!GenRand(n))
		throw std::runtime_error("CSafeRandom::GenRand() failed");
	return n;
}

uint32_t CSafeRandom<false>::n_Rand32() // throw(std::runtime_error)
{
	uint32_t n;
	if(!GenRand(n))
		throw std::runtime_error("CSafeRandom::GenRand() failed");
	return n;
}

uint64_t CSafeRandom<false>::n_Rand64() // throw(std::runtime_error)
{
	uint64_t n;
	if(!GenRand(n))
		throw std::runtime_error("CSafeRandom::GenRand() failed");
	return n;
}

/*
 *								=== ~CSafeRandom ===
 */
