summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2020-09-04 23:57:27 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2020-09-04 23:57:27 +0700
commit86a4479929bd5f3fa5ce2cabfe6a4ebb53944df4 (patch)
tree2610425aacd90c6153402495afa3ea84077c741c
parentRemove unnecessary locking (diff)
downloadBouncyCastle.NET-ed25519-86a4479929bd5f3fa5ce2cabfe6a4ebb53944df4.tar.xz
'safegcd' modular inversion
-rw-r--r--crypto/src/bcpg/RsaSecretBcpgKey.cs7
-rw-r--r--crypto/src/crypto/engines/RSABlindedEngine.cs2
-rw-r--r--crypto/src/crypto/engines/RSABlindingEngine.cs3
-rw-r--r--crypto/src/crypto/generators/RsaKeyPairGenerator.cs2
-rw-r--r--crypto/src/crypto/parameters/ECDomainParameters.cs2
-rw-r--r--crypto/src/crypto/signers/DsaSigner.cs6
-rw-r--r--crypto/src/crypto/signers/ECDsaSigner.cs5
-rw-r--r--crypto/src/crypto/signers/ECGOST3410Signer.cs2
-rw-r--r--crypto/src/crypto/signers/SM2Signer.cs3
-rw-r--r--crypto/src/math/ec/ECFieldElement.cs9
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519Field.cs51
-rw-r--r--crypto/src/math/ec/custom/gm/SM2P256V1Field.cs55
-rw-r--r--crypto/src/math/ec/custom/sec/SecP128R1Field.cs46
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R1Field.cs53
-rw-r--r--crypto/src/math/ec/custom/sec/SecP160R2Field.cs65
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Field.cs62
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Field.cs53
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Field.cs62
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Field.cs49
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Field.cs60
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Field.cs55
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Field.cs61
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Field.cs57
-rw-r--r--crypto/src/math/ec/rfc7748/X25519Field.cs76
-rw-r--r--crypto/src/math/ec/rfc7748/X448Field.cs80
-rw-r--r--crypto/src/math/ec/rfc8032/Ed25519.cs2
-rw-r--r--crypto/src/math/ec/rfc8032/Ed448.cs2
-rw-r--r--crypto/src/math/raw/Mod.cs554
-rw-r--r--crypto/src/math/raw/Nat.cs66
-rw-r--r--crypto/src/util/BigIntegers.cs50
-rw-r--r--crypto/src/util/Integers.cs16
31 files changed, 770 insertions, 846 deletions
diff --git a/crypto/src/bcpg/RsaSecretBcpgKey.cs b/crypto/src/bcpg/RsaSecretBcpgKey.cs
index 5c04d9f85..783f083ce 100644
--- a/crypto/src/bcpg/RsaSecretBcpgKey.cs
+++ b/crypto/src/bcpg/RsaSecretBcpgKey.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Bcpg
 {
@@ -21,7 +22,7 @@ namespace Org.BouncyCastle.Bcpg
 
 			this.expP = d.Value.Remainder(p.Value.Subtract(BigInteger.One));
 			this.expQ = d.Value.Remainder(q.Value.Subtract(BigInteger.One));
-			this.crt = q.Value.ModInverse(p.Value);
+			this.crt = BigIntegers.ModOddInverse(p.Value, q.Value);
 		}
 
 		public RsaSecretBcpgKey(
@@ -44,11 +45,11 @@ namespace Org.BouncyCastle.Bcpg
 			this.d = new MPInteger(d);
 			this.p = new MPInteger(p);
 			this.q = new MPInteger(q);
-			this.u = new MPInteger(p.ModInverse(q));
+			this.u = new MPInteger(BigIntegers.ModOddInverse(q, p));
 
 			this.expP = d.Remainder(p.Subtract(BigInteger.One));
 			this.expQ = d.Remainder(q.Subtract(BigInteger.One));
-			this.crt = q.ModInverse(p);
+			this.crt = BigIntegers.ModOddInverse(p, q);
 		}
 
 		public BigInteger Modulus
diff --git a/crypto/src/crypto/engines/RSABlindedEngine.cs b/crypto/src/crypto/engines/RSABlindedEngine.cs
index 98108f9b4..637bf3cc0 100644
--- a/crypto/src/crypto/engines/RSABlindedEngine.cs
+++ b/crypto/src/crypto/engines/RSABlindedEngine.cs
@@ -132,7 +132,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                     BigInteger blindedInput = r.ModPow(e, m).Multiply(input).Mod(m);
                     BigInteger blindedResult = core.ProcessBlock(blindedInput);
 
-                    BigInteger rInv = r.ModInverse(m);
+                    BigInteger rInv = BigIntegers.ModOddInverse(m, r);
                     result = blindedResult.Multiply(rInv).Mod(m);
 
                     // defence against Arjen Lenstra’s CRT attack
diff --git a/crypto/src/crypto/engines/RSABlindingEngine.cs b/crypto/src/crypto/engines/RSABlindingEngine.cs
index 1289456a6..11bb8d9d9 100644
--- a/crypto/src/crypto/engines/RSABlindingEngine.cs
+++ b/crypto/src/crypto/engines/RSABlindingEngine.cs
@@ -2,6 +2,7 @@ using System;
 
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
 {
@@ -139,7 +140,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 			BigInteger m = key.Modulus;
 			BigInteger msg = blindedMsg;
-			BigInteger blindFactorInverse = blindingFactor.ModInverse(m);
+			BigInteger blindFactorInverse = BigIntegers.ModOddInverse(m, blindingFactor);
 			msg = msg.Multiply(blindFactorInverse);
 			msg = msg.Mod(m);
 
diff --git a/crypto/src/crypto/generators/RsaKeyPairGenerator.cs b/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
index 449976550..78c9eb18f 100644
--- a/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
+++ b/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
@@ -127,7 +127,7 @@ namespace Org.BouncyCastle.Crypto.Generators
                 //
                 BigInteger dP = d.Remainder(pSub1);
                 BigInteger dQ = d.Remainder(qSub1);
-                BigInteger qInv = q.ModInverse(p);
+                BigInteger qInv = BigIntegers.ModOddInverse(p, q);
 
                 return new AsymmetricCipherKeyPair(
                     new RsaKeyParameters(false, n, e),
diff --git a/crypto/src/crypto/parameters/ECDomainParameters.cs b/crypto/src/crypto/parameters/ECDomainParameters.cs
index ce17ab6df..b5ca183de 100644
--- a/crypto/src/crypto/parameters/ECDomainParameters.cs
+++ b/crypto/src/crypto/parameters/ECDomainParameters.cs
@@ -89,7 +89,7 @@ namespace Org.BouncyCastle.Crypto.Parameters
                 {
                     if (hInv == null)
                     {
-                        hInv = h.ModInverse(n);
+                        hInv = BigIntegers.ModOddInverseVar(n, h);
                     }
                     return hInv;
                 }
diff --git a/crypto/src/crypto/signers/DsaSigner.cs b/crypto/src/crypto/signers/DsaSigner.cs
index 32a33d084..1f5d9b937 100644
--- a/crypto/src/crypto/signers/DsaSigner.cs
+++ b/crypto/src/crypto/signers/DsaSigner.cs
@@ -1,9 +1,9 @@
 using System;
 
-using Org.BouncyCastle.Crypto.Digests;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Signers
 {
@@ -104,7 +104,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 
             BigInteger r = parameters.G.ModPow(k, parameters.P).Mod(q);
 
-            k = k.ModInverse(q).Multiply(m.Add(x.Multiply(r)));
+            k = BigIntegers.ModOddInverse(q, k).Multiply(m.Add(x.Multiply(r)));
 
             BigInteger s = k.Mod(q);
 
@@ -132,7 +132,7 @@ namespace Org.BouncyCastle.Crypto.Signers
                 return false;
             }
 
-            BigInteger w = s.ModInverse(q);
+            BigInteger w = BigIntegers.ModOddInverseVar(q, s);
 
             BigInteger u1 = m.Multiply(w).Mod(q);
             BigInteger u2 = r.Multiply(w).Mod(q);
diff --git a/crypto/src/crypto/signers/ECDsaSigner.cs b/crypto/src/crypto/signers/ECDsaSigner.cs
index 7b04d1076..0a265d96e 100644
--- a/crypto/src/crypto/signers/ECDsaSigner.cs
+++ b/crypto/src/crypto/signers/ECDsaSigner.cs
@@ -6,6 +6,7 @@ using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Math.EC.Multiplier;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Signers
 {
@@ -123,7 +124,7 @@ namespace Org.BouncyCastle.Crypto.Signers
                 }
                 while (r.SignValue == 0);
 
-                s = k.ModInverse(n).Multiply(e.Add(d.Multiply(r))).Mod(n);
+                s = BigIntegers.ModOddInverse(n, k).Multiply(e.Add(d.Multiply(r))).Mod(n);
             }
             while (s.SignValue == 0);
 
@@ -148,7 +149,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
 
             BigInteger e = CalculateE(n, message);
-            BigInteger c = s.ModInverse(n);
+            BigInteger c = BigIntegers.ModOddInverseVar(n, s);
 
             BigInteger u1 = e.Multiply(c).Mod(n);
             BigInteger u2 = r.Multiply(c).Mod(n);
diff --git a/crypto/src/crypto/signers/ECGOST3410Signer.cs b/crypto/src/crypto/signers/ECGOST3410Signer.cs
index ac6b080a5..7df43f0a0 100644
--- a/crypto/src/crypto/signers/ECGOST3410Signer.cs
+++ b/crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -145,7 +145,7 @@ namespace Org.BouncyCastle.Crypto.Signers
                 return false;
             }
 
