/*******************************************************************
* Mouse Password
* Copyright (C)2005 CS Wagner <cs@kainaw.com>
********************************************************************
* This is more of a proof of concept than a functioning program.
* The goal is to convert user-controlled mouse movements into a
* consistant password that the user can recreate with the same mouse
* movements.
********************************************************************
* USAGE:
* Add a button, link, or anything that can be triggered with a mouse
* click.  Set the mouse click to run mp_record_start('pass_field')
* (where pass_field is the ID of the password field that this will
* populate with the Mouse Password).
********************************************************************
* SETTINGS:
* The only settings that should be messed with are:
* mp_sensitivity: The higher the number, the more complex the
*    password gets, but the harder it is to repeat.
* mp_messages: Messages to the user.  The are displayed in the order
*    entered, with the last being repeated over and over.
********************************************************************
* CONCEPT:
* The user starts the process and mouse positions are recorded.
* A sample of the positions (based on the sensitivity) are mapped on
* a grid (size based on the sensitivity).
* The grid mappings are translated into a password.
* The user repeats the process until two matching passwords are
* produced or the user cancels the attempt.
********************************************************************
* PROBLEMS:
* Any sensitivity that produces a worthwhile password is so
* sensitive that the user is rarely capable to repeat the same
* movements.
* The passwords produced are a simple series of numbers.  It would
* be very easy to hack if the hacker realized that only numbers were
* used.
* Mouse Gestures are normally 'trained'.  You cannot train this
* because doing so would require the program to have a preset number
* of password patterns.  I have considered programming basic shapes
* and programming it to convert random movements into shapes and
* then converting the shape sequences into passwords.
********************************************************************
* NOTE FROM THE AUTHOR:
* I had this idea while reading an article about keystroke recorders
* being used by spyware.  If the user never actually typed passwords
* into websites, there is nothing to record.  Unfortunately, I am
* unable to make this into a worthwhile program.  If you have any
* ideas in mind to improve this, please feel free to do so.  I only
* ask that you let me know about it so I can learn from my mistakes.
********************************************************************/

/** WEBMASTER SETTINGS *********************************************/
// The higher the sensitivity, the better the password, but the harder it is to use.
var mp_sensitivity = 4;
// Message prompts for the user.
var mp_messages = Array("To enter a Mouse Password, click OK and then hold down the left mouse button while moving the mouse in a slow steady pattern.  Release the left mouse button to finish.\nClick Cancel if you do not want to enter a Mouse Password.","Now, click OK and repeat the same pattern for verification.\nClick Cancel to abort.","Two identical patterns are required for verification, please try again.\nClick Cancel to abort.");

/** CAPTURE MOUSE EVENTS *******************************************/
if (document.captureEvents)
	document.captureEvents(Event.MOUSEDOWN | Event.MOUSEUP | Event.MOUSEMOVE)
document.onmousemove = mp_getMouseXY;
document.onmouseup = mp_record_stop;
document.onmousedown = mp_get_ready;

/** INTERNAL USE VARIABLES *****************************************/
var mp_mouseX = -999;
var mp_mouseY = -999;
var mp_listen = false;
var mp_ready = false;
var mp_record = false;
var mp_trackX = Array();
var mp_trackY = Array();
var mp_passes = Array();
var mp_field = null;

/**
* Start the recording process.
* This is a bit misnamed.  It actually prompts the user to start the process,
* or to continue the process if it has already started.
*/
function mp_record_start(field)
{
	var mid = mp_passes.length;
	if(mid >= mp_messages.length) mid = mp_messages.length - 1;
	if(mp_mouseX == -999)
	{
		alert("Either JavaScript is disabled or mouse tracking is not possible in your web browser.");
		mp_listen = false;
		mp_ready = false;
		mp_record = false;
	}
	else if(confirm(mp_messages[mid]))
	{
		mp_trackX = Array();
		mp_trackY = Array();
		if(field != mp_field)
		{
			mp_passes = Array();
			mp_field = field;
		}
		mp_listen = true;
		mp_ready = false;
		mp_record = false;
		setTimeout("mp_record_interval()", 200);
	}
}

