/* KeyLoader.java             nov 2000
 *
 * Erwan Lemonnier & Eric Nordenstam
 *
 * Run a set of tests on classes Math, RSA and KeyLoader.
 ***********************************************************************/

import java.math.BigInteger;
import java.util.Random;

/**
 * Run tests on classes Math, RSA and KeyLoader.
 * <b><code>java Test [-v] num</code></b> run the test number num.
 * The -v option is for 'verbose', and displays more details about
 * performed tests. The list of tests can be obtained by just
 * running <b><code>java Test</code></b>.
 * <p>
 * @author E.Lemonnier & E.Nordenstam
 */

public class Test {
	
	private static final int numBits = 150;
	
	private static boolean verbose = false;
	
	//entry point :)
	public static void main(String[] arg) {
		
		if (arg.length == 0) {
			System.out.println("Run tests on RSA related classes.");
			System.out.println("usage: java Test [-v] <num>");
			System.out.println("  -v  : show details about tests performed.");
			System.out.println("  num=0 : run all tests");
			System.out.println("     =1 : test gcd");
			System.out.println("     =2 : test modInverse");
			System.out.println("     =3 : test modPow");
			System.out.println("     =4 : test isProbablePrime");
			System.out.println("     =5 : test makePrime");
			System.out.println("     =6 : test class KeyLoader");
			System.out.println("     =7 : test RSA");
			System.exit(0);
		}
		
		if (arg.length>1 && arg[0].equals("-v")) {
			verbose = true;
			arg[0] = arg[1];
		}
		
		int t = 0;
		try {
			t = Integer.parseInt(arg[0]);
		} catch(Exception e) {
			System.out.println("argument must be a number.");
			System.out.println("type 'java Test' for help.");
			System.exit(0);
		}
		
		if (arg.length>1 && arg[1].equals("-v"))
			verbose = true;
		
		switch(t) {
		
			case 0: test1();
					test2();
					test3();
					test4();
					test5();
					test6();
					test7();
					break;
					
			case 1:	test1();
					break;
					
			case 2: test2();
					break;
					
			case 3: test3();
					break;
					
			case 4: test4();
					break;
					
			case 5: test5();
					break;
					
			case 6: test6();
					break;
					
			case 7: test7();
					break;
					
			default:	System.out.println("test number must be between 0 and 5");
						System.out.println("type 'java Test' for help.");
						System.exit(0);
		}		
	}
	
	//test Math.gcd
	private static void test1() {
		System.out.println("\n\n*** Test Math.gcd() ***********************");
		
		if (verbose) {
			System.out.println("compare the results of java.math.BigInteger.gcd()");
			System.out.println("and Math.gcd() run on 5 pairs of random large integer.");
		}
			
		Random rand = new Random();
		
		for(int i=0; i<30; i++) {
			BigInteger a = new BigInteger(numBits, rand);
			BigInteger b = new BigInteger(numBits, rand);
			
			BigInteger gcd = a.gcd(b);
			BigInteger mygcd = Math.gcd(a,b);
			
			if (mygcd.equals(gcd))
				System.out.print(".");
			else
				System.out.print("error!!");
			
			if (verbose) {
				System.out.println("    a=     "+a);
				System.out.println("    b=     "+b);
				System.out.println("    gcd=   "+gcd);
				System.out.println("    mygcd= "+mygcd);
			}
		}
	}

	//test Math.modInverse
	private static void test2() {
		System.out.println("\n\n*** Test Math.modInverse() ****************");

		if (verbose) {
			System.out.println("compare the results of java.math.BigInteger.modInverse()");
			System.out.println("and Math.modInverse() run on 5 pairs of random large integer.");
			System.out.println("when integer non invertible, retry.");
		}
			
		Random rand = new Random();
		
		for(int i=0; i<30; i++) {
			BigInteger a = new BigInteger(numBits, rand);
			BigInteger m = new BigInteger(numBits, rand);
			
			try {
				BigInteger inv = a.modInverse(m);
				BigInteger myinv = Math.modInverse(a,m);
				
				if (myinv.equals(inv))
					System.out.print(".");
				else
					System.out.print("error!!");
				
				if (verbose) {
					System.out.println("    a=     "+a);
					System.out.println("    m=     "+m);
					System.out.println("    inv=   "+inv);
					System.out.println("    myinv= "+myinv);
				}
			} catch(Exception e) {	i--; }
		}
	}
	
