summary refs log tree commit diff
path: root/crypto/src/math/ec/custom
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/math/ec/custom')
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519.cs77
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519Field.cs253
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs233
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519Point.cs313
-rw-r--r--crypto/src/math/ec/custom/sec/Nat192.cs962
-rw-r--r--crypto/src/math/ec/custom/sec/Nat224.cs1176
-rw-r--r--crypto/src/math/ec/custom/sec/Nat256.cs1300
-rw-r--r--crypto/src/math/ec/custom/sec/Nat384.cs46
-rw-r--r--crypto/src/math/ec/custom/sec/Nat512.cs46
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Curve.cs75
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Field.cs176
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs212
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Point.cs265
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Curve.cs78
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Field.cs281
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs187
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Point.cs277
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Curve.cs75
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Field.cs177
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs241
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Point.cs265
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Curve.cs78
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Field.cs295
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs268
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Point.cs277
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Curve.cs75
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Field.cs178
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs213
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Point.cs265
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Curve.cs77
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Field.cs309
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs187
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Point.cs277
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Curve.cs77
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Field.cs292
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs209
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Point.cs278
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Curve.cs77
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Field.cs153
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs166
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Point.cs273
41 files changed, 10739 insertions, 0 deletions
diff --git a/crypto/src/math/ec/custom/djb/Curve25519.cs b/crypto/src/math/ec/custom/djb/Curve25519.cs
new file mode 100644
index 000000000..712b68f29
--- /dev/null
+++ b/crypto/src/math/ec/custom/djb/Curve25519.cs
@@ -0,0 +1,77 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Custom.Sec;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Djb
+{
+    internal class Curve25519
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = Nat256.ToBigInteger(Curve25519Field.P);
+
+        private const int Curve25519_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
+
+        protected readonly Curve25519Point m_infinity;
+
+        public Curve25519()
+            : base(q)
+        {
+            this.m_infinity = new Curve25519Point(this, null, null);
+
+            this.m_a = FromBigInteger(new BigInteger(1,
+                Hex.Decode("2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144")));
+            this.m_b = FromBigInteger(new BigInteger(1,
+                Hex.Decode("7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864")));
+            this.m_order = new BigInteger(1, Hex.Decode("1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"));
+            this.m_cofactor = BigInteger.ValueOf(8);
+            this.m_coord = Curve25519_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new Curve25519();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+            case COORD_JACOBIAN_MODIFIED:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new Curve25519FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new Curve25519Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new Curve25519Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/djb/Curve25519Field.cs b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
new file mode 100644
index 000000000..809e51b80
--- /dev/null
+++ b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Math.EC.Custom.Sec;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Djb
+{
+    internal class Curve25519Field
+    {
+        // 2^255 - 2^4 - 2^1 - 1
+        internal static readonly uint[] P = new uint[]{ 0xFFFFFFED, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF, 0x7FFFFFFF };
+        private const uint P7 = 0x7FFFFFFF;
+        private static readonly uint[] PExt = new uint[]{ 0x00000169, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+            0x00000000, 0x00000000, 0x00000000, 0xFFFFFFED, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF, 0x3FFFFFFF };
+        private const uint PInv = 0x13;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            Nat256.Add(x, y, z);
+            if (Nat256.Gte(z, P))
+            {
+                SubPFrom(z);
+            }
+        }
+
+        public static void AddExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            Nat.Add(16, xx, yy, zz);
+            if (Nat.Gte(16, zz, PExt))
+            {
+                SubPExtFrom(zz);
+            }
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            Nat.Inc(8, x, z);
+            if (Nat256.Gte(z, P))
+            {
+                SubPFrom(z);
+            }
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat256.FromBigInteger(x);
+            while (Nat256.Gte(z, P))
+            {
+                Nat256.SubFrom(P, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            if ((x[0] & 1) == 0)
+            {
+                Nat.ShiftDownBit(8, x, 0, z);
+            }
+            else
+            {
+                Nat256.Add(x, P, z);
+                Nat.ShiftDownBit(8, z, 0);
+            }
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Mul(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz)
+        {
+            Nat256.MulAddTo(x, y, zz);
+            if (Nat.Gte(16, zz, PExt))
+            {
+                SubPExtFrom(zz);
+            }
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat256.IsZero(x))
+            {
+                Nat256.Zero(z);
+            }
+            else
+            {
+                Nat256.Sub(P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            Debug.Assert(xx[15] >> 30 == 0);
+
+            uint xx07 = xx[7];
+            Nat.ShiftUpBit(8, xx, 8, xx07, z, 0);
+            uint c = Nat256.MulByWordAddTo(PInv, xx, z) << 1;
+            uint z7 = z[7];
+            c += (z7 >> 31) - (xx07 >> 31);
+            z7 &= P7;
+            z7 += Nat.AddWordTo(7, c * PInv, z);
+            z[7] = z7;
+            if (z7 >= P7 && Nat256.Gte(z, P))
+            {
+                SubPFrom(z);
+            }
+        }
+
+        public static void Reduce27(uint x, uint[] z)
+        {
+            Debug.Assert(x >> 26 == 0);
+
+            uint z7 = z[7];
+            uint c = (x << 1 | z7 >> 31);
+            z7 &= P7;
+            z7 += Nat.AddWordTo(7, c * PInv, z);
+            z[7] = z7;
+            if (z7 >= P7 && Nat256.Gte(z, P))
+            {
+                SubPFrom(z);
+            }
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Square(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Square(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                Nat256.Square(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat256.Sub(x, y, z);
+            if (c != 0)
+            {
+                AddPTo(z);
+            }
+        }
+
+        public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            int c = Nat.Sub(16, xx, yy, zz);
+            if (c != 0)
+            {
+                AddPExtTo(zz);
+            }
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            Nat.ShiftUpBit(8, x, 0, z);
+            if (Nat256.Gte(z, P))
+            {
+                SubPFrom(z);
+            }
+        }
+
+        private static uint AddPTo(uint[] z)
+        {
+            long c = (long)z[0] - PInv;
+            z[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c = Nat.DecAt(7, z, 1);
+            }
+            c += (long)z[7] + (P7 + 1);
+            z[7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        private static uint AddPExtTo(uint[] zz)
+        {
+            long c = (long)zz[0] + PExt[0];
+            zz[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c = Nat.IncAt(8, zz, 1);
+            }
+            c += (long)zz[8] - PInv;
+            zz[8] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c = Nat.DecAt(15, zz, 9);
+            }
+            c += (long)zz[15] + (PExt[15] + 1);
+            zz[15] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        private static int SubPFrom(uint[] z)
+        {
+            long c = (long)z[0] + PInv;
+            z[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c = Nat.IncAt(7, z, 1);
+            }
+            c += (long)z[7] - (P7 + 1);
+            z[7] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        private static int SubPExtFrom(uint[] zz)
+        {
+            long c = (long)zz[0] - PExt[0];
+            zz[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c = Nat.DecAt(8, zz, 1);
+            }
+            c += (long)zz[8] + PInv;
+            zz[8] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c = Nat.IncAt(15, zz, 9);
+            }
+            c += (long)zz[15] - (PExt[15] + 1);
+            zz[15] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs b/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs
new file mode 100644
index 000000000..8d5a80326
--- /dev/null
+++ b/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs
@@ -0,0 +1,233 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Custom.Sec;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Djb
+{
+    internal class Curve25519FieldElement
+        :   ECFieldElement
+    {
+        public static readonly BigInteger Q = Curve25519.q;
+
+        // Calculated as ECConstants.TWO.modPow(Q.shiftRight(2), Q)
+        private static readonly uint[] PRECOMP_POW2 = new uint[]{ 0x4a0ea0b0, 0xc4ee1b27, 0xad2fe478, 0x2f431806,
+            0x3dfbd7a7, 0x2b4d0099, 0x4fc1df0b, 0x2b832480 };
+
+        protected internal readonly uint[] x;
+
+        public Curve25519FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for Curve25519FieldElement", "x");
+
+            this.x = Curve25519Field.FromBigInteger(x);
+        }
+
+        public Curve25519FieldElement()
+        {
+            this.x = Nat256.Create();
+        }
+
+        protected internal Curve25519FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat256.IsZero(x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat256.IsOne(x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat256.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat256.ToBigInteger(x);
+        }
+
+        public override string FieldName
+        {
+            get { return "Curve25519Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            Curve25519Field.Add(x, ((Curve25519FieldElement)b).x, z);
+            return new Curve25519FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat256.Create();
+            Curve25519Field.AddOne(x, z);
+            return new Curve25519FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            Curve25519Field.Subtract(x, ((Curve25519FieldElement)b).x, z);
+            return new Curve25519FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            Curve25519Field.Multiply(x, ((Curve25519FieldElement)b).x, z);
+            return new Curve25519FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat256.Create();
+            Mod.Invert(Curve25519Field.P, ((Curve25519FieldElement)b).x, z);
+            Curve25519Field.Multiply(z, x, z);
+            return new Curve25519FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat256.Create();
+            Curve25519Field.Negate(x, z);
+            return new Curve25519FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat256.Create();
+            Curve25519Field.Square(x, z);
+            return new Curve25519FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new Curve25519FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat256.Create();
+            Mod.Invert(Curve25519Field.P, x, z);
+            return new Curve25519FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            /*
+             * Q == 8m + 5, so we use Pocklington's method for this case.
+             *
+             * First, raise this element to the exponent 2^252 - 2^1 (i.e. m + 1)
+             * 
+             * Breaking up the exponent's binary representation into "repunits", we get:
+             * { 251 1s } { 1 0s }
+             * 
+             * Therefore we need an addition chain containing 251 (the lengths of the repunits)
+             * We use: 1, 2, 3, 4, 7, 11, 15, 30, 60, 120, 131, [251]
+             */
+
+            uint[] x1 = this.x;
+            if (Nat256.IsZero(x1) || Nat256.IsOne(x1))
+                return this;
+
+            uint[] x2 = Nat256.Create();
+            Curve25519Field.Square(x1, x2);
+            Curve25519Field.Multiply(x2, x1, x2);
+            uint[] x3 = x2;
+            Curve25519Field.Square(x2, x3);
+            Curve25519Field.Multiply(x3, x1, x3);
+            uint[] x4 = Nat256.Create();
+            Curve25519Field.Square(x3, x4);
+            Curve25519Field.Multiply(x4, x1, x4);
+            uint[] x7 = Nat256.Create();
+            Curve25519Field.SquareN(x4, 3, x7);
+            Curve25519Field.Multiply(x7, x3, x7);
+            uint[] x11 = x3;
+            Curve25519Field.SquareN(x7, 4, x11);
+            Curve25519Field.Multiply(x11, x4, x11);
+            uint[] x15 = x7;
+            Curve25519Field.SquareN(x11, 4, x15);
+            Curve25519Field.Multiply(x15, x4, x15);
+            uint[] x30 = x4;
+            Curve25519Field.SquareN(x15, 15, x30);
+            Curve25519Field.Multiply(x30, x15, x30);
+            uint[] x60 = x15;
+            Curve25519Field.SquareN(x30, 30, x60);
+            Curve25519Field.Multiply(x60, x30, x60);
+            uint[] x120 = x30;
+            Curve25519Field.SquareN(x60, 60, x120);
+            Curve25519Field.Multiply(x120, x60, x120);
+            uint[] x131 = x60;
+            Curve25519Field.SquareN(x120, 11, x131);
+            Curve25519Field.Multiply(x131, x11, x131);
+            uint[] x251 = x11;
+            Curve25519Field.SquareN(x131, 120, x251);
+            Curve25519Field.Multiply(x251, x120, x251);
+
+            uint[] t1 = x251;
+            Curve25519Field.Square(t1, t1);
+
+            uint[] t2 = x120;
+            Curve25519Field.Square(t1, t2);
+
+            if (Nat256.Eq(x1, t2))
+            {
+                return new Curve25519FieldElement(t1);
+            }
+
+            /*
+             * If the first guess is incorrect, we multiply by a precomputed power of 2 to get the second guess,
+             * which is ((4x)^(m + 1))/2 mod Q
+             */
+            Curve25519Field.Multiply(t1, PRECOMP_POW2, t1);
+
+            Curve25519Field.Square(t1, t2);
+
+            if (Nat256.Eq(x1, t2))
+            {
+                return new Curve25519FieldElement(t1);
+            }
+
+            return null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as Curve25519FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as Curve25519FieldElement);
+        }
+
+        public virtual bool Equals(Curve25519FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat256.Eq(x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 8);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/djb/Curve25519Point.cs b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
new file mode 100644
index 000000000..bfec1d11d
--- /dev/null
+++ b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
@@ -0,0 +1,313 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Custom.Sec;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Djb
+{
+    internal class Curve25519Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.CreatePoint to construct points
+         */
+        public Curve25519Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)}
+         */
+        public Curve25519Point(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");
+        }
+
+        internal Curve25519Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new Curve25519Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECFieldElement GetZCoord(int index)
+        {
+            if (index == 1)
+            {
+                return GetJacobianModifiedW();
+            }
+
+            return base.GetZCoord(index);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            Curve25519FieldElement X1 = (Curve25519FieldElement)this.RawXCoord, Y1 = (Curve25519FieldElement)this.RawYCoord,
+                Z1 = (Curve25519FieldElement)this.RawZCoords[0];
+            Curve25519FieldElement X2 = (Curve25519FieldElement)b.RawXCoord, Y2 = (Curve25519FieldElement)b.RawYCoord,
+                Z2 = (Curve25519FieldElement)b.RawZCoords[0];
+
+            uint c;
+            uint[] tt1 = Nat256.CreateExt();
+            uint[] t2 = Nat256.Create();
+            uint[] t3 = Nat256.Create();
+            uint[] t4 = Nat256.Create();
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                Curve25519Field.Square(Z1.x, S2);
+
+                U2 = t2;
+                Curve25519Field.Multiply(S2, X2.x, U2);
+
+                Curve25519Field.Multiply(S2, Z1.x, S2);
+                Curve25519Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                Curve25519Field.Square(Z2.x, S1);
+
+                U1 = tt1;
+                Curve25519Field.Multiply(S1, X1.x, U1);
+
+                Curve25519Field.Multiply(S1, Z2.x, S1);
+                Curve25519Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat256.Create();
+            Curve25519Field.Subtract(U1, U2, H);
+
+            uint[] R = t2;
+            Curve25519Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat256.IsZero(H))
+            {
+                if (Nat256.IsZero(R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = Nat256.Create();
+            Curve25519Field.Square(H, HSquared);
+
+            uint[] G = Nat256.Create();
+            Curve25519Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            Curve25519Field.Multiply(HSquared, U1, V);
+
+            Curve25519Field.Negate(G, G);
+            Nat256.Mul(S1, G, tt1);
+
+            c = Nat256.AddBothTo(V, V, G);
+            Curve25519Field.Reduce27(c, G);
+
+            Curve25519FieldElement X3 = new Curve25519FieldElement(t4);
+            Curve25519Field.Square(R, X3.x);
+            Curve25519Field.Subtract(X3.x, G, X3.x);
+
+            Curve25519FieldElement Y3 = new Curve25519FieldElement(G);
+            Curve25519Field.Subtract(V, X3.x, Y3.x);
+            Curve25519Field.MultiplyAddToExt(Y3.x, R, tt1);
+            Curve25519Field.Reduce(tt1, Y3.x);
+
+            Curve25519FieldElement Z3 = new Curve25519FieldElement(H);
+            if (!Z1IsOne)
+            {
+                Curve25519Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                Curve25519Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            uint[] Z3Squared = (Z1IsOne && Z2IsOne) ? HSquared : null;
+
+            // TODO If the result will only be used in a subsequent addition, we don't need W3
+            Curve25519FieldElement W3 = CalculateJacobianModifiedW((Curve25519FieldElement)Z3, Z3Squared);
+
+            ECFieldElement[] zs = new ECFieldElement[] { Z3, W3 };
+
+            return new Curve25519Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            ECFieldElement Y1 = this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            return TwiceJacobianModified(true);
+        }
+
+        public override ECPoint TwicePlus(ECPoint b)
+        {
+            if (this == b)
+                return ThreeTimes();
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return Twice();
+
+            ECFieldElement Y1 = this.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return TwiceJacobianModified(false).Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            return TwiceJacobianModified(false).Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new Curve25519Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+
+        protected virtual Curve25519FieldElement CalculateJacobianModifiedW(Curve25519FieldElement Z, uint[] ZSquared)
+        {
+            Curve25519FieldElement a4 = (Curve25519FieldElement)this.Curve.A;
+            if (Z.IsOne)
+                return a4;
+
+            Curve25519FieldElement W = new Curve25519FieldElement();
+            if (ZSquared == null)
+            {
+                ZSquared = W.x;
+                Curve25519Field.Square(Z.x, ZSquared);
+            }
+            Curve25519Field.Square(ZSquared, W.x);
+            Curve25519Field.Multiply(W.x, a4.x, W.x);
+            return W;
+        }
+
+        protected virtual Curve25519FieldElement GetJacobianModifiedW()
+        {
+            ECFieldElement[] ZZ = this.RawZCoords;
+            Curve25519FieldElement W = (Curve25519FieldElement)ZZ[1];
+            if (W == null)
+            {
+                // NOTE: Rarely, TwicePlus will result in the need for a lazy W1 calculation here
+                ZZ[1] = W = CalculateJacobianModifiedW((Curve25519FieldElement)ZZ[0], null);
+            }
+            return W;
+        }
+
+        protected virtual Curve25519Point TwiceJacobianModified(bool calculateW)
+        {
+            Curve25519FieldElement X1 = (Curve25519FieldElement)this.RawXCoord, Y1 = (Curve25519FieldElement)this.RawYCoord,
+                Z1 = (Curve25519FieldElement)this.RawZCoords[0], W1 = GetJacobianModifiedW();
+
+            uint c;
+
+            uint[] M = Nat256.Create();
+            Curve25519Field.Square(X1.x, M);
+            c = Nat256.AddBothTo(M, M, M);
+            c += Nat256.AddTo(W1.x, M);
+            Curve25519Field.Reduce27(c, M);
+
+            uint[] _2Y1 = Nat256.Create();
+            Curve25519Field.Twice(Y1.x, _2Y1);
+
+            uint[] _2Y1Squared = Nat256.Create();
+            Curve25519Field.Multiply(_2Y1, Y1.x, _2Y1Squared);
+
+            uint[] S = Nat256.Create();
+            Curve25519Field.Multiply(_2Y1Squared, X1.x, S);
+            Curve25519Field.Twice(S, S);
+
+            uint[] _8T = Nat256.Create();
+            Curve25519Field.Square(_2Y1Squared, _8T);
+            Curve25519Field.Twice(_8T, _8T);
+
+            Curve25519FieldElement X3 = new Curve25519FieldElement(_2Y1Squared);
+            Curve25519Field.Square(M, X3.x);
+            Curve25519Field.Subtract(X3.x, S, X3.x);
+            Curve25519Field.Subtract(X3.x, S, X3.x);
+
+            Curve25519FieldElement Y3 = new Curve25519FieldElement(S);
+            Curve25519Field.Subtract(S, X3.x, Y3.x);
+            Curve25519Field.Multiply(Y3.x, M, Y3.x);
+            Curve25519Field.Subtract(Y3.x, _8T, Y3.x);
+
+            Curve25519FieldElement Z3 = new Curve25519FieldElement(_2Y1);
+            if (!Nat256.IsOne(Z1.x))
+            {
+                Curve25519Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            Curve25519FieldElement W3 = null;
+            if (calculateW)
+            {
+                W3 = new Curve25519FieldElement(_8T);
+                Curve25519Field.Multiply(W3.x, W1.x, W3.x);
+                Curve25519Field.Twice(W3.x, W3.x);
+            }
+
+            return new Curve25519Point(this.Curve, X3, Y3, new ECFieldElement[] { Z3, W3 }, IsCompressed);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/Nat192.cs b/crypto/src/math/ec/custom/sec/Nat192.cs
new file mode 100644
index 000000000..94d7ed17c
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/Nat192.cs
@@ -0,0 +1,962 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal abstract class Nat192
+    {
+        private const ulong M = 0xFFFFFFFFUL;
+
+        public static uint Add(uint[] x, uint[] y, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddBothTo(uint[] x, uint[] y, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + y[0] + z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + y[1] + z[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + y[2] + z[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + y[3] + z[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + y[4] + z[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + y[5] + z[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddTo(uint[] x, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + z[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + z[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + z[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + z[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + z[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddTo(uint[] x, int xOff, uint[] z, int zOff, uint cIn)
+        {
+            ulong c = cIn;
+            c += (ulong)x[xOff + 0] + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 1] + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 2] + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 3] + z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 4] + z[zOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 5] + z[zOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddToEachOther(uint[] u, int uOff, uint[] v, int vOff)
+        {
+            ulong c = 0;
+            c += (ulong)u[uOff + 0] + v[vOff + 0];
+            u[uOff + 0] = (uint)c;
+            v[vOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 1] + v[vOff + 1];
+            u[uOff + 1] = (uint)c;
+            v[vOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 2] + v[vOff + 2];
+            u[uOff + 2] = (uint)c;
+            v[vOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 3] + v[vOff + 3];
+            u[uOff + 3] = (uint)c;
+            v[vOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 4] + v[vOff + 4];
+            u[uOff + 4] = (uint)c;
+            v[vOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 5] + v[vOff + 5];
+            u[uOff + 5] = (uint)c;
+            v[vOff + 5] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static void Copy(uint[] x, uint[] z)
+        {
+            z[0] = x[0];
+            z[1] = x[1];
+            z[2] = x[2];
+            z[3] = x[3];
+            z[4] = x[4];
+            z[5] = x[5];
+        }
+
+        public static uint[] Create()
+        {
+            return new uint[6];
+        }
+
+        public static uint[] CreateExt()
+        {
+            return new uint[12];
+        }
+
+        public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            bool pos = Gte(x, xOff, y, yOff);
+            if (pos)
+            {
+                Sub(x, xOff, y, yOff, z, zOff);
+            }
+            else
+            {
+                Sub(y, yOff, x, xOff, z, zOff);
+            }
+            return pos;
+        }
+
+        public static bool Eq(uint[] x, uint[] y)
+        {
+            for (int i = 5; i >= 0; --i)
+            {
+                if (x[i] != y[i])
+                    return false;
+            }
+            return true;
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            if (x.SignValue < 0 || x.BitLength > 192)
+                throw new ArgumentException();
+
+            uint[] z = Create();
+            int i = 0;
+            while (x.SignValue != 0)
+            {
+                z[i++] = (uint)x.IntValue;
+                x = x.ShiftRight(32);
+            }
+            return z;
+        }
+
+        public static uint GetBit(uint[] x, int bit)
+        {
+            if (bit == 0)
+            {
+                return x[0] & 1;
+            }
+            int w = bit >> 5;
+            if (w < 0 || w >= 6)
+            {
+                return 0;
+            }
+            int b = bit & 31;
+            return (x[w] >> b) & 1;
+        }
+
+        public static bool Gte(uint[] x, uint[] y)
+        {
+            for (int i = 5; i >= 0; --i)
+            {
+                uint x_i = x[i], y_i = y[i];
+                if (x_i < y_i)
+                    return false;
+                if (x_i > y_i)
+                    return true;
+            }
+            return true;
+        }
+
+        public static bool Gte(uint[] x, int xOff, uint[] y, int yOff)
+        {
+            for (int i = 5; i >= 0; --i)
+            {
+                uint x_i = x[xOff + i], y_i = y[yOff + i];
+                if (x_i < y_i)
+                    return false;
+                if (x_i > y_i)
+                    return true;
+            }
+            return true;
+        }
+
+        public static bool IsOne(uint[] x)
+        {
+            if (x[0] != 1)
+            {
+                return false;
+            }
+            for (int i = 1; i < 6; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static bool IsZero(uint[] x)
+        {
+            for (int i = 0; i < 6; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static void Mul(uint[] x, uint[] y, uint[] zz)
+        {
+            ulong y_0 = y[0];
+            ulong y_1 = y[1];
+            ulong y_2 = y[2];
+            ulong y_3 = y[3];
+            ulong y_4 = y[4];
+            ulong y_5 = y[5];
+
+            {
+                ulong c = 0, x_0 = x[0];
+                c += x_0 * y_0;
+                zz[0] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_1;
+                zz[1] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_2;
+                zz[2] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_3;
+                zz[3] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_4;
+                zz[4] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_5;
+                zz[5] = (uint)c;
+                c >>= 32;
+                zz[6] = (uint)c;
+            }
+
+            for (int i = 1; i < 6; ++i)
+            {
+                ulong c = 0, x_i = x[i];
+                c += x_i * y_0 + zz[i + 0];
+                zz[i + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[i + 1];
+                zz[i + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[i + 2];
+                zz[i + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[i + 3];
+                zz[i + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[i + 4];
+                zz[i + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[i + 5];
+                zz[i + 5] = (uint)c;
+                c >>= 32;
+                zz[i + 6] = (uint)c;
+            }
+        }
+
+        public static void Mul(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
+        {
+            ulong y_0 = y[yOff + 0];
+            ulong y_1 = y[yOff + 1];
+            ulong y_2 = y[yOff + 2];
+            ulong y_3 = y[yOff + 3];
+            ulong y_4 = y[yOff + 4];
+            ulong y_5 = y[yOff + 5];
+
+            {
+                ulong c = 0, x_0 = x[xOff + 0];
+                c += x_0 * y_0;
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_1;
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_2;
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_3;
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_4;
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_5;
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                zz[zzOff + 6] = (uint)c;
+            }
+
+            for (int i = 1; i < 6; ++i)
+            {
+                ++zzOff;
+                ulong c = 0, x_i = x[xOff + i];
+                c += x_i * y_0 + zz[zzOff + 0];
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[zzOff + 1];
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[zzOff + 2];
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[zzOff + 3];
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[zzOff + 4];
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[zzOff + 5];
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                zz[zzOff + 6] = (uint)c;
+            }
+        }
+
+        public static uint MulAddTo(uint[] x, uint[] y, uint[] zz)
+        {
+            ulong y_0 = y[0];
+            ulong y_1 = y[1];
+            ulong y_2 = y[2];
+            ulong y_3 = y[3];
+            ulong y_4 = y[4];
+            ulong y_5 = y[5];
+
+            ulong zc = 0;
+            for (int i = 0; i < 6; ++i)
+            {
+                ulong c = 0, x_i = x[i];
+                c += x_i * y_0 + zz[i + 0];
+                zz[i + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[i + 1];
+                zz[i + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[i + 2];
+                zz[i + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[i + 3];
+                zz[i + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[i + 4];
+                zz[i + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[i + 5];
+                zz[i + 5] = (uint)c;
+                c >>= 32;
+                c += zc + zz[i + 6];
+                zz[i + 6] = (uint)c;
+                zc = c >> 32;
+            }
+            return (uint)zc;
+        }
+
+        public static uint MulAddTo(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
+        {
+            ulong y_0 = y[yOff + 0];
+            ulong y_1 = y[yOff + 1];
+            ulong y_2 = y[yOff + 2];
+            ulong y_3 = y[yOff + 3];
+            ulong y_4 = y[yOff + 4];
+            ulong y_5 = y[yOff + 5];
+
+            ulong zc = 0;
+            for (int i = 0; i < 6; ++i)
+            {
+                ulong c = 0, x_i = x[xOff + i];
+                c += x_i * y_0 + zz[zzOff + 0];
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[zzOff + 1];
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[zzOff + 2];
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[zzOff + 3];
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[zzOff + 4];
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[zzOff + 5];
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                c += zc + zz[zzOff + 6];
+                zz[zzOff + 6] = (uint)c;
+                zc = c >> 32;
+                ++zzOff;
+            }
+            return (uint)zc;
+        }
+
+        public static ulong Mul33Add(uint w, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            Debug.Assert(w >> 31 == 0);
+
+            ulong c = 0, wVal = w;
+            ulong x0 = x[xOff + 0];
+            c += wVal * x0 + y[yOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            ulong x1 = x[xOff + 1];
+            c += wVal * x1 + x0 + y[yOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            ulong x2 = x[xOff + 2];
+            c += wVal * x2 + x1 + y[yOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            ulong x3 = x[xOff + 3];
+            c += wVal * x3 + x2 + y[yOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            ulong x4 = x[xOff + 4];
+            c += wVal * x4 + x3 + y[yOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            ulong x5 = x[xOff + 5];
+            c += wVal * x5 + x4 + y[yOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += x5;
+            return c;
+        }
+
+        public static uint MulWordAddExt(uint x, uint[] yy, int yyOff, uint[] zz, int zzOff)
+        {
+            Debug.Assert(yyOff <= 6);
+            Debug.Assert(zzOff <= 6);
+            ulong c = 0, xVal = x;
+            c += xVal * yy[yyOff + 0] + zz[zzOff + 0];
+            zz[zzOff + 0] = (uint)c;
+            c >>= 32;
+            c += xVal * yy[yyOff + 1] + zz[zzOff + 1];
+            zz[zzOff + 1] = (uint)c;
+            c >>= 32;
+            c += xVal * yy[yyOff + 2] + zz[zzOff + 2];
+            zz[zzOff + 2] = (uint)c;
+            c >>= 32;
+            c += xVal * yy[yyOff + 3] + zz[zzOff + 3];
+            zz[zzOff + 3] = (uint)c;
+            c >>= 32;
+            c += xVal * yy[yyOff + 4] + zz[zzOff + 4];
+            zz[zzOff + 4] = (uint)c;
+            c >>= 32;
+            c += xVal * yy[yyOff + 5] + zz[zzOff + 5];
+            zz[zzOff + 5] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint Mul33DWordAdd(uint x, ulong y, uint[] z, int zOff)
+        {
+            Debug.Assert(x >> 31 == 0);
+            Debug.Assert(zOff <= 2);
+            ulong c = 0, xVal = x;
+            ulong y00 = y & M;
+            c += xVal * y00 + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            ulong y01 = y >> 32;
+            c += xVal * y01 + y00 + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += y01 + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(6, z, zOff, 4);
+        }
+
+        public static uint Mul33WordAdd(uint x, uint y, uint[] z, int zOff)
+        {
+            Debug.Assert(x >> 31 == 0);
+            Debug.Assert(zOff <=3);
+            ulong c = 0, yVal = y;
+            c += yVal * x + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += yVal + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(6, z, zOff, 3);
+        }
+
+        public static uint MulWordDwordAdd(uint x, ulong y, uint[] z, int zOff)
+        {
+            Debug.Assert(zOff <= 3);
+            ulong c = 0, xVal = x;
+            c += xVal * y + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += xVal * (y >> 32) + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(6, z, zOff, 3);
+        }
+
+        public static uint MulWord(uint x, uint[] y, uint[] z, int zOff)
+        {
+            ulong c = 0, xVal = x;
+            int i = 0;
+            do
+            {
+                c += xVal * y[i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            while (++i < 6);
+            return (uint)c;
+        }
+
+        public static void Square(uint[] x, uint[] zz)
+        {
+            ulong x_0 = x[0];
+            ulong zz_1;
+
+            uint c = 0, w;
+            {
+                int i = 5, j = 12;
+                do
+                {
+                    ulong xVal = x[i--];
+                    ulong p = xVal * xVal;
+                    zz[--j] = (c << 31) | (uint)(p >> 33);
+                    zz[--j] = (uint)(p >> 1);
+                    c = (uint)p;
+                }
+                while (i > 0);
+
+                {
+                    ulong p = x_0 * x_0;
+                    zz_1 = (ulong)(c << 31) | (p >> 33);
+                    zz[0] = (uint)p;
+                    c = (uint)(p >> 32) & 1;
+                }
+            }
+
+            ulong x_1 = x[1];
+            ulong zz_2 = zz[2];
+
+            {
+                zz_1 += x_1 * x_0;
+                w = (uint)zz_1;
+                zz[1] = (w << 1) | c;
+                c = w >> 31;
+                zz_2 += zz_1 >> 32;
+            }
+
+            ulong x_2 = x[2];
+            ulong zz_3 = zz[3];
+            ulong zz_4 = zz[4];
+            {
+                zz_2 += x_2 * x_0;
+                w = (uint)zz_2;
+                zz[2] = (w << 1) | c;
+                c = w >> 31;
+                zz_3 += (zz_2 >> 32) + x_2 * x_1;
+                zz_4 += zz_3 >> 32;
+                zz_3 &= M;
+            }
+
+            ulong x_3 = x[3];
+            ulong zz_5 = zz[5];
+            ulong zz_6 = zz[6];
+            {
+                zz_3 += x_3 * x_0;
+                w = (uint)zz_3;
+                zz[3] = (w << 1) | c;
+                c = w >> 31;
+                zz_4 += (zz_3 >> 32) + x_3 * x_1;
+                zz_5 += (zz_4 >> 32) + x_3 * x_2;
+                zz_4 &= M;
+                zz_6 += zz_5 >> 32;
+                zz_5 &= M;
+            }
+
+            ulong x_4 = x[4];
+            ulong zz_7 = zz[7];
+            ulong zz_8 = zz[8];
+            {
+                zz_4 += x_4 * x_0;
+                w = (uint)zz_4;
+                zz[4] = (w << 1) | c;
+                c = w >> 31;
+                zz_5 += (zz_4 >> 32) + x_4 * x_1;
+                zz_6 += (zz_5 >> 32) + x_4 * x_2;
+                zz_5 &= M;
+                zz_7 += (zz_6 >> 32) + x_4 * x_3;
+                zz_6 &= M;
+                zz_8 += zz_7 >> 32;
+                zz_7 &= M;
+            }
+
+            ulong x_5 = x[5];
+            ulong zz_9 = zz[9];
+            ulong zz_10 = zz[10];
+            {
+                zz_5 += x_5 * x_0;
+                w = (uint)zz_5;
+                zz[5] = (w << 1) | c;
+                c = w >> 31;
+                zz_6 += (zz_5 >> 32) + x_5 * x_1;
+                zz_7 += (zz_6 >> 32) + x_5 * x_2;
+                zz_8 += (zz_7 >> 32) + x_5 * x_3;
+                zz_9 += (zz_8 >> 32) + x_5 * x_4;
+                zz_10 += zz_9 >> 32;
+            }
+
+            w = (uint)zz_6;
+            zz[6] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_7;
+            zz[7] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_8;
+            zz[8] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_9;
+            zz[9] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_10;
+            zz[10] = (w << 1) | c;
+            c = w >> 31;
+            w = zz[11] + (uint)(zz_10 >> 32);
+            zz[11] = (w << 1) | c;
+        }
+
+        public static void Square(uint[] x, int xOff, uint[] zz, int zzOff)
+        {
+            ulong x_0 = x[xOff + 0];
+            ulong zz_1;
+
+            uint c = 0, w;
+            {
+                int i = 5, j = 12;
+                do
+                {
+                    ulong xVal = x[xOff + i--];
+                    ulong p = xVal * xVal;
+                    zz[zzOff + --j] = (c << 31) | (uint)(p >> 33);
+                    zz[zzOff + --j] = (uint)(p >> 1);
+                    c = (uint)p;
+                }
+                while (i > 0);
+
+                {
+                    ulong p = x_0 * x_0;
+                    zz_1 = (ulong)(c << 31) | (p >> 33);
+                    zz[zzOff + 0] = (uint)p;
+                    c = (uint)(p >> 32) & 1;
+                }
+            }
+
+            ulong x_1 = x[xOff + 1];
+            ulong zz_2 = zz[zzOff + 2];
+
+            {
+                zz_1 += x_1 * x_0;
+                w = (uint)zz_1;
+                zz[zzOff + 1] = (w << 1) | c;
+                c = w >> 31;
+                zz_2 += zz_1 >> 32;
+            }
+
+            ulong x_2 = x[xOff + 2];
+            ulong zz_3 = zz[zzOff + 3];
+            ulong zz_4 = zz[zzOff + 4];
+            {
+                zz_2 += x_2 * x_0;
+                w = (uint)zz_2;
+                zz[zzOff + 2] = (w << 1) | c;
+                c = w >> 31;
+                zz_3 += (zz_2 >> 32) + x_2 * x_1;
+                zz_4 += zz_3 >> 32;
+                zz_3 &= M;
+            }
+
+            ulong x_3 = x[xOff + 3];
+            ulong zz_5 = zz[zzOff + 5];
+            ulong zz_6 = zz[zzOff + 6];
+            {
+                zz_3 += x_3 * x_0;
+                w = (uint)zz_3;
+                zz[zzOff + 3] = (w << 1) | c;
+                c = w >> 31;
+                zz_4 += (zz_3 >> 32) + x_3 * x_1;
+                zz_5 += (zz_4 >> 32) + x_3 * x_2;
+                zz_4 &= M;
+                zz_6 += zz_5 >> 32;
+                zz_5 &= M;
+            }
+
+            ulong x_4 = x[xOff + 4];
+            ulong zz_7 = zz[zzOff + 7];
+            ulong zz_8 = zz[zzOff + 8];
+            {
+                zz_4 += x_4 * x_0;
+                w = (uint)zz_4;
+                zz[zzOff + 4] = (w << 1) | c;
+                c = w >> 31;
+                zz_5 += (zz_4 >> 32) + x_4 * x_1;
+                zz_6 += (zz_5 >> 32) + x_4 * x_2;
+                zz_5 &= M;
+                zz_7 += (zz_6 >> 32) + x_4 * x_3;
+                zz_6 &= M;
+                zz_8 += zz_7 >> 32;
+                zz_7 &= M;
+            }
+
+            ulong x_5 = x[xOff + 5];
+            ulong zz_9 = zz[zzOff + 9];
+            ulong zz_10 = zz[zzOff + 10];
+            {
+                zz_5 += x_5 * x_0;
+                w = (uint)zz_5;
+                zz[zzOff + 5] = (w << 1) | c;
+                c = w >> 31;
+                zz_6 += (zz_5 >> 32) + x_5 * x_1;
+                zz_7 += (zz_6 >> 32) + x_5 * x_2;
+                zz_8 += (zz_7 >> 32) + x_5 * x_3;
+                zz_9 += (zz_8 >> 32) + x_5 * x_4;
+                zz_10 += zz_9 >> 32;
+            }
+
+            w = (uint)zz_6;
+            zz[zzOff + 6] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_7;
+            zz[zzOff + 7] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_8;
+            zz[zzOff + 8] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_9;
+            zz[zzOff + 9] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_10;
+            zz[zzOff + 10] = (w << 1) | c;
+            c = w >> 31;
+            w = zz[zzOff + 11] + (uint)(zz_10 >> 32);
+            zz[zzOff + 11] = (w << 1) | c;
+        }
+
+        public static int Sub(uint[] x, uint[] y, uint[] z)
+        {
+            long c = 0;
+            c += (long)x[0] - y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)x[1] - y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)x[2] - y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)x[3] - y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)x[4] - y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)x[5] - y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int Sub(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            c += (long)x[xOff + 0] - y[yOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 1] - y[yOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 2] - y[yOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 3] - y[yOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 4] - y[yOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 5] - y[yOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubBothFrom(uint[] x, uint[] y, uint[] z)
+        {
+            long c = 0;
+            c += (long)z[0] - x[0] - y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - x[1] - y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)z[2] - x[2] - y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)z[3] - x[3] - y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)z[4] - x[4] - y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)z[5] - x[5] - y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubFrom(uint[] x, uint[] z)
+        {
+            long c = 0;
+            c += (long)z[0] - x[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - x[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)z[2] - x[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)z[3] - x[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)z[4] - x[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)z[5] - x[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubFrom(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            c += (long)z[zOff + 0] - x[xOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 1] - x[xOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 2] - x[xOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 3] - x[xOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 4] - x[xOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 5] - x[xOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static BigInteger ToBigInteger(uint[] x)
+        {
+            byte[] bs = new byte[24];
+            for (int i = 0; i < 6; ++i)
+            {
+                uint x_i = x[i];
+                if (x_i != 0)
+                {
+                    Pack.UInt32_To_BE(x_i, bs, (5 - i) << 2);
+                }
+            }
+            return new BigInteger(1, bs);
+        }
+
+        public static void Zero(uint[] z)
+        {
+            z[0] = 0;
+            z[1] = 0;
+            z[2] = 0;
+            z[3] = 0;
+            z[4] = 0;
+            z[5] = 0;
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/Nat224.cs b/crypto/src/math/ec/custom/sec/Nat224.cs
new file mode 100644
index 000000000..d5b916a54
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/Nat224.cs
@@ -0,0 +1,1176 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal abstract class Nat224
+    {
+        private const ulong M = 0xFFFFFFFFUL;
+
+        public static uint Add(uint[] x, uint[] y, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[6] + y[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint Add(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0;
+            c += (ulong)x[xOff + 0] + y[yOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 1] + y[yOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 2] + y[yOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 3] + y[yOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 4] + y[yOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 5] + y[yOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 6] + y[yOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddBothTo(uint[] x, uint[] y, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + y[0] + z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + y[1] + z[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + y[2] + z[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + y[3] + z[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + y[4] + z[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + y[5] + z[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[6] + y[6] + z[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddBothTo(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0;
+            c += (ulong)x[xOff + 0] + y[yOff + 0] + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 1] + y[yOff + 1] + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 2] + y[yOff + 2] + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 3] + y[yOff + 3] + z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 4] + y[yOff + 4] + z[zOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 5] + y[yOff + 5] + z[zOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 6] + y[yOff + 6] + z[zOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddTo(uint[] x, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + z[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + z[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + z[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + z[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + z[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[6] + z[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddTo(uint[] x, int xOff, uint[] z, int zOff, uint cIn)
+        {
+            ulong c = cIn;
+            c += (ulong)x[xOff + 0] + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 1] + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 2] + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 3] + z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 4] + z[zOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 5] + z[zOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 6] + z[zOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddToEachOther(uint[] u, int uOff, uint[] v, int vOff)
+        {
+            ulong c = 0;
+            c += (ulong)u[uOff + 0] + v[vOff + 0];
+            u[uOff + 0] = (uint)c;
+            v[vOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 1] + v[vOff + 1];
+            u[uOff + 1] = (uint)c;
+            v[vOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 2] + v[vOff + 2];
+            u[uOff + 2] = (uint)c;
+            v[vOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 3] + v[vOff + 3];
+            u[uOff + 3] = (uint)c;
+            v[vOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 4] + v[vOff + 4];
+            u[uOff + 4] = (uint)c;
+            v[vOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 5] + v[vOff + 5];
+            u[uOff + 5] = (uint)c;
+            v[vOff + 5] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 6] + v[vOff + 6];
+            u[uOff + 6] = (uint)c;
+            v[vOff + 6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static void Copy(uint[] x, uint[] z)
+        {
+            z[0] = x[0];
+            z[1] = x[1];
+            z[2] = x[2];
+            z[3] = x[3];
+            z[4] = x[4];
+            z[5] = x[5];
+            z[6] = x[6];
+        }
+
+        public static uint[] Create()
+        {
+            return new uint[7];
+        }
+
+        public static uint[] CreateExt()
+        {
+            return new uint[14];
+        }
+
+        public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            bool pos = Gte(x, xOff, y, yOff);
+            if (pos)
+            {
+                Sub(x, xOff, y, yOff, z, zOff);
+            }
+            else
+            {
+                Sub(y, yOff, x, xOff, z, zOff);
+            }
+            return pos;
+        }
+
+        public static bool Eq(uint[] x, uint[] y)
+        {
+            for (int i = 6; i >= 0; --i)
+            {
+                if (x[i] != y[i])
+                    return false;
+            }
+            return true;
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            if (x.SignValue < 0 || x.BitLength > 224)
+                throw new ArgumentException();
+
+            uint[] z = Create();
+            int i = 0;
+            while (x.SignValue != 0)
+            {
+                z[i++] = (uint)x.IntValue;
+                x = x.ShiftRight(32);
+            }
+            return z;
+        }
+
+        public static uint GetBit(uint[] x, int bit)
+        {
+            if (bit == 0)
+            {
+                return x[0] & 1;
+            }
+            int w = bit >> 5;
+            if (w < 0 || w >= 7)
+            {
+                return 0;
+            }
+            int b = bit & 31;
+            return (x[w] >> b) & 1;
+        }
+
+        public static bool Gte(uint[] x, uint[] y)
+        {
+            for (int i = 6; i >= 0; --i)
+            {
+                uint x_i = x[i], y_i = y[i];
+                if (x_i < y_i)
+                    return false;
+                if (x_i > y_i)
+                    return true;
+            }
+            return true;
+        }
+
+        public static bool Gte(uint[] x, int xOff, uint[] y, int yOff)
+        {
+            for (int i = 6; i >= 0; --i)
+            {
+                uint x_i = x[xOff + i], y_i = y[yOff + i];
+                if (x_i < y_i)
+                    return false;
+                if (x_i > y_i)
+                    return true;
+            }
+            return true;
+        }
+
+        public static bool IsOne(uint[] x)
+        {
+            if (x[0] != 1)
+            {
+                return false;
+            }
+            for (int i = 1; i < 7; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static bool IsZero(uint[] x)
+        {
+            for (int i = 0; i < 7; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static void Mul(uint[] x, uint[] y, uint[] zz)
+        {
+            ulong y_0 = y[0];
+            ulong y_1 = y[1];
+            ulong y_2 = y[2];
+            ulong y_3 = y[3];
+            ulong y_4 = y[4];
+            ulong y_5 = y[5];
+            ulong y_6 = y[6];
+
+            {
+                ulong c = 0, x_0 = x[0];
+                c += x_0 * y_0;
+                zz[0] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_1;
+                zz[1] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_2;
+                zz[2] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_3;
+                zz[3] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_4;
+                zz[4] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_5;
+                zz[5] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_6;
+                zz[6] = (uint)c;
+                c >>= 32;
+                zz[7] = (uint)c;
+            }
+
+            for (int i = 1; i < 7; ++i)
+            {
+                ulong c = 0, x_i = x[i];
+                c += x_i * y_0 + zz[i + 0];
+                zz[i + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[i + 1];
+                zz[i + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[i + 2];
+                zz[i + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[i + 3];
+                zz[i + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[i + 4];
+                zz[i + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[i + 5];
+                zz[i + 5] = (uint)c;
+                c >>= 32;
+                c += x_i * y_6 + zz[i + 6];
+                zz[i + 6] = (uint)c;
+                c >>= 32;
+                zz[i + 7] = (uint)c;
+            }
+        }
+
+        public static void Mul(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
+        {
+            ulong y_0 = y[yOff + 0];
+            ulong y_1 = y[yOff + 1];
+            ulong y_2 = y[yOff + 2];
+            ulong y_3 = y[yOff + 3];
+            ulong y_4 = y[yOff + 4];
+            ulong y_5 = y[yOff + 5];
+            ulong y_6 = y[yOff + 6];
+
+            {
+                ulong c = 0, x_0 = x[xOff + 0];
+                c += x_0 * y_0;
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_1;
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_2;
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_3;
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_4;
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_5;
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_6;
+                zz[zzOff + 6] = (uint)c;
+                c >>= 32;
+                zz[zzOff + 7] = (uint)c;
+            }
+
+            for (int i = 1; i < 7; ++i)
+            {
+                ++zzOff;
+                ulong c = 0, x_i = x[xOff + i];
+                c += x_i * y_0 + zz[zzOff + 0];
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[zzOff + 1];
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[zzOff + 2];
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[zzOff + 3];
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[zzOff + 4];
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[zzOff + 5];
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                c += x_i * y_6 + zz[zzOff + 6];
+                zz[zzOff + 6] = (uint)c;
+                c >>= 32;
+                zz[zzOff + 7] = (uint)c;
+            }
+        }
+
+        public static uint MulAddTo(uint[] x, uint[] y, uint[] zz)
+        {
+            ulong y_0 = y[0];
+            ulong y_1 = y[1];
+            ulong y_2 = y[2];
+            ulong y_3 = y[3];
+            ulong y_4 = y[4];
+            ulong y_5 = y[5];
+            ulong y_6 = y[6];
+
+            ulong zc = 0;
+            for (int i = 0; i < 7; ++i)
+            {
+                ulong c = 0, x_i = x[i];
+                c += x_i * y_0 + zz[i + 0];
+                zz[i + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[i + 1];
+                zz[i + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[i + 2];
+                zz[i + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[i + 3];
+                zz[i + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[i + 4];
+                zz[i + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[i + 5];
+                zz[i + 5] = (uint)c;
+                c >>= 32;
+                c += x_i * y_6 + zz[i + 6];
+                zz[i + 6] = (uint)c;
+                c >>= 32;
+                c += zc + zz[i + 7];
+                zz[i + 7] = (uint)c;
+                zc = c >> 32;
+            }
+            return (uint)zc;
+        }
+
+        public static uint MulAddTo(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
+        {
+            ulong y_0 = y[yOff + 0];
+            ulong y_1 = y[yOff + 1];
+            ulong y_2 = y[yOff + 2];
+            ulong y_3 = y[yOff + 3];
+            ulong y_4 = y[yOff + 4];
+            ulong y_5 = y[yOff + 5];
+            ulong y_6 = y[yOff + 6];
+
+            ulong zc = 0;
+            for (int i = 0; i < 7; ++i)
+            {
+                ulong c = 0, x_i = x[xOff + i];
+                c += x_i * y_0 + zz[zzOff + 0];
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[zzOff + 1];
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[zzOff + 2];
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[zzOff + 3];
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[zzOff + 4];
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[zzOff + 5];
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                c += x_i * y_6 + zz[zzOff + 6];
+                zz[zzOff + 6] = (uint)c;
+                c >>= 32;
+                c += zc + zz[zzOff + 7];
+                zz[zzOff + 7] = (uint)c;
+                zc = c >> 32;
+                ++zzOff;
+            }
+            return (uint)zc;
+        }
+
+        public static ulong Mul33Add(uint w, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            Debug.Assert(w >> 31 == 0);
+
+            ulong c = 0, wVal = w;
+            ulong x0 = x[xOff + 0];
+            c += wVal * x0 + y[yOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            ulong x1 = x[xOff + 1];
+            c += wVal * x1 + x0 + y[yOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            ulong x2 = x[xOff + 2];
+            c += wVal * x2 + x1 + y[yOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            ulong x3 = x[xOff + 3];
+            c += wVal * x3 + x2 + y[yOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            ulong x4 = x[xOff + 4];
+            c += wVal * x4 + x3 + y[yOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            ulong x5 = x[xOff + 5];
+            c += wVal * x5 + x4 + y[yOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            ulong x6 = x[xOff + 6];
+            c += wVal * x6 + x5 + y[yOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            c += x6;
+            return c;
+        }
+
+        public static uint MulByWord(uint x, uint[] z)
+        {
+            ulong c = 0, xVal = x;
+            c += xVal * (ulong)z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint MulByWordAddTo(uint x, uint[] y, uint[] z)
+        {
+            ulong c = 0, xVal = x;
+            c += xVal * (ulong)z[0] + y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[1] + y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[2] + y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[3] + y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[4] + y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[5] + y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[6] + y[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint MulWordAddTo(uint x, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0, xVal = x;
+            c += xVal * y[yOff + 0] + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 1] + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 2] + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 3] + z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 4] + z[zOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 5] + z[zOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 6] + z[zOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint Mul33DWordAdd(uint x, ulong y, uint[] z, int zOff)
+        {
+            Debug.Assert(x >> 31 == 0);
+            Debug.Assert(zOff <= 3);
+            ulong c = 0, xVal = x;
+            ulong y00 = y & M;
+            c += xVal * y00 + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            ulong y01 = y >> 32;
+            c += xVal * y01 + y00 + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += y01 + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(7, z, zOff, 4);
+        }
+
+        public static uint Mul33WordAdd(uint x, uint y, uint[] z, int zOff)
+        {
+            Debug.Assert(x >> 31 == 0);
+            Debug.Assert(zOff <= 4);
+            ulong c = 0, yVal = y;
+            c += yVal * x + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += yVal + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(7, z, zOff, 3);
+        }
+
+        public static uint MulWordDwordAdd(uint x, ulong y, uint[] z, int zOff)
+        {
+            Debug.Assert(zOff <= 4);
+            ulong c = 0, xVal = x;
+            c += xVal * y + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += xVal * (y >> 32) + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(7, z, zOff, 3);
+        }
+
+        public static uint MulWord(uint x, uint[] y, uint[] z, int zOff)
+        {
+            ulong c = 0, xVal = x;
+            int i = 0;
+            do
+            {
+                c += xVal * y[i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            while (++i < 7);
+            return (uint)c;
+        }
+
+        public static void Square(uint[] x, uint[] zz)
+        {
+            ulong x_0 = x[0];
+            ulong zz_1;
+
+            uint c = 0, w;
+            {
+                int i = 6, j = 14;
+                do
+                {
+                    ulong xVal = x[i--];
+                    ulong p = xVal * xVal;
+                    zz[--j] = (c << 31) | (uint)(p >> 33);
+                    zz[--j] = (uint)(p >> 1);
+                    c = (uint)p;
+                }
+                while (i > 0);
+
+                {
+                    ulong p = x_0 * x_0;
+                    zz_1 = (ulong)(c << 31) | (p >> 33);
+                    zz[0] = (uint)p;
+                    c = (uint)(p >> 32) & 1;
+                }
+            }
+
+            ulong x_1 = x[1];
+            ulong zz_2 = zz[2];
+
+            {
+                zz_1 += x_1 * x_0;
+                w = (uint)zz_1;
+                zz[1] = (w << 1) | c;
+                c = w >> 31;
+                zz_2 += zz_1 >> 32;
+            }
+
+            ulong x_2 = x[2];
+            ulong zz_3 = zz[3];
+            ulong zz_4 = zz[4];
+            {
+                zz_2 += x_2 * x_0;
+                w = (uint)zz_2;
+                zz[2] = (w << 1) | c;
+                c = w >> 31;
+                zz_3 += (zz_2 >> 32) + x_2 * x_1;
+                zz_4 += zz_3 >> 32;
+                zz_3 &= M;
+            }
+
+            ulong x_3 = x[3];
+            ulong zz_5 = zz[5];
+            ulong zz_6 = zz[6];
+            {
+                zz_3 += x_3 * x_0;
+                w = (uint)zz_3;
+                zz[3] = (w << 1) | c;
+                c = w >> 31;
+                zz_4 += (zz_3 >> 32) + x_3 * x_1;
+                zz_5 += (zz_4 >> 32) + x_3 * x_2;
+                zz_4 &= M;
+                zz_6 += zz_5 >> 32;
+                zz_5 &= M;
+            }
+
+            ulong x_4 = x[4];
+            ulong zz_7 = zz[7];
+            ulong zz_8 = zz[8];
+            {
+                zz_4 += x_4 * x_0;
+                w = (uint)zz_4;
+                zz[4] = (w << 1) | c;
+                c = w >> 31;
+                zz_5 += (zz_4 >> 32) + x_4 * x_1;
+                zz_6 += (zz_5 >> 32) + x_4 * x_2;
+                zz_5 &= M;
+                zz_7 += (zz_6 >> 32) + x_4 * x_3;
+                zz_6 &= M;
+                zz_8 += zz_7 >> 32;
+                zz_7 &= M;
+            }
+
+            ulong x_5 = x[5];
+            ulong zz_9 = zz[9];
+            ulong zz_10 = zz[10];
+            {
+                zz_5 += x_5 * x_0;
+                w = (uint)zz_5;
+                zz[5] = (w << 1) | c;
+                c = w >> 31;
+                zz_6 += (zz_5 >> 32) + x_5 * x_1;
+                zz_7 += (zz_6 >> 32) + x_5 * x_2;
+                zz_6 &= M;
+                zz_8 += (zz_7 >> 32) + x_5 * x_3;
+                zz_7 &= M;
+                zz_9 += (zz_8 >> 32) + x_5 * x_4;
+                zz_8 &= M;
+                zz_10 += zz_9 >> 32;
+                zz_9 &= M;
+            }
+
+            ulong x_6 = x[6];
+            ulong zz_11 = zz[11];
+            ulong zz_12 = zz[12];
+            {
+                zz_6 += x_6 * x_0;
+                w = (uint)zz_6;
+                zz[6] = (w << 1) | c;
+                c = w >> 31;
+                zz_7 += (zz_6 >> 32) + x_6 * x_1;
+                zz_8 += (zz_7 >> 32) + x_6 * x_2;
+                zz_9 += (zz_8 >> 32) + x_6 * x_3;
+                zz_10 += (zz_9 >> 32) + x_6 * x_4;
+                zz_11 += (zz_10 >> 32) + x_6 * x_5;
+                zz_12 += zz_11 >> 32;
+            }
+
+            w = (uint)zz_7;
+            zz[7] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_8;
+            zz[8] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_9;
+            zz[9] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_10;
+            zz[10] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_11;
+            zz[11] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_12;
+            zz[12] = (w << 1) | c;
+            c = w >> 31;
+            w = zz[13] + (uint)(zz_12 >> 32);
+            zz[13] = (w << 1) | c;
+        }
+
+        public static void Square(uint[] x, int xOff, uint[] zz, int zzOff)
+        {
+            ulong x_0 = x[xOff + 0];
+            ulong zz_1;
+
+            uint c = 0, w;
+            {
+                int i = 6, j = 14;
+                do
+                {
+                    ulong xVal = x[xOff + i--];
+                    ulong p = xVal * xVal;
+                    zz[zzOff + --j] = (c << 31) | (uint)(p >> 33);
+                    zz[zzOff + --j] = (uint)(p >> 1);
+                    c = (uint)p;
+                }
+                while (i > 0);
+
+                {
+                    ulong p = x_0 * x_0;
+                    zz_1 = (ulong)(c << 31) | (p >> 33);
+                    zz[zzOff + 0] = (uint)p;
+                    c = (uint)(p >> 32) & 1;
+                }
+            }
+
+            ulong x_1 = x[xOff + 1];
+            ulong zz_2 = zz[zzOff + 2];
+
+            {
+                zz_1 += x_1 * x_0;
+                w = (uint)zz_1;
+                zz[zzOff + 1] = (w << 1) | c;
+                c = w >> 31;
+                zz_2 += zz_1 >> 32;
+            }
+
+            ulong x_2 = x[xOff + 2];
+            ulong zz_3 = zz[zzOff + 3];
+            ulong zz_4 = zz[zzOff + 4];
+            {
+                zz_2 += x_2 * x_0;
+                w = (uint)zz_2;
+                zz[zzOff + 2] = (w << 1) | c;
+                c = w >> 31;
+                zz_3 += (zz_2 >> 32) + x_2 * x_1;
+                zz_4 += zz_3 >> 32;
+                zz_3 &= M;
+            }
+
+            ulong x_3 = x[xOff + 3];
+            ulong zz_5 = zz[zzOff + 5];
+            ulong zz_6 = zz[zzOff + 6];
+            {
+                zz_3 += x_3 * x_0;
+                w = (uint)zz_3;
+                zz[zzOff + 3] = (w << 1) | c;
+                c = w >> 31;
+                zz_4 += (zz_3 >> 32) + x_3 * x_1;
+                zz_5 += (zz_4 >> 32) + x_3 * x_2;
+                zz_4 &= M;
+                zz_6 += zz_5 >> 32;
+                zz_5 &= M;
+            }
+
+            ulong x_4 = x[xOff + 4];
+            ulong zz_7 = zz[zzOff + 7];
+            ulong zz_8 = zz[zzOff + 8];
+            {
+                zz_4 += x_4 * x_0;
+                w = (uint)zz_4;
+                zz[zzOff + 4] = (w << 1) | c;
+                c = w >> 31;
+                zz_5 += (zz_4 >> 32) + x_4 * x_1;
+                zz_6 += (zz_5 >> 32) + x_4 * x_2;
+                zz_5 &= M;
+                zz_7 += (zz_6 >> 32) + x_4 * x_3;
+                zz_6 &= M;
+                zz_8 += zz_7 >> 32;
+                zz_7 &= M;
+            }
+
+            ulong x_5 = x[xOff + 5];
+            ulong zz_9 = zz[zzOff + 9];
+            ulong zz_10 = zz[zzOff + 10];
+            {
+                zz_5 += x_5 * x_0;
+                w = (uint)zz_5;
+                zz[zzOff + 5] = (w << 1) | c;
+                c = w >> 31;
+                zz_6 += (zz_5 >> 32) + x_5 * x_1;
+                zz_7 += (zz_6 >> 32) + x_5 * x_2;
+                zz_6 &= M;
+                zz_8 += (zz_7 >> 32) + x_5 * x_3;
+                zz_7 &= M;
+                zz_9 += (zz_8 >> 32) + x_5 * x_4;
+                zz_8 &= M;
+                zz_10 += zz_9 >> 32;
+                zz_9 &= M;
+            }
+
+            ulong x_6 = x[xOff + 6];
+            ulong zz_11 = zz[zzOff + 11];
+            ulong zz_12 = zz[zzOff + 12];
+            {
+                zz_6 += x_6 * x_0;
+                w = (uint)zz_6;
+                zz[zzOff + 6] = (w << 1) | c;
+                c = w >> 31;
+                zz_7 += (zz_6 >> 32) + x_6 * x_1;
+                zz_8 += (zz_7 >> 32) + x_6 * x_2;
+                zz_9 += (zz_8 >> 32) + x_6 * x_3;
+                zz_10 += (zz_9 >> 32) + x_6 * x_4;
+                zz_11 += (zz_10 >> 32) + x_6 * x_5;
+                zz_12 += zz_11 >> 32;
+            }
+
+            w = (uint)zz_7;
+            zz[zzOff + 7] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_8;
+            zz[zzOff + 8] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_9;
+            zz[zzOff + 9] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_10;
+            zz[zzOff + 10] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_11;
+            zz[zzOff + 11] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_12;
+            zz[zzOff + 12] = (w << 1) | c;
+            c = w >> 31;
+            w = zz[zzOff + 13] + (uint)(zz_12 >> 32);
+            zz[zzOff + 13] = (w << 1) | c;
+        }
+
+        public static int Sub(uint[] x, uint[] y, uint[] z)
+        {
+            long c = 0;
+            c += (long)x[0] - y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)x[1] - y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)x[2] - y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)x[3] - y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)x[4] - y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)x[5] - y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (long)x[6] - y[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int Sub(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            c += (long)x[xOff + 0] - y[yOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 1] - y[yOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 2] - y[yOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 3] - y[yOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 4] - y[yOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 5] - y[yOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 6] - y[yOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubBothFrom(uint[] x, uint[] y, uint[] z)
+        {
+            long c = 0;
+            c += (long)z[0] - x[0] - y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - x[1] - y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)z[2] - x[2] - y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)z[3] - x[3] - y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)z[4] - x[4] - y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)z[5] - x[5] - y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (long)z[6] - x[6] - y[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubFrom(uint[] x, uint[] z)
+        {
+            long c = 0;
+            c += (long)z[0] - x[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - x[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)z[2] - x[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)z[3] - x[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)z[4] - x[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)z[5] - x[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (long)z[6] - x[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubFrom(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            c += (long)z[zOff + 0] - x[xOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 1] - x[xOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 2] - x[xOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 3] - x[xOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 4] - x[xOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 5] - x[xOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 6] - x[xOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static BigInteger ToBigInteger(uint[] x)
+        {
+            byte[] bs = new byte[28];
+            for (int i = 0; i < 7; ++i)
+            {
+                uint x_i = x[i];
+                if (x_i != 0)
+                {
+                    Pack.UInt32_To_BE(x_i, bs, (6 - i) << 2);
+                }
+            }
+            return new BigInteger(1, bs);
+        }
+
+        public static void Zero(uint[] z)
+        {
+            z[0] = 0;
+            z[1] = 0;
+            z[2] = 0;
+            z[3] = 0;
+            z[4] = 0;
+            z[5] = 0;
+            z[6] = 0;
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/Nat256.cs b/crypto/src/math/ec/custom/sec/Nat256.cs
new file mode 100644
index 000000000..bd2d6da47
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/Nat256.cs
@@ -0,0 +1,1300 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal abstract class Nat256
+    {
+        private const ulong M = 0xFFFFFFFFUL;
+
+        public static uint Add(uint[] x, uint[] y, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[6] + y[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[7] + y[7];
+            z[7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint Add(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0;
+            c += (ulong)x[xOff + 0] + y[yOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 1] + y[yOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 2] + y[yOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 3] + y[yOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 4] + y[yOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 5] + y[yOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 6] + y[yOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 7] + y[yOff + 7];
+            z[zOff + 7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddBothTo(uint[] x, uint[] y, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + y[0] + z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + y[1] + z[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + y[2] + z[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + y[3] + z[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + y[4] + z[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + y[5] + z[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[6] + y[6] + z[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[7] + y[7] + z[7];
+            z[7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddBothTo(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0;
+            c += (ulong)x[xOff + 0] + y[yOff + 0] + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 1] + y[yOff + 1] + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 2] + y[yOff + 2] + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 3] + y[yOff + 3] + z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 4] + y[yOff + 4] + z[zOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 5] + y[yOff + 5] + z[zOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 6] + y[yOff + 6] + z[zOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 7] + y[yOff + 7] + z[zOff + 7];
+            z[zOff + 7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddTo(uint[] x, uint[] z)
+        {
+            ulong c = 0;
+            c += (ulong)x[0] + z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[1] + z[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[2] + z[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[3] + z[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[4] + z[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[5] + z[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[6] + z[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[7] + z[7];
+            z[7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddTo(uint[] x, int xOff, uint[] z, int zOff, uint cIn)
+        {
+            ulong c = cIn;
+            c += (ulong)x[xOff + 0] + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 1] + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 2] + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 3] + z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 4] + z[zOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 5] + z[zOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 6] + z[zOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            c += (ulong)x[xOff + 7] + z[zOff + 7];
+            z[zOff + 7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint AddToEachOther(uint[] u, int uOff, uint[] v, int vOff)
+        {
+            ulong c = 0;
+            c += (ulong)u[uOff + 0] + v[vOff + 0];
+            u[uOff + 0] = (uint)c;
+            v[vOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 1] + v[vOff + 1];
+            u[uOff + 1] = (uint)c;
+            v[vOff + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 2] + v[vOff + 2];
+            u[uOff + 2] = (uint)c;
+            v[vOff + 2] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 3] + v[vOff + 3];
+            u[uOff + 3] = (uint)c;
+            v[vOff + 3] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 4] + v[vOff + 4];
+            u[uOff + 4] = (uint)c;
+            v[vOff + 4] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 5] + v[vOff + 5];
+            u[uOff + 5] = (uint)c;
+            v[vOff + 5] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 6] + v[vOff + 6];
+            u[uOff + 6] = (uint)c;
+            v[vOff + 6] = (uint)c;
+            c >>= 32;
+            c += (ulong)u[uOff + 7] + v[vOff + 7];
+            u[uOff + 7] = (uint)c;
+            v[vOff + 7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static void Copy(uint[] x, uint[] z)
+        {
+            z[0] = x[0];
+            z[1] = x[1];
+            z[2] = x[2];
+            z[3] = x[3];
+            z[4] = x[4];
+            z[5] = x[5];
+            z[6] = x[6];
+            z[7] = x[7];
+        }
+
+        public static uint[] Create()
+        {
+            return new uint[8];
+        }
+
+        public static uint[] CreateExt()
+        {
+            return new uint[16];
+        }
+
+        public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            bool pos = Gte(x, xOff, y, yOff);
+            if (pos)
+            {
+                Sub(x, xOff, y, yOff, z, zOff);
+            }
+            else
+            {
+                Sub(y, yOff, x, xOff, z, zOff);
+            }
+            return pos;
+        }
+
+        public static bool Eq(uint[] x, uint[] y)
+        {
+            for (int i = 7; i >= 0; --i)
+            {
+                if (x[i] != y[i])
+                    return false;
+            }
+            return true;
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            if (x.SignValue < 0 || x.BitLength > 256)
+                throw new ArgumentException();
+
+            uint[] z = Create();
+            int i = 0;
+            while (x.SignValue != 0)
+            {
+                z[i++] = (uint)x.IntValue;
+                x = x.ShiftRight(32);
+            }
+            return z;
+        }
+
+        public static uint GetBit(uint[] x, int bit)
+        {
+            if (bit == 0)
+            {
+                return x[0] & 1;
+            }
+            if ((bit & 255) != bit)
+            {
+                return 0;
+            }
+            int w = bit >> 5;
+            int b = bit & 31;
+            return (x[w] >> b) & 1;
+        }
+
+        public static bool Gte(uint[] x, uint[] y)
+        {
+            for (int i = 7; i >= 0; --i)
+            {
+                uint x_i = x[i], y_i = y[i];
+                if (x_i < y_i)
+                    return false;
+                if (x_i > y_i)
+                    return true;
+            }
+            return true;
+        }
+
+        public static bool Gte(uint[] x, int xOff, uint[] y, int yOff)
+        {
+            for (int i = 7; i >= 0; --i)
+            {
+                uint x_i = x[xOff + i], y_i = y[yOff + i];
+                if (x_i < y_i)
+                    return false;
+                if (x_i > y_i)
+                    return true;
+            }
+            return true;
+        }
+
+        public static bool IsOne(uint[] x)
+        {
+            if (x[0] != 1)
+            {
+                return false;
+            }
+            for (int i = 1; i < 8; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static bool IsZero(uint[] x)
+        {
+            for (int i = 0; i < 8; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static void Mul(uint[] x, uint[] y, uint[] zz)
+        {
+            ulong y_0 = y[0];
+            ulong y_1 = y[1];
+            ulong y_2 = y[2];
+            ulong y_3 = y[3];
+            ulong y_4 = y[4];
+            ulong y_5 = y[5];
+            ulong y_6 = y[6];
+            ulong y_7 = y[7];
+
+            {
+                ulong c = 0, x_0 = x[0];
+                c += x_0 * y_0;
+                zz[0] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_1;
+                zz[1] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_2;
+                zz[2] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_3;
+                zz[3] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_4;
+                zz[4] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_5;
+                zz[5] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_6;
+                zz[6] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_7;
+                zz[7] = (uint)c;
+                c >>= 32;
+                zz[8] = (uint)c;
+            }
+
+            for (int i = 1; i < 8; ++i)
+            {
+                ulong c = 0, x_i = x[i];
+                c += x_i * y_0 + zz[i + 0];
+                zz[i + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[i + 1];
+                zz[i + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[i + 2];
+                zz[i + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[i + 3];
+                zz[i + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[i + 4];
+                zz[i + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[i + 5];
+                zz[i + 5] = (uint)c;
+                c >>= 32;
+                c += x_i * y_6 + zz[i + 6];
+                zz[i + 6] = (uint)c;
+                c >>= 32;
+                c += x_i * y_7 + zz[i + 7];
+                zz[i + 7] = (uint)c;
+                c >>= 32;
+                zz[i + 8] = (uint)c;
+            }
+        }
+
+        public static void Mul(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
+        {
+            ulong y_0 = y[yOff + 0];
+            ulong y_1 = y[yOff + 1];
+            ulong y_2 = y[yOff + 2];
+            ulong y_3 = y[yOff + 3];
+            ulong y_4 = y[yOff + 4];
+            ulong y_5 = y[yOff + 5];
+            ulong y_6 = y[yOff + 6];
+            ulong y_7 = y[yOff + 7];
+
+            {
+                ulong c = 0, x_0 = x[xOff + 0];
+                c += x_0 * y_0;
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_1;
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_2;
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_3;
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_4;
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_5;
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_6;
+                zz[zzOff + 6] = (uint)c;
+                c >>= 32;
+                c += x_0 * y_7;
+                zz[zzOff + 7] = (uint)c;
+                c >>= 32;
+                zz[zzOff + 8] = (uint)c;
+            }
+
+            for (int i = 1; i < 8; ++i)
+            {
+                ++zzOff;
+                ulong c = 0, x_i = x[xOff + i];
+                c += x_i * y_0 + zz[zzOff + 0];
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[zzOff + 1];
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[zzOff + 2];
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[zzOff + 3];
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[zzOff + 4];
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[zzOff + 5];
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                c += x_i * y_6 + zz[zzOff + 6];
+                zz[zzOff + 6] = (uint)c;
+                c >>= 32;
+                c += x_i * y_7 + zz[zzOff + 7];
+                zz[zzOff + 7] = (uint)c;
+                c >>= 32;
+                zz[zzOff + 8] = (uint)c;
+            }
+        }
+
+        public static uint MulAddTo(uint[] x, uint[] y, uint[] zz)
+        {
+            ulong y_0 = y[0];
+            ulong y_1 = y[1];
+            ulong y_2 = y[2];
+            ulong y_3 = y[3];
+            ulong y_4 = y[4];
+            ulong y_5 = y[5];
+            ulong y_6 = y[6];
+            ulong y_7 = y[7];
+
+            ulong zc = 0;
+            for (int i = 0; i < 8; ++i)
+            {
+                ulong c = 0, x_i = x[i];
+                c += x_i * y_0 + zz[i + 0];
+                zz[i + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[i + 1];
+                zz[i + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[i + 2];
+                zz[i + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[i + 3];
+                zz[i + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[i + 4];
+                zz[i + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[i + 5];
+                zz[i + 5] = (uint)c;
+                c >>= 32;
+                c += x_i * y_6 + zz[i + 6];
+                zz[i + 6] = (uint)c;
+                c >>= 32;
+                c += x_i * y_7 + zz[i + 7];
+                zz[i + 7] = (uint)c;
+                c >>= 32;
+                c += zc + zz[i + 8];
+                zz[i + 8] = (uint)c;
+                zc = c >> 32;
+            }
+            return (uint)zc;
+        }
+
+        public static uint MulAddTo(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
+        {
+            ulong y_0 = y[yOff + 0];
+            ulong y_1 = y[yOff + 1];
+            ulong y_2 = y[yOff + 2];
+            ulong y_3 = y[yOff + 3];
+            ulong y_4 = y[yOff + 4];
+            ulong y_5 = y[yOff + 5];
+            ulong y_6 = y[yOff + 6];
+            ulong y_7 = y[yOff + 7];
+
+            ulong zc = 0;
+            for (int i = 0; i < 8; ++i)
+            {
+                ulong c = 0, x_i = x[xOff + i];
+                c += x_i * y_0 + zz[zzOff + 0];
+                zz[zzOff + 0] = (uint)c;
+                c >>= 32;
+                c += x_i * y_1 + zz[zzOff + 1];
+                zz[zzOff + 1] = (uint)c;
+                c >>= 32;
+                c += x_i * y_2 + zz[zzOff + 2];
+                zz[zzOff + 2] = (uint)c;
+                c >>= 32;
+                c += x_i * y_3 + zz[zzOff + 3];
+                zz[zzOff + 3] = (uint)c;
+                c >>= 32;
+                c += x_i * y_4 + zz[zzOff + 4];
+                zz[zzOff + 4] = (uint)c;
+                c >>= 32;
+                c += x_i * y_5 + zz[zzOff + 5];
+                zz[zzOff + 5] = (uint)c;
+                c >>= 32;
+                c += x_i * y_6 + zz[zzOff + 6];
+                zz[zzOff + 6] = (uint)c;
+                c >>= 32;
+                c += x_i * y_7 + zz[zzOff + 7];
+                zz[zzOff + 7] = (uint)c;
+                c >>= 32;
+                c += zc + zz[zzOff + 8];
+                zz[zzOff + 8] = (uint)c;
+                zc = c >> 32;
+                ++zzOff;
+            }
+            return (uint)zc;
+        }
+
+        public static ulong Mul33Add(uint w, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            Debug.Assert(w >> 31 == 0);
+
+            ulong c = 0, wVal = w;
+            ulong x0 = x[xOff + 0];
+            c += wVal * x0 + y[yOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            ulong x1 = x[xOff + 1];
+            c += wVal * x1 + x0 + y[yOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            ulong x2 = x[xOff + 2];
+            c += wVal * x2 + x1 + y[yOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            ulong x3 = x[xOff + 3];
+            c += wVal * x3 + x2 + y[yOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            ulong x4 = x[xOff + 4];
+            c += wVal * x4 + x3 + y[yOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            ulong x5 = x[xOff + 5];
+            c += wVal * x5 + x4 + y[yOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            ulong x6 = x[xOff + 6];
+            c += wVal * x6 + x5 + y[yOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            ulong x7 = x[xOff + 7];
+            c += wVal * x7 + x6 + y[yOff + 7];
+            z[zOff + 7] = (uint)c;
+            c >>= 32;
+            c += x7;
+            return c;
+        }
+
+        public static uint MulByWord(uint x, uint[] z)
+        {
+            ulong c = 0, xVal = x;
+            c += xVal * (ulong)z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[7];
+            z[7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint MulByWordAddTo(uint x, uint[] y, uint[] z)
+        {
+            ulong c = 0, xVal = x;
+            c += xVal * (ulong)z[0] + y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[1] + y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[2] + y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[3] + y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[4] + y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[5] + y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[6] + y[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            c += xVal * (ulong)z[7] + y[7];
+            z[7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint MulWordAddTo(uint x, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0, xVal = x;
+            c += xVal * y[yOff + 0] + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 1] + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 2] + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 3] + z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 4] + z[zOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 5] + z[zOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 6] + z[zOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            c += xVal * y[yOff + 7] + z[zOff + 7];
+            z[zOff + 7] = (uint)c;
+            c >>= 32;
+            return (uint)c;
+        }
+
+        public static uint Mul33DWordAdd(uint x, ulong y, uint[] z, int zOff)
+        {
+            Debug.Assert(x >> 31 == 0);
+            Debug.Assert(zOff <= 4);
+            ulong c = 0, xVal = x;
+            ulong y00 = y & M;
+            c += xVal * y00 + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            ulong y01 = y >> 32;
+            c += xVal * y01 + y00 + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += y01 + z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(8, z, zOff, 4);
+        }
+
+        public static uint Mul33WordAdd(uint x, uint y, uint[] z, int zOff)
+        {
+            Debug.Assert(x >> 31 == 0);
+            Debug.Assert(zOff <= 5);
+            ulong c = 0, yVal = y;
+            c += yVal * x + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += yVal + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(8, z, zOff, 3);
+        }
+
+        public static uint MulWordDwordAdd(uint x, ulong y, uint[] z, int zOff)
+        {
+            Debug.Assert(zOff <= 5);
+            ulong c = 0, xVal = x;
+            c += xVal * y + z[zOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += xVal * (y >> 32) + z[zOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += z[zOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : Nat.IncAt(8, z, zOff, 3);
+        }
+
+        public static uint MulWord(uint x, uint[] y, uint[] z, int zOff)
+        {
+            ulong c = 0, xVal = x;
+            int i = 0;
+            do
+            {
+                c += xVal * y[i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            while (++i < 8);
+            return (uint)c;
+        }
+
+        public static void Square(uint[] x, uint[] zz)
+        {
+            ulong x_0 = x[0];
+            ulong zz_1;
+
+            uint c = 0, w;
+            {
+                int i = 7, j = 16;
+                do
+                {
+                    ulong xVal = x[i--];
+                    ulong p = xVal * xVal;
+                    zz[--j] = (c << 31) | (uint)(p >> 33);
+                    zz[--j] = (uint)(p >> 1);
+                    c = (uint)p;
+                }
+                while (i > 0);
+
+                {
+                    ulong p = x_0 * x_0;
+                    zz_1 = (ulong)(c << 31) | (p >> 33);
+                    zz[0] = (uint)p;
+                    c = (uint)(p >> 32) & 1;
+                }
+            }
+
+            ulong x_1 = x[1];
+            ulong zz_2 = zz[2];
+
+            {
+                zz_1 += x_1 * x_0;
+                w = (uint)zz_1;
+                zz[1] = (w << 1) | c;
+                c = w >> 31;
+                zz_2 += zz_1 >> 32;
+            }
+
+            ulong x_2 = x[2];
+            ulong zz_3 = zz[3];
+            ulong zz_4 = zz[4];
+            {
+                zz_2 += x_2 * x_0;
+                w = (uint)zz_2;
+                zz[2] = (w << 1) | c;
+                c = w >> 31;
+                zz_3 += (zz_2 >> 32) + x_2 * x_1;
+                zz_4 += zz_3 >> 32;
+                zz_3 &= M;
+            }
+
+            ulong x_3 = x[3];
+            ulong zz_5 = zz[5];
+            ulong zz_6 = zz[6];
+            {
+                zz_3 += x_3 * x_0;
+                w = (uint)zz_3;
+                zz[3] = (w << 1) | c;
+                c = w >> 31;
+                zz_4 += (zz_3 >> 32) + x_3 * x_1;
+                zz_5 += (zz_4 >> 32) + x_3 * x_2;
+                zz_4 &= M;
+                zz_6 += zz_5 >> 32;
+                zz_5 &= M;
+            }
+
+            ulong x_4 = x[4];
+            ulong zz_7 = zz[7];
+            ulong zz_8 = zz[8];
+            {
+                zz_4 += x_4 * x_0;
+                w = (uint)zz_4;
+                zz[4] = (w << 1) | c;
+                c = w >> 31;
+                zz_5 += (zz_4 >> 32) + x_4 * x_1;
+                zz_6 += (zz_5 >> 32) + x_4 * x_2;
+                zz_5 &= M;
+                zz_7 += (zz_6 >> 32) + x_4 * x_3;
+                zz_6 &= M;
+                zz_8 += zz_7 >> 32;
+                zz_7 &= M;
+            }
+
+            ulong x_5 = x[5];
+            ulong zz_9 = zz[9];
+            ulong zz_10 = zz[10];
+            {
+                zz_5 += x_5 * x_0;
+                w = (uint)zz_5;
+                zz[5] = (w << 1) | c;
+                c = w >> 31;
+                zz_6 += (zz_5 >> 32) + x_5 * x_1;
+                zz_7 += (zz_6 >> 32) + x_5 * x_2;
+                zz_6 &= M;
+                zz_8 += (zz_7 >> 32) + x_5 * x_3;
+                zz_7 &= M;
+                zz_9 += (zz_8 >> 32) + x_5 * x_4;
+                zz_8 &= M;
+                zz_10 += zz_9 >> 32;
+                zz_9 &= M;
+            }
+
+            ulong x_6 = x[6];
+            ulong zz_11 = zz[11];
+            ulong zz_12 = zz[12];
+            {
+                zz_6 += x_6 * x_0;
+                w = (uint)zz_6;
+                zz[6] = (w << 1) | c;
+                c = w >> 31;
+                zz_7 += (zz_6 >> 32) + x_6 * x_1;
+                zz_8 += (zz_7 >> 32) + x_6 * x_2;
+                zz_7 &= M;
+                zz_9 += (zz_8 >> 32) + x_6 * x_3;
+                zz_8 &= M;
+                zz_10 += (zz_9 >> 32) + x_6 * x_4;
+                zz_9 &= M;
+                zz_11 += (zz_10 >> 32) + x_6 * x_5;
+                zz_10 &= M;
+                zz_12 += zz_11 >> 32;
+                zz_11 &= M;
+            }
+
+            ulong x_7 = x[7];
+            ulong zz_13 = zz[13];
+            ulong zz_14 = zz[14];
+            {
+                zz_7 += x_7 * x_0;
+                w = (uint)zz_7;
+                zz[7] = (w << 1) | c;
+                c = w >> 31;
+                zz_8 += (zz_7 >> 32) + x_7 * x_1;
+                zz_9 += (zz_8 >> 32) + x_7 * x_2;
+                zz_10 += (zz_9 >> 32) + x_7 * x_3;
+                zz_11 += (zz_10 >> 32) + x_7 * x_4;
+                zz_12 += (zz_11 >> 32) + x_7 * x_5;
+                zz_13 += (zz_12 >> 32) + x_7 * x_6;
+                zz_14 += zz_13 >> 32;
+            }
+
+            w = (uint)zz_8;
+            zz[8] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_9;
+            zz[9] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_10;
+            zz[10] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_11;
+            zz[11] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_12;
+            zz[12] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_13;
+            zz[13] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_14;
+            zz[14] = (w << 1) | c;
+            c = w >> 31;
+            w = zz[15] + (uint)(zz_14 >> 32);
+            zz[15] = (w << 1) | c;
+        }
+
+        public static void Square(uint[] x, int xOff, uint[] zz, int zzOff)
+        {
+            ulong x_0 = x[xOff + 0];
+            ulong zz_1;
+
+            uint c = 0, w;
+            {
+                int i = 7, j = 16;
+                do
+                {
+                    ulong xVal = x[xOff + i--];
+                    ulong p = xVal * xVal;
+                    zz[zzOff + --j] = (c << 31) | (uint)(p >> 33);
+                    zz[zzOff + --j] = (uint)(p >> 1);
+                    c = (uint)p;
+                }
+                while (i > 0);
+
+                {
+                    ulong p = x_0 * x_0;
+                    zz_1 = (ulong)(c << 31) | (p >> 33);
+                    zz[zzOff + 0] = (uint)p;
+                    c = (uint)(p >> 32) & 1;
+                }
+            }
+
+            ulong x_1 = x[xOff + 1];
+            ulong zz_2 = zz[zzOff + 2];
+
+            {
+                zz_1 += x_1 * x_0;
+                w = (uint)zz_1;
+                zz[zzOff + 1] = (w << 1) | c;
+                c = w >> 31;
+                zz_2 += zz_1 >> 32;
+            }
+
+            ulong x_2 = x[xOff + 2];
+            ulong zz_3 = zz[zzOff + 3];
+            ulong zz_4 = zz[zzOff + 4];
+            {
+                zz_2 += x_2 * x_0;
+                w = (uint)zz_2;
+                zz[zzOff + 2] = (w << 1) | c;
+                c = w >> 31;
+                zz_3 += (zz_2 >> 32) + x_2 * x_1;
+                zz_4 += zz_3 >> 32;
+                zz_3 &= M;
+            }
+
+            ulong x_3 = x[xOff + 3];
+            ulong zz_5 = zz[zzOff + 5];
+            ulong zz_6 = zz[zzOff + 6];
+            {
+                zz_3 += x_3 * x_0;
+                w = (uint)zz_3;
+                zz[zzOff + 3] = (w << 1) | c;
+                c = w >> 31;
+                zz_4 += (zz_3 >> 32) + x_3 * x_1;
+                zz_5 += (zz_4 >> 32) + x_3 * x_2;
+                zz_4 &= M;
+                zz_6 += zz_5 >> 32;
+                zz_5 &= M;
+            }
+
+            ulong x_4 = x[xOff + 4];
+            ulong zz_7 = zz[zzOff + 7];
+            ulong zz_8 = zz[zzOff + 8];
+            {
+                zz_4 += x_4 * x_0;
+                w = (uint)zz_4;
+                zz[zzOff + 4] = (w << 1) | c;
+                c = w >> 31;
+                zz_5 += (zz_4 >> 32) + x_4 * x_1;
+                zz_6 += (zz_5 >> 32) + x_4 * x_2;
+                zz_5 &= M;
+                zz_7 += (zz_6 >> 32) + x_4 * x_3;
+                zz_6 &= M;
+                zz_8 += zz_7 >> 32;
+                zz_7 &= M;
+            }
+
+            ulong x_5 = x[xOff + 5];
+            ulong zz_9 = zz[zzOff + 9];
+            ulong zz_10 = zz[zzOff + 10];
+            {
+                zz_5 += x_5 * x_0;
+                w = (uint)zz_5;
+                zz[zzOff + 5] = (w << 1) | c;
+                c = w >> 31;
+                zz_6 += (zz_5 >> 32) + x_5 * x_1;
+                zz_7 += (zz_6 >> 32) + x_5 * x_2;
+                zz_6 &= M;
+                zz_8 += (zz_7 >> 32) + x_5 * x_3;
+                zz_7 &= M;
+                zz_9 += (zz_8 >> 32) + x_5 * x_4;
+                zz_8 &= M;
+                zz_10 += zz_9 >> 32;
+                zz_9 &= M;
+            }
+
+            ulong x_6 = x[xOff + 6];
+            ulong zz_11 = zz[zzOff + 11];
+            ulong zz_12 = zz[zzOff + 12];
+            {
+                zz_6 += x_6 * x_0;
+                w = (uint)zz_6;
+                zz[zzOff + 6] = (w << 1) | c;
+                c = w >> 31;
+                zz_7 += (zz_6 >> 32) + x_6 * x_1;
+                zz_8 += (zz_7 >> 32) + x_6 * x_2;
+                zz_7 &= M;
+                zz_9 += (zz_8 >> 32) + x_6 * x_3;
+                zz_8 &= M;
+                zz_10 += (zz_9 >> 32) + x_6 * x_4;
+                zz_9 &= M;
+                zz_11 += (zz_10 >> 32) + x_6 * x_5;
+                zz_10 &= M;
+                zz_12 += zz_11 >> 32;
+                zz_11 &= M;
+            }
+
+            ulong x_7 = x[xOff + 7];
+            ulong zz_13 = zz[zzOff + 13];
+            ulong zz_14 = zz[zzOff + 14];
+            {
+                zz_7 += x_7 * x_0;
+                w = (uint)zz_7;
+                zz[zzOff + 7] = (w << 1) | c;
+                c = w >> 31;
+                zz_8 += (zz_7 >> 32) + x_7 * x_1;
+                zz_9 += (zz_8 >> 32) + x_7 * x_2;
+                zz_10 += (zz_9 >> 32) + x_7 * x_3;
+                zz_11 += (zz_10 >> 32) + x_7 * x_4;
+                zz_12 += (zz_11 >> 32) + x_7 * x_5;
+                zz_13 += (zz_12 >> 32) + x_7 * x_6;
+                zz_14 += zz_13 >> 32;
+            }
+
+            w = (uint)zz_8;
+            zz[zzOff + 8] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_9;
+            zz[zzOff + 9] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_10;
+            zz[zzOff + 10] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_11;
+            zz[zzOff + 11] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_12;
+            zz[zzOff + 12] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_13;
+            zz[zzOff + 13] = (w << 1) | c;
+            c = w >> 31;
+            w = (uint)zz_14;
+            zz[zzOff + 14] = (w << 1) | c;
+            c = w >> 31;
+            w = zz[zzOff + 15] + (uint)(zz_14 >> 32);
+            zz[zzOff + 15] = (w << 1) | c;
+        }
+
+        public static int Sub(uint[] x, uint[] y, uint[] z)
+        {
+            long c = 0;
+            c += (long)x[0] - y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)x[1] - y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)x[2] - y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)x[3] - y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)x[4] - y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)x[5] - y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (long)x[6] - y[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            c += (long)x[7] - y[7];
+            z[7] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int Sub(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            c += (long)x[xOff + 0] - y[yOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 1] - y[yOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 2] - y[yOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 3] - y[yOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 4] - y[yOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 5] - y[yOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 6] - y[yOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            c += (long)x[xOff + 7] - y[yOff + 7];
+            z[zOff + 7] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubBothFrom(uint[] x, uint[] y, uint[] z)
+        {
+            long c = 0;
+            c += (long)z[0] - x[0] - y[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - x[1] - y[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)z[2] - x[2] - y[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)z[3] - x[3] - y[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)z[4] - x[4] - y[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)z[5] - x[5] - y[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (long)z[6] - x[6] - y[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            c += (long)z[7] - x[7] - y[7];
+            z[7] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubFrom(uint[] x, uint[] z)
+        {
+            long c = 0;
+            c += (long)z[0] - x[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - x[1];
+            z[1] = (uint)c;
+            c >>= 32;
+            c += (long)z[2] - x[2];
+            z[2] = (uint)c;
+            c >>= 32;
+            c += (long)z[3] - x[3];
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)z[4] - x[4];
+            z[4] = (uint)c;
+            c >>= 32;
+            c += (long)z[5] - x[5];
+            z[5] = (uint)c;
+            c >>= 32;
+            c += (long)z[6] - x[6];
+            z[6] = (uint)c;
+            c >>= 32;
+            c += (long)z[7] - x[7];
+            z[7] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static int SubFrom(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            c += (long)z[zOff + 0] - x[xOff + 0];
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 1] - x[xOff + 1];
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 2] - x[xOff + 2];
+            z[zOff + 2] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 3] - x[xOff + 3];
+            z[zOff + 3] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 4] - x[xOff + 4];
+            z[zOff + 4] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 5] - x[xOff + 5];
+            z[zOff + 5] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 6] - x[xOff + 6];
+            z[zOff + 6] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 7] - x[xOff + 7];
+            z[zOff + 7] = (uint)c;
+            c >>= 32;
+            return (int)c;
+        }
+
+        public static BigInteger ToBigInteger(uint[] x)
+        {
+            byte[] bs = new byte[32];
+            for (int i = 0; i < 8; ++i)
+            {
+                uint x_i = x[i];
+                if (x_i != 0)
+                {
+                    Pack.UInt32_To_BE(x_i, bs, (7 - i) << 2);
+                }
+            }
+            return new BigInteger(1, bs);
+        }
+
+        public static void Zero(uint[] z)
+        {
+            z[0] = 0;
+            z[1] = 0;
+            z[2] = 0;
+            z[3] = 0;
+            z[4] = 0;
+            z[5] = 0;
+            z[6] = 0;
+            z[7] = 0;
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/Nat384.cs b/crypto/src/math/ec/custom/sec/Nat384.cs
new file mode 100644
index 000000000..dd93e68b6
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/Nat384.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal abstract class Nat384
+    {
+        public static void Mul(uint[] x, uint[] y, uint[] zz)
+        {
+            Nat192.Mul(x, y, zz);
+            Nat192.Mul(x, 6, y, 6, zz, 12);
+
+            uint c18 = Nat192.AddToEachOther(zz, 6, zz, 12);
+            uint c12 = c18 + Nat192.AddTo(zz, 0, zz, 6, 0);
+            c18 += Nat192.AddTo(zz, 18, zz, 12, c12);
+
+            uint[] dx = Nat192.Create(), dy = Nat192.Create();
+            bool neg = Nat192.Diff(x, 6, x, 0, dx, 0) != Nat192.Diff(y, 6, y, 0, dy, 0);
+
+            uint[] tt = Nat192.CreateExt();
+            Nat192.Mul(dx, dy, tt);
+
+            c18 += neg ? Nat.AddTo(12, tt, 0, zz, 6) : (uint)Nat.SubFrom(12, tt, 0, zz, 6);
+            Nat.AddWordAt(24, c18, zz, 18);
+        }
+
+        public static void Square(uint[] x, uint[] zz)
+        {
+            Nat192.Square(x, zz);
+            Nat192.Square(x, 6, zz, 12);
+
+            uint c18 = Nat192.AddToEachOther(zz, 6, zz, 12);
+            uint c12 = c18 + Nat192.AddTo(zz, 0, zz, 6, 0);
+            c18 += Nat192.AddTo(zz, 18, zz, 12, c12);
+
+            uint[] dx = Nat192.Create();
+            Nat192.Diff(x, 6, x, 0, dx, 0);
+
+            uint[] m = Nat192.CreateExt();
+            Nat192.Square(dx, m);
+
+            c18 += (uint)Nat.SubFrom(12, m, 0, zz, 6);
+            Nat.AddWordAt(24, c18, zz, 18);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/Nat512.cs b/crypto/src/math/ec/custom/sec/Nat512.cs
new file mode 100644
index 000000000..46e10f995
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/Nat512.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal abstract class Nat512
+    {
+        public static void Mul(uint[] x, uint[] y, uint[] zz)
+        {
+            Nat256.Mul(x, y, zz);
+            Nat256.Mul(x, 8, y, 8, zz, 16);
+
+            uint c24 = Nat256.AddToEachOther(zz, 8, zz, 16);
+            uint c16 = c24 + Nat256.AddTo(zz, 0, zz, 8, 0);
+            c24 += Nat256.AddTo(zz, 24, zz, 16, c16);
+
+            uint[] dx = Nat256.Create(), dy = Nat256.Create();
+            bool neg = Nat256.Diff(x, 8, x, 0, dx, 0) != Nat256.Diff(y, 8, y, 0, dy, 0);
+
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Mul(dx, dy, tt);
+
+            c24 += neg ? Nat.AddTo(16, tt, 0, zz, 8) : (uint)Nat.SubFrom(16, tt, 0, zz, 8);
+            Nat.AddWordAt(32, c24, zz, 24); 
+        }
+
+        public static void Square(uint[] x, uint[] zz)
+        {
+            Nat256.Square(x, zz);
+            Nat256.Square(x, 8, zz, 16);
+
+            uint c24 = Nat256.AddToEachOther(zz, 8, zz, 16);
+            uint c16 = c24 + Nat256.AddTo(zz, 0, zz, 8, 0);
+            c24 += Nat256.AddTo(zz, 24, zz, 16, c16);
+
+            uint[] dx = Nat256.Create();
+            Nat256.Diff(x, 8, x, 0, dx, 0);
+
+            uint[] m = Nat256.CreateExt();
+            Nat256.Square(dx, m);
+
+            c24 += (uint)Nat.SubFrom(16, m, 0, zz, 8);
+            Nat.AddWordAt(32, c24, zz, 24); 
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
new file mode 100644
index 000000000..81f77197e
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
@@ -0,0 +1,75 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP192K1Curve
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = new BigInteger(1,
+            Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"));
+
+        private const int SECP192K1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+        protected readonly SecP192K1Point m_infinity;
+
+        public SecP192K1Curve()
+            : base(q)
+        {
+            this.m_infinity = new SecP192K1Point(this, null, null);
+
+            this.m_a = FromBigInteger(BigInteger.Zero);
+            this.m_b = FromBigInteger(BigInteger.ValueOf(3));
+            this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"));
+            this.m_cofactor = BigInteger.One;
+            this.m_coord = SECP192K1_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new SecP192K1Curve();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+                case COORD_JACOBIAN:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new SecP192K1FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new SecP192K1Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new SecP192K1Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Field.cs b/crypto/src/math/ec/custom/sec/SecP192K1Field.cs
new file mode 100644
index 000000000..d5ca903d1
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP192K1Field.cs
@@ -0,0 +1,176 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP192K1Field
+    {
+        // 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
+        internal static readonly uint[] P = new uint[]{ 0xFFFFEE37, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        internal static readonly uint[] PExt = new uint[]{ 0x013C4FD1, 0x00002392, 0x00000001, 0x00000000, 0x00000000,
+            0x00000000, 0xFFFFDC6E, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        private static readonly uint[] PExtInv = new uint[]{ 0xFEC3B02F, 0xFFFFDC6D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF, 0x00002391, 0x00000002 };
+        private const uint P5 = 0xFFFFFFFF;
+        private const uint PExt11 = 0xFFFFFFFF;
+        private const uint PInv33 = 0x11C9;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            uint c = Nat192.Add(x, y, z);
+            if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                Nat.Add33To(6, PInv33, z);
+            }
+        }
+
+        public static void AddExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            uint c = Nat.Add(12, xx, yy, zz);
+            if (c != 0 || (zz[11] == PExt11 && Nat.Gte(12, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(12, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            uint c = Nat.Inc(6, x, z);
+            if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                Nat.Add33To(6, PInv33, z);
+            }
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat192.FromBigInteger(x);
+            if (z[5] == P5 && Nat192.Gte(z, P))
+            {
+                Nat192.SubFrom(P, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            if ((x[0] & 1) == 0)
+            {
+                Nat.ShiftDownBit(6, x, 0, z);
+            }
+            else
+            {
+                uint c = Nat192.Add(x, P, z);
+                Nat.ShiftDownBit(6, z, c);
+            }
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat192.CreateExt();
+            Nat192.Mul(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz)
+        {
+            uint c = Nat192.MulAddTo(x, y, zz);
+            if (c != 0 || (zz[11] == PExt11 && Nat.Gte(12, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(12, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat192.IsZero(x))
+            {
+                Nat192.Zero(z);
+            }
+            else
+            {
+                Nat192.Sub(P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            ulong cc = Nat192.Mul33Add(PInv33, xx, 6, xx, 0, z, 0);
+            uint c = Nat192.Mul33DWordAdd(PInv33, cc, z, 0);
+
+            Debug.Assert(c == 0 || c == 1);
+
+            if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                Nat.Add33To(6, PInv33, z);
+            }
+        }
+
+        public static void Reduce32(uint x, uint[] z)
+        {
+            if ((x != 0 && Nat192.Mul33WordAdd(PInv33, x, z, 0) != 0)
+                || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                Nat.Add33To(6, PInv33, z);
+            }
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat192.CreateExt();
+            Nat192.Square(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            uint[] tt = Nat192.CreateExt();
+            Nat192.Square(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                Nat192.Square(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat192.Sub(x, y, z);
+            if (c != 0)
+            {
+                Nat.Sub33From(6, PInv33, z);
+            }
+        }
+
+        public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            int c = Nat.Sub(12, xx, yy, zz);
+            if (c != 0)
+            {
+                if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.DecAt(12, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            uint c = Nat.ShiftUpBit(6, x, 0, z);
+            if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                Nat.Add33To(6, PInv33, z);
+            }
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs
new file mode 100644
index 000000000..78886dd8c
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs
@@ -0,0 +1,212 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP192K1FieldElement
+        : ECFieldElement
+    {
+        public static readonly BigInteger Q = SecP192K1Curve.q;
+
+        protected internal readonly uint[] x;
+
+        public SecP192K1FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for SecP192K1FieldElement", "x");
+
+            this.x = SecP192K1Field.FromBigInteger(x);
+        }
+
+        public SecP192K1FieldElement()
+        {
+            this.x = Nat192.Create();
+        }
+
+        protected internal SecP192K1FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat192.IsZero(x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat192.IsOne(x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat192.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat192.ToBigInteger(x);
+        }
+
+        public override string FieldName
+        {
+            get { return "SecP192K1Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat192.Create();
+            SecP192K1Field.Add(x, ((SecP192K1FieldElement)b).x, z);
+            return new SecP192K1FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat192.Create();
+            SecP192K1Field.AddOne(x, z);
+            return new SecP192K1FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat192.Create();
+            SecP192K1Field.Subtract(x, ((SecP192K1FieldElement)b).x, z);
+            return new SecP192K1FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat192.Create();
+            SecP192K1Field.Multiply(x, ((SecP192K1FieldElement)b).x, z);
+            return new SecP192K1FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat192.Create();
+            Mod.Invert(SecP192K1Field.P, ((SecP192K1FieldElement)b).x, z);
+            SecP192K1Field.Multiply(z, x, z);
+            return new SecP192K1FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat192.Create();
+            SecP192K1Field.Negate(x, z);
+            return new SecP192K1FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat192.Create();
+            SecP192K1Field.Square(x, z);
+            return new SecP192K1FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new SecP192K1FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat192.Create();
+            Mod.Invert(SecP192K1Field.P, x, z);
+            return new SecP192K1FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            /*
+             * Raise this element to the exponent 2^190 - 2^30 - 2^10 - 2^6 - 2^5 - 2^4 - 2^1
+             * 
+             * Breaking up the exponent's binary representation into "repunits", we get:
+             * { 159 1s } { 1 0s } { 19 1s } { 1 0s } { 3 1s } { 3 0s} { 3 1s } { 1 0s }
+             * 
+             * Therefore we need an addition chain containing 3, 19, 159 (the lengths of the repunits)
+             * We use: 1, 2, [3], 6, 8, 16, [19], 35, 70, 140, [159]
+             */
+
+            uint[] x1 = this.x;
+            if (Nat192.IsZero(x1) || Nat192.IsOne(x1))
+                return this;
+
+            uint[] x2 = Nat192.Create();
+            SecP192K1Field.Square(x1, x2);
+            SecP192K1Field.Multiply(x2, x1, x2);
+            uint[] x3 = Nat192.Create();
+            SecP192K1Field.Square(x2, x3);
+            SecP192K1Field.Multiply(x3, x1, x3);
+            uint[] x6 = Nat192.Create();
+            SecP192K1Field.SquareN(x3, 3, x6);
+            SecP192K1Field.Multiply(x6, x3, x6);
+            uint[] x8 = x6;
+            SecP192K1Field.SquareN(x6, 2, x8);
+            SecP192K1Field.Multiply(x8, x2, x8);
+            uint[] x16 = x2;
+            SecP192K1Field.SquareN(x8, 8, x16);
+            SecP192K1Field.Multiply(x16, x8, x16);
+            uint[] x19 = x8;
+            SecP192K1Field.SquareN(x16, 3, x19);
+            SecP192K1Field.Multiply(x19, x3, x19);
+            uint[] x35 = Nat192.Create();
+            SecP192K1Field.SquareN(x19, 16, x35);
+            SecP192K1Field.Multiply(x35, x16, x35);
+            uint[] x70 = x16;
+            SecP192K1Field.SquareN(x35, 35, x70);
+            SecP192K1Field.Multiply(x70, x35, x70);
+            uint[] x140 = x35;
+            SecP192K1Field.SquareN(x70, 70, x140);
+            SecP192K1Field.Multiply(x140, x70, x140);
+            uint[] x159 = x70;
+            SecP192K1Field.SquareN(x140, 19, x159);
+            SecP192K1Field.Multiply(x159, x19, x159);
+
+            uint[] t1 = x159;
+            SecP192K1Field.SquareN(t1, 20, t1);
+            SecP192K1Field.Multiply(t1, x19, t1);
+            SecP192K1Field.SquareN(t1, 4, t1);
+            SecP192K1Field.Multiply(t1, x3, t1);
+            SecP192K1Field.SquareN(t1, 6, t1);
+            SecP192K1Field.Multiply(t1, x3, t1);
+            SecP192K1Field.Square(t1, t1);
+
+            uint[] t2 = x3;
+            SecP192K1Field.Square(t1, t2);
+
+            return Nat192.Eq(x1, t2) ? new SecP192K1FieldElement(t1) : null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as SecP192K1FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as SecP192K1FieldElement);
+        }
+
+        public virtual bool Equals(SecP192K1FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat192.Eq(x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 6);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Point.cs b/crypto/src/math/ec/custom/sec/SecP192K1Point.cs
new file mode 100644
index 000000000..648aca502
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP192K1Point.cs
@@ -0,0 +1,265 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP192K1Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
+         */
+        public SecP192K1Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer
+         *             {@link #getEncoded(bool)}
+         */
+        public SecP192K1Point(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");
+        }
+
+        internal SecP192K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs,
+            bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new SecP192K1Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            SecP192K1FieldElement X1 = (SecP192K1FieldElement)this.RawXCoord, Y1 = (SecP192K1FieldElement)this.RawYCoord;
+            SecP192K1FieldElement X2 = (SecP192K1FieldElement)b.RawXCoord, Y2 = (SecP192K1FieldElement)b.RawYCoord;
+
+            SecP192K1FieldElement Z1 = (SecP192K1FieldElement)this.RawZCoords[0];
+            SecP192K1FieldElement Z2 = (SecP192K1FieldElement)b.RawZCoords[0];
+
+            uint c;
+            uint[] tt1 = Nat192.CreateExt();
+            uint[] t2 = Nat192.Create();
+            uint[] t3 = Nat192.Create();
+            uint[] t4 = Nat192.Create();
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                SecP192K1Field.Square(Z1.x, S2);
+
+                U2 = t2;
+                SecP192K1Field.Multiply(S2, X2.x, U2);
+
+                SecP192K1Field.Multiply(S2, Z1.x, S2);
+                SecP192K1Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                SecP192K1Field.Square(Z2.x, S1);
+
+                U1 = tt1;
+                SecP192K1Field.Multiply(S1, X1.x, U1);
+
+                SecP192K1Field.Multiply(S1, Z2.x, S1);
+                SecP192K1Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat192.Create();
+            SecP192K1Field.Subtract(U1, U2, H);
+
+            uint[] R = t2;
+            SecP192K1Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat192.IsZero(H))
+            {
+                if (Nat192.IsZero(R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = t3;
+            SecP192K1Field.Square(H, HSquared);
+
+            uint[] G = Nat192.Create();
+            SecP192K1Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            SecP192K1Field.Multiply(HSquared, U1, V);
+
+            SecP192K1Field.Negate(G, G);
+            Nat192.Mul(S1, G, tt1);
+
+            c = Nat192.AddBothTo(V, V, G);
+            SecP192K1Field.Reduce32(c, G);
+
+            SecP192K1FieldElement X3 = new SecP192K1FieldElement(t4);
+            SecP192K1Field.Square(R, X3.x);
+            SecP192K1Field.Subtract(X3.x, G, X3.x);
+
+            SecP192K1FieldElement Y3 = new SecP192K1FieldElement(G);
+            SecP192K1Field.Subtract(V, X3.x, Y3.x);
+            SecP192K1Field.MultiplyAddToExt(Y3.x, R, tt1);
+            SecP192K1Field.Reduce(tt1, Y3.x);
+
+            SecP192K1FieldElement Z3 = new SecP192K1FieldElement(H);
+            if (!Z1IsOne)
+            {
+                SecP192K1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                SecP192K1Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            ECFieldElement[] zs = new ECFieldElement[] { Z3 };
+
+            return new SecP192K1Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            SecP192K1FieldElement Y1 = (SecP192K1FieldElement)this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            SecP192K1FieldElement X1 = (SecP192K1FieldElement)this.RawXCoord, Z1 = (SecP192K1FieldElement)this.RawZCoords[0];
+
+            uint c;
+
+            uint[] Y1Squared = Nat192.Create();
+            SecP192K1Field.Square(Y1.x, Y1Squared);
+
+            uint[] T = Nat192.Create();
+            SecP192K1Field.Square(Y1Squared, T);
+
+            uint[] M = Nat192.Create();
+            SecP192K1Field.Square(X1.x, M);
+            c = Nat192.AddBothTo(M, M, M);
+            SecP192K1Field.Reduce32(c, M);
+
+            uint[] S = Y1Squared;
+            SecP192K1Field.Multiply(Y1Squared, X1.x, S);
+            c = Nat.ShiftUpBits(6, S, 2, 0);
+            SecP192K1Field.Reduce32(c, S);
+
+            uint[] t1 = Nat192.Create();
+            c = Nat.ShiftUpBits(6, T, 3, 0, t1);
+            SecP192K1Field.Reduce32(c, t1);
+
+            SecP192K1FieldElement X3 = new SecP192K1FieldElement(T);
+            SecP192K1Field.Square(M, X3.x);
+            SecP192K1Field.Subtract(X3.x, S, X3.x);
+            SecP192K1Field.Subtract(X3.x, S, X3.x);
+
+            SecP192K1FieldElement Y3 = new SecP192K1FieldElement(S);
+            SecP192K1Field.Subtract(S, X3.x, Y3.x);
+            SecP192K1Field.Multiply(Y3.x, M, Y3.x);
+            SecP192K1Field.Subtract(Y3.x, t1, Y3.x);
+
+            SecP192K1FieldElement Z3 = new SecP192K1FieldElement(M);
+            SecP192K1Field.Twice(Y1.x, Z3.x);
+            if (!Z1.IsOne)
+            {
+                SecP192K1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            return new SecP192K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, 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.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return Twice().Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+            return Twice().Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new SecP192K1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
new file mode 100644
index 000000000..cb3a981c8
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
@@ -0,0 +1,78 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP192R1Curve
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = new BigInteger(1,
+            Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"));
+
+        private const int SecP192R1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+        protected readonly SecP192R1Point m_infinity;
+
+        public SecP192R1Curve()
+            : base(q)
+        {
+            this.m_infinity = new SecP192R1Point(this, null, null);
+
+            this.m_a = FromBigInteger(new BigInteger(1,
+                Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC")));
+            this.m_b = FromBigInteger(new BigInteger(1,
+                Hex.Decode("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1")));
+            this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"));
+            this.m_cofactor = BigInteger.One;
+
+            this.m_coord = SecP192R1_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new SecP192R1Curve();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+            case COORD_JACOBIAN:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new SecP192R1FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new SecP192R1Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new SecP192R1Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Field.cs b/crypto/src/math/ec/custom/sec/SecP192R1Field.cs
new file mode 100644
index 000000000..85e3a0394
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP192R1Field.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP192R1Field
+    {
+        // 2^192 - 2^64 - 1
+        internal static readonly uint[] P = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        internal static readonly uint[] PExt = new uint[]{ 0x00000001, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
+            0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        private static readonly uint[] PExtInv = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFE,
+            0xFFFFFFFF, 0x00000001, 0x00000000, 0x00000002 };
+        private const uint P5 = 0xFFFFFFFF;
+        private const uint PExt11 = 0xFFFFFFFF;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            uint c = Nat192.Add(x, y, z);
+            if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void AddExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            uint c = Nat.Add(12, xx, yy, zz);
+            if (c != 0 || (zz[11] == PExt11 && Nat.Gte(12, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(12, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            uint c = Nat.Inc(6, x, z);
+            if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat192.FromBigInteger(x);
+            if (z[5] == P5 && Nat192.Gte(z, P))
+            {
+                Nat192.SubFrom(P, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            if ((x[0] & 1) == 0)
+            {
+                Nat.ShiftDownBit(6, x, 0, z);
+            }
+            else
+            {
+                uint c = Nat192.Add(x, P, z);
+                Nat.ShiftDownBit(6, z, c);
+            }
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat192.CreateExt();
+            Nat192.Mul(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz)
+        {
+            uint c = Nat192.MulAddTo(x, y, zz);
+            if (c != 0 || (zz[11] == PExt11 && Nat.Gte(12, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(12, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat192.IsZero(x))
+            {
+                Nat192.Zero(z);
+            }
+            else
+            {
+                Nat192.Sub(P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            ulong xx06 = xx[6], xx07 = xx[7], xx08 = xx[8];
+            ulong xx09 = xx[9], xx10 = xx[10], xx11 = xx[11];
+
+            ulong t0 = xx06 + xx10;
+            ulong t1 = xx07 + xx11;
+
+            ulong cc = 0;
+            cc += (ulong)xx[0] + t0;
+            uint z0 = (uint)cc;
+            cc >>= 32;
+            cc += (ulong)xx[1] + t1;
+            z[1] = (uint)cc;
+            cc >>= 32;
+
+            t0 += xx08;
+            t1 += xx09;
+
+            cc += (ulong)xx[2] + t0;
+            ulong z2 = (uint)cc;
+            cc >>= 32;
+            cc += (ulong)xx[3] + t1;
+            z[3] = (uint)cc;
+            cc >>= 32;
+
+            t0 -= xx06;
+            t1 -= xx07;
+
+            cc += (ulong)xx[4] + t0;
+            z[4] = (uint)cc;
+            cc >>= 32;
+            cc += (ulong)xx[5] + t1;
+            z[5] = (uint)cc;
+            cc >>= 32;
+
+            z2 += cc;
+
+            cc += z0;
+            z[0] = (uint)cc;
+            cc >>= 32;
+            if (cc != 0)
+            {
+                cc += z[1];
+                z[1] = (uint)cc;
+                z2 += cc >> 32;
+            }
+            z[2] = (uint)z2;
+            cc  = z2 >> 32;
+
+            Debug.Assert(cc == 0 || cc == 1);
+
+            if ((cc != 0 && Nat.IncAt(6, z, 3) != 0)
+                || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void Reduce32(uint x, uint[] z)
+        {
+            ulong cc = 0;
+
+            if (x != 0)
+            {
+                cc += (ulong)z[0] + x;
+                z[0] = (uint)cc;
+                cc >>= 32;
+                if (cc != 0)
+                {
+                    cc += (ulong)z[1];
+                    z[1] = (uint)cc;
+                    cc >>= 32;
+                }
+                cc += (ulong)z[2] + x;
+                z[2] = (uint)cc;
+                cc >>= 32;
+
+                Debug.Assert(cc == 0 || cc == 1);
+            }
+
+            if ((cc != 0 && Nat.IncAt(6, z, 3) != 0)
+                || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat192.CreateExt();
+            Nat192.Square(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            uint[] tt = Nat192.CreateExt();
+            Nat192.Square(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                Nat192.Square(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat192.Sub(x, y, z);
+            if (c != 0)
+            {
+                SubPInvFrom(z);
+            }
+        }
+
+        public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            int c = Nat.Sub(12, xx, yy, zz);
+            if (c != 0)
+            {
+                if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.DecAt(12, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            uint c = Nat.ShiftUpBit(6, x, 0, z);
+            if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        private static void AddPInvTo(uint[] z)
+        {
+            long c = (long)z[0] + 1;
+            z[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[1];
+                z[1] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[2] + 1;
+            z[2] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                Nat.IncAt(6, z, 3);
+            }
+        }
+
+        private static void SubPInvFrom(uint[] z)
+        {
+            long c = (long)z[0] - 1;
+            z[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[1];
+                z[1] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[2] - 1;
+            z[2] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                Nat.DecAt(6, z, 3);
+            }
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs
new file mode 100644
index 000000000..020c5cdbb
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs
@@ -0,0 +1,187 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP192R1FieldElement
+        : ECFieldElement
+    {
+        public static readonly BigInteger Q = SecP192R1Curve.q;
+
+        protected internal readonly uint[] x;
+
+        public SecP192R1FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for SecP192R1FieldElement", "x");
+
+            this.x = SecP192R1Field.FromBigInteger(x);
+        }
+
+        public SecP192R1FieldElement()
+        {
+            this.x = Nat192.Create();
+        }
+
+        protected internal SecP192R1FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat192.IsZero(x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat192.IsOne(x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat192.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat192.ToBigInteger(x);
+        }
+
+        public override string FieldName
+        {
+            get { return "SecP192R1Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat192.Create();
+            SecP192R1Field.Add(x, ((SecP192R1FieldElement)b).x, z);
+            return new SecP192R1FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat192.Create();
+            SecP192R1Field.AddOne(x, z);
+            return new SecP192R1FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat192.Create();
+            SecP192R1Field.Subtract(x, ((SecP192R1FieldElement)b).x, z);
+            return new SecP192R1FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat192.Create();
+            SecP192R1Field.Multiply(x, ((SecP192R1FieldElement)b).x, z);
+            return new SecP192R1FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat192.Create();
+            Mod.Invert(SecP192R1Field.P, ((SecP192R1FieldElement)b).x, z);
+            SecP192R1Field.Multiply(z, x, z);
+            return new SecP192R1FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat192.Create();
+            SecP192R1Field.Negate(x, z);
+            return new SecP192R1FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat192.Create();
+            SecP192R1Field.Square(x, z);
+            return new SecP192R1FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new SecP192R1FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat192.Create();
+            Mod.Invert(SecP192R1Field.P, x, z);
+            return new SecP192R1FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            // Raise this element to the exponent 2^190 - 2^62
+
+            uint[] x1 = this.x;
+            if (Nat192.IsZero(x1) || Nat192.IsOne(x1))
+                return this;
+
+            uint[] t1 = Nat192.Create();
+            uint[] t2 = Nat192.Create();
+
+            SecP192R1Field.Square(x1, t1);
+            SecP192R1Field.Multiply(t1, x1, t1);
+
+            SecP192R1Field.SquareN(t1, 2, t2);
+            SecP192R1Field.Multiply(t2, t1, t2);
+
+            SecP192R1Field.SquareN(t2, 4, t1);
+            SecP192R1Field.Multiply(t1, t2, t1);
+
+            SecP192R1Field.SquareN(t1, 8, t2);
+            SecP192R1Field.Multiply(t2, t1, t2);
+
+            SecP192R1Field.SquareN(t2, 16, t1);
+            SecP192R1Field.Multiply(t1, t2, t1);
+
+            SecP192R1Field.SquareN(t1, 32, t2);
+            SecP192R1Field.Multiply(t2, t1, t2);
+
+            SecP192R1Field.SquareN(t2, 64, t1);
+            SecP192R1Field.Multiply(t1, t2, t1);
+
+            SecP192R1Field.SquareN(t1, 62, t1);
+            SecP192R1Field.Square(t1, t2);
+
+            return Nat192.Eq(x1, t2) ? new SecP192R1FieldElement(t1) : null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as SecP192R1FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as SecP192R1FieldElement);
+        }
+
+        public virtual bool Equals(SecP192R1FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat192.Eq(x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 6);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Point.cs b/crypto/src/math/ec/custom/sec/SecP192R1Point.cs
new file mode 100644
index 000000000..797a8de35
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP192R1Point.cs
@@ -0,0 +1,277 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP192R1Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
+         */
+        public SecP192R1Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer
+         *             {@link #getEncoded(bool)}
+         */
+        public SecP192R1Point(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");
+        }
+
+        internal SecP192R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new SecP192R1Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            SecP192R1FieldElement X1 = (SecP192R1FieldElement)this.RawXCoord, Y1 = (SecP192R1FieldElement)this.RawYCoord;
+            SecP192R1FieldElement X2 = (SecP192R1FieldElement)b.RawXCoord, Y2 = (SecP192R1FieldElement)b.RawYCoord;
+
+            SecP192R1FieldElement Z1 = (SecP192R1FieldElement)this.RawZCoords[0];
+            SecP192R1FieldElement Z2 = (SecP192R1FieldElement)b.RawZCoords[0];
+
+            uint c;
+            uint[] tt1 = Nat192.CreateExt();
+            uint[] t2 = Nat192.Create();
+            uint[] t3 = Nat192.Create();
+            uint[] t4 = Nat192.Create();
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                SecP192R1Field.Square(Z1.x, S2);
+
+                U2 = t2;
+                SecP192R1Field.Multiply(S2, X2.x, U2);
+
+                SecP192R1Field.Multiply(S2, Z1.x, S2);
+                SecP192R1Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                SecP192R1Field.Square(Z2.x, S1);
+
+                U1 = tt1;
+                SecP192R1Field.Multiply(S1, X1.x, U1);
+
+                SecP192R1Field.Multiply(S1, Z2.x, S1);
+                SecP192R1Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat192.Create();
+            SecP192R1Field.Subtract(U1, U2, H);
+
+            uint[] R = t2;
+            SecP192R1Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat192.IsZero(H))
+            {
+                if (Nat192.IsZero(R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = t3;
+            SecP192R1Field.Square(H, HSquared);
+
+            uint[] G = Nat192.Create();
+            SecP192R1Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            SecP192R1Field.Multiply(HSquared, U1, V);
+
+            SecP192R1Field.Negate(G, G);
+            Nat192.Mul(S1, G, tt1);
+
+            c = Nat192.AddBothTo(V, V, G);
+            SecP192R1Field.Reduce32(c, G);
+
+            SecP192R1FieldElement X3 = new SecP192R1FieldElement(t4);
+            SecP192R1Field.Square(R, X3.x);
+            SecP192R1Field.Subtract(X3.x, G, X3.x);
+
+            SecP192R1FieldElement Y3 = new SecP192R1FieldElement(G);
+            SecP192R1Field.Subtract(V, X3.x, Y3.x);
+            SecP192R1Field.MultiplyAddToExt(Y3.x, R, tt1);
+            SecP192R1Field.Reduce(tt1, Y3.x);
+
+            SecP192R1FieldElement Z3 = new SecP192R1FieldElement(H);
+            if (!Z1IsOne)
+            {
+                SecP192R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                SecP192R1Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            ECFieldElement[] zs = new ECFieldElement[] { Z3 };
+
+            return new SecP192R1Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            SecP192R1FieldElement Y1 = (SecP192R1FieldElement)this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            SecP192R1FieldElement X1 = (SecP192R1FieldElement)this.RawXCoord, Z1 = (SecP192R1FieldElement)this.RawZCoords[0];
+
+            uint c;
+            uint[] t1 = Nat192.Create();
+            uint[] t2 = Nat192.Create();
+
+            uint[] Y1Squared = Nat192.Create();
+            SecP192R1Field.Square(Y1.x, Y1Squared);
+
+            uint[] T = Nat192.Create();
+            SecP192R1Field.Square(Y1Squared, T);
+
+            bool Z1IsOne = Z1.IsOne;
+
+            uint[] Z1Squared = Z1.x;
+            if (!Z1IsOne)
+            {
+                Z1Squared = t2;
+                SecP192R1Field.Square(Z1.x, Z1Squared);
+            }
+
+            SecP192R1Field.Subtract(X1.x, Z1Squared, t1);
+
+            uint[] M = t2;
+            SecP192R1Field.Add(X1.x, Z1Squared, M);
+            SecP192R1Field.Multiply(M, t1, M);
+            c = Nat192.AddBothTo(M, M, M);
+            SecP192R1Field.Reduce32(c, M);
+
+            uint[] S = Y1Squared;
+            SecP192R1Field.Multiply(Y1Squared, X1.x, S);
+            c = Nat.ShiftUpBits(6, S, 2, 0);
+            SecP192R1Field.Reduce32(c, S);
+
+            c = Nat.ShiftUpBits(6, T, 3, 0, t1);
+            SecP192R1Field.Reduce32(c, t1);
+
+            SecP192R1FieldElement X3 = new SecP192R1FieldElement(T);
+            SecP192R1Field.Square(M, X3.x);
+            SecP192R1Field.Subtract(X3.x, S, X3.x);
+            SecP192R1Field.Subtract(X3.x, S, X3.x);
+
+            SecP192R1FieldElement Y3 = new SecP192R1FieldElement(S);
+            SecP192R1Field.Subtract(S, X3.x, Y3.x);
+            SecP192R1Field.Multiply(Y3.x, M, Y3.x);
+            SecP192R1Field.Subtract(Y3.x, t1, Y3.x);
+
+            SecP192R1FieldElement Z3 = new SecP192R1FieldElement(M);
+            SecP192R1Field.Twice(Y1.x, Z3.x);
+            if (!Z1IsOne)
+            {
+                SecP192R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            return new SecP192R1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, 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.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return Twice().Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+            return Twice().Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new SecP192R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
new file mode 100644
index 000000000..d4be7d8de
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
@@ -0,0 +1,75 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP224K1Curve
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = new BigInteger(1,
+            Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D"));
+
+        private const int SECP224K1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+        protected readonly SecP224K1Point m_infinity;
+
+        public SecP224K1Curve()
+            : base(q)
+        {
+            this.m_infinity = new SecP224K1Point(this, null, null);
+
+            this.m_a = FromBigInteger(BigInteger.Zero);
+            this.m_b = FromBigInteger(BigInteger.ValueOf(5));
+            this.m_order = new BigInteger(1, Hex.Decode("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7"));
+            this.m_cofactor = BigInteger.One;
+            this.m_coord = SECP224K1_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new SecP224K1Curve();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+                case COORD_JACOBIAN:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new SecP224K1FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new SecP224K1Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new SecP224K1Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Field.cs b/crypto/src/math/ec/custom/sec/SecP224K1Field.cs
new file mode 100644
index 000000000..a55810c6d
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP224K1Field.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP224K1Field
+    {
+        // 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 2 - 1
+        internal static readonly uint[] P = new uint[]{ 0xFFFFE56D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF };
+        internal static readonly uint[] PExt = new uint[]{ 0x02C23069, 0x00003526, 0x00000001, 0x00000000, 0x00000000,
+            0x00000000, 0x00000000, 0xFFFFCADA, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        private static readonly uint[] PExtInv = new uint[]{ 0xFD3DCF97, 0xFFFFCAD9, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF, 0xFFFFFFFF, 0x00003525, 0x00000002 };
+        private const uint P6 = 0xFFFFFFFF;
+        private const uint PExt13 = 0xFFFFFFFF;
+        private const uint PInv33 = 0x1A93;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            uint c = Nat224.Add(x, y, z);
+            if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                Nat.Add33To(7, PInv33, z);
+            }
+        }
+
+        public static void AddExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            uint c = Nat.Add(14, xx, yy, zz);
+            if (c != 0 || (zz[13] == PExt13 && Nat.Gte(14, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(14, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            uint c = Nat.Inc(7, x, z);
+            if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                Nat.Add33To(7, PInv33, z);
+            }
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat224.FromBigInteger(x);
+            if (z[6] == P6 && Nat224.Gte(z, P))
+            {
+                Nat224.SubFrom(P, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            if ((x[0] & 1) == 0)
+            {
+                Nat.ShiftDownBit(7, x, 0, z);
+            }
+            else
+            {
+                uint c = Nat224.Add(x, P, z);
+                Nat.ShiftDownBit(7, z, c);
+            }
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat224.CreateExt();
+            Nat224.Mul(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz)
+        {
+            uint c = Nat224.MulAddTo(x, y, zz);
+            if (c != 0 || (zz[13] == PExt13 && Nat.Gte(14, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(14, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat224.IsZero(x))
+            {
+                Nat224.Zero(z);
+            }
+            else
+            {
+                Nat224.Sub(P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            ulong cc = Nat224.Mul33Add(PInv33, xx, 7, xx, 0, z, 0);
+            uint c = Nat224.Mul33DWordAdd(PInv33, cc, z, 0);
+
+            Debug.Assert(c == 0 || c == 1);
+
+            if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                Nat.Add33To(7, PInv33, z);
+            }
+        }
+
+        public static void Reduce32(uint x, uint[] z)
+        {
+            if ((x != 0 && Nat224.Mul33WordAdd(PInv33, x, z, 0) != 0)
+                || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                Nat.Add33To(7, PInv33, z);
+            }
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat224.CreateExt();
+            Nat224.Square(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            uint[] tt = Nat224.CreateExt();
+            Nat224.Square(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                Nat224.Square(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat224.Sub(x, y, z);
+            if (c != 0)
+            {
+                Nat.Sub33From(7, PInv33, z);
+            }
+        }
+
+        public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            int c = Nat.Sub(14, xx, yy, zz);
+            if (c != 0)
+            {
+                if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.DecAt(14, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            uint c = Nat.ShiftUpBit(7, x, 0, z);
+            if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                Nat.Add33To(7, PInv33, z);
+            }
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs
new file mode 100644
index 000000000..72ff4b099
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs
@@ -0,0 +1,241 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP224K1FieldElement
+        : ECFieldElement
+    {
+        public static readonly BigInteger Q = SecP224K1Curve.q;
+
+        // Calculated as BigInteger.Two.ModPow(Q.ShiftRight(2), Q)
+        private static readonly uint[] PRECOMP_POW2 = new uint[]{ 0x33bfd202, 0xdcfad133, 0x2287624a, 0xc3811ba8,
+            0xa85558fc, 0x1eaef5d7, 0x8edf154c };
+
+        protected internal readonly uint[] x;
+
+        public SecP224K1FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for SecP224K1FieldElement", "x");
+
+            this.x = SecP224K1Field.FromBigInteger(x);
+        }
+
+        public SecP224K1FieldElement()
+        {
+            this.x = Nat224.Create();
+        }
+
+        protected internal SecP224K1FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat224.IsZero(x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat224.IsOne(x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat224.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat224.ToBigInteger(x);
+        }
+
+        public override string FieldName
+        {
+            get { return "SecP224K1Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat224.Create();
+            SecP224K1Field.Add(x, ((SecP224K1FieldElement)b).x, z);
+            return new SecP224K1FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat224.Create();
+            SecP224K1Field.AddOne(x, z);
+            return new SecP224K1FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat224.Create();
+            SecP224K1Field.Subtract(x, ((SecP224K1FieldElement)b).x, z);
+            return new SecP224K1FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat224.Create();
+            SecP224K1Field.Multiply(x, ((SecP224K1FieldElement)b).x, z);
+            return new SecP224K1FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat224.Create();
+            Mod.Invert(SecP224K1Field.P, ((SecP224K1FieldElement)b).x, z);
+            SecP224K1Field.Multiply(z, x, z);
+            return new SecP224K1FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat224.Create();
+            SecP224K1Field.Negate(x, z);
+            return new SecP224K1FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat224.Create();
+            SecP224K1Field.Square(x, z);
+            return new SecP224K1FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new SecP224K1FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat224.Create();
+            Mod.Invert(SecP224K1Field.P, x, z);
+            return new SecP224K1FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            /*
+             * Q == 8m + 5, so we use Pocklington's method for this case.
+             *
+             * First, raise this element to the exponent 2^221 - 2^29 - 2^9 - 2^8 - 2^6 - 2^4 - 2^1 (i.e. m + 1)
+             * 
+             * Breaking up the exponent's binary representation into "repunits", we get:
+             * { 191 1s } { 1 0s } { 19 1s } { 2 0s } { 1 1s } { 1 0s} { 1 1s } { 1 0s} { 3 1s } { 1 0s}
+             * 
+             * Therefore we need an addition chain containing 1, 3, 19, 191 (the lengths of the repunits)
+             * We use: [1], 2, [3], 4, 8, 11, [19], 23, 42, 84, 107, [191]
+             */
+
+            uint[] x1 = this.x;
+            if (Nat224.IsZero(x1) || Nat224.IsOne(x1))
+                return this;
+
+            uint[] x2 = Nat224.Create();
+            SecP224K1Field.Square(x1, x2);
+            SecP224K1Field.Multiply(x2, x1, x2);
+            uint[] x3 = x2;
+            SecP224K1Field.Square(x2, x3);
+            SecP224K1Field.Multiply(x3, x1, x3);
+            uint[] x4 = Nat224.Create();
+            SecP224K1Field.Square(x3, x4);
+            SecP224K1Field.Multiply(x4, x1, x4);
+            uint[] x8 = Nat224.Create();
+            SecP224K1Field.SquareN(x4, 4, x8);
+            SecP224K1Field.Multiply(x8, x4, x8);
+            uint[] x11 = Nat224.Create();
+            SecP224K1Field.SquareN(x8, 3, x11);
+            SecP224K1Field.Multiply(x11, x3, x11);
+            uint[] x19 = x11;
+            SecP224K1Field.SquareN(x11, 8, x19);
+            SecP224K1Field.Multiply(x19, x8, x19);
+            uint[] x23 = x8;
+            SecP224K1Field.SquareN(x19, 4, x23);
+            SecP224K1Field.Multiply(x23, x4, x23);
+            uint[] x42 = x4;
+            SecP224K1Field.SquareN(x23, 19, x42);
+            SecP224K1Field.Multiply(x42, x19, x42);
+            uint[] x84 = Nat224.Create();
+            SecP224K1Field.SquareN(x42, 42, x84);
+            SecP224K1Field.Multiply(x84, x42, x84);
+            uint[] x107 = x42;
+            SecP224K1Field.SquareN(x84, 23, x107);
+            SecP224K1Field.Multiply(x107, x23, x107);
+            uint[] x191 = x23;
+            SecP224K1Field.SquareN(x107, 84, x191);
+            SecP224K1Field.Multiply(x191, x84, x191);
+
+            uint[] t1 = x191;
+            SecP224K1Field.SquareN(t1, 20, t1);
+            SecP224K1Field.Multiply(t1, x19, t1);
+            SecP224K1Field.SquareN(t1, 3, t1);
+            SecP224K1Field.Multiply(t1, x1, t1);
+            SecP224K1Field.SquareN(t1, 2, t1);
+            SecP224K1Field.Multiply(t1, x1, t1);
+            SecP224K1Field.SquareN(t1, 4, t1);
+            SecP224K1Field.Multiply(t1, x3, t1);
+            SecP224K1Field.Square(t1, t1);
+
+            uint[] t2 = x84;
+            SecP224K1Field.Square(t1, t2);
+
+            if (Nat224.Eq(x1, t2))
+            {
+                return new SecP224K1FieldElement(t1);
+            }
+
+            /*
+             * If the first guess is incorrect, we multiply by a precomputed power of 2 to get the second guess,
+             * which is ((4x)^(m + 1))/2 mod Q
+             */
+            SecP224K1Field.Multiply(t1, PRECOMP_POW2, t1);
+
+            SecP224K1Field.Square(t1, t2);
+
+            if (Nat224.Eq(x1, t2))
+            {
+                return new SecP224K1FieldElement(t1);
+            }
+
+            return null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as SecP224K1FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as SecP224K1FieldElement);
+        }
+
+        public virtual bool Equals(SecP224K1FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat224.Eq(x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 7);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Point.cs b/crypto/src/math/ec/custom/sec/SecP224K1Point.cs
new file mode 100644
index 000000000..8cbd29699
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP224K1Point.cs
@@ -0,0 +1,265 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP224K1Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
+         */
+        public SecP224K1Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer
+         *             {@link #getEncoded(bool)}
+         */
+        public SecP224K1Point(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");
+        }
+
+        internal SecP224K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs,
+            bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new SecP224K1Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            SecP224K1FieldElement X1 = (SecP224K1FieldElement)this.RawXCoord, Y1 = (SecP224K1FieldElement)this.RawYCoord;
+            SecP224K1FieldElement X2 = (SecP224K1FieldElement)b.RawXCoord, Y2 = (SecP224K1FieldElement)b.RawYCoord;
+
+            SecP224K1FieldElement Z1 = (SecP224K1FieldElement)this.RawZCoords[0];
+            SecP224K1FieldElement Z2 = (SecP224K1FieldElement)b.RawZCoords[0];
+
+            uint c;
+            uint[] tt1 = Nat224.CreateExt();
+            uint[] t2 = Nat224.Create();
+            uint[] t3 = Nat224.Create();
+            uint[] t4 = Nat224.Create();
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                SecP224K1Field.Square(Z1.x, S2);
+
+                U2 = t2;
+                SecP224K1Field.Multiply(S2, X2.x, U2);
+
+                SecP224K1Field.Multiply(S2, Z1.x, S2);
+                SecP224K1Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                SecP224K1Field.Square(Z2.x, S1);
+
+                U1 = tt1;
+                SecP224K1Field.Multiply(S1, X1.x, U1);
+
+                SecP224K1Field.Multiply(S1, Z2.x, S1);
+                SecP224K1Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat224.Create();
+            SecP224K1Field.Subtract(U1, U2, H);
+
+            uint[] R = t2;
+            SecP224K1Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat224.IsZero(H))
+            {
+                if (Nat224.IsZero(R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = t3;
+            SecP224K1Field.Square(H, HSquared);
+
+            uint[] G = Nat224.Create();
+            SecP224K1Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            SecP224K1Field.Multiply(HSquared, U1, V);
+
+            SecP224K1Field.Negate(G, G);
+            Nat224.Mul(S1, G, tt1);
+
+            c = Nat224.AddBothTo(V, V, G);
+            SecP224K1Field.Reduce32(c, G);
+
+            SecP224K1FieldElement X3 = new SecP224K1FieldElement(t4);
+            SecP224K1Field.Square(R, X3.x);
+            SecP224K1Field.Subtract(X3.x, G, X3.x);
+
+            SecP224K1FieldElement Y3 = new SecP224K1FieldElement(G);
+            SecP224K1Field.Subtract(V, X3.x, Y3.x);
+            SecP224K1Field.MultiplyAddToExt(Y3.x, R, tt1);
+            SecP224K1Field.Reduce(tt1, Y3.x);
+
+            SecP224K1FieldElement Z3 = new SecP224K1FieldElement(H);
+            if (!Z1IsOne)
+            {
+                SecP224K1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                SecP224K1Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            ECFieldElement[] zs = new ECFieldElement[] { Z3 };
+
+            return new SecP224K1Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            SecP224K1FieldElement Y1 = (SecP224K1FieldElement)this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            SecP224K1FieldElement X1 = (SecP224K1FieldElement)this.RawXCoord, Z1 = (SecP224K1FieldElement)this.RawZCoords[0];
+
+            uint c;
+
+            uint[] Y1Squared = Nat224.Create();
+            SecP224K1Field.Square(Y1.x, Y1Squared);
+
+            uint[] T = Nat224.Create();
+            SecP224K1Field.Square(Y1Squared, T);
+
+            uint[] M = Nat224.Create();
+            SecP224K1Field.Square(X1.x, M);
+            c = Nat224.AddBothTo(M, M, M);
+            SecP224K1Field.Reduce32(c, M);
+
+            uint[] S = Y1Squared;
+            SecP224K1Field.Multiply(Y1Squared, X1.x, S);
+            c = Nat.ShiftUpBits(7, S, 2, 0);
+            SecP224K1Field.Reduce32(c, S);
+
+            uint[] t1 = Nat224.Create();
+            c = Nat.ShiftUpBits(7, T, 3, 0, t1);
+            SecP224K1Field.Reduce32(c, t1);
+
+            SecP224K1FieldElement X3 = new SecP224K1FieldElement(T);
+            SecP224K1Field.Square(M, X3.x);
+            SecP224K1Field.Subtract(X3.x, S, X3.x);
+            SecP224K1Field.Subtract(X3.x, S, X3.x);
+
+            SecP224K1FieldElement Y3 = new SecP224K1FieldElement(S);
+            SecP224K1Field.Subtract(S, X3.x, Y3.x);
+            SecP224K1Field.Multiply(Y3.x, M, Y3.x);
+            SecP224K1Field.Subtract(Y3.x, t1, Y3.x);
+
+            SecP224K1FieldElement Z3 = new SecP224K1FieldElement(M);
+            SecP224K1Field.Twice(Y1.x, Z3.x);
+            if (!Z1.IsOne)
+            {
+                SecP224K1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            return new SecP224K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, 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.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return Twice().Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+            return Twice().Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new SecP224K1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
new file mode 100644
index 000000000..cda8781ff
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
@@ -0,0 +1,78 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP224R1Curve
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = new BigInteger(1,
+            Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"));
+
+        private const int SecP224R1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+        protected readonly SecP224R1Point m_infinity;
+
+        public SecP224R1Curve()
+            : base(q)
+        {
+            this.m_infinity = new SecP224R1Point(this, null, null);
+
+            this.m_a = FromBigInteger(new BigInteger(1,
+                Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE")));
+            this.m_b = FromBigInteger(new BigInteger(1,
+                Hex.Decode("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4")));
+            this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"));
+            this.m_cofactor = BigInteger.One;
+
+            this.m_coord = SecP224R1_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new SecP224R1Curve();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+                case COORD_JACOBIAN:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new SecP224R1FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new SecP224R1Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new SecP224R1Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Field.cs b/crypto/src/math/ec/custom/sec/SecP224R1Field.cs
new file mode 100644
index 000000000..559593c66
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP224R1Field.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP224R1Field
+    {
+        // 2^224 - 2^96 + 1
+        internal static readonly uint[] P = new uint[] { 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        internal static readonly uint[] PExt = new uint[]{ 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF,
+            0xFFFFFFFF, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        private static readonly uint[] PExtInv = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001, 0x00000000,
+            0x00000000, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001 };
+        private const uint P6 = 0xFFFFFFFF;
+        private const uint PExt13 = 0xFFFFFFFF;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            uint c = Nat224.Add(x, y, z);
+            if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void AddExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            uint c = Nat.Add(14, xx, yy, zz);
+            if (c != 0 || (zz[13] == PExt13 && Nat.Gte(14, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(14, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            uint c = Nat.Inc(7, x, z);
+            if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat224.FromBigInteger(x);
+            if (z[6] == P6 && Nat224.Gte(z, P))
+            {
+                Nat224.SubFrom(P, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            if ((x[0] & 1) == 0)
+            {
+                Nat.ShiftDownBit(7, x, 0, z);
+            }
+            else
+            {
+                uint c = Nat224.Add(x, P, z);
+                Nat.ShiftDownBit(7, z, c);
+            }
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat224.CreateExt();
+            Nat224.Mul(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz)
+        {
+            uint c = Nat224.MulAddTo(x, y, zz);
+            if (c != 0 || (zz[13] == PExt13 && Nat.Gte(14, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(14, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat224.IsZero(x))
+            {
+                Nat224.Zero(z);
+            }
+            else
+            {
+                Nat224.Sub(P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            long xx10 = xx[10], xx11 = xx[11], xx12 = xx[12], xx13 = xx[13];
+
+            const long n = 1;
+
+            long t0 = (long)xx[7] + xx11 - n;
+            long t1 = (long)xx[8] + xx12;
+            long t2 = (long)xx[9] + xx13;
+
+            long cc = 0;
+            cc += (long)xx[0] - t0;
+            long z0 = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[1] - t1;
+            z[1] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[2] - t2;
+            z[2] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[3] + t0 - xx10;
+            long z3 = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[4] + t1 - xx11;
+            z[4] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[5] + t2 - xx12;
+            z[5] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[6] + xx10 - xx13;
+            z[6] = (uint)cc;
+            cc >>= 32;
+            cc += n;
+
+            Debug.Assert(cc >= 0);
+
+            z3 += cc;
+
+            z0 -= cc;
+            z[0] = (uint)z0;
+            cc = z0 >> 32;
+            if (cc != 0)
+            {
+                cc += (long)z[1];
+                z[1] = (uint)cc;
+                cc >>= 32;
+                cc += (long)z[2];
+                z[2] = (uint)cc;
+                z3 += cc >> 32;
+            }
+            z[3] = (uint)z3;
+            cc = z3 >> 32;
+
+            Debug.Assert(cc == 0 || cc == 1);
+
+            if ((cc != 0 && Nat.IncAt(7, z, 4) != 0)
+                || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void Reduce32(uint x, uint[] z)
+        {
+            long cc = 0;
+
+            if (x != 0)
+            {
+                long xx07 = x;
+
+                cc += (long)z[0] - xx07;
+                z[0] = (uint)cc;
+                cc >>= 32;
+                if (cc != 0)
+                {
+                    cc += (long)z[1];
+                    z[1] = (uint)cc;
+                    cc >>= 32;
+                    cc += (long)z[2];
+                    z[2] = (uint)cc;
+                    cc >>= 32;
+                }
+                cc += (long)z[3] + xx07;
+                z[3] = (uint)cc;
+                cc >>= 32;
+
+                Debug.Assert(cc == 0 || cc == 1);
+            }
+
+            if ((cc != 0 && Nat.IncAt(7, z, 4) != 0)
+                || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat224.CreateExt();
+            Nat224.Square(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            uint[] tt = Nat224.CreateExt();
+            Nat224.Square(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                Nat224.Square(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat224.Sub(x, y, z);
+            if (c != 0)
+            {
+                SubPInvFrom(z);
+            }
+        }
+
+        public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            int c = Nat.Sub(14, xx, yy, zz);
+            if (c != 0)
+            {
+                if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.DecAt(14, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            uint c = Nat.ShiftUpBit(7, x, 0, z);
+            if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        private static void AddPInvTo(uint[] z)
+        {
+            long c = (long)z[0] - 1;
+            z[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[1];
+                z[1] = (uint)c;
+                c >>= 32;
+                c += (long)z[2];
+                z[2] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[3] + 1;
+            z[3] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                Nat.IncAt(7, z, 4);
+            }
+        }
+
+        private static void SubPInvFrom(uint[] z)
+        {
+            long c = (long)z[0] + 1;
+            z[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[1];
+                z[1] = (uint)c;
+                c >>= 32;
+                c += (long)z[2];
+                z[2] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[3] - 1;
+            z[3] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                Nat.DecAt(7, z, 4);
+            }
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs
new file mode 100644
index 000000000..06f47cded
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs
@@ -0,0 +1,268 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP224R1FieldElement
+        : ECFieldElement
+    {
+        public static readonly BigInteger Q = SecP224R1Curve.q;
+
+        protected internal readonly uint[] x;
+
+        public SecP224R1FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for SecP224R1FieldElement", "x");
+
+            this.x = SecP224R1Field.FromBigInteger(x);
+        }
+
+        public SecP224R1FieldElement()
+        {
+            this.x = Nat224.Create();
+        }
+
+        protected internal SecP224R1FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat224.IsZero(x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat224.IsOne(x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat224.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat224.ToBigInteger(x);
+        }
+
+        public override string FieldName
+        {
+            get { return "SecP224R1Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat224.Create();
+            SecP224R1Field.Add(x, ((SecP224R1FieldElement)b).x, z);
+            return new SecP224R1FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat224.Create();
+            SecP224R1Field.AddOne(x, z);
+            return new SecP224R1FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat224.Create();
+            SecP224R1Field.Subtract(x, ((SecP224R1FieldElement)b).x, z);
+            return new SecP224R1FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat224.Create();
+            SecP224R1Field.Multiply(x, ((SecP224R1FieldElement)b).x, z);
+            return new SecP224R1FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat224.Create();
+            Mod.Invert(SecP224R1Field.P, ((SecP224R1FieldElement)b).x, z);
+            SecP224R1Field.Multiply(z, x, z);
+            return new SecP224R1FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat224.Create();
+            SecP224R1Field.Negate(x, z);
+            return new SecP224R1FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat224.Create();
+            SecP224R1Field.Square(x, z);
+            return new SecP224R1FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new SecP224R1FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat224.Create();
+            Mod.Invert(SecP224R1Field.P, x, z);
+            return new SecP224R1FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            uint[] c = this.x;
+            if (Nat224.IsZero(c) || Nat224.IsOne(c))
+                return this;
+
+            uint[] nc = Nat224.Create();
+            SecP224R1Field.Negate(c, nc);
+
+            uint[] r = Mod.Random(SecP224R1Field.P);
+            uint[] t = Nat224.Create();
+
+            if (!IsSquare(c))
+                return null;
+
+            while (!TrySqrt(nc, r, t))
+            {
+                SecP224R1Field.AddOne(r, r);
+            }
+
+            SecP224R1Field.Square(t, r);
+
+            return Nat224.Eq(c, r) ? new SecP224R1FieldElement(t) : null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as SecP224R1FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as SecP224R1FieldElement);
+        }
+
+        public virtual bool Equals(SecP224R1FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat224.Eq(x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 7);
+        }
+
+        private static bool IsSquare(uint[] x)
+        {
+            uint[] t1 = Nat224.Create();
+            uint[] t2 = Nat224.Create();
+            Nat224.Copy(x, t1);
+
+            for (int i = 0; i < 7; ++i)
+            {
+                Nat224.Copy(t1, t2);
+                SecP224R1Field.SquareN(t1, 1 << i, t1);
+                SecP224R1Field.Multiply(t1, t2, t1);
+            }
+
+            SecP224R1Field.SquareN(t1, 95, t1);
+            return Nat224.IsOne(t1);
+        }
+
+        private static void RM(uint[] nc, uint[] d0, uint[] e0, uint[] d1, uint[] e1, uint[] f1, uint[] t)
+        {
+            SecP224R1Field.Multiply(e1, e0, t);
+            SecP224R1Field.Multiply(t, nc, t);
+            SecP224R1Field.Multiply(d1, d0, f1);
+            SecP224R1Field.Add(f1, t, f1);
+            SecP224R1Field.Multiply(d1, e0, t);
+            Nat224.Copy(f1, d1);
+            SecP224R1Field.Multiply(e1, d0, e1);
+            SecP224R1Field.Add(e1, t, e1);
+            SecP224R1Field.Square(e1, f1);
+            SecP224R1Field.Multiply(f1, nc, f1);
+        }
+
+        private static void RP(uint[] nc, uint[] d1, uint[] e1, uint[] f1, uint[] t)
+        {
+            Nat224.Copy(nc, f1);
+
+            uint[] d0 = Nat224.Create();
+            uint[] e0 = Nat224.Create();
+
+            for (int i = 0; i < 7; ++i)
+            {
+                Nat224.Copy(d1, d0);
+                Nat224.Copy(e1, e0);
+
+                int j = 1 << i;
+                while (--j >= 0)
+                {
+                    RS(d1, e1, f1, t);
+                }
+
+                RM(nc, d0, e0, d1, e1, f1, t);
+            }
+        }
+
+        private static void RS(uint[] d, uint[] e, uint[] f, uint[] t)
+        {
+            SecP224R1Field.Multiply(e, d, e);
+            SecP224R1Field.Twice(e, e);
+            SecP224R1Field.Square(d, t);
+            SecP224R1Field.Add(f, t, d);
+            SecP224R1Field.Multiply(f, t, f);
+            uint c = Nat.ShiftUpBits(7, f, 2, 0);
+            SecP224R1Field.Reduce32(c, f);
+        }
+
+        private static bool TrySqrt(uint[] nc, uint[] r, uint[] t)
+        {
+            uint[] d1 = Nat224.Create();
+            Nat224.Copy(r, d1);
+            uint[] e1 = Nat224.Create();
+            e1[0] = 1;
+            uint[] f1 = Nat224.Create();
+            RP(nc, d1, e1, f1, t);
+
+            uint[] d0 = Nat224.Create();
+            uint[] e0 = Nat224.Create();
+
+            for (int k = 1; k < 96; ++k)
+            {
+                Nat224.Copy(d1, d0);
+                Nat224.Copy(e1, e0);
+
+                RS(d1, e1, f1, t);
+
+                if (Nat224.IsZero(d1))
+                {
+                    Mod.Invert(SecP224R1Field.P, e0, t);
+                    SecP224R1Field.Multiply(t, d0, t);
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Point.cs b/crypto/src/math/ec/custom/sec/SecP224R1Point.cs
new file mode 100644
index 000000000..c3f4efb59
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP224R1Point.cs
@@ -0,0 +1,277 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP224R1Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
+         */
+        public SecP224R1Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer
+         *             {@link #getEncoded(bool)}
+         */
+        public SecP224R1Point(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");
+        }
+
+        internal SecP224R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new SecP224R1Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            SecP224R1FieldElement X1 = (SecP224R1FieldElement)this.RawXCoord, Y1 = (SecP224R1FieldElement)this.RawYCoord;
+            SecP224R1FieldElement X2 = (SecP224R1FieldElement)b.RawXCoord, Y2 = (SecP224R1FieldElement)b.RawYCoord;
+
+            SecP224R1FieldElement Z1 = (SecP224R1FieldElement)this.RawZCoords[0];
+            SecP224R1FieldElement Z2 = (SecP224R1FieldElement)b.RawZCoords[0];
+
+            uint c;
+            uint[] tt1 = Nat224.CreateExt();
+            uint[] t2 = Nat224.Create();
+            uint[] t3 = Nat224.Create();
+            uint[] t4 = Nat224.Create();
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                SecP224R1Field.Square(Z1.x, S2);
+
+                U2 = t2;
+                SecP224R1Field.Multiply(S2, X2.x, U2);
+
+                SecP224R1Field.Multiply(S2, Z1.x, S2);
+                SecP224R1Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                SecP224R1Field.Square(Z2.x, S1);
+
+                U1 = tt1;
+                SecP224R1Field.Multiply(S1, X1.x, U1);
+
+                SecP224R1Field.Multiply(S1, Z2.x, S1);
+                SecP224R1Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat224.Create();
+            SecP224R1Field.Subtract(U1, U2, H);
+
+            uint[] R = t2;
+            SecP224R1Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat224.IsZero(H))
+            {
+                if (Nat224.IsZero(R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = t3;
+            SecP224R1Field.Square(H, HSquared);
+
+            uint[] G = Nat224.Create();
+            SecP224R1Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            SecP224R1Field.Multiply(HSquared, U1, V);
+
+            SecP224R1Field.Negate(G, G);
+            Nat224.Mul(S1, G, tt1);
+
+            c = Nat224.AddBothTo(V, V, G);
+            SecP224R1Field.Reduce32(c, G);
+
+            SecP224R1FieldElement X3 = new SecP224R1FieldElement(t4);
+            SecP224R1Field.Square(R, X3.x);
+            SecP224R1Field.Subtract(X3.x, G, X3.x);
+
+            SecP224R1FieldElement Y3 = new SecP224R1FieldElement(G);
+            SecP224R1Field.Subtract(V, X3.x, Y3.x);
+            SecP224R1Field.MultiplyAddToExt(Y3.x, R, tt1);
+            SecP224R1Field.Reduce(tt1, Y3.x);
+
+            SecP224R1FieldElement Z3 = new SecP224R1FieldElement(H);
+            if (!Z1IsOne)
+            {
+                SecP224R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                SecP224R1Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            ECFieldElement[] zs = new ECFieldElement[] { Z3 };
+
+            return new SecP224R1Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            SecP224R1FieldElement Y1 = (SecP224R1FieldElement)this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            SecP224R1FieldElement X1 = (SecP224R1FieldElement)this.RawXCoord, Z1 = (SecP224R1FieldElement)this.RawZCoords[0];
+
+            uint c;
+            uint[] t1 = Nat224.Create();
+            uint[] t2 = Nat224.Create();
+
+            uint[] Y1Squared = Nat224.Create();
+            SecP224R1Field.Square(Y1.x, Y1Squared);
+
+            uint[] T = Nat224.Create();
+            SecP224R1Field.Square(Y1Squared, T);
+
+            bool Z1IsOne = Z1.IsOne;
+
+            uint[] Z1Squared = Z1.x;
+            if (!Z1IsOne)
+            {
+                Z1Squared = t2;
+                SecP224R1Field.Square(Z1.x, Z1Squared);
+            }
+
+            SecP224R1Field.Subtract(X1.x, Z1Squared, t1);
+
+            uint[] M = t2;
+            SecP224R1Field.Add(X1.x, Z1Squared, M);
+            SecP224R1Field.Multiply(M, t1, M);
+            c = Nat224.AddBothTo(M, M, M);
+            SecP224R1Field.Reduce32(c, M);
+
+            uint[] S = Y1Squared;
+            SecP224R1Field.Multiply(Y1Squared, X1.x, S);
+            c = Nat.ShiftUpBits(7, S, 2, 0);
+            SecP224R1Field.Reduce32(c, S);
+
+            c = Nat.ShiftUpBits(7, T, 3, 0, t1);
+            SecP224R1Field.Reduce32(c, t1);
+
+            SecP224R1FieldElement X3 = new SecP224R1FieldElement(T);
+            SecP224R1Field.Square(M, X3.x);
+            SecP224R1Field.Subtract(X3.x, S, X3.x);
+            SecP224R1Field.Subtract(X3.x, S, X3.x);
+
+            SecP224R1FieldElement Y3 = new SecP224R1FieldElement(S);
+            SecP224R1Field.Subtract(S, X3.x, Y3.x);
+            SecP224R1Field.Multiply(Y3.x, M, Y3.x);
+            SecP224R1Field.Subtract(Y3.x, t1, Y3.x);
+
+            SecP224R1FieldElement Z3 = new SecP224R1FieldElement(M);
+            SecP224R1Field.Twice(Y1.x, Z3.x);
+            if (!Z1IsOne)
+            {
+                SecP224R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            return new SecP224R1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, 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.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return Twice().Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+            return Twice().Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new SecP224R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
new file mode 100644
index 000000000..59e2cefb2
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
@@ -0,0 +1,75 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP256K1Curve
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = new BigInteger(1,
+            Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"));
+
+        private const int SECP256K1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+        protected readonly SecP256K1Point m_infinity;
+
+        public SecP256K1Curve()
+            : base(q)
+        {
+            this.m_infinity = new SecP256K1Point(this, null, null);
+
+            this.m_a = FromBigInteger(BigInteger.Zero);
+            this.m_b = FromBigInteger(BigInteger.ValueOf(7));
+            this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"));
+            this.m_cofactor = BigInteger.One;
+            this.m_coord = SECP256K1_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new SecP256K1Curve();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+            case COORD_JACOBIAN:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new SecP256K1FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new SecP256K1Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new SecP256K1Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Field.cs b/crypto/src/math/ec/custom/sec/SecP256K1Field.cs
new file mode 100644
index 000000000..ba3a070a9
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP256K1Field.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP256K1Field
+    {
+        // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
+        internal static readonly uint[] P = new uint[]{ 0xFFFFFC2F, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF, 0xFFFFFFFF };
+        internal static readonly uint[] PExt = new uint[]{ 0x000E90A1, 0x000007A2, 0x00000001, 0x00000000, 0x00000000,
+            0x00000000, 0x00000000, 0x00000000, 0xFFFFF85E, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF, 0xFFFFFFFF };
+        private static readonly uint[] PExtInv = new uint[]{ 0xFFF16F5F, 0xFFFFF85D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000007A1, 0x00000002 };
+        private const uint P7 = 0xFFFFFFFF;
+        private const uint PExt15 = 0xFFFFFFFF;
+        private const uint PInv33 = 0x3D1;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            uint c = Nat256.Add(x, y, z);
+            if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                Nat.Add33To(8, PInv33, z);
+            }
+        }
+
+        public static void AddExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            uint c = Nat.Add(16, xx, yy, zz);
+            if (c != 0 || (zz[15] == PExt15 && Nat.Gte(16, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(16, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            uint c = Nat.Inc(8, x, z);
+            if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                Nat.Add33To(8, PInv33, z);
+            }
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat256.FromBigInteger(x);
+            if (z[7] == P7 && Nat256.Gte(z, P))
+            {
+                Nat256.SubFrom(P, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            if ((x[0] & 1) == 0)
+            {
+                Nat.ShiftDownBit(8, x, 0, z);
+            }
+            else
+            {
+                uint c = Nat256.Add(x, P, z);
+                Nat.ShiftDownBit(8, z, c);
+            }
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Mul(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz)
+        {
+            uint c = Nat256.MulAddTo(x, y, zz);
+            if (c != 0 || (zz[15] == PExt15 && Nat.Gte(16, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(16, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat256.IsZero(x))
+            {
+                Nat256.Zero(z);
+            }
+            else
+            {
+                Nat256.Sub(P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            ulong cc = Nat256.Mul33Add(PInv33, xx, 8, xx, 0, z, 0);
+            uint c = Nat256.Mul33DWordAdd(PInv33, cc, z, 0);
+
+            Debug.Assert(c == 0 || c == 1);
+
+            if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                Nat.Add33To(8, PInv33, z);
+            }
+        }
+
+        public static void Reduce32(uint x, uint[] z)
+        {
+            if ((x != 0 && Nat256.Mul33WordAdd(PInv33, x, z, 0) != 0)
+                || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                Nat.Add33To(8, PInv33, z);
+            }
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Square(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Square(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                Nat256.Square(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat256.Sub(x, y, z);
+            if (c != 0)
+            {
+                Nat.Sub33From(8, PInv33, z);
+            }
+        }
+
+        public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            int c = Nat.Sub(16, xx, yy, zz);
+            if (c != 0)
+            {
+                if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.DecAt(16, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            uint c = Nat.ShiftUpBit(8, x, 0, z);
+            if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                Nat.Add33To(8, PInv33, z);
+            }
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs
new file mode 100644
index 000000000..d9a039a4f
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs
@@ -0,0 +1,213 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP256K1FieldElement
+        : ECFieldElement
+    {
+        public static readonly BigInteger Q = SecP256K1Curve.q;
+
+        protected internal readonly uint[] x;
+
+        public SecP256K1FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for SecP256K1FieldElement", "x");
+
+            this.x = SecP256K1Field.FromBigInteger(x);
+        }
+
+        public SecP256K1FieldElement()
+        {
+            this.x = Nat256.Create();
+        }
+
+        protected internal SecP256K1FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat256.IsZero(x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat256.IsOne(x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat256.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat256.ToBigInteger(x);
+        }
+
+        public override string FieldName
+        {
+            get { return "SecP256K1Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            SecP256K1Field.Add(x, ((SecP256K1FieldElement)b).x, z);
+            return new SecP256K1FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat256.Create();
+            SecP256K1Field.AddOne(x, z);
+            return new SecP256K1FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            SecP256K1Field.Subtract(x, ((SecP256K1FieldElement)b).x, z);
+            return new SecP256K1FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            SecP256K1Field.Multiply(x, ((SecP256K1FieldElement)b).x, z);
+            return new SecP256K1FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat256.Create();
+            Mod.Invert(SecP256K1Field.P, ((SecP256K1FieldElement)b).x, z);
+            SecP256K1Field.Multiply(z, x, z);
+            return new SecP256K1FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat256.Create();
+            SecP256K1Field.Negate(x, z);
+            return new SecP256K1FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat256.Create();
+            SecP256K1Field.Square(x, z);
+            return new SecP256K1FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new SecP256K1FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat256.Create();
+            Mod.Invert(SecP256K1Field.P, x, z);
+            return new SecP256K1FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            /*
+             * Raise this element to the exponent 2^254 - 2^30 - 2^7 - 2^6 - 2^5 - 2^4 - 2^2
+             * 
+             * Breaking up the exponent's binary representation into "repunits", we get:
+             * { 223 1s } { 1 0s } { 22 1s } { 4 0s } { 2 1s } { 2 0s}
+             * 
+             * Therefore we need an addition chain containing 2, 22, 223 (the lengths of the repunits)
+             * We use: 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
+             */
+
+            uint[] x1 = this.x;
+            if (Nat256.IsZero(x1) || Nat256.IsOne(x1))
+                return this;
+
+            uint[] x2 = Nat256.Create();
+            SecP256K1Field.Square(x1, x2);
+            SecP256K1Field.Multiply(x2, x1, x2);
+            uint[] x3 = Nat256.Create();
+            SecP256K1Field.Square(x2, x3);
+            SecP256K1Field.Multiply(x3, x1, x3);
+            uint[] x6 = Nat256.Create();
+            SecP256K1Field.SquareN(x3, 3, x6);
+            SecP256K1Field.Multiply(x6, x3, x6);
+            uint[] x9 = x6;
+            SecP256K1Field.SquareN(x6, 3, x9);
+            SecP256K1Field.Multiply(x9, x3, x9);
+            uint[] x11 = x9;
+            SecP256K1Field.SquareN(x9, 2, x11);
+            SecP256K1Field.Multiply(x11, x2, x11);
+            uint[] x22 = Nat256.Create();
+            SecP256K1Field.SquareN(x11, 11, x22);
+            SecP256K1Field.Multiply(x22, x11, x22);
+            uint[] x44 = x11;
+            SecP256K1Field.SquareN(x22, 22, x44);
+            SecP256K1Field.Multiply(x44, x22, x44);
+            uint[] x88 = Nat256.Create();
+            SecP256K1Field.SquareN(x44, 44, x88);
+            SecP256K1Field.Multiply(x88, x44, x88);
+            uint[] x176 = Nat256.Create();
+            SecP256K1Field.SquareN(x88, 88, x176);
+            SecP256K1Field.Multiply(x176, x88, x176);
+            uint[] x220 = x88;
+            SecP256K1Field.SquareN(x176, 44, x220);
+            SecP256K1Field.Multiply(x220, x44, x220);
+            uint[] x223 = x44;
+            SecP256K1Field.SquareN(x220, 3, x223);
+            SecP256K1Field.Multiply(x223, x3, x223);
+
+            uint[] t1 = x223;
+            SecP256K1Field.SquareN(t1, 23, t1);
+            SecP256K1Field.Multiply(t1, x22, t1);
+            SecP256K1Field.SquareN(t1, 6, t1);
+            SecP256K1Field.Multiply(t1, x2, t1);
+            SecP256K1Field.SquareN(t1, 2, t1);
+
+            uint[] t2 = x2;
+            SecP256K1Field.Square(t1, t2);
+
+            return Nat256.Eq(x1, t2) ? new SecP256K1FieldElement(t1) : null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as SecP256K1FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as SecP256K1FieldElement);
+        }
+
+        public virtual bool Equals(SecP256K1FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat256.Eq(x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 8);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Point.cs b/crypto/src/math/ec/custom/sec/SecP256K1Point.cs
new file mode 100644
index 000000000..3165682fa
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP256K1Point.cs
@@ -0,0 +1,265 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP256K1Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
+         */
+        public SecP256K1Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer
+         *             {@link #getEncoded(bool)}
+         */
+        public SecP256K1Point(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");
+        }
+
+        internal SecP256K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs,
+            bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new SecP256K1Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.RawXCoord, Y1 = (SecP256K1FieldElement)this.RawYCoord;
+            SecP256K1FieldElement X2 = (SecP256K1FieldElement)b.RawXCoord, Y2 = (SecP256K1FieldElement)b.RawYCoord;
+
+            SecP256K1FieldElement Z1 = (SecP256K1FieldElement)this.RawZCoords[0];
+            SecP256K1FieldElement Z2 = (SecP256K1FieldElement)b.RawZCoords[0];
+
+            uint c;
+            uint[] tt1 = Nat256.CreateExt();
+            uint[] t2 = Nat256.Create();
+            uint[] t3 = Nat256.Create();
+            uint[] t4 = Nat256.Create();
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                SecP256K1Field.Square(Z1.x, S2);
+
+                U2 = t2;
+                SecP256K1Field.Multiply(S2, X2.x, U2);
+
+                SecP256K1Field.Multiply(S2, Z1.x, S2);
+                SecP256K1Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                SecP256K1Field.Square(Z2.x, S1);
+
+                U1 = tt1;
+                SecP256K1Field.Multiply(S1, X1.x, U1);
+
+                SecP256K1Field.Multiply(S1, Z2.x, S1);
+                SecP256K1Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat256.Create();
+            SecP256K1Field.Subtract(U1, U2, H);
+
+            uint[] R = t2;
+            SecP256K1Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat256.IsZero(H))
+            {
+                if (Nat256.IsZero(R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = t3;
+            SecP256K1Field.Square(H, HSquared);
+
+            uint[] G = Nat256.Create();
+            SecP256K1Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            SecP256K1Field.Multiply(HSquared, U1, V);
+
+            SecP256K1Field.Negate(G, G);
+            Nat256.Mul(S1, G, tt1);
+
+            c = Nat256.AddBothTo(V, V, G);
+            SecP256K1Field.Reduce32(c, G);
+
+            SecP256K1FieldElement X3 = new SecP256K1FieldElement(t4);
+            SecP256K1Field.Square(R, X3.x);
+            SecP256K1Field.Subtract(X3.x, G, X3.x);
+
+            SecP256K1FieldElement Y3 = new SecP256K1FieldElement(G);
+            SecP256K1Field.Subtract(V, X3.x, Y3.x);
+            SecP256K1Field.MultiplyAddToExt(Y3.x, R, tt1);
+            SecP256K1Field.Reduce(tt1, Y3.x);
+
+            SecP256K1FieldElement Z3 = new SecP256K1FieldElement(H);
+            if (!Z1IsOne)
+            {
+                SecP256K1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                SecP256K1Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            ECFieldElement[] zs = new ECFieldElement[] { Z3 };
+
+            return new SecP256K1Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            SecP256K1FieldElement Y1 = (SecP256K1FieldElement)this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.RawXCoord, Z1 = (SecP256K1FieldElement)this.RawZCoords[0];
+
+            uint c;
+
+            uint[] Y1Squared = Nat256.Create();
+            SecP256K1Field.Square(Y1.x, Y1Squared);
+
+            uint[] T = Nat256.Create();
+            SecP256K1Field.Square(Y1Squared, T);
+
+            uint[] M = Nat256.Create();
+            SecP256K1Field.Square(X1.x, M);
+            c = Nat256.AddBothTo(M, M, M);
+            SecP256K1Field.Reduce32(c, M);
+
+            uint[] S = Y1Squared;
+            SecP256K1Field.Multiply(Y1Squared, X1.x, S);
+            c = Nat.ShiftUpBits(8, S, 2, 0);
+            SecP256K1Field.Reduce32(c, S);
+
+            uint[] t1 = Nat256.Create();
+            c = Nat.ShiftUpBits(8, T, 3, 0, t1);
+            SecP256K1Field.Reduce32(c, t1);
+
+            SecP256K1FieldElement X3 = new SecP256K1FieldElement(T);
+            SecP256K1Field.Square(M, X3.x);
+            SecP256K1Field.Subtract(X3.x, S, X3.x);
+            SecP256K1Field.Subtract(X3.x, S, X3.x);
+
+            SecP256K1FieldElement Y3 = new SecP256K1FieldElement(S);
+            SecP256K1Field.Subtract(S, X3.x, Y3.x);
+            SecP256K1Field.Multiply(Y3.x, M, Y3.x);
+            SecP256K1Field.Subtract(Y3.x, t1, Y3.x);
+
+            SecP256K1FieldElement Z3 = new SecP256K1FieldElement(M);
+            SecP256K1Field.Twice(Y1.x, Z3.x);
+            if (!Z1.IsOne)
+            {
+                SecP256K1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            return new SecP256K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, 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.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return Twice().Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+            return Twice().Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new SecP256K1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
new file mode 100644
index 000000000..6b3448f06
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
@@ -0,0 +1,77 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP256R1Curve
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = new BigInteger(1,
+            Hex.Decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"));
+
+        private const int SecP256R1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+        protected readonly SecP256R1Point m_infinity;
+
+        public SecP256R1Curve()
+            : base(q)
+        {
+            this.m_infinity = new SecP256R1Point(this, null, null);
+
+            this.m_a = FromBigInteger(new BigInteger(1,
+                Hex.Decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC")));
+            this.m_b = FromBigInteger(new BigInteger(1,
+                Hex.Decode("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B")));
+            this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"));
+            this.m_cofactor = BigInteger.One;
+            this.m_coord = SecP256R1_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new SecP256R1Curve();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+            case COORD_JACOBIAN:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new SecP256R1FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new SecP256R1Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new SecP256R1Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Field.cs b/crypto/src/math/ec/custom/sec/SecP256R1Field.cs
new file mode 100644
index 000000000..9ed9dcd41
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP256R1Field.cs
@@ -0,0 +1,309 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP256R1Field
+    {
+        // 2^256 - 2^224 + 2^192 + 2^96 - 1
+        internal static readonly uint[] P = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
+            0x00000001, 0xFFFFFFFF };
+        internal static readonly uint[] PExt = new uint[]{ 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF,
+            0xFFFFFFFF, 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001, 0x00000001, 0xFFFFFFFE,
+            0x00000002, 0xFFFFFFFE };
+        internal const uint P7 = 0xFFFFFFFF;
+        internal const uint PExt15 = 0xFFFFFFFE;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            uint c = Nat256.Add(x, y, z);
+            if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void AddExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            uint c = Nat.Add(16, xx, yy, zz);
+            if (c != 0 || (zz[15] >= PExt15 && Nat.Gte(16, zz, PExt)))
+            {
+                Nat.SubFrom(16, PExt, zz);
+            }
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            uint c = Nat.Inc(8, x, z);
+            if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat256.FromBigInteger(x);
+            if (z[7] == P7 && Nat256.Gte(z, P))
+            {
+                Nat256.SubFrom(P, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            if ((x[0] & 1) == 0)
+            {
+                Nat.ShiftDownBit(8, x, 0, z);
+            }
+            else
+            {
+                uint c = Nat256.Add(x, P, z);
+                Nat.ShiftDownBit(8, z, c);
+            }
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Mul(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz)
+        {
+            uint c = Nat256.MulAddTo(x, y, zz);
+            if (c != 0 || (zz[15] >= PExt15 && Nat.Gte(16, zz, PExt)))
+            {
+                Nat.SubFrom(16, PExt, zz);
+            }
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat256.IsZero(x))
+            {
+                Nat256.Zero(z);
+            }
+            else
+            {
+                Nat256.Sub(P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            long xx08 = xx[8], xx09 = xx[9], xx10 = xx[10], xx11 = xx[11];
+            long xx12 = xx[12], xx13 = xx[13], xx14 = xx[14], xx15 = xx[15];
+
+            const long n = 6;
+
+            xx08 -= n;
+
+            long t0 = xx08 + xx09;
+            long t1 = xx09 + xx10;
+            long t2 = xx10 + xx11 - xx15;
+            long t3 = xx11 + xx12;
+            long t4 = xx12 + xx13;
+            long t5 = xx13 + xx14;
+            long t6 = xx14 + xx15;
+
+            long cc = 0;
+            cc += (long)xx[0] + t0 - t3 - t5;
+            z[0] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[1] + t1 - t4 - t6;
+            z[1] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[2] + t2 - t5;
+            z[2] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[3] + (t3 << 1) + xx13 - xx15 - t0;
+            z[3] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[4] + (t4 << 1) + xx14 - t1;
+            z[4] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[5] + (t5 << 1) - t2;
+            z[5] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[6] + (t6 << 1) + t5 - t0;
+            z[6] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[7] + (xx15 << 1) + xx08 - t2 - t4;
+            z[7] = (uint)cc;
+            cc >>= 32;
+            cc += n;
+
+            Debug.Assert(cc >= 0);
+
+            Reduce32((uint)cc, z);
+        }
+
+        public static void Reduce32(uint x, uint[] z)
+        {
+            long cc = 0;
+
+            if (x != 0)
+            {
+                long xx08 = x;
+
+                cc += (long)z[0] + xx08;
+                z[0] = (uint)cc;
+                cc >>= 32;
+                if (cc != 0)
+                {
+                    cc += (long)z[1];
+                    z[1] = (uint)cc;
+                    cc >>= 32;
+                    cc += (long)z[2];
+                    z[2] = (uint)cc;
+                    cc >>= 32;
+                }
+                cc += (long)z[3] - xx08;
+                z[3] = (uint)cc;
+                cc >>= 32;
+                if (cc != 0)
+                {
+                    cc += (long)z[4];
+                    z[4] = (uint)cc;
+                    cc >>= 32;
+                    cc += (long)z[5];
+                    z[5] = (uint)cc;
+                    cc >>= 32;
+                }
+                cc += (long)z[6] - xx08;
+                z[6] = (uint)cc;
+                cc >>= 32;
+                cc += (long)z[7] + xx08;
+                z[7] = (uint)cc;
+                cc >>= 32;
+
+                Debug.Assert(cc == 0 || cc == 1);
+            }
+
+            if (cc != 0 || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Square(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            uint[] tt = Nat256.CreateExt();
+            Nat256.Square(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                Nat256.Square(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat256.Sub(x, y, z);
+            if (c != 0)
+            {
+                SubPInvFrom(z);
+            }
+        }
+
+        public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            int c = Nat.Sub(16, xx, yy, zz);
+            if (c != 0)
+            {
+                Nat.AddTo(16, PExt, zz);
+            }
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            uint c = Nat.ShiftUpBit(8, x, 0, z);
+            if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        private static void AddPInvTo(uint[] z)
+        {
+            long c = (long)z[0] + 1;
+            z[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[1];
+                z[1] = (uint)c;
+                c >>= 32;
+                c += (long)z[2];
+                z[2] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[3] - 1;
+            z[3] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[4];
+                z[4] = (uint)c;
+                c >>= 32;
+                c += (long)z[5];
+                z[5] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[6] - 1;
+            z[6] = (uint)c;
+            c >>= 32;
+            c += (long)z[7] + 1;
+            z[7] = (uint)c;
+            //c >>= 32;
+        }
+
+        private static void SubPInvFrom(uint[] z)
+        {
+            long c = (long)z[0] - 1;
+            z[0] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[1];
+                z[1] = (uint)c;
+                c >>= 32;
+                c += (long)z[2];
+                z[2] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[3] + 1;
+            z[3] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[4];
+                z[4] = (uint)c;
+                c >>= 32;
+                c += (long)z[5];
+                z[5] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[6] + 1;
+            z[6] = (uint)c;
+            c >>= 32;
+            c += (long)z[7] - 1;
+            z[7] = (uint)c;
+            //c >>= 32;
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs
new file mode 100644
index 000000000..b22763cfa
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs
@@ -0,0 +1,187 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP256R1FieldElement
+        : ECFieldElement
+    {
+        public static readonly BigInteger Q = SecP256R1Curve.q;
+
+        protected internal readonly uint[] x;
+
+        public SecP256R1FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for SecP256R1FieldElement", "x");
+
+            this.x = SecP256R1Field.FromBigInteger(x);
+        }
+
+        public SecP256R1FieldElement()
+        {
+            this.x = Nat256.Create();
+        }
+
+        protected internal SecP256R1FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat256.IsZero(x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat256.IsOne(x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat256.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat256.ToBigInteger(x);
+        }
+
+        public override string FieldName
+        {
+            get { return "SecP256R1Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            SecP256R1Field.Add(x, ((SecP256R1FieldElement)b).x, z);
+            return new SecP256R1FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat256.Create();
+            SecP256R1Field.AddOne(x, z);
+            return new SecP256R1FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            SecP256R1Field.Subtract(x, ((SecP256R1FieldElement)b).x, z);
+            return new SecP256R1FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat256.Create();
+            SecP256R1Field.Multiply(x, ((SecP256R1FieldElement)b).x, z);
+            return new SecP256R1FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat256.Create();
+            Mod.Invert(SecP256R1Field.P, ((SecP256R1FieldElement)b).x, z);
+            SecP256R1Field.Multiply(z, x, z);
+            return new SecP256R1FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat256.Create();
+            SecP256R1Field.Negate(x, z);
+            return new SecP256R1FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat256.Create();
+            SecP256R1Field.Square(x, z);
+            return new SecP256R1FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new SecP256R1FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat256.Create();
+            Mod.Invert(SecP256R1Field.P, x, z);
+            return new SecP256R1FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            // Raise this element to the exponent 2^254 - 2^222 + 2^190 + 2^94
+
+            uint[] x1 = this.x;
+            if (Nat256.IsZero(x1) || Nat256.IsOne(x1))
+                return this;
+
+            uint[] t1 = Nat256.Create();
+            uint[] t2 = Nat256.Create();
+
+            SecP256R1Field.Square(x1, t1);
+            SecP256R1Field.Multiply(t1, x1, t1);
+
+            SecP256R1Field.SquareN(t1, 2, t2);
+            SecP256R1Field.Multiply(t2, t1, t2);
+
+            SecP256R1Field.SquareN(t2, 4, t1);
+            SecP256R1Field.Multiply(t1, t2, t1);
+
+            SecP256R1Field.SquareN(t1, 8, t2);
+            SecP256R1Field.Multiply(t2, t1, t2);
+
+            SecP256R1Field.SquareN(t2, 16, t1);
+            SecP256R1Field.Multiply(t1, t2, t1);
+
+            SecP256R1Field.SquareN(t1, 32, t1);
+            SecP256R1Field.Multiply(t1, x1, t1);
+
+            SecP256R1Field.SquareN(t1, 96, t1);
+            SecP256R1Field.Multiply(t1, x1, t1);
+
+            SecP256R1Field.SquareN(t1, 94, t1);
+            SecP256R1Field.Multiply(t1, t1, t2);
+
+            return Nat256.Eq(x1, t2) ? new SecP256R1FieldElement(t1) : null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as SecP256R1FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as SecP256R1FieldElement);
+        }
+
+        public virtual bool Equals(SecP256R1FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat256.Eq(x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 8);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Point.cs b/crypto/src/math/ec/custom/sec/SecP256R1Point.cs
new file mode 100644
index 000000000..1de4a0b4a
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP256R1Point.cs
@@ -0,0 +1,277 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP256R1Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
+         */
+        public SecP256R1Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer
+         *             {@link #getEncoded(bool)}
+         */
+        public SecP256R1Point(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");
+        }
+
+        internal SecP256R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new SecP256R1Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.RawXCoord, Y1 = (SecP256R1FieldElement)this.RawYCoord;
+            SecP256R1FieldElement X2 = (SecP256R1FieldElement)b.RawXCoord, Y2 = (SecP256R1FieldElement)b.RawYCoord;
+
+            SecP256R1FieldElement Z1 = (SecP256R1FieldElement)this.RawZCoords[0];
+            SecP256R1FieldElement Z2 = (SecP256R1FieldElement)b.RawZCoords[0];
+
+            uint c;
+            uint[] tt1 = Nat256.CreateExt();
+            uint[] t2 = Nat256.Create();
+            uint[] t3 = Nat256.Create();
+            uint[] t4 = Nat256.Create();
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                SecP256R1Field.Square(Z1.x, S2);
+
+                U2 = t2;
+                SecP256R1Field.Multiply(S2, X2.x, U2);
+
+                SecP256R1Field.Multiply(S2, Z1.x, S2);
+                SecP256R1Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                SecP256R1Field.Square(Z2.x, S1);
+
+                U1 = tt1;
+                SecP256R1Field.Multiply(S1, X1.x, U1);
+
+                SecP256R1Field.Multiply(S1, Z2.x, S1);
+                SecP256R1Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat256.Create();
+            SecP256R1Field.Subtract(U1, U2, H);
+
+            uint[] R = t2;
+            SecP256R1Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat256.IsZero(H))
+            {
+                if (Nat256.IsZero(R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = t3;
+            SecP256R1Field.Square(H, HSquared);
+
+            uint[] G = Nat256.Create();
+            SecP256R1Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            SecP256R1Field.Multiply(HSquared, U1, V);
+
+            SecP256R1Field.Negate(G, G);
+            Nat256.Mul(S1, G, tt1);
+
+            c = Nat256.AddBothTo(V, V, G);
+            SecP256R1Field.Reduce32(c, G);
+
+            SecP256R1FieldElement X3 = new SecP256R1FieldElement(t4);
+            SecP256R1Field.Square(R, X3.x);
+            SecP256R1Field.Subtract(X3.x, G, X3.x);
+
+            SecP256R1FieldElement Y3 = new SecP256R1FieldElement(G);
+            SecP256R1Field.Subtract(V, X3.x, Y3.x);
+            SecP256R1Field.MultiplyAddToExt(Y3.x, R, tt1);
+            SecP256R1Field.Reduce(tt1, Y3.x);
+
+            SecP256R1FieldElement Z3 = new SecP256R1FieldElement(H);
+            if (!Z1IsOne)
+            {
+                SecP256R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                SecP256R1Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
+
+            return new SecP256R1Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            SecP256R1FieldElement Y1 = (SecP256R1FieldElement)this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.RawXCoord, Z1 = (SecP256R1FieldElement)this.RawZCoords[0];
+
+            uint c;
+            uint[] t1 = Nat256.Create();
+            uint[] t2 = Nat256.Create();
+
+            uint[] Y1Squared = Nat256.Create();
+            SecP256R1Field.Square(Y1.x, Y1Squared);
+
+            uint[] T = Nat256.Create();
+            SecP256R1Field.Square(Y1Squared, T);
+
+            bool Z1IsOne = Z1.IsOne;
+
+            uint[] Z1Squared = Z1.x;
+            if (!Z1IsOne)
+            {
+                Z1Squared = t2;
+                SecP256R1Field.Square(Z1.x, Z1Squared);
+            }
+
+            SecP256R1Field.Subtract(X1.x, Z1Squared, t1);
+
+            uint[] M = t2;
+            SecP256R1Field.Add(X1.x, Z1Squared, M);
+            SecP256R1Field.Multiply(M, t1, M);
+            c = Nat256.AddBothTo(M, M, M);
+            SecP256R1Field.Reduce32(c, M);
+
+            uint[] S = Y1Squared;
+            SecP256R1Field.Multiply(Y1Squared, X1.x, S);
+            c = Nat.ShiftUpBits(8, S, 2, 0);
+            SecP256R1Field.Reduce32(c, S);
+
+            c = Nat.ShiftUpBits(8, T, 3, 0, t1);
+            SecP256R1Field.Reduce32(c, t1);
+
+            SecP256R1FieldElement X3 = new SecP256R1FieldElement(T);
+            SecP256R1Field.Square(M, X3.x);
+            SecP256R1Field.Subtract(X3.x, S, X3.x);
+            SecP256R1Field.Subtract(X3.x, S, X3.x);
+
+            SecP256R1FieldElement Y3 = new SecP256R1FieldElement(S);
+            SecP256R1Field.Subtract(S, X3.x, Y3.x);
+            SecP256R1Field.Multiply(Y3.x, M, Y3.x);
+            SecP256R1Field.Subtract(Y3.x, t1, Y3.x);
+
+            SecP256R1FieldElement Z3 = new SecP256R1FieldElement(M);
+            SecP256R1Field.Twice(Y1.x, Z3.x);
+            if (!Z1IsOne)
+            {
+                SecP256R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            return new SecP256R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, 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.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return Twice().Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+            return Twice().Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new SecP256R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
new file mode 100644
index 000000000..7fd58276a
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
@@ -0,0 +1,77 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP384R1Curve
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = new BigInteger(1,
+            Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF"));
+
+        private const int SecP384R1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+        protected readonly SecP384R1Point m_infinity;
+
+        public SecP384R1Curve()
+            : base(q)
+        {
+            this.m_infinity = new SecP384R1Point(this, null, null);
+
+            this.m_a = FromBigInteger(new BigInteger(1,
+                Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC")));
+            this.m_b = FromBigInteger(new BigInteger(1,
+                Hex.Decode("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF")));
+            this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973"));
+            this.m_cofactor = BigInteger.One;
+            this.m_coord = SecP384R1_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new SecP384R1Curve();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+                case COORD_JACOBIAN:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new SecP384R1FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new SecP384R1Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new SecP384R1Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Field.cs b/crypto/src/math/ec/custom/sec/SecP384R1Field.cs
new file mode 100644
index 000000000..508b01e3c
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP384R1Field.cs
@@ -0,0 +1,292 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP384R1Field
+    {
+            // 2^384 - 2^128 - 2^96 + 2^32 - 1
+        internal static readonly uint[] P = new uint[]{ 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF,
+            0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        internal static readonly uint[] PExt = new uint[]{ 0x00000001, 0xFFFFFFFE, 0x00000000, 0x00000002, 0x00000000, 0xFFFFFFFE,
+            0x00000000, 0x00000002, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFE, 0x00000001, 0x00000000,
+            0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+        private static readonly uint[] PExtInv = new uint[]{ 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0x00000001,
+            0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFE, 0xFFFFFFFF,
+            0x00000001, 0x00000002 };
+        private const uint P11 = 0xFFFFFFFF;
+        private const uint PExt23 = 0xFFFFFFFF;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            uint c = Nat.Add(12, x, y, z);
+            if (c != 0 || (z[11] == P11 && Nat.Gte(12, z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void AddExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            uint c = Nat.Add(24, xx, yy, zz);
+            if (c != 0 || (zz[23] == PExt23 && Nat.Gte(24, zz, PExt)))
+            {
+                if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.IncAt(24, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            uint c = Nat.Inc(12, x, z);
+            if (c != 0 || (z[11] == P11 && Nat.Gte(12, z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat.FromBigInteger(384, x);
+            if (z[11] == P11 && Nat.Gte(12, z, P))
+            {
+                Nat.SubFrom(12, P, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            if ((x[0] & 1) == 0)
+            {
+                Nat.ShiftDownBit(12, x, 0, z);
+            }
+            else
+            {
+                uint c = Nat.Add(12, x, P, z);
+                Nat.ShiftDownBit(12, z, c);
+            }
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat.Create(24);
+            Nat384.Mul(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat.IsZero(12, x))
+            {
+                Nat.Zero(12, z);
+            }
+            else
+            {
+                Nat.Sub(12, P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            long xx16 = xx[16], xx17 = xx[17], xx18 = xx[18], xx19 = xx[19];
+            long xx20 = xx[20], xx21 = xx[21], xx22 = xx[22], xx23 = xx[23];
+
+            const long n = 1;
+
+            long t0 = (long)xx[12] + xx20 - n;
+            long t1 = (long)xx[13] + xx22;
+            long t2 = (long)xx[14] + xx22 + xx23;
+            long t3 = (long)xx[15] + xx23;
+            long t4 = xx17 + xx21;
+            long t5 = xx21 - xx23;
+            long t6 = xx22 - xx23;
+
+            long cc = 0;
+            cc += (long)xx[0] + t0 + t5;
+            z[0] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[1] + xx23 - t0 + t1;
+            z[1] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[2] - xx21 - t1 + t2;
+            z[2] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[3] + t0 - t2 + t3 + t5;
+            z[3] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[4] + xx16 + xx21 + t0 + t1 - t3 + t5;
+            z[4] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[5] - xx16 + t1 + t2 + t4;
+            z[5] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[6] + xx18 - xx17 + t2 + t3;
+            z[6] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[7] + xx16 + xx19 - xx18 + t3;
+            z[7] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[8] + xx16 + xx17 + xx20 - xx19;
+            z[8] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[9] + xx18 - xx20 + t4;
+            z[9] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[10] + xx18 + xx19 - t5 + t6;
+            z[10] = (uint)cc;
+            cc >>= 32;
+            cc += (long)xx[11] + xx19 + xx20 - t6;
+            z[11] = (uint)cc;
+            cc >>= 32;
+            cc += n;
+
+            Debug.Assert(cc >= 0);
+
+            Reduce32((uint)cc, z);
+        }
+
+        public static void Reduce32(uint x, uint[] z)
+        {
+            long cc = 0;
+
+            if (x != 0)
+            {
+                long xx12 = x;
+
+                cc += (long)z[0] + xx12;
+                z[0] = (uint)cc;
+                cc >>= 32;
+                cc += (long)z[1] - xx12;
+                z[1] = (uint)cc;
+                cc >>= 32;
+                if (cc != 0)
+                {
+                    cc += (long)z[2];
+                    z[2] = (uint)cc;
+                    cc >>= 32;
+                }
+                cc += (long)z[3] + xx12;
+                z[3] = (uint)cc;
+                cc >>= 32;
+                cc += (long)z[4] + xx12;
+                z[4] = (uint)cc;
+                cc >>= 32;
+
+                Debug.Assert(cc == 0 || cc == 1);
+            }
+
+            if ((cc != 0 && Nat.IncAt(12, z, 5) != 0)
+                || (z[11] == P11 && Nat.Gte(12, z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat.Create(24);
+            Nat384.Square(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            uint[] tt = Nat.Create(24);
+            Nat384.Square(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                Nat384.Square(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat.Sub(12, x, y, z);
+            if (c != 0)
+            {
+                SubPInvFrom(z);
+            }
+        }
+
+        public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz)
+        {
+            int c = Nat.Sub(24, xx, yy, zz);
+            if (c != 0)
+            {
+                if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0)
+                {
+                    Nat.DecAt(24, zz, PExtInv.Length);
+                }
+            }
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            uint c = Nat.ShiftUpBit(12, x, 0, z);
+            if (c != 0 || (z[11] == P11 && Nat.Gte(12, z, P)))
+            {
+                AddPInvTo(z);
+            }
+        }
+
+        private static void AddPInvTo(uint[] z)
+        {
+            long c = (long)z[0] + 1;
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - 1;
+            z[1] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[2];
+                z[2] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[3] + 1;
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)z[4] + 1;
+            z[4] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                Nat.IncAt(12, z, 5);
+            }
+        }
+
+        private static void SubPInvFrom(uint[] z)
+        {
+            long c = (long)z[0] - 1;
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] + 1;
+            z[1] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                c += (long)z[2];
+                z[2] = (uint)c;
+                c >>= 32;
+            }
+            c += (long)z[3] - 1;
+            z[3] = (uint)c;
+            c >>= 32;
+            c += (long)z[4] - 1;
+            z[4] = (uint)c;
+            c >>= 32;
+            if (c != 0)
+            {
+                Nat.DecAt(12, z, 5);
+            }
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs
new file mode 100644
index 000000000..40086978d
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs
@@ -0,0 +1,209 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP384R1FieldElement
+        : ECFieldElement
+    {
+        public static readonly BigInteger Q = SecP384R1Curve.q;
+
+        protected internal readonly uint[] x;
+
+        public SecP384R1FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for SecP384R1FieldElement", "x");
+
+            this.x = SecP384R1Field.FromBigInteger(x);
+        }
+
+        public SecP384R1FieldElement()
+        {
+            this.x = Nat.Create(12);
+        }
+
+        protected internal SecP384R1FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat.IsZero(12, x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat.IsOne(12, x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat.ToBigInteger(12, x);
+        }
+
+        public override string FieldName
+        {
+            get { return "SecP384R1Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat.Create(12);
+            SecP384R1Field.Add(x, ((SecP384R1FieldElement)b).x, z);
+            return new SecP384R1FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat.Create(12);
+            SecP384R1Field.AddOne(x, z);
+            return new SecP384R1FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat.Create(12);
+            SecP384R1Field.Subtract(x, ((SecP384R1FieldElement)b).x, z);
+            return new SecP384R1FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat.Create(12);
+            SecP384R1Field.Multiply(x, ((SecP384R1FieldElement)b).x, z);
+            return new SecP384R1FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat.Create(12);
+            Mod.Invert(SecP384R1Field.P, ((SecP384R1FieldElement)b).x, z);
+            SecP384R1Field.Multiply(z, x, z);
+            return new SecP384R1FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat.Create(12);
+            SecP384R1Field.Negate(x, z);
+            return new SecP384R1FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat.Create(12);
+            SecP384R1Field.Square(x, z);
+            return new SecP384R1FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new SecP384R1FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat.Create(12);
+            Mod.Invert(SecP384R1Field.P, x, z);
+            return new SecP384R1FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            // Raise this element to the exponent 2^382 - 2^126 - 2^94 + 2^30
+
+            uint[] x1 = this.x;
+            if (Nat.IsZero(12, x1) || Nat.IsOne(12, x1))
+                return this;
+
+            uint[] t1 = Nat.Create(12);
+            uint[] t2 = Nat.Create(12);
+            uint[] t3 = Nat.Create(12);
+            uint[] t4 = Nat.Create(12);
+
+            SecP384R1Field.Square(x1, t1);
+            SecP384R1Field.Multiply(t1, x1, t1);
+
+            SecP384R1Field.SquareN(t1, 2, t2);
+            SecP384R1Field.Multiply(t2, t1, t2);
+
+            SecP384R1Field.Square(t2, t2);
+            SecP384R1Field.Multiply(t2, x1, t2);
+
+            SecP384R1Field.SquareN(t2, 5, t3);
+            SecP384R1Field.Multiply(t3, t2, t3);
+
+            SecP384R1Field.SquareN(t3, 5, t4);
+            SecP384R1Field.Multiply(t4, t2, t4);
+
+            SecP384R1Field.SquareN(t4, 15, t2);
+            SecP384R1Field.Multiply(t2, t4, t2);
+
+            SecP384R1Field.SquareN(t2, 2, t3);
+            SecP384R1Field.Multiply(t1, t3, t1);
+
+            SecP384R1Field.SquareN(t3, 28, t3);
+            SecP384R1Field.Multiply(t2, t3, t2);
+
+            SecP384R1Field.SquareN(t2, 60, t3);
+            SecP384R1Field.Multiply(t3, t2, t3);
+
+            uint[] r = t2;
+
+            SecP384R1Field.SquareN(t3, 120, r);
+            SecP384R1Field.Multiply(r, t3, r);
+
+            SecP384R1Field.SquareN(r, 15, r);
+            SecP384R1Field.Multiply(r, t4, r);
+
+            SecP384R1Field.SquareN(r, 33, r);
+            SecP384R1Field.Multiply(r, t1, r);
+
+            SecP384R1Field.SquareN(r, 64, r);
+            SecP384R1Field.Multiply(r, x1, r);
+
+            SecP384R1Field.SquareN(r, 30, t1);
+            SecP384R1Field.Square(t1, t2);
+
+            return Nat.Eq(12, x1, t2) ? new SecP384R1FieldElement(t1) : null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as SecP384R1FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as SecP384R1FieldElement);
+        }
+
+        public virtual bool Equals(SecP384R1FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat.Eq(12, x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 12);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Point.cs b/crypto/src/math/ec/custom/sec/SecP384R1Point.cs
new file mode 100644
index 000000000..68c601611
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP384R1Point.cs
@@ -0,0 +1,278 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP384R1Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
+         */
+        public SecP384R1Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer
+         *             {@link #getEncoded(bool)}
+         */
+        public SecP384R1Point(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");
+        }
+
+        internal SecP384R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new SecP384R1Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.RawXCoord, Y1 = (SecP384R1FieldElement)this.RawYCoord;
+            SecP384R1FieldElement X2 = (SecP384R1FieldElement)b.RawXCoord, Y2 = (SecP384R1FieldElement)b.RawYCoord;
+
+            SecP384R1FieldElement Z1 = (SecP384R1FieldElement)this.RawZCoords[0];
+            SecP384R1FieldElement Z2 = (SecP384R1FieldElement)b.RawZCoords[0];
+
+            uint c;
+            uint[] tt1 = Nat.Create(24);
+            uint[] tt2 = Nat.Create(24);
+            uint[] t3 = Nat.Create(12);
+            uint[] t4 = Nat.Create(12);
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                SecP384R1Field.Square(Z1.x, S2);
+
+                U2 = tt2;
+                SecP384R1Field.Multiply(S2, X2.x, U2);
+
+                SecP384R1Field.Multiply(S2, Z1.x, S2);
+                SecP384R1Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                SecP384R1Field.Square(Z2.x, S1);
+
+                U1 = tt1;
+                SecP384R1Field.Multiply(S1, X1.x, U1);
+
+                SecP384R1Field.Multiply(S1, Z2.x, S1);
+                SecP384R1Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat.Create(12);
+            SecP384R1Field.Subtract(U1, U2, H);
+
+            uint[] R = Nat.Create(12);
+            SecP384R1Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat.IsZero(12, H))
+            {
+                if (Nat.IsZero(12, R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = t3;
+            SecP384R1Field.Square(H, HSquared);
+
+            uint[] G = Nat.Create(12);
+            SecP384R1Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            SecP384R1Field.Multiply(HSquared, U1, V);
+
+            SecP384R1Field.Negate(G, G);
+            Nat384.Mul(S1, G, tt1);
+
+            c = Nat.AddBothTo(12, V, V, G);
+            SecP384R1Field.Reduce32(c, G);
+
+            SecP384R1FieldElement X3 = new SecP384R1FieldElement(t4);
+            SecP384R1Field.Square(R, X3.x);
+            SecP384R1Field.Subtract(X3.x, G, X3.x);
+
+            SecP384R1FieldElement Y3 = new SecP384R1FieldElement(G);
+            SecP384R1Field.Subtract(V, X3.x, Y3.x);
+            Nat384.Mul(Y3.x, R, tt2);
+            SecP384R1Field.AddExt(tt1, tt2, tt1);
+            SecP384R1Field.Reduce(tt1, Y3.x);
+
+            SecP384R1FieldElement Z3 = new SecP384R1FieldElement(H);
+            if (!Z1IsOne)
+            {
+                SecP384R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                SecP384R1Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            ECFieldElement[] zs = new ECFieldElement[] { Z3 };
+
+            return new SecP384R1Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            SecP384R1FieldElement Y1 = (SecP384R1FieldElement)this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.RawXCoord, Z1 = (SecP384R1FieldElement)this.RawZCoords[0];
+
+            uint c;
+            uint[] t1 = Nat.Create(12);
+            uint[] t2 = Nat.Create(12);
+
+            uint[] Y1Squared = Nat.Create(12);
+            SecP384R1Field.Square(Y1.x, Y1Squared);
+
+            uint[] T = Nat.Create(12);
+            SecP384R1Field.Square(Y1Squared, T);
+
+            bool Z1IsOne = Z1.IsOne;
+
+            uint[] Z1Squared = Z1.x;
+            if (!Z1IsOne)
+            {
+                Z1Squared = t2;
+                SecP384R1Field.Square(Z1.x, Z1Squared);
+            }
+
+            SecP384R1Field.Subtract(X1.x, Z1Squared, t1);
+
+            uint[] M = t2;
+            SecP384R1Field.Add(X1.x, Z1Squared, M);
+            SecP384R1Field.Multiply(M, t1, M);
+            c = Nat.AddBothTo(12, M, M, M);
+            SecP384R1Field.Reduce32(c, M);
+
+            uint[] S = Y1Squared;
+            SecP384R1Field.Multiply(Y1Squared, X1.x, S);
+            c = Nat.ShiftUpBits(12, S, 2, 0);
+            SecP384R1Field.Reduce32(c, S);
+
+            c = Nat.ShiftUpBits(12, T, 3, 0, t1);
+            SecP384R1Field.Reduce32(c, t1);
+
+            SecP384R1FieldElement X3 = new SecP384R1FieldElement(T);
+            SecP384R1Field.Square(M, X3.x);
+            SecP384R1Field.Subtract(X3.x, S, X3.x);
+            SecP384R1Field.Subtract(X3.x, S, X3.x);
+
+            SecP384R1FieldElement Y3 = new SecP384R1FieldElement(S);
+            SecP384R1Field.Subtract(S, X3.x, Y3.x);
+            SecP384R1Field.Multiply(Y3.x, M, Y3.x);
+            SecP384R1Field.Subtract(Y3.x, t1, Y3.x);
+
+            SecP384R1FieldElement Z3 = new SecP384R1FieldElement(M);
+            SecP384R1Field.Twice(Y1.x, Z3.x);
+            if (!Z1IsOne)
+            {
+                SecP384R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            return new SecP384R1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, 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.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return Twice().Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+            return Twice().Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new SecP384R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
new file mode 100644
index 000000000..e5083c7f0
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
@@ -0,0 +1,77 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP521R1Curve
+        : AbstractFpCurve
+    {
+        public static readonly BigInteger q = new BigInteger(1,
+            Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));
+
+        private const int SecP521R1_DEFAULT_COORDS = COORD_JACOBIAN;
+
+        protected readonly SecP521R1Point m_infinity;
+
+        public SecP521R1Curve()
+            : base(q)
+        {
+            this.m_infinity = new SecP521R1Point(this, null, null);
+
+            this.m_a = FromBigInteger(new BigInteger(1,
+                Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC")));
+            this.m_b = FromBigInteger(new BigInteger(1,
+                Hex.Decode("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00")));
+            this.m_order = new BigInteger(1, Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409"));
+            this.m_cofactor = BigInteger.One;
+            this.m_coord = SecP521R1_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new SecP521R1Curve();
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+                case COORD_JACOBIAN:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new SecP521R1FieldElement(x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new SecP521R1Point(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new SecP521R1Point(this, x, y, zs, withCompression);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Field.cs b/crypto/src/math/ec/custom/sec/SecP521R1Field.cs
new file mode 100644
index 000000000..3568156d8
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP521R1Field.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP521R1Field
+    {
+        // 2^521 - 1
+        internal static readonly uint[] P = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+            0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x1FF };
+        private const int P16 = 0x1FF;
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            uint c = Nat.Add(16, x, y, z) + x[16] + y[16];
+            if (c > P16 || (c == P16 && Nat.Eq(16, z, P)))
+            {
+                c += Nat.Inc(16, z);
+                c &= P16;
+            }
+            z[16] = c;
+        }
+
+        public static void AddOne(uint[] x, uint[] z)
+        {
+            uint c = Nat.Inc(16, x, z) + x[16];
+            if (c > P16 || (c == P16 && Nat.Eq(16, z, P)))
+            {
+                c += Nat.Inc(16, z);
+                c &= P16;
+            }
+            z[16] = c;
+        }
+
+        public static uint[] FromBigInteger(BigInteger x)
+        {
+            uint[] z = Nat.FromBigInteger(521, x);
+            if (Nat.Eq(17, z, P))
+            {
+                Nat.Zero(17, z);
+            }
+            return z;
+        }
+
+        public static void Half(uint[] x, uint[] z)
+        {
+            uint x16 = x[16];
+            uint c = Nat.ShiftDownBit(16, x, x16, z);
+            z[16] = (x16 >> 1) | (c >> 23);
+        }
+
+        public static void Multiply(uint[] x, uint[] y, uint[] z)
+        {
+            uint[] tt = Nat.Create(33);
+            ImplMultiply(x, y, tt);
+            Reduce(tt, z);
+        }
+
+        public static void Negate(uint[] x, uint[] z)
+        {
+            if (Nat.IsZero(17, x))
+            {
+                Nat.Zero(17, z);
+            }
+            else
+            {
+                Nat.Sub(17, P, x, z);
+            }
+        }
+
+        public static void Reduce(uint[] xx, uint[] z)
+        {
+            Debug.Assert(xx[32] >> 18 == 0);
+            uint xx32 = xx[32];
+            uint c = Nat.ShiftDownBits(16, xx, 16, 9, xx32, z, 0) >> 23;
+            c += xx32 >> 9;
+            c += Nat.AddTo(16, xx, z);
+            if (c > P16 || (c == P16 && Nat.Eq(16, z, P)))
+            {
+                c += Nat.Inc(16, z);
+                c &= P16;
+            }
+            z[16] = c;
+        }
+
+        public static void Reduce23(uint[] z)
+        {
+            uint z16 = z[16];
+            uint c = Nat.AddWordTo(16, z16 >> 9, z) + (z16 & P16);
+            if (c > P16 || (c == P16 && Nat.Eq(16, z, P)))
+            {
+                c += Nat.Inc(16, z);
+                c &= P16;
+            }
+            z[16] = c;
+        }
+
+        public static void Square(uint[] x, uint[] z)
+        {
+            uint[] tt = Nat.Create(33);
+            ImplSquare(x, tt);
+            Reduce(tt, z);
+        }
+
+        public static void SquareN(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+            uint[] tt = Nat.Create(33);
+            ImplSquare(x, tt);
+            Reduce(tt, z);
+
+            while (--n > 0)
+            {
+                ImplSquare(z, tt);
+                Reduce(tt, z);
+            }
+        }
+
+        public static void Subtract(uint[] x, uint[] y, uint[] z)
+        {
+            int c = Nat.Sub(16, x, y, z) + (int)(x[16] - y[16]);
+            if (c < 0)
+            {
+                c += Nat.Dec(16, z);
+                c &= P16;
+            }
+            z[16] = (uint)c;
+        }
+
+        public static void Twice(uint[] x, uint[] z)
+        {
+            uint x16 = x[16];
+            uint c = Nat.ShiftUpBit(16, x, x16 << 23, z) | (x16 << 1);
+            z[16] = c & P16;
+        }
+
+        protected static void ImplMultiply(uint[] x, uint[] y, uint[] zz)
+        {
+            Nat512.Mul(x, y, zz);
+
+            uint x16 = x[16], y16 = y[16];
+            zz[32] = Nat.Mul31BothAdd(16, x16, y, y16, x, zz, 16) + (x16 * y16);
+        }
+
+        protected static void ImplSquare(uint[] x, uint[] zz)
+        {
+            Nat512.Square(x, zz);
+
+            uint x16 = x[16];
+            zz[32] = Nat.MulWordAddTo(16, x16 << 1, x, 0, zz, 16) + (x16 * x16);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs
new file mode 100644
index 000000000..83a615928
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs
@@ -0,0 +1,166 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP521R1FieldElement
+        : ECFieldElement
+    {
+        public static readonly BigInteger Q = SecP521R1Curve.q;
+
+        protected internal readonly uint[] x;
+
+        public SecP521R1FieldElement(BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+                throw new ArgumentException("value invalid for SecP521R1FieldElement", "x");
+
+            this.x = SecP521R1Field.FromBigInteger(x);
+        }
+
+        public SecP521R1FieldElement()
+        {
+            this.x = Nat.Create(17);
+        }
+
+        protected internal SecP521R1FieldElement(uint[] x)
+        {
+            this.x = x;
+        }
+
+        public override bool IsZero
+        {
+            get { return Nat.IsZero(17, x); }
+        }
+
+        public override bool IsOne
+        {
+            get { return Nat.IsOne(17, x); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return Nat.GetBit(x, 0) == 1;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return Nat.ToBigInteger(17, x);
+        }
+
+        public override string FieldName
+        {
+            get { return "SecP521R1Field"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return Q.BitLength; }
+        }
+
+        public override ECFieldElement Add(ECFieldElement b)
+        {
+            uint[] z = Nat.Create(17);
+            SecP521R1Field.Add(x, ((SecP521R1FieldElement)b).x, z);
+            return new SecP521R1FieldElement(z);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            uint[] z = Nat.Create(17);
+            SecP521R1Field.AddOne(x, z);
+            return new SecP521R1FieldElement(z);
+        }
+
+        public override ECFieldElement Subtract(ECFieldElement b)
+        {
+            uint[] z = Nat.Create(17);
+            SecP521R1Field.Subtract(x, ((SecP521R1FieldElement)b).x, z);
+            return new SecP521R1FieldElement(z);
+        }
+
+        public override ECFieldElement Multiply(ECFieldElement b)
+        {
+            uint[] z = Nat.Create(17);
+            SecP521R1Field.Multiply(x, ((SecP521R1FieldElement)b).x, z);
+            return new SecP521R1FieldElement(z);
+        }
+
+        public override ECFieldElement Divide(ECFieldElement b)
+        {
+            //return Multiply(b.Invert());
+            uint[] z = Nat.Create(17);
+            Mod.Invert(SecP521R1Field.P, ((SecP521R1FieldElement)b).x, z);
+            SecP521R1Field.Multiply(z, x, z);
+            return new SecP521R1FieldElement(z);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            uint[] z = Nat.Create(17);
+            SecP521R1Field.Negate(x, z);
+            return new SecP521R1FieldElement(z);
+        }
+
+        public override ECFieldElement Square()
+        {
+            uint[] z = Nat.Create(17);
+            SecP521R1Field.Square(x, z);
+            return new SecP521R1FieldElement(z);
+        }
+
+        public override ECFieldElement Invert()
+        {
+            //return new SecP521R1FieldElement(ToBigInteger().ModInverse(Q));
+            uint[] z = Nat.Create(17);
+            Mod.Invert(SecP521R1Field.P, x, z);
+            return new SecP521R1FieldElement(z);
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation returns the right value - if
+         * none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            // Raise this element to the exponent 2^519
+
+            uint[] x1 = this.x;
+            if (Nat.IsZero(17, x1) || Nat.IsOne(17, x1))
+                return this;
+
+            uint[] t1 = Nat.Create(17);
+            uint[] t2 = Nat.Create(17);
+
+            SecP521R1Field.SquareN(x1, 519, t1);
+            SecP521R1Field.Square(t1, t2);
+
+            return Nat.Eq(17, x1, t2) ? new SecP521R1FieldElement(t1) : null;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as SecP521R1FieldElement);
+        }
+
+        public override bool Equals(ECFieldElement other)
+        {
+            return Equals(other as SecP521R1FieldElement);
+        }
+
+        public virtual bool Equals(SecP521R1FieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Nat.Eq(17, x, other.x);
+        }
+
+        public override int GetHashCode()
+        {
+            return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 17);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Point.cs b/crypto/src/math/ec/custom/sec/SecP521R1Point.cs
new file mode 100644
index 000000000..fb1996cfd
--- /dev/null
+++ b/crypto/src/math/ec/custom/sec/SecP521R1Point.cs
@@ -0,0 +1,273 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Sec
+{
+    internal class SecP521R1Point
+        : AbstractFpPoint
+    {
+        /**
+         * 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
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
+         */
+        public SecP521R1Point(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
+         * 
+         * @deprecated per-point compression property will be removed, refer
+         *             {@link #getEncoded(bool)}
+         */
+        public SecP521R1Point(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");
+        }
+
+        internal SecP521R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new SecP521R1Point(null, AffineXCoord, AffineYCoord);
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.RawXCoord, Y1 = (SecP521R1FieldElement)this.RawYCoord;
+            SecP521R1FieldElement X2 = (SecP521R1FieldElement)b.RawXCoord, Y2 = (SecP521R1FieldElement)b.RawYCoord;
+
+            SecP521R1FieldElement Z1 = (SecP521R1FieldElement)this.RawZCoords[0];
+            SecP521R1FieldElement Z2 = (SecP521R1FieldElement)b.RawZCoords[0];
+
+            uint[] t1 = Nat.Create(17);
+            uint[] t2 = Nat.Create(17);
+            uint[] t3 = Nat.Create(17);
+            uint[] t4 = Nat.Create(17);
+
+            bool Z1IsOne = Z1.IsOne;
+            uint[] U2, S2;
+            if (Z1IsOne)
+            {
+                U2 = X2.x;
+                S2 = Y2.x;
+            }
+            else
+            {
+                S2 = t3;
+                SecP521R1Field.Square(Z1.x, S2);
+
+                U2 = t2;
+                SecP521R1Field.Multiply(S2, X2.x, U2);
+
+                SecP521R1Field.Multiply(S2, Z1.x, S2);
+                SecP521R1Field.Multiply(S2, Y2.x, S2);
+            }
+
+            bool Z2IsOne = Z2.IsOne;
+            uint[] U1, S1;
+            if (Z2IsOne)
+            {
+                U1 = X1.x;
+                S1 = Y1.x;
+            }
+            else
+            {
+                S1 = t4;
+                SecP521R1Field.Square(Z2.x, S1);
+
+                U1 = t1;
+                SecP521R1Field.Multiply(S1, X1.x, U1);
+
+                SecP521R1Field.Multiply(S1, Z2.x, S1);
+                SecP521R1Field.Multiply(S1, Y1.x, S1);
+            }
+
+            uint[] H = Nat.Create(17);
+            SecP521R1Field.Subtract(U1, U2, H);
+
+            uint[] R = t2;
+            SecP521R1Field.Subtract(S1, S2, R);
+
+            // Check if b == this or b == -this
+            if (Nat.IsZero(17, H))
+            {
+                if (Nat.IsZero(17, R))
+                {
+                    // this == b, i.e. this must be doubled
+                    return this.Twice();
+                }
+
+                // this == -b, i.e. the result is the point at infinity
+                return curve.Infinity;
+            }
+
+            uint[] HSquared = t3;
+            SecP521R1Field.Square(H, HSquared);
+
+            uint[] G = Nat.Create(17);
+            SecP521R1Field.Multiply(HSquared, H, G);
+
+            uint[] V = t3;
+            SecP521R1Field.Multiply(HSquared, U1, V);
+
+            SecP521R1Field.Multiply(S1, G, t1);
+
+            SecP521R1FieldElement X3 = new SecP521R1FieldElement(t4);
+            SecP521R1Field.Square(R, X3.x);
+            SecP521R1Field.Add(X3.x, G, X3.x);
+            SecP521R1Field.Subtract(X3.x, V, X3.x);
+            SecP521R1Field.Subtract(X3.x, V, X3.x);
+
+            SecP521R1FieldElement Y3 = new SecP521R1FieldElement(G);
+            SecP521R1Field.Subtract(V, X3.x, Y3.x);
+            SecP521R1Field.Multiply(Y3.x, R, t2);
+            SecP521R1Field.Subtract(t2, t1, Y3.x);
+
+            SecP521R1FieldElement Z3 = new SecP521R1FieldElement(H);
+            if (!Z1IsOne)
+            {
+                SecP521R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+            if (!Z2IsOne)
+            {
+                SecP521R1Field.Multiply(Z3.x, Z2.x, Z3.x);
+            }
+
+            ECFieldElement[] zs = new ECFieldElement[] { Z3 };
+
+            return new SecP521R1Point(curve, X3, Y3, zs, IsCompressed);
+        }
+
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            SecP521R1FieldElement Y1 = (SecP521R1FieldElement)this.RawYCoord;
+            if (Y1.IsZero)
+                return curve.Infinity;
+
+            SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.RawXCoord, Z1 = (SecP521R1FieldElement)this.RawZCoords[0];
+
+            uint[] t1 = Nat.Create(17);
+            uint[] t2 = Nat.Create(17);
+
+            uint[] Y1Squared = Nat.Create(17);
+            SecP521R1Field.Square(Y1.x, Y1Squared);
+
+            uint[] T = Nat.Create(17);
+            SecP521R1Field.Square(Y1Squared, T);
+
+            bool Z1IsOne = Z1.IsOne;
+
+            uint[] Z1Squared = Z1.x;
+            if (!Z1IsOne)
+            {
+                Z1Squared = t2;
+                SecP521R1Field.Square(Z1.x, Z1Squared);
+            }
+
+            SecP521R1Field.Subtract(X1.x, Z1Squared, t1);
+
+            uint[] M = t2;
+            SecP521R1Field.Add(X1.x, Z1Squared, M);
+            SecP521R1Field.Multiply(M, t1, M);
+            Nat.AddBothTo(17, M, M, M);
+            SecP521R1Field.Reduce23(M);
+
+            uint[] S = Y1Squared;
+            SecP521R1Field.Multiply(Y1Squared, X1.x, S);
+            Nat.ShiftUpBits(17, S, 2, 0);
+            SecP521R1Field.Reduce23(S);
+
+            Nat.ShiftUpBits(17, T, 3, 0, t1);
+            SecP521R1Field.Reduce23(t1);
+
+            SecP521R1FieldElement X3 = new SecP521R1FieldElement(T);
+            SecP521R1Field.Square(M, X3.x);
+            SecP521R1Field.Subtract(X3.x, S, X3.x);
+            SecP521R1Field.Subtract(X3.x, S, X3.x);
+
+            SecP521R1FieldElement Y3 = new SecP521R1FieldElement(S);
+            SecP521R1Field.Subtract(S, X3.x, Y3.x);
+            SecP521R1Field.Multiply(Y3.x, M, Y3.x);
+            SecP521R1Field.Subtract(Y3.x, t1, Y3.x);
+
+            SecP521R1FieldElement Z3 = new SecP521R1FieldElement(M);
+            SecP521R1Field.Twice(Y1.x, Z3.x);
+            if (!Z1IsOne)
+            {
+                SecP521R1Field.Multiply(Z3.x, Z1.x, Z3.x);
+            }
+
+            return new SecP521R1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, 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.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            return Twice().Add(b);
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity || this.RawYCoord.IsZero)
+                return this;
+
+            // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+            return Twice().Add(this);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            return new SecP521R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+        }
+    }
+}