summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-12-10 23:06:49 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-12-10 23:06:49 +0700
commitabece317fd90c4f0fb1393e03e937cac77116889 (patch)
treeddc2189099b009a69192635debbe666f1a05f2e0 /crypto
parentAdd fast coprime test (diff)
downloadBouncyCastle.NET-ed25519-abece317fd90c4f0fb1393e03e937cac77116889.tar.xz
Update safegcd implementation
Diffstat (limited to 'crypto')
-rw-r--r--crypto/src/crypto/parameters/RsaKeyParameters.cs2
-rw-r--r--crypto/src/math/raw/Mod.cs475
-rw-r--r--crypto/src/util/BigIntegers.cs10
3 files changed, 161 insertions, 326 deletions
diff --git a/crypto/src/crypto/parameters/RsaKeyParameters.cs b/crypto/src/crypto/parameters/RsaKeyParameters.cs
index 8826bb1ab..f982a14ac 100644
--- a/crypto/src/crypto/parameters/RsaKeyParameters.cs
+++ b/crypto/src/crypto/parameters/RsaKeyParameters.cs
@@ -20,7 +20,7 @@ namespace Org.BouncyCastle.Crypto.Parameters
         private static bool HasAnySmallFactors(BigInteger modulus)
         {
             BigInteger M = modulus, X = SmallPrimesProduct;
-            if (modulus.CompareTo(SmallPrimesProduct) < 0)
+            if (modulus.BitLength < SmallPrimesProduct.BitLength)
             {
                 M = SmallPrimesProduct;
                 X = modulus;
diff --git a/crypto/src/math/raw/Mod.cs b/crypto/src/math/raw/Mod.cs
index 984c4808d..9059e479c 100644
--- a/crypto/src/math/raw/Mod.cs
+++ b/crypto/src/math/raw/Mod.cs
@@ -7,11 +7,14 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Math.Raw
 {
-    /*
-     * Modular inversion as implemented in this class is based on the paper "Fast constant-time gcd
-     * computation and modular inversion" by Daniel J. Bernstein and Bo-Yin Yang.
-     */
-
+    /// <summary>
+    /// Modular inversion as implemented in this class is based on the paper "Fast constant-time gcd computation and
+    /// modular inversion" by Daniel J. Bernstein and Bo-Yin Yang.
+    /// </summary>
+    /// <remarks>
+    /// In some cases (when it is faster) we use the "half delta" variant of safegcd based on
+    /// <a href="https://github.com/sipa/safegcd-bounds">hddivsteps</a>. 
+    /// </remarks>
     internal static class Mod
     {
         private const int M30 = 0x3FFFFFFF;
@@ -68,56 +71,11 @@ namespace Org.BouncyCastle.Math.Raw
             return x;
         }
 
-        public static uint ModOddInverse(uint[] m, uint[] x, uint[] z)
-        {
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            return ModOddInverse(m.AsSpan(), x.AsSpan(), z.AsSpan());
+        public static uint ModOddInverse(ReadOnlySpan<uint> m, ReadOnlySpan<uint> x, Span<uint> z)
 #else
-            int len32 = m.Length;
-            Debug.Assert(len32 > 0);
-            Debug.Assert((m[0] & 1) != 0);
-            Debug.Assert(m[len32 - 1] != 0);
-
-            int bits = (len32 << 5) - Integers.NumberOfLeadingZeros((int)m[len32 - 1]);
-            int len30 = (bits + 29) / 30;
-
-            int[] t = new int[4];
-            int[] D = new int[len30];
-            int[] E = new int[len30];
-            int[] F = new int[len30];
-            int[] G = new int[len30];
-            int[] M = new int[len30];
-
-            E[0] = 1;
-            Encode30(bits, x, 0, G, 0);
-            Encode30(bits, m, 0, M, 0);
-            Array.Copy(M, 0, F, 0, len30);
-
-            int delta = 0;
-            int m0Inv32 = (int)Inverse32((uint)M[0]);
-            int maxDivsteps = GetMaximumDivsteps(bits);
-
-            for (int divSteps = 0; divSteps < maxDivsteps; divSteps += 30)
-            {
-                delta = Divsteps30(delta, F[0], G[0], t);
-                UpdateDE30(len30, D, E, t, m0Inv32, M);
-                UpdateFG30(len30, F, G, t);
-            }
-
-            int signF = F[len30 - 1] >> 31;
-            CNegate30(len30, signF, F);
-
-            CNormalize30(len30, signF, D, M);
-
-            Decode30(bits, D, 0, z, 0);
-            Debug.Assert(0 != Nat.LessThan(m.Length, z, m));
-
-            return (uint)(EqualTo(len30, F, 1) & EqualTo(len30, G, 0));
+        public static uint ModOddInverse(uint[] m, uint[] x, uint[] z)
 #endif
-        }
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public static uint ModOddInverse(ReadOnlySpan<uint> m, ReadOnlySpan<uint> x, Span<uint> z)
         {
             int len32 = m.Length;
             Debug.Assert(len32 > 0);
@@ -127,6 +85,7 @@ namespace Org.BouncyCastle.Math.Raw
             int bits = (len32 << 5) - Integers.NumberOfLeadingZeros((int)m[len32 - 1]);
             int len30 = (bits + 29) / 30;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
             int allocSize = len30 * 5;
             Span<int> alloc = (allocSize * Integers.NumBytes <= MaxStackAlloc)
                 ? stackalloc int[allocSize]
@@ -138,19 +97,33 @@ namespace Org.BouncyCastle.Math.Raw
             Span<int> F = alloc[..len30]; alloc = alloc[len30..];
             Span<int> G = alloc[..len30]; alloc = alloc[len30..];
             Span<int> M = alloc[..len30];
+#else
+            int[] t = new int[4];
+            int[] D = new int[len30];
+            int[] E = new int[len30];
+            int[] F = new int[len30];
+            int[] G = new int[len30];
+            int[] M = new int[len30];
+#endif
 
             E[0] = 1;
             Encode30(bits, x, G);
             Encode30(bits, m, M);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
             M.CopyTo(F);
+#else
+            Array.Copy(M, 0, F, 0, len30);
+#endif
 
-            int delta = 0;
+            // We use the "half delta" variant here, with theta == delta - 1/2
+            int theta = 0;
             int m0Inv32 = (int)Inverse32((uint)M[0]);
-            int maxDivsteps = GetMaximumDivsteps(bits);
+            int maxDivsteps = GetMaximumHDDivsteps(bits);
 
             for (int divSteps = 0; divSteps < maxDivsteps; divSteps += 30)
             {
-                delta = Divsteps30(delta, F[0], G[0], t);
+                theta = HDDivsteps30(theta, F[0], G[0], t);
                 UpdateDE30(len30, D, E, t, m0Inv32, M);
                 UpdateFG30(len30, F, G, t);
             }
@@ -165,90 +138,12 @@ namespace Org.BouncyCastle.Math.Raw
 
             return (uint)(EqualTo(len30, F, 1) & EqualTo(len30, G, 0));
         }
-#endif
 
-        public static bool ModOddInverseVar(uint[] m, uint[] x, uint[] z)
-        {
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            return ModOddInverseVar(m.AsSpan(), x.AsSpan(), z.AsSpan());
+        public static bool ModOddInverseVar(ReadOnlySpan<uint> m, ReadOnlySpan<uint> x, Span<uint> z)
 #else
-            int len32 = m.Length;
-            Debug.Assert(len32 > 0);
-            Debug.Assert((m[0] & 1) != 0);
-            Debug.Assert(m[len32 - 1] != 0);
-
-            int bits = (len32 << 5) - Integers.NumberOfLeadingZeros((int)m[len32 - 1]);
-            int len30 = (bits + 29) / 30;
-
-            int[] t = new int[4];
-            int[] D = new int[len30];
-            int[] E = new int[len30];
-            int[] F = new int[len30];
-            int[] G = new int[len30];
-            int[] M = new int[len30];
-
-            E[0] = 1;
-            Encode30(bits, x, 0, G, 0);
-            Encode30(bits, m, 0, M, 0);
-            Array.Copy(M, 0, F, 0, len30);
-
-            int clz = System.Math.Max(0, bits - 1 - Nat.GetBitLength(len32, x));
-            int eta = -1 - clz;
-            int lenDE = len30, lenFG = len30;
-            int m0Inv32 = (int)Inverse32((uint)M[0]);
-            int maxDivsteps = GetMaximumDivsteps(bits) - clz;
-
-            int divsteps = 0;
-            while (!EqualToVar(lenFG, G, 0))
-            {
-                if (divsteps >= maxDivsteps)
-                    return false;
-
-                divsteps += 30;
-
-                eta = Divsteps30Var(eta, F[0], G[0], t);
-                UpdateDE30(lenDE, D, E, t, m0Inv32, M);
-                UpdateFG30(lenFG, F, G, t);
-                lenFG = TrimFG30Var(lenFG, F, G);
-            }
-
-            int signF = F[lenFG - 1] >> 31;
-
-            /*
-             * D is in the range (-2.M, M). First, conditionally add M if D is negative, to bring it
-             * into the range (-M, M). Then normalize by conditionally negating (according to signF)
-             * and/or then adding M, to bring it into the range [0, M).
-             */
-            int signD = D[lenDE - 1] >> 31;
-            if (signD < 0)
-            {
-                signD = Add30(lenDE, D, M);
-            }
-            if (signF < 0)
-            {
-                signD = Negate30(lenDE, D);
-                signF = Negate30(lenFG, F);
-            }
-            Debug.Assert(0 == signF);
-
-            if (!EqualToVar(lenFG, F, 1))
-                return false;
-
-            if (signD < 0)
-            {
-                signD = Add30(lenDE, D, M);
-            }
-            Debug.Assert(0 == signD);
-
-            Decode30(bits, D, 0, z, 0);
-            Debug.Assert(!Nat.Gte(m.Length, z, m));
-
-            return true;
+        public static bool ModOddInverseVar(uint[] m, uint[] x, uint[] z)
 #endif
-        }
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public static bool ModOddInverseVar(ReadOnlySpan<uint> m, ReadOnlySpan<uint> x, Span<uint> z)
         {
             int len32 = m.Length;
             Debug.Assert(len32 > 0);
@@ -258,6 +153,10 @@ namespace Org.BouncyCastle.Math.Raw
             int bits = (len32 << 5) - Integers.NumberOfLeadingZeros((int)m[len32 - 1]);
             int len30 = (bits + 29) / 30;
 
+            int clz = bits - Nat.GetBitLength(len32, x);
+            Debug.Assert(clz >= 0);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
             int allocSize = len30 * 5;
             Span<int> alloc = (allocSize * Integers.NumBytes <= MaxStackAlloc)
                 ? stackalloc int[allocSize]
@@ -269,19 +168,33 @@ namespace Org.BouncyCastle.Math.Raw
             Span<int> F = alloc[..len30]; alloc = alloc[len30..];
             Span<int> G = alloc[..len30]; alloc = alloc[len30..];
             Span<int> M = alloc[..len30];
+#else
+            int[] t = new int[4];
+            int[] D = new int[len30];
+            int[] E = new int[len30];
+            int[] F = new int[len30];
+            int[] G = new int[len30];
+            int[] M = new int[len30];
+#endif
 
             E[0] = 1;
             Encode30(bits, x, G);
             Encode30(bits, m, M);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
             M.CopyTo(F);
+#else
+            Array.Copy(M, 0, F, 0, len30);
+#endif
 
-            int clz = System.Math.Max(0, bits - 1 - Nat.GetBitLength(len32, x));
-            int eta = -1 - clz;
+            // We use the original safegcd here, with eta == 1 - delta
+            // For shorter x, configure as if low zeros of x had been shifted away by divsteps
+            int eta = -clz;
             int lenDE = len30, lenFG = len30;
             int m0Inv32 = (int)Inverse32((uint)M[0]);
-            int maxDivsteps = GetMaximumDivsteps(bits) - clz;
+            int maxDivsteps = GetMaximumDivsteps(bits);
 
-            int divsteps = 0;
+            int divsteps = clz;
             while (!EqualToVar(lenFG, G, 0))
             {
                 if (divsteps >= maxDivsteps)
@@ -328,10 +241,12 @@ namespace Org.BouncyCastle.Math.Raw
 
             return true;
         }
-#endif
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public static uint ModOddIsCoprime(ReadOnlySpan<uint> m, ReadOnlySpan<uint> x)
+#else
+        public static uint ModOddIsCoprime(uint[] m, uint[] x)
+#endif
         {
             int len32 = m.Length;
             Debug.Assert(len32 > 0);
@@ -341,6 +256,7 @@ namespace Org.BouncyCastle.Math.Raw
             int bits = (len32 << 5) - Integers.NumberOfLeadingZeros((int)m[len32 - 1]);
             int len30 = (bits + 29) / 30;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
             int allocSize = len30 * 3;
             Span<int> alloc = (allocSize * Integers.NumBytes <= MaxStackAlloc)
                 ? stackalloc int[allocSize]
@@ -350,51 +266,29 @@ namespace Org.BouncyCastle.Math.Raw
             Span<int> F = alloc[..len30]; alloc = alloc[len30..];
             Span<int> G = alloc[..len30]; alloc = alloc[len30..];
             Span<int> M = alloc[..len30];
-
-            Encode30(bits, x, G);
-            Encode30(bits, m, M);
-            M.CopyTo(F);
-
-            int delta = 0;
-            int maxDivsteps = GetMaximumDivsteps(bits);
-
-            for (int divSteps = 0; divSteps < maxDivsteps; divSteps += 30)
-            {
-                delta = Divsteps30(delta, F[0], G[0], t);
-                UpdateFG30(len30, F, G, t);
-            }
-
-            int signF = F[len30 - 1] >> 31;
-            CNegate30(len30, signF, F);
-
-            return (uint)(EqualTo(len30, F, 1) & EqualTo(len30, G, 0));
-        }
 #else
-        public static uint ModOddIsCoprime(uint[] m, uint[] x)
-        {
-            int len32 = m.Length;
-            Debug.Assert(len32 > 0);
-            Debug.Assert((m[0] & 1) != 0);
-            Debug.Assert(m[len32 - 1] != 0);
-
-            int bits = (len32 << 5) - Integers.NumberOfLeadingZeros((int)m[len32 - 1]);
-            int len30 = (bits + 29) / 30;
-
             int[] t = new int[4];
             int[] F = new int[len30];
             int[] G = new int[len30];
             int[] M = new int[len30];
+#endif
 
-            Encode30(bits, x, 0, G, 0);
-            Encode30(bits, m, 0, M, 0);
+            Encode30(bits, x, G);
+            Encode30(bits, m, M);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            M.CopyTo(F);
+#else
             Array.Copy(M, 0, F, 0, len30);
+#endif
 
-            int delta = 0;
-            int maxDivsteps = GetMaximumDivsteps(bits);
+            // We use the "half delta" variant here, with theta == delta - 1/2
+            int theta = 0;
+            int maxDivsteps = GetMaximumHDDivsteps(bits);
 
             for (int divSteps = 0; divSteps < maxDivsteps; divSteps += 30)
             {
-                delta = Divsteps30(delta, F[0], G[0], t);
+                theta = HDDivsteps30(theta, F[0], G[0], t);
                 UpdateFG30(len30, F, G, t);
             }
 
@@ -403,10 +297,12 @@ namespace Org.BouncyCastle.Math.Raw
 
             return (uint)(EqualTo(len30, F, 1) & EqualTo(len30, G, 0));
         }
-#endif
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public static bool ModOddIsCoprimeVar(ReadOnlySpan<uint> m, ReadOnlySpan<uint> x)
+#else
+        public static bool ModOddIsCoprimeVar(uint[] m, uint[] x)
+#endif
         {
             int len32 = m.Length;
             Debug.Assert(len32 > 0);
@@ -416,6 +312,10 @@ namespace Org.BouncyCastle.Math.Raw
             int bits = (len32 << 5) - Integers.NumberOfLeadingZeros((int)m[len32 - 1]);
             int len30 = (bits + 29) / 30;
 
+            int clz = bits - Nat.GetBitLength(len32, x);
+            Debug.Assert(clz >= 0);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
             int allocSize = len30 * 3;
             Span<int> alloc = (allocSize * Integers.NumBytes <= MaxStackAlloc)
                 ? stackalloc int[allocSize]
@@ -425,64 +325,29 @@ namespace Org.BouncyCastle.Math.Raw
             Span<int> F = alloc[..len30]; alloc = alloc[len30..];
             Span<int> G = alloc[..len30]; alloc = alloc[len30..];
             Span<int> M = alloc[..len30];
-
-            Encode30(bits, x, G);
-            Encode30(bits, m, M);
-            M.CopyTo(F);
-
-            int clz = System.Math.Max(0, bits - 1 - Nat.GetBitLength(len32, x));
-            int eta = -1 - clz;
-            int lenFG = len30;
-            int maxDivsteps = GetMaximumDivsteps(bits) - clz;
-
-            int divsteps = 0;
-            while (!EqualToVar(lenFG, G, 0))
-            {
-                if (divsteps >= maxDivsteps)
-                    return false;
-
-                divsteps += 30;
-
-                eta = Divsteps30Var(eta, F[0], G[0], t);
-                UpdateFG30(lenFG, F, G, t);
-                lenFG = TrimFG30Var(lenFG, F, G);
-            }
-
-            int signF = F[lenFG - 1] >> 31;
-            if (signF < 0)
-            {
-                signF = Negate30(lenFG, F);
-            }
-            Debug.Assert(0 == signF);
-
-            return EqualToVar(lenFG, F, 1);
-        }
 #else
-        public static bool ModOddIsCoprimeVar(uint[] m, uint[] x)
-        {
-            int len32 = m.Length;
-            Debug.Assert(len32 > 0);
-            Debug.Assert((m[0] & 1) != 0);
-            Debug.Assert(m[len32 - 1] != 0);
-
-            int bits = (len32 << 5) - Integers.NumberOfLeadingZeros((int)m[len32 - 1]);
-            int len30 = (bits + 29) / 30;
-
             int[] t = new int[4];
             int[] F = new int[len30];
             int[] G = new int[len30];
             int[] M = new int[len30];
+#endif
+
+            Encode30(bits, x, G);
+            Encode30(bits, m, M);
 
-            Encode30(bits, x, 0, G, 0);
-            Encode30(bits, m, 0, M, 0);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            M.CopyTo(F);
+#else
             Array.Copy(M, 0, F, 0, len30);
+#endif
 
-            int clz = System.Math.Max(0, bits - 1 - Nat.GetBitLength(len32, x));
-            int eta = -1 - clz;
+            // We use the original safegcd here, with eta == 1 - delta
+            // For shorter x, configure as if low zeros of x had been shifted away by divsteps
+            int eta = -clz;
             int lenFG = len30;
-            int maxDivsteps = GetMaximumDivsteps(bits) - clz;
+            int maxDivsteps = GetMaximumDivsteps(bits);
 
-            int divsteps = 0;
+            int divsteps = clz;
             while (!EqualToVar(lenFG, G, 0))
             {
                 if (divsteps >= maxDivsteps)
@@ -504,7 +369,6 @@ namespace Org.BouncyCastle.Math.Raw
 
             return EqualToVar(lenFG, F, 1);
         }
-#endif
 
         public static uint[] Random(SecureRandom random, uint[] p)
         {
@@ -651,34 +515,16 @@ namespace Org.BouncyCastle.Math.Raw
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         private static void Decode30(int bits, ReadOnlySpan<int> x, Span<uint> z)
-        {
-            Debug.Assert(bits > 0);
-
-            int avail = 0;
-            ulong data = 0UL;
-
-            int xOff = 0, zOff = 0;
-            while (bits > 0)
-            {
-                while (avail < System.Math.Min(32, bits))
-                {
-                    data |= (ulong)x[xOff++] << avail;
-                    avail += 30;
-                }
-
-                z[zOff++] = (uint)data; data >>= 32;
-                avail -= 32;
-                bits -= 32;
-            }
-        }
 #else
-        private static void Decode30(int bits, int[] x, int xOff, uint[] z, int zOff)
+        private static void Decode30(int bits, int[] x, uint[] z)
+#endif
         {
             Debug.Assert(bits > 0);
 
             int avail = 0;
             ulong data = 0UL;
 
+            int xOff = 0, zOff = 0;
             while (bits > 0)
             {
                 while (avail < System.Math.Min(32, bits))
@@ -692,53 +538,6 @@ namespace Org.BouncyCastle.Math.Raw
                 bits -= 32;
             }
         }
-#endif
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        private static int Divsteps30(int delta, int f0, int g0, Span<int> t)
-#else
-        private static int Divsteps30(int delta, int f0, int g0, int[] t)
-#endif
-        {
-            int u = 1 << 30, v = 0, q = 0, r = 1 << 30;
-            int f = f0, g = g0;
-
-            for (int i = 0; i < 30; ++i)
-            {
-                Debug.Assert((f & 1) == 1);
-                Debug.Assert(((u >> (30 - i)) * f0 + (v >> (30 - i)) * g0) == f << i);
-                Debug.Assert(((q >> (30 - i)) * f0 + (r >> (30 - i)) * g0) == g << i);
-
-                int c1 = delta >> 31;
-                int c2 = -(g & 1);
-
-                int x = f ^ c1;
-                int y = u ^ c1;
-                int z = v ^ c1;
-
-                g -= x & c2;
-                q -= y & c2;
-                r -= z & c2;
-
-                c2 &= ~c1;
-                delta = (delta ^ c2) - (c2 - 1);
-
-                f += g & c2;
-                u += q & c2;
-                v += r & c2;
-
-                g >>= 1;
-                q >>= 1;
-                r >>= 1;
-            }
-
-            t[0] = u;
-            t[1] = v;
-            t[2] = q;
-            t[3] = r;
-
-            return delta;
-        }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         private static int Divsteps30Var(int eta, int f0, int g0, Span<int> t)
@@ -750,7 +549,7 @@ namespace Org.BouncyCastle.Math.Raw
             int f = f0, g = g0, m, w, x, y, z;
             int i = 30, limit, zeros;
 
-            for (; ; )
+            for (;;)
             {
                 // Use a sentinel bit to count zeros only up to i.
                 zeros = Integers.NumberOfTrailingZeros(g | (-1 << i));
@@ -769,15 +568,15 @@ namespace Org.BouncyCastle.Math.Raw
                 Debug.Assert((u * f0 + v * g0) == f << (30 - i));
                 Debug.Assert((q * f0 + r * g0) == g << (30 - i));
 
-                if (eta < 0)
+                if (eta <= 0)
                 {
-                    eta = -eta;
+                    eta = 2 - eta;
                     x = f; f = g; g = -x;
                     y = u; u = q; q = -y;
                     z = v; v = r; r = -z;
 
                     // Handle up to 6 divsteps at once, subject to eta and i.
-                    limit = (eta + 1) > i ? i : (eta + 1);
+                    limit = eta > i ? i : eta;
                     m = (int)((uint.MaxValue >> (32 - limit)) & 63U);
 
                     w = (f * g * (f * f - 2)) & m;
@@ -785,11 +584,11 @@ namespace Org.BouncyCastle.Math.Raw
                 else
                 {
                     // Handle up to 4 divsteps at once, subject to eta and i.
-                    limit = (eta + 1) > i ? i : (eta + 1);
+                    limit = eta > i ? i : eta;
                     m = (int)((uint.MaxValue >> (32 - limit)) & 15U);
 
                     w = f + (((f + 1) & 4) << 1);
-                    w = (-w * g) & m;
+                    w = (w * -g) & m;
                 }
 
                 g += f * w;
@@ -809,34 +608,16 @@ namespace Org.BouncyCastle.Math.Raw
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         private static void Encode30(int bits, ReadOnlySpan<uint> x, Span<int> z)
-        {
-            Debug.Assert(bits > 0);
-
-            int avail = 0;
-            ulong data = 0UL;
-
-            int xOff = 0, zOff = 0;
-            while (bits > 0)
-            {
-                if (avail < System.Math.Min(30, bits))
-                {
-                    data |= (x[xOff++] & M32UL) << avail;
-                    avail += 32;
-                }
-
-                z[zOff++] = (int)data & M30; data >>= 30;
-                avail -= 30;
-                bits -= 30;
-            }
-        }
 #else
-        private static void Encode30(int bits, uint[] x, int xOff, int[] z, int zOff)
+        private static void Encode30(int bits, uint[] x, int[] z)
+#endif
         {
             Debug.Assert(bits > 0);
 
             int avail = 0;
             ulong data = 0UL;
 
+            int xOff = 0, zOff = 0;
             while (bits > 0)
             {
                 if (avail < System.Math.Min(30, bits))
@@ -850,7 +631,6 @@ namespace Org.BouncyCastle.Math.Raw
                 bits -= 30;
             }
         }
-#endif
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         private static int EqualTo(int len, ReadOnlySpan<int> x, int y)
@@ -886,7 +666,60 @@ namespace Org.BouncyCastle.Math.Raw
 
         private static int GetMaximumDivsteps(int bits)
         {
-            return (49 * bits + (bits < 46 ? 80 : 47)) / 17;
+            //return (49 * bits + (bits < 46 ? 80 : 47)) / 17;
+            return (int)((188898L * bits + (bits < 46 ? 308405 : 181188)) >> 16);
+        }
+
+        private static int GetMaximumHDDivsteps(int bits)
+        {
+            //return (int)((45907L * bits + 30179) / 19929);
+            return (int)((150964L * bits + 99243) >> 16);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private static int HDDivsteps30(int theta, int f0, int g0, Span<int> t)
+#else
+        private static int HDDivsteps30(int theta, int f0, int g0, int[] t)
+#endif
+        {
+            int u = 1 << 30, v = 0, q = 0, r = 1 << 30;
+            int f = f0, g = g0;
+
+            for (int i = 0; i < 30; ++i)
+            {
+                Debug.Assert((f & 1) == 1);
+                Debug.Assert(((u >> (30 - i)) * f0 + (v >> (30 - i)) * g0) == f << i);
+                Debug.Assert(((q >> (30 - i)) * f0 + (r >> (30 - i)) * g0) == g << i);
+
+                int c1 = theta >> 31;
+                int c2 = -(g & 1);
+
+                int x = f ^ c1;
+                int y = u ^ c1;
+                int z = v ^ c1;
+
+                g -= x & c2;
+                q -= y & c2;
+                r -= z & c2;
+
+                int c3 = c2 & ~c1;
+                theta = (theta ^ c3) + 1;
+
+                f += g & c3;
+                u += q & c3;
+                v += r & c3;
+
+                g >>= 1;
+                q >>= 1;
+                r >>= 1;
+            }
+
+            t[0] = u;
+            t[1] = v;
+            t[2] = q;
+            t[3] = r;
+
+            return theta;
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
diff --git a/crypto/src/util/BigIntegers.cs b/crypto/src/util/BigIntegers.cs
index 8c2068688..419e98701 100644
--- a/crypto/src/util/BigIntegers.cs
+++ b/crypto/src/util/BigIntegers.cs
@@ -190,7 +190,7 @@ namespace Org.BouncyCastle.Utilities
                 throw new ArgumentException("must be odd", nameof(M));
             if (M.SignValue != 1)
                 throw new ArithmeticException("BigInteger: modulus not positive");
-            if (X.SignValue < 0 || X.CompareTo(M) >= 0)
+            if (X.SignValue < 0 || X.BitLength > M.BitLength)
             {
                 X = X.Mod(M);
             }
@@ -231,7 +231,7 @@ namespace Org.BouncyCastle.Utilities
                 throw new ArithmeticException("BigInteger: modulus not positive");
             if (M.Equals(One))
                 return Zero;
-            if (X.SignValue < 0 || X.CompareTo(M) >= 0)
+            if (X.SignValue < 0 || X.BitLength > M.BitLength)
             {
                 X = X.Mod(M);
             }
@@ -272,7 +272,7 @@ namespace Org.BouncyCastle.Utilities
                 throw new ArgumentException("must be odd", nameof(M));
             if (M.SignValue != 1)
                 throw new ArithmeticException("BigInteger: modulus not positive");
-            if (X.SignValue < 0 || X.CompareTo(M) >= 0)
+            if (X.SignValue < 0 || X.BitLength > M.BitLength)
             {
                 X = X.Mod(M);
             }
@@ -304,10 +304,12 @@ namespace Org.BouncyCastle.Utilities
                 throw new ArgumentException("must be odd", nameof(M));
             if (M.SignValue != 1)
                 throw new ArithmeticException("BigInteger: modulus not positive");
-            if (X.SignValue < 0 || X.CompareTo(M) >= 0)
+            if (X.SignValue < 0 || X.BitLength > M.BitLength)
             {
                 X = X.Mod(M);
             }
+            if (X.Equals(One))
+                return true;
 
             int bits = M.BitLength;