summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2014-07-23 15:17:12 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2014-07-23 15:17:12 +0700
commit6e306046568f9a4d13639b913f0ff6d5879fa165 (patch)
tree994b8711674bb146ff578c1f0dff649282962acb
parentUpdate encrypt_then_mac entry (diff)
downloadBouncyCastle.NET-ed25519-6e306046568f9a4d13639b913f0ff6d5879fa165.tar.xz
Add automatic EC point validation for decoded points and for multiplier outputs
-rw-r--r--crypto/src/math/ec/ECAlgorithms.cs54
-rw-r--r--crypto/src/math/ec/ECCurve.cs164
-rw-r--r--crypto/src/math/ec/ECPoint.cs204
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519.cs27
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519Point.cs15
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Point.cs15
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Point.cs15
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Point.cs15
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Point.cs15
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Point.cs15
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Point.cs15
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Point.cs15
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Curve.cs27
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Point.cs15
-rw-r--r--crypto/src/math/ec/multiplier/AbstractECMultiplier.cs8
-rw-r--r--crypto/src/math/ec/multiplier/ReferenceMultiplier.cs28
-rw-r--r--crypto/test/src/math/ec/test/ECPointTest.cs33
24 files changed, 375 insertions, 494 deletions
diff --git a/crypto/src/math/ec/ECAlgorithms.cs b/crypto/src/math/ec/ECAlgorithms.cs
index d82cafedf..3c911b173 100644
--- a/crypto/src/math/ec/ECAlgorithms.cs
+++ b/crypto/src/math/ec/ECAlgorithms.cs
@@ -49,10 +49,10 @@ namespace Org.BouncyCastle.Math.EC
             GlvEndomorphism glvEndomorphism = c.GetEndomorphism() as GlvEndomorphism;
             if (glvEndomorphism != null)
             {
-                return ImplSumOfMultipliesGlv(imported, ks, glvEndomorphism);
+                return ValidatePoint(ImplSumOfMultipliesGlv(imported, ks, glvEndomorphism));
             }
 
-            return ImplSumOfMultiplies(imported, ks);
+            return ValidatePoint(ImplSumOfMultiplies(imported, ks));
         }
 
         public static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger a, ECPoint Q, BigInteger b)
@@ -66,17 +66,18 @@ namespace Org.BouncyCastle.Math.EC
                 F2mCurve f2mCurve = (F2mCurve) cp;
                 if (f2mCurve.IsKoblitz)
                 {
-                    return P.Multiply(a).Add(Q.Multiply(b));
+                    return ValidatePoint(P.Multiply(a).Add(Q.Multiply(b)));
                 }
             }
 
             GlvEndomorphism glvEndomorphism = cp.GetEndomorphism() as GlvEndomorphism;
             if (glvEndomorphism != null)
             {
-                return ImplSumOfMultipliesGlv(new ECPoint[] { P, Q }, new BigInteger[] { a, b }, glvEndomorphism);
+                return ValidatePoint(
+                    ImplSumOfMultipliesGlv(new ECPoint[] { P, Q }, new BigInteger[] { a, b }, glvEndomorphism));
             }
 