-            BigInteger v = e.ModInverse(n);
+            BigInteger v = BigIntegers.ModOddInverseVar(n, e);
 
             BigInteger z1 = s.Multiply(v).Mod(n);
             BigInteger z2 = (n.Subtract(r)).Multiply(v).Mod(n);
diff --git a/crypto/src/crypto/signers/SM2Signer.cs b/crypto/src/crypto/signers/SM2Signer.cs
index 2597cbf3d..c344a220a 100644
--- a/crypto/src/crypto/signers/SM2Signer.cs
+++ b/crypto/src/crypto/signers/SM2Signer.cs
@@ -6,6 +6,7 @@ using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Math.EC.Multiplier;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Crypto.Signers
@@ -164,7 +165,7 @@ namespace Org.BouncyCastle.Crypto.Signers
                 while (r.SignValue == 0 || r.Add(k).Equals(n));
 
                 // A6
-                BigInteger dPlus1ModN = d.Add(BigInteger.One).ModInverse(n);
+                BigInteger dPlus1ModN = BigIntegers.ModOddInverse(n, d.Add(BigIntegers.One));
 
                 s = k.Subtract(r.Multiply(d)).Mod(n);
                 s = dPlus1ModN.Multiply(s).Mod(n);
diff --git a/crypto/src/math/ec/ECFieldElement.cs b/crypto/src/math/ec/ECFieldElement.cs
index ef10dbf90..ed530b6b7 100644
--- a/crypto/src/math/ec/ECFieldElement.cs
+++ b/crypto/src/math/ec/ECFieldElement.cs
@@ -1,7 +1,6 @@
 using System;
 using System.Diagnostics;
 
-using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Math.EC
@@ -434,13 +433,7 @@ namespace Org.BouncyCastle.Math.EC
 
         protected virtual BigInteger ModInverse(BigInteger x)
         {
-            int bits = FieldSize;
-            int len = (bits + 31) >> 5;
-            uint[] p = Nat.FromBigInteger(bits, q);
-            uint[] n = Nat.FromBigInteger(bits, x);
-            uint[] z = Nat.Create(len);
-            Mod.Invert(p, n, z);
-            return Nat.ToBigInteger(len, z);
+            return BigIntegers.ModOddInverse(q, x);
         }
 
         protected virtual BigInteger ModMult(BigInteger x1, BigInteger x2)
