CSC395 Spring 2006

Spring 2008, CSC395 Special Topics: Information Security
Project 2
Due: March. 2, 2008, 23:59.
How: 1 copy in your digital drop box, and 1 copy in your Unix directory
(\CSC395\Project-2). No excuse if you do not meet this requirement.
Objectives:
Prime is important for generating keys for public key cryptography. In this project, we
would learn the theory and implementation of several prime generators.
Prime Generator:
Three prime generators are covered in following:
 Rabin-Miller: find probable prime:
o (Miller Test): Let n be a positive integer with n-1 = 2st where s is a nonnegative integer, and t is an odd positive integer. We say that n passes
Miller’s test for the base b if either bt ≡ 1(mod n) or bkt ≡ -1(mod n) for
some k=2j, 0≤j≤s-1.
o If we repeat Miller’s test on n with different bases b, we can either
discover that n is composite, or force the probability that it is prime as
close to 1 as desired. This is in fact what modern computers do when
searching for large primes, and this particular method of finding
“probable” primes is called the Rabin-Miller test. You need pass n (num)
and numberBases to BigIntegerMath class. Include following code into
your main function.
BigInteger num=null;
do {
byte[] ba=new byte[byteLength];
sr.nextBytes(ba);
num=new BigInteger(1,ba);
} while (BigIntegerMath.primeProbability(num,numberBases,sr)==0);
o If an integer n passes Rabin-Miller for q different bases, then the
probability that n is prime is no less than 1 – (1/4)q. It is important to note
that the bases used for the Rain-Miller test be chosen as randomly as
possible.
o Codes as following (located in BigIntegerMath.java)
//Implements the Rabin-Miller test.
//Number of different bases to try is passed in as an int
//If the BigInteger passes all tests, returns the probabilty it is prime as a double.
//Returns zero if the BigInteger is determined to be composite.
public static double primeProbability(BigInteger n,int numPasses,SecureRandom sr) {
if (n.compareTo(TWO)<0) return 0;
BigInteger b,x;
BigInteger nMinusOne=n.subtract(ONE);
if (numPasses<1) throw new IllegalArgumentException("Number of bases must be positive!");
//If the number is small, just factor it
if (n.compareTo(new BigInteger("2000000"))<0) {
int smalln=n.intValue();
for (int i=2;i<=Math.sqrt(smalln);i++) if (smalln%i==0) return 0;
return 1;
}
for (int i=0;i<numPasses;i++) {
//Choose a random base smaller than n
b=new BigInteger(n.bitLength()-1,sr);
//Test Fermat's condition first
x=b.modPow(nMinusOne,n);
if (!x.equals(ONE)) return 0.0;//not prime
//Divide n-1 by 2
BigInteger[] dr=nMinusOne.divideAndRemainder(TWO);
//Perform the root tests
while (dr[1].equals(ZERO)) {
x=b.modPow(dr[0],n);
//if you get -1, this is a PASS; get out
if (x.equals(nMinusOne)) break;//pass
//Now, if its not -1 or 1, this is a FAIL, return 0
if (!x.equals(ONE)) return 0.0;//not prime
//If its 1, so far its a pass
//We can continue with the test; divide by 2
dr=dr[0].divideAndRemainder(TWO);
}
}
//Only way to get here is by passing all tests
return 1.0-Math.pow(0.25,numPasses);
}

Strong Primes:
o A prime number p is said to be a strong prime if integers r, s, and t exit
such that:
 p-1 has a large number prime factor r.
 p+1 has a large number prime factor s.
 r-1 has a large prime factor t.
o Prime Generator-Gordon’s Algorithm: This algorithm produces strong
primes. When we produce integers that are the product of large strong
primes, these integers are highly resistant to factoring methods.
 Generate two large random primes s and t of approximately the
same size.
 Choose an integer i*, then find the first prime r = 2it + 1, where i =
i*, i*+1, i*+2, …
 Compute z = the lnr of sr-1(mode r), where lrn is least nonnegative
residue.
 Choose an integer k*, then find the first prime p = p* + 2krs,
where k=k*, k*+1, k*+2, …
 P is a strong prime.