/**
* When the user agrees to start, a mouse-down will trigger this to
* actually start the recorder.
*/
function mp_get_ready()
{
	field_obj = document.getElementById(mp_field);
	if(mp_ready)
	{
		mp_record = true;
		field_obj.value = "recording";
		setTimeout("mp_record_interval()", 10);
	}
}

/**
* Stop the recorder and store the password.
* If two passwords match, stick the good password in the password field.
*/
function mp_record_stop()
{
	if(mp_record)
	{
		mp_listen = false;
		mp_ready = false;
		mp_record = false;
		if(mp_field != null)
		{
			field_obj = document.getElementById(mp_field);
			if(field_obj != null)
			{
				mp_passes.push(mp_calculate_pass());
				var match = "";
				for(i=0; i<mp_passes.length; i++)
				{
					for(j=i+1; j<mp_passes.length; j++)
					{
						if(mp_passes[i] == mp_passes[j])
							match = mp_passes[i];
					}
				}
				field_obj.value = match;
				if(match == "")
					mp_record_start(mp_field);
				else
					mp_field = null;
				field_obj.disabled = false;
			}
		}
	}
}

/**
* Record the X/Y position of the mouse
*/
function mp_record_interval()
{
	if(mp_listen)
	{
		mp_ready = true;
		if(mp_record)
		{
			field_obj = document.getElementById(mp_field);
			field_obj.disabled = true;
			mp_trackX.push(mp_mouseX);
			mp_trackY.push(mp_mouseY);
			setTimeout("mp_record_interval()", 10);
		}
		setTimeout("mp_record_interval()", 200);
	}
}

/**
* Turn the series of X/Y positions into a password.
*/
function mp_calculate_pass()
{
	if(mp_trackX.length < 1) return "no-track";
	var minX, maxX, minY, maxY;
	minX = maxX = mp_trackX[0];
	minY = maxY = mp_trackY[0];
	for(i = 1; i < mp_trackX.length; i++)
	{
		minX = Math.min(minX, mp_trackX[i]);
		maxX = Math.max(maxX, mp_trackX[i]);
		minY = Math.min(minY, mp_trackY[i]);
		maxY = Math.max(maxY, mp_trackY[i]);
	}
	
	var pass = "";
	var grid = Array(mp_sensitivity);
	for(r=0; r<mp_sensitivity; r++)
	{
		grid[r] = Array(mp_sensitivity);
		for(c=0; c<mp_sensitivity; c++)
		{
			grid[r][c] = 0;
		}
	}
	var hit = 1;
	for(i=0; i < mp_trackX.length; i += Math.round(mp_trackX.length / mp_sensitivity))
	{
		var r = Math.floor(mp_sensitivity * (mp_trackX[i]-minX)/(maxX+1-minX))
		var c = Math.floor(mp_sensitivity * (mp_trackY[i]-minY)/(maxY+1-minY))
		grid[r][c]+= hit++;
	}
	
	for(r=0; r<mp_sensitivity; r++)
	{
		var total = 0;
		for(c=0; c<mp_sensitivity; c++)
		{
			total += grid[r][c];
		}
		pass+= total;
	}
	for(r=0; r<mp_sensitivity; r++)
	{
		var total = 0;
		for(c=0; c<mp_sensitivity; c++)
		{
			total += grid[c][r];
		}
		pass+= total;
	}
	return pass;
}

/**
* Keep track of the mouse's X/Y position.
*/
function mp_getMouseXY(e)
{
	if(e.screenX && e.screenY)
	{
		mp_mouseX = e.screenX;
		mp_mouseY = e.screenY;
	}
}

// Enjoy!

