package org.bouncycastle.pqc.math.linearalgebra; import java.util.Random; 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 */ public GF2nPolynomialField(int deg) { 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 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, boolean file) { 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 polynomial the field polynomial to use * @throws PolynomialIsNotIrreducibleException if the given polynomial is not irreducible in GF(2^<i>i</i>) */ public GF2nPolynomialField(int deg, GF2Polynomial polynomial) throws RuntimeException { 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 * @throws GFException if the field polynomial is not a 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 * @throws GFException if the field polynomial is not a 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, new 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; } }