-            return ImplShamirsTrickWNaf(P, a, Q, b);
+            return ValidatePoint(ImplShamirsTrickWNaf(P, a, Q, b));
         }
 
         /*
@@ -102,7 +103,7 @@ namespace Org.BouncyCastle.Math.EC
             ECCurve cp = P.Curve;
             Q = ImportPoint(cp, Q);
 
-            return ImplShamirsTrickJsf(P, k, Q, l);
+            return ValidatePoint(ImplShamirsTrickJsf(P, k, Q, l));
         }
 
         public static ECPoint ImportPoint(ECCurve c, ECPoint p)
@@ -145,6 +146,47 @@ namespace Org.BouncyCastle.Math.EC
             zs[off] = u;
         }
 
+        /**
+         * Simple shift-and-add multiplication. Serves as reference implementation
+         * to verify (possibly faster) implementations, and for very small scalars.
+         * 
+         * @param p
+         *            The point to multiply.
+         * @param k
+         *            The multiplier.
+         * @return The result of the point multiplication <code>kP</code>.
+         */
+        public static ECPoint ReferenceMultiply(ECPoint p, BigInteger k)
+        {
+            BigInteger x = k.Abs();
+            ECPoint q = p.Curve.Infinity;
+            int t = x.BitLength;
+            if (t > 0)
+            {
+                if (x.TestBit(0))
+                {
+                    q = p;
+                }
+                for (int i = 1; i < t; i++)
+                {
+                    p = p.Twice();
+                    if (x.TestBit(i))
+                    {
+                        q = q.Add(p);
+                    }
+                }
+            }
+            return k.SignValue < 0 ? q.Negate() : q;
+        }
+
+        public static ECPoint ValidatePoint(ECPoint p)
+        {
+            if (!p.IsValid())
+                throw new ArgumentException("Invalid point", "p");
+
+            return p;
+        }
+
         internal static ECPoint ImplShamirsTrickJsf(ECPoint P, BigInteger k, ECPoint Q, BigInteger l)
         {
             ECCurve curve = P.Curve;
diff --git a/crypto/src/math/ec/ECCurve.cs b/crypto/src/math/ec/ECCurve.cs
index 9c16375e6..889da292f 100644
--- a/crypto/src/math/ec/ECCurve.cs
+++ b/crypto/src/math/ec/ECCurve.cs
@@ -102,6 +102,27 @@ namespace Org.BouncyCastle.Math.EC
             return new Config(this, this.m_coord, this.m_endomorphism, this.m_multiplier);
         }
 
+        public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y)
+        {
+            ECPoint p = CreatePoint(x, y);
+            if (!p.IsValid())
+            {
+                throw new ArgumentException("Invalid point coordinates");
+            }
+            return p;
+        }
+
+        [Obsolete("Per-point compression property will be removed")]
+        public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y, bool withCompression)
+        {
+            ECPoint p = CreatePoint(x, y, withCompression);
+            if (!p.IsValid())
+            {
+                throw new ArgumentException("Invalid point coordinates");
+            }
+            return p;
+        }
+
         public virtual ECPoint CreatePoint(BigInteger x, BigInteger y)
         {
             return CreatePoint(x, y, false);
@@ -185,7 +206,7 @@ namespace Org.BouncyCastle.Math.EC
             // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates.
             p = p.Normalize();
 
-            return CreatePoint(p.XCoord.ToBigInteger(), p.YCoord.ToBigInteger(), p.IsCompressed);
+            return ValidatePoint(p.XCoord.ToBigInteger(), p.YCoord.ToBigInteger(), p.IsCompressed);
         }
 
         /**
@@ -344,7 +365,8 @@ namespace Org.BouncyCastle.Math.EC
             ECPoint p = null;
             int expectedLength = (FieldSize + 7) / 8;
 
-            switch (encoded[0])
+            byte type = encoded[0];
+            switch (type)
             {
                 case 0x00: // infinity
                 {
@@ -361,7 +383,7 @@ namespace Org.BouncyCastle.Math.EC
                     if (encoded.Length != (expectedLength + 1))
                         throw new ArgumentException("Incorrect length for compressed encoding", "encoded");
 
-                    int yTilde = encoded[0] & 1;
+                    int yTilde = type & 1;
                     BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
 
                     p = DecompressPoint(yTilde, X);
@@ -376,7 +398,7 @@ namespace Org.BouncyCastle.Math.EC
                     BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
                     BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength);
 
-                    p = CreatePoint(X, Y);
+                    p = ValidatePoint(X, Y);
                     break;
                 }
 
@@ -389,26 +411,59 @@ namespace Org.BouncyCastle.Math.EC
                     BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
                     BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength);
 
-                    if (Y.TestBit(0) != (encoded[0] == 0x07))
+                    if (Y.TestBit(0) != (type == 0x07))
                         throw new ArgumentException("Inconsistent Y coordinate in hybrid encoding", "encoded");
 
-                    p = CreatePoint(X, Y);
+                    p = ValidatePoint(X, Y);
                     break;
                 }
 
                 default:
-                    throw new FormatException("Invalid point encoding " + encoded[0]);
+                    throw new FormatException("Invalid point encoding " + type);
             }
 
+            if (type != 0x00 && p.IsInfinity)
+                throw new ArgumentException("Invalid infinity encoding", "encoded");
+
             return p;
         }
     }
 
+    public abstract class AbstractFpCurve
+        : ECCurve
+    {
+        protected AbstractFpCurve(BigInteger q)
+            : base(FiniteFields.GetPrimeField(q))
+        {
+        }
+
+        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
+        {
+            ECFieldElement x = FromBigInteger(X1);
+            ECFieldElement rhs = x.Square().Add(A).Multiply(x).Add(B);
+            ECFieldElement y = rhs.Sqrt();
+
+            /*
+             * If y is not a square, then we haven't got a point on the curve
+             */
+            if (y == null)
+                throw new ArgumentException("Invalid point compression");
+
+            if (y.TestBitZero() != (yTilde == 1))
+            {
+                // Use the other root
+                y = y.Negate();
+            }
+
+            return CreateRawPoint(x, y, true);
+        }
+    }
+
     /**
      * Elliptic curve over Fp
      */
     public class FpCurve
