// PureNoise CryptoLib (c) 1997-2004, PureNoise Ltd Vaduz <www.cryptolib.com>
// 
// CryptoLib Chaos 2002 (Six-In-One) Package Version 1.96
// 
// Recommended compilation parameters:
// 
// Pentium Pro code, optimized for speed, 4 or 8 byte aligned, multithreaded; GCC: -O3 -fomit-frame-pointer

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <crypto/chaos/chaos.h>

#if defined (WIN32) || defined (_WIN32) || defined (WIN32_WINNT) || defined (_WIN32_WINNT) || defined (__WIN32__) || defined (WINDOWS) || defined (_WINDOWS)

	#include <windows.h>
	#include <process.h>
	
	#define sleep_1ms()		Sleep(1)
	
#else
	
	#include <time.h>
	
	static __forceinline void __fastcall sleep_1ms (void)
	{
		struct timespec req = {1, 100000};
		struct timespec rem;
		
		nanosleep (&req, &rem);
	}
	
#endif

/// Keeping Chaos RNG constantly reseeded with a lot of entropy in the background is as simple as that

static void __cdecl reseeding (void *x) { for (;;) { rand32 (); sleep_1ms (); } }

int __cdecl main (void)
{
	unsigned long		i, k;					/// index variables
	unsigned long		data[16];				/// data storage
	unsigned long		key[128];				/// key storage
	unsigned long		hash[128];				/// hash storage
	unsigned long		secret[128];			/// PRNG internal state
	unsigned long		seed = 0x7BED1AFD;		/// PRNG initialization seed
	chaos_prng			prng = {secret, 128};	/// PRNG pointers
	chaos_prng			stream = {secret, 128};	/// STREAM cipher pointers
	FILE				*f;						/// RNG/PRNG output file
	unsigned __int64	cb, ce;
	
	/// It's more secure to kick off a self-reseeding thread on startup
	/// It's only recommended where large secret key generation is required
	/// As soon as the new thread starts, chaos_init will be called automatically
	
	_beginthread (reseeding, 0, NULL);
	
	///
	/// 1) To use as a block cipher for blocks of data of any size:
	///
	
	/// Some key
	for (k = 0; k < 128; k++) key[k] = k * 0x7BED1AFD;
	/// Some data
	strcpy ((char *) data, "BLOCK CIPHER TEST OK");
	/// Encrypting 5*32 = 160 bit of data with a 128*32 = 4096 bit key
	chaos_block (data, 5, key, 128);
	/// Decrypting the same block of data with the same key
	order_block (data, 5, key, 128);
	/// Let's see what we've got there
	printf ("\"%s\"\n", data);
	
	///
	/// 2,3) To use as a HASH or a MAC function:
	///
	
	/// Setting the initial HASH value to a constant value for a HASH or to a secret value for a MAC
	/// It could be all 0's, but it better be a more or less uniformly distributed set of bits
	for (k = 0; k < 32; k++) ((unsigned char *)hash)[k] = (unsigned char) k*0x7B+0xED;
	/// Some data
	strcpy ((char *) data, "HASH TEST INPUT");	// for a MAC also attach the key
	/// Hashing 4*32 = 128 bit of data into a 8*32 = 256 bit hash
	chaos_block (hash, 8, data, 4);		// 256 bit HASH of "HASH TEST INPUT"
	//	chaos_block (hash, 8, data, 4 + 128);	// 256 bit MAC of "HASH TEST INPUT" with a 128*32 = 4096 bit key
	//or
	//	chaos_block (hash, 8, data, 4);		// 256 bit MAC of "HASH TEST INPUT"
	//	chaos_block (hash, 8, key, 128);	// with a 128*32 = 4096 bit key
	//just make sure the key is hashed in after the data, not before it
	/// Printing out the resulting HASH
	printf ("HASH of \"%s\" is\n", data);
	for (k =  0; k < 16; k++) printf (" %02X", ((unsigned char *)hash)[k]); printf ("\n");
	for (k = 16; k < 32; k++) printf (" %02X", ((unsigned char *)hash)[k]); printf ("\n");
	
	///
	/// 4) To use as a stream cipher:
	///
	
	/// Some key
	for (k = 0; k < 17; k++) key[k] = k * 0x7BED1AFD;
	/// Some data
	strcpy ((char *) data, "STREAM CIPHER TEST OK");
	/// Initializing the PRNG's internal state with a 17*32 = 544 bit key
	pseudo_chaos_init (&stream, key, 17);
	/// Encrypting 6*32 = 192 bit of data with a 17*32 = 544 bit key and a 128*32 = 4096 bit internal state
	for (i = 0; i < 6; i++) data[i] ^= chaos_rand32 (&stream, key, 17);
	/// Initializing the PRNG's internal state with the same key
	pseudo_chaos_init (&stream, key, 17);
	/// Decrypting the same block of data with the same stream
	for (i = 0; i < 6; i++) data[i] ^= chaos_rand32 (&stream, key, 17);
	/// Let's see what we've got there
	printf ("\"%s\"\n", data);
	
	///
	/// 5) To use as a hassle-free fast Random Number Generator:
	///
	
	if (!(f = fopen ("RANDOM.BIN", "wb"))) goto _norng;
	
	printf ("Saving random data into RANDOM.BIN for DIEHARD tests.\n");
	for (i = 0; i < 0x6000; i++)
	{
		for (k = 0; k < 128; k++) data[k] = rand32 ();	// it's self-initializing; no need to call chaos_init ();
		fwrite (data, 128, 4, f);
	}
	fclose (f);
_norng:
	
	///
	/// 6) To use as a PRNG:
	///
	
	if (!(f = fopen ("PRNG.BIN", "wb"))) goto _noprng;
	
	/// Initializing the PRNG's internal state with one 32-bit seed
	pseudo_chaos_init (&prng, &seed, 1);
	
	printf ("Saving pseudo-random data into PRNG.BIN for DIEHARD tests.\n");
	for (i = 0; i < 0x6000; i++)
	{
		for (k = 0; k < 128; k++) data[k] = chaos_rand32 (&prng, prng.key, 128);
		fwrite (data, 128, 4, f);
	}
	fclose (f);
_noprng:
	printf ("DONE.\n");
	return 0;
}
