package org.bouncycastle.pqc.math.linearalgebra; import java.security.SecureRandom; /** * This class describes operations with elements from the finite field F = * GF(2^m). ( GF(2^m)= GF(2)[A] where A is a root of irreducible polynomial with * degree m, each field element B has a polynomial basis representation, i.e. it * is represented by a different binary polynomial of degree less than m, B = * poly(A) ) All operations are defined only for field with 1< m <32. For the * representation of field elements the map f: F->Z, poly(A)->poly(2) is used, * where integers have the binary representation. For example: A^7+A^3+A+1 -> * (00...0010001011)=139 Also for elements type Integer is used. * * @see PolynomialRingGF2 */ public class GF2mField { /* * degree - degree of the field polynomial - the field polynomial ring - * polynomial ring over the finite field GF(2) */ private int degree = 0; private int polynomial; /** * create a finite field GF(2^m) * * @param degree the degree of the field */ public GF2mField(int degree) { if (degree >= 32) { throw new IllegalArgumentException( " Error: the degree of field is too large "); } if (degree < 1) { throw new IllegalArgumentException( " Error: the degree of field is non-positive "); } this.degree = degree; polynomial = PolynomialRingGF2.getIrreduciblePolynomial(degree); } /** * create a finite field GF(2^m) with the fixed field polynomial * * @param degree the degree of the field * @param poly the field polynomial */ public GF2mField(int degree, int poly) { if (degree != PolynomialRingGF2.degree(poly)) { throw new IllegalArgumentException( " Error: the degree is not correct"); } if (!PolynomialRingGF2.isIrreducible(poly)) { throw new IllegalArgumentException( " Error: given polynomial is reducible"); } this.degree = degree; polynomial = poly; } public GF2mField(byte[] enc) { if (enc.length != 4) { throw new IllegalArgumentException( "byte array is not an encoded finite field"); } polynomial = LittleEndianConversions.OS2IP(enc); if (!PolynomialRingGF2.isIrreducible(polynomial)) { throw new IllegalArgumentException( "byte array is not an encoded finite field"); } degree = PolynomialRingGF2.degree(polynomial); } public GF2mField(GF2mField field) { degree = field.degree; polynomial = field.polynomial; } /** * return degree of the field * * @return degree of the field */ public int getDegree() { return degree; } /** * return the field polynomial * * @return the field polynomial */ public int getPolynomial() { return polynomial; } /** * return the encoded form of this field * * @return the field in byte array form */ public byte[] getEncoded() { return LittleEndianConversions.I2OSP(polynomial); } /** * Return sum of two elements * * @param a * @param b * @return a+b */ public int add(int a, int b) { return a ^ b; } /** * Return product of two elements * * @param a * @param b * @return a*b */ public int mult(int a, int b) { return PolynomialRingGF2.modMultiply(a, b, polynomial); } /** * compute exponentiation a^k * * @param a a field element a * @param k k degree * @return a^k */ public int exp(int a, int k) { if (a == 0) { return 0; } if (a == 1) { return 1; } int result = 1; if (k < 0) { a = inverse(a); k = -k; } while (k != 0) { if ((k & 1) == 1) { result = mult(result, a); } a = mult(a, a); k >>>= 1; } return result; } /** * compute the multiplicative inverse of a * * @param a a field element a * @return a<sup>-1</sup> */ public int inverse(int a) { int d = (1 << degree) - 2; return exp(a, d); } /** * compute the square root of an integer * * @param a a field element a * @return a<sup>1/2</sup> */ public int sqRoot(int a) { for (int i = 1; i < degree; i++) { a = mult(a, a); } return a; } /** * create a random field element using PRNG sr * * @param sr SecureRandom * @return a random element */ public int getRandomElement(SecureRandom sr) { int result = RandUtils.nextInt(sr, 1 << degree); return result; } /** * create a random non-zero field element * * @return a random element */ public int getRandomNonZeroElement() { return getRandomNonZeroElement(new SecureRandom()); } /** * create a random non-zero field element using PRNG sr * * @param sr SecureRandom * @return a random non-zero element */ public int getRandomNonZeroElement(SecureRandom sr) { int controltime = 1 << 20; int count = 0; int result = RandUtils.nextInt(sr, 1 << degree); while ((result == 0) && (count < controltime)) { result = RandUtils.nextInt(sr, 1 << degree); count++; } if (count == controltime) { result = 1; } return result; } /** * @return true if e is encoded element of this field and false otherwise */ public boolean isElementOfThisField(int e) { // e is encoded element of this field iff 0<= e < |2^m| if (degree == 31) { return e >= 0; } return e >= 0 && e < (1 << degree); } /* * help method for visual control */ public String elementToStr(int a) { String s = ""; for (int i = 0; i < degree; i++) { if (((byte)a & 0x01) == 0) { s = "0" + s; } else { s = "1" + s; } a >>>= 1; } return s; } /** * checks if given object is equal to this field. * <p/> * The method returns false whenever the given object is not GF2m. * * @param other object * @return true or false */ public boolean equals(Object other) { if ((other == null) || !(other instanceof GF2mField)) { return false; } GF2mField otherField = (GF2mField)other; if ((degree == otherField.degree) && (polynomial == otherField.polynomial)) { return true; } return false; } public int hashCode() { return polynomial; } /** * Returns a human readable form of this field. * <p/> * * @return a human readable form of this field. */ public String toString() { String str = "Finite Field GF(2^" + degree + ") = " + "GF(2)[X]/<" + polyToString(polynomial) + "> "; return str; } private static String polyToString(int p) { String str = ""; if (p == 0) { str = "0"; } else { byte b = (byte)(p & 0x01); if (b == 1) { str = "1"; } p >>>= 1; int i = 1; while (p != 0) { b = (byte)(p & 0x01); if (b == 1) { str = str + "+x^" + i; } p >>>= 1; i++; } } return str; } }