summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorOren Novotny <oren@novotny.org>2018-05-19 18:22:31 -0400
committerOren Novotny <oren@novotny.org>2018-05-19 18:22:31 -0400
commite28885d6c3f7d3f713b72669a156034fa42aacd1 (patch)
tree224bcad40723f16f32175c7fde92ee691aa83f92 /crypto
parentremove appveyor file (diff)
parentBCrypt: Add method for explicitly including trailing zero on password (diff)
downloadBouncyCastle.NET-ed25519-e28885d6c3f7d3f713b72669a156034fa42aacd1.tar.xz
merge master into netstandard
Diffstat (limited to 'crypto')
-rw-r--r--crypto/NBuild.build2
-rw-r--r--crypto/Readme.html18
-rw-r--r--crypto/crypto.csproj45
-rw-r--r--crypto/src/asn1/x509/TBSCertificateStructure.cs53
-rw-r--r--crypto/src/asn1/x509/X509Extensions.cs5
-rw-r--r--crypto/src/crypto/digests/KeccakDigest.cs322
-rw-r--r--crypto/src/crypto/digests/ShakeDigest.cs4
-rw-r--r--crypto/src/crypto/generators/BCrypt.cs11
-rw-r--r--crypto/src/crypto/generators/OpenBsdBCrypt.cs49
-rw-r--r--crypto/src/crypto/generators/SCrypt.cs16
-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/ec/rfc7748/X25519.cs237
-rw-r--r--crypto/src/math/ec/rfc7748/X25519Field.cs520
-rw-r--r--crypto/src/math/ec/rfc7748/X448.cs255
-rw-r--r--crypto/src/math/ec/rfc7748/X448Field.cs904
-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
-rw-r--r--crypto/src/pkix/PkixAttrCertPathBuilder.cs2
-rw-r--r--crypto/src/pkix/PkixCertPathBuilder.cs2
-rw-r--r--crypto/src/pkix/PkixCertPathValidator.cs38
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorUtilities.cs14
-rw-r--r--crypto/src/tsp/TSPAlgorithms.cs10
-rw-r--r--crypto/src/tsp/TSPUtil.cs14
-rw-r--r--crypto/test/src/crypto/test/BCryptTest.cs2
-rw-r--r--crypto/test/src/crypto/test/OpenBsdBCryptTest.cs55
-rw-r--r--crypto/test/src/math/ec/rfc7748/test/X25519Test.cs184
-rw-r--r--crypto/test/src/math/ec/rfc7748/test/X448Test.cs183
-rw-r--r--crypto/test/src/math/ec/test/AllTests.cs1
-rw-r--r--crypto/test/src/math/ec/test/FixedPointTest.cs66
-rw-r--r--crypto/test/src/test/BlockCipherTest.cs4
-rw-r--r--crypto/test/src/test/CertPathValidatorTest.cs26
85 files changed, 5081 insertions, 320 deletions
diff --git a/crypto/NBuild.build b/crypto/NBuild.build
index 20267e600..89c557702 100644
--- a/crypto/NBuild.build
+++ b/crypto/NBuild.build
@@ -3,7 +3,7 @@
 
   <!-- Source control properties -->
   <property name="GITURL" value="bcgit@git.bouncycastle.org:bc-csharp" />
-  <property name="GITCMD" value="C:/Program Files (x86)/Git/bin/git.exe" />
+  <property name="GITCMD" value="C:/Program Files/Git/bin/git.exe" />
 
   <property name="api-debugpath" value="./api/bin/debug" />
   <property name="api-releasepath" value="./api/bin/release" />
diff --git a/crypto/Readme.html b/crypto/Readme.html
index 8594f8b96..c05560d93 100644
--- a/crypto/Readme.html
+++ b/crypto/Readme.html
@@ -31,6 +31,8 @@
 				<a href="#mozTocId3413">Notes:</a>
 		<ol>
             <li>
+                <a href="#mozTocId85317">Release 1.8.3</a>
+            <li>
                 <a href="#mozTocId85316">Release 1.8.2</a>
             <li>
                 <a href="#mozTocId85315">Release 1.8.1</a>
@@ -292,6 +294,22 @@ We state, where EC MQV has not otherwise been disabled or removed:
 		<hr style="WIDTH: 100%; HEIGHT: 2px">
 		<h3><a class="mozTocH3" name="mozTocId3413"></a>Notes:</h3>
 
+        <h4><a class="mozTocH4" name="mozTocId85317"></a>Release 1.8.3, TBD</h4>
+
+        <h5>Additional Features and Functionality</h5>
+        <ul>
+            <li>Further work has been done on improving SHA-3 performance.</li>
+            <li>EC key generation and signing now use cache-timing resistant table lookups.</li>
+            <li>RFC 7748: Added low-level implementations of X25519 and X448.</li>
+        </ul>
+        <h5>Additional Notes</h5>
+        <ul>
+            <li>
+                See the (cumulative) list of GitHub pull requests that we have accepted at
+                <a href="https://github.com/bcgit/bc-csharp/pulls?q=is%3Apr+is%3Aclosed">bcgit/bc-csharp</a>.
+            </li>
+        </ul>
+
         <h4><a class="mozTocH4" name="mozTocId85316"></a>Release 1.8.2, Monday April 9, 2018</h4>
 
         <h5>Security Advisory</h5>
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 4115560c4..d0afb9f14 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -5509,6 +5509,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\math\ec\ECLookupTable.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\math\ec\ECPoint.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -5534,6 +5539,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\math\ec\SimpleLookupTable.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\math\ec\abc\SimpleBigDecimal.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -6209,6 +6219,26 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\math\ec\rfc7748\X25519.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\rfc7748\X25519Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\rfc7748\X448.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\math\ec\rfc7748\X448Field.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\math\field\FiniteFields.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -12300,6 +12330,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\math\ec\rfc7748\test\X25519Test.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\math\ec\rfc7748\test\X448Test.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\math\ec\test\AllTests.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -12325,6 +12365,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\math\ec\test\FixedPointTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\math\ec\test\TnafTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/asn1/x509/TBSCertificateStructure.cs b/crypto/src/asn1/x509/TBSCertificateStructure.cs
index fc7c39ba2..9df078539 100644
--- a/crypto/src/asn1/x509/TBSCertificateStructure.cs
+++ b/crypto/src/asn1/x509/TBSCertificateStructure.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Asn1.X509
 {
@@ -78,6 +79,22 @@ namespace Org.BouncyCastle.Asn1.X509
 				version = new DerInteger(0);
 			}
 
+            bool isV1 = false;
+            bool isV2 = false;
+
+            if (version.Value.Equals(BigInteger.Zero))
+            {
+                isV1 = true;
+            }
+            else if (version.Value.Equals(BigInteger.One))
+            {
+                isV2 = true;
+            }
+            else if (!version.Value.Equals(BigInteger.Two))
+            {
+                throw new ArgumentException("version number not recognised");
+            }
+
 			serialNumber = DerInteger.GetInstance(seq[seqStart + 1]);
 
 			signature = AlgorithmIdentifier.GetInstance(seq[seqStart + 2]);
@@ -98,22 +115,36 @@ namespace Org.BouncyCastle.Asn1.X509
 			//
 			subjectPublicKeyInfo = SubjectPublicKeyInfo.GetInstance(seq[seqStart + 6]);
 
-			for (int extras = seq.Count - (seqStart + 6) - 1; extras > 0; extras--)
+            int extras = seq.Count - (seqStart + 6) - 1;
+            if (extras != 0 && isV1)
+                throw new ArgumentException("version 1 certificate contains extra data");
+
+            while (extras > 0)
 			{
 				DerTaggedObject extra = (DerTaggedObject) seq[seqStart + 6 + extras];
 
 				switch (extra.TagNo)
 				{
-					case 1:
-						issuerUniqueID = DerBitString.GetInstance(extra, false);
-						break;
-					case 2:
-						subjectUniqueID = DerBitString.GetInstance(extra, false);
-						break;
-					case 3:
-						extensions = X509Extensions.GetInstance(extra);
-						break;
-				}
+				case 1:
+                {
+					issuerUniqueID = DerBitString.GetInstance(extra, false);
+					break;
+                }
+                case 2:
+                {
+                    subjectUniqueID = DerBitString.GetInstance(extra, false);
+                    break;
+                }
+				case 3:
+                {
+                    if (isV2)
+                        throw new ArgumentException("version 2 certificate cannot contain extensions");
+
+                    extensions = X509Extensions.GetInstance(extra);
+					break;
+                }
+                }
+                extras--;
 			}
 		}
 
diff --git a/crypto/src/asn1/x509/X509Extensions.cs b/crypto/src/asn1/x509/X509Extensions.cs
index 049d728bb..d1b9fa39a 100644
--- a/crypto/src/asn1/x509/X509Extensions.cs
+++ b/crypto/src/asn1/x509/X509Extensions.cs
@@ -224,7 +224,10 @@ namespace Org.BouncyCastle.Asn1.X509
 
 				Asn1OctetString octets = Asn1OctetString.GetInstance(s[s.Count - 1].ToAsn1Object());
 
-				extensions.Add(oid, new X509Extension(isCritical, octets));
+                if (extensions.Contains(oid))
+                    throw new ArgumentException("repeated extension found: " + oid);
+
+                extensions.Add(oid, new X509Extension(isCritical, octets));
 				ordering.Add(oid);
 			}
         }
diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs
index 8b16e5d3a..3cb5e8957 100644
--- a/crypto/src/crypto/digests/KeccakDigest.cs
+++ b/crypto/src/crypto/digests/KeccakDigest.cs
@@ -15,74 +15,21 @@ namespace Org.BouncyCastle.Crypto.Digests
     public class KeccakDigest
         : IDigest, IMemoable
     {
-        private static readonly ulong[] KeccakRoundConstants = KeccakInitializeRoundConstants();
-
-        private static readonly int[] KeccakRhoOffsets = KeccakInitializeRhoOffsets();
-
-        private static ulong[] KeccakInitializeRoundConstants()
-        {
-            ulong[] keccakRoundConstants = new ulong[24];
-            byte LFSRState = 0x01;
-
-            for (int i = 0; i < 24; i++)
-            {
-                keccakRoundConstants[i] = 0;
-                for (int j = 0; j < 7; j++)
-                {
-                    int bitPosition = (1 << j) - 1;
-
-                    // LFSR86540
-
-                    bool loBit = (LFSRState & 0x01) != 0;
-                    if (loBit)
-                    {
-                        keccakRoundConstants[i] ^= 1UL << bitPosition;
-                    }
-
-                    bool hiBit = (LFSRState & 0x80) != 0;
-                    LFSRState <<= 1;
-                    if (hiBit)
-                    {
-                        LFSRState ^= 0x71;
-                    }
-
-                }
-            }
-
-            return keccakRoundConstants;
-        }
-
-        private static int[] KeccakInitializeRhoOffsets()
-        {
-            int[] keccakRhoOffsets = new int[25];
-            int x, y, t, newX, newY;
-
-            int rhoOffset = 0;
-            keccakRhoOffsets[0] = rhoOffset;
-            x = 1;
-            y = 0;
-            for (t = 1; t < 25; t++)
-            {
-                rhoOffset = (rhoOffset + t) & 63;
-                keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = rhoOffset;
-                newX = (0 * x + 1 * y) % 5;
-                newY = (2 * x + 3 * y) % 5;
-                x = newX;
-                y = newY;
-            }
-
-            return keccakRhoOffsets;
-        }
-
-        private static readonly int STATE_LENGTH = (1600 / 8);
-
-        private ulong[] state = new ulong[STATE_LENGTH / 8];
-        protected byte[] dataQueue = new byte[1536 / 8];
+        private static readonly ulong[] KeccakRoundConstants = new ulong[]{
+            0x0000000000000001UL, 0x0000000000008082UL, 0x800000000000808aUL, 0x8000000080008000UL,
+            0x000000000000808bUL, 0x0000000080000001UL, 0x8000000080008081UL, 0x8000000000008009UL,
+            0x000000000000008aUL, 0x0000000000000088UL, 0x0000000080008009UL, 0x000000008000000aUL,
+            0x000000008000808bUL, 0x800000000000008bUL, 0x8000000000008089UL, 0x8000000000008003UL,
+            0x8000000000008002UL, 0x8000000000000080UL, 0x000000000000800aUL, 0x800000008000000aUL,
+            0x8000000080008081UL, 0x8000000000008080UL, 0x0000000080000001UL, 0x8000000080008008UL
+        };
+
+        private ulong[] state = new ulong[25];
+        protected byte[] dataQueue = new byte[192];
         protected int rate;
         protected int bitsInQueue;
         protected int fixedOutputLength;
         protected bool squeezing;
-        protected int bitsAvailableForSqueezing;
 
         public KeccakDigest()
             : this(288)
@@ -107,7 +54,6 @@ namespace Org.BouncyCastle.Crypto.Digests
             this.bitsInQueue = source.bitsInQueue;
             this.fixedOutputLength = source.fixedOutputLength;
             this.squeezing = source.squeezing;
-            this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing;
         }
 
         public virtual string AlgorithmName
@@ -132,7 +78,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         public virtual int DoFinal(byte[] output, int outOff)
         {
-            Squeeze(output, outOff, fixedOutputLength >> 3);
+            Squeeze(output, outOff, fixedOutputLength);
 
             Reset();
 
@@ -149,7 +95,7 @@ namespace Org.BouncyCastle.Crypto.Digests
                 AbsorbBits(partialByte, partialBits);
             }
 
-            Squeeze(output, outOff, fixedOutputLength >> 3);
+            Squeeze(output, outOff, fixedOutputLength);
 
             Reset();
 
@@ -198,7 +144,6 @@ namespace Org.BouncyCastle.Crypto.Digests
             Arrays.Fill(this.dataQueue, (byte)0);
             this.bitsInQueue = 0;
             this.squeezing = false;
-            this.bitsAvailableForSqueezing = 0;
             this.fixedOutputLength = (1600 - rate) >> 1;
         }
 
@@ -288,34 +233,34 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
 
             KeccakPermutation();
+
             KeccakExtract();
-            bitsAvailableForSqueezing = rate;
+            bitsInQueue = rate;
 
-            bitsInQueue = 0;
             squeezing = true;
         }
 
-        protected void Squeeze(byte[] output, int off, int len)
+        protected void Squeeze(byte[] output, int offset, long outputLength)
         {
             if (!squeezing)
             {
                 PadAndSwitchToSqueezingPhase();
             }
+            if ((outputLength & 7L) != 0L)
+                throw new InvalidOperationException("outputLength not a multiple of 8");
 
-            long outputLength = (long)len << 3;
             long i = 0;
             while (i < outputLength)
             {
-                if (bitsAvailableForSqueezing == 0)
+                if (bitsInQueue == 0)
                 {
                     KeccakPermutation();
                     KeccakExtract();
-                    bitsAvailableForSqueezing = rate;
+                    bitsInQueue = rate;
                 }
-
-                int partialBlock = (int)System.Math.Min((long)bitsAvailableForSqueezing, outputLength - i);
-                Array.Copy(dataQueue, (rate - bitsAvailableForSqueezing) >> 3, output, off + (int)(i >> 3), partialBlock >> 3);
-                bitsAvailableForSqueezing -= partialBlock;
+                int partialBlock = (int)System.Math.Min((long)bitsInQueue, outputLength - i);
+                Array.Copy(dataQueue, (rate - bitsInQueue) >> 3, output, offset + (int)(i >> 3), partialBlock >> 3);
+                bitsInQueue -= partialBlock;
                 i += partialBlock;
             }
         }
@@ -339,131 +284,112 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         private void KeccakPermutation()
         {
-            for (int i = 0; i < 24; i++)
-            {
-                Theta(state);
-                Rho(state);
-                Pi(state);
-                Chi(state);
-                Iota(state, i);
-            }
-        }
+            ulong[] A = state;
 
-        private static ulong leftRotate(ulong v, int r)
-        {
-            return (v << r) | (v >> -r);
-        }
-
-        private static void Theta(ulong[] A)
-        {
-            ulong C0 = A[0 + 0] ^ A[0 + 5] ^ A[0 + 10] ^ A[0 + 15] ^ A[0 + 20];
-            ulong C1 = A[1 + 0] ^ A[1 + 5] ^ A[1 + 10] ^ A[1 + 15] ^ A[1 + 20];
-            ulong C2 = A[2 + 0] ^ A[2 + 5] ^ A[2 + 10] ^ A[2 + 15] ^ A[2 + 20];
-            ulong C3 = A[3 + 0] ^ A[3 + 5] ^ A[3 + 10] ^ A[3 + 15] ^ A[3 + 20];
-            ulong C4 = A[4 + 0] ^ A[4 + 5] ^ A[4 + 10] ^ A[4 + 15] ^ A[4 + 20];
-
-            ulong dX = leftRotate(C1, 1) ^ C4;
-
-            A[0] ^= dX;
-            A[5] ^= dX;
-            A[10] ^= dX;
-            A[15] ^= dX;
-            A[20] ^= dX;
-
-            dX = leftRotate(C2, 1) ^ C0;
-
-            A[1] ^= dX;
-            A[6] ^= dX;
-            A[11] ^= dX;
-            A[16] ^= dX;
-            A[21] ^= dX;
-
-            dX = leftRotate(C3, 1) ^ C1;
-
-            A[2] ^= dX;
-            A[7] ^= dX;
-            A[12] ^= dX;
-            A[17] ^= dX;
-            A[22] ^= dX;
-
-            dX = leftRotate(C4, 1) ^ C2;
-
-            A[3] ^= dX;
-            A[8] ^= dX;
-            A[13] ^= dX;
-            A[18] ^= dX;
-            A[23] ^= dX;
-
-            dX = leftRotate(C0, 1) ^ C3;
-
-            A[4] ^= dX;
-            A[9] ^= dX;
-            A[14] ^= dX;
-            A[19] ^= dX;
-            A[24] ^= dX;
-        }
+            ulong a00 = A[ 0], a01 = A[ 1], a02 = A[ 2], a03 = A[ 3], a04 = A[ 4];
+            ulong a05 = A[ 5], a06 = A[ 6], a07 = A[ 7], a08 = A[ 8], a09 = A[ 9];
+            ulong a10 = A[10], a11 = A[11], a12 = A[12], a13 = A[13], a14 = A[14];
+            ulong a15 = A[15], a16 = A[16], a17 = A[17], a18 = A[18], a19 = A[19];
+            ulong a20 = A[20], a21 = A[21], a22 = A[22], a23 = A[23], a24 = A[24];
 
