import javax.swing.text.*;
import java.text.*;
/**
	PasswordCrackProb

	@author Stefan Wagner
	@date Sa M&auml;r  4 05:45:06 CET 2006

	License: GPL
	Contact author: wagner.stefan (a t)  berlin.de
*/
public class PasswordCrackProb
{
	/** the number of possible passwords */
	private final long M;

	/**
		@param len of passwords
		@param signs to generate a password (a-zA-Z0-9 => 2*26 +  10 = 62
		@param attacksPerDay
		@param change after number of days
		@param interval how many intervals to calculate probabilities to be cracked for.
	*/
	public PasswordCrackProb (int len, int signs, long attacksPerDay, int changeAfterDays, int intervals)
	{
		DecimalFormat numFormat = new DecimalFormat(); // "# ###");
		NumberFormatter formatter = new NumberFormatter (numFormat);
		numFormat.setGroupingUsed (true);
		numFormat.setGroupingSize(3);
		DecimalFormatSymbols dfs = new DecimalFormatSymbols ();
		dfs.setGroupingSeparator(' ');
		numFormat.setDecimalFormatSymbols (dfs);

		M = (long) Math.pow (signs, len);
		long attacksUntilChange = attacksPerDay * changeAfterDays;
		long attacks = intervals * attacksUntilChange;
		System.out.println ("len \t\t\t= " + len);
		System.out.println ("signs \t\t\t= " + signs);
		try
		{
			System.out.println ("attacks per day \t= " + formatter.valueToString (attacksPerDay));

		}
		catch (Exception e)
		{
			System.out.println ("attacks per day \t= " + attacksPerDay);
		}
		System.out.println ("change after days \t= " + changeAfterDays);
		System.out.println ("intervals \t\t= " + intervals + "\tdays = " + (intervals * changeAfterDays)  + "\tyears = " + ((intervals * changeAfterDays) / 365));
		try
		{
			System.out.println ("M \t\t\t= " + formatter.valueToString (M));
		}
		catch (Exception e)
		{
			System.out.println ("M (ex)\t\t\t= " + M);
		}
		try
		{
			System.out.println ("attacks \t\t= " + formatter.valueToString (attacks));
		}
		catch (Exception e)
		{
			System.out.println ("attacks \t\t= " + attacks);
		}
		System.out.printf  ("p(cracked) \t\t= %8.7f without change\n", sp (attacks));
		System.out.printf  ("p(cracked) \t\t= %8.7f with change\n", sp (attacksUntilChange, intervals));
	}

	/** summarized probability */
	public double sp (final long logins)
	{
		return ((double)logins/(double)M);
	}

	/** summarized probability */
	public double sp (final long attacksUntilChange, int intervals)
	{
		double sp = 0.0;
		while (intervals-- > 0)
		{
			sp += (1.0 - sp) * sp (attacksUntilChange);
			// System.out.printf ("intervals to do:" + intervals + " \t= %8.7f\n", sp);
		}
		return (sp);
	}

	/** probability */
	public double p (long logins)
	{
		long keysLeft = M - logins;
		return (1.0/keysLeft);
	}

	/**
		How much will an expiration of passwords improve security?

		Assume an attacker might try 250 000 passwords per second.
		Assume you say the password expires after 183 days, (about 6 months).
		The password is generated from a-zA-Z0-9 which is 62 signs.
		Assume the password is 8 signs long.
		Check how probable is a breakin after 10 year, which is 20 change intervals.

		Call the program with 8 94 250000 s 183 20
	*/
	public static void main (String args[])
	{
		int len = 8;
		int signs = 94;
		int changeAfterDays = 183;
		int intervals = 20;
		long loginsPerTime = 250000;
		char timeMark = 's';

		if (args.length == 6)
		{
			len	= Integer.parseInt (args[0]);
			signs	= Integer.parseInt (args[1]);
			loginsPerTime = Long.parseLong (args[2]);
			timeMark = args[3].charAt (0);
			changeAfterDays	= Integer.parseInt (args[4]);
			intervals	= Integer.parseInt (args[5]);
		}
		else if (args.length != 0)
		{
			usage ();
		}
		long loginsPerDay = loginsPerTime;
		switch (timeMark)
		{
			// break intentionally omitted
			case 's': loginsPerDay *= 60;
			case 'm': loginsPerDay *= 60;
			case 'h': loginsPerDay *= 24;
			case 'd': break;
			default: usage ();
		}
		new PasswordCrackProb (len, signs, loginsPerDay, changeAfterDays, intervals);
	}

	/** */
	public static void usage ()
	{
		System.out.println ("Usage:\tjava PasswordCrackProb len signs loginsPerTime timeflag changeAfterDays intervals");
		System.out.println ("\t\ttimeflag: [smhd] second/ minute/ hours /days");
		System.out.println ("\t\texample:ava PasswordCrackProb 8 94 250000 s 183 20");
		System.exit (1);
	}
}