diff --git a/crypto/src/math/ec/custom/djb/Curve25519Field.cs b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
index 4e4cfbaa5..0006acd94 100644
--- a/crypto/src/math/ec/custom/djb/Curve25519Field.cs
+++ b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
@@ -70,56 +70,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Djb
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^255 - 21
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 250 1s } { 1 0s } { 1 1s } { 1 0s } { 2 1s }
-             *
-             * Therefore we need an addition chain containing 1, 2, 250 (the lengths of the repunits)
-             * We use: [1], [2], 3, 5, 10, 15, 25, 50, 75, 125, [250]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat256.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = Nat256.Create();
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x5 = x3;
-            SquareN(x3, 2, x5);
-            Multiply(x5, x2, x5);
-            uint[] x10 = Nat256.Create();
-            SquareN(x5, 5, x10);
-            Multiply(x10, x5, x10);
-            uint[] x15 = Nat256.Create();
-            SquareN(x10, 5, x15);
-            Multiply(x15, x5, x15);
-            uint[] x25 = x5;
-            SquareN(x15, 10, x25);
-            Multiply(x25, x10, x25);
-            uint[] x50 = x10;
-            SquareN(x25, 25, x50);
-            Multiply(x50, x25, x50);
-            uint[] x75 = x15;
-            SquareN(x50, 25, x75);
-            Multiply(x75, x25, x75);
-            uint[] x125 = x25;
-            SquareN(x75, 50, x125);
-            Multiply(x125, x50, x125);
-            uint[] x250 = x50;
-            SquareN(x125, 125, x250);
-            Multiply(x250, x125, x250);
-
-            uint[] t = x250;
-            SquareN(t, 2, t);
-            Multiply(t, x1, t);
-            SquareN(t, 3, t);
-            Multiply(t, x2, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/gm/SM2P256V1Field.cs b/crypto/src/math/ec/custom/gm/SM2P256V1Field.cs
index 55596b844..38743189a 100644
--- a/crypto/src/math/ec/custom/gm/SM2P256V1Field.cs
+++ b/crypto/src/math/ec/custom/gm/SM2P256V1Field.cs
@@ -57,60 +57,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.GM
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^256 - 2^224 - 2^96 + 2^64 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 31 1s } { 1 0s } { 128 1s } { 32 0s } { 62 1s } { 1 0s } { 1 1s }
-             *
-             * We use an addition chain for the beginning: [1], 2, [4], 6, 12, 24, 30, [31] 
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat256.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x4 = Nat256.Create();
-            SquareN(x2, 2, x4);
-            Multiply(x4, x2, x4);
-            uint[] x6 = Nat256.Create();
-            SquareN(x4, 2, x6);
-            Multiply(x6, x2, x6);
-            uint[] x12 = x2;
-            SquareN(x6, 6, x12);
-            Multiply(x12, x6, x12);
-            uint[] x24 = Nat256.Create();
-            SquareN(x12, 12, x24);
-            Multiply(x24, x12, x24);
-            uint[] x30 = x12;
-            SquareN(x24, 6, x30);
-            Multiply(x30, x6, x30);
-            uint[] x31 = x6;
-            Square(x30, x31);
-            Multiply(x31, x1, x31);
-
-            uint[] t = x24;
-            SquareN(x31, 32, t);
-            Multiply(t, x31, t);
-            SquareN(t, 31, t);
-            Multiply(t, x31, t);
-            SquareN(t, 31, t);
-            Multiply(t, x31, t);
-            SquareN(t, 31, t);
-            Multiply(t, x31, t);
-            SquareN(t, 4, t);
-            Multiply(t, x4, t);
-            SquareN(t, 63, t);
-            Multiply(t, x31, t);
-            SquareN(t, 31, t);
-            Multiply(t, x31, t);
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static void Half(uint[] x, uint[] z)
diff --git a/crypto/src/math/ec/custom/sec/SecP128R1Field.cs b/crypto/src/math/ec/custom/sec/SecP128R1Field.cs
index 23ea361a0..03a07f79b 100644
--- a/crypto/src/math/ec/custom/sec/SecP128R1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP128R1Field.cs
@@ -70,51 +70,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^128 - 2^97 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 30 1s } { 1 0s } { 95 1s } { 1 0s } { 1 1s }
-             *
-             * We use an addition chain for the beginning: [1], 2, 3, [5], 10, 20, [30]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat128.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = Nat128.Create();
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x5 = x3;
-            SquareN(x3, 2, x5);
-            Multiply(x5, x2, x5);
-            uint[] x10 = x2;
-            SquareN(x5, 5, x10);
-            Multiply(x10, x5, x10);
-            uint[] x20 = Nat128.Create();
-            SquareN(x10, 10, x20);
-            Multiply(x20, x10, x20);
-            uint[] x30 = x20;
-            SquareN(x20, 10, x30);
-            Multiply(x30, x10, x30);
-
-            uint[] t = x10;
-            SquareN(x30, 31, t);
-            Multiply(t, x30, t);
-            SquareN(t, 30, t);
-            Multiply(t, x30, t);
-            SquareN(t, 30, t);
-            Multiply(t, x30, t);
-            SquareN(t, 5, t);
-            Multiply(t, x5, t);
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP160R1Field.cs b/crypto/src/math/ec/custom/sec/SecP160R1Field.cs
index 139cd80d6..31c957301 100644
--- a/crypto/src/math/ec/custom/sec/SecP160R1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP160R1Field.cs
@@ -74,58 +74,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^160 - 2^31 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 128 1s } { 1 0s } { 29 1s } { 1 0s } { 1 1s }
-             *
-             * Therefore we need an addition chain containing 1, 29, 128 (the lengths of the repunits)
-             * We use: [1], 2, 3, 6, 12, 24, 27, [29], 32, 64, [128]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat160.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = Nat160.Create();
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x6 = Nat160.Create();
-            SquareN(x3, 3, x6);
-            Multiply(x6, x3, x6);
-            uint[] x12 = Nat160.Create();
-            SquareN(x6, 6, x12);
-            Multiply(x12, x6, x12);
-            uint[] x24 = x6;
-            SquareN(x12, 12, x24);
-            Multiply(x24, x12, x24);
-            uint[] x27 = x12;
-            SquareN(x24, 3, x27);
-            Multiply(x27, x3, x27);
-            uint[] x29 = x24;
-            SquareN(x27, 2, x29);
-            Multiply(x29, x2, x29);
-            uint[] x32 = x2;
-            SquareN(x29, 3, x32);
-            Multiply(x32, x3, x32);
-            uint[] x64 = x3;
-            SquareN(x32, 32, x64);
-            Multiply(x64, x32, x64);
-            uint[] x128 = x27;
-            SquareN(x64, 64, x128);
-            Multiply(x128, x64, x128);
-
-            uint[] t = x128;
-            SquareN(t, 30, t);
-            Multiply(t, x29, t);
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP160R2Field.cs b/crypto/src/math/ec/custom/sec/SecP160R2Field.cs
index bc36d9de1..55f02e438 100644
--- a/crypto/src/math/ec/custom/sec/SecP160R2Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP160R2Field.cs
@@ -74,70 +74,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 127 1s } { 1 0s } { 17 1s } "010110001110001"
-             *
-             * Therefore we need an addition chain containing 1, 2, 3, 17, 127 (the lengths of the repunits)
-             * We use: 1, 2, 3, 6, 12, 15, [17], 34, 68, 102, 119, 125, [127]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat160.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = Nat160.Create();
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x6 = Nat160.Create();
-            SquareN(x3, 3, x6);
-            Multiply(x6, x3, x6);
-            uint[] x12 = Nat160.Create();
-            SquareN(x6, 6, x12);
-            Multiply(x12, x6, x12);
-            uint[] x15 = x12;
-            SquareN(x12, 3, x15);
-            Multiply(x15, x3, x15);
-            uint[] x17 = x15;
-            SquareN(x15, 2, x17);
-            Multiply(x17, x2, x17);
-            uint[] x34 = Nat160.Create();
-            SquareN(x17, 17, x34);
-            Multiply(x34, x17, x34);
-            uint[] x68 = Nat160.Create();
-            SquareN(x34, 34, x68);
-            Multiply(x68, x34, x68);
-            uint[] x102 = x68;
-            SquareN(x68, 34, x102);
-            Multiply(x102, x34, x102);
-            uint[] x119 = x34;
-            SquareN(x102, 17, x119);
-            Multiply(x119, x17, x119);
-            uint[] x125 = x102;
-            SquareN(x119, 6, x125);
-            Multiply(x125, x6, x125);
-            uint[] x127 = x6;
-            SquareN(x125, 2, x127);
-            Multiply(x127, x2, x127);
-
-            uint[] t = x127;
-            SquareN(t, 18, t);
-            Multiply(t, x17, t);
-            SquareN(t, 2, t);
-            Multiply(t, x1, t);
-            SquareN(t, 3, t);
-            Multiply(t, x2, t);
-            SquareN(t, 6, t);
-            Multiply(t, x3, t);
-            SquareN(t, 4, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Field.cs b/crypto/src/math/ec/custom/sec/SecP192K1Field.cs
index 30d53f7dc..23bd732bd 100644
--- a/crypto/src/math/ec/custom/sec/SecP192K1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP192K1Field.cs
@@ -75,67 +75,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 159 1s } { 1 0s } { 19 1s } { 1 0s } { 3 1s } "000110101"
-             *
-             * Therefore we need an addition chain containing 1, 2, 3, 19, 159 (the lengths of the repunits)
-             * We use: [1], [2], [3], 6, 12, 18, [19], 38, 76, 152, 158, [159]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat192.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = Nat192.Create();
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x6 = Nat192.Create();
-            SquareN(x3, 3, x6);
-            Multiply(x6, x3, x6);
-            uint[] x12 = Nat192.Create();
-            SquareN(x6, 6, x12);
-            Multiply(x12, x6, x12);
-            uint[] x18 = x12;
-            SquareN(x12, 6, x18);
-            Multiply(x18, x6, x18);
-            uint[] x19 = x18;
-            Square(x18, x19);
-            Multiply(x19, x1, x19);
-            uint[] x38 = Nat192.Create();
-            SquareN(x19, 19, x38);
-            Multiply(x38, x19, x38);
-            uint[] x76 = Nat192.Create();
-            SquareN(x38, 38, x76);
-            Multiply(x76, x38, x76);
-            uint[] x152 = x38;
-            SquareN(x76, 76, x152);
-            Multiply(x152, x76, x152);
-            uint[] x158 = x76;
-            SquareN(x152, 6, x158);
-            Multiply(x158, x6, x158);
-            uint[] x159 = x6;
-            Square(x158, x159);
-            Multiply(x159, x1, x159);
-
-            uint[] t = x159;
-            SquareN(t, 20, t);
-            Multiply(t, x19, t);
-            SquareN(t, 4, t);
-            Multiply(t, x3, t);
-            SquareN(t, 5, t);
-            Multiply(t, x2, t);
-            SquareN(t, 2, t);
-            Multiply(t, x1, t);
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Field.cs b/crypto/src/math/ec/custom/sec/SecP192R1Field.cs
index 2061d1359..a4fb4bb76 100644
--- a/crypto/src/math/ec/custom/sec/SecP192R1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP192R1Field.cs
@@ -74,58 +74,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^192 - 2^64 - 1
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 127 1s } { 1 0s } { 62 1s } { 1 0s } { 1 1s }
-             *
-             * Therefore we need an addition chain containing 1, 62, 127 (the lengths of the repunits)
-             * We use: [1], 2, 3, 6, 12, 24, 30, 32, [62], 65, [127]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat192.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = Nat192.Create();
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x6 = Nat192.Create();
-            SquareN(x3, 3, x6);
-            Multiply(x6, x3, x6);
-            uint[] x12 = Nat192.Create();
-            SquareN(x6, 6, x12);
-            Multiply(x12, x6, x12);
-            uint[] x24 = Nat192.Create();
-            SquareN(x12, 12, x24);
-            Multiply(x24, x12, x24);
-            uint[] x30 = x12;
-            SquareN(x24, 6, x30);
-            Multiply(x30, x6, x30);
-            uint[] x32 = x6;
-            SquareN(x30, 2, x32);
-            Multiply(x32, x2, x32);
-            uint[] x62 = x2;
-            SquareN(x32, 30, x62);
-            Multiply(x62, x30, x62);
-            uint[] x65 = x24;
-            SquareN(x62, 3, x65);
-            Multiply(x65, x3, x65);
-            uint[] x127 = x3;
-            SquareN(x65, 62, x127);
-            Multiply(x127, x62, x127);
-
-            uint[] t = x127;
-            SquareN(t, 63, t);
-            Multiply(t, x62, t);
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Field.cs b/crypto/src/math/ec/custom/sec/SecP224K1Field.cs
index d20ac63f3..5d4237708 100644
--- a/crypto/src/math/ec/custom/sec/SecP224K1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP224K1Field.cs
@@ -76,67 +76,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 5
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 191 1s } { 1 0s } { 19 1s } "0010101101011"
-             *
-             * Therefore we need an addition chain containing 1, 2, 19, 191 (the lengths of the repunits)
-             * We use: [1], [2], 4, 5, 9, 10, [19], 38, 76, 152, 190 [191]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat224.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x4 = Nat224.Create();
-            SquareN(x2, 2, x4);
-            Multiply(x4, x2, x4);
-            uint[] x5 = Nat224.Create();
-            Square(x4, x5);
-            Multiply(x5, x1, x5);
-            uint[] x9 = x5;
-            SquareN(x5, 4, x9);
-            Multiply(x9, x4, x9);
-            uint[] x10 = x4;
-            Square(x9, x10);
-            Multiply(x10, x1, x10);
-            uint[] x19 = x10;
-            SquareN(x10, 9, x19);
-            Multiply(x19, x9, x19);
-            uint[] x38 = x9;
-            SquareN(x19, 19, x38);
-            Multiply(x38, x19, x38);
-            uint[] x76 = Nat224.Create();
-            SquareN(x38, 38, x76);
-            Multiply(x76, x38, x76);
-            uint[] x152 = Nat224.Create();
-            SquareN(x76, 76, x152);
-            Multiply(x152, x76, x152);
-            uint[] x190 = x76;
-            SquareN(x152, 38, x190);
-            Multiply(x190, x38, x190);
-            uint[] x191 = x38;
-            Square(x190, x191);
-            Multiply(x191, x1, x191);
-
-            uint[] t = x191;
-            SquareN(t, 20, t);
-            Multiply(t, x19, t);
-            SquareN(t, 3, t);
-            Multiply(t, x1, t);
-            SquareN(t, 2, t);
-            Multiply(t, x1, t);
-            SquareN(t, 3, t);
-            Multiply(t, x2, t);
-            SquareN(t, 2, t);
-            Multiply(t, x1, t);
-            SquareN(t, 3, t);
-            Multiply(t, x2, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Field.cs b/crypto/src/math/ec/custom/sec/SecP224R1Field.cs
index 06d451c2b..dde291d5e 100644
--- a/crypto/src/math/ec/custom/sec/SecP224R1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP224R1Field.cs
@@ -75,54 +75,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^224 - 2^96 - 1
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 127 1s } { 1 0s } { 96 1s }
-             *
-             * Therefore we need an addition chain containing 96, 127 (the lengths of the repunits)
-             * We use: 1, 2, 3, 6, 12, 24, 48, [96], 120, 126, [127]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat224.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = x2;
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x6 = Nat224.Create();
-            SquareN(x3, 3, x6);
-            Multiply(x6, x3, x6);
-            uint[] x12 = x3;
-            SquareN(x6, 6, x12);
-            Multiply(x12, x6, x12);
-            uint[] x24 = Nat224.Create();
-            SquareN(x12, 12, x24);
-            Multiply(x24, x12, x24);
-            uint[] x48 = x12;
-            SquareN(x24, 24, x48);
-            Multiply(x48, x24, x48);
-            uint[] x96 = Nat224.Create();
-            SquareN(x48, 48, x96);
-            Multiply(x96, x48, x96);
-            uint[] x120 = x48;
-            SquareN(x96, 24, x120);
-            Multiply(x120, x24, x120);
-            uint[] x126 = x24;
-            SquareN(x120, 6, x126);
-            Multiply(x126, x6, x126);
-            uint[] x127 = x6;
-            Square(x126, x127);
-            Multiply(x127, x1, x127);
-
-            uint[] t = x127;
-            SquareN(t, 97, t);
-            Multiply(t, x96, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Field.cs b/crypto/src/math/ec/custom/sec/SecP256K1Field.cs
index 2193c94e6..acdb1f362 100644
--- a/crypto/src/math/ec/custom/sec/SecP256K1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP256K1Field.cs
@@ -76,65 +76,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 223 1s } { 1 0s } { 22 1s } { 4 0s } { 1 1s } { 1 0s } { 2 1s } { 1 0s } { 1 1s }
-             *
-             * Therefore we need an addition chain containing 1, 2, 22, 223 (the lengths of the repunits)
-             * We use: [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat256.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = Nat256.Create();
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x6 = Nat256.Create();
-            SquareN(x3, 3, x6);
-            Multiply(x6, x3, x6);
-            uint[] x9 = x6;
-            SquareN(x6, 3, x9);
-            Multiply(x9, x3, x9);
-            uint[] x11 = x9;
-            SquareN(x9, 2, x11);
-            Multiply(x11, x2, x11);
-            uint[] x22 = Nat256.Create();
-            SquareN(x11, 11, x22);
-            Multiply(x22, x11, x22);
-            uint[] x44 = x11;
-            SquareN(x22, 22, x44);
-            Multiply(x44, x22, x44);
-            uint[] x88 = Nat256.Create();
-            SquareN(x44, 44, x88);
-            Multiply(x88, x44, x88);
-            uint[] x176 = Nat256.Create();
-            SquareN(x88, 88, x176);
-            Multiply(x176, x88, x176);
-            uint[] x220 = x88;
-            SquareN(x176, 44, x220);
-            Multiply(x220, x44, x220);
-            uint[] x223 = x44;
-            SquareN(x220, 3, x223);
-            Multiply(x223, x3, x223);
-
-            uint[] t = x223;
-            SquareN(t, 23, t);
-            Multiply(t, x22, t);
-            SquareN(t, 5, t);
-            Multiply(t, x1, t);
-            SquareN(t, 3, t);
-            Multiply(t, x2, t);
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Field.cs b/crypto/src/math/ec/custom/sec/SecP256R1Field.cs
index eadc7ee58..668efc895 100644
--- a/crypto/src/math/ec/custom/sec/SecP256R1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP256R1Field.cs
@@ -70,60 +70,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^256 - 2^224 + 2^192 + 2^96 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 32 1s } { 31 0s } { 1 1s } { 96 0s } { 94 1s } { 1 0s } { 1 1s }
-             *
-             * Therefore we need an addition chain containing 1, 32, 94 (the lengths of the repunits)
-             * We use: [1], 2, 4, 8, 16, [32], 64, 80, 88, 92, [94]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat256.Create();
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x4 = Nat256.Create();
-            SquareN(x2, 2, x4);
-            Multiply(x4, x2, x4);
-            uint[] x8 = Nat256.Create();
-            SquareN(x4, 4, x8);
-            Multiply(x8, x4, x8);
-            uint[] x16 = Nat256.Create();
-            SquareN(x8, 8, x16);
-            Multiply(x16, x8, x16);
-            uint[] x32 = Nat256.Create();
-            SquareN(x16, 16, x32);
-            Multiply(x32, x16, x32);
-            uint[] x64 = Nat256.Create();
-            SquareN(x32, 32, x64);
-            Multiply(x64, x32, x64);
-            uint[] x80 = x64;
-            SquareN(x64, 16, x80);
-            Multiply(x80, x16, x80);
-            uint[] x88 = x16;
-            SquareN(x80, 8, x88);
-            Multiply(x88, x8, x88);
-            uint[] x92 = x8;
-            SquareN(x88, 4, x92);
-            Multiply(x92, x4, x92);
-            uint[] x94 = x4;
-            SquareN(x92, 2, x94);
-            Multiply(x94, x2, x94);
-
-            uint[] t = x32;
-            SquareN(t, 32, t);
-            Multiply(t, x1, t);
-            SquareN(t, 190, t);
-            Multiply(t, x94, t);
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Field.cs b/crypto/src/math/ec/custom/sec/SecP384R1Field.cs
index 9b20db1b0..cddb46895 100644
--- a/crypto/src/math/ec/custom/sec/SecP384R1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP384R1Field.cs
@@ -77,66 +77,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^384 - 2^128 - 2^96 + 2^32 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 255 1s } { 1 0s } { 32 1s } { 64 0s } { 30 1s } { 1 0s } { 1 1s }
-             *
-             * Therefore we need an addition chain containing 1, 30, 32, 255 (the lengths of the repunits)
-             * We use: [1], 2, 3, 6, 12, 24, [30], [32], 62, 124, 248, 254, [255]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat.Create(12);
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x3 = Nat.Create(12);
-            Square(x2, x3);
-            Multiply(x3, x1, x3);
-            uint[] x6 = Nat.Create(12);
-            SquareN(x3, 3, x6);
-            Multiply(x6, x3, x6);
-            uint[] x12 = x3;
-            SquareN(x6, 6, x12);
-            Multiply(x12, x6, x12);
-            uint[] x24 = Nat.Create(12);
-            SquareN(x12, 12, x24);
-            Multiply(x24, x12, x24);
-            uint[] x30 = x12;
-            SquareN(x24, 6, x30);
-            Multiply(x30, x6, x30);
-            uint[] x32 = x24;
-            SquareN(x30, 2, x32);
-            Multiply(x32, x2, x32);
-            uint[] x62 = x2;
-            SquareN(x32, 30, x62);
-            Multiply(x62, x30, x62);
-            uint[] x124 = Nat.Create(12);
-            SquareN(x62, 62, x124);
-            Multiply(x124, x62, x124);
-            uint[] x248 = x62;
-            SquareN(x124, 124, x248);
-            Multiply(x248, x124, x248);
-            uint[] x254 = x124;
-            SquareN(x248, 6, x254);
-            Multiply(x254, x6, x254);
-            uint[] x255 = x6;
-            Square(x254, x255);
-            Multiply(x255, x1, x255);
-
-            uint[] t = x255;
-            SquareN(t, 33, t);
-            Multiply(t, x32, t);
-            SquareN(t, 94, t);
-            Multiply(t, x30, t);
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Field.cs b/crypto/src/math/ec/custom/sec/SecP521R1Field.cs
index 10b98fc21..0f1922f36 100644
--- a/crypto/src/math/ec/custom/sec/SecP521R1Field.cs
+++ b/crypto/src/math/ec/custom/sec/SecP521R1Field.cs
@@ -56,62 +56,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
 
         public static void Inv(uint[] x, uint[] z)
         {
-            /*
-             * Raise this element to the exponent 2^521 - 3
-             *
-             * Breaking up the exponent's binary representation into "repunits", we get:
-             * { 519 1s } { 1 0s } { 1 1s }
-             *
-             * Therefore we need an addition chain containing 1, 519 (the lengths of the repunits)
-             * We use: [1], 2, 4, 8, 16, 32, 64, 128, 256, 512, 516, 518, [519]
-             */
-
-            if (0 != IsZero(x))
-                throw new ArgumentException("cannot be 0", "x");
-
-            uint[] x1 = x;
-            uint[] x2 = Nat.Create(17);
-            Square(x1, x2);
-            Multiply(x2, x1, x2);
-            uint[] x4 = Nat.Create(17);
-            SquareN(x2, 2, x4);
-            Multiply(x4, x2, x4);
-            uint[] x8 = Nat.Create(17);
-            SquareN(x4, 4, x8);
-            Multiply(x8, x4, x8);
-            uint[] x16 = Nat.Create(17);
-            SquareN(x8, 8, x16);
-            Multiply(x16, x8, x16);
-            uint[] x32 = x8;
-            SquareN(x16, 16, x32);
-            Multiply(x32, x16, x32);
-            uint[] x64 = x16;
-            SquareN(x32, 32, x64);
-            Multiply(x64, x32, x64);
-            uint[] x128 = x32;
-            SquareN(x64, 64, x128);
-            Multiply(x128, x64, x128);
-            uint[] x256 = x64;
-            SquareN(x128, 128, x256);
-            Multiply(x256, x128, x256);
-            uint[] x512 = x128;
-            SquareN(x256, 256, x512);
-            Multiply(x512, x256, x512);
-            uint[] x516 = x256;
-            SquareN(x512, 4, x516);
-            Multiply(x516, x4, x516);
-            uint[] x518 = x4;
-            SquareN(x516, 2, x518);
-            Multiply(x518, x2, x518);
-            uint[] x519 = x2;
-            Square(x518, x519);
-            Multiply(x519, x1, x519);
-
-            uint[] t = x519;
-            SquareN(t, 2, t);
-
-            // NOTE that x1 and z could be the same array
-            Multiply(x1, t, z);
+            Mod.CheckedModOddInverse(P, x, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/rfc7748/X25519Field.cs b/crypto/src/math/ec/rfc7748/X25519Field.cs
index 6843e274a..ffede563b 100644
--- a/crypto/src/math/ec/rfc7748/X25519Field.cs
+++ b/crypto/src/math/ec/rfc7748/X25519Field.cs
@@ -1,6 +1,8 @@
 using System;
 using System.Diagnostics;
 
+using Org.BouncyCastle.Math.Raw;
+
 namespace Org.BouncyCastle.Math.EC.Rfc7748
 {
     public abstract class X25519Field
@@ -11,6 +13,8 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
         private const int M25 = 0x01FFFFFF;
         private const int M26 = 0x03FFFFFF;
 
+        private static readonly uint[] P32 = new uint[]{ 0xFFFFFFEDU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU,
+            0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0x7FFFFFFFU };
         private static readonly int[] RootNegOne = { 0x020EA0B0, 0x0386C9D2, 0x00478C4E, 0x0035697F, 0x005E8630,
             0x01FBD7A7, 0x0340264F, 0x01F0B2B4, 0x00027E0E, 0x00570649 };
 
@@ -128,6 +132,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
             }
         }
 
+        [CLSCompliantAttribute(false)]
+        public static void Decode(uint[] x, int xOff, int[] z)
+        {
+            Decode128(x, xOff, z, 0);
+            Decode128(x, xOff + 4, z, 5);
+            z[9] &= M24;
+        }
+
         public static void Decode(byte[] x, int xOff, int[] z)
         {
             Decode128(x, xOff, z, 0);
@@ -135,6 +147,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
             z[9] &= M24;
         }
 
+        private static void Decode128(uint[] x, int xOff, int[] z, int zOff)
+        {
+            uint t0 = x[xOff + 0], t1 = x[xOff + 1], t2 = x[xOff + 2], t3 = x[xOff + 3];
+
+            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 void Decode128(byte[] bs, int off, int[] z, int zOff)
         {
             uint t0 = Decode32(bs, off + 0);
@@ -158,12 +181,30 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
             return n;
         }
 
+        [CLSCompliantAttribute(false)]
+        public static void Encode(int[] x, uint[] z, int zOff)
+        {
+            Encode128(x, 0, z, zOff);
+            Encode128(x, 5, z, zOff + 4);
+        }
+
         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, uint[] z, int zOff)
+        {
+            uint x0 = (uint)x[xOff + 0], x1 = (uint)x[xOff + 1], x2 = (uint)x[xOff + 2], x3 = (uint)x[xOff + 3],
+                x4 = (uint)x[xOff + 4];
+
+            z[zOff + 0] =  x0        | (x1 << 26);
+            z[zOff + 1] = (x1 >>  6) | (x2 << 20);
+            z[zOff + 2] = (x2 >> 12) | (x3 << 13);
+            z[zOff + 3] = (x3 >> 19) | (x4 <<  7);
+        }
+
         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];