o Codes are as following (located in PrimeGenerator.java)
//This method finds and returns a strong prime
public BigInteger getStrongPrime() {
//The strong prime p will be such that p+1 has a large prime factor s
BigInteger s=new BigInteger(minBitLength/2-8,certainty,sr);
//t will be a large prime factor of r, which follows
BigInteger t=new BigInteger(minBitLength/2-8,certainty,sr);
BigInteger i=BigInteger.valueOf(1);
//p-1 will have a large prime factor r
//r is the first prime in the sequence 2t+1, 2*2t+1, 2*3t+1,...
BigInteger r;
do {
r=BigIntegerMath.TWO.multiply(i).multiply(t).add(BigIntegerMath.ONE);
i=i.add(BigIntegerMath.ONE);
} while (!r.isProbablePrime(certainty));
BigInteger z=s.modPow(r.subtract(BigIntegerMath.TWO),r);
BigInteger pstar=BigIntegerMath.TWO.multiply(z).multiply(s).subtract(BigIntegerMath.ONE);
BigInteger k=BigInteger.valueOf(1);
//The strong prime p is the first prime in the sequence 2rs+p*, 2*2rs+p*, 2*3rs+p*,...
BigInteger p=BigIntegerMath.TWO.multiply(r).multiply(s).add(pstar);
while (p.bitLength()<=minBitLength) {
k=k.multiply(BigIntegerMath.TWO);
p=BigIntegerMath.TWO.multiply(k).multiply(r).multiply(s).add(pstar);
}
while (!p.isProbablePrime(certainty)) {
k=k.add(BigIntegerMath.ONE);
p=BigIntegerMath.TWO.multiply(k).multiply(r).multiply(s).add(pstar);
}
return p;
}

Safe Prime and Generator Selection:
o Safe Prime:
 A safe prime is a prime number of the form 2p+1, where p is also a
prime.
o Generator Selection:
e
e
e
e
 Suppose p is a prime, and p1 1  p2 2  p3 3  ...  pn n is the prime
factorization of p-1. To find a generator g modulo p, do the
following:
1. Choose a random integer x between 2 and p-2.
2. For i = 1 to n do
a. Computer z = p/pi
b. Calculate the 1nr of xz modulo p. If the least
nonnegative residue is 1 modulo p, x is not a
generator. Return to step 1.
3. x is a generator modulo p.
o Codes are as following (located in PrimeGenerator.java)
//This method returns a safe prime of form 2rt+1 where t is a large prime
public BigInteger getSafePrime() {
BigInteger p;
BigInteger r=BigInteger.valueOf(0x7fffffff);
BigInteger t=new BigInteger(minBitLength-30,certainty,sr);
//p is the first prime in the sequence 2rt+1, 2*2rt+1, 2*3rt+1,...
do {
r=r.add(BigIntegerMath.ONE);
p=BigIntegerMath.TWO.multiply(r).multiply(t).add(BigIntegerMath.ONE);
} while (!p.isProbablePrime(certainty));
return p;
}
//This method returns a safe prime of form 2rt+1 where t is a large prime,
//and the factorization of r is known
//It also returns a generator for the safe prime
//The zero-th slot in the resulting array is the safe prime
//Slot 1 of the result is the generator
public BigInteger[] getSafePrimeAndGenerator() {
BigInteger[] p=new BigInteger[2];
BigInteger r=BigInteger.valueOf(0x7fffffff);
BigInteger t=new BigInteger(minBitLength-30,certainty,sr);
//p is the first prime in the sequence 2rt+1, 2*2rt+1, 2*3rt+1,...
do {
r=r.add(BigIntegerMath.ONE);
p[0]=BigIntegerMath.TWO.multiply(r).multiply(t).add(BigIntegerMath.ONE);
} while (!p[0].isProbablePrime(certainty));
//We must get the prime factors of p-1=2rt
//Put the prime factors in a Vector-list each prime factor only once
Vector factors=new Vector();
//Add t to the vector, since t is a prime factor of p-1=2rt
factors.addElement(t);
//We know 2 is a factor of p-1=2rt, so add 2 to the Vector
factors.addElement(BigInteger.valueOf(2));
//r may be prime-don't factor it if you don't have to
if (r.isProbablePrime(10)) factors.addElement(r);
//otherwise, find the factors of r and add them to the Vector
else {
//Divide all the 2's out of r, since 2 is already in the Vector
while (r.mod(BigIntegerMath.TWO).equals(BigIntegerMath.ZERO)) {
r=r.divide(BigIntegerMath.TWO);
}
//We now get the prime factors of r, which should be small enough to factor
//Start with 3 - 2 is already in the Vector
BigInteger divisor=BigInteger.valueOf(3);
//Do not search for factors past the square root of r
//Square the divisor for comparison to r
BigInteger square=divisor.multiply(divisor);
while (square.compareTo(r)<=0) {
//If this divisor divides r, add it to the Vector
if (r.mod(divisor).equals(BigIntegerMath.ZERO)) {
factors.addElement(divisor);
//Divide r by this divisor until it no longer divides
while (r.mod(divisor).equals(BigIntegerMath.ZERO)) r=r.divide(divisor);
}
divisor=divisor.add(BigIntegerMath.ONE);
//Do not search for factors past the square root of r
//Square the divisor for comparison to r
square=divisor.multiply(divisor);
}
}
//Now, start looking for a generator
boolean isGenerator;
BigInteger pMinusOne=p[0].subtract(BigIntegerMath.ONE);
BigInteger x,z,lnr;
do {
//Start by assuming the test # is a generator
isGenerator=true;
//Pick a random integer x smaller than the safe prime p
x=new BigInteger(p[0].bitLength()-1,sr);
for (int i=0;i<factors.size();i++) {
//Compute z as p-1 divided by the i-th prime factor in the Vector
z=pMinusOne.divide((BigInteger)factors.elementAt(i));
//Raise x to the z power modulo p
lnr=x.modPow(z,p[0]);
//If this equals 1, x is not a generator
if (lnr.equals(BigIntegerMath.ONE)) {
isGenerator=false;
//break-no reason to try the other prime factors for this failed x
break;
}
}
//While x is not a generator, go back and pick another random x
} while (!isGenerator);
//If we get here, we found a generator-set it and return it
p[1]=x;
return p;
}
}
Implementations:
 Implement a Java console or Java applet by yourself..
 Output should look like following:
Requirements:
 Must following the structure (platform) provided.
o Adds the necessary codes.
 Must be compilable and executable.
 Must be implemented in Java.
 Must be implemented in Unix/Linux environment.
Grading criteria:
 Following instances will be grade 0:
o Cheating & Plagiarism.
o Late or not hand in.
o Can not be compiled.
o Can not be executed.
 Certain penalty will be given if following instances occur:
o Not follow the platform provided.
o Compilable and executable but
 Certain functionalities are not met the requirements.
 Certain outputs are not correct.
Platform:
PrimeGenerator.java
import java.security.*;
import java.math.*;
import java.util.*;
public class PrimeGenerator {
//To find primes, we first specify the minimum bit length
//The methods will produce primes 1 to 3 bits larger than requested, but never smaller
int minBitLength;
//certainty is the number of primality tests to pass
int certainty;
SecureRandom sr;
public PrimeGenerator(int minBitLength, int certainty, SecureRandom sr) {
//The bit length of the prime will exceed minBitLength
if (minBitLength<512) throw new IllegalArgumentException("Strong/Safe primes must be at least 64 bytes long.");
//Set the values
this.minBitLength=minBitLength;
this.certainty=certainty;
this.sr=sr;
}
//This method finds and returns a strong prime
public BigInteger getStrongPrime() {
//add your codes here
}
//This method returns a safe prime of form 2rt+1 where t is a large prime
public BigInteger getSafePrime() {
//add your codes here
}
//This method returns a safe prime of form 2rt+1 where t is a large prime,
//and the factorization of r is known
//It also returns a generator for the safe prime
//The zero-th slot in the resulting array is the safe prime
//Slot 1 of the result is the generator
public BigInteger[] getSafePrimeAndGenerator() {
//add your codes here
}
}
BigIntegerMath.java
import java.math.*;
import java.security.SecureRandom;
import java.util.*;
public class BigIntegerMath {
//Define some BigInteger constants; this is handy for comparisons
static final BigInteger ZERO=new BigInteger("0");
static final BigInteger ONE=new BigInteger("1");
static final BigInteger TWO=new BigInteger("2");
static final BigInteger THREE=new BigInteger("3");
static final BigInteger FOUR=new BigInteger("4");
//A nonrecursive version of euclid. It returns an array answer of 3 BigIntegers
//answer[0] is the gcd, answer[1] is the coefficient of a, answer[2] the coeff of b
public static BigInteger[] euclid(BigInteger a,BigInteger b) throws IllegalArgumentException {
//Throw an exception if either argument is not positive
if (a.compareTo(ZERO)<=0||b.compareTo(ZERO)<=0) throw new IllegalArgumentException("Euclid requires both arguments to
be positive!");
BigInteger[] answer=new BigInteger[3];
//Set up all the initial table entries
BigInteger r0=new BigInteger(a.toByteArray());
BigInteger r1=new BigInteger(b.toByteArray());
BigInteger s0=new BigInteger("1");
BigInteger s1=new BigInteger("0");
BigInteger t0=new BigInteger("0");
BigInteger t1=new BigInteger("1");
BigInteger q1=r0.divide(r1);
BigInteger r2=r0.mod(r1);
BigInteger s2,t2;
//When r2 becomes zero, the previous table entries are the answers
while (r2.compareTo(ZERO)>0) {
s2=s0.subtract(q1.multiply(s1)); s0=s1; s1=s2;
t2=t0.subtract(q1.multiply(t1)); t0=t1; t1=t2;
r0=r1; r1=r2; q1=r0.divide(r1); r2=r0.mod(r1);
}
answer[0]=r1; answer[1]=s1; answer[2]=t1;
return answer;
}
//Returns a particular solution (if any solutions exist) of linear
//diophantine equations of the form ax+by=c.
//Returns an array z of 3 elements; z[0] is the gcd of a and b,
//z[1] is x, z[2] is y.
public static BigInteger[] solveLinearDiophantine(BigInteger a, BigInteger b, BigInteger c) throws IllegalArgumentException {
if (a.compareTo(ZERO)<=0||b.compareTo(ZERO)<=0||c.compareTo(ZERO)<0)
throw new IllegalArgumentException("All constants must be positive in linear diophantine equation.");
BigInteger[] euclidAnswers=euclid(a,b);
if (c.mod(euclidAnswers[0]).compareTo(ZERO)!=0)
throw new IllegalArgumentException("No solution since "+euclidAnswers[0]+" does not divide "+c+".");
BigInteger[] answer=new BigInteger[3];
BigInteger q=c.divide(euclidAnswers[0]);
answer[0]=euclidAnswers[0];
answer[1]=q.multiply(euclidAnswers[1]);
answer[2]=q.multiply(euclidAnswers[2]);
return answer;
}
//Computes the least nonnegative residue of b mod m, where m>0.
public static BigInteger lnr(BigInteger b, BigInteger m) {
if (m.compareTo(ZERO)<=0) throw new IllegalArgumentException("Modulus must be positive.");
BigInteger answer=b.mod(m);
return (answer.compareTo(ZERO)<0)?answer.add(m):answer;
}
//Returns a solution of x for linear congruences of the form
//ax congruent to b (mod m)
//Returns an array z; z[0] is the gcd of a and m, z[1] is the solution for x
public static BigInteger[] solveLinearCongruence(BigInteger a, BigInteger b, BigInteger m) {
BigInteger[] answers=solveLinearDiophantine(lnr(a,m),m,lnr(b,m));
return answers;
}
//Implements the Rabin-Miller test.
//Number of different bases to try is passed in as an int
//If the BigInteger passes all tests, returns the probabilty it is prime as a double.
//Returns zero if the BigInteger is determined to be composite.
public static double primeProbability(BigInteger n,int numPasses,SecureRandom sr) {
//add your codes here
}
//Finds simultaneous solutions to a linear system of congruences
//involving only one variable and multiple moduli.
public static BigInteger[] solveCRT(BigInteger[] residue, BigInteger[] modulus) {
//Test if the number of moduli and residues match
if (residue.length!=modulus.length)
throw new IllegalArgumentException("Residues and moduli are in different amounts.");
//Test if the moduli are pairwise relatively prime
for (int i=0; i<modulus.length-1; i++) {
for (int j=i+1; j<modulus.length; j++) {
if (!(modulus[i].gcd(modulus[j]).equals(ONE)))
throw new IllegalArgumentException("Moduli are not pairwise relatively prime.");
}
}
//Form the product of the individual moduli
BigInteger M=new BigInteger("1");
for (int i=0; i<modulus.length; i++)
M=M.multiply(modulus[i]);
//Form the solution as in Chinese Remainder Theorem
BigInteger solution=new BigInteger("0");
for (int i=0;i<modulus.length; i++) {
BigInteger Mi=M.divide(modulus[i]);
solution=solution.add(residue[i].multiply(Mi).multiply(Mi.modInverse(modulus[i])));
}
solution=lnr(solution,M);
//Answer must be returned as a two dimensional array.
BigInteger[] result=new BigInteger[2];
result[0]=solution;
result[1]=M;
return result;
}
//Solves quadratic congruences ax^2+bx+c congruent to 0 mod n=pq
//Returns four solutions when they exist
public static BigInteger[] solveQuadratic(BigInteger a, BigInteger b, BigInteger c,
BigInteger p, BigInteger q, int primeTolerance) {
//Check that the factors of the modulus are distinct
if (p.equals(q))
throw new IllegalArgumentException("The modulus factors are not unique!");
//Check that the factors are congruent to 3 modulo 4
BigInteger n=p.multiply(q);
if (!lnr(p.mod(FOUR),n).equals(THREE))
throw new IllegalArgumentException(p+" is not of form 4k+3!");
if (!lnr(q.mod(FOUR),n).equals(THREE))
throw new IllegalArgumentException(q+" is not of form 4k+3!");
//Check that the factors of the modulus are prime
if (!p.isProbablePrime(primeTolerance))
throw new IllegalArgumentException(p+" is not prime!");
if (!q.isProbablePrime(primeTolerance))
throw new IllegalArgumentException(q+" is not prime!");
//Create the array of solutions
BigInteger[] result=new BigInteger[4];
//Start forming the terms
BigInteger aInv=a.modInverse(n);
BigInteger pInv=p.modInverse(q);
BigInteger qInv=q.modInverse(p);
BigInteger twoInv=TWO.modInverse(n);
BigInteger term1=aInv.multiply(twoInv.multiply(b).modPow(TWO,n).multiply(aInv).subtract(c));
BigInteger term2=twoInv.multiply(aInv).multiply(b);
BigInteger t1=lnr(term1.modPow(p.add(ONE).divide(FOUR),n).subtract(term2).multiply(q).multiply(qInv),n);
BigInteger t2=lnr(term1.modPow(q.add(ONE).divide(FOUR),n).subtract(term2).multiply(p).multiply(pInv),n);
BigInteger t3=lnr(term1.modPow(p.add(ONE).divide(FOUR),n).negate().subtract(term2).multiply(q).multiply(qInv),n);
BigInteger t4=lnr(term1.modPow(q.add(ONE).divide(FOUR),n).negate().subtract(term2).multiply(p).multiply(pInv),n);
//Form the solutions
result[0]=lnr(t1.add(t2),n);
result[1]=lnr(t1.add(t4),n);
result[2]=lnr(t3.add(t2),n);
result[3]=lnr(t3.add(t4),n);
//Check the solutions; if any are bad, throw an exception
BigInteger x;
for (int i=0;i<4;i++) {
x=result[i];
if (!lnr(a.multiply(x.multiply(x)).add(b.multiply(x)).add(c),n).equals(ZERO))
throw new IllegalArgumentException("Solution x="+x+" does not check!");
}
return result;
}
//Monte Carlo factorization method returns a Monte Carlo factor.
//An array holds a sequence of random numbers; must specify max
//size of this array.
//This puppy returns null if no factor is found.
public static BigInteger monteCarloFactor(BigInteger n,int maxArraySize) throws IllegalArgumentException {
if (n.compareTo(THREE)<=0) throw new IllegalArgumentException("Number to factor must be > 3");
BigInteger[] m=new BigInteger[maxArraySize+1];
m[0]=BigInteger.valueOf(new Random().nextInt());
BigInteger g;
for (int i=1;i<=maxArraySize;i++) {
m[i]=m[i-1].multiply(m[i-1]).add(ONE).mod(n);
if (i%2==0) {
g=m[i].subtract(m[i/2]).gcd(n);
if (g.compareTo(ONE)>0&&g.compareTo(n)<0) return g;
}
}
return null;
}
//Pollard p-1 factorization-runs until a factor is found
public static BigInteger pMinusOneFactor(BigInteger n) throws IllegalArgumentException {
Random rand=new Random();
BigInteger power=BigInteger.valueOf(1);
BigInteger residue=lnr(BigInteger.valueOf(rand.nextInt()),n);
BigInteger test=residue.subtract(ONE);
BigInteger gcd=test.gcd(n);
while (true) {
while (gcd.equals(ONE)) {
power=power.add(ONE);
residue=residue.modPow(power,n);
test=residue.subtract(ONE);
gcd=test.gcd(n);
}
if (gcd.equals(n)) {
power=BigInteger.valueOf(1);
residue=lnr(BigInteger.valueOf(rand.nextInt()),n);
test=residue.subtract(ONE);
gcd=test.gcd(n);
} else return gcd;
}
}
public static BigInteger sqrt(BigInteger m) {
//Uses the Newton method to find largest integer whose square does not exceed m
//We search for a zero of f(x)=x^2-p ==> note that derivative f'(x)=2x
int diff=m.compareTo(ZERO);
//Throw an exception for negative arguments
if (diff<0) throw new IllegalArgumentException("Cannot compute square root of a negative integer!");
//Return 0 in case m is 0
if (diff==0) return BigInteger.valueOf(0);
BigDecimal two=new BigDecimal(TWO);
//Convert the parameter to a BigDecimal
BigDecimal n=new BigDecimal(m);
//Begin with an initial guess-the square root will be half the size of m
//Make a byte array at least that long, & set bit in the high order byte
byte[] barray=new byte[m.bitLength()/16+1];
barray[0]=(byte)255;
//This is the first guess-it will be too high
BigDecimal r=new BigDecimal(new BigInteger(1,barray));
//Next approximation is computed by taking r-f(r)/f'(r)
r=r.subtract(r.multiply(r).subtract(n).divide(r.multiply(two),BigDecimal.ROUND_UP));
//As long as our new approximation squared exceeds m, we continue to approximate
while (r.multiply(r).compareTo(n)>0) {
r=r.subtract(r.multiply(r).subtract(n).divide(r.multiply(two),BigDecimal.ROUND_UP));
}
return r.toBigInteger();
}
public static BigInteger logExhaustiveSearch(BigInteger base, BigInteger residue, BigInteger modulus) {
//This algorithm solves base^x = residue (mod modulus) for x using exhaustive search
BigInteger basePow=BigInteger.valueOf(1);
BigInteger j;
for (j=BigInteger.valueOf(1);j.compareTo(modulus)<0;j=j.add(ONE)) {
basePow=basePow.multiply(base).mod(modulus);
if (basePow.equals(residue)) break;
}
if (j.equals(modulus)) throw new NoSuchElementException("No solution");
return j;
}
public static BigInteger logBabyStepGiantStep(BigInteger base, BigInteger residue, BigInteger modulus) {
//This algorithm solves base^x = residue (mod modulus) for x using baby step giant step
BigInteger m=sqrt(modulus).add(ONE);
//Use a hash table to store the entries-use Java Hashtable class
Hashtable h=new Hashtable();
BigInteger basePow=BigInteger.valueOf(1);
//Build the hash table base^j is the key, index j is the value
for (BigInteger j=BigInteger.valueOf(0);j.compareTo(m)<0;j=j.add(ONE)) {
h.put(basePow,j);
basePow=basePow.multiply(base).mod(modulus);
}
//Compute an inverse of base^m modulo p
BigInteger basetotheminv=base.modPow(m,modulus).modInverse(modulus);
BigInteger y=new BigInteger(residue.toByteArray());
//Search the hashtable for a base^j such that y=base^j for some j
BigInteger target;
for (BigInteger i=BigInteger.valueOf(0);i.compareTo(m)<0;i=i.add(ONE)) {
target = (BigInteger)h.get(y);
if (target!=null) return i.multiply(m).add(target);
y=y.multiply(basetotheminv).mod(modulus);
}
throw new NoSuchElementException("No solution");
}
}