-        private static void Rho(ulong[] A)
-        {
-            // KeccakRhoOffsets[0] == 0
-            for (int x = 1; x < 25; x++)
-            {
-                A[x] = leftRotate(A[x], KeccakRhoOffsets[x]);
-            }
-        }
-
-        private static void Pi(ulong[] A)
-        {
-            ulong a1 = A[1];
-            A[1] = A[6];
-            A[6] = A[9];
-            A[9] = A[22];
-            A[22] = A[14];
-            A[14] = A[20];
-            A[20] = A[2];
-            A[2] = A[12];
-            A[12] = A[13];
-            A[13] = A[19];
-            A[19] = A[23];
-            A[23] = A[15];
-            A[15] = A[4];
-            A[4] = A[24];
-            A[24] = A[21];
-            A[21] = A[8];
-            A[8] = A[16];
-            A[16] = A[5];
-            A[5] = A[3];
-            A[3] = A[18];
-            A[18] = A[17];
-            A[17] = A[11];
-            A[11] = A[7];
-            A[7] = A[10];
-            A[10] = a1;
-        }
-
-        private static void Chi(ulong[] A)
-        {
-            ulong chiC0, chiC1, chiC2, chiC3, chiC4;
-
-            for (int yBy5 = 0; yBy5 < 25; yBy5 += 5)
+            for (int i = 0; i < 24; i++)
             {
-                chiC0 = A[0 + yBy5] ^ ((~A[(((0 + 1) % 5) + yBy5)]) & A[(((0 + 2) % 5) + yBy5)]);
-                chiC1 = A[1 + yBy5] ^ ((~A[(((1 + 1) % 5) + yBy5)]) & A[(((1 + 2) % 5) + yBy5)]);
-                chiC2 = A[2 + yBy5] ^ ((~A[(((2 + 1) % 5) + yBy5)]) & A[(((2 + 2) % 5) + yBy5)]);
-                chiC3 = A[3 + yBy5] ^ ((~A[(((3 + 1) % 5) + yBy5)]) & A[(((3 + 2) % 5) + yBy5)]);
-                chiC4 = A[4 + yBy5] ^ ((~A[(((4 + 1) % 5) + yBy5)]) & A[(((4 + 2) % 5) + yBy5)]);
-
-                A[0 + yBy5] = chiC0;
-                A[1 + yBy5] = chiC1;
-                A[2 + yBy5] = chiC2;
-                A[3 + yBy5] = chiC3;
-                A[4 + yBy5] = chiC4;
+                // theta
+                ulong c0 = a00 ^ a05 ^ a10 ^ a15 ^ a20;
+                ulong c1 = a01 ^ a06 ^ a11 ^ a16 ^ a21;
+                ulong c2 = a02 ^ a07 ^ a12 ^ a17 ^ a22;
+                ulong c3 = a03 ^ a08 ^ a13 ^ a18 ^ a23;
+                ulong c4 = a04 ^ a09 ^ a14 ^ a19 ^ a24;
+
+                ulong d1 = (c1 << 1 | c1 >> -1) ^ c4;
+                ulong d2 = (c2 << 1 | c2 >> -1) ^ c0;
+                ulong d3 = (c3 << 1 | c3 >> -1) ^ c1;
+                ulong d4 = (c4 << 1 | c4 >> -1) ^ c2;
+                ulong d0 = (c0 << 1 | c0 >> -1) ^ c3;
+
+                a00 ^= d1; a05 ^= d1; a10 ^= d1; a15 ^= d1; a20 ^= d1;
+                a01 ^= d2; a06 ^= d2; a11 ^= d2; a16 ^= d2; a21 ^= d2;
+                a02 ^= d3; a07 ^= d3; a12 ^= d3; a17 ^= d3; a22 ^= d3;
+                a03 ^= d4; a08 ^= d4; a13 ^= d4; a18 ^= d4; a23 ^= d4;
+                a04 ^= d0; a09 ^= d0; a14 ^= d0; a19 ^= d0; a24 ^= d0;
+
+                // rho/pi
+                c1  = a01 <<  1 | a01 >> 63;
+                a01 = a06 << 44 | a06 >> 20;
+                a06 = a09 << 20 | a09 >> 44;
+                a09 = a22 << 61 | a22 >>  3;
+                a22 = a14 << 39 | a14 >> 25;
+                a14 = a20 << 18 | a20 >> 46;
+                a20 = a02 << 62 | a02 >>  2;
+                a02 = a12 << 43 | a12 >> 21;
+                a12 = a13 << 25 | a13 >> 39;
+                a13 = a19 <<  8 | a19 >> 56;
+                a19 = a23 << 56 | a23 >>  8;
+                a23 = a15 << 41 | a15 >> 23;
+                a15 = a04 << 27 | a04 >> 37;
+                a04 = a24 << 14 | a24 >> 50;
+                a24 = a21 <<  2 | a21 >> 62;
+                a21 = a08 << 55 | a08 >>  9;
+                a08 = a16 << 45 | a16 >> 19;
+                a16 = a05 << 36 | a05 >> 28;
+                a05 = a03 << 28 | a03 >> 36;
+                a03 = a18 << 21 | a18 >> 43;
+                a18 = a17 << 15 | a17 >> 49;
+                a17 = a11 << 10 | a11 >> 54;
+                a11 = a07 <<  6 | a07 >> 58;
+                a07 = a10 <<  3 | a10 >> 61;
+                a10 = c1;
+
+                // chi
+                c0 = a00 ^ (~a01 & a02);
+                c1 = a01 ^ (~a02 & a03);
+                a02 ^= ~a03 & a04;
+                a03 ^= ~a04 & a00;
+                a04 ^= ~a00 & a01;
+                a00 = c0;
+                a01 = c1;
+
+                c0 = a05 ^ (~a06 & a07);
+                c1 = a06 ^ (~a07 & a08);
+                a07 ^= ~a08 & a09;
+                a08 ^= ~a09 & a05;
+                a09 ^= ~a05 & a06;
+                a05 = c0;
+                a06 = c1;
+
+                c0 = a10 ^ (~a11 & a12);
+                c1 = a11 ^ (~a12 & a13);
+                a12 ^= ~a13 & a14;
+                a13 ^= ~a14 & a10;
+                a14 ^= ~a10 & a11;
+                a10 = c0;
+                a11 = c1;
+
+                c0 = a15 ^ (~a16 & a17);
+                c1 = a16 ^ (~a17 & a18);
+                a17 ^= ~a18 & a19;
+                a18 ^= ~a19 & a15;
+                a19 ^= ~a15 & a16;
+                a15 = c0;
+                a16 = c1;
+
+                c0 = a20 ^ (~a21 & a22);
+                c1 = a21 ^ (~a22 & a23);
+                a22 ^= ~a23 & a24;
+                a23 ^= ~a24 & a20;
+                a24 ^= ~a20 & a21;
+                a20 = c0;
+                a21 = c1;
+
+                // iota
+                a00 ^= KeccakRoundConstants[i];
             }
-        }
 
-        private static void Iota(ulong[] A, int indexRound)
-        {
-            A[0] ^= KeccakRoundConstants[indexRound];
+            A[ 0] = a00; A[ 1] = a01; A[ 2] = a02; A[ 3] = a03; A[ 4] = a04;
+            A[ 5] = a05; A[ 6] = a06; A[ 7] = a07; A[ 8] = a08; A[ 9] = a09;
+            A[10] = a10; A[11] = a11; A[12] = a12; A[13] = a13; A[14] = a14;
+            A[15] = a15; A[16] = a16; A[17] = a17; A[18] = a18; A[19] = a19;
+            A[20] = a20; A[21] = a21; A[22] = a22; A[23] = a23; A[24] = a24;
         }
 
         public virtual IMemoable Copy()
diff --git a/crypto/src/crypto/digests/ShakeDigest.cs b/crypto/src/crypto/digests/ShakeDigest.cs
index 13e8838c1..e58452c36 100644
--- a/crypto/src/crypto/digests/ShakeDigest.cs
+++ b/crypto/src/crypto/digests/ShakeDigest.cs
@@ -67,7 +67,7 @@ namespace Org.BouncyCastle.Crypto.Digests
                 AbsorbBits(0x0F, 4);
             }
 
-            Squeeze(output, outOff, outLen);
+            Squeeze(output, outOff, (long)outLen << 3);
 
             return outLen;
         }
@@ -104,7 +104,7 @@ namespace Org.BouncyCastle.Crypto.Digests
                 AbsorbBits(finalInput, finalBits);
             }
 
-            Squeeze(output, outOff, outLen);
+            Squeeze(output, outOff, (long)outLen << 3);
 
             Reset();
 
diff --git a/crypto/src/crypto/generators/BCrypt.cs b/crypto/src/crypto/generators/BCrypt.cs
index af8029a1b..6e9611ad2 100644
--- a/crypto/src/crypto/generators/BCrypt.cs
+++ b/crypto/src/crypto/generators/BCrypt.cs
@@ -587,6 +587,17 @@ namespace Org.BouncyCastle.Crypto.Generators
         internal const int MAX_PASSWORD_BYTES = 72;
 
         /**
+         * Converts a character password to bytes incorporating the required trailing zero byte.
+         *
+         * @param password the password to be encoded.
+         * @return a byte representation of the password in UTF8 + trailing zero.
+         */
+        public static byte[] PasswordToByteArray(char[] password)
+        {
+            return Arrays.Append(Strings.ToUtf8ByteArray(password), 0);
+        }
+
+        /**
          * Calculates the <b>bcrypt</b> hash of a password.
          * <p>
          * This implements the raw <b>bcrypt</b> function as defined in the bcrypt specification, not
diff --git a/crypto/src/crypto/generators/OpenBsdBCrypt.cs b/crypto/src/crypto/generators/OpenBsdBCrypt.cs
index 85c34d769..49f79f95b 100644
--- a/crypto/src/crypto/generators/OpenBsdBCrypt.cs
+++ b/crypto/src/crypto/generators/OpenBsdBCrypt.cs
@@ -3,6 +3,7 @@ using System.IO;
 using System.Text;
 
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Crypto.Generators
 {
@@ -33,10 +34,16 @@ namespace Org.BouncyCastle.Crypto.Generators
          * set up the decoding table.
          */
         private static readonly byte[] DecodingTable = new byte[128];
-        private static readonly string Version = "2a"; // previous version was not UTF-8
+        private static readonly string DefaultVersion = "2y";
+        private static readonly ISet AllowedVersions = new HashSet();
 
         static OpenBsdBCrypt()
         {
+            // Presently just the Bcrypt versions.
+            AllowedVersions.Add("2a");
+            AllowedVersions.Add("2y");
+            AllowedVersions.Add("2b");
+
             for (int i = 0; i < DecodingTable.Length; i++)
             {
                 DecodingTable[i] = (byte)0xff;
@@ -56,16 +63,20 @@ namespace Org.BouncyCastle.Crypto.Generators
          * Creates a 60 character Bcrypt String, including
          * version, cost factor, salt and hash, separated by '$'
          *
+         * @param version  the version, 2y,2b or 2a. (2a is not backwards compatible.)
          * @param cost     the cost factor, treated as an exponent of 2
          * @param salt     a 16 byte salt
          * @param password the password
          * @return a 60 character Bcrypt String
          */
-        private static string CreateBcryptString(byte[] password, byte[] salt, int cost)
+        private static string CreateBcryptString(string version, byte[] password, byte[] salt, int cost)
         {
+            if (!AllowedVersions.Contains(version))
+                throw new ArgumentException("Version " + version + " is not accepted by this implementation.", "version");
+
             StringBuilder sb = new StringBuilder(60);
             sb.Append('$');
-            sb.Append(Version);
+            sb.Append(version);
             sb.Append('$');
             sb.Append(cost < 10 ? ("0" + cost) : cost.ToString());
             sb.Append('$');
@@ -80,7 +91,8 @@ namespace Org.BouncyCastle.Crypto.Generators
 
         /**
          * Creates a 60 character Bcrypt String, including
-         * version, cost factor, salt and hash, separated by '$'
+         * version, cost factor, salt and hash, separated by '$' using version
+         * '2y'.
          *
          * @param cost     the cost factor, treated as an exponent of 2
          * @param salt     a 16 byte salt
@@ -89,6 +101,23 @@ namespace Org.BouncyCastle.Crypto.Generators
          */
         public static string Generate(char[] password, byte[] salt, int cost)
         {
+            return Generate(DefaultVersion, password, salt, cost);
+        }
+
+        /**
+         * Creates a 60 character Bcrypt String, including
+         * version, cost factor, salt and hash, separated by '$'
+         *
+         * @param version  the version, may be 2b, 2y or 2a. (2a is not backwards compatible.)
+         * @param cost     the cost factor, treated as an exponent of 2
+         * @param salt     a 16 byte salt
+         * @param password the password
+         * @return a 60 character Bcrypt String
+         */
+        public static string Generate(string version, char[] password, byte[] salt, int cost)
+        {
+            if (!AllowedVersions.Contains(version))
+                throw new ArgumentException("Version " + version + " is not accepted by this implementation.", "version");
             if (password == null)
                 throw new ArgumentNullException("password");
             if (salt == null)
@@ -109,7 +138,7 @@ namespace Org.BouncyCastle.Crypto.Generators
 
             Array.Clear(psw, 0, psw.Length);
 
-            string rv = CreateBcryptString(tmp, salt, cost);
+            string rv = CreateBcryptString(version, tmp, salt, cost);
 
             Array.Clear(tmp, 0, tmp.Length);
 
@@ -133,8 +162,10 @@ namespace Org.BouncyCastle.Crypto.Generators
                 throw new DataLengthException("Bcrypt String length: " + bcryptString.Length + ", 60 required.");
             if (bcryptString[0] != '$' || bcryptString[3] != '$' || bcryptString[6] != '$')
                 throw new ArgumentException("Invalid Bcrypt String format.", "bcryptString");
-            if (!bcryptString.Substring(1, 2).Equals(Version))
-                throw new ArgumentException("Wrong Bcrypt version, 2a expected.", "bcryptString");
+
+            string version = bcryptString.Substring(1, 2);
+            if (!AllowedVersions.Contains(version))
+                throw new ArgumentException("Bcrypt version '" + version + "' is not supported by this implementation", "bcryptString");
 
             int cost = 0;
             try
@@ -143,7 +174,7 @@ namespace Org.BouncyCastle.Crypto.Generators
             }
             catch (Exception nfe)
             {
-                throw new ArgumentException("Invalid cost factor: " + bcryptString.Substring(4, 2), "bcryptString");
+                throw new ArgumentException("Invalid cost factor: " + bcryptString.Substring(4, 2), "bcryptString", nfe);
             }
             if (cost < 4 || cost > 31)
                 throw new ArgumentException("Invalid cost factor: " + cost + ", 4 < cost < 31 expected.");
@@ -155,7 +186,7 @@ namespace Org.BouncyCastle.Crypto.Generators
             int start = bcryptString.LastIndexOf('$') + 1, end = bcryptString.Length - 31;
             byte[] salt = DecodeSaltString(bcryptString.Substring(start, end - start));
 
-            string newBcryptString = Generate(password, salt, cost);
+            string newBcryptString = Generate(version, password, salt, cost);
 
             return bcryptString.Equals(newBcryptString);
         }
diff --git a/crypto/src/crypto/generators/SCrypt.cs b/crypto/src/crypto/generators/SCrypt.cs
index 64a36df63..4d15bb3d7 100644
--- a/crypto/src/crypto/generators/SCrypt.cs
+++ b/crypto/src/crypto/generators/SCrypt.cs
@@ -1,5 +1,5 @@
 using System;
-using System.Threading;
+using System.Diagnostics;
 
 using Org.BouncyCastle.Crypto.Digests;
 using Org.BouncyCastle.Crypto.Engines;
@@ -31,8 +31,8 @@ namespace Org.BouncyCastle.Crypto.Generators
                 throw new ArgumentNullException("Passphrase P must be provided.");
             if (S == null)
                 throw new ArgumentNullException("Salt S must be provided.");
-            if (N <= 1)
-                throw new ArgumentException("Cost parameter N must be > 1.");
+            if (N <= 1 || !IsPowerOf2(N))
+                throw new ArgumentException("Cost parameter N must be > 1 and a power of 2.");
             // Only value of r that cost (as an int) could be exceeded for is 1
             if (r == 1 && N >= 65536)
                 throw new ArgumentException("Cost parameter N must be > 1 and < 65536.");
@@ -170,5 +170,13 @@ namespace Org.BouncyCastle.Crypto.Generators
 				Clear(array);
 			}
 		}