@@ -185,15 +226,36 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
 
         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();
+            //int[] t = Create();
+            //PowPm5d8(x, x2, t);
+            //Sqr(t, 3, t);
+            //Mul(t, x2, z);
 
-            int[] x2 = Create();
             int[] t = Create();
-            PowPm5d8(x, x2, t);
-            Sqr(t, 3, t);
-            Mul(t, x2, z);
+            uint[] u = new uint[8];
+
+            Copy(x, 0, t, 0);
+            Normalize(t);
+            Encode(t, u, 0);
+
+            Mod.ModOddInverse(P32, u, u);
+
+            Decode(u, 0, z);
+        }
+
+        public static void InvVar(int[] x, int[] z)
+        {
+            int[] t = Create();
+            uint[] u = new uint[8];
+
+            Copy(x, 0, t, 0);
+            Normalize(t);
+            Encode(t, u, 0);
+
+            Mod.ModOddInverseVar(P32, u, u);
+
+            Decode(u, 0, z);
         }
 
         public static int IsZero(int[] x)
diff --git a/crypto/src/math/ec/rfc7748/X448Field.cs b/crypto/src/math/ec/rfc7748/X448Field.cs
index 240518cde..4d3be5cda 100644
--- a/crypto/src/math/ec/rfc7748/X448Field.cs
+++ b/crypto/src/math/ec/rfc7748/X448Field.cs
@@ -1,6 +1,8 @@
 using System;
 using System.Diagnostics;
 
