summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2018-04-15 21:12:11 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2018-04-15 21:12:11 +0700
commitd79a501212d4012139c714e361577669c75171aa (patch)
treef78e8c7d34c9448698e17bc341fd8d293814dd3e /crypto/src
parentUpdate Readme.html for SHA-3 perf. opts. (diff)
downloadBouncyCastle.NET-ed25519-d79a501212d4012139c714e361577669c75171aa.tar.xz
Cache-safety for EC lookup tables
- creation of cache-safe lookup tables delegated to ECCurve
- FixedPointCombMultiplier uses cache-safe lookup table
- FixedPointCombMultiplier avoids BigInteger.TestBit
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/math/ec/ECCurve.cs137
-rw-r--r--crypto/src/math/ec/ECFieldElement.cs4
-rw-r--r--crypto/src/math/ec/ECLookupTable.cs10
-rw-r--r--crypto/src/math/ec/LongArray.cs5
-rw-r--r--crypto/src/math/ec/SimpleLookupTable.cs35
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519.cs58
-rw-r--r--crypto/src/math/ec/custom/gm/SM2P256V1Curve.cs59
-rw-r--r--crypto/src/math/ec/custom/sec/SecP128R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160K1Curve.cs59
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R2Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Curve.cs59
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Curve.cs59
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Curve.cs59
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT113R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT113R2Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT131R2Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163K1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT163R2Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT193R2Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233K1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT233R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT239FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecT239K1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283K1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT283R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409K1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT409R1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571FieldElement.cs2
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571K1Curve.cs63
-rw-r--r--crypto/src/math/ec/custom/sec/SecT571R1Curve.cs63
-rw-r--r--crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs27
-rw-r--r--crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs17
-rw-r--r--crypto/src/math/ec/multiplier/FixedPointUtilities.cs8
-rw-r--r--crypto/src/math/raw/Nat.cs5
-rw-r--r--crypto/src/math/raw/Nat128.cs14
-rw-r--r--crypto/src/math/raw/Nat160.cs9
-rw-r--r--crypto/src/math/raw/Nat192.cs17
-rw-r--r--crypto/src/math/raw/Nat224.cs11
-rw-r--r--crypto/src/math/raw/Nat256.cs20
-rw-r--r--crypto/src/math/raw/Nat320.cs9
-rw-r--r--crypto/src/math/raw/Nat448.cs11
-rw-r--r--crypto/src/math/raw/Nat576.cs13
57 files changed, 2284 insertions, 75 deletions
diff --git a/crypto/src/math/ec/ECCurve.cs b/crypto/src/math/ec/ECCurve.cs
index 6ccd97e7b..6a9342722 100644
--- a/crypto/src/math/ec/ECCurve.cs
+++ b/crypto/src/math/ec/ECCurve.cs
@@ -5,6 +5,7 @@ using Org.BouncyCastle.Math.EC.Abc;
 using Org.BouncyCastle.Math.EC.Endo;
 using Org.BouncyCastle.Math.EC.Multiplier;
 using Org.BouncyCastle.Math.Field;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Math.EC
@@ -321,6 +322,33 @@ namespace Org.BouncyCastle.Math.EC
             get { return m_coord; }
         }
 
+        /**
+         * Create a cache-safe lookup table for the specified sequence of points. All the points MUST
+         * belong to this <code>ECCurve</code> instance, and MUST already be normalized.
+         */
+        public virtual ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            int FE_BYTES = (FieldSize + 7) / 8;
+            byte[] table = new byte[len * FE_BYTES * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    byte[] px = p.RawXCoord.ToBigInteger().ToByteArray();
+                    byte[] py = p.RawYCoord.ToBigInteger().ToByteArray();
+
+                    int pxStart = px.Length > FE_BYTES ? 1 : 0, pxLen = px.Length - pxStart;
+                    int pyStart = py.Length > FE_BYTES ? 1 : 0, pyLen = py.Length - pyStart;
+
+                    Array.Copy(px, pxStart, table, pos + FE_BYTES - pxLen, pxLen); pos += FE_BYTES;
+                    Array.Copy(py, pyStart, table, pos + FE_BYTES - pyLen, pyLen); pos += FE_BYTES;
+                }
+            }
+
+            return new DefaultLookupTable(this, table, len);
+        }
+
         protected virtual void CheckPoint(ECPoint point)
         {
             if (null == point || (this != point.Curve))
@@ -468,6 +496,50 @@ namespace Org.BouncyCastle.Math.EC
 
             return p;
         }
+
+        private class DefaultLookupTable
+            : ECLookupTable
+        {
+            private readonly ECCurve m_outer;
+            private readonly byte[] m_table;
+            private readonly int m_size;
+
+            internal DefaultLookupTable(ECCurve outer, byte[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                int FE_BYTES = (m_outer.FieldSize + 7) / 8;
+                byte[] x = new byte[FE_BYTES], y = new byte[FE_BYTES];
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    byte MASK = (byte)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < FE_BYTES; ++j)
+                    {
+                        x[j] ^= (byte)(m_table[pos + j] & MASK);
+                        y[j] ^= (byte)(m_table[pos + FE_BYTES + j] & MASK);
+                    }
+
+                    pos += (FE_BYTES * 2);
+                }
+
+                ECFieldElement X = m_outer.FromBigInteger(new BigInteger(1, x));
+                ECFieldElement Y = m_outer.FromBigInteger(new BigInteger(1, y));
+                return m_outer.CreateRawPoint(X, Y, false);
+            }
+        }
     }
 
     public abstract class AbstractFpCurve
