using System; using System.Collections; using Org.BouncyCastle.Math.EC.Abc; namespace Org.BouncyCastle.Math.EC { /// Base class for an elliptic curve. public abstract class ECCurve { internal ECFieldElement a, b; public abstract int FieldSize { get; } public abstract ECFieldElement FromBigInteger(BigInteger x); public abstract ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression); public abstract ECPoint Infinity { get; } public ECFieldElement A { get { return a; } } public ECFieldElement B { get { return b; } } public override bool Equals( object obj) { if (obj == this) return true; ECCurve other = obj as ECCurve; if (other == null) return false; return Equals(other); } protected bool Equals( ECCurve other) { return a.Equals(other.a) && b.Equals(other.b); } public override int GetHashCode() { return a.GetHashCode() ^ b.GetHashCode(); } protected abstract ECPoint DecompressPoint(int yTilde, BigInteger X1); /** * Decode a point on this curve from its ASN.1 encoding. The different * encodings are taken account of, including point compression for * Fp (X9.62 s 4.2.1 pg 17). * @return The decoded point. */ public virtual ECPoint DecodePoint(byte[] encoded) { ECPoint p = null; int expectedLength = (FieldSize + 7) / 8; switch (encoded[0]) { case 0x00: // infinity { if (encoded.Length != 1) throw new ArgumentException("Incorrect length for infinity encoding", "encoded"); p = Infinity; break; } case 0x02: // compressed case 0x03: // compressed { if (encoded.Length != (expectedLength + 1)) throw new ArgumentException("Incorrect length for compressed encoding", "encoded"); int yTilde = encoded[0] & 1; BigInteger X1 = new BigInteger(1, encoded, 1, expectedLength); p = DecompressPoint(yTilde, X1); break; } case 0x04: // uncompressed case 0x06: // hybrid case 0x07: // hybrid { if (encoded.Length != (2 * expectedLength + 1)) throw new ArgumentException("Incorrect length for uncompressed/hybrid encoding", "encoded"); BigInteger X1 = new BigInteger(1, encoded, 1, expectedLength); BigInteger Y1 = new BigInteger(1, encoded, 1 + expectedLength, expectedLength); p = CreatePoint(X1, Y1, false); break; } default: throw new FormatException("Invalid point encoding " + encoded[0]); } return p; } } /** * Elliptic curve over Fp */ public class FpCurve : ECCurve { private readonly BigInteger q, r; private readonly FpPoint infinity; public FpCurve(BigInteger q, BigInteger a, BigInteger b) { this.q = q; this.r = FpFieldElement.CalculateResidue(q); this.a = FromBigInteger(a); this.b = FromBigInteger(b); this.infinity = new FpPoint(this, null, null); } public BigInteger Q { get { return q; } } public override ECPoint Infinity { get { return infinity; } } public override int FieldSize { get { return q.BitLength; } } public override ECFieldElement FromBigInteger(BigInteger x) { return new FpFieldElement(this.q, this.r, x); } public override ECPoint CreatePoint( BigInteger X1, BigInteger Y1, bool withCompression) { // TODO Validation of X1, Y1? return new FpPoint( this, FromBigInteger(X1), FromBigInteger(Y1), withCompression); } protected override ECPoint DecompressPoint( int yTilde, BigInteger X1) { ECFieldElement x = FromBigInteger(X1); 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 ArithmeticException("Invalid point compression"); BigInteger betaValue = beta.ToBigInteger(); int bit0 = betaValue.TestBit(0) ? 1 : 0; if (bit0 != yTilde) { // Use the other root beta = beta.Negate(); } return new FpPoint(this, x, beta, true); } public override bool Equals( object obj) { if (obj == this) return true; FpCurve other = obj as FpCurve; if (other == null) return false; return Equals(other); } protected bool Equals( FpCurve other) { return base.Equals(other) && q.Equals(other.q); } public override int GetHashCode() { return base.GetHashCode() ^ q.GetHashCode(); } } /** * Elliptic curves over F2m. The Weierstrass equation is given by * y2 + xy = x3 + ax2 + b. */ public class F2mCurve : ECCurve { /** * The exponent m of F2m. */ private readonly int m; /** * 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).
*/ private readonly int k1; /** * TPB: Always set to 0
* PPB: The integer k2 where xm + * xk3 + xk2 + xk1 + 1 * represents the reduction polynomial f(z).
*/ private readonly int k2; /** * TPB: Always set to 0
* PPB: The integer k3 where xm + * xk3 + xk2 + xk1 + 1 * represents the reduction polynomial f(z).
*/ private readonly int k3; /** * The order of the base point of the curve. */ private readonly BigInteger n; /** * The cofactor of the curve. */ private readonly BigInteger h; /** * The point at infinity on this curve. */ private readonly F2mPoint infinity; /** * The parameter μ of the elliptic curve if this is * a Koblitz curve. */ private sbyte mu = 0; /** * The auxiliary values s0 and * s1 used for partial modular reduction for * Koblitz curves. */ private BigInteger[] si = null; /** * Constructor for Trinomial Polynomial Basis (TPB). * @param m The exponent m of * F2m. * @param k The integer k where xm + * xk + 1 represents the reduction * polynomial f(z). * @param a The coefficient a in the Weierstrass equation * for non-supersingular elliptic curves over * F2m. * @param b The coefficient b in the Weierstrass equation * for non-supersingular elliptic curves over * F2m. */ public F2mCurve( 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 m of * F2m. * @param k The integer k where xm + * xk + 1 represents the reduction * polynomial f(z). * @param a The coefficient a in the Weierstrass equation * for non-supersingular elliptic curves over * F2m. * @param b The coefficient b in the Weierstrass equation * for non-supersingular elliptic curves over * F2m. * @param n The order of the main subgroup of the elliptic curve. * @param h The cofactor of the elliptic curve, i.e. * #Ea(F2m) = h * n. */ public F2mCurve( 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 m of * F2m. * @param k1 The integer k1 where xm + * xk3 + xk2 + xk1 + 1 * represents the reduction polynomial f(z). * @param k2 The integer k2 where xm + * xk3 + xk2 + xk1 + 1 * represents the reduction polynomial f(z). * @param k3 The integer k3 where xm + * xk3 + xk2 + xk1 + 1 * represents the reduction polynomial f(z). * @param a The coefficient a in the Weierstrass equation * for non-supersingular elliptic curves over * F2m. * @param b The coefficient b in the Weierstrass equation * for non-supersingular elliptic curves over * F2m. */ public F2mCurve( 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 m of * F2m. * @param k1 The integer k1 where xm + * xk3 + xk2 + xk1 + 1 * represents the reduction polynomial f(z). * @param k2 The integer k2 where xm + * xk3 + xk2 + xk1 + 1 * represents the reduction polynomial f(z). * @param k3 The integer k3 where xm + * xk3 + xk2 + xk1 + 1 * represents the reduction polynomial f(z). * @param a The coefficient a in the Weierstrass equation * for non-supersingular elliptic curves over * F2m. * @param b The coefficient b in the Weierstrass equation * for non-supersingular elliptic curves over * F2m. * @param n The order of the main subgroup of the elliptic curve. * @param h The cofactor of the elliptic curve, i.e. * #Ea(F2m) = h * n. */ public F2mCurve( 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; this.infinity = new F2mPoint(this, null, null); if (k1 == 0) throw new ArgumentException("k1 must be > 0"); if (k2 == 0) { if (k3 != 0) throw new ArgumentException("k3 must be 0 if k2 == 0"); } else { if (k2 <= k1) throw new ArgumentException("k2 must be > k1"); if (k3 <= k2) throw new ArgumentException("k3 must be > k2"); } this.a = FromBigInteger(a); this.b = FromBigInteger(b); } public override ECPoint Infinity { get { return infinity; } } public override int FieldSize { get { return m; } } public override ECFieldElement FromBigInteger(BigInteger x) { return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, x); } /** * Returns true if this is a Koblitz curve (ABC curve). * @return true if this is a Koblitz curve (ABC curve), false otherwise */ public bool IsKoblitz { get { return n != null && h != null && (a.ToBigInteger().Equals(BigInteger.Zero) || a.ToBigInteger().Equals(BigInteger.One)) && b.ToBigInteger().Equals(BigInteger.One); } } /** * Returns the parameter μ of the elliptic curve. * @return μ of the elliptic curve. * @throws ArgumentException if the given ECCurve is not a * Koblitz curve. */ internal sbyte GetMu() { if (mu == 0) { lock (this) { if (mu == 0) { mu = Tnaf.GetMu(this); } } } return mu; } /** * @return the auxiliary values s0 and * s1 used for partial modular reduction for * Koblitz curves. */ internal BigInteger[] GetSi() { if (si == null) { lock (this) { if (si == null) { si = Tnaf.GetSi(this); } } } return si; } public override ECPoint CreatePoint( BigInteger X1, BigInteger Y1, bool withCompression) { // TODO Validation of X1, Y1? return new F2mPoint( this, FromBigInteger(X1), FromBigInteger(Y1), withCompression); } protected override ECPoint DecompressPoint( int yTilde, BigInteger X1) { ECFieldElement xp = FromBigInteger(X1); ECFieldElement yp = null; if (xp.ToBigInteger().SignValue == 0) { yp = (F2mFieldElement)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 ArithmeticException("Invalid point compression"); int zBit = z.ToBigInteger().TestBit(0) ? 1 : 0; if (zBit != yTilde) { z = z.Add(FromBigInteger(BigInteger.One)); } yp = xp.Multiply(z); } return new F2mPoint(this, xp, yp, true); } /** * Solves a quadratic equation z2 + z = beta(X9.62 * D.1.6) The other solution is z + 1. * * @param beta * The value to solve the qradratic equation for. * @return the solution for z2 + z = beta or * null if no solution exists. */ private ECFieldElement solveQuadradicEquation(ECFieldElement beta) { if (beta.ToBigInteger().SignValue == 0) { return FromBigInteger(BigInteger.Zero); } ECFieldElement z = null; ECFieldElement gamma = FromBigInteger(BigInteger.Zero); while (gamma.ToBigInteger().SignValue == 0) { ECFieldElement t = FromBigInteger(new BigInteger(m, new Random())); z = FromBigInteger(BigInteger.Zero); 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().SignValue != 0) { return null; } gamma = z.Square().Add(z); } return z; } public override bool Equals( object obj) { if (obj == this) return true; F2mCurve other = obj as F2mCurve; if (other == null) return false; return Equals(other); } protected bool Equals( F2mCurve other) { return m == other.m && k1 == other.k1 && k2 == other.k2 && k3 == other.k3 && base.Equals(other); } public override int GetHashCode() { return base.GetHashCode() ^ m ^ k1 ^ k2 ^ k3; } public int M { get { return m; } } /** * Return true if curve uses a Trinomial basis. * * @return true if curve Trinomial, false otherwise. */ public bool IsTrinomial() { return k2 == 0 && k3 == 0; } public int K1 { get { return k1; } } public int K2 { get { return k2; } } public int K3 { get { return k3; } } public BigInteger N { get { return n; } } public BigInteger H { get { return h; } } } }