+using Org.BouncyCastle.Math.Raw;
+
 namespace Org.BouncyCastle.Math.EC.Rfc7748
 {
     [CLSCompliantAttribute(false)]
@@ -10,6 +12,10 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
 
         private const uint M28 = 0x0FFFFFFFU;
 
+        private static readonly uint[] P32 = new uint[]{ 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU,
+            0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFEU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU,
+            0xFFFFFFFFU, 0xFFFFFFFFU };
+
         protected X448Field() {}
 
         public static void Add(uint[] x, uint[] y, uint[] z)
@@ -129,6 +135,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
             }
         }
 
+        public static void Decode(uint[] x, int xOff, uint[] z)
+        {
+            Decode224(x, xOff, z, 0);
+            Decode224(x, xOff + 7, z, 8);
+        }
+
         public static void Decode(byte[] x, int xOff, uint[] z)
         {
             Decode56(x, xOff, z, 0);
@@ -141,6 +153,21 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
             Decode56(x, xOff + 49, z, 14);
         }
 
+        private static void Decode224(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            uint x0 = x[xOff + 0], x1 = x[xOff + 1], x2 = x[xOff + 2], x3 = x[xOff + 3];
+            uint x4 = x[xOff + 4], x5 = x[xOff + 5], x6 = x[xOff + 6];
+
+            z[zOff + 0] = x0 & M28;
+            z[zOff + 1] = (x0 >> 28 | x1 <<  4) & M28;
+            z[zOff + 2] = (x1 >> 24 | x2 <<  8) & M28;
+            z[zOff + 3] = (x2 >> 20 | x3 << 12) & M28;
+            z[zOff + 4] = (x3 >> 16 | x4 << 16) & M28;
+            z[zOff + 5] = (x4 >> 12 | x5 << 20) & M28;
+            z[zOff + 6] = (x5 >>  8 | x6 << 24) & M28;
+            z[zOff + 7] = x6 >> 4;
+        }
+
         private static uint Decode24(byte[] bs, int off)
         {
             uint n = bs[off];
@@ -166,6 +193,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
             z[zOff + 1] = (lo >> 28) | (hi << 4);
         }
 
+        public static void Encode(uint[] x, uint[] z, int zOff)
+        {
+            Encode224(x, 0, z, zOff);
+            Encode224(x, 8, z, zOff + 7);
+        }
+
         public static void Encode(uint[] x, byte[] z, int zOff)
         {
             Encode56(x, 0, z, zOff);
@@ -178,6 +211,20 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
             Encode56(x, 14, z, zOff + 49);
         }
 
