blob: 6ed15f6e3c3cded0a3c3f077b08456df8f0d69bb [file] [log] [blame]
package org.bouncycastle.pqc.math.linearalgebra;
import java.security.SecureRandom;
import java.util.Vector;
/**
* This class implements the abstract class <tt>GF2nField</tt> for polynomial
* representation. It computes the field polynomial and the squaring matrix.
* GF2nField is used by GF2nPolynomialElement which implements the elements of
* this field.
*
* @see GF2nField
* @see GF2nPolynomialElement
*/
public class GF2nPolynomialField
extends GF2nField
{
/**
* Matrix used for fast squaring
*/
GF2Polynomial[] squaringMatrix;
// field polynomial is a trinomial
private boolean isTrinomial = false;
// field polynomial is a pentanomial
private boolean isPentanomial = false;
// middle coefficient of the field polynomial in case it is a trinomial
private int tc;
// middle 3 coefficients of the field polynomial in case it is a pentanomial
private int[] pc = new int[3];
/**
* constructs an instance of the finite field with 2<sup>deg</sup>
* elements and characteristic 2.
*
* @param deg the extention degree of this field
* @param random source of randomness for generating new polynomials.
*/
public GF2nPolynomialField(int deg, SecureRandom random)
{
super(random);
if (deg < 3)
{
throw new IllegalArgumentException("k must be at least 3");
}
mDegree = deg;
computeFieldPolynomial();
computeSquaringMatrix();
fields = new Vector();
matrices = new Vector();
}
/**
* constructs an instance of the finite field with 2<sup>deg</sup>
* elements and characteristic 2.
*
* @param deg the degree of this field
* @param random source of randomness for generating new polynomials.
* @param file true if you want to read the field polynomial from the
* file false if you want to use a random fielpolynomial
* (this can take very long for huge degrees)
*/
public GF2nPolynomialField(int deg, SecureRandom random, boolean file)
{
super(random);
if (deg < 3)
{
throw new IllegalArgumentException("k must be at least 3");
}
mDegree = deg;
if (file)
{
computeFieldPolynomial();
}
else
{
computeFieldPolynomial2();
}
computeSquaringMatrix();
fields = new Vector();
matrices = new Vector();
}
/**
* Creates a new GF2nField of degree <i>i</i> and uses the given
* <i>polynomial</i> as field polynomial. The <i>polynomial</i> is checked
* whether it is irreducible. This can take some time if <i>i</i> is huge!
*
* @param deg degree of the GF2nField
* @param random source of randomness for generating new polynomials.
* @param polynomial the field polynomial to use
*/
public GF2nPolynomialField(int deg, SecureRandom random, GF2Polynomial polynomial)
throws RuntimeException
{
super(random);
if (deg < 3)
{
throw new IllegalArgumentException("degree must be at least 3");
}
if (polynomial.getLength() != deg + 1)
{
throw new RuntimeException();
}
if (!polynomial.isIrreducible())
{
throw new RuntimeException();
}
mDegree = deg;
// fieldPolynomial = new Bitstring(polynomial);
fieldPolynomial = polynomial;
computeSquaringMatrix();
int k = 2; // check if the polynomial is a trinomial or pentanomial
for (int j = 1; j < fieldPolynomial.getLength() - 1; j++)
{
if (fieldPolynomial.testBit(j))
{
k++;
if (k == 3)
{
tc = j;
}
if (k <= 5)
{
pc[k - 3] = j;
}
}
}
if (k == 3)
{
isTrinomial = true;
}
if (k == 5)
{
isPentanomial = true;
}
fields = new Vector();
matrices = new Vector();
}
/**
* Returns true if the field polynomial is a trinomial. The coefficient can
* be retrieved using getTc().
*
* @return true if the field polynomial is a trinomial
*/
public boolean isTrinomial()
{
return isTrinomial;
}
/**
* Returns true if the field polynomial is a pentanomial. The coefficients
* can be retrieved using getPc().
*
* @return true if the field polynomial is a pentanomial
*/
public boolean isPentanomial()
{
return isPentanomial;
}
/**
* Returns the degree of the middle coefficient of the used field trinomial
* (x^n + x^(getTc()) + 1).
*
* @return the middle coefficient of the used field trinomial
*/
public int getTc()
throws RuntimeException
{
if (!isTrinomial)
{
throw new RuntimeException();
}
return tc;
}
/**
* Returns the degree of the middle coefficients of the used field
* pentanomial (x^n + x^(getPc()[2]) + x^(getPc()[1]) + x^(getPc()[0]) + 1).
*
* @return the middle coefficients of the used field pentanomial
*/
public int[] getPc()
throws RuntimeException
{
if (!isPentanomial)
{
throw new RuntimeException();
}
int[] result = new int[3];
System.arraycopy(pc, 0, result, 0, 3);
return result;
}
/**
* Return row vector i of the squaring matrix.
*
* @param i the index of the row vector to return
* @return a copy of squaringMatrix[i]
* @see GF2nPolynomialElement#squareMatrix
*/
public GF2Polynomial getSquaringVector(int i)
{
return new GF2Polynomial(squaringMatrix[i]);
}
/**
* Compute a random root of the given GF2Polynomial.
*
* @param polynomial the polynomial
* @return a random root of <tt>polynomial</tt>
*/
protected GF2nElement getRandomRoot(GF2Polynomial polynomial)
{
// We are in B1!!!
GF2nPolynomial c;
GF2nPolynomial ut;
GF2nElement u;
GF2nPolynomial h;
int hDegree;
// 1. Set g(t) <- f(t)
GF2nPolynomial g = new GF2nPolynomial(polynomial, this);
int gDegree = g.getDegree();
int i;
// 2. while deg(g) > 1
while (gDegree > 1)
{
do
{
// 2.1 choose random u (element of) GF(2^m)
u = new GF2nPolynomialElement(this, random);
ut = new GF2nPolynomial(2, GF2nPolynomialElement.ZERO(this));
// 2.2 Set c(t) <- ut
ut.set(1, u);
c = new GF2nPolynomial(ut);
// 2.3 For i from 1 to m-1 do
for (i = 1; i <= mDegree - 1; i++)
{
// 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t)
c = c.multiplyAndReduce(c, g);
c = c.add(ut);
}
// 2.4 set h(t) <- GCD(c(t), g(t))
h = c.gcd(g);
// 2.5 if h(t) is constant or deg(g) = deg(h) then go to
// step 2.1
hDegree = h.getDegree();
gDegree = g.getDegree();
}
while ((hDegree == 0) || (hDegree == gDegree));
// 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ...
if ((hDegree << 1) > gDegree)
{
g = g.quotient(h);
}
else
{
// ... else g(t) <- h(t)
g = new GF2nPolynomial(h);
}
gDegree = g.getDegree();
}
// 3. Output g(0)
return g.at(0);
}
/**
* Computes the change-of-basis matrix for basis conversion according to
* 1363. The result is stored in the lists fields and matrices.
*
* @param B1 the GF2nField to convert to
* @see "P1363 A.7.3, p111ff"
*/
protected void computeCOBMatrix(GF2nField B1)
{
// we are in B0 here!
if (mDegree != B1.mDegree)
{
throw new IllegalArgumentException(
"GF2nPolynomialField.computeCOBMatrix: B1 has a different "
+ "degree and thus cannot be coverted to!");
}
if (B1 instanceof GF2nONBField)
{
// speedup (calculation is done in PolynomialElements instead of
// ONB)
B1.computeCOBMatrix(this);
return;
}
int i, j;
GF2nElement[] gamma;
GF2nElement u;
GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree];
for (i = 0; i < mDegree; i++)
{
COBMatrix[i] = new GF2Polynomial(mDegree);
}
// find Random Root
do
{
// u is in representation according to B1
u = B1.getRandomRoot(fieldPolynomial);
}
while (u.isZero());
// build gamma matrix by multiplying by u
if (u instanceof GF2nONBElement)
{
gamma = new GF2nONBElement[mDegree];
gamma[mDegree - 1] = GF2nONBElement.ONE((GF2nONBField)B1);
}
else
{
gamma = new GF2nPolynomialElement[mDegree];
gamma[mDegree - 1] = GF2nPolynomialElement
.ONE((GF2nPolynomialField)B1);
}
gamma[mDegree - 2] = u;
for (i = mDegree - 3; i >= 0; i--)
{
gamma[i] = (GF2nElement)gamma[i + 1].multiply(u);
}
if (B1 instanceof GF2nONBField)
{
// convert horizontal gamma matrix by vertical Bitstrings
for (i = 0; i < mDegree; i++)
{
for (j = 0; j < mDegree; j++)
{
// TODO remember: ONB treats its Bits in reverse order !!!
if (gamma[i].testBit(mDegree - j - 1))
{
COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
}
}
}
}
else
{
// convert horizontal gamma matrix by vertical Bitstrings
for (i = 0; i < mDegree; i++)
{
for (j = 0; j < mDegree; j++)
{
if (gamma[i].testBit(j))
{
COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1);
}
}
}
}
// store field and matrix for further use
fields.addElement(B1);
matrices.addElement(COBMatrix);
// store field and inverse matrix for further use in B1
B1.fields.addElement(this);
B1.matrices.addElement(invertMatrix(COBMatrix));
}
/**
* Computes a new squaring matrix used for fast squaring.
*
* @see GF2nPolynomialElement#square
*/
private void computeSquaringMatrix()
{
GF2Polynomial[] d = new GF2Polynomial[mDegree - 1];
int i, j;
squaringMatrix = new GF2Polynomial[mDegree];
for (i = 0; i < squaringMatrix.length; i++)
{
squaringMatrix[i] = new GF2Polynomial(mDegree, "ZERO");
}
for (i = 0; i < mDegree - 1; i++)
{
d[i] = new GF2Polynomial(1, "ONE").shiftLeft(mDegree + i)
.remainder(fieldPolynomial);
}
for (i = 1; i <= Math.abs(mDegree >> 1); i++)
{
for (j = 1; j <= mDegree; j++)
{
if (d[mDegree - (i << 1)].testBit(mDegree - j))
{
squaringMatrix[j - 1].setBit(mDegree - i);
}
}
}
for (i = Math.abs(mDegree >> 1) + 1; i <= mDegree; i++)
{
squaringMatrix[(i << 1) - mDegree - 1].setBit(mDegree - i);
}
}
/**
* Computes the field polynomial. This can take a long time for big degrees.
*/
protected void computeFieldPolynomial()
{
if (testTrinomials())
{
return;
}
if (testPentanomials())
{
return;
}
testRandom();
}
/**
* Computes the field polynomial. This can take a long time for big degrees.
*/
protected void computeFieldPolynomial2()
{
if (testTrinomials())
{
return;
}
if (testPentanomials())
{
return;
}
testRandom();
}
/**
* Tests all trinomials of degree (n+1) until a irreducible is found and
* stores the result in <i>field polynomial</i>. Returns false if no
* irreducible trinomial exists in GF(2^n). This can take very long for huge
* degrees.
*
* @return true if an irreducible trinomial is found
*/
private boolean testTrinomials()
{
int i, l;
boolean done = false;
l = 0;
fieldPolynomial = new GF2Polynomial(mDegree + 1);
fieldPolynomial.setBit(0);
fieldPolynomial.setBit(mDegree);
for (i = 1; (i < mDegree) && !done; i++)
{
fieldPolynomial.setBit(i);
done = fieldPolynomial.isIrreducible();
l++;
if (done)
{
isTrinomial = true;
tc = i;
return done;
}
fieldPolynomial.resetBit(i);
done = fieldPolynomial.isIrreducible();
}
return done;
}
/**
* Tests all pentanomials of degree (n+1) until a irreducible is found and
* stores the result in <i>field polynomial</i>. Returns false if no
* irreducible pentanomial exists in GF(2^n). This can take very long for
* huge degrees.
*
* @return true if an irreducible pentanomial is found
*/
private boolean testPentanomials()
{
int i, j, k, l;
boolean done = false;
l = 0;
fieldPolynomial = new GF2Polynomial(mDegree + 1);
fieldPolynomial.setBit(0);
fieldPolynomial.setBit(mDegree);
for (i = 1; (i <= (mDegree - 3)) && !done; i++)
{
fieldPolynomial.setBit(i);
for (j = i + 1; (j <= (mDegree - 2)) && !done; j++)
{
fieldPolynomial.setBit(j);
for (k = j + 1; (k <= (mDegree - 1)) && !done; k++)
{
fieldPolynomial.setBit(k);
if (((mDegree & 1) != 0) | ((i & 1) != 0) | ((j & 1) != 0)
| ((k & 1) != 0))
{
done = fieldPolynomial.isIrreducible();
l++;
if (done)
{
isPentanomial = true;
pc[0] = i;
pc[1] = j;
pc[2] = k;
return done;
}
}
fieldPolynomial.resetBit(k);
}
fieldPolynomial.resetBit(j);
}
fieldPolynomial.resetBit(i);
}
return done;
}
/**
* Tests random polynomials of degree (n+1) until an irreducible is found
* and stores the result in <i>field polynomial</i>. This can take very
* long for huge degrees.
*
* @return true
*/
private boolean testRandom()
{
int l;
boolean done = false;
fieldPolynomial = new GF2Polynomial(mDegree + 1);
l = 0;
while (!done)
{
l++;
fieldPolynomial.randomize();
fieldPolynomial.setBit(mDegree);
fieldPolynomial.setBit(0);
if (fieldPolynomial.isIrreducible())
{
done = true;
return done;
}
}
return done;
}
}