-        : ECCurve
+        : AbstractFpCurve
     {
         private const int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
 
@@ -421,7 +476,7 @@ namespace Org.BouncyCastle.Math.EC
         }
 
         public FpCurve(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor)
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_q = q;
             this.m_r = FpFieldElement.CalculateResidue(q);
@@ -440,7 +495,7 @@ namespace Org.BouncyCastle.Math.EC
         }
 
         protected FpCurve(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor)
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_q = q;
             this.m_r = r;
@@ -523,38 +578,11 @@ namespace Org.BouncyCastle.Math.EC
 
             return base.ImportPoint(p);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Add(m_a).Multiply(x).Add(m_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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new FpPoint(this, x, beta, true);
-        }
     }
 
-    /**
-     * 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 class F2mCurve : ECCurve
+    public abstract class AbstractF2mCurve
+        : ECCurve
     {
-        private const int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
-
         private static IFiniteField BuildField(int m, int k1, int k2, int k3)
         {
             if (k1 == 0)
@@ -585,6 +613,21 @@ namespace Org.BouncyCastle.Math.EC
             return FiniteFields.GetBinaryExtensionField(new int[]{ 0, k1, k2, k3, m });
         }
 
+        protected AbstractF2mCurve(int m, int k1, int k2, int k3)
+            : base(BuildField(m, k1, k2, k3))
+        {
+        }
+    }
+
+    /**
+     * 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 class F2mCurve
+        : AbstractF2mCurve
+    {
+        private const int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
         /**
          * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
          */
@@ -748,7 +791,7 @@ namespace Org.BouncyCastle.Math.EC
             BigInteger	b,
             BigInteger	order,
             BigInteger	cofactor)
-            : base(BuildField(m, k1, k2, k3))
+            : base(m, k1, k2, k3)
         {
             this.m = m;
             this.k1 = k1;
@@ -781,7 +824,7 @@ namespace Org.BouncyCastle.Math.EC
         }
 
         protected F2mCurve(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor)