-	}
+
+        // note: we know X is non-zero
+        private static bool IsPowerOf2(int x)
+        {
+            Debug.Assert(x != 0);
+
+            return (x & (x - 1)) == 0;
+        }
+    }
 }
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/ec/rfc7748/X25519.cs b/crypto/src/math/ec/rfc7748/X25519.cs
new file mode 100644
index 000000000..16f680d90
--- /dev/null
+++ b/crypto/src/math/ec/rfc7748/X25519.cs
@@ -0,0 +1,237 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Org.BouncyCastle.Math.EC.Rfc7748
+{
+    public abstract class X25519
+    {
+        private const int C_A = 486662;
+        private const int C_A24 = (C_A + 2)/4;
+
+        // 0x1
+        //private static readonly int[] S_x = new int[] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+        // 0x215132111D8354CB52385F46DCA2B71D440F6A51EB4D1207816B1E0137D48290
+        private static readonly int[] PsubS_x = new int[]{ 0x03D48290, 0x02C7804D, 0x01207816, 0x028F5A68, 0x00881ED4, 0x00A2B71D,
+            0x0217D1B7, 0x014CB523, 0x0088EC1A, 0x0042A264 };
+
+        private static int[] precompBase = null;
+
+        private static uint Decode32(byte[] bs, int off)
+        {
+            uint n = bs[off];
+            n |= (uint)bs[++off] << 8;
+            n |= (uint)bs[++off] << 16;
+            n |= (uint)bs[++off] << 24;
+            return n;
+        }
+
+        private static void DecodeScalar(byte[] k, int kOff, uint[] n)
+        {
+            for (int i = 0; i < 8; ++i)
+            {
+                n[i] = Decode32(k, kOff + i * 4);
+            }
+
+            n[0] &= 0xFFFFFFF8U;
+            n[7] &= 0x7FFFFFFFU;
+            n[7] |= 0x40000000U;
+        }
+
+        private static void PointDouble(int[] x, int[] z)
+        {
+            int[] A = X25519Field.Create();
+            int[] B = X25519Field.Create();
+
+            X25519Field.Apm(x, z, A, B);
+            X25519Field.Sqr(A, A);
+            X25519Field.Sqr(B, B);
+            X25519Field.Mul(A, B, x);
+            X25519Field.Sub(A, B, A);
+            X25519Field.Mul(A, C_A24, z);
+            X25519Field.Add(z, B, z);
+            X25519Field.Mul(z, A, z);
+        }
+
+        [MethodImpl(MethodImplOptions.Synchronized)]
+        public static void Precompute()
+        {
+            if (precompBase != null)
+                return;
+
+            precompBase = new int[X25519Field.Size * 252];
+
+            int[] xs = precompBase;
+            int[] zs = new int[X25519Field.Size * 251];
+
+            int[] x = X25519Field.Create();     x[0] = 9;          
+            int[] z = X25519Field.Create();     z[0] = 1;
+
+            int[] n = X25519Field.Create();
+            int[] d = X25519Field.Create();
+
+            X25519Field.Apm(x, z, n, d);
+
+            int[] c = X25519Field.Create();     X25519Field.Copy(d, 0, c, 0);
+
+            int off = 0;
+            for (;;)
+            {
+                X25519Field.Copy(n, 0, xs, off);
+
+                if (off == (X25519Field.Size * 251))
+                    break;
+
+                PointDouble(x, z);
+
+                X25519Field.Apm(x, z, n, d);
+                X25519Field.Mul(n, c, n);
+                X25519Field.Mul(c, d, c);
+
+                X25519Field.Copy(d, 0, zs, off);
+
+                off += X25519Field.Size;
+            }
+
+            int[] u = X25519Field.Create();
+            X25519Field.Inv(c, u);
+
+            for (;;)
+            {
+                X25519Field.Copy(xs, off, x, 0);
+
+                X25519Field.Mul(x, u, x);
+                //X25519Field.Normalize(x);
+                X25519Field.Copy(x, 0, precompBase, off);
+
+                if (off == 0)
+                    break;
+
+                off -= X25519Field.Size;
+                X25519Field.Copy(zs, off, z, 0);
+                X25519Field.Mul(u, z, u);
+            }
+        }
+
+        public static void ScalarMult(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff)
+        {
+            uint[] n = new uint[8];     DecodeScalar(k, kOff, n);
+
+            int[] x1 = X25519Field.Create();        X25519Field.Decode(u, uOff, x1);
+            int[] x2 = X25519Field.Create();        X25519Field.Copy(x1, 0, x2, 0);
+            int[] z2 = X25519Field.Create();        z2[0] = 1;
+            int[] x3 = X25519Field.Create();        x3[0] = 1;
+            int[] z3 = X25519Field.Create();
+
+            int[] t1 = X25519Field.Create();
+            int[] t2 = X25519Field.Create();
+
+            Debug.Assert(n[7] >> 30 == 1U);
+
+            int bit = 254, swap = 1;
+            do
+            {
+                X25519Field.Apm(x3, z3, t1, x3);
+                X25519Field.Apm(x2, z2, z3, x2);
+                X25519Field.Mul(t1, x2, t1);
+                X25519Field.Mul(x3, z3, x3);
+                X25519Field.Sqr(z3, z3);
+                X25519Field.Sqr(x2, x2);
+
+                X25519Field.Sub(z3, x2, t2);
+                X25519Field.Mul(t2, C_A24, z2);
+                X25519Field.Add(z2, x2, z2);
+                X25519Field.Mul(z2, t2, z2);
+                X25519Field.Mul(x2, z3, x2);
+
+                X25519Field.Apm(t1, x3, x3, z3);
+                X25519Field.Sqr(x3, x3);
+                X25519Field.Sqr(z3, z3);
+                X25519Field.Mul(z3, x1, z3);
+
+                --bit;
+
+                int word = bit >> 5, shift = bit & 0x1F;
+                int kt = (int)(n[word] >> shift) & 1;
+                swap ^= kt;
+                X25519Field.CSwap(swap, x2, x3);
+                X25519Field.CSwap(swap, z2, z3);
+                swap = kt;
+            }
+            while (bit >= 3);
+
+            Debug.Assert(swap == 0);
+
+            for (int i = 0; i < 3; ++i)
+            {
+                PointDouble(x2, z2);
+            }
+
+            X25519Field.Inv(z2, z2);
+            X25519Field.Mul(x2, z2, x2);
+
+            X25519Field.Normalize(x2);
+            X25519Field.Encode(x2, r, rOff);
+        }
+
+        public static void ScalarMultBase(byte[] k, int kOff, byte[] r, int rOff)
+        {
+            Precompute();
+
+            uint[] n = new uint[8];     DecodeScalar(k, kOff, n);
+
+            int[] x0 = X25519Field.Create();
+            //int[] x1 = X25519Field.Create();        X25519Field.Copy(S_x, 0, x1, 0);
+            int[] x1 = X25519Field.Create();        x1[0] = 1;
+            int[] z1 = X25519Field.Create();        z1[0] = 1;        
+            int[] x2 = X25519Field.Create();        X25519Field.Copy(PsubS_x, 0, x2, 0);
+            int[] z2 = X25519Field.Create();        z2[0] = 1;        
+
+            int[] A = x1;
+            int[] B = z1;
+            int[] C = x0;
+            int[] D = A;
+            int[] E = B;
+
+            Debug.Assert(n[7] >> 30 == 1U);
+
+            int off = 0, bit = 3, swap = 1;
+            do
+            {
+                X25519Field.Copy(precompBase, off, x0, 0);
+                off += X25519Field.Size;
+
+                int word = bit >> 5, shift = bit & 0x1F;
+                int kt = (int)(n[word] >> shift) & 1;
+                swap ^= kt;
+                X25519Field.CSwap(swap, x1, x2);
+                X25519Field.CSwap(swap, z1, z2);
+                swap = kt;
+
+                X25519Field.Apm(x1, z1, A, B);
+                X25519Field.Mul(x0, B, C);
+                X25519Field.Carry(A);
+                X25519Field.Apm(A, C, D, E);
+                X25519Field.Sqr(D, D);
+                X25519Field.Sqr(E, E);
+                X25519Field.Mul(z2, D, x1);
+                X25519Field.Mul(x2, E, z1);
+            }
+            while (++bit < 255);
+
+            Debug.Assert(swap == 1);
+
+            for (int i = 0; i < 3; ++i)
+            {
+                PointDouble(x1, z1);
+            }
+
+            X25519Field.Inv(z1, z1);
+            X25519Field.Mul(x1, z1, x1);
+
+            X25519Field.Normalize(x1);
+            X25519Field.Encode(x1, r, rOff);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/rfc7748/X25519Field.cs b/crypto/src/math/ec/rfc7748/X25519Field.cs
new file mode 100644
index 000000000..282f41628
--- /dev/null
+++ b/crypto/src/math/ec/rfc7748/X25519Field.cs
@@ -0,0 +1,520 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Rfc7748
+{
+    public abstract class X25519Field
+    {
+        public const int Size = 10;
+
+        private const int M24 = 0x00FFFFFF;
+        private const int M25 = 0x01FFFFFF;
+        private const int M26 = 0x03FFFFFF;
+
+        private X25519Field() {}
+
+        public static void Add(int[] x, int[] y, int[] z)
+        {
+            for (int i = 0; i < Size; ++i)
+            {
+                z[i] = x[i] + y[i];
+            }
+        }
+
+        public static void Apm(int[] x, int[] y, int[] zp, int[] zm)
+        {
+            for (int i = 0; i < Size; ++i)
+            {
+                int xi = x[i], yi = y[i];
+                zp[i] = xi + yi;
+                zm[i] = xi - yi;
+            }
+        }
+
+        public static void Carry(int[] z)
+        {
+            int z0 = z[0], z1 = z[1], z2 = z[2], z3 = z[3], z4 = z[4];
+            int z5 = z[5], z6 = z[6], z7 = z[7], z8 = z[8], z9 = z[9];
+
+            z3 += (z2 >> 25); z2 &= M25;
+            z5 += (z4 >> 25); z4 &= M25;
+            z8 += (z7 >> 25); z7 &= M25;
+            //z0 += (z9 >> 24) * 19; z9 &= M24;
+            z0 += (z9 >> 25) * 38; z9 &= M25;
+
+            z1 += (z0 >> 26); z0 &= M26;
+            z6 += (z5 >> 26); z5 &= M26;
+
+            z2 += (z1 >> 26); z1 &= M26;
+            z4 += (z3 >> 26); z3 &= M26;
+            z7 += (z6 >> 26); z6 &= M26;
+            z9 += (z8 >> 26); z8 &= M26;
+
+            z[0] = z0; z[1] = z1; z[2] = z2; z[3] = z3; z[4] = z4;
+            z[5] = z5; z[6] = z6; z[7] = z7; z[8] = z8; z[9] = z9;
+        }
+
+        public static void Copy(int[] x, int xOff, int[] z, int zOff)
+        {
+            for (int i = 0; i < Size; ++i)
+            {
+                z[zOff + i] = x[xOff + i];
+            }
+        }
+
+        public static int[] Create()
+        {
+            return new int[Size];
+        }
+
+        public static void CSwap(int swap, int[] a, int[] b)
+        {
+            Debug.Assert(swap >> 1 == 0);
+            Debug.Assert(a != b);
+
+            int mask = 0 - swap;
+            for (int i = 0; i < Size; ++i)
+            {
+                int ai = a[i], bi = b[i];
+                int dummy = mask & (ai ^ bi);
+                a[i] = ai ^ dummy; 
+                b[i] = bi ^ dummy; 
+            }
+        }
+
+        public static void Decode(byte[] x, int xOff, int[] z)
+        {
+            Decode128(x, xOff, z, 0);
+            Decode128(x, xOff + 16, z, 5);
+            z[9] &= M24;
+        }
+
+        private static void Decode128(byte[] bs, int off, int[] z, int zOff)
+        {
+            uint t0 = Decode32(bs, off + 0);
+            uint t1 = Decode32(bs, off + 4);
+            uint t2 = Decode32(bs, off + 8);
+            uint t3 = Decode32(bs, off + 12);
+
+            z[zOff + 0] = (int)t0 & M26;
+            z[zOff + 1] = (int)((t1 << 6) | (t0 >> 26)) & M26;
+            z[zOff + 2] = (int)((t2 << 12) | (t1 >> 20)) & M25;
+            z[zOff + 3] = (int)((t3 << 19) | (t2 >> 13)) & M26;
+            z[zOff + 4] = (int)(t3 >> 7);
+        }
+
+        private static uint Decode32(byte[] bs, int off)
+        {
+            uint n = bs[off];
+            n |= (uint)bs[++off] << 8;
+            n |= (uint)bs[++off] << 16;
+            n |= (uint)bs[++off] << 24;
+            return n;
+        }
+
+        public static void Encode(int[] x, byte[] z, int zOff)
+        {
+            Encode128(x, 0, z, zOff);
+            Encode128(x, 5, z, zOff + 16);
+        }
+
+        private static void Encode128(int[] x, int xOff, byte[] bs, int off)
+        {
+            uint x0 = (uint)x[xOff + 0], x1 = (uint)x[xOff + 1], x2 = (uint)x[xOff + 2];
+            uint x3 = (uint)x[xOff + 3], x4 = (uint)x[xOff + 4];
+
+            uint t0 =  x0        | (x1 << 26);  Encode32(t0, bs, off + 0);
+            uint t1 = (x1 >>  6) | (x2 << 20);  Encode32(t1, bs, off + 4);
+            uint t2 = (x2 >> 12) | (x3 << 13);  Encode32(t2, bs, off + 8);
+            uint t3 = (x3 >> 19) | (x4 <<  7);  Encode32(t3, bs, off + 12);
+        }
+
+        private static void Encode32(uint n, byte[] bs, int off)
+        {
+            bs[  off] = (byte)(n      );
+            bs[++off] = (byte)(n >>  8);
+            bs[++off] = (byte)(n >> 16);
+            bs[++off] = (byte)(n >> 24);
+        }
+
+        public static void Inv(int[] x, int[] z)
+        {
+            // z = x^(p-2) = x^7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB
+            // (250 1s) (1 0s) (1 1s) (1 0s) (2 1s)
+            // Addition chain: [1] [2] 3 5 10 15 25 50 75 125 [250]
+
+            int[] x2 = Create();    Sqr(x, x2);             Mul(x, x2, x2);
+            int[] x3 = Create();    Sqr(x2, x3);            Mul(x, x3, x3);
+            int[] x5 = x3;          Sqr(x3, 2, x5);         Mul(x2, x5, x5);
+            int[] x10 = Create();   Sqr(x5, 5, x10);        Mul(x5, x10, x10);
+            int[] x15 = Create();   Sqr(x10, 5, x15);       Mul(x5, x15, x15);
+            int[] x25 = x5;         Sqr(x15, 10, x25);      Mul(x10, x25, x25);
+            int[] x50 = x10;        Sqr(x25, 25, x50);      Mul(x25, x50, x50);
+            int[] x75 = x15;        Sqr(x50, 25, x75);      Mul(x25, x75, x75);
+            int[] x125 = x25;       Sqr(x75, 50, x125);     Mul(x50, x125, x125);
+            int[] x250 = x50;       Sqr(x125, 125, x250);   Mul(x125, x250, x250);
+
+            int[] t = x125;
+            Sqr(x250, 2, t);
+            Mul(t, x, t);
+            Sqr(t, 3, t);
+            Mul(t, x2, z);
+        }
+
+        public static void Mul(int[] x, int y, int[] z)
+        {
+            int x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4];
+            int x5 = x[5], x6 = x[6], x7 = x[7], x8 = x[8], x9 = x[9];
+            long c0, c1, c2, c3;
+
+            c0  = (long)x2 * y; x2 = (int)c0 & M25; c0 >>= 25;
+            c1  = (long)x4 * y; x4 = (int)c1 & M25; c1 >>= 25;
+            c2  = (long)x7 * y; x7 = (int)c2 & M25; c2 >>= 25;
+            //c3 = (long)x9 * y; x9 = (int)c3 & M24; c3 >>= 24;
+            //c3 *= 19;
+            c3  = (long)x9 * y; x9 = (int)c3 & M25; c3 >>= 25;
+            c3 *= 38;
+
+            c3 += (long)x0 * y; z[0] = (int)c3 & M26; c3 >>= 26;
+            c1 += (long)x5 * y; z[5] = (int)c1 & M26; c1 >>= 26;
+
+            c3 += (long)x1 * y; z[1] = (int)c3 & M26; c3 >>= 26;
+            c0 += (long)x3 * y; z[3] = (int)c0 & M26; c0 >>= 26;
+            c1 += (long)x6 * y; z[6] = (int)c1 & M26; c1 >>= 26;
+            c2 += (long)x8 * y; z[8] = (int)c2 & M26; c2 >>= 26;
+
+            z[2] = x2 + (int)c3;
+            z[4] = x4 + (int)c0;
+            z[7] = x7 + (int)c1;
+            z[9] = x9 + (int)c2;
+        }
+
+        public static void Mul(int[] x, int[] y, int[] z)
+        {
+            int x0 = x[0], y0 = y[0];
+            int x1 = x[1], y1 = y[1];
+            int x2 = x[2], y2 = y[2];
+            int x3 = x[3], y3 = y[3];
+            int x4 = x[4], y4 = y[4];
+
+            int u0 = x[5], v0 = y[5];
+            int u1 = x[6], v1 = y[6];
+            int u2 = x[7], v2 = y[7];
+            int u3 = x[8], v3 = y[8];
+            int u4 = x[9], v4 = y[9];
+
+            long a0  = (long)x0 * y0;
+            long a1  = (long)x0 * y1
+                     + (long)x1 * y0;
+            long a2  = (long)x0 * y2
+                     + (long)x1 * y1
+                     + (long)x2 * y0;
+            long a3  = (long)x1 * y2
+                     + (long)x2 * y1;
+            a3     <<= 1;
+            a3      += (long)x0 * y3
+                     + (long)x3 * y0;
+            long a4  = (long)x2 * y2;
+            a4     <<= 1;
+            a4      += (long)x0 * y4
+                     + (long)x1 * y3
+                     + (long)x3 * y1
+                     + (long)x4 * y0;
+            long a5  = (long)x1 * y4
+                     + (long)x2 * y3
+                     + (long)x3 * y2
+                     + (long)x4 * y1;
+            a5     <<= 1;
+            long a6  = (long)x2 * y4
+                     + (long)x4 * y2;
+            a6     <<= 1;
+            a6      += (long)x3 * y3;
+            long a7  = (long)x3 * y4
+                     + (long)x4 * y3;
+            long a8  = (long)x4 * y4;
+            a8     <<= 1;
+
+            long b0  = (long)u0 * v0;
+            long b1  = (long)u0 * v1
+                     + (long)u1 * v0;
+            long b2  = (long)u0 * v2
+                     + (long)u1 * v1
+                     + (long)u2 * v0;
+            long b3  = (long)u1 * v2
+                     + (long)u2 * v1;
+            b3     <<= 1;
+            b3      += (long)u0 * v3
+                     + (long)u3 * v0;
+            long b4  = (long)u2 * v2;
+            b4     <<= 1;
+            b4      += (long)u0 * v4
+                     + (long)u1 * v3
+                     + (long)u3 * v1
+                     + (long)u4 * v0;
+            long b5  = (long)u1 * v4
+                     + (long)u2 * v3
+                     + (long)u3 * v2
+                     + (long)u4 * v1;
+            //b5     <<= 1;
+            long b6  = (long)u2 * v4
+                     + (long)u4 * v2;
+            b6     <<= 1;
+            b6      += (long)u3 * v3;
+            long b7  = (long)u3 * v4
+                     + (long)u4 * v3;
+            long b8  = (long)u4 * v4;
+            //b8     <<= 1;
+
+            a0 -= b5 * 76;
+            a1 -= b6 * 38;
+            a2 -= b7 * 38;
+            a3 -= b8 * 76;
+
+            a5 -= b0;
+            a6 -= b1;
+            a7 -= b2;
+            a8 -= b3;
+            //long a9 = -b4;
+
+            x0 += u0; y0 += v0;
+            x1 += u1; y1 += v1;
+            x2 += u2; y2 += v2;
+            x3 += u3; y3 += v3;
+            x4 += u4; y4 += v4;
+
+            long c0  = (long)x0 * y0;
+            long c1  = (long)x0 * y1
+                     + (long)x1 * y0;
+            long c2  = (long)x0 * y2
+                     + (long)x1 * y1
+                     + (long)x2 * y0;
+            long c3  = (long)x1 * y2
+                     + (long)x2 * y1;
+            c3     <<= 1;
+            c3      += (long)x0 * y3
+                     + (long)x3 * y0;
+            long c4  = (long)x2 * y2;
+            c4     <<= 1;
+            c4      += (long)x0 * y4
+                     + (long)x1 * y3
+                     + (long)x3 * y1
+                     + (long)x4 * y0;
+            long c5  = (long)x1 * y4
+                     + (long)x2 * y3
+                     + (long)x3 * y2
+                     + (long)x4 * y1;
+            c5     <<= 1;
+            long c6  = (long)x2 * y4
+                     + (long)x4 * y2;
+            c6     <<= 1;
+            c6      += (long)x3 * y3;
+            long c7  = (long)x3 * y4
+                     + (long)x4 * y3;
+            long c8  = (long)x4 * y4;
+            c8     <<= 1;
+
+            int z8, z9;
+            long t;
+
+            t        = a8 + (c3 - a3);
+            z8       = (int)t & M26; t >>= 26;
+            //t       += a9 + (c4 - a4);
+            t       +=      (c4 - a4) - b4;
+            //z9       = (int)t & M24; t >>= 24;
+            //t        = a0 + (t + ((c5 - a5) << 1)) * 19;
+            z9       = (int)t & M25; t >>= 25;
+            t        = a0 + (t + c5 - a5) * 38;
+            z[0]     = (int)t & M26; t >>= 26;
+            t       += a1 + (c6 - a6) * 38;
+            z[1]     = (int)t & M26; t >>= 26;
+            t       += a2 + (c7 - a7) * 38;
+            z[2]     = (int)t & M25; t >>= 25;
+            t       += a3 + (c8 - a8) * 38;
+            z[3]     = (int)t & M26; t >>= 26;
+            //t       += a4 - a9 * 38;
+            t       += a4 + b4 * 38;
+            z[4]     = (int)t & M25; t >>= 25;
+            t       += a5 + (c0 - a0);
+            z[5]     = (int)t & M26; t >>= 26;
+            t       += a6 + (c1 - a1);
+            z[6]     = (int)t & M26; t >>= 26;
+            t       += a7 + (c2 - a2);
+            z[7]     = (int)t & M25; t >>= 25;
+            t       += z8;
+            z[8]     = (int)t & M26; t >>= 26;
+            z[9]     = z9 + (int)t;
+        }
+
+        public static void Normalize(int[] z)
+        {
+            int x = (z[9] >> 23) & 1;
+            Reduce(z, x);
+            Reduce(z, -x);
+            Debug.Assert(z[9] >> 24 == 0);
+        }
+
+        private static void Reduce(int[] z, int c)
+        {
+            int z9 = z[9], t = z9;
+                       z9   = t & M24; t >>= 24;
+            t += c;
+            t *= 19;
+            t += z[0]; z[0] = t & M26; t >>= 26;
+            t += z[1]; z[1] = t & M26; t >>= 26;
+            t += z[2]; z[2] = t & M25; t >>= 25;
+            t += z[3]; z[3] = t & M26; t >>= 26;
+            t += z[4]; z[4] = t & M25; t >>= 25;
+            t += z[5]; z[5] = t & M26; t >>= 26;
+            t += z[6]; z[6] = t & M26; t >>= 26;
+            t += z[7]; z[7] = t & M25; t >>= 25;
+            t += z[8]; z[8] = t & M26; t >>= 26;
+            t += z9;   z[9] = t;
+        }
+
+        public static void Sqr(int[] x, int[] z)
+        {
+            int x0 = x[0];
+            int x1 = x[1];
+            int x2 = x[2];
+            int x3 = x[3];
+            int x4 = x[4];
+
+            int u0 = x[5];
+            int u1 = x[6];
+            int u2 = x[7];
+            int u3 = x[8];
+            int u4 = x[9];
+
+            int x1_2 = x1 * 2;
+            int x2_2 = x2 * 2;
+            int x3_2 = x3 * 2;
+            int x4_2 = x4 * 2;
+
+            long a0  = (long)x0 * x0;
+            long a1  = (long)x0 * x1_2;
+            long a2  = (long)x0 * x2_2
+                     + (long)x1 * x1;
+            long a3  = (long)x1_2 * x2_2
+                     + (long)x0 * x3_2;
+            long a4  = (long)x2 * x2_2
+                     + (long)x0 * x4_2
+                     + (long)x1 * x3_2;
+            long a5  = (long)x1_2 * x4_2
+                     + (long)x2_2 * x3_2;
+            long a6  = (long)x2_2 * x4_2
+                     + (long)x3 * x3;
+            long a7  = (long)x3 * x4_2;
+            long a8  = (long)x4 * x4_2;
+
+            int u1_2 = u1 * 2;
+            int u2_2 = u2 * 2;
+            int u3_2 = u3 * 2;
+            int u4_2 = u4 * 2;
+        
+            long b0  = (long)u0 * u0;
+            long b1  = (long)u0 * u1_2;
+            long b2  = (long)u0 * u2_2
+                     + (long)u1 * u1;
+            long b3  = (long)u1_2 * u2_2
+                     + (long)u0 * u3_2;
+            long b4  = (long)u2 * u2_2
+                     + (long)u0 * u4_2
+                     + (long)u1 * u3_2;
+            long b5  = (long)u1_2 * u4_2
+                     + (long)u2_2 * u3_2;
+            long b6  = (long)u2_2 * u4_2
+                     + (long)u3 * u3;
+            long b7  = (long)u3 * u4_2;
+            long b8  = (long)u4 * u4_2;
+
+            a0 -= b5 * 38;
+            a1 -= b6 * 38;
+            a2 -= b7 * 38;
+            a3 -= b8 * 38;
+
+            a5 -= b0;
+            a6 -= b1;
+            a7 -= b2;
+            a8 -= b3;
+            //long a9 = -b4;
+
+            x0 += u0;
+            x1 += u1;
+            x2 += u2;
+            x3 += u3;
+            x4 += u4;
+
+            x1_2 = x1 * 2;
+            x2_2 = x2 * 2;
+            x3_2 = x3 * 2;
+            x4_2 = x4 * 2;
+
+            long c0  = (long)x0 * x0;
+            long c1  = (long)x0 * x1_2;
+            long c2  = (long)x0 * x2_2
+                     + (long)x1 * x1;
+            long c3  = (long)x1_2 * x2_2
+                     + (long)x0 * x3_2;
+            long c4  = (long)x2 * x2_2
+                     + (long)x0 * x4_2
+                     + (long)x1 * x3_2;
+            long c5  = (long)x1_2 * x4_2
+                     + (long)x2_2 * x3_2;
+            long c6  = (long)x2_2 * x4_2
+                     + (long)x3 * x3;
+            long c7  = (long)x3 * x4_2;
+            long c8  = (long)x4 * x4_2;
+
+            int z8, z9;
+            long t;
+
+            t        = a8 + (c3 - a3);
+            z8       = (int)t & M26; t >>= 26;
+            //t       += a9 + (c4 - a4);
+            t       +=      (c4 - a4) - b4;
+            //z9       = (int)t & M24; t >>= 24;
+            //t        = a0 + (t + ((c5 - a5) << 1)) * 19;
+            z9       = (int)t & M25; t >>= 25;
+            t        = a0 + (t + c5 - a5) * 38;
+            z[0]     = (int)t & M26; t >>= 26;
+            t       += a1 + (c6 - a6) * 38;
+            z[1]     = (int)t & M26; t >>= 26;
+            t       += a2 + (c7 - a7) * 38;
+            z[2]     = (int)t & M25; t >>= 25;
+            t       += a3 + (c8 - a8) * 38;
+            z[3]     = (int)t & M26; t >>= 26;
+            //t       += a4 - a9 * 38;
+            t       += a4 + b4 * 38;
+            z[4]     = (int)t & M25; t >>= 25;
+            t       += a5 + (c0 - a0);
+            z[5]     = (int)t & M26; t >>= 26;
+            t       += a6 + (c1 - a1);
+            z[6]     = (int)t & M26; t >>= 26;
+            t       += a7 + (c2 - a2);
+            z[7]     = (int)t & M25; t >>= 25;
+            t       += z8;
+            z[8]     = (int)t & M26; t >>= 26;
+            z[9]     = z9 + (int)t;
+        }
+
+        public static void Sqr(int[] x, int n, int[] z)
+        {
+            Debug.Assert(n > 0);
+
+            Sqr(x, z);
+
+            while (--n > 0)
+            {
+                Sqr(z, z);
+            }
+        }
+
+        public static void Sub(int[] x, int[] y, int[] z)
+        {
+            for (int i = 0; i < Size; ++i)
+            {
+                z[i] = x[i] - y[i];
+            }
+        }
+    }
+}
diff --git a/crypto/src/math/ec/rfc7748/X448.cs b/crypto/src/math/ec/rfc7748/X448.cs
new file mode 100644
index 000000000..32a4a9e2a
--- /dev/null
+++ b/crypto/src/math/ec/rfc7748/X448.cs
@@ -0,0 +1,255 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Org.BouncyCastle.Math.EC.Rfc7748
+{
+    public abstract class X448
+    {
+        private const uint C_A = 156326;
+        private const uint C_A24 = (C_A + 2)/4;
+
+        // 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+        private static readonly uint[] S_x = new uint[]{ 0x0FFFFFFEU, 0x0FFFFFFFU, 0x0FFFFFFFU, 0x0FFFFFFFU, 0x0FFFFFFFU, 0x0FFFFFFFU,
+            0x0FFFFFFFU, 0x0FFFFFFFU, 0x0FFFFFFEU, 0x0FFFFFFFU, 0x0FFFFFFFU, 0x0FFFFFFFU, 0x0FFFFFFFU, 0x0FFFFFFFU, 0x0FFFFFFFU,
+            0x0FFFFFFFU };
+
+        // 0xF0FAB725013244423ACF03881AFFEB7BDACDD1031C81B9672954459D84C1F823F1BD65643ACE1B5123AC33FF1C69BAF8ACB1197DC99D2720
+        private static readonly uint[] PsubS_x = new uint[]{ 0x099d2720U, 0x0b1197dcU, 0x09baf8acU, 0x033ff1c6U, 0x0b5123acU,
+            0x0643ace1U, 0x03f1bd65U, 0x084c1f82U, 0x0954459dU, 0x081b9672U, 0x0dd1031cU, 0x0eb7bdacU, 0x03881affU, 0x0423acf0U,
+            0x05013244U, 0x0f0fab72U };
+
+        private static uint[] precompBase = null;
+
+        private static uint Decode32(byte[] bs, int off)
+        {
+            uint n = bs[off];
+            n |= (uint)bs[++off] << 8;
+            n |= (uint)bs[++off] << 16;
+            n |= (uint)bs[++off] << 24;
+            return n;
+        }
+
+        private static void DecodeScalar(byte[] k, int kOff, uint[] n)
+        {
+            for (int i = 0; i < 14; ++i)
+            {
+                n[i] = Decode32(k, kOff + i * 4);
+            }
+
+            n[ 0] &= 0xFFFFFFFCU;
+            n[13] |= 0x80000000U;
+        }
+
+        private static void PointDouble(uint[] x, uint[] z)
+        {
+            uint[] A = X448Field.Create();
+            uint[] B = X448Field.Create();
+
+            //X448Field.Apm(x, z, A, B);
+            X448Field.Add(x, z, A);
+            X448Field.Sub(x, z, B);
+            X448Field.Sqr(A, A);
+            X448Field.Sqr(B, B);
+            X448Field.Mul(A, B, x);
+            X448Field.Sub(A, B, A);
+            X448Field.Mul(A, C_A24, z);
+            X448Field.Add(z, B, z);
+            X448Field.Mul(z, A, z);
+        }
+
+        [MethodImpl(MethodImplOptions.Synchronized)]
+        public static void Precompute()
+        {
+            if (precompBase != null)
+                return;
+
+            precompBase = new uint[X448Field.Size * 446];
+
+            uint[] xs = precompBase;
+            uint[] zs = new uint[X448Field.Size * 445];
+
+            uint[] x = X448Field.Create();     x[0] = 5;          
+            uint[] z = X448Field.Create();     z[0] = 1;
+
+            uint[] n = X448Field.Create();
+            uint[] d = X448Field.Create();
+
+            //X448Field.Apm(x, z, n, d);
+            X448Field.Add(x, z, n);
+            X448Field.Sub(x, z, d);
+
+            uint[] c = X448Field.Create();     X448Field.Copy(d, 0, c, 0);
+
+            int off = 0;
+            for (;;)
+            {
+                X448Field.Copy(n, 0, xs, off);
+
+                if (off == (X448Field.Size * 445))
+                    break;
+
+                PointDouble(x, z);
+
+                //X448Field.Apm(x, z, n, d);
+                X448Field.Add(x, z, n);
+                X448Field.Sub(x, z, d);
+                X448Field.Mul(n, c, n);
+                X448Field.Mul(c, d, c);
+
+                X448Field.Copy(d, 0, zs, off);
+
+                off += X448Field.Size;
+            }
+
+            uint[] u = X448Field.Create();
+            X448Field.Inv(c, u);
+
+            for (;;)
+            {
+                X448Field.Copy(xs, off, x, 0);
+
+                X448Field.Mul(x, u, x);
+                //X448Field.Normalize(x);
+                X448Field.Copy(x, 0, precompBase, off);
+
+                if (off == 0)
+                    break;
+
+                off -= X448Field.Size;
+                X448Field.Copy(zs, off, z, 0);
+                X448Field.Mul(u, z, u);
+            }
+        }
+
+        public static void ScalarMult(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff)
+        {
+            uint[] n = new uint[14];    DecodeScalar(k, kOff, n);
+
+            uint[] x1 = X448Field.Create();     X448Field.Decode(u, uOff, x1);
+            uint[] x2 = X448Field.Create();     X448Field.Copy(x1, 0, x2, 0);
+            uint[] z2 = X448Field.Create();     z2[0] = 1;
+            uint[] x3 = X448Field.Create();     x3[0] = 1;
+            uint[] z3 = X448Field.Create();
+
+            uint[] t1 = X448Field.Create();
+            uint[] t2 = X448Field.Create();
+
+            Debug.Assert(n[13] >> 31 == 1U);
+
+            int bit = 447, swap = 1;
+            do
+            {
+                //X448Field.Apm(x3, z3, t1, x3);
+                X448Field.Add(x3, z3, t1);
+                X448Field.Sub(x3, z3, x3);
+                //X448Field.Apm(x2, z2, z3, x2);
+                X448Field.Add(x2, z2, z3);
+                X448Field.Sub(x2, z2, x2);
+
+                X448Field.Mul(t1, x2, t1);
+                X448Field.Mul(x3, z3, x3);
+                X448Field.Sqr(z3, z3);
+                X448Field.Sqr(x2, x2);
+
+                X448Field.Sub(z3, x2, t2);
+                X448Field.Mul(t2, C_A24, z2);
+                X448Field.Add(z2, x2, z2);
+                X448Field.Mul(z2, t2, z2);
+                X448Field.Mul(x2, z3, x2);
+
+                //X448Field.Apm(t1, x3, x3, z3);
+                X448Field.Sub(t1, x3, z3);
+                X448Field.Add(t1, x3, x3);
+                X448Field.Sqr(x3, x3);
+                X448Field.Sqr(z3, z3);
+                X448Field.Mul(z3, x1, z3);
+
+                --bit;
+
+                int word = bit >> 5, shift = bit & 0x1F;
+                int kt = (int)(n[word] >> shift) & 1;
+                swap ^= kt;
+                X448Field.CSwap(swap, x2, x3);
+                X448Field.CSwap(swap, z2, z3);
+                swap = kt;
+            }
+            while (bit >= 2);
+
+            Debug.Assert(swap == 0);
+
+            for (int i = 0; i < 2; ++i)
+            {
+                PointDouble(x2, z2);
+            }
+
+            X448Field.Inv(z2, z2);
+            X448Field.Mul(x2, z2, x2);
+
+            X448Field.Normalize(x2);
+            X448Field.Encode(x2, r, rOff);
+        }
+
+        public static void ScalarMultBase(byte[] k, int kOff, byte[] r, int rOff)
+        {
+            Precompute();
+
+            uint[] n = new uint[14];    DecodeScalar(k, kOff, n);
+
+            uint[] x0 = X448Field.Create();
+            uint[] x1 = X448Field.Create();     X448Field.Copy(S_x, 0, x1, 0);
+            uint[] z1 = X448Field.Create();     z1[0] = 1;        
+            uint[] x2 = X448Field.Create();     X448Field.Copy(PsubS_x, 0, x2, 0);
+            uint[] z2 = X448Field.Create();     z2[0] = 1;
+
+            uint[] A = X448Field.Create();
+            uint[] B = z1;
+            uint[] C = x0;
+            uint[] D = x1;
+            uint[] E = B;
+
+            Debug.Assert(n[13] >> 31 == 1U);
+
+            int off = 0, bit = 2, swap = 1;
+            do
+            {
+                X448Field.Copy(precompBase, off, x0, 0);
+                off += X448Field.Size;
+
+                int word = bit >> 5, shift = bit & 0x1F;
+                int kt = (int)(n[word] >> shift) & 1;
+                swap ^= kt;
+                X448Field.CSwap(swap, x1, x2);
+                X448Field.CSwap(swap, z1, z2);
+                swap = kt;
+
+                //X448Field.Apm(x1, z1, A, B);
+                X448Field.Add(x1, z1, A);
+                X448Field.Sub(x1, z1, B);
+                X448Field.Mul(x0, B, C);
+                X448Field.Carry(A);
+                //X448Field.Apm(A, C, D, E);
+                X448Field.Add(A, C, D);
+                X448Field.Sub(A, C, E);
+                X448Field.Sqr(D, D);
+                X448Field.Sqr(E, E);
+                X448Field.Mul(z2, D, x1);
+                X448Field.Mul(x2, E, z1);
+            }
+            while (++bit < 448);
+
+            Debug.Assert(swap == 1);
+
+            for (int i = 0; i < 2; ++i)
+            {
+                PointDouble(x1, z1);
+            }
+
+            X448Field.Inv(z1, z1);
+            X448Field.Mul(x1, z1, x1);
+
+            X448Field.Normalize(x1);
+            X448Field.Encode(x1, r, rOff);
+        }
+    }
+}
diff --git a/crypto/src/math/ec/rfc7748/X448Field.cs b/crypto/src/math/ec/rfc7748/X448Field.cs
new file mode 100644
index 000000000..0c44f1eb5
--- /dev/null
+++ b/crypto/src/math/ec/rfc7748/X448Field.cs
@@ -0,0 +1,904 @@
+using System;
+using System.Diagnostics;
+
+namespace Org.BouncyCastle.Math.EC.Rfc7748
+{
+    [CLSCompliantAttribute(false)]
+    public abstract class X448Field
+    {
+        public const int Size = 16;
+
+        private const uint M28 = 0x0FFFFFFFU;
+
+        private X448Field() {}
+
+        public static void Add(uint[] x, uint[] y, uint[] z)
+        {
+            for (int i = 0; i < Size; ++i)
+            {
+                z[i] = x[i] + y[i];
+            }
+        }
+
+        //public static void Apm(int[] x, int[] y, int[] zp, int[] zm)
+        //{
+        //    for (int i = 0; i < Size; ++i)
+        //    {
+        //        int xi = x[i], yi = y[i];
+        //        zp[i] = xi + yi;
+        //        zm[i] = xi - yi;
+        //    }
+        //}
+
+        public static void Carry(uint[] z)
+        {
+            uint z0 = z[0], z1 = z[1], z2 = z[2], z3 = z[3], z4 = z[4], z5 = z[5], z6 = z[6], z7 = z[7];
+            uint z8 = z[8], z9 = z[9], z10 = z[10], z11 = z[11], z12 = z[12], z13 = z[13], z14 = z[14], z15 = z[15];
+
+            z2   += (z1 >> 28); z1 &= M28;
+            z6   += (z5 >> 28); z5 &= M28;
+            z10  += (z9 >> 28); z9 &= M28;
+            z14  += (z13 >> 28); z13 &= M28;
+
+            z3   += (z2 >> 28); z2 &= M28;
+            z7   += (z6 >> 28); z6 &= M28;
+            z11  += (z10 >> 28); z10 &= M28;
+            z15  += (z14 >> 28); z14 &= M28;
+
+            uint t = z15 >> 28; z15 &= M28;
+            z0   += t;
+            z8   += t;
+
+            z4   += (z3 >> 28); z3 &= M28;
+            z8   += (z7 >> 28); z7 &= M28;
+            z12  += (z11 >> 28); z11 &= M28;
+
+            z1   += (z0 >> 28); z0 &= M28;
+            z5   += (z4 >> 28); z4 &= M28;
+            z9   += (z8 >> 28); z8 &= M28;
+            z13  += (z12 >> 28); z12 &= M28;
+
+            z[0] = z0; z[1] = z1; z[2] = z2; z[3] = z3; z[4] = z4; z[5] = z5; z[6] = z6; z[7] = z7;
+            z[8] = z8; z[9] = z9; z[10] = z10; z[11] = z11; z[12] = z12; z[13] = z13; z[14] = z14; z[15] = z15;
+        }
+
+        public static void Copy(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            for (int i = 0; i < Size; ++i)
+            {
+                z[zOff + i] = x[xOff + i];
+            }
+        }
+
+        public static uint[] Create()
+        {
+            return new uint[Size];
+        }
+
+        public static void CSwap(int swap, uint[] a, uint[] b)
+        {
+            Debug.Assert(swap >> 1 == 0);
+            Debug.Assert(a != b);
+
+            uint mask = (uint)(0 - swap);
+            for (int i = 0; i < Size; ++i)
+            {
+                uint ai = a[i], bi = b[i];
+                uint dummy = mask & (ai ^ bi);
+                a[i] = ai ^ dummy; 
+                b[i] = bi ^ dummy; 
+            }
+        }
+
+        public static void Decode(byte[] x, int xOff, uint[] z)
+        {
+            Decode56(x, xOff, z, 0);
+            Decode56(x, xOff + 7, z, 2);
+            Decode56(x, xOff + 14, z, 4);
+            Decode56(x, xOff + 21, z, 6);
+            Decode56(x, xOff + 28, z, 8);
+            Decode56(x, xOff + 35, z, 10);
+            Decode56(x, xOff + 42, z, 12);
+            Decode56(x, xOff + 49, z, 14);
+        }
+
+        private static uint Decode24(byte[] bs, int off)
+        {
+            uint n = bs[off];
+            n |= (uint)bs[++off] << 8;
+            n |= (uint)bs[++off] << 16;
+            return n;
+        }
+
+        private static uint Decode32(byte[] bs, int off)
+        {
+            uint n = bs[off];
+            n |= (uint)bs[++off] << 8;
+            n |= (uint)bs[++off] << 16;
+            n |= (uint)bs[++off] << 24;
+            return n;
+        }
+
+        private static void Decode56(byte[] bs, int off, uint[] z, int zOff)
+        {
+            uint lo = Decode32(bs, off);
+            uint hi = Decode24(bs, off + 4);
+            z[zOff] = lo & M28;
+            z[zOff + 1] = (lo >> 28) | (hi << 4);
+        }
+
+        public static void Encode(uint[] x, byte[] z, int zOff)
+        {
+            Encode56(x, 0, z, zOff);
+            Encode56(x, 2, z, zOff + 7);
+            Encode56(x, 4, z, zOff + 14);
+            Encode56(x, 6, z, zOff + 21);
+            Encode56(x, 8, z, zOff + 28);
+            Encode56(x, 10, z, zOff + 35);
+            Encode56(x, 12, z, zOff + 42);
+            Encode56(x, 14, z, zOff + 49);
+        }
+
+        private static void Encode24(uint n, byte[] bs, int off)
+        {
+            bs[  off] = (byte)(n      );
+            bs[++off] = (byte)(n >>  8);
+            bs[++off] = (byte)(n >> 16);
+        }
+
+        private static void Encode32(uint n, byte[] bs, int off)
+        {
+            bs[  off] = (byte)(n      );
+            bs[++off] = (byte)(n >>  8);
+            bs[++off] = (byte)(n >> 16);
+            bs[++off] = (byte)(n >> 24);
+        }
+
+        private static void Encode56(uint[] x, int xOff, byte[] bs, int off)
+        {
+            uint lo = x[xOff], hi = x[xOff + 1];
+            Encode32(lo | (hi << 28), bs, off);
+            Encode24(hi >> 4, bs, off + 4);
+        }
+
+        public static void Inv(uint[] x, uint[] z)
+        {
+            // z = x^(p-2) = x^(2^448 - 2^224 - 3)
+            // (223 1s) (1 0s) (222 1s) (1 0s) (1 1s)
+            // Addition chain: [1] 2 3 6 9 18 19 37 74 111 [222] [223]
+            uint[] x2 = Create();    Sqr(x, x2);             Mul(x, x2, x2);
+            uint[] x3 = Create();    Sqr(x2, x3);            Mul(x, x3, x3);
+            uint[] x6 = Create();    Sqr(x3, 3, x6);         Mul(x3, x6, x6);
+            uint[] x9 = Create();    Sqr(x6, 3, x9);         Mul(x3, x9, x9);
+            uint[] x18 = Create();   Sqr(x9, 9, x18);        Mul(x9, x18, x18);
+            uint[] x19 = Create();   Sqr(x18, x19);          Mul(x, x19, x19);
+            uint[] x37 = Create();   Sqr(x19, 18, x37);      Mul(x18, x37, x37);
+            uint[] x74 = Create();   Sqr(x37, 37, x74);      Mul(x37, x74, x74);
+            uint[] x111 = Create();  Sqr(x74, 37, x111);     Mul(x37, x111, x111);
+            uint[] x222 = Create();  Sqr(x111, 111, x222);   Mul(x111, x222, x222);
+            uint[] x223 = Create();  Sqr(x222, x223);        Mul(x, x223, x223);
+
+            uint[] t = Create();
+            Sqr(x223, 223, t);
+            Mul(t, x222, t);
+            Sqr(t, 2, t);
+            Mul(t, x, z);
+        }
+
+        public static void Mul(uint[] x, uint y, uint[] z)
+        {
+            uint x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7];
+            uint x8 = x[8], x9 = x[9], x10 = x[10], x11 = x[11], x12 = x[12], x13 = x[13], x14 = x[14], x15 = x[15];
+
+            uint z1, z5, z9, z13;
+            ulong c, d, e, f;
+
+            c     = (ulong)x1 * y;
+            z1    = (uint)c & M28; c >>= 28;
+            d     = (ulong)x5 * y;
+            z5    = (uint)d & M28; d >>= 28;
+            e     = (ulong)x9 * y;
+            z9    = (uint)e & M28; e >>= 28;
+            f     = (ulong)x13 * y;
+            z13   = (uint)f & M28; f >>= 28;
+
+            c    += (ulong)x2 * y;
+            z[2]  = (uint)c & M28; c >>= 28;
+            d    += (ulong)x6 * y;
+            z[6]  = (uint)d & M28; d >>= 28;
+            e    += (ulong)x10 * y;
+            z[10] = (uint)e & M28; e >>= 28;
+            f    += (ulong)x14 * y;
+            z[14] = (uint)f & M28; f >>= 28;
+
+            c    += (ulong)x3 * y;
+            z[3]  = (uint)c & M28; c >>= 28;
+            d    += (ulong)x7 * y;
+            z[7]  = (uint)d & M28; d >>= 28;
+            e    += (ulong)x11 * y;
+            z[11] = (uint)e & M28; e >>= 28;
+            f    += (ulong)x15 * y;
+            z[15] = (uint)f & M28; f >>= 28;
+
+            d    += f;
+
+            c    += (ulong)x4 * y;
+            z[4]  = (uint)c & M28; c >>= 28;
+            d    += (ulong)x8 * y;
+            z[8]  = (uint)d & M28; d >>= 28;
+            e    += (ulong)x12 * y;
+            z[12] = (uint)e & M28; e >>= 28;
+            f    += (ulong)x0 * y;
+            z[0]  = (uint)f & M28; f >>= 28;
+
+            z[1]  = z1 + (uint)f;
+            z[5]  = z5 + (uint)c;
+            z[9]  = z9 + (uint)d;
+            z[13] = z13 + (uint)e;
+        }
+
+        public static void Mul(uint[] x, uint[] y, uint[] z)
+        {
+            uint x0 = x[0];
+            uint x1 = x[1];
+            uint x2 = x[2];
+            uint x3 = x[3];
+            uint x4 = x[4];
+            uint x5 = x[5];
+            uint x6 = x[6];
+            uint x7 = x[7];
+
+            uint u0 = x[8];
+            uint u1 = x[9];
+            uint u2 = x[10];
+            uint u3 = x[11];
+            uint u4 = x[12];
+            uint u5 = x[13];
+            uint u6 = x[14];
+            uint u7 = x[15];
+
+            uint y0 = y[0];
+            uint y1 = y[1];
+            uint y2 = y[2];
+            uint y3 = y[3];
+            uint y4 = y[4];
+            uint y5 = y[5];
+            uint y6 = y[6];
+            uint y7 = y[7];
+
+            uint v0 = y[8];
+            uint v1 = y[9];
+            uint v2 = y[10];
+            uint v3 = y[11];
+            uint v4 = y[12];
+            uint v5 = y[13];
+            uint v6 = y[14];
+            uint v7 = y[15];
+
+            uint s0 = x0 + u0;
+            uint s1 = x1 + u1;
+            uint s2 = x2 + u2;
+            uint s3 = x3 + u3;
+            uint s4 = x4 + u4;
+            uint s5 = x5 + u5;
+            uint s6 = x6 + u6;
+            uint s7 = x7 + u7;
+
+            uint t0 = y0 + v0;
+            uint t1 = y1 + v1;
+            uint t2 = y2 + v2;
+            uint t3 = y3 + v3;
+            uint t4 = y4 + v4;
+            uint t5 = y5 + v5;
+            uint t6 = y6 + v6;
+            uint t7 = y7 + v7;
+
+            uint z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13, z14, z15;
+            ulong c, d;
+
+            ulong f0  = (ulong)x0 * y0;
+            ulong f8  = (ulong)x7 * y1
+                      + (ulong)x6 * y2
+                      + (ulong)x5 * y3
+                      + (ulong)x4 * y4
+                      + (ulong)x3 * y5
+                      + (ulong)x2 * y6
+                      + (ulong)x1 * y7;
+            ulong g0  = (ulong)u0 * v0;
+            ulong g8  = (ulong)u7 * v1
+                      + (ulong)u6 * v2
+                      + (ulong)u5 * v3
+                      + (ulong)u4 * v4
+                      + (ulong)u3 * v5
+                      + (ulong)u2 * v6
+                      + (ulong)u1 * v7;
+            ulong h0  = (ulong)s0 * t0;
+            ulong h8  = (ulong)s7 * t1
+                      + (ulong)s6 * t2
+                      + (ulong)s5 * t3
+                      + (ulong)s4 * t4
+                      + (ulong)s3 * t5
+                      + (ulong)s2 * t6
+                      + (ulong)s1 * t7;
+
+            c         = f0 + g0 + h8 - f8;
+            z0        = (uint)c & M28; c >>= 28;
+            d         = g8 + h0 - f0 + h8;
+            z8        = (uint)d & M28; d >>= 28;
+
+            ulong f1  = (ulong)x1 * y0
+                      + (ulong)x0 * y1;
+            ulong f9  = (ulong)x7 * y2
+                      + (ulong)x6 * y3
+                      + (ulong)x5 * y4
+                      + (ulong)x4 * y5
+                      + (ulong)x3 * y6
+                      + (ulong)x2 * y7;
+            ulong g1  = (ulong)u1 * v0
+                      + (ulong)u0 * v1;
+            ulong g9  = (ulong)u7 * v2
+                      + (ulong)u6 * v3
+                      + (ulong)u5 * v4
+                      + (ulong)u4 * v5
+                      + (ulong)u3 * v6
+                      + (ulong)u2 * v7;
+            ulong h1  = (ulong)s1 * t0
+                      + (ulong)s0 * t1;
+            ulong h9  = (ulong)s7 * t2
+                      + (ulong)s6 * t3
+                      + (ulong)s5 * t4
+                      + (ulong)s4 * t5
+                      + (ulong)s3 * t6
+                      + (ulong)s2 * t7;
+
+            c        += f1 + g1 + h9 - f9;
+            z1        = (uint)c & M28; c >>= 28;
+            d        += g9 + h1 - f1 + h9;
+            z9        = (uint)d & M28; d >>= 28;
+
+            ulong f2  = (ulong)x2 * y0
+                      + (ulong)x1 * y1
+                      + (ulong)x0 * y2;
+            ulong f10 = (ulong)x7 * y3
+                      + (ulong)x6 * y4
+                      + (ulong)x5 * y5
+                      + (ulong)x4 * y6
+                      + (ulong)x3 * y7;
+            ulong g2  = (ulong)u2 * v0
+                      + (ulong)u1 * v1
+                      + (ulong)u0 * v2;
+            ulong g10 = (ulong)u7 * v3
+                      + (ulong)u6 * v4
+                      + (ulong)u5 * v5
+                      + (ulong)u4 * v6
+                      + (ulong)u3 * v7;
+            ulong h2  = (ulong)s2 * t0
+                      + (ulong)s1 * t1
+                      + (ulong)s0 * t2;
+            ulong h10 = (ulong)s7 * t3
+                      + (ulong)s6 * t4
+                      + (ulong)s5 * t5
+                      + (ulong)s4 * t6
+                      + (ulong)s3 * t7;
+
+            c        += f2 + g2 + h10 - f10;
+            z2        = (uint)c & M28; c >>= 28;
+            d        += g10 + h2 - f2 + h10;
+            z10       = (uint)d & M28; d >>= 28;
+
+            ulong f3  = (ulong)x3 * y0
+                      + (ulong)x2 * y1
+                      + (ulong)x1 * y2
+                      + (ulong)x0 * y3;
+            ulong f11 = (ulong)x7 * y4
+                      + (ulong)x6 * y5
+                      + (ulong)x5 * y6
+                      + (ulong)x4 * y7;
+            ulong g3  = (ulong)u3 * v0
+                      + (ulong)u2 * v1
+                      + (ulong)u1 * v2
+                      + (ulong)u0 * v3;
+            ulong g11 = (ulong)u7 * v4
+                      + (ulong)u6 * v5
+                      + (ulong)u5 * v6
+                      + (ulong)u4 * v7;
+            ulong h3  = (ulong)s3 * t0
+                      + (ulong)s2 * t1
+                      + (ulong)s1 * t2
+                      + (ulong)s0 * t3;
+            ulong h11 = (ulong)s7 * t4
+                      + (ulong)s6 * t5
+                      + (ulong)s5 * t6
+                      + (ulong)s4 * t7;
+
+            c        += f3 + g3 + h11 - f11;
+            z3        = (uint)c & M28; c >>= 28;
+            d        += g11 + h3 - f3 + h11;
+            z11       = (uint)d & M28; d >>= 28;
+
+            ulong f4  = (ulong)x4 * y0
+                      + (ulong)x3 * y1
+                      + (ulong)x2 * y2
+                      + (ulong)x1 * y3
+                      + (ulong)x0 * y4;
+            ulong f12 = (ulong)x7 * y5
+                      + (ulong)x6 * y6
+                      + (ulong)x5 * y7;
+            ulong g4  = (ulong)u4 * v0
+                      + (ulong)u3 * v1
+                      + (ulong)u2 * v2
+                      + (ulong)u1 * v3
+                      + (ulong)u0 * v4;
+            ulong g12 = (ulong)u7 * v5
+                      + (ulong)u6 * v6
+                      + (ulong)u5 * v7;
+            ulong h4  = (ulong)s4 * t0
+                      + (ulong)s3 * t1
+                      + (ulong)s2 * t2
+                      + (ulong)s1 * t3
+                      + (ulong)s0 * t4;
+            ulong h12 = (ulong)s7 * t5
+                      + (ulong)s6 * t6
+                      + (ulong)s5 * t7;
+
+            c        += f4 + g4 + h12 - f12;
+            z4        = (uint)c & M28; c >>= 28;
+            d        += g12 + h4 - f4 + h12;
+            z12       = (uint)d & M28; d >>= 28;
+
+            ulong f5  = (ulong)x5 * y0
+                      + (ulong)x4 * y1
+                      + (ulong)x3 * y2
+                      + (ulong)x2 * y3
+                      + (ulong)x1 * y4
+                      + (ulong)x0 * y5;
+            ulong f13 = (ulong)x7 * y6
+                      + (ulong)x6 * y7;
+            ulong g5  = (ulong)u5 * v0
+                      + (ulong)u4 * v1
+                      + (ulong)u3 * v2
+                      + (ulong)u2 * v3
+                      + (ulong)u1 * v4
+                      + (ulong)u0 * v5;
+            ulong g13 = (ulong)u7 * v6
+                      + (ulong)u6 * v7;
+            ulong h5  = (ulong)s5 * t0
+                      + (ulong)s4 * t1
+                      + (ulong)s3 * t2
+                      + (ulong)s2 * t3
+                      + (ulong)s1 * t4
+                      + (ulong)s0 * t5;
+            ulong h13 = (ulong)s7 * t6
+                      + (ulong)s6 * t7;
+
+            c        += f5 + g5 + h13 - f13;
+            z5        = (uint)c & M28; c >>= 28;
+            d        += g13 + h5 - f5 + h13;
+            z13       = (uint)d & M28; d >>= 28;
+
+            ulong f6  = (ulong)x6 * y0
+                      + (ulong)x5 * y1
+                      + (ulong)x4 * y2
+                      + (ulong)x3 * y3
+                      + (ulong)x2 * y4
+                      + (ulong)x1 * y5
+                      + (ulong)x0 * y6;
+            ulong f14 = (ulong)x7 * y7;
+            ulong g6  = (ulong)u6 * v0
+                      + (ulong)u5 * v1
+                      + (ulong)u4 * v2
+                      + (ulong)u3 * v3
+                      + (ulong)u2 * v4
+                      + (ulong)u1 * v5
+                      + (ulong)u0 * v6;
+            ulong g14 = (ulong)u7 * v7;
+            ulong h6  = (ulong)s6 * t0
+                      + (ulong)s5 * t1
+                      + (ulong)s4 * t2
+                      + (ulong)s3 * t3
+                      + (ulong)s2 * t4
+                      + (ulong)s1 * t5
+                      + (ulong)s0 * t6;
+            ulong h14 = (ulong)s7 * t7;
+
+            c        += f6 + g6 + h14 - f14;
+            z6        = (uint)c & M28; c >>= 28;
+            d        += g14 + h6 - f6 + h14;
+            z14       = (uint)d & M28; d >>= 28;
+
+            ulong f7  = (ulong)x7 * y0
+                      + (ulong)x6 * y1
+                      + (ulong)x5 * y2
+                      + (ulong)x4 * y3
+                      + (ulong)x3 * y4
+                      + (ulong)x2 * y5
+                      + (ulong)x1 * y6
+                      + (ulong)x0 * y7;
+            ulong g7  = (ulong)u7 * v0
+                      + (ulong)u6 * v1
+                      + (ulong)u5 * v2
+                      + (ulong)u4 * v3
+                      + (ulong)u3 * v4
+                      + (ulong)u2 * v5
+                      + (ulong)u1 * v6
+                      + (ulong)u0 * v7;
+            ulong h7  = (ulong)s7 * t0
+                      + (ulong)s6 * t1
+                      + (ulong)s5 * t2
+                      + (ulong)s4 * t3
+                      + (ulong)s3 * t4
+                      + (ulong)s2 * t5
+                      + (ulong)s1 * t6
+                      + (ulong)s0 * t7;
+
+            c        += f7 + g7;
+            z7        = (uint)c & M28; c >>= 28;
+            d        += h7 - f7;
+            z15       = (uint)d & M28; d >>= 28;
+
+            c        += d;
+
+            c        += z8;
+            z8        = (uint)c & M28; c >>= 28;
+            d        += z0;
+            z0        = (uint)d & M28; d >>= 28;
+            z9       += (uint)c;
+            z1       += (uint)d;
+
+            z[0] = z0;
+            z[1] = z1;
+            z[2] = z2;
+            z[3] = z3;
+            z[4] = z4;
+            z[5] = z5;
+            z[6] = z6;
+            z[7] = z7;
+            z[8] = z8;
+            z[9] = z9;
+            z[10] = z10;
+            z[11] = z11;
+            z[12] = z12;
+            z[13] = z13;
+            z[14] = z14;
+            z[15] = z15;
+        }
+
+        public static void Normalize(uint[] z)
+        {
+            //int x = (z[15] >> (28 - 1)) & 1;
+            Reduce(z, 1);
+            Reduce(z, -1);
+            Debug.Assert(z[15] >> 28 == 0U);
+        }
+
+        private static void Reduce(uint[] z, int c)
+        {
+            uint z15 = z[15];
+            long t = z15;
+            z15 &= M28;
+            t = (t >> 28) + c;
+            z[8] += (uint)t;
+            for (int i = 0; i < 15; ++i)
+            {
+                t += z[i]; z[i] = (uint)t & M28; t >>= 28;
+            }
+            z[15] = z15 + (uint)t;
+        }
+
+        public static void Sqr(uint[] x, uint[] z)
+        {
+            uint x0 = x[0];
+            uint x1 = x[1];
+            uint x2 = x[2];
+            uint x3 = x[3];
+            uint x4 = x[4];
+            uint x5 = x[5];
+            uint x6 = x[6];
+            uint x7 = x[7];
+
+            uint u0 = x[8];
+            uint u1 = x[9];
+            uint u2 = x[10];
+            uint u3 = x[11];
+            uint u4 = x[12];
+            uint u5 = x[13];
+            uint u6 = x[14];
+            uint u7 = x[15];
+
+            uint x0_2 = x0 * 2;
+            uint x1_2 = x1 * 2;
+            uint x2_2 = x2 * 2;
+            uint x3_2 = x3 * 2;
+            uint x4_2 = x4 * 2;
+            uint x5_2 = x5 * 2;
+            uint x6_2 = x6 * 2;
+
+            uint u0_2 = u0 * 2;
+            uint u1_2 = u1 * 2;
+            uint u2_2 = u2 * 2;
+            uint u3_2 = u3 * 2;
+            uint u4_2 = u4 * 2;
+            uint u5_2 = u5 * 2;
+            uint u6_2 = u6 * 2;
+        
+            uint s0 = x0 + u0;
+            uint s1 = x1 + u1;
+            uint s2 = x2 + u2;
+            uint s3 = x3 + u3;
+            uint s4 = x4 + u4;
+            uint s5 = x5 + u5;
+            uint s6 = x6 + u6;
+            uint s7 = x7 + u7;
+
+            uint s0_2 = s0 * 2;
+            uint s1_2 = s1 * 2;
+            uint s2_2 = s2 * 2;
+            uint s3_2 = s3 * 2;
+            uint s4_2 = s4 * 2;
+            uint s5_2 = s5 * 2;
+            uint s6_2 = s6 * 2;
+
+            uint z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13, z14, z15;
+            ulong c, d;
+
+            ulong f0  = (ulong)x0 * x0;
+            ulong f8  = (ulong)x7 * x1_2
+                      + (ulong)x6 * x2_2
+                      + (ulong)x5 * x3_2
+                      + (ulong)x4 * x4;
+            ulong g0  = (ulong)u0 * u0;
+            ulong g8  = (ulong)u7 * u1_2
+                      + (ulong)u6 * u2_2
+                      + (ulong)u5 * u3_2
+                      + (ulong)u4 * u4;
+            ulong h0  = (ulong)s0 * s0;
+            ulong h8  = (ulong)s7 * s1_2
+                      + (ulong)s6 * s2_2
+                      + (ulong)s5 * s3_2
+                      + (ulong)s4 * s4;
+
+            c         = f0 + g0 + h8 - f8;
+            z0        = (uint)c & M28; c >>= 28;
+            d         = g8 + h0 - f0 + h8;
+            z8        = (uint)d & M28; d >>= 28;
+
+            ulong f1  = (ulong)x1 * x0_2;
+            ulong f9  = (ulong)x7 * x2_2
+                      + (ulong)x6 * x3_2
+                      + (ulong)x5 * x4_2;
+            ulong g1  = (ulong)u1 * u0_2;
+            ulong g9  = (ulong)u7 * u2_2
+                      + (ulong)u6 * u3_2
+                      + (ulong)u5 * u4_2;
+            ulong h1  = (ulong)s1 * s0_2;
+            ulong h9  = (ulong)s7 * s2_2
+                      + (ulong)s6 * s3_2
+                      + (ulong)s5 * s4_2;
+
+            c        += f1 + g1 + h9 - f9;
+            z1        = (uint)c & M28; c >>= 28;
+            d        += g9 + h1 - f1 + h9;
+            z9        = (uint)d & M28; d >>= 28;
+
+            ulong f2  = (ulong)x2 * x0_2
+                      + (ulong)x1 * x1;
+            ulong f10 = (ulong)x7 * x3_2
+                      + (ulong)x6 * x4_2
+                      + (ulong)x5 * x5;
+            ulong g2  = (ulong)u2 * u0_2
+                      + (ulong)u1 * u1;
+            ulong g10 = (ulong)u7 * u3_2
+                      + (ulong)u6 * u4_2
+                      + (ulong)u5 * u5;
+            ulong h2  = (ulong)s2 * s0_2
+                      + (ulong)s1 * s1;
+            ulong h10 = (ulong)s7 * s3_2
+                      + (ulong)s6 * s4_2
+                      + (ulong)s5 * s5;
+
+            c        += f2 + g2 + h10 - f10;
+            z2        = (uint)c & M28; c >>= 28;
+            d        += g10 + h2 - f2 + h10;
+            z10       = (uint)d & M28; d >>= 28;
+
+            ulong f3  = (ulong)x3 * x0_2
+                      + (ulong)x2 * x1_2;
+            ulong f11 = (ulong)x7 * x4_2
+                      + (ulong)x6 * x5_2;
+            ulong g3  = (ulong)u3 * u0_2
+                      + (ulong)u2 * u1_2;
+            ulong g11 = (ulong)u7 * u4_2
+                      + (ulong)u6 * u5_2;
+            ulong h3  = (ulong)s3 * s0_2
+                      + (ulong)s2 * s1_2;
+            ulong h11 = (ulong)s7 * s4_2
+                      + (ulong)s6 * s5_2;
+
+            c        += f3 + g3 + h11 - f11;
+            z3        = (uint)c & M28; c >>= 28;
+            d        += g11 + h3 - f3 + h11;
+            z11       = (uint)d & M28; d >>= 28;
+
+            ulong f4  = (ulong)x4 * x0_2
+                      + (ulong)x3 * x1_2
+                      + (ulong)x2 * x2;
+            ulong f12 = (ulong)x7 * x5_2
+                      + (ulong)x6 * x6;
+            ulong g4  = (ulong)u4 * u0_2
+                      + (ulong)u3 * u1_2
+                      + (ulong)u2 * u2;
+            ulong g12 = (ulong)u7 * u5_2
+                      + (ulong)u6 * u6;
+            ulong h4  = (ulong)s4 * s0_2
+                      + (ulong)s3 * s1_2
+                      + (ulong)s2 * s2;
+            ulong h12 = (ulong)s7 * s5_2
+                      + (ulong)s6 * s6;
+
+            c        += f4 + g4 + h12 - f12;
+            z4        = (uint)c & M28; c >>= 28;
+            d        += g12 + h4 - f4 + h12;
+            z12       = (uint)d & M28; d >>= 28;
+
+            ulong f5  = (ulong)x5 * x0_2
+                      + (ulong)x4 * x1_2
+                      + (ulong)x3 * x2_2;
+            ulong f13 = (ulong)x7 * x6_2;
+            ulong g5  = (ulong)u5 * u0_2
+                      + (ulong)u4 * u1_2
+                      + (ulong)u3 * u2_2;
+            ulong g13 = (ulong)u7 * u6_2;
+            ulong h5  = (ulong)s5 * s0_2
+                      + (ulong)s4 * s1_2
+                      + (ulong)s3 * s2_2;
+            ulong h13 = (ulong)s7 * s6_2;
+
+            c        += f5 + g5 + h13 - f13;
+            z5        = (uint)c & M28; c >>= 28;
+            d        += g13 + h5 - f5 + h13;
+            z13       = (uint)d & M28; d >>= 28;
+
+            ulong f6  = (ulong)x6 * x0_2
+                      + (ulong)x5 * x1_2
+                      + (ulong)x4 * x2_2
+                      + (ulong)x3 * x3;
+            ulong f14 = (ulong)x7 * x7;
+            ulong g6  = (ulong)u6 * u0_2
+                      + (ulong)u5 * u1_2
+                      + (ulong)u4 * u2_2
+                      + (ulong)u3 * u3;
+            ulong g14 = (ulong)u7 * u7;
+            ulong h6  = (ulong)s6 * s0_2
+                      + (ulong)s5 * s1_2
+                      + (ulong)s4 * s2_2
+                      + (ulong)s3 * s3;
+            ulong h14 = (ulong)s7 * s7;
+
+            c        += f6 + g6 + h14 - f14;
+            z6        = (uint)c & M28; c >>= 28;
+            d        += g14 + h6 - f6 + h14;
+            z14       = (uint)d & M28; d >>= 28;
+
+            ulong f7  = (ulong)x7 * x0_2
+                      + (ulong)x6 * x1_2
+                      + (ulong)x5 * x2_2
+                      + (ulong)x4 * x3_2;
+            ulong g7  = (ulong)u7 * u0_2
+                      + (ulong)u6 * u1_2
+                      + (ulong)u5 * u2_2
+                      + (ulong)u4 * u3_2;
+            ulong h7  = (ulong)s7 * s0_2
+                      + (ulong)s6 * s1_2
+                      + (ulong)s5 * s2_2
+                      + (ulong)s4 * s3_2;
+
+            c        += f7 + g7;
+            z7        = (uint)c & M28; c >>= 28;
+            d        += h7 - f7;
+            z15       = (uint)d & M28; d >>= 28;
+
+            c        += d;
+
+            c        += z8;
+            z8        = (uint)c & M28; c >>= 28;
+            d        += z0;
+            z0        = (uint)d & M28; d >>= 28;
+            z9       += (uint)c;
+            z1       += (uint)d;
+
+            z[0] = z0;
+            z[1] = z1;
+            z[2] = z2;
+            z[3] = z3;
+            z[4] = z4;
+            z[5] = z5;
+            z[6] = z6;
+            z[7] = z7;
+            z[8] = z8;
+            z[9] = z9;
+            z[10] = z10;
+            z[11] = z11;
+            z[12] = z12;
+            z[13] = z13;
+            z[14] = z14;
+            z[15] = z15;
+        }
+
+        public static void Sqr(uint[] x, int n, uint[] z)
+        {
+            Debug.Assert(n > 0);
+
+            Sqr(x, z);
+
+            while (--n > 0)
+            {
+                Sqr(z, z);
+            }
+        }
+
+        public static void Sub(uint[] x, uint[] y, uint[] z)
+        {
+            uint x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7];
+            uint x8 = x[8], x9 = x[9], x10 = x[10], x11 = x[11], x12 = x[12], x13 = x[13], x14 = x[14], x15 = x[15];
+            uint y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3], y4 = y[4], y5 = y[5], y6 = y[6], y7 = y[7];
+            uint y8 = y[8], y9 = y[9], y10 = y[10], y11 = y[11], y12 = y[12], y13 = y[13], y14 = y[14], y15 = y[15];
+
+            uint z0 = x0 + 0x1FFFFFFEU - y0;
+            uint z1 = x1 + 0x1FFFFFFEU - y1;
+            uint z2 = x2 + 0x1FFFFFFEU - y2;
+            uint z3 = x3 + 0x1FFFFFFEU - y3;
+            uint z4 = x4 + 0x1FFFFFFEU - y4;
+            uint z5 = x5 + 0x1FFFFFFEU - y5;
+            uint z6 = x6 + 0x1FFFFFFEU - y6;
+            uint z7 = x7 + 0x1FFFFFFEU - y7;
+            uint z8 = x8 + 0x1FFFFFFCU - y8;
+            uint z9 = x9 + 0x1FFFFFFEU - y9;
+            uint z10 = x10 + 0x1FFFFFFEU - y10;
+            uint z11 = x11 + 0x1FFFFFFEU - y11;
+            uint z12 = x12 + 0x1FFFFFFEU - y12;
+            uint z13 = x13 + 0x1FFFFFFEU - y13;
+            uint z14 = x14 + 0x1FFFFFFEU - y14;
+            uint z15 = x15 + 0x1FFFFFFEU - y15;
+
+            z2   += z1 >> 28; z1 &= M28;
+            z6   += z5 >> 28; z5 &= M28;
+            z10  += z9 >> 28; z9 &= M28;
+            z14  += z13 >> 28; z13 &= M28;
+
+            z3   += z2 >> 28; z2 &= M28;
+            z7   += z6 >> 28; z6 &= M28;
+            z11  += z10 >> 28; z10 &= M28;
+            z15  += z14 >> 28; z14 &= M28;
+
+            uint t = z15 >> 28; z15 &= M28;
+            z0   += t;
+            z8   += t;
+
+            z4   += z3 >> 28; z3 &= M28;
+            z8   += z7 >> 28; z7 &= M28;
+            z12  += z11 >> 28; z11 &= M28;
+
+            z1   += z0 >> 28; z0 &= M28;
+            z5   += z4 >> 28; z4 &= M28;
+            z9   += z8 >> 28; z8 &= M28;
+            z13  += z12 >> 28; z12 &= M28;
+
+            z[0] = z0;
+            z[1] = z1;
+            z[2] = z2;
+            z[3] = z3;
+            z[4] = z4;
+            z[5] = z5;
+            z[6] = z6;
+            z[7] = z7;
+            z[8] = z8;
+            z[9] = z9;
+            z[10] = z10;
+            z[11] = z11;
+            z[12] = z12;
+            z[13] = z13;
+            z[14] = z14;
+            z[15] = z15;
+        }
+    }
+}
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];
diff --git a/crypto/src/pkix/PkixAttrCertPathBuilder.cs b/crypto/src/pkix/PkixAttrCertPathBuilder.cs
index 646cc5db5..3d5fa18e3 100644
--- a/crypto/src/pkix/PkixAttrCertPathBuilder.cs
+++ b/crypto/src/pkix/PkixAttrCertPathBuilder.cs
@@ -143,7 +143,7 @@ namespace Org.BouncyCastle.Pkix
 			try
 			{
 				// check whether the issuer of <tbvCert> is a TrustAnchor
-				if (PkixCertPathValidatorUtilities.FindTrustAnchor(tbvCert, pkixParams.GetTrustAnchors()) != null)
+				if (PkixCertPathValidatorUtilities.IsIssuerTrustAnchor(tbvCert, pkixParams.GetTrustAnchors()))
 				{
 					PkixCertPath certPath = new PkixCertPath(tbvPath);
 					PkixCertPathValidatorResult result;
diff --git a/crypto/src/pkix/PkixCertPathBuilder.cs b/crypto/src/pkix/PkixCertPathBuilder.cs
index fa38a5ec0..37a1c8c9c 100644
--- a/crypto/src/pkix/PkixCertPathBuilder.cs
+++ b/crypto/src/pkix/PkixCertPathBuilder.cs
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Pkix
 			try
 			{
 				// check whether the issuer of <tbvCert> is a TrustAnchor
-				if (PkixCertPathValidatorUtilities.FindTrustAnchor(tbvCert, pkixParams.GetTrustAnchors()) != null)
+				if (PkixCertPathValidatorUtilities.IsIssuerTrustAnchor(tbvCert, pkixParams.GetTrustAnchors()))
 				{
 					// exception message from possibly later tried certification
 					// chains
diff --git a/crypto/src/pkix/PkixCertPathValidator.cs b/crypto/src/pkix/PkixCertPathValidator.cs
index fcfa63837..64039f9f1 100644
--- a/crypto/src/pkix/PkixCertPathValidator.cs
+++ b/crypto/src/pkix/PkixCertPathValidator.cs
@@ -3,6 +3,7 @@ using System.Collections;
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security.Certificates;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Collections;
 using Org.BouncyCastle.X509;
@@ -81,16 +82,18 @@ namespace Org.BouncyCastle.Pkix
                 trust = PkixCertPathValidatorUtilities.FindTrustAnchor(
 					(X509Certificate)certs[certs.Count - 1],
 					paramsPkix.GetTrustAnchors());
+
+                if (trust == null)
+                    throw new PkixCertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1);
+
+                CheckCertificate(trust.TrustedCert);
             }
             catch (Exception e)
             {
-                throw new PkixCertPathValidatorException(e.Message, e, certPath, certs.Count - 1);
+                throw new PkixCertPathValidatorException(e.Message, e.InnerException, certPath, certs.Count - 1);
             }
 
-            if (trust == null)
-                throw new PkixCertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1);
-
-			//
+            //
             // (e), (f), (g) are part of the paramsPkix object.
             //
             IEnumerator certIter;
@@ -253,6 +256,15 @@ namespace Org.BouncyCastle.Pkix
                 //
                 cert = (X509Certificate)certs[index];
 
+                try
+                {
+                    CheckCertificate(cert);
+                }
+                catch (Exception e)
+                {
+                    throw new PkixCertPathValidatorException(e.Message, e.InnerException, certPath, index);
+                }
+
                 //
                 // 6.1.3
                 //
@@ -277,6 +289,10 @@ namespace Org.BouncyCastle.Pkix
                 {
                     if (cert != null && cert.Version == 1)
                     {
+                        // we've found the trust anchor at the top of the path, ignore and keep going
+                        if ((i == 1) && cert.Equals(trust.TrustedCert))
+                            continue;
+
                         throw new PkixCertPathValidatorException(
 							"Version 1 certificates can't be used as CA ones.", null, certPath, index);
                     }
@@ -416,5 +432,17 @@ namespace Org.BouncyCastle.Pkix
 
 			throw new PkixCertPathValidatorException("Path processing failed on policy.", null, certPath, index);
         }
+
+        internal static void CheckCertificate(X509Certificate cert)
+        {
+            try
+            {
+                TbsCertificateStructure.GetInstance(cert.CertificateStructure.TbsCertificate);
+            }
+            catch (CertificateEncodingException e)
+            {
+                throw new Exception("unable to process TBSCertificate", e);
+            }
+        }
     }
 }
