package org.spongycastle.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: if (encoded.length > 1) { throw new RuntimeException("Invalid point encoding"); } 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.spongycastle.math.ec.ECCurve#decodePoint(byte[]) */ public ECPoint decodePoint(byte[] encoded) { ECPoint p = null; switch (encoded[0]) { // infinity case 0x00: if (encoded.length > 1) { throw new RuntimeException("Invalid point encoding"); } 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; } } }