org.bouncycastle.pqc.math.linearalgebra.GF2nPolynomialElement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gwt-crypto Show documentation
Show all versions of gwt-crypto Show documentation
A GWT cryptography library ported from Legion of the Bouncy Castle.
The newest version!
package org.bouncycastle.pqc.math.linearalgebra;
import java.math.BigInteger;
import java.util.Random;
/**
* This class implements elements of finite binary fields GF(2n)
* using polynomial representation. For more information on the arithmetic see
* for example IEEE Standard 1363 or Certicom online-tutorial.
*
* @see "GF2nField"
* @see GF2nPolynomialField
* @see GF2nONBElement
* @see GF2Polynomial
*/
public class GF2nPolynomialElement
extends GF2nElement
{
// pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a
private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004,
0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080,
0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000,
0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000,
0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000,
0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000};
// the used GF2Polynomial which stores the coefficients
private GF2Polynomial polynomial;
/**
* Create a new random GF2nPolynomialElement using the given field and
* source of randomness.
*
* @param f the GF2nField to use
* @param rand the source of randomness
*/
public GF2nPolynomialElement(GF2nPolynomialField f, Random rand)
{
mField = f;
mDegree = mField.getDegree();
polynomial = new GF2Polynomial(mDegree);
randomize(rand);
}
/**
* Creates a new GF2nPolynomialElement using the given field and Bitstring.
*
* @param f the GF2nPolynomialField to use
* @param bs the desired value as Bitstring
*/
public GF2nPolynomialElement(GF2nPolynomialField f, GF2Polynomial bs)
{
mField = f;
mDegree = mField.getDegree();
polynomial = new GF2Polynomial(bs);
polynomial.expandN(mDegree);
}
/**
* Creates a new GF2nPolynomialElement using the given field f and
* byte[] os as value. The conversion is done according to 1363.
*
* @param f the GF2nField to use
* @param os the octet string to assign to this GF2nPolynomialElement
* @see "P1363 5.5.5 p23, OS2FEP/OS2BSP"
*/
public GF2nPolynomialElement(GF2nPolynomialField f, byte[] os)
{
mField = f;
mDegree = mField.getDegree();
polynomial = new GF2Polynomial(mDegree, os);
polynomial.expandN(mDegree);
}
/**
* Creates a new GF2nPolynomialElement using the given field f and
* int[] is as value.
*
* @param f the GF2nField to use
* @param is the integer string to assign to this GF2nPolynomialElement
*/
public GF2nPolynomialElement(GF2nPolynomialField f, int[] is)
{
mField = f;
mDegree = mField.getDegree();
polynomial = new GF2Polynomial(mDegree, is);
polynomial.expandN(f.mDegree);
}
/**
* Creates a new GF2nPolynomialElement by cloning the given
* GF2nPolynomialElement b.
*
* @param other the GF2nPolynomialElement to clone
*/
public GF2nPolynomialElement(GF2nPolynomialElement other)
{
mField = other.mField;
mDegree = other.mDegree;
polynomial = new GF2Polynomial(other.polynomial);
}
// /////////////////////////////////////////////////////////////////////
// pseudo-constructors
// /////////////////////////////////////////////////////////////////////
/**
* Creates a new GF2nPolynomialElement by cloning this
* GF2nPolynomialElement.
*
* @return a copy of this element
*/
public Object clone()
{
return new GF2nPolynomialElement(this);
}
// /////////////////////////////////////////////////////////////////////
// assignments
// /////////////////////////////////////////////////////////////////////
/**
* Assigns the value 'zero' to this Polynomial.
*/
void assignZero()
{
polynomial.assignZero();
}
/**
* Create the zero element.
*
* @param f the finite field
* @return the zero element in the given finite field
*/
public static GF2nPolynomialElement ZERO(GF2nPolynomialField f)
{
GF2Polynomial polynomial = new GF2Polynomial(f.getDegree());
return new GF2nPolynomialElement(f, polynomial);
}
/**
* Create the one element.
*
* @param f the finite field
* @return the one element in the given finite field
*/
public static GF2nPolynomialElement ONE(GF2nPolynomialField f)
{
GF2Polynomial polynomial = new GF2Polynomial(f.getDegree(),
new int[]{1});
return new GF2nPolynomialElement(f, polynomial);
}
/**
* Assigns the value 'one' to this Polynomial.
*/
void assignOne()
{
polynomial.assignOne();
}
/**
* Assign a random value to this GF2nPolynomialElement using the specified
* source of randomness.
*
* @param rand the source of randomness
*/
private void randomize(Random rand)
{
polynomial.expandN(mDegree);
polynomial.randomize(rand);
}
// /////////////////////////////////////////////////////////////////////
// comparison
// /////////////////////////////////////////////////////////////////////
/**
* Checks whether this element is zero.
*
* @return true if this is the zero element
*/
public boolean isZero()
{
return polynomial.isZero();
}
/**
* Tests if the GF2nPolynomialElement has 'one' as value.
*
* @return true if this equals one (this == 1)
*/
public boolean isOne()
{
return polynomial.isOne();
}
/**
* Compare this element with another object.
*
* @param other the other object
* @return true if the two objects are equal, false
* otherwise
*/
public boolean equals(Object other)
{
if (other == null || !(other instanceof GF2nPolynomialElement))
{
return false;
}
GF2nPolynomialElement otherElem = (GF2nPolynomialElement)other;
if (mField != otherElem.mField)
{
if (!mField.getFieldPolynomial().equals(
otherElem.mField.getFieldPolynomial()))
{
return false;
}
}
return polynomial.equals(otherElem.polynomial);
}
/**
* @return the hash code of this element
*/
public int hashCode()
{
return mField.hashCode() + polynomial.hashCode();
}
// /////////////////////////////////////////////////////////////////////
// access
// /////////////////////////////////////////////////////////////////////
/**
* Returns the value of this GF2nPolynomialElement in a new Bitstring.
*
* @return the value of this GF2nPolynomialElement in a new Bitstring
*/
private GF2Polynomial getGF2Polynomial()
{
return new GF2Polynomial(polynomial);
}
/**
* Checks whether the indexed bit of the bit representation is set.
*
* @param index the index of the bit to test
* @return true if the indexed bit is set
*/
boolean testBit(int index)
{
return polynomial.testBit(index);
}
/**
* Returns whether the rightmost bit of the bit representation is set. This
* is needed for data conversion according to 1363.
*
* @return true if the rightmost bit of this element is set
*/
public boolean testRightmostBit()
{
return polynomial.testBit(0);
}
/**
* Compute the sum of this element and addend.
*
* @param addend the addend
* @return this + other (newly created)
*/
public GFElement add(GFElement addend)
throws RuntimeException
{
GF2nPolynomialElement result = new GF2nPolynomialElement(this);
result.addToThis(addend);
return result;
}
/**
* Compute this + addend (overwrite this).
*
* @param addend the addend
*/
public void addToThis(GFElement addend)
throws RuntimeException
{
if (!(addend instanceof GF2nPolynomialElement))
{
throw new RuntimeException();
}
if (!mField.equals(((GF2nPolynomialElement)addend).mField))
{
throw new RuntimeException();
}
polynomial.addToThis(((GF2nPolynomialElement)addend).polynomial);
}
/**
* Returns this element + 'one".
*
* @return this + 'one'
*/
public GF2nElement increase()
{
GF2nPolynomialElement result = new GF2nPolynomialElement(this);
result.increaseThis();
return result;
}
/**
* Increases this element by 'one'.
*/
public void increaseThis()
{
polynomial.increaseThis();
}
/**
* Compute the product of this element and factor.
*
* @param factor the factor
* @return this * factor (newly created)
*/
public GFElement multiply(GFElement factor)
throws RuntimeException
{
GF2nPolynomialElement result = new GF2nPolynomialElement(this);
result.multiplyThisBy(factor);
return result;
}
/**
* Compute this * factor (overwrite this).
*
* @param factor the factor
*/
public void multiplyThisBy(GFElement factor)
throws RuntimeException
{
if (!(factor instanceof GF2nPolynomialElement))
{
throw new RuntimeException();
}
if (!mField.equals(((GF2nPolynomialElement)factor).mField))
{
throw new RuntimeException();
}
if (equals(factor))
{
squareThis();
return;
}
polynomial = polynomial
.multiply(((GF2nPolynomialElement)factor).polynomial);
reduceThis();
}
/**
* Compute the multiplicative inverse of this element.
*
* @return this-1 (newly created)
* @throws ArithmeticException if this is the zero element.
* @see GF2nPolynomialElement#invertMAIA
* @see GF2nPolynomialElement#invertEEA
* @see GF2nPolynomialElement#invertSquare
*/
public GFElement invert()
throws ArithmeticException
{
return invertMAIA();
}
/**
* Calculates the multiplicative inverse of this and returns the
* result in a new GF2nPolynomialElement.
*
* @return this^(-1)
* @throws ArithmeticException if this equals zero
*/
public GF2nPolynomialElement invertEEA()
throws ArithmeticException
{
if (isZero())
{
throw new ArithmeticException();
}
GF2Polynomial b = new GF2Polynomial(mDegree + 32, "ONE");
b.reduceN();
GF2Polynomial c = new GF2Polynomial(mDegree + 32);
c.reduceN();
GF2Polynomial u = getGF2Polynomial();
GF2Polynomial v = mField.getFieldPolynomial();
GF2Polynomial h;
int j;
u.reduceN();
while (!u.isOne())
{
u.reduceN();
v.reduceN();
j = u.getLength() - v.getLength();
if (j < 0)
{
h = u;
u = v;
v = h;
h = b;
b = c;
c = h;
j = -j;
c.reduceN(); // this increases the performance
}
u.shiftLeftAddThis(v, j);
b.shiftLeftAddThis(c, j);
}
b.reduceN();
return new GF2nPolynomialElement((GF2nPolynomialField)mField, b);
}
/**
* Calculates the multiplicative inverse of this and returns the
* result in a new GF2nPolynomialElement.
*
* @return this^(-1)
* @throws ArithmeticException if this equals zero
*/
public GF2nPolynomialElement invertSquare()
throws ArithmeticException
{
GF2nPolynomialElement n;
GF2nPolynomialElement u;
int i, j, k, b;
if (isZero())
{
throw new ArithmeticException();
}
// b = (n-1)
b = mField.getDegree() - 1;
// n = a
n = new GF2nPolynomialElement(this);
n.polynomial.expandN((mDegree << 1) + 32); // increase performance
n.polynomial.reduceN();
// k = 1
k = 1;
// for i = (r-1) downto 0 do, r=bitlength(b)
for (i = IntegerFunctions.floorLog(b) - 1; i >= 0; i--)
{
// u = n
u = new GF2nPolynomialElement(n);
// for j = 1 to k do
for (j = 1; j <= k; j++)
{
// u = u^2
u.squareThisPreCalc();
}
// n = nu
n.multiplyThisBy(u);
// k = 2k
k <<= 1;
// if b(i)==1
if ((b & bitMask[i]) != 0)
{
// n = n^2 * b
n.squareThisPreCalc();
n.multiplyThisBy(this);
// k = k+1
k += 1;
}
}
// outpur n^2
n.squareThisPreCalc();
return n;
}
/**
* Calculates the multiplicative inverse of this using the modified
* almost inverse algorithm and returns the result in a new
* GF2nPolynomialElement.
*
* @return this^(-1)
* @throws ArithmeticException if this equals zero
*/
public GF2nPolynomialElement invertMAIA()
throws ArithmeticException
{
if (isZero())
{
throw new ArithmeticException();
}
GF2Polynomial b = new GF2Polynomial(mDegree, "ONE");
GF2Polynomial c = new GF2Polynomial(mDegree);
GF2Polynomial u = getGF2Polynomial();
GF2Polynomial v = mField.getFieldPolynomial();
GF2Polynomial h;
while (true)
{
while (!u.testBit(0))
{ // x|u (x divides u)
u.shiftRightThis(); // u = u / x
if (!b.testBit(0))
{
b.shiftRightThis();
}
else
{
b.addToThis(mField.getFieldPolynomial());
b.shiftRightThis();
}
}
if (u.isOne())
{
return new GF2nPolynomialElement((GF2nPolynomialField)mField,
b);
}
u.reduceN();
v.reduceN();
if (u.getLength() < v.getLength())
{
h = u;
u = v;
v = h;
h = b;
b = c;
c = h;
}
u.addToThis(v);
b.addToThis(c);
}
}
/**
* This method is used internally to map the square()-calls within
* GF2nPolynomialElement to one of the possible squaring methods.
*
* @return this2 (newly created)
* @see GF2nPolynomialElement#squarePreCalc
*/
public GF2nElement square()
{
return squarePreCalc();
}
/**
* This method is used internally to map the square()-calls within
* GF2nPolynomialElement to one of the possible squaring methods.
*/
public void squareThis()
{
squareThisPreCalc();
}
/**
* Squares this GF2nPolynomialElement using GF2nField's squaring matrix.
* This is supposed to be fast when using a polynomial (no tri- or
* pentanomial) as fieldpolynomial. Use squarePreCalc when using a tri- or
* pentanomial as fieldpolynomial instead.
*
* @return this2 (newly created)
* @see GF2Polynomial#vectorMult
* @see GF2nPolynomialElement#squarePreCalc
* @see GF2nPolynomialElement#squareBitwise
*/
public GF2nPolynomialElement squareMatrix()
{
GF2nPolynomialElement result = new GF2nPolynomialElement(this);
result.squareThisMatrix();
result.reduceThis();
return result;
}
/**
* Squares this GF2nPolynomialElement using GF2nFields squaring matrix. This
* is supposed to be fast when using a polynomial (no tri- or pentanomial)
* as fieldpolynomial. Use squarePreCalc when using a tri- or pentanomial as
* fieldpolynomial instead.
*
* @see GF2Polynomial#vectorMult
* @see GF2nPolynomialElement#squarePreCalc
* @see GF2nPolynomialElement#squareBitwise
*/
public void squareThisMatrix()
{
GF2Polynomial result = new GF2Polynomial(mDegree);
for (int i = 0; i < mDegree; i++)
{
if (polynomial
.vectorMult(((GF2nPolynomialField)mField).squaringMatrix[mDegree
- i - 1]))
{
result.setBit(i);
}
}
polynomial = result;
}
/**
* Squares this GF2nPolynomialElement by shifting left its Bitstring and
* reducing. This is supposed to be the slowest method. Use squarePreCalc or
* squareMatrix instead.
*
* @return this2 (newly created)
* @see GF2nPolynomialElement#squareMatrix
* @see GF2nPolynomialElement#squarePreCalc
* @see GF2Polynomial#squareThisBitwise
*/
public GF2nPolynomialElement squareBitwise()
{
GF2nPolynomialElement result = new GF2nPolynomialElement(this);
result.squareThisBitwise();
result.reduceThis();
return result;
}
/**
* Squares this GF2nPolynomialElement by shifting left its Bitstring and
* reducing. This is supposed to be the slowest method. Use squarePreCalc or
* squareMatrix instead.
*
* @see GF2nPolynomialElement#squareMatrix
* @see GF2nPolynomialElement#squarePreCalc
* @see GF2Polynomial#squareThisBitwise
*/
public void squareThisBitwise()
{
polynomial.squareThisBitwise();
reduceThis();
}
/**
* Squares this GF2nPolynomialElement by using precalculated values and
* reducing. This is supposed to de fastest when using a trinomial or
* pentanomial as field polynomial. Use squareMatrix when using a ordinary
* polynomial as field polynomial.
*
* @return this2 (newly created)
* @see GF2nPolynomialElement#squareMatrix
* @see GF2Polynomial#squareThisPreCalc
*/
public GF2nPolynomialElement squarePreCalc()
{
GF2nPolynomialElement result = new GF2nPolynomialElement(this);
result.squareThisPreCalc();
result.reduceThis();
return result;
}
/**
* Squares this GF2nPolynomialElement by using precalculated values and
* reducing. This is supposed to de fastest when using a tri- or pentanomial
* as fieldpolynomial. Use squareMatrix when using a ordinary polynomial as
* fieldpolynomial.
*
* @see GF2nPolynomialElement#squareMatrix
* @see GF2Polynomial#squareThisPreCalc
*/
public void squareThisPreCalc()
{
polynomial.squareThisPreCalc();
reduceThis();
}
/**
* Calculates this to the power of k and returns the result
* in a new GF2nPolynomialElement.
*
* @param k the power
* @return this^k in a new GF2nPolynomialElement
*/
public GF2nPolynomialElement power(int k)
{
if (k == 1)
{
return new GF2nPolynomialElement(this);
}
GF2nPolynomialElement result = GF2nPolynomialElement
.ONE((GF2nPolynomialField)mField);
if (k == 0)
{
return result;
}
GF2nPolynomialElement x = new GF2nPolynomialElement(this);
x.polynomial.expandN((x.mDegree << 1) + 32); // increase performance
x.polynomial.reduceN();
for (int i = 0; i < mDegree; i++)
{
if ((k & (1 << i)) != 0)
{
result.multiplyThisBy(x);
}
x.square();
}
return result;
}
/**
* Compute the square root of this element and return the result in a new
* {@link GF2nPolynomialElement}.
*
* @return this1/2 (newly created)
*/
public GF2nElement squareRoot()
{
GF2nPolynomialElement result = new GF2nPolynomialElement(this);
result.squareRootThis();
return result;
}
/**
* Compute the square root of this element.
*/
public void squareRootThis()
{
// increase performance
polynomial.expandN((mDegree << 1) + 32);
polynomial.reduceN();
for (int i = 0; i < mField.getDegree() - 1; i++)
{
squareThis();
}
}
/**
* Solves the quadratic equation z2 + z = this if
* such a solution exists. This method returns one of the two possible
* solutions. The other solution is z + 1. Use z.increase() to
* compute this solution.
*
* @return a GF2nPolynomialElement representing one z satisfying the
* equation z2 + z = this
* @see "IEEE 1363, Annex A.4.7"
*/
public GF2nElement solveQuadraticEquation()
throws RuntimeException
{
if (isZero())
{
return ZERO((GF2nPolynomialField)mField);
}
if ((mDegree & 1) == 1)
{
return halfTrace();
}
// TODO this can be sped-up by precomputation of p and w's
GF2nPolynomialElement z, w;
do
{
// step 1.
GF2nPolynomialElement p = new GF2nPolynomialElement(
(GF2nPolynomialField)mField, new Random());
// step 2.
z = ZERO((GF2nPolynomialField)mField);
w = (GF2nPolynomialElement)p.clone();
// step 3.
for (int i = 1; i < mDegree; i++)
{
// compute z = z^2 + w^2 * this
// and w = w^2 + p
z.squareThis();
w.squareThis();
z.addToThis(w.multiply(this));
w.addToThis(p);
}
}
while (w.isZero()); // step 4.
if (!equals(z.square().add(z)))
{
throw new RuntimeException();
}
// step 5.
return z;
}
/**
* Returns the trace of this GF2nPolynomialElement.
*
* @return the trace of this GF2nPolynomialElement
*/
public int trace()
{
GF2nPolynomialElement t = new GF2nPolynomialElement(this);
int i;
for (i = 1; i < mDegree; i++)
{
t.squareThis();
t.addToThis(this);
}
if (t.isOne())
{
return 1;
}
return 0;
}
/**
* Returns the half-trace of this GF2nPolynomialElement.
*
* @return a GF2nPolynomialElement representing the half-trace of this
* GF2nPolynomialElement.
*/
private GF2nPolynomialElement halfTrace()
throws RuntimeException
{
if ((mDegree & 0x01) == 0)
{
throw new RuntimeException();
}
int i;
GF2nPolynomialElement h = new GF2nPolynomialElement(this);
for (i = 1; i <= ((mDegree - 1) >> 1); i++)
{
h.squareThis();
h.squareThis();
h.addToThis(this);
}
return h;
}
/**
* Reduces this GF2nPolynomialElement modulo the field-polynomial.
*
* @see GF2Polynomial#reduceTrinomial
* @see GF2Polynomial#reducePentanomial
*/
private void reduceThis()
{
if (polynomial.getLength() > mDegree)
{ // really reduce ?
if (((GF2nPolynomialField)mField).isTrinomial())
{ // fieldpolonomial
// is trinomial
int tc;
try
{
tc = ((GF2nPolynomialField)mField).getTc();
}
catch (RuntimeException NATExc)
{
throw new RuntimeException(
"GF2nPolynomialElement.reduce: the field"
+ " polynomial is not a trinomial");
}
if (((mDegree - tc) <= 32) // do we have to use slow
// bitwise reduction ?
|| (polynomial.getLength() > (mDegree << 1)))
{
reduceTrinomialBitwise(tc);
return;
}
polynomial.reduceTrinomial(mDegree, tc);
return;
}
else if (((GF2nPolynomialField)mField).isPentanomial())
{ // fieldpolynomial
// is
// pentanomial
int[] pc;
try
{
pc = ((GF2nPolynomialField)mField).getPc();
}
catch (RuntimeException NATExc)
{
throw new RuntimeException(
"GF2nPolynomialElement.reduce: the field"
+ " polynomial is not a pentanomial");
}
if (((mDegree - pc[2]) <= 32) // do we have to use slow
// bitwise reduction ?
|| (polynomial.getLength() > (mDegree << 1)))
{
reducePentanomialBitwise(pc);
return;
}
polynomial.reducePentanomial(mDegree, pc);
return;
}
else
{ // fieldpolynomial is something else
polynomial = polynomial.remainder(mField.getFieldPolynomial());
polynomial.expandN(mDegree);
return;
}
}
if (polynomial.getLength() < mDegree)
{
polynomial.expandN(mDegree);
}
}
/**
* Reduce this GF2nPolynomialElement using the trinomial x^n + x^tc + 1 as
* fieldpolynomial. The coefficients are reduced bit by bit.
*/
private void reduceTrinomialBitwise(int tc)
{
int i;
int k = mDegree - tc;
for (i = polynomial.getLength() - 1; i >= mDegree; i--)
{
if (polynomial.testBit(i))
{
polynomial.xorBit(i);
polynomial.xorBit(i - k);
polynomial.xorBit(i - mDegree);
}
}
polynomial.reduceN();
polynomial.expandN(mDegree);
}
/**
* Reduce this GF2nPolynomialElement using the pentanomial x^n + x^pc[2] +
* x^pc[1] + x^pc[0] + 1 as fieldpolynomial. The coefficients are reduced
* bit by bit.
*/
private void reducePentanomialBitwise(int[] pc)
{
int i;
int k = mDegree - pc[2];
int l = mDegree - pc[1];
int m = mDegree - pc[0];
for (i = polynomial.getLength() - 1; i >= mDegree; i--)
{
if (polynomial.testBit(i))
{
polynomial.xorBit(i);
polynomial.xorBit(i - k);
polynomial.xorBit(i - l);
polynomial.xorBit(i - m);
polynomial.xorBit(i - mDegree);
}
}
polynomial.reduceN();
polynomial.expandN(mDegree);
}
// /////////////////////////////////////////////////////////////////////
// conversion
// /////////////////////////////////////////////////////////////////////
/**
* Returns a string representing this Bitstrings value using hexadecimal
* radix in MSB-first order.
*
* @return a String representing this Bitstrings value.
*/
public String toString()
{
return polynomial.toString(16);
}
/**
* Returns a string representing this Bitstrings value using hexadecimal or
* binary radix in MSB-first order.
*
* @param radix the radix to use (2 or 16, otherwise 2 is used)
* @return a String representing this Bitstrings value.
*/
public String toString(int radix)
{
return polynomial.toString(radix);
}
/**
* Converts this GF2nPolynomialElement to a byte[] according to 1363.
*
* @return a byte[] representing the value of this GF2nPolynomialElement
* @see "P1363 5.5.2 p22f BS2OSP, FE2OSP"
*/
public byte[] toByteArray()
{
return polynomial.toByteArray();
}
/**
* Converts this GF2nPolynomialElement to an integer according to 1363.
*
* @return a BigInteger representing the value of this
* GF2nPolynomialElement
* @see "P1363 5.5.1 p22 BS2IP"
*/
public BigInteger toFlexiBigInt()
{
return polynomial.toFlexiBigInt();
}
}