diff --git a/crypto/src/pkix/PkixCertPathValidatorUtilities.cs b/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
index a2704a746..2ccaa32ce 100644
--- a/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
+++ b/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
@@ -143,6 +143,20 @@ namespace Org.BouncyCastle.Pkix
 			return trust;
 		}
 
+        internal static bool IsIssuerTrustAnchor(
+            X509Certificate cert,
+            ISet trustAnchors)
+        {
+            try
+            {
+                return FindTrustAnchor(cert, trustAnchors) != null;
+            }
+            catch (Exception e)
+            {
+                return false;
+            }
+        }
+
 		internal static void AddAdditionalStoresFromAltNames(
 			X509Certificate	cert,
 			PkixParameters	pkixParams)
diff --git a/crypto/src/tsp/TSPAlgorithms.cs b/crypto/src/tsp/TSPAlgorithms.cs
index e3dfc7916..928468ed7 100644
--- a/crypto/src/tsp/TSPAlgorithms.cs
+++ b/crypto/src/tsp/TSPAlgorithms.cs
@@ -1,9 +1,11 @@
 using System.Collections;
 
 using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.GM;
 using Org.BouncyCastle.Asn1.Nist;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Rosstandart;
 using Org.BouncyCastle.Asn1.TeleTrust;
 using Org.BouncyCastle.Utilities;
 