-            : base(BuildField(m, k1, k2, k3))
+            : base(m, k1, k2, k3)
         {
             this.m = m;
             this.k1 = k1;
@@ -936,7 +979,7 @@ namespace Org.BouncyCastle.Math.EC
 
         protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
         {
-            ECFieldElement xp = FromBigInteger(X1), yp;
+            ECFieldElement xp = FromBigInteger(X1), yp = null;
             if (xp.IsZero)
             {
                 yp = m_b.Sqrt();
@@ -946,31 +989,34 @@ namespace Org.BouncyCastle.Math.EC
                 ECFieldElement beta = xp.Square().Invert().Multiply(B).Add(A).Add(xp);
                 ECFieldElement z = SolveQuadradicEquation(beta);
 
-                if (z == null)
-                    throw new ArithmeticException("Invalid point compression");
-
-                if (z.TestBitZero() != (yTilde == 1))
-                {
-                    z = z.AddOne();
-                }
-
-                switch (this.CoordinateSystem)
+                if (z != null)
                 {
-                    case COORD_LAMBDA_AFFINE:
-                    case COORD_LAMBDA_PROJECTIVE:
+                    if (z.TestBitZero() != (yTilde == 1))
                     {
-                        yp = z.Add(xp);
-                        break;
+                        z = z.AddOne();
                     }
-                    default:
+
+                    switch (this.CoordinateSystem)
                     {
-                        yp = z.Multiply(xp);
-                        break;
+                        case COORD_LAMBDA_AFFINE:
+                        case COORD_LAMBDA_PROJECTIVE:
+                        {
+                            yp = z.Add(xp);
+                            break;
+                        }
+                        default:
+                        {
+                            yp = z.Multiply(xp);
+                            break;
+                        }
                     }
                 }
             }
 
-            return new F2mPoint(this, xp, yp, true);
+            if (yp == null)
+                throw new ArgumentException("Invalid point compression");
+
+            return CreateRawPoint(xp, yp, true);
         }
 
         /**
diff --git a/crypto/src/math/ec/ECPoint.cs b/crypto/src/math/ec/ECPoint.cs
index 0430a6110..dbeaf31aa 100644
--- a/crypto/src/math/ec/ECPoint.cs
+++ b/crypto/src/math/ec/ECPoint.cs
@@ -67,6 +67,8 @@ namespace Org.BouncyCastle.Math.EC
             this.m_withCompression = withCompression;
         }
 
+        protected abstract bool SatisfiesCurveEquation();
+
         public ECPoint GetDetachedPoint()
         {
             return Normalize().Detach();
@@ -289,6 +291,30 @@ namespace Org.BouncyCastle.Math.EC
             get { return m_withCompression; }
         }
 
+        public bool IsValid()
+        {
+            if (IsInfinity)
+                return true;
+
+            // TODO Sanity-check the field elements
+
+            ECCurve curve = Curve;
+            if (curve != null)
+            {
+                if (!SatisfiesCurveEquation())
+                    return false;
+
+                BigInteger h = curve.Cofactor;
+                if (h != null && !h.Equals(BigInteger.One)
+                    && ECAlgorithms.ReferenceMultiply(this, h).IsInfinity)
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
         public virtual ECPoint ScaleX(ECFieldElement scale)
         {
             return IsInfinity
@@ -497,14 +523,84 @@ namespace Org.BouncyCastle.Math.EC
         }
     }
 
+    public abstract class AbstractFpPoint
+        : ECPointBase
+    {
+        protected AbstractFpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression)
+            : base(curve, x, y, withCompression)
+        {
+        }
+
+        protected AbstractFpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected internal override bool CompressionYTilde
+        {
+            get { return this.AffineYCoord.TestBitZero(); }
+        }
+
+        protected override bool SatisfiesCurveEquation()
+        {
+            ECFieldElement X = this.RawXCoord, Y = this.RawYCoord, A = Curve.A, B = Curve.B;
+            ECFieldElement lhs = Y.Square();
+
+            switch (CurveCoordinateSystem)
+            {
+            case ECCurve.COORD_AFFINE:
+                break;
+            case ECCurve.COORD_HOMOGENEOUS:
+            {
+                ECFieldElement Z = this.RawZCoords[0];
+                if (!Z.IsOne)
+                {
+                    ECFieldElement Z2 = Z.Square(), Z3 = Z.Multiply(Z2);
+                    lhs = lhs.Multiply(Z);
+                    A = A.Multiply(Z2);
+                    B = B.Multiply(Z3);
+                }
+                break;
+            }
+            case ECCurve.COORD_JACOBIAN:
+            case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+            case ECCurve.COORD_JACOBIAN_MODIFIED:
+            {
+                ECFieldElement Z = this.RawZCoords[0];
+                if (!Z.IsOne)
+                {
+                    ECFieldElement Z2 = Z.Square(), Z4 = Z2.Square(), Z6 = Z2.Multiply(Z4);
+                    A = A.Multiply(Z4);
+                    B = B.Multiply(Z6);
+                }
+                break;
+            }
+            default:
+                throw new InvalidOperationException("unsupported coordinate system");
+            }
+
+            ECFieldElement rhs = X.Square().Add(A).Multiply(X).Add(B);
+            return lhs.Equals(rhs);
+        }
+
+        public override ECPoint Subtract(ECPoint b)
+        {
+            if (b.IsInfinity)
+                return this;
+
+            // Add -b
+            return Add(b.Negate());
+        }
+    }
+
     /**
      * Elliptic curve points over Fp
      */
     public class FpPoint
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
-         * Create a point which encodes with point compression.
+         * Create a point which encodes without point compression.
          *
          * @param curve the curve to use
          * @param x affine x co-ordinate
@@ -516,7 +612,7 @@ namespace Org.BouncyCastle.Math.EC
         }
 
         /**
-         * Create a point that encodes with or without point compresion.
+         * Create a point that encodes with or without point compression.
          *
          * @param curve the curve to use
          * @param x affine x co-ordinate
@@ -540,11 +636,6 @@ namespace Org.BouncyCastle.Math.EC
             return new FpPoint(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECFieldElement GetZCoord(int index)
         {
             if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.CurveCoordinateSystem)
@@ -1135,16 +1226,6 @@ namespace Org.BouncyCastle.Math.EC
             return a.Add(b).Square().Subtract(aSquared).Subtract(bSquared);
         }
 
-        public override ECPoint Subtract(
-            ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            // Add -b
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
@@ -1217,11 +1298,96 @@ namespace Org.BouncyCastle.Math.EC
         }
     }
 
+    public abstract class AbstractF2mPoint 
+        : ECPointBase
+    {
+        protected AbstractF2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression)
+            : base(curve, x, y, withCompression)
+        {
+        }
+
+        protected AbstractF2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override bool SatisfiesCurveEquation()
+        {
+            ECCurve curve = Curve;
+            ECFieldElement X = this.RawXCoord, Y = this.RawYCoord, A = curve.A, B = curve.B;
+            ECFieldElement lhs, rhs;
+
+            int coord = curve.CoordinateSystem;
+            if (coord == ECCurve.COORD_LAMBDA_PROJECTIVE)
+            {
+                ECFieldElement Z = this.RawZCoords[0];
+                bool ZIsOne = Z.IsOne;
+
+                if (X.IsZero)
+                {
+                    // NOTE: For x == 0, we expect the affine-y instead of the lambda-y 
+                    lhs = Y.Square();
+                    rhs = B;
+                    if (!ZIsOne)
+                    {
+                        ECFieldElement Z2 = Z.Square();
+                        rhs = rhs.Multiply(Z2);
+                    }
+                }
+                else
+                {
+                    ECFieldElement L = Y, X2 = X.Square();
+                    if (ZIsOne)
+                    {
+                        lhs = L.Square().Add(L).Add(A);
+                        rhs = X2.Square().Add(B);
+                    }
+                    else
+                    {
+                        ECFieldElement Z2 = Z.Square(), Z4 = Z2.Square();
+                        lhs = L.Add(Z).MultiplyPlusProduct(L, A, Z2);
+                        // TODO If sqrt(b) is precomputed this can be simplified to a single square
+                        rhs = X2.SquarePlusProduct(B, Z4);
+                    }
+                    lhs = lhs.Multiply(X2);
+                }
+            }
+            else
+            {
+                lhs = Y.Add(X).Multiply(Y);
+
+                switch (coord)
+                {
+                    case ECCurve.COORD_AFFINE:
+                        break;
+                    case ECCurve.COORD_HOMOGENEOUS:
+                        {
+                            ECFieldElement Z = this.RawZCoords[0];
+                            if (!Z.IsOne)
+                            {
+                                ECFieldElement Z2 = Z.Square(), Z3 = Z.Multiply(Z2);
+                                lhs = lhs.Multiply(Z);
+                                A = A.Multiply(Z);
+                                B = B.Multiply(Z3);
+                            }
+                            break;
+                        }
+                    default:
+                        throw new InvalidOperationException("unsupported coordinate system");
+                }
+
+                rhs = X.Add(A).Multiply(X.Square()).Add(B);
+            }
+
+            return lhs.Equals(rhs);
+        }
+    }
+
     /**
      * Elliptic curve points over F2m
      */
     public class F2mPoint
