public IBigInteger ModPow( IBigInteger exponent, IBigInteger m) { if (m.SignValue < 1) throw new ArithmeticException("Modulus must be positive"); if (m.Equals(One)) return Zero; if (exponent.SignValue == 0) return One; if (SignValue == 0) return Zero; int[] zVal = null; int[] yAccum = null; int[] yVal; // Montgomery exponentiation is only possible if the modulus is odd, // but AFAIK, this is always the case for crypto algo's bool useMonty = ((m.Magnitude[m.Magnitude.Length - 1] & 1) == 1); long mQ = 0; if (useMonty) { mQ = m.GetMQuote(); // tmp = this * R mod m IBigInteger tmp = ShiftLeft(32*m.Magnitude.Length).Mod(m); zVal = tmp.Magnitude; useMonty = (zVal.Length <= m.Magnitude.Length); if (useMonty) { yAccum = new int[m.Magnitude.Length + 1]; if (zVal.Length < m.Magnitude.Length) { var longZ = new int[m.Magnitude.Length]; zVal.CopyTo(longZ, longZ.Length - zVal.Length); zVal = longZ; } } } if (!useMonty) { if (Magnitude.Length <= m.Magnitude.Length) { //zAccum = new int[m.Magnitude.Length * 2]; zVal = new int[m.Magnitude.Length]; Magnitude.CopyTo(zVal, zVal.Length - Magnitude.Length); } else { // // in normal practice we'll never see this... // IBigInteger tmp = Remainder(m); //zAccum = new int[m.Magnitude.Length * 2]; zVal = new int[m.Magnitude.Length]; tmp.Magnitude.CopyTo(zVal, zVal.Length - tmp.Magnitude.Length); } yAccum = new int[m.Magnitude.Length*2]; } yVal = new int[m.Magnitude.Length]; // // from LSW to MSW // for (int i = 0; i < exponent.Magnitude.Length; i++) { int v = exponent.Magnitude[i]; int bits = 0; if (i == 0) { while (v > 0) { v <<= 1; bits++; } // // first time in initialise y // zVal.CopyTo(yVal, 0); v <<= 1; bits++; } while (v != 0) { if (useMonty) { // Montgomery square algo doesn't exist, and a normal // square followed by a Montgomery reduction proved to // be almost as heavy as a Montgomery mulitply. MultiplyMonty(yAccum, yVal, yVal, m.Magnitude, mQ); } else { Square(yAccum, yVal); Remainder(yAccum, m.Magnitude); Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); ZeroOut(yAccum); } bits++; if (v < 0) { if (useMonty) { MultiplyMonty(yAccum, yVal, zVal, m.Magnitude, mQ); } else { Multiply(yAccum, yVal, zVal); Remainder(yAccum, m.Magnitude); Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); ZeroOut(yAccum); } } v <<= 1; } while (bits < 32) { if (useMonty) { MultiplyMonty(yAccum, yVal, yVal, m.Magnitude, mQ); } else { Square(yAccum, yVal); Remainder(yAccum, m.Magnitude); Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); ZeroOut(yAccum); } bits++; } } if (useMonty) { // Return y * R^(-1) mod m by doing y * 1 * R^(-1) mod m ZeroOut(zVal); zVal[zVal.Length - 1] = 1; MultiplyMonty(yAccum, yVal, zVal, m.Magnitude, mQ); } IBigInteger result = new BigInteger(1, yVal, true); return exponent.SignValue > 0 ? result : result.ModInverse(m); }