+        private static void Encode224(uint[] x, int xOff, uint[] z, int zOff)
+        {
+            uint x0 = x[xOff + 0], x1 = x[xOff + 1], x2 = x[xOff + 2], x3 = x[xOff + 3];
+            uint x4 = x[xOff + 4], x5 = x[xOff + 5], x6 = x[xOff + 6], x7 = x[xOff + 7];
+
+            z[zOff + 0] =  x0        | (x1 << 28);
+            z[zOff + 1] = (x1 >>  4) | (x2 << 24);
+            z[zOff + 2] = (x2 >>  8) | (x3 << 20);
+            z[zOff + 3] = (x3 >> 12) | (x4 << 16);
+            z[zOff + 4] = (x4 >> 16) | (x5 << 12);
+            z[zOff + 5] = (x5 >> 20) | (x6 <<  8);
+            z[zOff + 6] = (x6 >> 24) | (x7 <<  4);
+        }
+
         private static void Encode24(uint n, byte[] bs, int off)
         {
             bs[  off] = (byte)(n      );
@@ -202,14 +249,35 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748
 
         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[] t = Create();
+            //PowPm3d4(x, t);
+            //Sqr(t, 2, t);
+            //Mul(t, x, z);
 
             uint[] t = Create();
-            PowPm3d4(x, t);
-            Sqr(t, 2, t);
-            Mul(t, x, z);
+            uint[] u = new uint[14];
+
+            Copy(x, 0, t, 0);
+            Normalize(t);
+            Encode(t, u, 0);
+
+            Mod.ModOddInverse(P32, u, u);
+
+            Decode(u, 0, z);
+        }
+
+        public static void InvVar(uint[] x, uint[] z)
+        {
+            uint[] t = Create();
+            uint[] u = new uint[14];
+
+            Copy(x, 0, t, 0);
+            Normalize(t);
+            Encode(t, u, 0);
+
+            Mod.ModOddInverseVar(P32, u, u);
+
+            Decode(u, 0, z);
         }
 
         public static int IsZero(uint[] x)
diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs
index 95ba43472..3a39ae53d 100644
--- a/crypto/src/math/ec/rfc8032/Ed25519.cs
+++ b/crypto/src/math/ec/rfc8032/Ed25519.cs
@@ -754,7 +754,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
 
                         X25519Field.Add(q.z, q.z, x);
                         // TODO[ed25519] Batch inversion
-                        X25519Field.Inv(x, y);
+                        X25519Field.InvVar(x, y);
                         X25519Field.Mul(q.x, y, x);
                         X25519Field.Mul(q.y, y, y);
 
diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs
index 12f24c1ff..c1202dc02 100644
--- a/crypto/src/math/ec/rfc8032/Ed448.cs
+++ b/crypto/src/math/ec/rfc8032/Ed448.cs
@@ -702,7 +702,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032
                     {
                         PointExt q = points[i];
                         // TODO[ed448] Batch inversion
-                        X448Field.Inv(q.z, q.z);
+                        X448Field.InvVar(q.z, q.z);
                         X448Field.Mul(q.x, q.z, q.x);
                         X448Field.Mul(q.y, q.z, q.y);
 
diff --git a/crypto/src/math/raw/Mod.cs b/crypto/src/math/raw/Mod.cs
index 197b5c82b..1a1524c11 100644
--- a/crypto/src/math/raw/Mod.cs
+++ b/crypto/src/math/raw/Mod.cs
@@ -7,74 +7,190 @@ 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.
+     */
+
     internal abstract class Mod
     {
         private static readonly SecureRandom RandomSource = new SecureRandom();
 
-        public static void Invert(uint[] p, uint[] x, uint[] z)
+        private const int M30 = 0x3FFFFFFF;
+        private const ulong M32UL = 0xFFFFFFFFUL;
+
+        [Obsolete("Will be removed")]
+        public static void Add(uint[] p, uint[] x, uint[] y, uint[] z)
         {
             int len = p.Length;
-            if (Nat.IsZero(len, x))
-                throw new ArgumentException("cannot be 0", "x");
-            if (Nat.IsOne(len, x))
+            uint c = Nat.Add(len, x, y, z);
+            if (c != 0)
             {
-                Array.Copy(x, 0, z, 0, len);
-                return;
+                Nat.SubFrom(len, p, z);
             }
+        }
 
-            uint[] u = Nat.Copy(len, x);
-            uint[] a = Nat.Create(len);
-            a[0] = 1;
-            int ac = 0;
+        public static void CheckedModOddInverse(uint[] m, uint[] x, uint[] z)
+        {
+            if (0 == ModOddInverse(m, x, z))
+                throw new ArithmeticException("Inverse does not exist.");
+        }
 
-            if ((u[0] & 1) == 0)
-            {
-                InversionStep(p, u, len, a, ref ac);
-            }
-            if (Nat.IsOne(len, u))
+        public static void CheckedModOddInverseVar(uint[] m, uint[] x, uint[] z)
+        {
+            if (!ModOddInverseVar(m, x, z))
+                throw new ArithmeticException("Inverse does not exist.");
+        }
+
+        public static uint Inverse32(uint d)
+        {
+            Debug.Assert((d & 1) == 1);
+
+            //int x = d + (((d + 1) & 4) << 1);   // d.x == 1 mod 2**4
+            uint x = d;                         // d.x == 1 mod 2**3
+            x *= 2 - d * x;                     // d.x == 1 mod 2**6
+            x *= 2 - d * x;                     // d.x == 1 mod 2**12
+            x *= 2 - d * x;                     // d.x == 1 mod 2**24
+            x *= 2 - d * x;                     // d.x == 1 mod 2**48
+            Debug.Assert(d * x == 1);
+            return x;
+        }
+
+        [Obsolete("Use 'CheckedModOddInverseVar' instead")]
+        public static void Invert(uint[] m, uint[] x, uint[] z)
+        {
+            CheckedModOddInverseVar(m, x, z);
+        }
+
+        public static uint ModOddInverse(uint[] m, uint[] x, uint[] z)
+        {
+            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 m0Inv30x4 = -(int)Inverse32(m[0]) << 2;
+
+            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 eta = -1;
+            int maxDivsteps = GetMaximumDivsteps(bits);
+
+            for (int divSteps = 0; divSteps < maxDivsteps; divSteps += 30)
             {
-                InversionResult(p, ac, a, z);
-                return;
+                eta = Divsteps30(eta, F[0], G[0], t);
+                UpdateDE30(len30, D, E, t, m0Inv30x4, M);
+                UpdateFG30(len30, F, G, t);
             }
 
-            uint[] v = Nat.Copy(len, p);
-            uint[] b = Nat.Create(len);
-            int bc = 0;
+            int signF = F[len30 - 1] >> 31;
+            Debug.Assert(-1 == signF | 0 == signF);
 
-            int uvLen = len;
+            CNegate30(len30, signF, F);
+            CNegate30(len30, signF, D);
 
-            for (;;)
+            Decode30(bits, D, 0, z, 0);
+
+            int signD = D[len30 - 1] >> 31;
+            Debug.Assert(-1 == signD | 0 == signD);
+
+            signD += (int)Nat.CAdd(len32, signD, z, m, z);
+            Debug.Assert(0 == signD & 0 != Nat.LessThan(len32, z, m));
+
+            return (uint)(EqualTo(len30, F, 1) & EqualToZero(len30, G));
+        }
+
+        public static bool ModOddInverseVar(uint[] m, uint[] x, uint[] z)
+        {
+            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 m0Inv30x4 = -(int)Inverse32(m[0]) << 2;
+
+            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 clzG = Integers.NumberOfLeadingZeros(G[len30 - 1] | 1) - (len30 * 30 + 2 - bits);
+            int eta = -1 - clzG;
+            int lenDE = len30, lenFG = len30;
+            int maxDivsteps = GetMaximumDivsteps(bits);
+
+            int divsteps = 0;
+            while (!IsZero(lenFG, G))
             {
-                while (u[uvLen - 1] == 0 && v[uvLen - 1] == 0)
-                {
-                    --uvLen;
-                }
+                if (divsteps >= maxDivsteps)
+                    return false;
 
-                if (Nat.Gte(len, u, v))
-                {
-                    Nat.SubFrom(len, v, u);
-                    Debug.Assert((u[0] & 1) == 0);
-                    ac += Nat.SubFrom(len, b, a) - bc;
-                    InversionStep(p, u, uvLen, a, ref ac);
-                    if (Nat.IsOne(len, u))
-                    {
-                        InversionResult(p, ac, a, z);
-                        return;
-                    }
-                }
-                else
+                divsteps += 30;
+
+                eta = Divsteps30Var(eta, F[0], G[0], t);
+                UpdateDE30(lenDE, D, E, t, m0Inv30x4, M);
+                UpdateFG30(lenFG, F, G, t);
+
+                int fn = F[lenFG - 1];
+                int gn = G[lenFG - 1];
+
+                int cond = (lenFG - 2) >> 31;
+                cond |= fn ^ (fn >> 31);
+                cond |= gn ^ (gn >> 31);
+
+                if (cond == 0)
                 {
-                    Nat.SubFrom(len, u, v);
-                    Debug.Assert((v[0] & 1) == 0);
-                    bc += Nat.SubFrom(len, a, b) - ac;
-                    InversionStep(p, v, uvLen, b, ref bc);
-                    if (Nat.IsOne(len, v))
-                    {
-                        InversionResult(p, bc, b, z);
-                        return;
-                    }
+                    F[lenFG - 2] |= fn << 30;
+                    G[lenFG - 2] |= gn << 30;
+                    --lenFG;
                 }
             }
+
+            int signF = F[lenFG - 1] >> 31;
+            Debug.Assert(-1 == signF | 0 == signF);
+
+            if (0 != signF)
+            {
+                Negate30(lenFG, F);
+                Negate30(lenDE, D);
+            }
+
+            if (!IsOne(lenFG, F))
+                return false;
+
+            Decode30(bits, D, 0, z, 0);
+
+            int signD = D[lenDE - 1] >> 31;
+            Debug.Assert(-1 == signD | 0 == signD);
+
+            if (signD < 0)
+            {
+                signD += (int)Nat.AddTo(len32, m, z);
+            }
+            Debug.Assert(0 == signD && !Nat.Gte(len32, z, m));
+
+            return true;
         }
 
         public static uint[] Random(uint[] p)
@@ -101,74 +217,346 @@ namespace Org.BouncyCastle.Math.Raw
             return s;
         }
 
-        public static void Add(uint[] p, uint[] x, uint[] y, uint[] z)
+        [Obsolete("Will be removed")]
+        public static void Subtract(uint[] p, uint[] x, uint[] y, uint[] z)
         {
             int len = p.Length;
-            uint c = Nat.Add(len, x, y, z);
+            int c = Nat.Sub(len, x, y, z);
             if (c != 0)
             {
-                Nat.SubFrom(len, p, z);
+                Nat.AddTo(len, p, z);
             }
         }
 
-        public static void Subtract(uint[] p, uint[] x, uint[] y, uint[] z)
+        private static void CNegate30(int len, int cond, int[] D)
         {
-            int len = p.Length;
-            int c = Nat.Sub(len, x, y, z);
-            if (c != 0)
+            Debug.Assert(len > 0);
+            Debug.Assert(D.Length >= len);
+
+            int last = len - 1;
+            long cd = 0L;
+
+            for (int i = 0; i < last; ++i)
             {
-                Nat.AddTo(len, p, z);
+                cd += (D[i] ^ cond) - cond;
+                D[i] = (int)cd & M30; cd >>= 30;
             }
+
+            cd += (D[last] ^ cond) - cond;
+            D[last] = (int)cd;
         }
 
-        private static void InversionResult(uint[] p, int ac, uint[] a, uint[] z)
+        private static void Decode30(int bits, int[] x, int xOff, uint[] z, int zOff)
         {
-            if (ac < 0)
+            Debug.Assert(bits > 0);
+
+            int avail = 0;
+            ulong data = 0L;
+
+            while (bits > 0)
             {
-                Nat.Add(p.Length, a, p, z);
+                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 int Divsteps30(int eta, int f0, int g0, int[] t)
+        {
+            int u = 1, v = 0, q = 0, r = 1;
+            int f = f0, g = g0;
+
+            for (int i = 0; i < 30; ++i)
             {
-                Array.Copy(a, 0, z, 0, p.Length);
+                Debug.Assert((f & 1) == 1);
+                Debug.Assert((u * f0 + v * g0) == f << i);
+                Debug.Assert((q * f0 + r * g0) == g << i);
+
+                int p = -(g & 1);
+                int s = eta >> 31;
+
+                int c1 = p & s;
+                int c2 = p & ~s;
+
+                eta = (eta ^ c1) - (c1 + 1);
+
+                g += f & c2;
+                q += u & c2;
+                r += v & c2;
+
+                g -= f & c1;
+                q -= u & c1;
+                r -= v & c1;
+
+                f += g & c1;
+                u += q & c1;
+                v += r & c1;
+
+                g >>= 1;
+                u <<= 1;
+                v <<= 1;
             }
+
+            t[0] = u;
+            t[1] = v;
+            t[2] = q;
+            t[3] = r;
+
+            return eta;
         }
 
-        private static void InversionStep(uint[] p, uint[] u, int uLen, uint[] x, ref int xc)
+        private static int Divsteps30Var(int eta, int f0, int g0, int[] t)
         {
-            int len = p.Length;
-            int count = 0;
-            while (u[0] == 0)
+            int u = 1, v = 0, q = 0, r = 1;
+            int f = f0, g = g0, m, w, x, y, z;
+            int i = 30, limit, zeros;
+
+            for (;;)
             {
-                Nat.ShiftDownWord(uLen, u, 0);
-                count += 32;
+                // Use a sentinel bit to count zeros only up to i.
+                zeros = Integers.NumberOfTrailingZeros(g | (-1 << i));
+
+                g >>= zeros;
+                u <<= zeros;
+                v <<= zeros;
+                eta -= zeros;
+                i -= zeros;
+
+                if (i <= 0)
+                    break;
+
+                Debug.Assert((f & 1) == 1);
+                Debug.Assert((g & 1) == 1);
+                Debug.Assert((u * f0 + v * g0) == f << (30 - i));
+                Debug.Assert((q * f0 + r * g0) == g << (30 - i));
+
+                if (eta < 0)
+                {
+                    eta = -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);
+                    m = (int)((uint.MaxValue >> (32 - limit)) & 63U);
+
+                    w = (f * g * (f * f - 2)) & m;
+                }
+                else
+                {
+                    // Handle up to 4 divsteps at once, subject to eta and i.
+                    limit = (eta + 1) > i ? i : (eta + 1);
+                    m = (int)((uint.MaxValue >> (32 - limit)) & 15U);
+
+                    w = f + (((f + 1) & 4) << 1);
+                    w = (-w * g) & m;
+                }
+
+                g += f * w;
+                q += u * w;
+                r += v * w;
+
+                Debug.Assert((g & m) == 0);
             }
 
+            t[0] = u;
+            t[1] = v;
+            t[2] = q;
+            t[3] = r;
+
+            return eta;
+        }
+
+        private static void Encode30(int bits, uint[] x, int xOff, int[] z, int zOff)
+        {
+            Debug.Assert(bits > 0);
+
+            int avail = 0;
+            ulong data = 0UL;
+
+            while (bits > 0)
             {
-                int zeroes = Integers.NumberOfTrailingZeros((int)u[0]);
-                if (zeroes > 0)
+                if (avail < System.Math.Min(30, bits))
                 {
-                    Nat.ShiftDownBits(uLen, u, zeroes, 0);
-                    count += zeroes;
+                    data |= (x[xOff++] & M32UL) << avail;
+                    avail += 32;
                 }
+
+                z[zOff++] = (int)data & M30; data >>= 30;
+                avail -= 30;
+                bits -= 30;
+            }
+        }
+
+        private static int EqualTo(int len, int[] x, int y)
+        {
+            int d = x[0] ^ y;
+            for (int i = 1; i < len; ++i)
+            {
+                d |= x[i];
+            }
+            d = (int)((uint)d >> 1) | (d & 1);
+            return (d - 1) >> 31;
+        }
+
+        private static int EqualToZero(int len, int[] x)
+        {
+            int d = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                d |= x[i];
             }
+            d = (int)((uint)d >> 1) | (d & 1);
+            return (d - 1) >> 31;
+        }
+
+        private static int GetMaximumDivsteps(int bits)
+        {
+            return (49 * bits + (bits < 46 ? 80 : 47)) / 17;
+        }
 
-            for (int i = 0; i < count; ++i)
+        private static bool IsOne(int len, int[] x)
+        {
+            if (x[0] != 1)
             {
-                if ((x[0] & 1) != 0)
+                return false;
+            }
+            for (int i = 1; i < len; ++i)
+            {
+                if (x[i] != 0)
                 {
-                    if (xc < 0)
-                    {
-                        xc += (int)Nat.AddTo(len, p, x);
-                    }
-                    else
-                    {
-                        xc += Nat.SubFrom(len, p, x);
-                    }
+                    return false;
                 }
+            }
+            return true;
+        }
 
-                Debug.Assert(xc == 0 || xc == -1);
-                Nat.ShiftDownBit(len, x, (uint)xc);
+        private static bool IsZero(int len, int[] x)
+        {
+            if (x[0] != 0)
+            {
+                return false;
+            }
+            for (int i = 1; i < len; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private static void Negate30(int len, int[] D)
+        {
+            Debug.Assert(len > 0);
+            Debug.Assert(D.Length >= len);
+
+            int last = len - 1;
+            long cd = 0L;
+
+            for (int i = 0; i < last; ++i)
+            {
+                cd -= D[i];
+                D[i] = (int)cd & M30; cd >>= 30;
+            }
+
+            cd -= D[last];
+            D[last] = (int)cd;
+        }
+
+        private static void UpdateDE30(int len, int[] D, int[] E, int[] t, int m0Inv30x4, int[] M)
+        {
+            Debug.Assert(len > 0);
+            Debug.Assert(D.Length >= len);
+            Debug.Assert(E.Length >= len);
+            Debug.Assert(M.Length >= len);
+            Debug.Assert(m0Inv30x4 * M[0] == -1 << 2);
+
+            int u = t[0], v = t[1], q = t[2], r = t[3];
+            int di, ei, i, md, me;
+            long cd, ce;
+
+            di = D[0];
+            ei = E[0];
+
+            cd = (long)u * di + (long)v * ei;
+            ce = (long)q * di + (long)r * ei;
+
+            md = (m0Inv30x4 * (int)cd) >> 2;
+            me = (m0Inv30x4 * (int)ce) >> 2;
+
+            cd += (long)M[0] * md;
+            ce += (long)M[0] * me;
+
+            Debug.Assert(((int)cd & M30) == 0);
+            Debug.Assert(((int)ce & M30) == 0);
+
+            cd >>= 30;
+            ce >>= 30;
+
+            for (i = 1; i < len; ++i)
+            {
+                di = D[i];
+                ei = E[i];
+
+                cd += (long)u * di + (long)v * ei;
+                ce += (long)q * di + (long)r * ei;
+
+                cd += (long)M[i] * md;
+                ce += (long)M[i] * me;
+
+                D[i - 1] = (int)cd & M30; cd >>= 30;
+                E[i - 1] = (int)ce & M30; ce >>= 30;
+            }
+
+            D[len - 1] = (int)cd;
+            E[len - 1] = (int)ce;
+        }
+
+        private static void UpdateFG30(int len, int[] F, int[] G, int[] t)
+        {
+            Debug.Assert(len > 0);
+            Debug.Assert(F.Length >= len);
+            Debug.Assert(G.Length >= len);
+
+            int u = t[0], v = t[1], q = t[2], r = t[3];
+            int fi, gi, i;
+            long cf, cg;
+
+            fi = F[0];
+            gi = G[0];
+
+            cf = (long)u * fi + (long)v * gi;
+            cg = (long)q * fi + (long)r * gi;
+
+            Debug.Assert(((int)cf & M30) == 0);
+            Debug.Assert(((int)cg & M30) == 0);
+
+            cf >>= 30;
+            cg >>= 30;
+
+            for (i = 1; i < len; ++i)
+            {
+                fi = F[i];
+                gi = G[i];
+
+                cf += (long)u * fi + (long)v * gi;
+                cg += (long)q * fi + (long)r * gi;
+
+                F[i - 1] = (int)cf & M30; cf >>= 30;
+                G[i - 1] = (int)cg & M30; cg >>= 30;
             }
+
+            F[len - 1] = (int)cf;
+            G[len - 1] = (int)cg;
         }
     }
 }
diff --git a/crypto/src/math/raw/Nat.cs b/crypto/src/math/raw/Nat.cs
index 69942661f..9f2412580 100644
--- a/crypto/src/math/raw/Nat.cs
+++ b/crypto/src/math/raw/Nat.cs
@@ -447,6 +447,72 @@ namespace Org.BouncyCastle.Math.Raw
             return true;
         }
 