-        : ECPointBase
+        : AbstractF2mPoint
     {
         /**
          * @param curve base curve
diff --git a/crypto/src/math/ec/custom/djb/Curve25519.cs b/crypto/src/math/ec/custom/djb/Curve25519.cs
index 3dbdac051..712b68f29 100644
--- a/crypto/src/math/ec/custom/djb/Curve25519.cs
+++ b/crypto/src/math/ec/custom/djb/Curve25519.cs
@@ -1,13 +1,12 @@
 using System;
 
 using Org.BouncyCastle.Math.EC.Custom.Sec;
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Djb
 {
     internal class Curve25519
-        :   ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = Nat256.ToBigInteger(Curve25519Field.P);
 
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb
         protected readonly Curve25519Point m_infinity;
 
         public Curve25519()
-            :   base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new Curve25519Point(this, null, null);
 
@@ -74,27 +73,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb
         {
             return new Curve25519Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Add(A).Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new Curve25519Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/djb/Curve25519Point.cs b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
index f3da59d16..bfec1d11d 100644
--- a/crypto/src/math/ec/custom/djb/Curve25519Point.cs
+++ b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
@@ -5,7 +5,7 @@ using Org.BouncyCastle.Math.EC.Custom.Sec;
 namespace Org.BouncyCastle.Math.EC.Custom.Djb
 {
     internal class Curve25519Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -48,11 +48,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb
             return new Curve25519Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECFieldElement GetZCoord(int index)
         {
             if (index == 1)
@@ -224,14 +219,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb
             return TwiceJacobianModified(false).Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
index 54b87588b..81f77197e 100644
--- a/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
@@ -1,12 +1,11 @@
 using System;
 
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP192K1Curve
-        : ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"));
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         protected readonly SecP192K1Point m_infinity;
 
         public SecP192K1Curve()
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new SecP192K1Point(this, null, null);
 
@@ -72,27 +71,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP192K1Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new SecP192K1Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Point.cs b/crypto/src/math/ec/custom/sec/SecP192K1Point.cs
index 561324f8e..648aca502 100644
--- a/crypto/src/math/ec/custom/sec/SecP192K1Point.cs
+++ b/crypto/src/math/ec/custom/sec/SecP192K1Point.cs
@@ -3,7 +3,7 @@
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP192K1Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -55,11 +55,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return new SecP192K1Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECPoint Add(ECPoint b)
         {
             if (this.IsInfinity)
@@ -259,14 +254,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return Twice().Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
index 57b20d31e..cb3a981c8 100644
--- a/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
@@ -1,12 +1,11 @@
 using System;
 
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP192R1Curve
-        : ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"));
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         protected readonly SecP192R1Point m_infinity;
 
         public SecP192R1Curve()
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new SecP192R1Point(this, null, null);
 
@@ -75,27 +74,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP192R1Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Add(A).Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new SecP192R1Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Point.cs b/crypto/src/math/ec/custom/sec/SecP192R1Point.cs
index c249c1269..797a8de35 100644
--- a/crypto/src/math/ec/custom/sec/SecP192R1Point.cs
+++ b/crypto/src/math/ec/custom/sec/SecP192R1Point.cs
@@ -3,7 +3,7 @@
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP192R1Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -54,11 +54,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return new SecP192R1Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECPoint Add(ECPoint b)
         {
             if (this.IsInfinity)
@@ -271,14 +266,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return Twice().Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
index 70de308bb..d4be7d8de 100644
--- a/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
@@ -1,12 +1,11 @@
 using System;
 
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP224K1Curve
-        : ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D"));
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         protected readonly SecP224K1Point m_infinity;
 
         public SecP224K1Curve()
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new SecP224K1Point(this, null, null);
 
@@ -72,27 +71,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP224K1Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new SecP224K1Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Point.cs b/crypto/src/math/ec/custom/sec/SecP224K1Point.cs
index dd6faa829..8cbd29699 100644
--- a/crypto/src/math/ec/custom/sec/SecP224K1Point.cs
+++ b/crypto/src/math/ec/custom/sec/SecP224K1Point.cs
@@ -3,7 +3,7 @@
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP224K1Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -55,11 +55,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return new SecP224K1Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECPoint Add(ECPoint b)
         {
             if (this.IsInfinity)
@@ -259,14 +254,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return Twice().Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
index 33b66be82..cda8781ff 100644
--- a/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
@@ -1,12 +1,11 @@
 using System;
 
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP224R1Curve
-        : ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"));
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         protected readonly SecP224R1Point m_infinity;
 
         public SecP224R1Curve()
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new SecP224R1Point(this, null, null);
 
@@ -75,27 +74,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP224R1Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Add(A).Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new SecP224R1Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Point.cs b/crypto/src/math/ec/custom/sec/SecP224R1Point.cs
index 3b339720d..c3f4efb59 100644
--- a/crypto/src/math/ec/custom/sec/SecP224R1Point.cs
+++ b/crypto/src/math/ec/custom/sec/SecP224R1Point.cs
@@ -3,7 +3,7 @@
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP224R1Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -54,11 +54,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return new SecP224R1Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECPoint Add(ECPoint b)
         {
             if (this.IsInfinity)
@@ -271,14 +266,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return Twice().Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
index 89de61706..59e2cefb2 100644
--- a/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
@@ -1,12 +1,11 @@
 using System;
 
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP256K1Curve
-        : ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"));
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         protected readonly SecP256K1Point m_infinity;
 
         public SecP256K1Curve()
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new SecP256K1Point(this, null, null);
 
@@ -72,27 +71,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP256K1Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new SecP256K1Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Point.cs b/crypto/src/math/ec/custom/sec/SecP256K1Point.cs
index b12eadb72..3165682fa 100644
--- a/crypto/src/math/ec/custom/sec/SecP256K1Point.cs
+++ b/crypto/src/math/ec/custom/sec/SecP256K1Point.cs
@@ -3,7 +3,7 @@
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP256K1Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -55,11 +55,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return new SecP256K1Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECPoint Add(ECPoint b)
         {
             if (this.IsInfinity)
@@ -259,14 +254,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return Twice().Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
index 9a94eb8d1..6b3448f06 100644
--- a/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
@@ -1,12 +1,11 @@
 using System;
 
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP256R1Curve
-        : ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"));
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         protected readonly SecP256R1Point m_infinity;
 
         public SecP256R1Curve()
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new SecP256R1Point(this, null, null);
 
@@ -74,27 +73,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP256R1Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Add(A).Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new SecP256R1Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Point.cs b/crypto/src/math/ec/custom/sec/SecP256R1Point.cs
index 0e4b95a10..1de4a0b4a 100644
--- a/crypto/src/math/ec/custom/sec/SecP256R1Point.cs
+++ b/crypto/src/math/ec/custom/sec/SecP256R1Point.cs
@@ -3,7 +3,7 @@
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP256R1Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -54,11 +54,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return new SecP256R1Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECPoint Add(ECPoint b)
         {
             if (this.IsInfinity)
@@ -271,14 +266,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return Twice().Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
index f3dec05c9..7fd58276a 100644
--- a/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
@@ -1,12 +1,11 @@
 using System;
 
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP384R1Curve
-        : ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF"));
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         protected readonly SecP384R1Point m_infinity;
 
         public SecP384R1Curve()
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new SecP384R1Point(this, null, null);
 
@@ -74,27 +73,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP384R1Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Add(A).Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new SecP384R1Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Point.cs b/crypto/src/math/ec/custom/sec/SecP384R1Point.cs
index 1ca8489dc..68c601611 100644
--- a/crypto/src/math/ec/custom/sec/SecP384R1Point.cs
+++ b/crypto/src/math/ec/custom/sec/SecP384R1Point.cs
@@ -3,7 +3,7 @@
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP384R1Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -54,11 +54,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return new SecP384R1Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECPoint Add(ECPoint b)
         {
             if (this.IsInfinity)
@@ -272,14 +267,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return Twice().Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
index cb42304ef..e5083c7f0 100644
--- a/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
@@ -1,12 +1,11 @@
 using System;
 
-using Org.BouncyCastle.Math.Field;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP521R1Curve
-        : ECCurve
+        : AbstractFpCurve
     {
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));
@@ -16,7 +15,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         protected readonly SecP521R1Point m_infinity;
 
         public SecP521R1Curve()
-            : base(FiniteFields.GetPrimeField(q))
+            : base(q)
         {
             this.m_infinity = new SecP521R1Point(this, null, null);
 
@@ -74,27 +73,5 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP521R1Point(this, x, y, zs, withCompression);
         }
-
-        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
-        {
-            ECFieldElement x = FromBigInteger(X1);
-            ECFieldElement alpha = x.Square().Add(A).Multiply(x).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");
-
-            if (beta.TestBitZero() != (yTilde == 1))
-            {
-                // Use the other root
-                beta = beta.Negate();
-            }
-
-            return new SecP521R1Point(this, x, beta, true);
-        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Point.cs b/crypto/src/math/ec/custom/sec/SecP521R1Point.cs
index 44d590f08..fb1996cfd 100644
--- a/crypto/src/math/ec/custom/sec/SecP521R1Point.cs
+++ b/crypto/src/math/ec/custom/sec/SecP521R1Point.cs
@@ -3,7 +3,7 @@
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
 {
     internal class SecP521R1Point
-        : ECPointBase
+        : AbstractFpPoint
     {
         /**
          * Create a point which encodes with point compression.
@@ -54,11 +54,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return new SecP521R1Point(null, AffineXCoord, AffineYCoord);
         }
 
-        protected internal override bool CompressionYTilde
-        {
-            get { return this.AffineYCoord.TestBitZero(); }
-        }
-
         public override ECPoint Add(ECPoint b)
         {
             if (this.IsInfinity)
@@ -267,14 +262,6 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             return Twice().Add(this);
         }
 
-        public override ECPoint Subtract(ECPoint b)
-        {
-            if (b.IsInfinity)
-                return this;
-
-            return Add(b.Negate());
-        }
-
         public override ECPoint Negate()
         {
             if (IsInfinity)
diff --git a/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs b/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs
index fe683726f..517881323 100644
--- a/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs
+++ b/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs
@@ -10,7 +10,13 @@
                 return p.Curve.Infinity;
 
             ECPoint positive = MultiplyPositive(p, k.Abs());
-            return sign > 0 ? positive : positive.Negate();
+            ECPoint result = sign > 0 ? positive : positive.Negate();
+
+            /*
+             * Although the various multipliers ought not to produce invalid output under normal
+             * circumstances, a final check here is advised to guard against fault attacks.
+             */
+            return ECAlgorithms.ValidatePoint(result);
         }
 
         protected abstract ECPoint MultiplyPositive(ECPoint p, BigInteger k);