@@ -1127,5 +1199,70 @@ namespace Org.BouncyCastle.Math.EC
         {
             get { return m_cofactor; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            int FE_LONGS = (m + 63) / 64;
+
+            long[] table = new long[len * FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    ((F2mFieldElement)p.RawXCoord).x.CopyTo(table, pos); pos += FE_LONGS;
+                    ((F2mFieldElement)p.RawYCoord).x.CopyTo(table, pos); pos += FE_LONGS;
+                }
+            }
+
+            return new DefaultF2mLookupTable(this, table, len);
+        }
+
+        private class DefaultF2mLookupTable
+            : ECLookupTable
+        {
+            private readonly F2mCurve m_outer;
+            private readonly long[] m_table;
+            private readonly int m_size;
+
+            internal DefaultF2mLookupTable(F2mCurve outer, long[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                int m = m_outer.m;
+                int[] ks = m_outer.IsTrinomial() ? new int[]{ m_outer.k1 } : new int[]{ m_outer.k1, m_outer.k2, m_outer.k3 }; 
+
+                int FE_LONGS = (m_outer.m + 63) / 64;
+                long[] x = new long[FE_LONGS], y = new long[FE_LONGS];
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    long MASK =((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                ECFieldElement X = new F2mFieldElement(m, ks, new LongArray(x));
+                ECFieldElement Y = new F2mFieldElement(m, ks, new LongArray(y));
+                return m_outer.CreateRawPoint(X, Y, false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/ECFieldElement.cs b/crypto/src/math/ec/ECFieldElement.cs
index d0e008aab..3676c81b1 100644
--- a/crypto/src/math/ec/ECFieldElement.cs
+++ b/crypto/src/math/ec/ECFieldElement.cs
@@ -579,7 +579,7 @@ namespace Org.BouncyCastle.Math.EC
         /**
          * The <code>LongArray</code> holding the bits.
          */
-        private LongArray x;
+        internal LongArray x;
 
         /**
             * Constructor for Ppb.
@@ -644,7 +644,7 @@ namespace Org.BouncyCastle.Math.EC
             // Set k1 to k, and set k2 and k3 to 0
         }
 
-        private F2mFieldElement(int m, int[] ks, LongArray x)
+        internal F2mFieldElement(int m, int[] ks, LongArray x)
         {
             this.m = m;
             this.representation = (ks.Length == 1) ? Tpb : Ppb;
diff --git a/crypto/src/math/ec/ECLookupTable.cs b/crypto/src/math/ec/ECLookupTable.cs
new file mode 100644
index 000000000..35995d426
--- /dev/null
+++ b/crypto/src/math/ec/ECLookupTable.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC
+{
+    public interface ECLookupTable
+    {
+        int Size { get; }
+        ECPoint Lookup(int index);
+    }
+}
diff --git a/crypto/src/math/ec/LongArray.cs b/crypto/src/math/ec/LongArray.cs
index 84462e0ea..a6b834fbe 100644
--- a/crypto/src/math/ec/LongArray.cs
+++ b/crypto/src/math/ec/LongArray.cs
@@ -372,6 +372,11 @@ namespace Org.BouncyCastle.Math.EC
             }
         }
 
+        internal void CopyTo(long[] z, int zOff)
+        {
+            Array.Copy(m_ints, 0, z, zOff, m_ints.Length);
+        }
+
         public bool IsOne()
         {
             long[] a = m_ints;
diff --git a/crypto/src/math/ec/SimpleLookupTable.cs b/crypto/src/math/ec/SimpleLookupTable.cs
new file mode 100644
index 000000000..f1e32f215
--- /dev/null
+++ b/crypto/src/math/ec/SimpleLookupTable.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace Org.BouncyCastle.Math.EC
+{
+    public class SimpleLookupTable
+        : ECLookupTable
+    {
+        private static ECPoint[] Copy(ECPoint[] points, int off, int len)
+        {
+            ECPoint[] result = new ECPoint[len];
+            for (int i = 0; i < len; ++i)
+            {
+                result[i] = points[off + i];
+            }
+            return result;
+        }
+
+        private readonly ECPoint[] points;
+
+        public SimpleLookupTable(ECPoint[] points, int off, int len)
+        {
+            this.points = Copy(points, off, len);
+        }
+
+        public virtual int Size
+        {
+            get { return points.Length; }
+        }
+
+        public virtual ECPoint Lookup(int index)
+        {
+            return points[index];
+        }
+    }
+}
diff --git a/crypto/src/math/ec/custom/djb/Curve25519.cs b/crypto/src/math/ec/custom/djb/Curve25519.cs
index 6ed7c0648..c0f911a9c 100644
--- a/crypto/src/math/ec/custom/djb/Curve25519.cs
+++ b/crypto/src/math/ec/custom/djb/Curve25519.cs
@@ -11,6 +11,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb
         public static readonly BigInteger q = Nat256.ToBigInteger(Curve25519Field.P);
 
         private const int Curve25519_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
+        private const int CURVE25519_FE_INTS = 8;
 
         protected readonly Curve25519Point m_infinity;
 
@@ -73,5 +74,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb
         {
             return new Curve25519Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * CURVE25519_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy(((Curve25519FieldElement)p.RawXCoord).x, 0, table, pos); pos += CURVE25519_FE_INTS;
+                    Nat256.Copy(((Curve25519FieldElement)p.RawYCoord).x, 0, table, pos); pos += CURVE25519_FE_INTS;
+                }
+            }
+
+            return new Curve25519LookupTable(this, table, len);
+        }
+
+        private class Curve25519LookupTable
+            : ECLookupTable
+        {
+            private readonly Curve25519 m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal Curve25519LookupTable(Curve25519 outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat256.Create(), y = Nat256.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < CURVE25519_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + CURVE25519_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (CURVE25519_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new Curve25519FieldElement(x), new Curve25519FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/gm/SM2P256V1Curve.cs b/crypto/src/math/ec/custom/gm/SM2P256V1Curve.cs
index 70b1190c9..2c5d8f3a3 100644
--- a/crypto/src/math/ec/custom/gm/SM2P256V1Curve.cs
+++ b/crypto/src/math/ec/custom/gm/SM2P256V1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.GM
@@ -11,6 +12,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.GM
             Hex.Decode("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF"));
 
         private const int SM2P256V1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SM2P256V1_FE_INTS = 8;
 
         protected readonly SM2P256V1Point m_infinity;
 
@@ -73,5 +75,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.GM
         {
             return new SM2P256V1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SM2P256V1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy(((SM2P256V1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SM2P256V1_FE_INTS;
+                    Nat256.Copy(((SM2P256V1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SM2P256V1_FE_INTS;
+                }
+            }
+
+            return new SM2P256V1LookupTable(this, table, len);
+        }
+
+        private class SM2P256V1LookupTable
+            : ECLookupTable
+        {
+            private readonly SM2P256V1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SM2P256V1LookupTable(SM2P256V1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat256.Create(), y = Nat256.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SM2P256V1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SM2P256V1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SM2P256V1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SM2P256V1FieldElement(x), new SM2P256V1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP128R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP128R1Curve.cs
index 9da27b470..cc50f50d2 100644
--- a/crypto/src/math/ec/custom/sec/SecP128R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP128R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,7 +11,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"));
 
-        private const int SecP128R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP128R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP128R1_FE_INTS = 4;
 
         protected readonly SecP128R1Point m_infinity;
 
@@ -26,7 +28,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFE0000000075A30D1B9038A115"));
             this.m_cofactor = BigInteger.One;
 
-            this.m_coord = SecP128R1_DEFAULT_COORDS;
+            this.m_coord = SECP128R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -74,5 +76,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP128R1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP128R1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat128.Copy(((SecP128R1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP128R1_FE_INTS;
+                    Nat128.Copy(((SecP128R1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP128R1_FE_INTS;
+                }
+            }
+
+            return new SecP128R1LookupTable(this, table, len);
+        }
+
+        private class SecP128R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP128R1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP128R1LookupTable(SecP128R1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat128.Create(), y = Nat128.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP128R1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP128R1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP128R1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP128R1FieldElement(x), new SecP128R1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP160K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP160K1Curve.cs
index 7d45c6227..234c86b87 100644
--- a/crypto/src/math/ec/custom/sec/SecP160K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP160K1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,6 +11,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = SecP160R2Curve.q;
 
         private const int SECP160K1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP160K1_FE_INTS = 5;
 
         protected readonly SecP160K1Point m_infinity;
 
@@ -70,5 +72,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP160K1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP160K1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat160.Copy(((SecP160R2FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP160K1_FE_INTS;
+                    Nat160.Copy(((SecP160R2FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP160K1_FE_INTS;
+                }
+            }
+
+            return new SecP160K1LookupTable(this, table, len);
+        }
+
+        private class SecP160K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP160K1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP160K1LookupTable(SecP160K1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat256.Create(), y = Nat256.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP160K1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP160K1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP160K1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP160R2FieldElement(x), new SecP160R2FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP160R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP160R1Curve.cs
index 87389af36..958eb2765 100644
--- a/crypto/src/math/ec/custom/sec/SecP160R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP160R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,7 +11,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"));
 
-        private const int SecP160R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP160R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP160R1_FE_INTS = 5;
 
         protected readonly SecP160R1Point m_infinity;
 
@@ -26,7 +28,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("0100000000000000000001F4C8F927AED3CA752257"));
             this.m_cofactor = BigInteger.One;
 
-            this.m_coord = SecP160R1_DEFAULT_COORDS;
+            this.m_coord = SECP160R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -74,5 +76,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP160R1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP160R1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat160.Copy(((SecP160R1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP160R1_FE_INTS;
+                    Nat160.Copy(((SecP160R1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP160R1_FE_INTS;
+                }
+            }
+
+            return new SecP160R1LookupTable(this, table, len);
+        }
+
+        private class SecP160R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP160R1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP160R1LookupTable(SecP160R1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat160.Create(), y = Nat160.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP160R1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP160R1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP160R1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP160R1FieldElement(x), new SecP160R1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP160R2Curve.cs b/crypto/src/math/ec/custom/sec/SecP160R2Curve.cs
index 100561453..252312e62 100644
--- a/crypto/src/math/ec/custom/sec/SecP160R2Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP160R2Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,7 +11,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"));
 
-        private const int SecP160R2_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP160R2_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP160R2_FE_INTS = 5;
 
         protected readonly SecP160R2Point m_infinity;
 
@@ -26,7 +28,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("0100000000000000000000351EE786A818F3A1A16B"));
             this.m_cofactor = BigInteger.One;
 
-            this.m_coord = SecP160R2_DEFAULT_COORDS;
+            this.m_coord = SECP160R2_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -74,5 +76,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP160R2Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP160R2_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat160.Copy(((SecP160R2FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP160R2_FE_INTS;
+                    Nat160.Copy(((SecP160R2FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP160R2_FE_INTS;
+                }
+            }
+
+            return new SecP160R2LookupTable(this, table, len);
+        }
+
+        private class SecP160R2LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP160R2Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP160R2LookupTable(SecP160R2Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat160.Create(), y = Nat160.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP160R2_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP160R2_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP160R2_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP160R2FieldElement(x), new SecP160R2FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
index 81f77197e..518e0a131 100644
--- a/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -11,6 +12,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"));
 
         private const int SECP192K1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP192K1_FE_INTS = 6;
 
         protected readonly SecP192K1Point m_infinity;
 
@@ -71,5 +73,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP192K1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP192K1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat192.Copy(((SecP192K1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP192K1_FE_INTS;
+                    Nat192.Copy(((SecP192K1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP192K1_FE_INTS;
+                }
+            }
+
+            return new SecP192K1LookupTable(this, table, len);
+        }
+
+        private class SecP192K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP192K1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP192K1LookupTable(SecP192K1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat192.Create(), y = Nat192.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP192K1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP192K1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP192K1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP192K1FieldElement(x), new SecP192K1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
index cb3a981c8..91d31932a 100644
--- a/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,7 +11,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"));
 
-        private const int SecP192R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP192R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP192R1_FE_INTS = 6;
 
         protected readonly SecP192R1Point m_infinity;
 
@@ -26,7 +28,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"));
             this.m_cofactor = BigInteger.One;
 
-            this.m_coord = SecP192R1_DEFAULT_COORDS;
+            this.m_coord = SECP192R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -74,5 +76,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP192R1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP192R1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat192.Copy(((SecP192R1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP192R1_FE_INTS;
+                    Nat192.Copy(((SecP192R1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP192R1_FE_INTS;
+                }
+            }
+
+            return new SecP192R1LookupTable(this, table, len);
+        }
+
+        private class SecP192R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP192R1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP192R1LookupTable(SecP192R1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat192.Create(), y = Nat192.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP192R1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP192R1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP192R1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP192R1FieldElement(x), new SecP192R1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
index d4be7d8de..a9c55090f 100644
--- a/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -11,6 +12,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D"));
 
         private const int SECP224K1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP224K1_FE_INTS = 7;
 
         protected readonly SecP224K1Point m_infinity;
 
@@ -71,5 +73,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP224K1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP224K1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat224.Copy(((SecP224K1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP224K1_FE_INTS;
+                    Nat224.Copy(((SecP224K1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP224K1_FE_INTS;
+                }
+            }
+
+            return new SecP224K1LookupTable(this, table, len);
+        }
+
+        private class SecP224K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP224K1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP224K1LookupTable(SecP224K1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat224.Create(), y = Nat224.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP224K1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP224K1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP224K1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP224K1FieldElement(x), new SecP224K1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
index cda8781ff..ec34390aa 100644
--- a/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,7 +11,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"));
 
-        private const int SecP224R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP224R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP224R1_FE_INTS = 7;
 
         protected readonly SecP224R1Point m_infinity;
 
@@ -26,7 +28,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"));
             this.m_cofactor = BigInteger.One;
 
-            this.m_coord = SecP224R1_DEFAULT_COORDS;
+            this.m_coord = SECP224R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -74,5 +76,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP224R1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP224R1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat224.Copy(((SecP224R1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP224R1_FE_INTS;
+                    Nat224.Copy(((SecP224R1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP224R1_FE_INTS;
+                }
+            }
+
+            return new SecP224R1LookupTable(this, table, len);
+        }
+
+        private class SecP224R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP224R1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP224R1LookupTable(SecP224R1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat224.Create(), y = Nat224.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP224R1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP224R1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP224R1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP224R1FieldElement(x), new SecP224R1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
index 59e2cefb2..b3a5dd646 100644
--- a/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -11,6 +12,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"));
 
         private const int SECP256K1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP256K1_FE_INTS = 8;
 
         protected readonly SecP256K1Point m_infinity;
 
@@ -71,5 +73,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP256K1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP256K1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy(((SecP256K1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP256K1_FE_INTS;
+                    Nat256.Copy(((SecP256K1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP256K1_FE_INTS;
+                }
+            }
+
+            return new SecP256K1LookupTable(this, table, len);
+        }
+
+        private class SecP256K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP256K1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP256K1LookupTable(SecP256K1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat256.Create(), y = Nat256.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP256K1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP256K1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP256K1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP256K1FieldElement(x), new SecP256K1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
index 6b3448f06..2d9a88b72 100644
--- a/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,7 +11,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"));
 
-        private const int SecP256R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP256R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP256R1_FE_INTS = 8;
 
         protected readonly SecP256R1Point m_infinity;
 
@@ -25,7 +27,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
                 Hex.Decode("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B")));
             this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"));
             this.m_cofactor = BigInteger.One;
-            this.m_coord = SecP256R1_DEFAULT_COORDS;
+            this.m_coord = SECP256R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -73,5 +75,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP256R1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP256R1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy(((SecP256R1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP256R1_FE_INTS;
+                    Nat256.Copy(((SecP256R1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP256R1_FE_INTS;
+                }
+            }
+
+            return new SecP256R1LookupTable(this, table, len);
+        }
+
+        private class SecP256R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP256R1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP256R1LookupTable(SecP256R1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat256.Create(), y = Nat256.Create();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP256R1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP256R1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP256R1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP256R1FieldElement(x), new SecP256R1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
index 7fd58276a..26b057198 100644
--- a/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,7 +11,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF"));
 
-        private const int SecP384R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP384R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP384R1_FE_INTS = 12;
 
         protected readonly SecP384R1Point m_infinity;
 
@@ -25,7 +27,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
                 Hex.Decode("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF")));
             this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973"));
             this.m_cofactor = BigInteger.One;
-            this.m_coord = SecP384R1_DEFAULT_COORDS;
+            this.m_coord = SECP384R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -73,5 +75,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP384R1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP384R1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat.Copy(SECP384R1_FE_INTS, ((SecP384R1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP384R1_FE_INTS;
+                    Nat.Copy(SECP384R1_FE_INTS, ((SecP384R1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP384R1_FE_INTS;
+                }
+            }
+
+            return new SecP384R1LookupTable(this, table, len);
+        }
+
+        private class SecP384R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP384R1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP384R1LookupTable(SecP384R1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat.Create(SECP384R1_FE_INTS), y = Nat.Create(SECP384R1_FE_INTS);
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP384R1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP384R1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP384R1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP384R1FieldElement(x), new SecP384R1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
index e5083c7f0..810be85b5 100644
--- a/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -10,7 +11,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         public static readonly BigInteger q = new BigInteger(1,
             Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));
 
-        private const int SecP521R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP521R1_DEFAULT_COORDS = COORD_JACOBIAN;
+        private const int SECP521R1_FE_INTS = 17;
 
         protected readonly SecP521R1Point m_infinity;
 
@@ -25,7 +27,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
                 Hex.Decode("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00")));
             this.m_order = new BigInteger(1, Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409"));
             this.m_cofactor = BigInteger.One;
-            this.m_coord = SecP521R1_DEFAULT_COORDS;
+            this.m_coord = SECP521R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -73,5 +75,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             return new SecP521R1Point(this, x, y, zs, withCompression);
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            uint[] table = new uint[len * SECP521R1_FE_INTS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat.Copy(SECP521R1_FE_INTS, ((SecP521R1FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECP521R1_FE_INTS;
+                    Nat.Copy(SECP521R1_FE_INTS, ((SecP521R1FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECP521R1_FE_INTS;
+                }
+            }
+
+            return new SecP521R1LookupTable(this, table, len);
+        }
+
+        private class SecP521R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecP521R1Curve m_outer;
+            private readonly uint[] m_table;
+            private readonly int m_size;
+
+            internal SecP521R1LookupTable(SecP521R1Curve outer, uint[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                uint[] x = Nat.Create(SECP521R1_FE_INTS), y = Nat.Create(SECP521R1_FE_INTS);
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    uint MASK = (uint)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECP521R1_FE_INTS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECP521R1_FE_INTS + j] & MASK;
+                    }
+
+                    pos += (SECP521R1_FE_INTS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecP521R1FieldElement(x), new SecP521R1FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT113R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT113R1Curve.cs
index 2705c94aa..e85f68e60 100644
--- a/crypto/src/math/ec/custom/sec/SecT113R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT113R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT113R1Curve
         : AbstractF2mCurve
     {
-        private const int SecT113R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT113R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT113R1_FE_LONGS = 2;
 
         protected readonly SecT113R1Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("0100000000000000D9CCEC8A39E56F"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT113R1_DEFAULT_COORDS;
+            this.m_coord = SECT113R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT113R1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat128.Copy64(((SecT113FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT113R1_FE_LONGS;
+                    Nat128.Copy64(((SecT113FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT113R1_FE_LONGS;
+                }
+            }
+
+            return new SecT113R1LookupTable(this, table, len);
+        }
+
+        private class SecT113R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT113R1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT113R1LookupTable(SecT113R1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat128.Create64(), y = Nat128.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT113R1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT113R1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT113R1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT113FieldElement(x), new SecT113FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT113R2Curve.cs b/crypto/src/math/ec/custom/sec/SecT113R2Curve.cs
index abfd26d5b..efe422806 100644
--- a/crypto/src/math/ec/custom/sec/SecT113R2Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT113R2Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT113R2Curve
         : AbstractF2mCurve
     {
-        private const int SecT113R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT113R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT113R2_FE_LONGS = 2;
 
         protected readonly SecT113R2Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("010000000000000108789B2496AF93"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT113R2_DEFAULT_COORDS;
+            this.m_coord = SECT113R2_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT113R2_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat128.Copy64(((SecT113FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT113R2_FE_LONGS;
+                    Nat128.Copy64(((SecT113FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT113R2_FE_LONGS;
+                }
+            }
+
+            return new SecT113R2LookupTable(this, table, len);
+        }
+
+        private class SecT113R2LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT113R2Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT113R2LookupTable(SecT113R2Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat128.Create64(), y = Nat128.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT113R2_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT113R2_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT113R2_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT113FieldElement(x), new SecT113FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT131FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT131FieldElement.cs
index e0ecc100f..c38e8eb0a 100644
--- a/crypto/src/math/ec/custom/sec/SecT131FieldElement.cs
+++ b/crypto/src/math/ec/custom/sec/SecT131FieldElement.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT131FieldElement
         : ECFieldElement
     {
-        protected readonly ulong[] x;
+        protected internal readonly ulong[] x;
 
         public SecT131FieldElement(BigInteger x)
         {
diff --git a/crypto/src/math/ec/custom/sec/SecT131R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT131R1Curve.cs
index b73964c39..06f0a79ae 100644
--- a/crypto/src/math/ec/custom/sec/SecT131R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT131R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT131R1Curve
         : AbstractF2mCurve
     {
-        private const int SecT131R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT131R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT131R1_FE_LONGS = 3;
 
         protected readonly SecT131R1Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("0400000000000000023123953A9464B54D"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT131R1_DEFAULT_COORDS;
+            this.m_coord = SECT131R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 8; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT131R1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat192.Copy64(((SecT131FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT131R1_FE_LONGS;
+                    Nat192.Copy64(((SecT131FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT131R1_FE_LONGS;
+                }
+            }
+
+            return new SecT131R1LookupTable(this, table, len);
+        }
+
+        private class SecT131R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT131R1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT131R1LookupTable(SecT131R1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat192.Create64(), y = Nat192.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT131R1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT131R1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT131R1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT131FieldElement(x), new SecT131FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT131R2Curve.cs b/crypto/src/math/ec/custom/sec/SecT131R2Curve.cs
index 724921c94..0120b3059 100644
--- a/crypto/src/math/ec/custom/sec/SecT131R2Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT131R2Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT131R2Curve
         : AbstractF2mCurve
     {
-        private const int SecT131R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT131R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT131R2_FE_LONGS = 3;
 
         protected readonly SecT131R2Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("0400000000000000016954A233049BA98F"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT131R2_DEFAULT_COORDS;
+            this.m_coord = SECT131R2_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 8; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT131R2_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat192.Copy64(((SecT131FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT131R2_FE_LONGS;
+                    Nat192.Copy64(((SecT131FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT131R2_FE_LONGS;
+                }
+            }
+
+            return new SecT131R2LookupTable(this, table, len);
+        }
+
+        private class SecT131R2LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT131R2Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT131R2LookupTable(SecT131R2Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat192.Create64(), y = Nat192.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT131R2_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT131R2_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT131R2_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT131FieldElement(x), new SecT131FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT163FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT163FieldElement.cs
index 8953fb529..07bd07652 100644
--- a/crypto/src/math/ec/custom/sec/SecT163FieldElement.cs
+++ b/crypto/src/math/ec/custom/sec/SecT163FieldElement.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT163FieldElement
         : ECFieldElement
     {
-        protected readonly ulong[] x;
+        protected internal readonly ulong[] x;
 
         public SecT163FieldElement(BigInteger x)
         {
diff --git a/crypto/src/math/ec/custom/sec/SecT163K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT163K1Curve.cs
index 68ff646ca..5e1431f46 100644
--- a/crypto/src/math/ec/custom/sec/SecT163K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT163K1Curve.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -8,7 +9,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT163K1Curve
         : AbstractF2mCurve
     {
-        private const int SecT163K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT163K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT163K1_FE_LONGS = 3;
 
         protected readonly SecT163K1Point m_infinity;
 
@@ -22,7 +24,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("04000000000000000000020108A2E0CC0D99F8A5EF"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT163K1_DEFAULT_COORDS;
+            this.m_coord = SECT163K1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -100,5 +102,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 7; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT163K1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat192.Copy64(((SecT163FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT163K1_FE_LONGS;
+                    Nat192.Copy64(((SecT163FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT163K1_FE_LONGS;
+                }
+            }
+
+            return new SecT163K1LookupTable(this, table, len);
+        }
+
+        private class SecT163K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT163K1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT163K1LookupTable(SecT163K1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat192.Create64(), y = Nat192.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT163K1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT163K1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT163K1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT163FieldElement(x), new SecT163FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT163R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT163R1Curve.cs
index 8ae58ccef..e212ad4ea 100644
--- a/crypto/src/math/ec/custom/sec/SecT163R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT163R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT163R1Curve
         : AbstractF2mCurve
     {
-        private const int SecT163R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT163R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT163R1_FE_LONGS = 3;
 
         protected readonly SecT163R1Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT163R1_DEFAULT_COORDS;
+            this.m_coord = SECT163R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 7; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT163R1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat192.Copy64(((SecT163FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT163R1_FE_LONGS;
+                    Nat192.Copy64(((SecT163FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT163R1_FE_LONGS;
+                }
+            }
+
+            return new SecT163R1LookupTable(this, table, len);
+        }
+
+        private class SecT163R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT163R1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT163R1LookupTable(SecT163R1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat192.Create64(), y = Nat192.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT163R1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT163R1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT163R1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT163FieldElement(x), new SecT163FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT163R2Curve.cs b/crypto/src/math/ec/custom/sec/SecT163R2Curve.cs
index 5a4fa5ad1..b0365388a 100644
--- a/crypto/src/math/ec/custom/sec/SecT163R2Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT163R2Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT163R2Curve
         : AbstractF2mCurve
     {
-        private const int SecT163R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT163R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT163R2_FE_LONGS = 3;
 
         protected readonly SecT163R2Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("040000000000000000000292FE77E70C12A4234C33"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT163R2_DEFAULT_COORDS;
+            this.m_coord = SECT163R2_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 7; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT163R2_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat192.Copy64(((SecT163FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT163R2_FE_LONGS;
+                    Nat192.Copy64(((SecT163FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT163R2_FE_LONGS;
+                }
+            }
+
+            return new SecT163R2LookupTable(this, table, len);
+        }
+
+        private class SecT163R2LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT163R2Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT163R2LookupTable(SecT163R2Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat192.Create64(), y = Nat192.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT163R2_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT163R2_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT163R2_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT163FieldElement(x), new SecT163FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT193FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT193FieldElement.cs
index a1150b3f9..d04e68d3f 100644
--- a/crypto/src/math/ec/custom/sec/SecT193FieldElement.cs
+++ b/crypto/src/math/ec/custom/sec/SecT193FieldElement.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT193FieldElement
         : ECFieldElement
     {
-        protected readonly ulong[] x;
+        protected internal readonly ulong[] x;
 
         public SecT193FieldElement(BigInteger x)
         {
diff --git a/crypto/src/math/ec/custom/sec/SecT193R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT193R1Curve.cs
index a2cb5a8ac..e6cb3b4d8 100644
--- a/crypto/src/math/ec/custom/sec/SecT193R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT193R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT193R1Curve
         : AbstractF2mCurve
     {
-        private const int SecT193R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT193R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT193R1_FE_LONGS = 4;
 
         protected readonly SecT193R1Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("01000000000000000000000000C7F34A778F443ACC920EBA49"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT193R1_DEFAULT_COORDS;
+            this.m_coord = SECT193R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT193R1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy64(((SecT193FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT193R1_FE_LONGS;
+                    Nat256.Copy64(((SecT193FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT193R1_FE_LONGS;
+                }
+            }
+
+            return new SecT193R1LookupTable(this, table, len);
+        }
+
+        private class SecT193R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT193R1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT193R1LookupTable(SecT193R1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat256.Create64(), y = Nat256.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT193R1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT193R1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT193R1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT193FieldElement(x), new SecT193FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT193R2Curve.cs b/crypto/src/math/ec/custom/sec/SecT193R2Curve.cs
index 1c84a3eac..cfd690c65 100644
--- a/crypto/src/math/ec/custom/sec/SecT193R2Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT193R2Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT193R2Curve
         : AbstractF2mCurve
     {
-        private const int SecT193R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT193R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT193R2_FE_LONGS = 4;
 
         protected readonly SecT193R2Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("010000000000000000000000015AAB561B005413CCD4EE99D5"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT193R2_DEFAULT_COORDS;
+            this.m_coord = SECT193R2_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT193R2_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy64(((SecT193FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT193R2_FE_LONGS;
+                    Nat256.Copy64(((SecT193FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT193R2_FE_LONGS;
+                }
+            }
+
+            return new SecT193R2LookupTable(this, table, len);
+        }
+
+        private class SecT193R2LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT193R2Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT193R2LookupTable(SecT193R2Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat256.Create64(), y = Nat256.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT193R2_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT193R2_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT193R2_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT193FieldElement(x), new SecT193FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT233FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT233FieldElement.cs
index 91b8e2f1c..64d09bd6d 100644
--- a/crypto/src/math/ec/custom/sec/SecT233FieldElement.cs
+++ b/crypto/src/math/ec/custom/sec/SecT233FieldElement.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT233FieldElement
         : ECFieldElement
     {
-        protected readonly ulong[] x;
+        protected internal readonly ulong[] x;
 
         public SecT233FieldElement(BigInteger x)
         {
diff --git a/crypto/src/math/ec/custom/sec/SecT233K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT233K1Curve.cs
index 72935913d..07eae1564 100644
--- a/crypto/src/math/ec/custom/sec/SecT233K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT233K1Curve.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -8,7 +9,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT233K1Curve
         : AbstractF2mCurve
     {
-        private const int SecT233K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT233K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT233K1_FE_LONGS = 4;
 
         protected readonly SecT233K1Point m_infinity;
 
@@ -22,7 +24,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF"));
             this.m_cofactor = BigInteger.ValueOf(4);
 
-            this.m_coord = SecT233K1_DEFAULT_COORDS;
+            this.m_coord = SECT233K1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -100,5 +102,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT233K1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy64(((SecT233FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT233K1_FE_LONGS;
+                    Nat256.Copy64(((SecT233FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT233K1_FE_LONGS;
+                }
+            }
+
+            return new SecT233K1LookupTable(this, table, len);
+        }
+
+        private class SecT233K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT233K1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT233K1LookupTable(SecT233K1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat256.Create64(), y = Nat256.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT233K1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT233K1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT233K1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT233FieldElement(x), new SecT233FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT233R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT233R1Curve.cs
index db6e6e1d4..5e8dee875 100644
--- a/crypto/src/math/ec/custom/sec/SecT233R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT233R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT233R1Curve
         : AbstractF2mCurve
     {
-        private const int SecT233R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT233R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT233R1_FE_LONGS = 4;
 
         protected readonly SecT233R1Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT233R1_DEFAULT_COORDS;
+            this.m_coord = SECT233R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT233R1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy64(((SecT233FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT233R1_FE_LONGS;
+                    Nat256.Copy64(((SecT233FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT233R1_FE_LONGS;
+                }
+            }
+
+            return new SecT233R1LookupTable(this, table, len);
+        }
+
+        private class SecT233R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT233R1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT233R1LookupTable(SecT233R1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat256.Create64(), y = Nat256.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT233R1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT233R1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT233R1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT233FieldElement(x), new SecT233FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT239FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT239FieldElement.cs
index a32ffc5d2..18563f746 100644
--- a/crypto/src/math/ec/custom/sec/SecT239FieldElement.cs
+++ b/crypto/src/math/ec/custom/sec/SecT239FieldElement.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT239FieldElement
         : ECFieldElement
     {
-        protected ulong[] x;
+        protected internal readonly ulong[] x;
 
         public SecT239FieldElement(BigInteger x)
         {
diff --git a/crypto/src/math/ec/custom/sec/SecT239K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT239K1Curve.cs
index a499d48b4..33792e631 100644
--- a/crypto/src/math/ec/custom/sec/SecT239K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT239K1Curve.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -8,7 +9,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT239K1Curve
         : AbstractF2mCurve
     {
-        private const int SecT239K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT239K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT239K1_FE_LONGS = 4;
 
         protected readonly SecT239K1Point m_infinity;
 
@@ -22,7 +24,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5"));
             this.m_cofactor = BigInteger.ValueOf(4);
 
-            this.m_coord = SecT239K1_DEFAULT_COORDS;
+            this.m_coord = SECT239K1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -100,5 +102,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT239K1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat256.Copy64(((SecT239FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT239K1_FE_LONGS;
+                    Nat256.Copy64(((SecT239FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT239K1_FE_LONGS;
+                }
+            }
+
+            return new SecT239K1LookupTable(this, table, len);
+        }
+
+        private class SecT239K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT239K1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT239K1LookupTable(SecT239K1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat256.Create64(), y = Nat256.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT239K1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT239K1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT239K1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT239FieldElement(x), new SecT239FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT283FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT283FieldElement.cs
index adfd4e0ed..b054bedfb 100644
--- a/crypto/src/math/ec/custom/sec/SecT283FieldElement.cs
+++ b/crypto/src/math/ec/custom/sec/SecT283FieldElement.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT283FieldElement
         : ECFieldElement
     {
-        protected readonly ulong[] x;
+        protected internal readonly ulong[] x;
 
         public SecT283FieldElement(BigInteger x)
         {
diff --git a/crypto/src/math/ec/custom/sec/SecT283K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT283K1Curve.cs
index 4053287ec..51725bc20 100644
--- a/crypto/src/math/ec/custom/sec/SecT283K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT283K1Curve.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -8,7 +9,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT283K1Curve
         : AbstractF2mCurve
     {
-        private const int SecT283K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT283K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT283K1_FE_LONGS = 5;
 
         protected readonly SecT283K1Point m_infinity;
 
@@ -22,7 +24,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61"));
             this.m_cofactor = BigInteger.ValueOf(4);
 
-            this.m_coord = SecT283K1_DEFAULT_COORDS;
+            this.m_coord = SECT283K1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -100,5 +102,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 12; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT283K1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat320.Copy64(((SecT283FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT283K1_FE_LONGS;
+                    Nat320.Copy64(((SecT283FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT283K1_FE_LONGS;
+                }
+            }
+
+            return new SecT283K1LookupTable(this, table, len);
+        }
+
+        private class SecT283K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT283K1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT283K1LookupTable(SecT283K1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat320.Create64(), y = Nat320.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT283K1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT283K1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT283K1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT283FieldElement(x), new SecT283FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT283R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT283R1Curve.cs
index e659675ce..567df7686 100644
--- a/crypto/src/math/ec/custom/sec/SecT283R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT283R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT283R1Curve
         : AbstractF2mCurve
     {
-        private const int SecT283R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT283R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT283R1_FE_LONGS = 5;
 
         protected readonly SecT283R1Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT283R1_DEFAULT_COORDS;
+            this.m_coord = SECT283R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 12; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT283R1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat320.Copy64(((SecT283FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT283R1_FE_LONGS;
+                    Nat320.Copy64(((SecT283FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT283R1_FE_LONGS;
+                }
+            }
+
+            return new SecT283R1LookupTable(this, table, len);
+        }
+
+        private class SecT283R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT283R1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT283R1LookupTable(SecT283R1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat320.Create64(), y = Nat320.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT283R1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT283R1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT283R1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT283FieldElement(x), new SecT283FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT409FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT409FieldElement.cs
index f954f46e7..7076905bb 100644
--- a/crypto/src/math/ec/custom/sec/SecT409FieldElement.cs
+++ b/crypto/src/math/ec/custom/sec/SecT409FieldElement.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT409FieldElement
         : ECFieldElement
     {
-        protected ulong[] x;
+        protected internal readonly ulong[] x;
 
         public SecT409FieldElement(BigInteger x)
         {
diff --git a/crypto/src/math/ec/custom/sec/SecT409K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT409K1Curve.cs
index 4f573553e..839ec8059 100644
--- a/crypto/src/math/ec/custom/sec/SecT409K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT409K1Curve.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -8,7 +9,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT409K1Curve
         : AbstractF2mCurve
     {
-        private const int SecT409K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT409K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT409K1_FE_LONGS = 7;
 
         protected readonly SecT409K1Point m_infinity;
 
@@ -22,7 +24,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF"));
             this.m_cofactor = BigInteger.ValueOf(4);
 
-            this.m_coord = SecT409K1_DEFAULT_COORDS;
+            this.m_coord = SECT409K1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -100,5 +102,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT409K1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat448.Copy64(((SecT409FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT409K1_FE_LONGS;
+                    Nat448.Copy64(((SecT409FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT409K1_FE_LONGS;
+                }
+            }
+
+            return new SecT409K1LookupTable(this, table, len);
+        }
+
+        private class SecT409K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT409K1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT409K1LookupTable(SecT409K1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat448.Create64(), y = Nat448.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT409K1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT409K1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT409K1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT409FieldElement(x), new SecT409FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT409R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT409R1Curve.cs
index 9212fb5d2..f70dd5f8e 100644
--- a/crypto/src/math/ec/custom/sec/SecT409R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT409R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT409R1Curve
         : AbstractF2mCurve
     {
-        private const int SecT409R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT409R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT409R1_FE_LONGS = 7;
 
         protected readonly SecT409R1Point m_infinity;
 
@@ -21,7 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT409R1_DEFAULT_COORDS;
+            this.m_coord = SECT409R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -94,5 +96,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 0; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT409R1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat448.Copy64(((SecT409FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT409R1_FE_LONGS;
+                    Nat448.Copy64(((SecT409FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT409R1_FE_LONGS;
+                }
+            }
+
+            return new SecT409R1LookupTable(this, table, len);
+        }
+
+        private class SecT409R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT409R1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT409R1LookupTable(SecT409R1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat448.Create64(), y = Nat448.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT409R1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT409R1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT409R1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT409FieldElement(x), new SecT409FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT571FieldElement.cs b/crypto/src/math/ec/custom/sec/SecT571FieldElement.cs
index c43b8dc3a..5f28c01be 100644
--- a/crypto/src/math/ec/custom/sec/SecT571FieldElement.cs
+++ b/crypto/src/math/ec/custom/sec/SecT571FieldElement.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT571FieldElement
         : ECFieldElement
     {
-        protected readonly ulong[] x;
+        protected internal readonly ulong[] x;
 
         public SecT571FieldElement(BigInteger x)
         {
diff --git a/crypto/src/math/ec/custom/sec/SecT571K1Curve.cs b/crypto/src/math/ec/custom/sec/SecT571K1Curve.cs
index f5806f09c..3d84797f7 100644
--- a/crypto/src/math/ec/custom/sec/SecT571K1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT571K1Curve.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -8,7 +9,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT571K1Curve
         : AbstractF2mCurve
     {
-        private const int SecT571K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT571K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT571K1_FE_LONGS = 9;
 
         protected readonly SecT571K1Point m_infinity;
 
@@ -22,7 +24,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001"));
             this.m_cofactor = BigInteger.ValueOf(4);
 
-            this.m_coord = SecT571K1_DEFAULT_COORDS;
+            this.m_coord = SECT571K1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -100,5 +102,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 10; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT571K1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat576.Copy64(((SecT571FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT571K1_FE_LONGS;
+                    Nat576.Copy64(((SecT571FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT571K1_FE_LONGS;
+                }
+            }
+
+            return new SecT571K1LookupTable(this, table, len);
+        }
+
+        private class SecT571K1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT571K1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT571K1LookupTable(SecT571K1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat576.Create64(), y = Nat576.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT571K1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT571K1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT571K1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT571FieldElement(x), new SecT571FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/custom/sec/SecT571R1Curve.cs b/crypto/src/math/ec/custom/sec/SecT571R1Curve.cs
index 082afa5bd..7ebf90856 100644
--- a/crypto/src/math/ec/custom/sec/SecT571R1Curve.cs
+++ b/crypto/src/math/ec/custom/sec/SecT571R1Curve.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Math.EC.Custom.Sec
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
     internal class SecT571R1Curve
         : AbstractF2mCurve
     {
-        private const int SecT571R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT571R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+        private const int SECT571R1_FE_LONGS = 9;
 
         protected readonly SecT571R1Point m_infinity;
 
@@ -25,7 +27,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
             this.m_order = new BigInteger(1, Hex.Decode("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47"));
             this.m_cofactor = BigInteger.Two;
 
-            this.m_coord = SecT571R1_DEFAULT_COORDS;
+            this.m_coord = SECT571R1_DEFAULT_COORDS;
         }
 
         protected override ECCurve CloneCurve()
@@ -98,5 +100,62 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
         {
             get { return 10; }
         }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            ulong[] table = new ulong[len * SECT571R1_FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    Nat576.Copy64(((SecT571FieldElement)p.RawXCoord).x, 0, table, pos); pos += SECT571R1_FE_LONGS;
+                    Nat576.Copy64(((SecT571FieldElement)p.RawYCoord).x, 0, table, pos); pos += SECT571R1_FE_LONGS;
+                }
+            }
+
+            return new SecT571R1LookupTable(this, table, len);
+        }
+
+        private class SecT571R1LookupTable
+            : ECLookupTable
+        {
+            private readonly SecT571R1Curve m_outer;
+            private readonly ulong[] m_table;
+            private readonly int m_size;
+
+            internal SecT571R1LookupTable(SecT571R1Curve outer, ulong[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                ulong[] x = Nat576.Create64(), y = Nat576.Create64();
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < SECT571R1_FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + SECT571R1_FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (SECT571R1_FE_LONGS * 2);
+                }
+
+                return m_outer.CreateRawPoint(new SecT571FieldElement(x), new SecT571FieldElement(y), false);
+            }
+        }
     }
 }
diff --git a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
index 05bb4000b..adaedb809 100644
--- a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
+++ b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Math.Raw;
+
 namespace Org.BouncyCastle.Math.EC.Multiplier
 {
     public class FixedPointCombMultiplier
@@ -21,36 +23,37 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
                 throw new InvalidOperationException("fixed-point comb doesn't support scalars larger than the curve order");
             }
 
-            int minWidth = GetWidthForCombSize(size);
-
-            FixedPointPreCompInfo info = FixedPointUtilities.Precompute(p, minWidth);
-            ECPoint[] lookupTable = info.PreComp;
+            FixedPointPreCompInfo info = FixedPointUtilities.Precompute(p);
+            ECLookupTable lookupTable = info.LookupTable;
             int width = info.Width;
 
             int d = (size + width - 1) / width;
 
             ECPoint R = c.Infinity;
 
-            int top = d * width - 1;
+            int fullComb = d * width;
+            uint[] K = Nat.FromBigInteger(fullComb, k);
+
+            int top = fullComb - 1;
             for (int i = 0; i < d; ++i)
             {
-                int index = 0;
+                int secretIndex = 0;
 
                 for (int j = top - i; j >= 0; j -= d)
                 {
-                    index <<= 1;
-                    if (k.TestBit(j))
-                    {
-                        index |= 1;
-                    }
+                    secretIndex <<= 1;
+                    secretIndex |= (int)Nat.GetBit(K, j);
                 }
 
-                R = R.TwicePlus(lookupTable[index]);
+                ECPoint add = lookupTable.Lookup(secretIndex);
+
+                R = R.TwicePlus(add);
             }
 
             return R.Add(info.Offset);
         }
 
+        [Obsolete("Is no longer used; remove any overrides in subclasses.")]
         protected virtual int GetWidthForCombSize(int combSize)
         {
             return combSize > 257 ? 6 : 5;
diff --git a/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs b/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs
index 11bdadc6f..4c0b404df 100644
--- a/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs
+++ b/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs
@@ -1,4 +1,6 @@
-namespace Org.BouncyCastle.Math.EC.Multiplier
+using System;
+
+namespace Org.BouncyCastle.Math.EC.Multiplier
 {
     /**
      * Class holding precomputation data for fixed-point multiplications.
@@ -12,21 +14,34 @@
          * Array holding the precomputed <code>ECPoint</code>s used for a fixed
          * point multiplication.
          */
+        [Obsolete("Will be removed")]
 		protected ECPoint[] m_preComp = null;
 
         /**
+         * Lookup table for the precomputed <code>ECPoint</code>s used for a fixed point multiplication.
+         */
+        protected ECLookupTable m_lookupTable = null;
+
+        /**
          * The width used for the precomputation. If a larger width precomputation
          * is already available this may be larger than was requested, so calling
          * code should refer to the actual width.
          */
         protected int m_width = -1;
 
+        public virtual ECLookupTable LookupTable
+        {
+            get { return m_lookupTable; }
+            set { this.m_lookupTable = value; }
+        }
+
         public virtual ECPoint Offset
         {
 			get { return m_offset; }
 			set { this.m_offset = value; }
 		}
 
+        [Obsolete("Use 'LookupTable' property instead.")]
         public virtual ECPoint[] PreComp
         {
             get { return m_preComp; }
diff --git a/crypto/src/math/ec/multiplier/FixedPointUtilities.cs b/crypto/src/math/ec/multiplier/FixedPointUtilities.cs
index 8e129a8f3..cc7203314 100644
--- a/crypto/src/math/ec/multiplier/FixedPointUtilities.cs
+++ b/crypto/src/math/ec/multiplier/FixedPointUtilities.cs
@@ -22,9 +22,16 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
             return new FixedPointPreCompInfo();
         }
 
+        [Obsolete("Use 'Precompute(ECPoint)' instead, as minWidth parameter is now ignored")]
         public static FixedPointPreCompInfo Precompute(ECPoint p, int minWidth)
         {
+            return Precompute(p);
+        }
+
+        public static FixedPointPreCompInfo Precompute(ECPoint p)
+        {
             ECCurve c = p.Curve;
+            int minWidth = GetCombSize(c) > 257 ? 6 : 5;
 
             int n = 1 << minWidth;
             FixedPointPreCompInfo info = GetFixedPointPreCompInfo(c.GetPreCompInfo(p, PRECOMP_NAME));
@@ -63,6 +70,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
 
                 c.NormalizeAll(lookupTable);
 
+                info.LookupTable = c.CreateCacheSafeLookupTable(lookupTable, 0, lookupTable.Length);
                 info.Offset = pow2Table[minWidth];
                 info.PreComp = lookupTable;
                 info.Width = minWidth;
diff --git a/crypto/src/math/raw/Nat.cs b/crypto/src/math/raw/Nat.cs
index 1f9ab00ec..cf6516c61 100644
--- a/crypto/src/math/raw/Nat.cs
+++ b/crypto/src/math/raw/Nat.cs
@@ -207,6 +207,11 @@ namespace Org.BouncyCastle.Math.Raw
             return z;
         }
 
+        public static void Copy(int len, uint[] x, int xOff, uint[] z, int zOff)
+        {
+            Array.Copy(x, xOff, z, zOff, len);
+        }
+
         public static uint[] Create(int len)
         {
             return new uint[len];
diff --git a/crypto/src/math/raw/Nat128.cs b/crypto/src/math/raw/Nat128.cs
index 1d3b64d32..27ed5abe4 100644
--- a/crypto/src/math/raw/Nat128.cs
+++ b/crypto/src/math/raw/Nat128.cs
@@ -111,12 +111,26 @@ namespace Org.BouncyCastle.Math.Raw
             z[3] = x[3];
         }
 
+        public static void Copy(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+        }
+
         public static void Copy64(ulong[] x, ulong[] z)
         {
             z[0] = x[0];
             z[1] = x[1];
         }
 
+        public static void Copy64(ulong[] x, int xOff, ulong[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+        }
+
         public static uint[] Create()
         {
             return new uint[4];
diff --git a/crypto/src/math/raw/Nat160.cs b/crypto/src/math/raw/Nat160.cs
index 1fd00e576..57212cae0 100644
--- a/crypto/src/math/raw/Nat160.cs
+++ b/crypto/src/math/raw/Nat160.cs
@@ -129,6 +129,15 @@ namespace Org.BouncyCastle.Math.Raw
             z[4] = x[4];
         }
 
+        public static void Copy(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+            z[zOff + 4] = x[xOff + 4];
+        }
+
         public static uint[] Create()
         {
             return new uint[5];
diff --git a/crypto/src/math/raw/Nat192.cs b/crypto/src/math/raw/Nat192.cs
index 3099bafab..06c75aa54 100644
--- a/crypto/src/math/raw/Nat192.cs
+++ b/crypto/src/math/raw/Nat192.cs
@@ -145,6 +145,16 @@ namespace Org.BouncyCastle.Math.Raw
             z[5] = x[5];
         }
 
+        public static void Copy(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+            z[zOff + 4] = x[xOff + 4];
+            z[zOff + 5] = x[xOff + 5];
+        }
+
         public static void Copy64(ulong[] x, ulong[] z)
         {
             z[0] = x[0];
@@ -152,6 +162,13 @@ namespace Org.BouncyCastle.Math.Raw
             z[2] = x[2];
         }
 
+        public static void Copy64(ulong[] x, int xOff, ulong[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+        }
+
         public static uint[] Create()
         {
             return new uint[6];
diff --git a/crypto/src/math/raw/Nat224.cs b/crypto/src/math/raw/Nat224.cs
index 978caf265..ff1eb6306 100644
--- a/crypto/src/math/raw/Nat224.cs
+++ b/crypto/src/math/raw/Nat224.cs
@@ -216,6 +216,17 @@ namespace Org.BouncyCastle.Math.Raw
             z[6] = x[6];
         }
 
+        public static void Copy(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+            z[zOff + 4] = x[xOff + 4];
+            z[zOff + 5] = x[xOff + 5];
+            z[zOff + 6] = x[xOff + 6];
+        }
+
         public static uint[] Create()
         {
             return new uint[7];
diff --git a/crypto/src/math/raw/Nat256.cs b/crypto/src/math/raw/Nat256.cs
index 09c751a5a..2be03d642 100644
--- a/crypto/src/math/raw/Nat256.cs
+++ b/crypto/src/math/raw/Nat256.cs
@@ -239,6 +239,18 @@ namespace Org.BouncyCastle.Math.Raw
             z[7] = x[7];
         }
 
+        public static void Copy(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+            z[zOff + 4] = x[xOff + 4];
+            z[zOff + 5] = x[xOff + 5];
+            z[zOff + 6] = x[xOff + 6];
+            z[zOff + 7] = x[xOff + 7];
+        }
+
         public static void Copy64(ulong[] x, ulong[] z)
         {
             z[0] = x[0];
@@ -247,6 +259,14 @@ namespace Org.BouncyCastle.Math.Raw
             z[3] = x[3];
         }
 
+        public static void Copy64(ulong[] x, int xOff, ulong[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+        }
+
         public static uint[] Create()
         {
             return new uint[8];
diff --git a/crypto/src/math/raw/Nat320.cs b/crypto/src/math/raw/Nat320.cs
index c7daa71e2..0ad677db4 100644
--- a/crypto/src/math/raw/Nat320.cs
+++ b/crypto/src/math/raw/Nat320.cs
@@ -16,6 +16,15 @@ namespace Org.BouncyCastle.Math.Raw
             z[4] = x[4];
         }
 
+        public static void Copy64(ulong[] x, int xOff, ulong[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+            z[zOff + 4] = x[xOff + 4];
+        }
+
         public static ulong[] Create64()
         {
             return new ulong[5];
diff --git a/crypto/src/math/raw/Nat448.cs b/crypto/src/math/raw/Nat448.cs
index 52a253f1b..b0774b37a 100644
--- a/crypto/src/math/raw/Nat448.cs
+++ b/crypto/src/math/raw/Nat448.cs
@@ -18,6 +18,17 @@ namespace Org.BouncyCastle.Math.Raw
             z[6] = x[6];
         }
 
+        public static void Copy64(ulong[] x, int xOff, ulong[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+            z[zOff + 4] = x[xOff + 4];
+            z[zOff + 5] = x[xOff + 5];
+            z[zOff + 6] = x[xOff + 6];
+        }
+
         public static ulong[] Create64()
         {
             return new ulong[7];
diff --git a/crypto/src/math/raw/Nat576.cs b/crypto/src/math/raw/Nat576.cs
index 813fb86be..14279b61a 100644
--- a/crypto/src/math/raw/Nat576.cs
+++ b/crypto/src/math/raw/Nat576.cs
@@ -20,6 +20,19 @@ namespace Org.BouncyCastle.Math.Raw
             z[8] = x[8];
         }
 
+        public static void Copy64(ulong[] x, int xOff, ulong[] z, int zOff)
+        {
+            z[zOff + 0] = x[xOff + 0];
+            z[zOff + 1] = x[xOff + 1];
+            z[zOff + 2] = x[xOff + 2];
+            z[zOff + 3] = x[xOff + 3];
+            z[zOff + 4] = x[xOff + 4];
+            z[zOff + 5] = x[xOff + 5];
+            z[zOff + 6] = x[xOff + 6];
+            z[zOff + 7] = x[xOff + 7];
+            z[zOff + 8] = x[xOff + 8];
+        }
+
         public static ulong[] Create64()
         {
             return new ulong[9];