package org.bouncycastle.pqc.math.linearalgebra; import java.util.Vector; /** * This abstract class defines the finite field <i>GF(2<sup>n</sup>)</i>. It * holds the extension degree <i>n</i>, the characteristic, the irreducible * fieldpolynomial and conversion matrices. GF2nField is implemented by the * classes GF2nPolynomialField and GF2nONBField. * * @see GF2nONBField * @see GF2nPolynomialField */ public abstract class GF2nField { /** * the degree of this field */ protected int mDegree; /** * the irreducible fieldPolynomial stored in normal order (also for ONB) */ protected GF2Polynomial fieldPolynomial; /** * holds a list of GF2nFields to which elements have been converted and thus * a COB-Matrix exists */ protected Vector fields; /** * the COB matrices */ protected Vector matrices; /** * Returns the degree <i>n</i> of this field. * * @return the degree <i>n</i> of this field */ public final int getDegree() { return mDegree; } /** * Returns the fieldpolynomial as a new Bitstring. * * @return a copy of the fieldpolynomial as a new Bitstring */ public final GF2Polynomial getFieldPolynomial() { if (fieldPolynomial == null) { computeFieldPolynomial(); } return new GF2Polynomial(fieldPolynomial); } /** * Decides whether the given object <tt>other</tt> is the same as this * field. * * @param other another object * @return (this == other) */ public final boolean equals(Object other) { if (other == null || !(other instanceof GF2nField)) { return false; } GF2nField otherField = (GF2nField)other; if (otherField.mDegree != mDegree) { return false; } if (!fieldPolynomial.equals(otherField.fieldPolynomial)) { return false; } if ((this instanceof GF2nPolynomialField) && !(otherField instanceof GF2nPolynomialField)) { return false; } if ((this instanceof GF2nONBField) && !(otherField instanceof GF2nONBField)) { return false; } return true; } /** * @return the hash code of this field */ public int hashCode() { return mDegree + fieldPolynomial.hashCode(); } /** * Computes a random root from the given irreducible fieldpolynomial * according to IEEE 1363 algorithm A.5.6. This cal take very long for big * degrees. * * @param B0FieldPolynomial the fieldpolynomial if the other basis as a Bitstring * @return a random root of BOFieldPolynomial in representation according to * this field * @see "P1363 A.5.6, p103f" */ protected abstract GF2nElement getRandomRoot(GF2Polynomial B0FieldPolynomial); /** * 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 abstract void computeCOBMatrix(GF2nField B1); /** * Computes the fieldpolynomial. This can take a long time for big degrees. */ protected abstract void computeFieldPolynomial(); /** * Inverts the given matrix represented as bitstrings. * * @param matrix the matrix to invert as a Bitstring[] * @return matrix^(-1) */ protected final GF2Polynomial[] invertMatrix(GF2Polynomial[] matrix) { GF2Polynomial[] a = new GF2Polynomial[matrix.length]; GF2Polynomial[] inv = new GF2Polynomial[matrix.length]; GF2Polynomial dummy; int i, j; // initialize a as a copy of matrix and inv as E(inheitsmatrix) for (i = 0; i < mDegree; i++) { try { a[i] = new GF2Polynomial(matrix[i]); inv[i] = new GF2Polynomial(mDegree); inv[i].setBit(mDegree - 1 - i); } catch (RuntimeException BDNEExc) { BDNEExc.printStackTrace(); } } // construct triangle matrix so that for each a[i] the first i bits are // zero for (i = 0; i < mDegree - 1; i++) { // find column where bit i is set j = i; while ((j < mDegree) && !a[j].testBit(mDegree - 1 - i)) { j++; } if (j >= mDegree) { throw new RuntimeException( "GF2nField.invertMatrix: Matrix cannot be inverted!"); } if (i != j) { // swap a[i]/a[j] and inv[i]/inv[j] dummy = a[i]; a[i] = a[j]; a[j] = dummy; dummy = inv[i]; inv[i] = inv[j]; inv[j] = dummy; } for (j = i + 1; j < mDegree; j++) { // add column i to all columns>i // having their i-th bit set if (a[j].testBit(mDegree - 1 - i)) { a[j].addToThis(a[i]); inv[j].addToThis(inv[i]); } } } // construct Einheitsmatrix from a for (i = mDegree - 1; i > 0; i--) { for (j = i - 1; j >= 0; j--) { // eliminate the i-th bit in all // columns < i if (a[j].testBit(mDegree - 1 - i)) { a[j].addToThis(a[i]); inv[j].addToThis(inv[i]); } } } return inv; } /** * Converts the given element in representation according to this field to a * new element in representation according to B1 using the change-of-basis * matrix calculated by computeCOBMatrix. * * @param elem the GF2nElement to convert * @param basis the basis to convert <tt>elem</tt> to * @return <tt>elem</tt> converted to a new element representation * according to <tt>basis</tt> * @throws DifferentFieldsException if <tt>elem</tt> cannot be converted according to * <tt>basis</tt>. * @see GF2nField#computeCOBMatrix * @see GF2nField#getRandomRoot * @see GF2nPolynomial * @see "P1363 A.7 p109ff" */ public final GF2nElement convert(GF2nElement elem, GF2nField basis) throws RuntimeException { if (basis == this) { return (GF2nElement)elem.clone(); } if (fieldPolynomial.equals(basis.fieldPolynomial)) { return (GF2nElement)elem.clone(); } if (mDegree != basis.mDegree) { throw new RuntimeException("GF2nField.convert: B1 has a" + " different degree and thus cannot be coverted to!"); } int i; GF2Polynomial[] COBMatrix; i = fields.indexOf(basis); if (i == -1) { computeCOBMatrix(basis); i = fields.indexOf(basis); } COBMatrix = (GF2Polynomial[])matrices.elementAt(i); GF2nElement elemCopy = (GF2nElement)elem.clone(); if (elemCopy instanceof GF2nONBElement) { // remember: ONB treats its bits in reverse order ((GF2nONBElement)elemCopy).reverseOrder(); } GF2Polynomial bs = new GF2Polynomial(mDegree, elemCopy.toFlexiBigInt()); bs.expandN(mDegree); GF2Polynomial result = new GF2Polynomial(mDegree); for (i = 0; i < mDegree; i++) { if (bs.vectorMult(COBMatrix[i])) { result.setBit(mDegree - 1 - i); } } if (basis instanceof GF2nPolynomialField) { return new GF2nPolynomialElement((GF2nPolynomialField)basis, result); } else if (basis instanceof GF2nONBField) { GF2nONBElement res = new GF2nONBElement((GF2nONBField)basis, result.toFlexiBigInt()); // TODO Remember: ONB treats its Bits in reverse order !!! res.reverseOrder(); return res; } else { throw new RuntimeException( "GF2nField.convert: B1 must be an instance of " + "GF2nPolynomialField or GF2nONBField!"); } } }