HR$ENTROPYSTATE$VERSION = "2004-12-06"
//* ====================================================================================================================
//* hr$entropystate.js -- Copyright (c) 2004 Henk Reints (http://henk-reints.nl)
//* ----------------------------------------------------------------------------
//#
//# This script defines a Constructor for an EntropyState Object, which maintains a sort of entropy pool by gathering
//# info about some mouse and keyboard events plus the system time when they occur, and also scrambles it using the
//# built-in 'Math.random' method.
//# The idea is to collect information that is not controlled by the software but by the user's unpredictable and
//# irreproducable behaviour.
//# The EntropyState Object can be used in a web page to generate random sequences that should be reasonably secure
//# for cryptographic purposes.
//#
//# NOTE: This script uses some properties and methods available in HTML pages if running in a modern browser.
//#       If running in an old browser or in the Windows Script Host it will fail.
//#
//# Constructor:
//#	EntropyState(size)	size is optional (default and minimum = 32)
//#
//# Properties:
//#	none exposed
//#
//# Methods:
//#	update()		to be invoked by event handlers to modify the state as user activity produces events
//#	randomInt()		returns a random 32-bit integer
//#	random()		returns a random number between 0 (inclusive) and 1 (exclusive)
//#	randomString(length)	returns a random string of 'length' characters, all from the base64 character set,
//#				  length is optional, default = 32
//#	toString()		converts the entire state to a multiline string, each line holding the binary value,
//#				  the hexadecimal value, the decimal value, and the real number in [0,1) to which the
//#				  value corresponds.
//#				  the last line shows the count of available numbers, the put index, and the get index
//#				  of the state array.
//#
//# Note:	The entropy state is filled with one new longword in every invocation of the 'update' method.
//#		All methods returning a random number will decrement this count by 1,
//#		and the 'randomString' method can produce 5 characters from one single randomInt result.
//#		If all available numbers of the state array are used up, then the randomXXX methods
//#		will return NaN (Not a Number), randomString will then return a single equals sign: "=".
//#		For obtaining long random strings, the entropy state size should be large enough;
//#		when requesting a string longer than 5 times the state size it will always fail.
//#		The calling program should always check for that situation and ask the user to move his mouse for
//#		a while (or do some other activity that is monitored by YOUR webpage using this EntropyState object)
//#		in order to fill the entropy state with new info.
//#
//# Usage:
//#	- obtain an EntropyState object:	myEsObj = new EntropyState()
//#	- event handlers should invoke myEsobj.update() whenever they are invoked
//#	- use the myEsObj.randomInt, myEsObj.random, or myEsObj.randomString method to obtain a random result.
//#
//# Example:
//#	See entropystate.htm
//#
//# ====================================================================================================================
//# ====================================================================================================================

	function EntropyState(size)
	{
		var MinEntropyStateSize  = 32
		var MaxEntropyStateSize  = MinEntropyStateSize
		var EntropyStatePutIndex = 0
		var EntropyStateGetIndex = 0
		var EntropyStateCount    = 0
		var EntropyStateArray    = []

		this.update = function update()
		{	var E = EntropyStateArray [EntropyStatePutIndex++]
			EntropyStatePutIndex %= MaxEntropyStateSize
			var e = window.event
			var n = 1+Math.random()*31
			var a = e.button | (e.repeat ? 512 : 0)
			var b = (e.x << 16) | e.y
			var c = (document.body.scrollTop   << 16) | document.body.scrollLeft
			var d = (document.body.clientWidth << 16) | document.body.clientHeight
			a |= (e.altKey   ?   8 : 0) | (e.altLeft   ?  16 : 0)
			a |= (e.ctrlKey  ?  32 : 0) | (e.ctrlLeft  ?  64 : 0)
			a |= (e.shiftKey ? 128 : 0) | (e.shiftLeft ? 256 : 0)
			a |= (e.keyCode << 10) | (e.wheelDelta << 27)
			E=(E>>>13)|(E<<19); E^=~a
			E=(E>>>17)|(E<<15); E^=(b+c+d)&0xffffffff
			E=(E>>> 7)|(E<<25); E^=Math.floor(Math.random()*0x100000000)
			E=(E>>>11)|(E<<21); E^=((new Date()).valueOf()%0x100000000)
			E=(E<<n)|(E>>>(32-n))
			if (E < 0) E += 0x100000000
			EntropyStateArray [EntropyStatePutIndex] = E
			EntropyStateCount = Math.min (++EntropyStateCount, EntropyStateArray.length)
		}

		this.randomInt = function randomInt()
		{	if (EntropyStateCount == 0) return NaN
			var E = EntropyStateArray [EntropyStateGetIndex++]
			EntropyStateGetIndex %= EntropyStateArray.length
			EntropyStateCount = Math.max (--EntropyStateCount, 0)
			return E
		}

		this.random = function random()
		{	return randomInt()/0x100000000
		}

		this.randomString = function randomString(length)
		{	var i=0, x="", E=0, C="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
			var L = (arguments.length > 0 ? length : 32)
			for (; i < L; i++)
			{	if (i%5 == 0) {E = this.randomInt(); if (isNaN(E)) return "="}
				x += C.charAt(E&0x3f)
				E >>>= 6
			}
			return x
		}

		this.toString = function toString()
		{	var y,E,E1
			for (var i = 0, x = []; i < EntropyStateArray.length; i++)
			{	y = (i == EntropyStatePutIndex ?                   ">"  : " ")
				y = (i == EntropyStateGetIndex ? (y == ">" ? "=" : "<") : y)
				E = EntropyStateArray[i]
				E1 = 0x100000000+E
				x[i]= y+[ E1.toString( 2).slice(1)	// binary
					, E1.toString(16).slice(1)	// hexadecimal
					,("          "+E).slice(-10)	// decimal
					, E/0x100000000			// real in [0,1)
					].join(" | ")
			}
			x[i] = " " + [EntropyStateCount, EntropyStatePutIndex, EntropyStateGetIndex].join(" | ")
			return x.join("\n")
		}

		function initState()
		{	var E,i,n
			for (i = 0; i < MaxEntropyStateSize; i++)
			{	n = 1+Math.random()*31
				E = ((new Date()).valueOf()%0x100000000)
				E = (E<<n)|(E>>>(32-n))
				E^= Math.floor(Math.random()*0x100000000)
				if (E < 0) E += 0x100000000
				EntropyStateArray[i] = E
			}
		}
		if (arguments.length > 0 && isFinite(size) ) MaxEntropyStateSize = Math.round(Math.abs(size))
		MaxEntropyStateSize = Math.max(MaxEntropyStateSize,MinEntropyStateSize)
		initState()
	}

//* ====================================================================================================================
//* [end of file] hr$entropystate.js -- Copyright (c) 2004 Henk Reints (http://henk-reints.nl)
//* ------------------------------------------------------------------------------------------