@@ -28,14 +30,18 @@ namespace Org.BouncyCastle.Tsp
 		public static readonly string RipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id;
 
 		public static readonly string Gost3411 = CryptoProObjectIdentifiers.GostR3411.Id;
+        public static readonly string Gost3411_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Id;
+        public static readonly string Gost3411_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Id;
 
-		public static readonly IList Allowed;
+        public static readonly string SM3 = GMObjectIdentifiers.sm3.Id;
+
+        public static readonly IList Allowed;
 
 		static TspAlgorithms()
 		{
 			string[] algs = new string[]
 			{
-				Gost3411, MD5, Sha1, Sha224, Sha256, Sha384, Sha512, RipeMD128, RipeMD160, RipeMD256
+				Gost3411, Gost3411_2012_256, Gost3411_2012_512, MD5, RipeMD128, RipeMD160, RipeMD256, Sha1, Sha224, Sha256, Sha384, Sha512, SM3
 			};
 
 			Allowed = Platform.CreateArrayList();
diff --git a/crypto/src/tsp/TSPUtil.cs b/crypto/src/tsp/TSPUtil.cs
index dc8ed3c21..a17657472 100644
--- a/crypto/src/tsp/TSPUtil.cs
+++ b/crypto/src/tsp/TSPUtil.cs
@@ -4,9 +4,11 @@ using System.IO;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.GM;
 using Org.BouncyCastle.Asn1.Nist;
 using Org.BouncyCastle.Asn1.Oiw;
 using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Rosstandart;
 using Org.BouncyCastle.Asn1.TeleTrust;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Cms;
@@ -38,6 +40,9 @@ namespace Org.BouncyCastle.Tsp
             digestLengths.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, 20);
             digestLengths.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, 32);
             digestLengths.Add(CryptoProObjectIdentifiers.GostR3411.Id, 32);