	//test Math.modPow
	private static void test3() {
		System.out.println("\n\n*** Test Math.modPow() ********************");
		
		if (verbose) {
			System.out.println("compare the results of java.math.BigInteger.modPow()");
			System.out.println("and Math.modPow() run on 5 pairs of random large integer.");
		}
			
		Random rand = new Random();
		
		for(int i=0; i<30; i++) {
			BigInteger a = new BigInteger(numBits, rand);
			BigInteger e = new BigInteger(numBits, rand);
			BigInteger m = new BigInteger(numBits, rand);
			
			BigInteger pow = a.modPow(e,m);
			BigInteger mypow = Math.modPow(a,e,m);
			
			if (mypow.equals(pow))
				System.out.print(".");
			else
				System.out.print("error!!");
			
			if (verbose) {
				System.out.println("    a=     "+a);
				System.out.println("    e=     "+e);
				System.out.println("    m=     "+m);
				System.out.println("    pow=   "+pow);
				System.out.println("    mypow= "+mypow);
			}
		}
	}

	//test Math.isProbablePrime
	private static void test4() {
		System.out.println("\n\n*** Test Math.isProbablePrime *****************");
		System.out.println("2) Test primality of numbers from 2 to 100");
		
		for(int i=2; i<100; i++) {
			BigInteger a = new BigInteger(""+i);
			boolean r1 = a.isProbablePrime(10);
			boolean r2 = Math.isProbablePrime(a, 10);
			
			if (r1==r2)
				System.out.print(".");
			else
				System.out.print("error!!");
		}
		
		
		
		System.out.println("\n2) Test primality of random large number");
		Random rand = new Random();
		
		for(int i=1; i<20; i++) {
			BigInteger a = new BigInteger(numBits, rand);
			boolean r1 = a.isProbablePrime(10);
			boolean r2 = Math.isProbablePrime(a, 10);
			
			if (r1==r2)
				System.out.print(".");
			else
				System.out.print("error!!");
		}
		
		System.out.println("\n3) Test if large primes are recognized");
		
		for(int i=1; i<20; i++) {
			BigInteger a = new BigInteger(numBits, 50, rand);
			boolean r2 = Math.isProbablePrime(a, 30);
			
			if (r2)
				System.out.print(".");
			else
				System.out.print("error!!");
		}
	}
	
	//test Math.makePrime
	private static void test5() {
		System.out.println("\n\n*** Test Math.makePrime **************************");
		for(int i=1; i<30; i++) {
			BigInteger a = Math.makePrime(numBits, 30);
			boolean r2 = a.isProbablePrime(30);
			
			if (r2)
				System.out.print(".");
			else
				System.out.print("error!!");
		}
	}

	//Test class KeyLoader
	private static void test6() {
		System.out.println("\n\n*** Test KeyLoader ********************************");
		
		if (verbose) {
			System.out.println("try to save/load a BigNumber");
		}
			
		Random rand = new Random();
		BigInteger a = new BigInteger(numBits, rand);
		KeyLoader kl = new KeyLoader("user");
		
		kl.savePublicKey(a);
		BigInteger b = kl.loadPublicKey();
		
		if (a.equals(b))
			System.out.println(" ok.");
		else
			System.out.println(" error !!");
	}
	
	//test RSA.keys, RSA.encrypt and RSA.decrypt
	private static void test7() {
		System.out.println("\n\n*** Test RSA.keys(), RSA.encrypt() and RSA.decrypt() *******");
		System.out.println("1) Make keys for user 'test'");
		BigInteger[] keys = RSA.keys();
		System.out.println("2) Try encrypt/decrypt 30 large integer");
		
		BigInteger e = keys[0];
		BigInteger d = keys[1];
		BigInteger n = keys[2];
		for (int i=0; i<30; i++) {
			BigInteger msg = new BigInteger(1000, new Random());
			BigInteger c = RSA.encrypt(msg,e,n);
			BigInteger dc = RSA.decrypt(c,d,n);
		
			if (dc.compareTo(msg)==0)
				System.out.print(".");
			else
				System.out.print("error!!");
		}
		
		if (verbose) {
			System.out.println("encryption key =\n"+e);
			System.out.println("decryption key =\n"+d);
			System.out.println("modulus =\n"+n);
			System.out.println("d length = \t"+d.bitLength());
			System.out.println("n length = \t"+n.bitLength());
		}
	}
	
	//displays the bit representation of a BigInteger
	private static void showBinary(BigInteger b) {
		System.out.print("> "+b.bitLength()+" digits\n> ");
		
		for(int i=b.bitLength()-1; i>=0; i--) {
			if (b.testBit(i))
				System.out.print("1");
			else
				System.out.print("0");
		}
		System.out.print("\n");
	}
}