diff --git a/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs b/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs
index 832fd7be4..4848ada39 100644
--- a/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs
+++ b/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs
@@ -3,35 +3,9 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
     public class ReferenceMultiplier
         : AbstractECMultiplier
     {
-        /**
-         * Simple shift-and-add multiplication. Serves as reference implementation
-         * to verify (possibly faster) implementations in
-         * {@link org.bouncycastle.math.ec.ECPoint ECPoint}.
-         * 
-         * @param p The point to multiply.
-         * @param k The factor by which to multiply.
-         * @return The result of the point multiplication <code>k * p</code>.
-         */
         protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k)
         {
-            ECPoint q = p.Curve.Infinity;
-            int t = k.BitLength;
-            if (t > 0)
-            {
-                if (k.TestBit(0))
-                {
-                    q = p;
-                }
-                for (int i = 1; i < t; i++)
-                {
-                    p = p.Twice();
-                    if (k.TestBit(i))
-                    {
-                        q = q.Add(p);
-                    }
-                }
-            }
-            return q;
+            return ECAlgorithms.ReferenceMultiply(p, k);
         }
     }
 }
diff --git a/crypto/test/src/math/ec/test/ECPointTest.cs b/crypto/test/src/math/ec/test/ECPointTest.cs
index 1a23e0bc7..8430f437d 100644
--- a/crypto/test/src/math/ec/test/ECPointTest.cs
+++ b/crypto/test/src/math/ec/test/ECPointTest.cs
@@ -288,35 +288,6 @@ namespace Org.BouncyCastle.Math.EC.Tests
         }
 
         /**
-         * Simple shift-and-add multiplication. Serves as reference implementation
-         * to verify (possibly faster) implementations in
-         * {@link org.bouncycastle.math.ec.ECPoint ECPoint}.
-         *
-         * @param p
-         *            The point to multiply.
-         * @param k
-         *            The multiplier.
-         * @return The result of the point multiplication <code>kP</code>.
-         */
-        private ECPoint Multiply(ECPoint p, BigInteger k)
-        {
-            ECPoint q = p.Curve.Infinity;
-            int t = k.BitLength;
-            for (int i = 0; i < t; i++)
-            {
-                if (i != 0)
-                {
-                    p = p.Twice();
-                }
-                if (k.TestBit(i))
-                {
-                    q = q.Add(p);
-                }
-            }
-            return q;
-        }
-
-        /**
          * Checks, if the point multiplication algorithm of the given point yields
          * the same result as point multiplication done by the reference
          * implementation given in <code>multiply()</code>. This method chooses a
@@ -331,7 +302,7 @@ namespace Org.BouncyCastle.Math.EC.Tests
         private void ImplTestMultiply(ECPoint p, int numBits)
         {
             BigInteger k = new BigInteger(numBits, secRand);
-            ECPoint reff = Multiply(p, k);
+            ECPoint reff = ECAlgorithms.ReferenceMultiply(p, k);
             ECPoint q = p.Multiply(k);
             AssertPointsEqual("ECPoint.Multiply is incorrect", reff, q);
         }
@@ -355,7 +326,7 @@ namespace Org.BouncyCastle.Math.EC.Tests
 
             do
             {
-                ECPoint reff = Multiply(p, k);
+                ECPoint reff = ECAlgorithms.ReferenceMultiply(p, k);
                 ECPoint q = p.Multiply(k);
                 AssertPointsEqual("ECPoint.Multiply is incorrect", reff, q);
                 k = k.Add(BigInteger.One);