+            digestLengths.Add(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Id, 32);
+            digestLengths.Add(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Id, 64);
+            digestLengths.Add(GMObjectIdentifiers.sm3.Id, 32);
 
             digestNames.Add(PkcsObjectIdentifiers.MD5.Id, "MD5");
             digestNames.Add(OiwObjectIdentifiers.IdSha1.Id, "SHA1");
@@ -45,7 +50,7 @@ namespace Org.BouncyCastle.Tsp
             digestNames.Add(NistObjectIdentifiers.IdSha256.Id, "SHA256");
             digestNames.Add(NistObjectIdentifiers.IdSha384.Id, "SHA384");
             digestNames.Add(NistObjectIdentifiers.IdSha512.Id, "SHA512");
-            digestNames.Add(PkcsObjectIdentifiers.MD5WithRsaEncryption, "MD5");
+            digestNames.Add(PkcsObjectIdentifiers.MD5WithRsaEncryption.Id, "MD5");
 			digestNames.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption.Id, "SHA1");
             digestNames.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption.Id, "SHA224");
             digestNames.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id, "SHA256");
@@ -57,8 +62,11 @@ namespace Org.BouncyCastle.Tsp
             digestNames.Add(CryptoProObjectIdentifiers.GostR3411.Id, "GOST3411");
             digestNames.Add(OiwObjectIdentifiers.DsaWithSha1.Id, "SHA1");
             digestNames.Add(OiwObjectIdentifiers.Sha1WithRsa.Id, "SHA1");
