An Entropy Generator in JavaScript

The JavaScript code below, when run, attempts to gather “entropy” (randomness) from the time between the user’s keypresses, and displays the gathered entropy as bits, hexadecimal digits, and Base64 digits. To see it in action, copy it into an .html file and open that file in a web browser that runs JavaScript.

I’m not sure how well it works. With rapid typing, long runs of 1’s or 0’s tend to appear, which doesn’t seem very random. I’m not sure if the problem is just the nature of typing, the limitations of the way JavaScript runs, or just my own misunderstanding of how entropy works.

entropy


<html>
<body>

<div id="divMain"></div>

<script type="text/javascript">

// main
	
function main()
{
	var session = new Session();
	Globals.Instance.initialize(session);	
}

// classes

function Globals()
{
	// do nothing
}
{
	Globals.Instance = new Globals();

	Globals.prototype.initialize = function(session)
	{
		this.session = session;
		this.session.initialize();
	}
}

function Session()
{
	this.entropyBitsGatheredAsString = "";
	this.timeOfPreviousKeyEvent = new Date();
}
{
	// methods

	Session.prototype.entropyGatherFromEventKey = function(event)
	{
		var now = new Date();

		var millisecondsSincePreviousKeyEvent = 
			now.getTime() 
			- this.timeOfPreviousKeyEvent.getTime();

		var entropyBitGathered = 
			millisecondsSincePreviousKeyEvent % 2;

		this.entropyBitsGatheredAsString += "" + entropyBitGathered; 

		this.domElementUpdate();

		this.timeOfPreviousKeyEvent = now;
	}

	Session.prototype.entropyGatheringReset = function()
	{
		this.entropyBitsGatheredAsString = "";	

		this.domElementUpdate();
	}

	Session.prototype.initialize = function()
	{
		this.domElementUpdate();
		document.body.onkeyup = this.entropyGatherFromEventKey.bind(this);
	}

	// dom

	Session.prototype.domElementUpdate = function()
	{
		if (this.domElement == null)
		{
			var divSession = document.createElement("div");

			var divBits = document.createElement("div");

			var labelBitsGatheredSoFar = document.createElement("label");
			labelBitsGatheredSoFar.innerHTML = "Bits Gathered So Far:";
			divBits.appendChild(labelBitsGatheredSoFar);
			
			var inputBitsGatheredSoFar = document.createElement("textarea");
			inputBitsGatheredSoFar.cols = 40;
			inputBitsGatheredSoFar.rows = 10;
			inputBitsGatheredSoFar.disabled = true;
			this.inputBitsGatheredSoFar = inputBitsGatheredSoFar;
			divBits.appendChild(inputBitsGatheredSoFar);
		
			divSession.appendChild(divBits);

			var divHexadecimal = document.createElement("div");

			var labelAsHexadecimal = document.createElement("label");
			labelAsHexadecimal.innerHTML = "As Hexadecimal:";
			divHexadecimal.appendChild(labelAsHexadecimal);

			var inputAsHexadecimal = document.createElement("textarea");
			inputAsHexadecimal.cols = 40;
			inputAsHexadecimal.rows = 10;
			inputAsHexadecimal.readOnly = true;
			this.inputAsHexadecimal = inputAsHexadecimal;
			divHexadecimal.appendChild(inputAsHexadecimal);

			divSession.appendChild(divHexadecimal);

			var divBase64 = document.createElement("div");

			var labelAsBase64 = document.createElement("label");
			labelAsBase64.innerHTML = "As Base64:";
			divBase64.appendChild(labelAsBase64);

			var inputAsBase64 = document.createElement("textarea");
			inputAsBase64.cols = 40;
			inputAsBase64.rows = 10;
			inputAsBase64.readOnly = true;
			this.inputAsBase64 = inputAsBase64;
			divBase64.appendChild(inputAsBase64);

			divSession.appendChild(divBase64);

			var divControls = document.createElement("div");

			var buttonReset = document.createElement("button");
			buttonReset.innerHTML = "Reset";
			buttonReset.onclick = this.entropyGatheringReset.bind(this);
			divControls.appendChild(buttonReset);

			divSession.appendChild(divControls);

			var divMain = document.getElementById("divMain");
			divMain.appendChild(divSession);

			this.domElement = divSession;

		}

		this.inputBitsGatheredSoFar.value = this.entropyBitsGatheredAsString;

		var numberOfBits = this.entropyBitsGatheredAsString.length;

		var bitsPerNibble = 4;
		var numberOfNibbles = Math.floor(numberOfBits / bitsPerNibble);
		var entropyGatheredAsHexadecimal = "";

		for (var i = 0; i < numberOfNibbles; i++)
		{
			var nibbleAsBitString = this.entropyBitsGatheredAsString.substr
			(
				i * bitsPerNibble, bitsPerNibble
			);

			var nibbleValue = parseInt(nibbleAsBitString, 2);

			var nibbleValueAsHexadecimal = nibbleValue.toString(16);

			entropyGatheredAsHexadecimal += nibbleValueAsHexadecimal;
		}

		this.inputAsHexadecimal.value = entropyGatheredAsHexadecimal;

		var base64DigitsAll = 
			"0123456789"
			+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
			+ "abcdefghijklmnopqrstuvwxyz"
			+ "+/";

		var bitsPerSextet = 6;
		var numberOfSextets = Math.floor(numberOfBits / bitsPerSextet);
		var entropyGatheredAsBase64 = "";

		for (var i = 0; i < numberOfSextets; i++)
		{
			var sextetAsBitString = this.entropyBitsGatheredAsString.substr
			(
				i * bitsPerSextet, bitsPerSextet
			);

			var sextetValue = parseInt(sextetAsBitString, 2);

			var sextetValueAsBase64 = base64DigitsAll[sextetValue];

			entropyGatheredAsBase64 += sextetValueAsBase64;
		}

		this.inputAsBase64.value = entropyGatheredAsBase64;

		return this.domElement;
	}

}

// run

main();

</script>
</body>
</html>

This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s