using System; using System.Collections; using System.Diagnostics; using Org.BouncyCastle.Math.EC.Multiplier; namespace Org.BouncyCastle.Math.EC { /** * base class for points on elliptic curves. */ public abstract class ECPoint { protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0]; protected static ECFieldElement[] GetInitialZCoords(ECCurve curve) { // Cope with null curve, most commonly used by implicitlyCa int coord = null == curve ? ECCurve.COORD_AFFINE : curve.CoordinateSystem; switch (coord) { case ECCurve.COORD_AFFINE: case ECCurve.COORD_LAMBDA_AFFINE: return EMPTY_ZS; default: break; } ECFieldElement one = curve.FromBigInteger(BigInteger.One); switch (coord) { case ECCurve.COORD_HOMOGENEOUS: case ECCurve.COORD_JACOBIAN: case ECCurve.COORD_LAMBDA_PROJECTIVE: return new ECFieldElement[] { one }; case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: return new ECFieldElement[] { one, one, one }; case ECCurve.COORD_JACOBIAN_MODIFIED: return new ECFieldElement[] { one, curve.A }; default: throw new ArgumentException("unknown coordinate system"); } } protected internal readonly ECCurve m_curve; protected internal readonly ECFieldElement m_x, m_y; protected internal readonly ECFieldElement[] m_zs; protected internal readonly bool m_withCompression; protected internal PreCompInfo m_preCompInfo = null; protected ECPoint( ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) { this.m_curve = curve; this.m_x = x; this.m_y = y; this.m_withCompression = withCompression; } protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs) { this.m_curve = curve; this.m_x = x; this.m_y = y; this.m_zs = zs; } public virtual ECCurve Curve { get { return m_curve; } } protected virtual int CurveCoordinateSystem { get { // Cope with null curve, most commonly used by implicitlyCa return null == m_curve ? ECCurve.COORD_AFFINE : m_curve.CoordinateSystem; } } public virtual ECFieldElement X { get { return m_x; } } public virtual ECFieldElement Y { get { return m_y; } } public virtual ECFieldElement GetZCoord(int index) { return (index < 0 || index >= m_zs.Length) ? null : m_zs[index]; } public virtual ECFieldElement[] GetZCoords() { int zsLen = m_zs.Length; if (zsLen == 0) { return m_zs; } ECFieldElement[] copy = new ECFieldElement[zsLen]; Array.Copy(m_zs, 0, copy, 0, zsLen); return copy; } protected virtual void CheckNormalized() { if (!IsNormalized()) throw new InvalidOperationException("point not in normal form"); } public virtual bool IsNormalized() { int coord = this.CurveCoordinateSystem; return coord == ECCurve.COORD_AFFINE || coord == ECCurve.COORD_LAMBDA_AFFINE || IsInfinity; //|| zs[0].isOne(); } /** * Normalization ensures that any projective coordinate is 1, and therefore that the x, y * coordinates reflect those of the equivalent point in an affine coordinate system. * * @return a new ECPoint instance representing the same point, but with normalized coordinates */ public virtual ECPoint Normalize() { if (this.IsInfinity) { return this; } switch (this.CurveCoordinateSystem) { case ECCurve.COORD_AFFINE: case ECCurve.COORD_LAMBDA_AFFINE: { return this; } default: { ECFieldElement Z1 = GetZCoord(0); if (Z1.IsOne) { return this; } return Normalize(Z1.Invert()); } } } internal virtual ECPoint Normalize(ECFieldElement zInv) { throw new InvalidOperationException("not a projective coordinate system"); //switch (this.CurveCoordinateSystem) //{ // case ECCurve.COORD_HOMOGENEOUS: // case ECCurve.COORD_LAMBDA_PROJECTIVE: // { // return CreateScaledPoint(zInv, zInv); // } // case ECCurve.COORD_JACOBIAN: // case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: // case ECCurve.COORD_JACOBIAN_MODIFIED: // { // ECFieldElement zInv2 = zInv.Square(), zInv3 = zInv2.Multiply(zInv); // return CreateScaledPoint(zInv2, zInv3); // } // default: // { // throw new InvalidOperationException("not a projective coordinate system"); // } //} } public bool IsInfinity { get { return m_x == null && m_y == null; } } public bool IsCompressed { get { return m_withCompression; } } public override bool Equals(object obj) { return Equals(obj as ECPoint); } public virtual bool Equals(ECPoint other) { if (this == other) return true; if (null == other) return false; bool i1 = IsInfinity, i2 = other.IsInfinity; if (i1 || i2) { return i1 && i2; } return X.Equals(other.X) && Y.Equals(other.Y); } public override int GetHashCode() { int hc = 0; if (!IsInfinity) { hc ^= X.GetHashCode() * 17; hc ^= Y.GetHashCode() * 257; } return hc; } public virtual byte[] GetEncoded() { return GetEncoded(m_withCompression); } public abstract byte[] GetEncoded(bool compressed); protected internal abstract bool CompressionYTilde { get; } public abstract ECPoint Add(ECPoint b); public abstract ECPoint Subtract(ECPoint b); public abstract ECPoint Negate(); public virtual ECPoint TimesPow2(int e) { if (e < 0) throw new ArgumentException("cannot be negative", "e"); ECPoint p = this; while (--e >= 0) { p = p.Twice(); } return p; } public abstract ECPoint Twice(); public abstract ECPoint Multiply(BigInteger b); public virtual ECPoint TwicePlus(ECPoint b) { return Twice().Add(b); } public virtual ECPoint ThreeTimes() { return TwicePlus(this); } } public abstract class ECPointBase : ECPoint { protected internal ECPointBase( ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) : base(curve, x, y, withCompression) { } /** * return the field element encoded with point compression. (S 4.3.6) */ public override byte[] GetEncoded(bool compressed) { if (this.IsInfinity) { return new byte[1]; } ECPoint normed = Normalize(); byte[] X = normed.X.GetEncoded(); if (compressed) { byte[] PO = new byte[X.Length + 1]; PO[0] = (byte)(normed.CompressionYTilde ? 0x03 : 0x02); Array.Copy(X, 0, PO, 1, X.Length); return PO; } byte[] Y = normed.Y.GetEncoded(); { byte[] PO = new byte[X.Length + Y.Length + 1]; PO[0] = 0x04; Array.Copy(X, 0, PO, 1, X.Length); Array.Copy(Y, 0, PO, X.Length + 1, Y.Length); return PO; } } /** * Multiplies this ECPoint by the given number. * @param k The multiplicator. * @return k * this. */ public override ECPoint Multiply( BigInteger k) { if (k.SignValue < 0) throw new ArgumentException("The multiplicator cannot be negative", "k"); if (this.IsInfinity) return this; if (k.SignValue == 0) return Curve.Infinity; return Curve.GetMultiplier().Multiply(this, k); } } /** * Elliptic curve points over Fp */ public class FpPoint : ECPointBase { /** * Create a point which encodes with point compression. * * @param curve the curve to use * @param x affine x co-ordinate * @param y affine y co-ordinate */ public FpPoint( ECCurve curve, ECFieldElement x, ECFieldElement y) : this(curve, x, y, false) { } /** * Create a point that encodes with or without point compresion. * * @param curve the curve to use * @param x affine x co-ordinate * @param y affine y co-ordinate * @param withCompression if true encode with point compression */ public FpPoint( ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) : base(curve, x, y, withCompression) { if ((x == null) != (y == null)) throw new ArgumentException("Exactly one of the field elements is null"); } protected internal override bool CompressionYTilde { get { return this.Y.TestBitZero(); } } // B.3 pg 62 public override ECPoint Add( ECPoint b) { if (this.IsInfinity) { return b; } if (b.IsInfinity) { return this; } if (this == b) { return Twice(); } ECFieldElement X1 = this.X, Y1 = this.Y; ECFieldElement X2 = b.X, Y2 = b.Y; ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1); if (dx.IsZero) { if (dy.IsZero) { // this == b, i.e. this must be doubled return Twice(); } // this == -b, i.e. the result is the point at infinity return Curve.Infinity; } ECFieldElement gamma = dy.Divide(dx); ECFieldElement X3 = gamma.Square().Subtract(X1).Subtract(X2); ECFieldElement Y3 = gamma.Multiply(X1.Subtract(X3)).Subtract(Y1); return new FpPoint(Curve, X3, Y3, IsCompressed); } // B.3 pg 62 public override ECPoint Twice() { if (this.IsInfinity) { return this; } ECFieldElement Y1 = this.Y; if (Y1.IsZero) { return Curve.Infinity; } ECFieldElement X1 = this.X; ECFieldElement X1Squared = X1.Square(); ECFieldElement gamma = Three(X1Squared).Add(this.Curve.A).Divide(Two(Y1)); ECFieldElement X3 = gamma.Square().Subtract(Two(X1)); ECFieldElement Y3 = gamma.Multiply(X1.Subtract(X3)).Subtract(Y1); return new FpPoint(Curve, X3, Y3, IsCompressed); } public override ECPoint TwicePlus(ECPoint b) { if (this == b) { return ThreeTimes(); } if (this.IsInfinity) { return b; } if (b.IsInfinity) { return Twice(); } ECFieldElement Y1 = this.Y; if (Y1.IsZero) { return b; } ECFieldElement X1 = this.X; ECFieldElement X2 = b.X, Y2 = b.Y; ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1); if (dx.IsZero) { if (dy.IsZero) { // this == b i.e. the result is 3P return ThreeTimes(); } // this == -b, i.e. the result is P return this; } /* * Optimized calculation of 2P + Q, as described in "Trading Inversions for * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery. */ ECFieldElement X = dx.Square(), Y = dy.Square(); ECFieldElement d = X.Multiply(Two(X1).Add(X2)).Subtract(Y); if (d.IsZero) { return Curve.Infinity; } ECFieldElement D = d.Multiply(dx); ECFieldElement I = D.Invert(); ECFieldElement L1 = d.Multiply(I).Multiply(dy); ECFieldElement L2 = Two(Y1).Multiply(X).Multiply(dx).Multiply(I).Subtract(L1); ECFieldElement X4 = (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(X2); ECFieldElement Y4 = (X1.Subtract(X4)).Multiply(L2).Subtract(Y1); return new FpPoint(Curve, X4, Y4, IsCompressed); } public override ECPoint ThreeTimes() { if (IsInfinity || this.Y.IsZero) { return this; } ECFieldElement X1 = this.X, Y1 = this.Y; ECFieldElement _2Y1 = Two(Y1); ECFieldElement X = _2Y1.Square(); ECFieldElement Z = Three(X1.Square()).Add(Curve.A); ECFieldElement Y = Z.Square(); ECFieldElement d = Three(X1).Multiply(X).Subtract(Y); if (d.IsZero) { return Curve.Infinity; } ECFieldElement D = d.Multiply(_2Y1); ECFieldElement I = D.Invert(); ECFieldElement L1 = d.Multiply(I).Multiply(Z); ECFieldElement L2 = X.Square().Multiply(I).Subtract(L1); ECFieldElement X4 = (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(X1); ECFieldElement Y4 = (X1.Subtract(X4)).Multiply(L2).Subtract(Y1); return new FpPoint(Curve, X4, Y4, IsCompressed); } protected virtual ECFieldElement Two(ECFieldElement x) { return x.Add(x); } protected virtual ECFieldElement Three(ECFieldElement x) { return Two(x).Add(x); } protected virtual ECFieldElement Four(ECFieldElement x) { return Two(Two(x)); } protected virtual ECFieldElement Eight(ECFieldElement x) { return Four(Two(x)); } protected virtual ECFieldElement DoubleProductFromSquares(ECFieldElement a, ECFieldElement b, ECFieldElement aSquared, ECFieldElement bSquared) { /* * NOTE: If squaring in the field is faster than multiplication, then this is a quicker * way to calculate 2.A.B, if A^2 and B^2 are already known. */ return a.Add(b).Square().Subtract(aSquared).Subtract(bSquared); } // D.3.2 pg 102 (see Note:) public override ECPoint Subtract( ECPoint b) { if (b.IsInfinity) return this; // Add -b return Add(b.Negate()); } public override ECPoint Negate() { if (IsInfinity) { return this; } ECCurve curve = this.Curve; //int coord = curve.CoordinateSystem; //if (ECCurve.COORD_AFFINE != coord) //{ // return new FpPoint(curve, X, Y.Negate(), this.m_zs, IsCompressed); //} return new FpPoint(curve, X, Y.Negate(), IsCompressed); } } /** * Elliptic curve points over F2m */ public class F2mPoint : ECPointBase { /** * @param curve base curve * @param x x point * @param y y point */ public F2mPoint( ECCurve curve, ECFieldElement x, ECFieldElement y) : this(curve, x, y, false) { } /** * @param curve base curve * @param x x point * @param y y point * @param withCompression true if encode with point compression. */ public F2mPoint( ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) : base(curve, x, y, withCompression) { if ((x != null && y == null) || (x == null && y != null)) { throw new ArgumentException("Exactly one of the field elements is null"); } if (x != null) { // Check if x and y are elements of the same field F2mFieldElement.CheckFieldElements(x, y); // Check if x and a are elements of the same field F2mFieldElement.CheckFieldElements(x, curve.A); } } /** * Constructor for point at infinity */ [Obsolete("Use ECCurve.Infinity property")] public F2mPoint( ECCurve curve) : this(curve, null, null) { } protected internal override bool CompressionYTilde { get { // X9.62 4.2.2 and 4.3.6: // if x = 0 then ypTilde := 0, else ypTilde is the rightmost // bit of y * x^(-1) return !this.X.IsZero && this.Y.Divide(this.X).TestBitZero(); } } /** * Check, if two ECPoints can be added or subtracted. * @param a The first ECPoint to check. * @param b The second ECPoint to check. * @throws IllegalArgumentException if a and b * cannot be added. */ private static void CheckPoints( ECPoint a, ECPoint b) { // Check, if points are on the same curve if (!a.Curve.Equals(b.Curve)) throw new ArgumentException("Only points on the same curve can be added or subtracted"); // F2mFieldElement.CheckFieldElements(a.x, b.x); } /* (non-Javadoc) * @see org.bouncycastle.math.ec.ECPoint#add(org.bouncycastle.math.ec.ECPoint) */ public override ECPoint Add(ECPoint b) { CheckPoints(this, b); return AddSimple((F2mPoint) b); } /** * Adds another ECPoints.F2m to this without * checking if both points are on the same curve. Used by multiplication * algorithms, because there all points are a multiple of the same point * and hence the checks can be omitted. * @param b The other ECPoints.F2m to add to * this. * @return this + b */ internal F2mPoint AddSimple(F2mPoint b) { if (this.IsInfinity) return b; if (b.IsInfinity) return this; F2mFieldElement x2 = (F2mFieldElement) b.X; F2mFieldElement y2 = (F2mFieldElement) b.Y; // Check if b == this or b == -this if (this.X.Equals(x2)) { // this == b, i.e. this must be doubled if (this.Y.Equals(y2)) return (F2mPoint) this.Twice(); // this = -other, i.e. the result is the point at infinity return (F2mPoint) Curve.Infinity; } ECFieldElement xSum = this.X.Add(x2); F2mFieldElement lambda = (F2mFieldElement)(this.Y.Add(y2)).Divide(xSum); F2mFieldElement x3 = (F2mFieldElement)lambda.Square().Add(lambda).Add(xSum).Add(Curve.A); F2mFieldElement y3 = (F2mFieldElement)lambda.Multiply(this.X.Add(x3)).Add(x3).Add(this.Y); return new F2mPoint(Curve, x3, y3, IsCompressed); } /* (non-Javadoc) * @see org.bouncycastle.math.ec.ECPoint#subtract(org.bouncycastle.math.ec.ECPoint) */ public override ECPoint Subtract( ECPoint b) { CheckPoints(this, b); return SubtractSimple((F2mPoint) b); } /** * Subtracts another ECPoints.F2m from this * without checking if both points are on the same curve. Used by * multiplication algorithms, because there all points are a multiple * of the same point and hence the checks can be omitted. * @param b The other ECPoints.F2m to subtract from * this. * @return this - b */ internal F2mPoint SubtractSimple( F2mPoint b) { if (b.IsInfinity) return this; // Add -b return AddSimple((F2mPoint) b.Negate()); } /* (non-Javadoc) * @see Org.BouncyCastle.Math.EC.ECPoint#twice() */ public override ECPoint Twice() { // Twice identity element (point at infinity) is identity if (this.IsInfinity) return this; // if x1 == 0, then (x1, y1) == (x1, x1 + y1) // and hence this = -this and thus 2(x1, y1) == infinity if (this.X.IsZero) { return Curve.Infinity; } F2mFieldElement lambda = (F2mFieldElement) this.X.Add(this.Y.Divide(this.X)); F2mFieldElement x2 = (F2mFieldElement)lambda.Square().Add(lambda).Add(Curve.A); ECFieldElement ONE = Curve.FromBigInteger(BigInteger.One); F2mFieldElement y2 = (F2mFieldElement)this.X.Square().Add( x2.Multiply(lambda.Add(ONE))); return new F2mPoint(Curve, x2, y2, IsCompressed); } public override ECPoint Negate() { if (IsInfinity) { return this; } ECFieldElement X1 = this.X; if (X1.IsZero) { return this; } return new F2mPoint(Curve, X1, X1.Add(this.Y), IsCompressed); } } }