package com.google.bitcoin.bouncycastle.math.ec;
import java.math.BigInteger;
import java.util.Random;
/**
* base class for an elliptic curve
*/
public abstract class ECCurve
{
ECFieldElement a, b;
public abstract int getFieldSize();
public abstract ECFieldElement fromBigInteger(BigInteger x);
public abstract ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression);
public abstract ECPoint decodePoint(byte[] encoded);
public abstract ECPoint getInfinity();
public ECFieldElement getA()
{
return a;
}
public ECFieldElement getB()
{
return b;
}
/**
* Elliptic curve over Fp
*/
public static class Fp extends ECCurve
{
BigInteger q;
ECPoint.Fp infinity;
public Fp(BigInteger q, BigInteger a, BigInteger b)
{
this.q = q;
this.a = fromBigInteger(a);
this.b = fromBigInteger(b);
this.infinity = new ECPoint.Fp(this, null, null);
}
public BigInteger getQ()
{
return q;
}
public int getFieldSize()
{
return q.bitLength();
}
public ECFieldElement fromBigInteger(BigInteger x)
{
return new ECFieldElement.Fp(this.q, x);
}
public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
{
return new ECPoint.Fp(this, fromBigInteger(x), fromBigInteger(y), withCompression);
}
/**
* Decode a point on this curve from its ASN.1 encoding. The different
* encodings are taken account of, including point compression for
* <code>F<sub>p</sub></code> (X9.62 s 4.2.1 pg 17).
* @return The decoded point.
*/
public ECPoint decodePoint(byte[] encoded)
{
ECPoint p = null;
switch (encoded[0])
{
// infinity
case 0x00:
p = getInfinity();
break;
// compressed
case 0x02:
case 0x03:
int ytilde = encoded[0] & 1;
byte[] i = new byte[encoded.length - 1];
System.arraycopy(encoded, 1, i, 0, i.length);
ECFieldElement x = new ECFieldElement.Fp(this.q, new BigInteger(1, i));
ECFieldElement alpha = x.multiply(x.square().add(a)).add(b);
ECFieldElement beta = alpha.sqrt();
//
// if we can't find a sqrt we haven't got a point on the
// curve - run!
//
if (beta == null)
{
throw new RuntimeException("Invalid point compression");
}
int bit0 = (beta.toBigInteger().testBit(0) ? 1 : 0);
if (bit0 == ytilde)
{
p = new ECPoint.Fp(this, x, beta, true);
}
else
{
p = new ECPoint.Fp(this, x,
new ECFieldElement.Fp(this.q, q.subtract(beta.toBigInteger())), true);
}
break;
// uncompressed
case 0x04:
// hybrid
case 0x06:
case 0x07:
byte[] xEnc = new byte[(encoded.length - 1) / 2];
byte[] yEnc = new byte[(encoded.length - 1) / 2];
System.arraycopy(encoded, 1, xEnc, 0, xEnc.length);
System.arraycopy(encoded, xEnc.length + 1, yEnc, 0, yEnc.length);
p = new ECPoint.Fp(this,
new ECFieldElement.Fp(this.q, new BigInteger(1, xEnc)),
new ECFieldElement.Fp(this.q, new BigInteger(1, yEnc)));
break;
default:
throw new RuntimeException("Invalid point encoding 0x" + Integer.toString(encoded[0], 16));
}
return p;
}
public ECPoint getInfinity()
{
return infinity;
}
public boolean equals(
Object anObject)
{
if (anObject == this)
{
return true;
}
if (!(anObject instanceof ECCurve.Fp))
{
return false;
}
ECCurve.Fp other = (ECCurve.Fp) anObject;
return this.q.equals(other.q)
&& a.equals(other.a) && b.equals(other.b);
}
public int hashCode()
{
return a.hashCode() ^ b.hashCode() ^ q.hashCode();
}
}
/**
* Elliptic curves over F2m. The Weierstrass equation is given by
* <code>y<sup>2</sup> + xy = x<sup>3</sup> + ax<sup>2</sup> + b</code>.
*/
public static class F2m extends ECCurve
{
/**
* The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
*/
private int m; // can't be final - JDK 1.1
/**
* TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
* x<sup>k</sup> + 1</code> represents the reduction polynomial
* <code>f(z)</code>.<br>
* PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.<br>
*/
private int k1; // can't be final - JDK 1.1
/**
* TPB: Always set to <code>0</code><br>
* PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.<br>
*/
private int k2; // can't be final - JDK 1.1
/**
* TPB: Always set to <code>0</code><br>
* PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.<br>
*/
private int k3; // can't be final - JDK 1.1
/**
* The order of the base point of the curve.
*/
private BigInteger n; // can't be final - JDK 1.1
/**
* The cofactor of the curve.
*/
private BigInteger h; // can't be final - JDK 1.1
/**
* The point at infinity on this curve.
*/
private ECPoint.F2m infinity; // can't be final - JDK 1.1
/**
* The parameter <code>μ</code> of the elliptic curve if this is
* a Koblitz curve.
*/
private byte mu = 0;
/**
* The auxiliary values <code>s<sub>0</sub></code> and
* <code>s<sub>1</sub></code> used for partial modular reduction for
* Koblitz curves.
*/
private BigInteger[] si = null;
/**
* Constructor for Trinomial Polynomial Basis (TPB).
* @param m The exponent <code>m</code> of
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param k The integer <code>k</code> where <code>x<sup>m</sup> +
* x<sup>k</sup> + 1</code> represents the reduction
* polynomial <code>f(z)</code>.
* @param a The coefficient <code>a</code> in the Weierstrass equation
* for non-supersingular elliptic curves over
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param b The coefficient <code>b</code> in the Weierstrass equation
* for non-supersingular elliptic curves over
* <code>F<sub>2<sup>m</sup></sub></code>.
*/
public F2m(
int m,
int k,
BigInteger a,
BigInteger b)
{
this(m, k, 0, 0, a, b, null, null);
}
/**
* Constructor for Trinomial Polynomial Basis (TPB).
* @param m The exponent <code>m</code> of
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param k The integer <code>k</code> where <code>x<sup>m</sup> +
* x<sup>k</sup> + 1</code> represents the reduction
* polynomial <code>f(z)</code>.
* @param a The coefficient <code>a</code> in the Weierstrass equation
* for non-supersingular elliptic curves over
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param b The coefficient <code>b</code> in the Weierstrass equation
* for non-supersingular elliptic curves over
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param n The order of the main subgroup of the elliptic curve.
* @param h The cofactor of the elliptic curve, i.e.
* <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>.
*/
public F2m(
int m,
int k,
BigInteger a,
BigInteger b,
BigInteger n,
BigInteger h)
{
this(m, k, 0, 0, a, b, n, h);
}
/**
* Constructor for Pentanomial Polynomial Basis (PPB).
* @param m The exponent <code>m</code> of
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.
* @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.
* @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.
* @param a The coefficient <code>a</code> in the Weierstrass equation
* for non-supersingular elliptic curves over
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param b The coefficient <code>b</code> in the Weierstrass equation
* for non-supersingular elliptic curves over
* <code>F<sub>2<sup>m</sup></sub></code>.
*/
public F2m(
int m,
int k1,
int k2,
int k3,
BigInteger a,
BigInteger b)
{
this(m, k1, k2, k3, a, b, null, null);
}
/**
* Constructor for Pentanomial Polynomial Basis (PPB).
* @param m The exponent <code>m</code> of
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.
* @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.
* @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
* x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
* represents the reduction polynomial <code>f(z)</code>.
* @param a The coefficient <code>a</code> in the Weierstrass equation
* for non-supersingular elliptic curves over
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param b The coefficient <code>b</code> in the Weierstrass equation
* for non-supersingular elliptic curves over
* <code>F<sub>2<sup>m</sup></sub></code>.
* @param n The order of the main subgroup of the elliptic curve.
* @param h The cofactor of the elliptic curve, i.e.
* <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>.
*/
public F2m(
int m,
int k1,
int k2,
int k3,
BigInteger a,
BigInteger b,
BigInteger n,
BigInteger h)
{
this.m = m;
this.k1 = k1;
this.k2 = k2;
this.k3 = k3;
this.n = n;
this.h = h;
if (k1 == 0)
{
throw new IllegalArgumentException("k1 must be > 0");
}
if (k2 == 0)
{
if (k3 != 0)
{
throw new IllegalArgumentException("k3 must be 0 if k2 == 0");
}
}
else
{
if (k2 <= k1)
{
throw new IllegalArgumentException("k2 must be > k1");
}
if (k3 <= k2)
{
throw new IllegalArgumentException("k3 must be > k2");
}
}
this.a = fromBigInteger(a);
this.b = fromBigInteger(b);
this.infinity = new ECPoint.F2m(this, null, null);
}
public int getFieldSize()
{
return m;
}
public ECFieldElement fromBigInteger(BigInteger x)
{
return new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3, x);
}
public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
{
return new ECPoint.F2m(this, fromBigInteger(x), fromBigInteger(y), withCompression);
}
/* (non-Javadoc)
* @see org.bouncycastle.math.ec.ECCurve#decodePoint(byte[])
*/
public ECPoint decodePoint(byte[] encoded)
{
ECPoint p = null;
switch (encoded[0])
{
// infinity
case 0x00:
p = getInfinity();
break;
// compressed
case 0x02:
case 0x03:
byte[] enc = new byte[encoded.length - 1];
System.arraycopy(encoded, 1, enc, 0, enc.length);
if (encoded[0] == 0x02)
{
p = decompressPoint(enc, 0);
}
else
{
p = decompressPoint(enc, 1);
}
break;
// uncompressed
case 0x04:
// hybrid
case 0x06:
case 0x07:
byte[] xEnc = new byte[(encoded.length - 1) / 2];
byte[] yEnc = new byte[(encoded.length - 1) / 2];
System.arraycopy(encoded, 1, xEnc, 0, xEnc.length);
System.arraycopy(encoded, xEnc.length + 1, yEnc, 0, yEnc.length);
p = new ECPoint.F2m(this,
new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3,
new BigInteger(1, xEnc)),
new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3,
new BigInteger(1, yEnc)), false);
break;
default:
throw new RuntimeException("Invalid point encoding 0x" + Integer.toString(encoded[0], 16));
}
return p;
}
public ECPoint getInfinity()
{
return infinity;
}
/**
* Returns true if this is a Koblitz curve (ABC curve).
* @return true if this is a Koblitz curve (ABC curve), false otherwise
*/
public boolean isKoblitz()
{
return ((n != null) && (h != null) &&
((a.toBigInteger().equals(ECConstants.ZERO)) ||
(a.toBigInteger().equals(ECConstants.ONE))) &&
(b.toBigInteger().equals(ECConstants.ONE)));
}
/**
* Returns the parameter <code>μ</code> of the elliptic curve.
* @return <code>μ</code> of the elliptic curve.
* @throws IllegalArgumentException if the given ECCurve is not a
* Koblitz curve.
*/
synchronized byte getMu()
{
if (mu == 0)
{
mu = Tnaf.getMu(this);
}
return mu;
}
/**
* @return the auxiliary values <code>s<sub>0</sub></code> and
* <code>s<sub>1</sub></code> used for partial modular reduction for
* Koblitz curves.
*/
synchronized BigInteger[] getSi()
{
if (si == null)
{
si = Tnaf.getSi(this);
}
return si;
}
/**
* Decompresses a compressed point P = (xp, yp) (X9.62 s 4.2.2).
*
* @param xEnc
* The encoding of field element xp.
* @param ypBit
* ~yp, an indication bit for the decompression of yp.
* @return the decompressed point.
*/
private ECPoint decompressPoint(
byte[] xEnc,
int ypBit)
{
ECFieldElement xp = new ECFieldElement.F2m(
this.m, this.k1, this.k2, this.k3, new BigInteger(1, xEnc));
ECFieldElement yp = null;
if (xp.toBigInteger().equals(ECConstants.ZERO))
{
yp = (ECFieldElement.F2m)b;
for (int i = 0; i < m - 1; i++)
{
yp = yp.square();
}
}
else
{
ECFieldElement beta = xp.add(a).add(
b.multiply(xp.square().invert()));
ECFieldElement z = solveQuadradicEquation(beta);
if (z == null)
{
throw new RuntimeException("Invalid point compression");
}
int zBit = 0;
if (z.toBigInteger().testBit(0))
{
zBit = 1;
}
if (zBit != ypBit)
{
z = z.add(new ECFieldElement.F2m(this.m, this.k1, this.k2,
this.k3, ECConstants.ONE));
}
yp = xp.multiply(z);
}
return new ECPoint.F2m(this, xp, yp);
}
/**
* Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62
* D.1.6) The other solution is <code>z + 1</code>.
*
* @param beta
* The value to solve the qradratic equation for.
* @return the solution for <code>z<sup>2</sup> + z = beta</code> or
* <code>null</code> if no solution exists.
*/
private ECFieldElement solveQuadradicEquation(ECFieldElement beta)
{
ECFieldElement zeroElement = new ECFieldElement.F2m(
this.m, this.k1, this.k2, this.k3, ECConstants.ZERO);
if (beta.toBigInteger().equals(ECConstants.ZERO))
{
return zeroElement;
}
ECFieldElement z = null;
ECFieldElement gamma = zeroElement;
Random rand = new Random();
do
{
ECFieldElement t = new ECFieldElement.F2m(this.m, this.k1,
this.k2, this.k3, new BigInteger(m, rand));
z = zeroElement;
ECFieldElement w = beta;
for (int i = 1; i <= m - 1; i++)
{
ECFieldElement w2 = w.square();
z = z.square().add(w2.multiply(t));
w = w2.add(beta);
}
if (!w.toBigInteger().equals(ECConstants.ZERO))
{
return null;
}
gamma = z.square().add(z);
}
while (gamma.toBigInteger().equals(ECConstants.ZERO));
return z;
}
public boolean equals(
Object anObject)
{
if (anObject == this)
{
return true;
}
if (!(anObject instanceof ECCurve.F2m))
{
return false;
}
ECCurve.F2m other = (ECCurve.F2m)anObject;
return (this.m == other.m) && (this.k1 == other.k1)
&& (this.k2 == other.k2) && (this.k3 == other.k3)
&& a.equals(other.a) && b.equals(other.b);
}
public int hashCode()
{
return this.a.hashCode() ^ this.b.hashCode() ^ m ^ k1 ^ k2 ^ k3;
}
public int getM()
{
return m;
}
/**
* Return true if curve uses a Trinomial basis.
*
* @return true if curve Trinomial, false otherwise.
*/
public boolean isTrinomial()
{
return k2 == 0 && k3 == 0;
}
public int getK1()
{
return k1;
}
public int getK2()
{
return k2;
}
public int getK3()
{
return k3;
}
public BigInteger getN()
{
return n;
}
public BigInteger getH()
{
return h;
}
}
}