-            digestNames.Add(OiwObjectIdentifiers.MD5WithRsa, "MD5");
-		}
+            digestNames.Add(OiwObjectIdentifiers.MD5WithRsa.Id, "MD5");
+            digestNames.Add(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Id, "GOST3411-2012-256");
+            digestNames.Add(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Id, "GOST3411-2012-512");
+            digestNames.Add(GMObjectIdentifiers.sm3.Id, "SM3");
+        }
 
 
 	    /**
diff --git a/crypto/test/src/crypto/test/BCryptTest.cs b/crypto/test/src/crypto/test/BCryptTest.cs
index 1d71f62af..45bc7fd18 100644
--- a/crypto/test/src/crypto/test/BCryptTest.cs
+++ b/crypto/test/src/crypto/test/BCryptTest.cs
@@ -134,6 +134,8 @@ namespace Org.BouncyCastle.Crypto.Tests
                 DoTest(password, salt, cost, expected);
             }
 
+            IsTrue(AreEqual(BCrypt.Generate(BCrypt.PasswordToByteArray("12341234".ToCharArray()), Hex.Decode("01020304050607080102030405060708"), 5), Hex.Decode("cdd19088721c50e5cb49a7b743d93b5a6e67bef0f700cd78")));
+            IsTrue(AreEqual(BCrypt.Generate(BCrypt.PasswordToByteArray("1234".ToCharArray()), Hex.Decode("01020304050607080102030405060708"), 5), Hex.Decode("02a3269aca2732484057b40c614204814cbfc2becd8e093e")));
         }
 
         private void DoTest(byte[] password, byte[] salt, int cost, byte[] expected)
diff --git a/crypto/test/src/crypto/test/OpenBsdBCryptTest.cs b/crypto/test/src/crypto/test/OpenBsdBCryptTest.cs
index bf10fe144..0eb932e42 100644
--- a/crypto/test/src/crypto/test/OpenBsdBCryptTest.cs
+++ b/crypto/test/src/crypto/test/OpenBsdBCryptTest.cs
@@ -74,6 +74,28 @@ namespace Org.BouncyCastle.Crypto.Tests
             new string[]{"8nv;PAN~-FQ]Emh@.TKG=^.t8R0EQC0T?x9|9g4xzxYmSbBO1qDx8kv-ehh0IBv>3KWhz.Z~jUF0tt8[5U@8;5:=[v6pf.IEJ", "$2a$08$eXo9KDc1BZyybBgMurpcD.GA1/ch3XhgBnIH10Xvjc2ogZaGg3t/m"},
         };
 
+
+        // 2y vectors generated from htpasswd -nB -C 12, nb leading username was removed.
+        private static readonly string[,] twoYVec = new string[,]{
+            {"a", "$2y$12$DB3BUbYa/SsEL7kCOVji0OauTkPkB5Y1OeyfxJHM7jvMrbml5sgD2"},
+            {"abc", "$2y$12$p.xODEbFcXUlHGbNxWZqAe6AA5FWupqXmN9tZea2ACDhwIx4EA2a6"},
+            {"hello world", "$2y$12$wfkxITYXjNLVpEi9nOjz7uXMhCXKSTY7O2y7X4bwY89aGSvRziguq"},
+            {"ABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXY", "$2y$12$QwAt5kuG68nW7v.87q0QPuwdki3romFc/RU/RV3Qqk4FPw6WdbQzu"}
+        };
+
+        // Same as 2y vectors only version changed to 2b to verify handling of that version.
+        private static readonly string[,] twoBVec = new string[,]{
+            {"a", "$2b$12$DB3BUbYa/SsEL7kCOVji0OauTkPkB5Y1OeyfxJHM7jvMrbml5sgD2"},
+            {"abc", "$2b$12$p.xODEbFcXUlHGbNxWZqAe6AA5FWupqXmN9tZea2ACDhwIx4EA2a6"},
+            {"hello world", "$2b$12$wfkxITYXjNLVpEi9nOjz7uXMhCXKSTY7O2y7X4bwY89aGSvRziguq"},
+            {"ABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXY", "$2b$12$QwAt5kuG68nW7v.87q0QPuwdki3romFc/RU/RV3Qqk4FPw6WdbQzu"}
+        };
+
+        public static void MainOld(string[] args)
+        {
+            RunTest(new OpenBsdBCryptTest());
+        }
+
         public override string Name
         {
             get { return "OpenBsdBCrypt"; }
@@ -129,11 +151,36 @@ namespace Org.BouncyCastle.Crypto.Tests
                     Fail("test4 mismatch: " + "[" + i + "] " + password);
                 }
             }
-        }
 
-        public static void MainOld(string[] args)
-        {
-            RunTest(new OpenBsdBCryptTest());
+            {
+                int lower = twoYVec.GetLowerBound(0);
+                int upper = twoYVec.GetUpperBound(0);
+                for (int i = lower; i <= upper; i++)
+                {
+                    password = twoYVec[i, 0];
+                    encoded = twoYVec[i, 1];
+
+                    if (!OpenBsdBCrypt.CheckPassword(encoded, password.ToCharArray()))
+                    {
+                        Fail("twoYVec mismatch: " + "[" + i + "] " + password);
+                    }
+                }
+            }
+
+            {
+                int lower = twoBVec.GetLowerBound(0);
+                int upper = twoBVec.GetUpperBound(0);
+                for (int i = lower; i <= upper; i++)
+                {
+                    password = twoBVec[i, 0];
+                    encoded = twoBVec[i, 1];
+
+                    if (!OpenBsdBCrypt.CheckPassword(encoded, password.ToCharArray()))
+                    {
+                        Fail("twoBVec mismatch: " + "[" + i + "] " + password);
+                    }
+                }
+            }
         }
 
         [Test]
diff --git a/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs b/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs
new file mode 100644
index 000000000..89c325fd5
--- /dev/null
+++ b/crypto/test/src/math/ec/rfc7748/test/X25519Test.cs
@@ -0,0 +1,184 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
+{
+    [TestFixture]
+    public class X25519Test
+    {
+        private static readonly SecureRandom Random = new SecureRandom();
+
+        [SetUp]
+        public void SetUp()
+        {
+            X25519.Precompute();
+        }
+
+        [Test]
+        public void TestConsistency()
+        {
+            byte[] u = new byte[32];    u[0] = 9;
+            byte[] k = new byte[32];
+            byte[] rF = new byte[32];
+            byte[] rV = new byte[32];
+
+            for (int i = 1; i <= 100; ++i)
+            {
+                Random.NextBytes(k);
+                X25519.ScalarMultBase(k, 0, rF, 0);
+                X25519.ScalarMult(k, 0, u, 0, rV, 0);
+                Assert.IsTrue(Arrays.AreEqual(rF, rV), "Consistency #" + i);
+            }
+        }
+
+        [Test]
+        public void TestECDH()
+        {
+            byte[] kA = new byte[32];
+            byte[] kB = new byte[32];
+            byte[] qA = new byte[32];
+            byte[] qB = new byte[32];
+            byte[] sA = new byte[32];
+            byte[] sB = new byte[32];
+
+            for (int i = 1; i <= 100; ++i)
+            {
+                // Each party generates an ephemeral private key, ...
+                Random.NextBytes(kA);
+                Random.NextBytes(kB);
+
+                // ... publishes their public key, ...
+                X25519.ScalarMultBase(kA, 0, qA, 0);
+                X25519.ScalarMultBase(kB, 0, qB, 0);
+
+                // ... computes the shared secret, ...
+                X25519.ScalarMult(kA, 0, qB, 0, sA, 0);
+                X25519.ScalarMult(kB, 0, qA, 0, sB, 0);
+
+                // ... which is the same for both parties.
+                //Assert.IsTrue(Arrays.AreEqual(sA, sB), "ECDH #" + i);
+                if (!Arrays.AreEqual(sA, sB))
+                {
+                    Console.WriteLine(" " + i);
+                }
+            }
+        }
+
+        [Test]
+        public void TestECDHVector1()
+        {
+            CheckECDHVector(
+                "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
+                "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
+                "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
+                "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
+                "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
+                "ECDH Vector #1");
+        }
+
+        [Test]
+        public void TestX25519Iterated()
+        {
+            CheckIterated(1000);
+        }
+
+        //[Test, Explicit]
+        //public void TestX25519IteratedFull()
+        //{
+        //    CheckIterated(1000000);
+        //}
+
+        [Test]
+        public void TestX25519Vector1()
+        {
+            CheckX25519Vector(
+                "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
+                "e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
+                "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
+                "Vector #1");
+        }
+
+        [Test]
+        public void TestX25519Vector2()
+        {
+            CheckX25519Vector(
+                "4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
+                "e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
+                "95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957",
+                "Vector #2");
+        }
+
+        private static void CheckECDHVector(string sA, string sAPub, string sB, string sBPub, string sK, string text)
+        {
+            byte[] a = Hex.Decode(sA);
+            byte[] b = Hex.Decode(sB);
+
+            byte[] aPub = new byte[32];
+            X25519.ScalarMultBase(a, 0, aPub, 0);
+            CheckValue(aPub, text, sAPub);
+
+            byte[] bPub = new byte[32];
+            X25519.ScalarMultBase(b, 0, bPub, 0);
+            CheckValue(bPub, text, sBPub);
+
+            byte[] aK = new byte[32];
+            X25519.ScalarMult(a, 0, bPub, 0, aK, 0);
+            CheckValue(aK, text, sK);
+
+            byte[] bK = new byte[32];
+            X25519.ScalarMult(b, 0, aPub, 0, bK, 0);
+            CheckValue(bK, text, sK);
+        }
+
+        private static void CheckIterated(int count)
+        {
+            byte[] k = new byte[32]; k[0] = 9;
+            byte[] u = new byte[32]; u[0] = 9;
+            byte[] r = new byte[32];
+
+            int iterations = 0;
+            while (iterations < count)
+            {
+                X25519.ScalarMult(k, 0, u, 0, r, 0);
+
+                Array.Copy(k, 0, u, 0, 32);
+                Array.Copy(r, 0, k, 0, 32);
+
+                switch (++iterations)
+                {
+                case 1:
+                    CheckValue(k, "Iterated @1", "422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079");
+                    break;
+                case 1000:
+                    CheckValue(k, "Iterated @1000", "684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51");
+                    break;
+                case 1000000:
+                    CheckValue(k, "Iterated @1000000", "7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424");
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+
+        private static void CheckValue(byte[] n, string text, string se)
+        {
+            byte[] e = Hex.Decode(se);
+            Assert.IsTrue(Arrays.AreEqual(e, n), text);
+        }
+
+        private static void CheckX25519Vector(string sk, string su, string se, string text)
+        {
+            byte[] k = Hex.Decode(sk);
+            byte[] u = Hex.Decode(su);
+            byte[] r = new byte[32];
+            X25519.ScalarMult(k, 0, u, 0, r, 0);
+            CheckValue(r, text, se);
+        }
+    }
+}
diff --git a/crypto/test/src/math/ec/rfc7748/test/X448Test.cs b/crypto/test/src/math/ec/rfc7748/test/X448Test.cs
new file mode 100644
index 000000000..b095eade0
--- /dev/null
+++ b/crypto/test/src/math/ec/rfc7748/test/X448Test.cs
@@ -0,0 +1,183 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Rfc7748.Tests
+{
+    [TestFixture]
+    public class X448Test
+    {
+        private static readonly SecureRandom Random = new SecureRandom();
+
+        [SetUp]
+        public void SetUp()
+        {
+            X448.Precompute();
+        }
+
+        [Test]
+        public void TestConsistency()
+        {
+            byte[] u = new byte[56];    u[0] = 5;
+            byte[] k = new byte[56];
+            byte[] rF = new byte[56];
+            byte[] rV = new byte[56];
+
+            for (int i = 1; i <= 100; ++i)
+            {
+                Random.NextBytes(k);
+                X448.ScalarMultBase(k, 0, rF, 0);
+                X448.ScalarMult(k, 0, u, 0, rV, 0);
+                Assert.IsTrue(Arrays.AreEqual(rF, rV), "Consistency #" + i);
+            }
+        }
+
+        [Test]
+        public void TestECDH()
+        {
+            byte[] kA = new byte[56];
+            byte[] kB = new byte[56];
+            byte[] qA = new byte[56];
+            byte[] qB = new byte[56];
+            byte[] sA = new byte[56];
+            byte[] sB = new byte[56];
+
+            for (int i = 1; i <= 100; ++i)
+            {
+                // Each party generates an ephemeral private key, ...
+                Random.NextBytes(kA);
+                Random.NextBytes(kB);
+
+                // ... publishes their public key, ...
+                X448.ScalarMultBase(kA, 0, qA, 0);
+                X448.ScalarMultBase(kB, 0, qB, 0);
+
+                // ... computes the shared secret, ...
+                X448.ScalarMult(kA, 0, qB, 0, sA, 0);
+                X448.ScalarMult(kB, 0, qA, 0, sB, 0);
+
+                // ... which is the same for both parties.
+                Assert.IsTrue(Arrays.AreEqual(sA, sB), "ECDH #" + i);
+            }
+        }
+
+        [Test]
+        public void TestECDHVector1()
+        {
+            CheckECDHVector(
+                "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b",
+                "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0",
+                "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d",
+                "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609",
+                "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d",
+                "ECDH Vector #1");
+        }
+
+        [Test]
+        public void TestX448Iterated()
+        {
+            CheckIterated(1000);
+        }
+
+        //[Test, Explicit]
+        //public void TestX448IteratedFull()
+        //{
+        //    CheckIterated(1000000);
+        //}
+
+        [Test]
+        public void TestX448Vector1()
+        {
+            CheckX448Vector(
+                "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
+                "06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086",
+                "ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f",
+                "Vector #1");
+        }
+
+        [Test]
+        public void TestX448Vector2()
+        {
+            CheckX448Vector(
+                "203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f",
+                "0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db",
+                "884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d",
+                "Vector #2");
+        }
+
+        private static void CheckECDHVector(string sA, string sAPub, string sB, string sBPub, string sK, string text)
+        {
+            byte[] a = Hex.Decode(sA);
+            byte[] b = Hex.Decode(sB);
+
+            byte[] aPub = new byte[56];
+            X448.ScalarMultBase(a, 0, aPub, 0);
+            CheckValue(aPub, text, sAPub);
+
+            byte[] bPub = new byte[56];
+            X448.ScalarMultBase(b, 0, bPub, 0);
+            CheckValue(bPub, text, sBPub);
+
+            byte[] aK = new byte[56];
+            X448.ScalarMult(a, 0, bPub, 0, aK, 0);
+            CheckValue(aK, text, sK);
+
+            byte[] bK = new byte[56];
+            X448.ScalarMult(b, 0, aPub, 0, bK, 0);
+            CheckValue(bK, text, sK);
+        }
+
+        private static void CheckIterated(int count)
+        {
+            byte[] k = new byte[56]; k[0] = 5;
+            byte[] u = new byte[56]; u[0] = 5;
+            byte[] r = new byte[56];
+
+            int iterations = 0;
+            while (iterations < count)
+            {
+                X448.ScalarMult(k, 0, u, 0, r, 0);
+
+                Array.Copy(k, 0, u, 0, 56);
+                Array.Copy(r, 0, k, 0, 56);
+
+                switch (++iterations)
+                {
+                case 1:
+                    CheckValue(k, "Iterated @1",
+                        "3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113");
+                    break;
+                case 1000:
+                    CheckValue(k, "Iterated @1000",
+                        "aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38");
+                    break;
+                case 1000000:
+                    CheckValue(k, "Iterated @1000000",
+                        "077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37");
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+
+        private static void CheckValue(byte[] n, String text, String se)
+        {
+            byte[] e = Hex.Decode(se);
+            Assert.IsTrue(Arrays.AreEqual(e, n), text);
+        }
+
+        private static void CheckX448Vector(String sk, String su, String se, String text)
+        {
+            byte[] k = Hex.Decode(sk);
+            byte[] u = Hex.Decode(su);
+            byte[] r = new byte[56];
+            X448.ScalarMult(k, 0, u, 0, r, 0);
+            CheckValue(r, text, se);
+        }
+    }
+}
diff --git a/crypto/test/src/math/ec/test/AllTests.cs b/crypto/test/src/math/ec/test/AllTests.cs
index 2b0d349ec..9373e3cfc 100644
--- a/crypto/test/src/math/ec/test/AllTests.cs
+++ b/crypto/test/src/math/ec/test/AllTests.cs
@@ -21,6 +21,7 @@ namespace Org.BouncyCastle.Math.EC.Tests
                 TestSuite suite = new TestSuite("EC Math tests");
                 suite.Add(new ECAlgorithmsTest());
                 suite.Add(new ECPointTest());
+                suite.Add(new FixedPointTest());
                 return suite;
             }
         }
diff --git a/crypto/test/src/math/ec/test/FixedPointTest.cs b/crypto/test/src/math/ec/test/FixedPointTest.cs
new file mode 100644
index 000000000..83e5fab8f
--- /dev/null
+++ b/crypto/test/src/math/ec/test/FixedPointTest.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.EC;
+using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Math.EC.Tests
+{
+    [TestFixture]
+    public class FixedPointTest
+    {
+        private static readonly SecureRandom Random = new SecureRandom();
+
+        private const int TestsPerCurve = 5;
+
+        [Test]
+        public void TestFixedPointMultiplier()
+        {
+            FixedPointCombMultiplier M = new FixedPointCombMultiplier();
+
+            ArrayList names = new ArrayList();
+            CollectionUtilities.AddRange(names, ECNamedCurveTable.Names);
+            CollectionUtilities.AddRange(names, CustomNamedCurves.Names);
+
+            ISet uniqNames = new HashSet(names);
+
+            foreach (string name in uniqNames)
+            {
+                X9ECParameters x9A = ECNamedCurveTable.GetByName(name);
+                X9ECParameters x9B = CustomNamedCurves.GetByName(name);
+
+                X9ECParameters x9 = x9B != null ? x9B : x9A;
+
+                for (int i = 0; i < TestsPerCurve; ++i)
+                {
+                    BigInteger k = new BigInteger(x9.N.BitLength, Random);
+                    ECPoint pRef = ECAlgorithms.ReferenceMultiply(x9.G, k);
+
+                    if (x9A != null)
+                    {
+                        ECPoint pA = M.Multiply(x9A.G, k);
+                        AssertPointsEqual("Standard curve fixed-point failure", pRef, pA);
+                    }
+
+                    if (x9B != null)
+                    {
+                        ECPoint pB = M.Multiply(x9B.G, k);
+                        AssertPointsEqual("Custom curve fixed-point failure", pRef, pB);
+                    }
+                }
+            }
+        }
+
+        private void AssertPointsEqual(string message, ECPoint a, ECPoint b)
+        {
+            // NOTE: We intentionally test points for equality in both directions
+            Assert.AreEqual(a, b, message);
+            Assert.AreEqual(b, a, message);
+        }
+    }
+}
diff --git a/crypto/test/src/test/BlockCipherTest.cs b/crypto/test/src/test/BlockCipherTest.cs
index ddb0e1a7e..3b6db0c27 100644
--- a/crypto/test/src/test/BlockCipherTest.cs
+++ b/crypto/test/src/test/BlockCipherTest.cs
@@ -313,7 +313,7 @@ namespace Org.BouncyCastle.Tests
             "SEED/OCB/NoPadding",
             "eb04b3612769e1ad681f975af1a6f401d94dc88276dd50fc3ebce791c28825c652b7351acbad8c63d4d66191de94c970",
             "SEED/CCM/NoPadding",
-            "8bb16b37e7f1d4eb97bb1fa3b9bfd411aca64a3581bb3c5b2a91346983aa334984d73ad629a847f7",
+            "da684e8cab782d4ebae835726f43c3aeea97ee270897255714d464e981ac39af06c9483153f8a05a",
             "SEED/GCM/NoPadding",
             "ed5f6293c9a4f280af6695750bfb3bb3b60c214565a049494df955152757812ebfb93705895606c4378498a93f2541b5",
             //"SM4/GCM/NoPadding",
@@ -440,7 +440,7 @@ namespace Org.BouncyCastle.Tests
                 throw new Exception("Unhandled mode: " + mode);
 
             if (baseMode == "CCM")
-                return 13;
+                return 12;
             if (baseMode == "ECB")
                 return 0;
             if (baseMode == "OCB")
diff --git a/crypto/test/src/test/CertPathValidatorTest.cs b/crypto/test/src/test/CertPathValidatorTest.cs
index df2ba4c8f..cae6eb387 100644
--- a/crypto/test/src/test/CertPathValidatorTest.cs
+++ b/crypto/test/src/test/CertPathValidatorTest.cs
@@ -165,6 +165,7 @@ namespace Org.BouncyCastle.Tests
             IList certchain = new ArrayList();
             certchain.Add(finalCert);
             certchain.Add(interCert);
+
 //			CertPath cp = CertificateFactory.GetInstance("X.509").GenerateCertPath(certchain);
             PkixCertPath cp = new PkixCertPath(certchain);
             ISet trust = new HashSet();
@@ -179,7 +180,7 @@ namespace Org.BouncyCastle.Tests
             MyChecker checker = new MyChecker();
             param.AddCertPathChecker(checker);
 
-            PkixCertPathValidatorResult result = (PkixCertPathValidatorResult) cpv.Validate(cp, param);
+            PkixCertPathValidatorResult result = (PkixCertPathValidatorResult)cpv.Validate(cp, param);
             PkixPolicyNode policyTree = result.PolicyTree;
             AsymmetricKeyParameter subjectPublicKey = result.SubjectPublicKey;
 
@@ -193,6 +194,28 @@ namespace Org.BouncyCastle.Tests
                 Fail("wrong public key returned");
             }
 
+            IsTrue(result.TrustAnchor.TrustedCert.Equals(rootCert));
+
+            // try a path with trust anchor included.
+            certchain.Clear();
+            certchain.Add(finalCert);
+            certchain.Add(interCert);
+            certchain.Add(rootCert);
+
+            cp = new PkixCertPath(certchain);
+
+            cpv = new PkixCertPathValidator();
+            param = new PkixParameters(trust);
+            param.AddStore(x509CertStore);
+            param.AddStore(x509CrlStore);
+            param.Date = new DateTimeObject(validDate);
+            checker = new MyChecker();
+            param.AddCertPathChecker(checker);
+
+            result = (PkixCertPathValidatorResult)cpv.Validate(cp, param);
+
+            IsTrue(result.TrustAnchor.TrustedCert.Equals(rootCert));
+
             //
             // invalid path containing a valid one test
             //
@@ -223,6 +246,7 @@ namespace Org.BouncyCastle.Tests
                 certchain = new ArrayList();
                 certchain.Add(finalCert);
                 certchain.Add(interCert);
+
 //				cp = CertificateFactory.GetInstance("X.509").GenerateCertPath(certchain);
                 cp = new PkixCertPath(certchain);
                 trust = new HashSet();