org.bouncycastle.math.ec.ECFieldElement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-ext-debug-jdk18on Show documentation
Show all versions of bcprov-ext-debug-jdk18on Show documentation
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for Java 1.8 and later with debug enabled.
The newest version!
package org.bouncycastle.math.ec;
import java.math.BigInteger;
import java.util.Random;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Integers;
public abstract class ECFieldElement
implements ECConstants
{
public abstract BigInteger toBigInteger();
public abstract String getFieldName();
public abstract int getFieldSize();
public abstract ECFieldElement add(ECFieldElement b);
public abstract ECFieldElement addOne();
public abstract ECFieldElement subtract(ECFieldElement b);
public abstract ECFieldElement multiply(ECFieldElement b);
public abstract ECFieldElement divide(ECFieldElement b);
public abstract ECFieldElement negate();
public abstract ECFieldElement square();
public abstract ECFieldElement invert();
public abstract ECFieldElement sqrt();
public ECFieldElement()
{
}
public int bitLength()
{
return toBigInteger().bitLength();
}
public boolean isOne()
{
return bitLength() == 1;
}
public boolean isZero()
{
return 0 == toBigInteger().signum();
}
public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
{
return multiply(b).subtract(x.multiply(y));
}
public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
{
return multiply(b).add(x.multiply(y));
}
public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
{
return square().subtract(x.multiply(y));
}
public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
{
return square().add(x.multiply(y));
}
public ECFieldElement squarePow(int pow)
{
ECFieldElement r = this;
for (int i = 0; i < pow; ++i)
{
r = r.square();
}
return r;
}
public boolean testBitZero()
{
return toBigInteger().testBit(0);
}
public String toString()
{
return this.toBigInteger().toString(16);
}
public byte[] getEncoded()
{
return BigIntegers.asUnsignedByteArray((getFieldSize() + 7) / 8, toBigInteger());
}
public static abstract class AbstractFp extends ECFieldElement
{
}
public static class Fp extends AbstractFp
{
BigInteger q, r, x;
static BigInteger calculateResidue(BigInteger p)
{
int bitLength = p.bitLength();
if (bitLength >= 96)
{
BigInteger firstWord = p.shiftRight(bitLength - 64);
if (firstWord.longValue() == -1L)
{
return ONE.shiftLeft(bitLength).subtract(p);
}
}
return null;
}
Fp(BigInteger q, BigInteger r, BigInteger x)
{
this.q = q;
this.r = r;
this.x = x;
}
public BigInteger toBigInteger()
{
return x;
}
/**
* return the field name for this field.
*
* @return the string "Fp".
*/
public String getFieldName()
{
return "Fp";
}
public int getFieldSize()
{
return q.bitLength();
}
public BigInteger getQ()
{
return q;
}
public ECFieldElement add(ECFieldElement b)
{
return new Fp(q, r, modAdd(x, b.toBigInteger()));
}
public ECFieldElement addOne()
{
BigInteger x2 = x.add(ECConstants.ONE);
if (x2.compareTo(q) == 0)
{
x2 = ECConstants.ZERO;
}
return new Fp(q, r, x2);
}
public ECFieldElement subtract(ECFieldElement b)
{
return new Fp(q, r, modSubtract(x, b.toBigInteger()));
}
public ECFieldElement multiply(ECFieldElement b)
{
return new Fp(q, r, modMult(x, b.toBigInteger()));
}
public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
{
BigInteger ax = this.x, bx = b.toBigInteger(), xx = x.toBigInteger(), yx = y.toBigInteger();
BigInteger ab = ax.multiply(bx);
BigInteger xy = xx.multiply(yx);
return new Fp(q, r, modReduce(ab.subtract(xy)));
}
public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
{
BigInteger ax = this.x, bx = b.toBigInteger(), xx = x.toBigInteger(), yx = y.toBigInteger();
BigInteger ab = ax.multiply(bx);
BigInteger xy = xx.multiply(yx);
return new Fp(q, r, modReduce(ab.add(xy)));
}
public ECFieldElement divide(ECFieldElement b)
{
return new Fp(q, r, modMult(x, modInverse(b.toBigInteger())));
}
public ECFieldElement negate()
{
return x.signum() == 0 ? this : new Fp(q, r, q.subtract(x));
}
public ECFieldElement square()
{
return new Fp(q, r, modMult(x, x));
}
public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
{
BigInteger ax = this.x, xx = x.toBigInteger(), yx = y.toBigInteger();
BigInteger aa = ax.multiply(ax);
BigInteger xy = xx.multiply(yx);
return new Fp(q, r, modReduce(aa.subtract(xy)));
}
public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
{
BigInteger ax = this.x, xx = x.toBigInteger(), yx = y.toBigInteger();
BigInteger aa = ax.multiply(ax);
BigInteger xy = xx.multiply(yx);
return new Fp(q, r, modReduce(aa.add(xy)));
}
public ECFieldElement invert()
{
// TODO Modular inversion can be faster for a (Generalized) Mersenne Prime.
return new Fp(q, r, modInverse(x));
}
// D.1.4 91
/**
* return a sqrt root - the routine verifies that the calculation
* returns the right value - if none exists it returns null.
*/
public ECFieldElement sqrt()
{
if (this.isZero() || this.isOne()) // earlier JDK compatibility
{
return this;
}
if (!q.testBit(0))
{
throw new RuntimeException("not done yet");
}
// note: even though this class implements ECConstants don't be tempted to
// remove the explicit declaration, some J2ME environments don't cope.
if (q.testBit(1)) // q == 4m + 3
{
BigInteger e = q.shiftRight(2).add(ECConstants.ONE);
return checkSqrt(new Fp(q, r, x.modPow(e, q)));
}
if (q.testBit(2)) // q == 8m + 5
{
BigInteger t1 = x.modPow(q.shiftRight(3), q);
BigInteger t2 = modMult(t1, x);
BigInteger t3 = modMult(t2, t1);
if (t3.equals(ECConstants.ONE))
{
return checkSqrt(new Fp(q, r, t2));
}
// TODO This is constant and could be precomputed
BigInteger t4 = ECConstants.TWO.modPow(q.shiftRight(2), q);
BigInteger y = modMult(t2, t4);
return checkSqrt(new Fp(q, r, y));
}
// q == 8m + 1
BigInteger legendreExponent = q.shiftRight(1);
if (!(x.modPow(legendreExponent, q).equals(ECConstants.ONE)))
{
return null;
}
BigInteger X = this.x;
BigInteger fourX = modDouble(modDouble(X));
BigInteger k = legendreExponent.add(ECConstants.ONE), qMinusOne = q.subtract(ECConstants.ONE);
BigInteger U, V;
Random rand = new Random();
do
{
BigInteger P;
do
{
P = new BigInteger(q.bitLength(), rand);
}
while (P.compareTo(q) >= 0
|| !modReduce(P.multiply(P).subtract(fourX)).modPow(legendreExponent, q).equals(qMinusOne));
BigInteger[] result = lucasSequence(P, X, k);
U = result[0];
V = result[1];
if (modMult(V, V).equals(fourX))
{
return new ECFieldElement.Fp(q, r, modHalfAbs(V));
}
}
while (U.equals(ECConstants.ONE) || U.equals(qMinusOne));
return null;
}
private ECFieldElement checkSqrt(ECFieldElement z)
{
return z.square().equals(this) ? z : null;
}
private BigInteger[] lucasSequence(
BigInteger P,
BigInteger Q,
BigInteger k)
{
// TODO Research and apply "common-multiplicand multiplication here"
int n = k.bitLength();
int s = k.getLowestSetBit();
// assert k.testBit(s);
BigInteger Uh = ECConstants.ONE;
BigInteger Vl = ECConstants.TWO;
BigInteger Vh = P;
BigInteger Ql = ECConstants.ONE;
BigInteger Qh = ECConstants.ONE;
for (int j = n - 1; j >= s + 1; --j)
{
Ql = modMult(Ql, Qh);
if (k.testBit(j))
{
Qh = modMult(Ql, Q);
Uh = modMult(Uh, Vh);
Vl = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
Vh = modReduce(Vh.multiply(Vh).subtract(Qh.shiftLeft(1)));
}
else
{
Qh = Ql;
Uh = modReduce(Uh.multiply(Vl).subtract(Ql));
Vh = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
Vl = modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1)));
}
}
Ql = modMult(Ql, Qh);
Qh = modMult(Ql, Q);
Uh = modReduce(Uh.multiply(Vl).subtract(Ql));
Vl = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
Ql = modMult(Ql, Qh);
for (int j = 1; j <= s; ++j)
{
Uh = modMult(Uh, Vl);
Vl = modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1)));
Ql = modMult(Ql, Ql);
}
return new BigInteger[]{ Uh, Vl };
}
protected BigInteger modAdd(BigInteger x1, BigInteger x2)
{
BigInteger x3 = x1.add(x2);
if (x3.compareTo(q) >= 0)
{
x3 = x3.subtract(q);
}
return x3;
}
protected BigInteger modDouble(BigInteger x)
{
BigInteger _2x = x.shiftLeft(1);
if (_2x.compareTo(q) >= 0)
{
_2x = _2x.subtract(q);
}
return _2x;
}
protected BigInteger modHalf(BigInteger x)
{
if (x.testBit(0))
{
x = q.add(x);
}
return x.shiftRight(1);
}
protected BigInteger modHalfAbs(BigInteger x)
{
if (x.testBit(0))
{
x = q.subtract(x);
}
return x.shiftRight(1);
}
protected BigInteger modInverse(BigInteger x)
{
return BigIntegers.modOddInverse(q, x);
}
protected BigInteger modMult(BigInteger x1, BigInteger x2)
{
return modReduce(x1.multiply(x2));
}
protected BigInteger modReduce(BigInteger x)
{
if (r != null)
{
boolean negative = x.signum() < 0;
if (negative)
{
x = x.abs();
}
int qLen = q.bitLength();
boolean rIsOne = r.equals(ECConstants.ONE);
while (x.bitLength() > (qLen + 1))
{
BigInteger u = x.shiftRight(qLen);
BigInteger v = x.subtract(u.shiftLeft(qLen));
if (!rIsOne)
{
u = u.multiply(r);
}
x = u.add(v);
}
while (x.compareTo(q) >= 0)
{
x = x.subtract(q);
}
if (negative && x.signum() != 0)
{
x = q.subtract(x);
}
}
else
{
x = x.mod(q);
}
return x;
}
protected BigInteger modSubtract(BigInteger x1, BigInteger x2)
{
BigInteger x3 = x1.subtract(x2);
if (x3.signum() < 0)
{
x3 = x3.add(q);
}
return x3;
}
public boolean equals(Object other)
{
if (other == this)
{
return true;
}
if (!(other instanceof ECFieldElement.Fp))
{
return false;
}
ECFieldElement.Fp o = (ECFieldElement.Fp)other;
return q.equals(o.q) && x.equals(o.x);
}
public int hashCode()
{
return q.hashCode() ^ x.hashCode();
}
}
public static abstract class AbstractF2m extends ECFieldElement
{
public ECFieldElement halfTrace()
{
int m = this.getFieldSize();
if ((m & 1) == 0)
{
throw new IllegalStateException("Half-trace only defined for odd m");
}
// ECFieldElement ht = this;
// for (int i = 1; i < m; i += 2)
// {
// ht = ht.squarePow(2).add(this);
// }
int n = (m + 1) >>> 1;
int k = 31 - Integers.numberOfLeadingZeros(n);
int nk = 1;
ECFieldElement ht = this;
while (k > 0)
{
ht = ht.squarePow(nk << 1).add(ht);
nk = n >>> --k;
if (0 != (nk & 1))
{
ht = ht.squarePow(2).add(this);
}
}
return ht;
}
public boolean hasFastTrace()
{
return false;
}
public int trace()
{
int m = this.getFieldSize();
// ECFieldElement tr = this;
// for (int i = 1; i < m; ++i)
// {
// tr = tr.square().add(this);
// }
int k = 31 - Integers.numberOfLeadingZeros(m);
int mk = 1;
ECFieldElement tr = this;
while (k > 0)
{
tr = tr.squarePow(mk).add(tr);
mk = m >>> --k;
if (0 != (mk & 1))
{
tr = tr.square().add(this);
}
}
if (tr.isZero())
{
return 0;
}
if (tr.isOne())
{
return 1;
}
throw new IllegalStateException("Internal error in trace calculation");
}
}
/**
* Class representing the Elements of the finite field
* F2m
in polynomial basis (PB)
* representation. Both trinomial (TPB) and pentanomial (PPB) polynomial
* basis representations are supported. Gaussian normal basis (GNB)
* representation is not supported.
*/
public static class F2m extends AbstractF2m
{
/**
* Indicates gaussian normal basis representation (GNB). Number chosen
* according to X9.62. GNB is not implemented at present.
*/
public static final int GNB = 1;
/**
* Indicates trinomial basis representation (TPB). Number chosen
* according to X9.62.
*/
public static final int TPB = 2;
/**
* Indicates pentanomial basis representation (PPB). Number chosen
* according to X9.62.
*/
public static final int PPB = 3;
/**
* TPB or PPB.
*/
private int representation;
/**
* The exponent m
of F2m
.
*/
private int m;
private int[] ks;
/**
* The LongArray
holding the bits.
*/
LongArray x;
F2m(int m, int[] ks, LongArray x)
{
this.m = m;
this.representation = (ks.length == 1) ? TPB : PPB;
this.ks = ks;
this.x = x;
}
public int bitLength()
{
return x.degree();
}
public boolean isOne()
{
return x.isOne();
}
public boolean isZero()
{
return x.isZero();
}
public boolean testBitZero()
{
return x.testBitZero();
}
public BigInteger toBigInteger()
{
return x.toBigInteger();
}
public String getFieldName()
{
return "F2m";
}
public int getFieldSize()
{
return m;
}
public ECFieldElement add(final ECFieldElement b)
{
// No check performed here for performance reasons. Instead the
// elements involved are checked in ECPoint.F2m
// checkFieldElements(this, b);
LongArray iarrClone = (LongArray)this.x.clone();
F2m bF2m = (F2m)b;
iarrClone.addShiftedByWords(bF2m.x, 0);
return new F2m(m, ks, iarrClone);
}
public ECFieldElement addOne()
{
return new F2m(m, ks, x.addOne());
}
public ECFieldElement subtract(final ECFieldElement b)
{
// Addition and subtraction are the same in F2m
return add(b);
}
public ECFieldElement multiply(final ECFieldElement b)
{
// Right-to-left comb multiplication in the LongArray
// Input: Binary polynomials a(z) and b(z) of degree at most m-1
// Output: c(z) = a(z) * b(z) mod f(z)
// No check performed here for performance reasons. Instead the
// elements involved are checked in ECPoint.F2m
// checkFieldElements(this, b);
return new F2m(m, ks, x.modMultiply(((F2m)b).x, m, ks));
}
public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
{
return multiplyPlusProduct(b, x, y);
}
public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
{
LongArray ax = this.x, bx = ((F2m)b).x, xx = ((F2m)x).x, yx = ((F2m)y).x;
LongArray ab = ax.multiply(bx, m, ks);
LongArray xy = xx.multiply(yx, m, ks);
if (ab == ax || ab == bx)
{
ab = (LongArray)ab.clone();
}
ab.addShiftedByWords(xy, 0);
ab.reduce(m, ks);
return new F2m(m, ks, ab);
}
public ECFieldElement divide(final ECFieldElement b)
{
// There may be more efficient implementations
ECFieldElement bInv = b.invert();
return multiply(bInv);
}
public ECFieldElement negate()
{
// -x == x holds for all x in F2m
return this;
}
public ECFieldElement square()
{
return new F2m(m, ks, x.modSquare(m, ks));
}
public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y)
{
return squarePlusProduct(x, y);
}
public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y)
{
LongArray ax = this.x, xx = ((F2m)x).x, yx = ((F2m)y).x;
LongArray aa = ax.square(m, ks);
LongArray xy = xx.multiply(yx, m, ks);
if (aa == ax)
{
aa = (LongArray)aa.clone();
}
aa.addShiftedByWords(xy, 0);
aa.reduce(m, ks);
return new F2m(m, ks, aa);
}
public ECFieldElement squarePow(int pow)
{
return pow < 1 ? this : new F2m(m, ks, x.modSquareN(pow, m, ks));
}
public ECFieldElement invert()
{
return new ECFieldElement.F2m(this.m, this.ks, this.x.modInverse(m, ks));
}
public ECFieldElement sqrt()
{
return (x.isZero() || x.isOne()) ? this : squarePow(m - 1);
}
/**
* @return the representation of the field
* F2m
, either of
* TPB (trinomial
* basis representation) or
* PPB (pentanomial
* basis representation).
*/
public int getRepresentation()
{
return this.representation;
}
/**
* @return the degree m
of the reduction polynomial
* f(z)
.
*/
public int getM()
{
return this.m;
}
/**
* @return TPB: The integer k
where xm +
* xk + 1
represents the reduction polynomial
* f(z)
.
* PPB: The integer k1
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
*/
public int getK1()
{
return this.ks[0];
}
/**
* @return TPB: Always returns 0
* PPB: The integer k2
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
*/
public int getK2()
{
return this.ks.length >= 2 ? this.ks[1] : 0;
}
/**
* @return TPB: Always set to 0
* PPB: The integer k3
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
*/
public int getK3()
{
return this.ks.length >= 3 ? this.ks[2] : 0;
}
public boolean equals(Object anObject)
{
if (anObject == this)
{
return true;
}
if (!(anObject instanceof ECFieldElement.F2m))
{
return false;
}
ECFieldElement.F2m b = (ECFieldElement.F2m)anObject;
return ((this.m == b.m)
&& (this.representation == b.representation)
&& Arrays.areEqual(this.ks, b.ks)
&& (this.x.equals(b.x)));
}
public int hashCode()
{
return x.hashCode() ^ m ^ Arrays.hashCode(ks);
}
}
}