+        public static uint EqualTo(int len, uint[] x, uint y)
+        {
+            uint d = x[0] ^ y;
+            for (int i = 1; i < len; ++i)
+            {
+                d |= x[i];
+            }
+            d = (d >> 1) | (d & 1);
+            return (uint)(((int)d - 1) >> 31);
+        }
+
+        public static uint EqualTo(int len, uint[] x, int xOff, uint y)
+        {
+            uint d = x[xOff] ^ y;
+            for (int i = 1; i < len; ++i)
+            {
+                d |= x[xOff + i];
+            }
+            d = (d >> 1) | (d & 1);
+            return (uint)(((int)d - 1) >> 31);
+        }
+
+        public static uint EqualTo(int len, uint[] x, uint[] y)
+        {
+            uint d = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                d |= x[i] ^ y[i];
+            }
+            d = (d >> 1) | (d & 1);
+            return (uint)(((int)d - 1) >> 31);
+        }
+
+        public static uint EqualTo(int len, uint[] x, int xOff, uint[] y, int yOff)
+        {
+            uint d = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                d |= x[xOff + i] ^ y[yOff + i];
+            }
+            d = (d >> 1) | (d & 1);
+            return (uint)(((int)d - 1) >> 31);
+        }
+
+        public static uint EqualToZero(int len, uint[] x)
+        {
+            uint d = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                d |= x[i];
+            }
+            d = (d >> 1) | (d & 1);
+            return (uint)(((int)d - 1) >> 31);
+        }
+
+        public static uint EqualToZero(int len, uint[] x, int xOff)
+        {
+            uint d = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                d |= x[xOff + i];
+            }
+            d = (d >> 1) | (d & 1);
+            return (uint)(((int)d - 1) >> 31);
+        }
+
         public static uint[] FromBigInteger(int bits, BigInteger x)
         {
             if (x.SignValue < 0 || x.BitLength > bits)
diff --git a/crypto/src/util/BigIntegers.cs b/crypto/src/util/BigIntegers.cs
index bac5f12c0..d9c898676 100644
--- a/crypto/src/util/BigIntegers.cs
+++ b/crypto/src/util/BigIntegers.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.Raw;
 using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Utilities
@@ -10,6 +11,9 @@ namespace Org.BouncyCastle.Utilities
      */
     public abstract class BigIntegers
     {
+        public static readonly BigInteger Zero = BigInteger.Zero;
+        public static readonly BigInteger One = BigInteger.One;
+
         private const int MaxIterations = 1000;
 
         /**
@@ -131,6 +135,52 @@ namespace Org.BouncyCastle.Utilities
             return new BigInteger(max.Subtract(min).BitLength - 1, random).Add(min);
         }
 
+        public static BigInteger ModOddInverse(BigInteger M, BigInteger X)
+        {
+            if (!M.TestBit(0))
+                throw new ArgumentException("must be odd", "M");
+            if (M.SignValue != 1)
+                throw new ArithmeticException("BigInteger: modulus not positive");
+            if (X.SignValue < 0 || X.CompareTo(M) >= 0)
+            {
+                X = X.Mod(M);
+            }
+
+            int bits = M.BitLength;
+            uint[] m = Nat.FromBigInteger(bits, M);
+            uint[] x = Nat.FromBigInteger(bits, X);
+            int len = m.Length;
+            uint[] z = Nat.Create(len);
+            if (0 == Mod.ModOddInverse(m, x, z))
+                throw new ArithmeticException("BigInteger not invertible");
+            return Nat.ToBigInteger(len, z);
+        }
+
+        public static BigInteger ModOddInverseVar(BigInteger M, BigInteger X)
+        {
+            if (!M.TestBit(0))
+                throw new ArgumentException("must be odd", "M");
+            if (M.SignValue != 1)
+                throw new ArithmeticException("BigInteger: modulus not positive");
+            if (M.Equals(One))
+                return Zero;
+            if (X.SignValue < 0 || X.CompareTo(M) >= 0)
+            {
+                X = X.Mod(M);
+            }
+            if (X.Equals(One))
+                return One;
+
+            int bits = M.BitLength;
+            uint[] m = Nat.FromBigInteger(bits, M);
+            uint[] x = Nat.FromBigInteger(bits, X);
+            int len = m.Length;
+            uint[] z = Nat.Create(len);
+            if (!Mod.ModOddInverseVar(m, x, z))
+                throw new ArithmeticException("BigInteger not invertible");
+            return Nat.ToBigInteger(len, z);
+        }
+
         public static int GetUnsignedByteLength(BigInteger n)
         {
             return (n.BitLength + 7) / 8;
diff --git a/crypto/src/util/Integers.cs b/crypto/src/util/Integers.cs
index afb4b827f..7d98de586 100644
--- a/crypto/src/util/Integers.cs
+++ b/crypto/src/util/Integers.cs
@@ -4,6 +4,11 @@ namespace Org.BouncyCastle.Utilities
 {
     public abstract class Integers
     {
+        private static readonly byte[] DeBruijnTZ = {
+            0x00, 0x01, 0x02, 0x18, 0x03, 0x13, 0x06, 0x19, 0x16, 0x04, 0x14, 0x0A,
+            0x10, 0x07, 0x0C, 0x1A, 0x1F, 0x17, 0x12, 0x05, 0x15, 0x09, 0x0F, 0x0B,
+            0x1E, 0x11, 0x08, 0x0E, 0x1D, 0x0D, 0x1C, 0x1B };
+
         public static int NumberOfLeadingZeros(int i)
         {
             if (i <= 0)
@@ -21,16 +26,7 @@ namespace Org.BouncyCastle.Utilities
 
         public static int NumberOfTrailingZeros(int i)
         {
-            if (i == 0)
-                return 32;
-
-            int count = 0;
-            while ((i & 1) == 0)
-            {
-                i >>= 1;
-                ++count;
-            }
-            return count;
+            return DeBruijnTZ[(uint)((i & -i) * 0x04D7651F) >> 27];
         }
 
         public static int RotateLeft(int i, int distance)