public static string GetCertificateOfPrimality(BigInteger probable, BigInteger accuracy) { BigInteger witness = CryptoRandomSingleton.RandomRange(Two, probable - Two); BigInteger modPow = BigInteger.ModPow(witness, probable - 1, probable); string result = null; if (modPow == 1) // a^n-1 mod n == 1 { modPow = BigInteger.ModPow(witness, 2 * accuracy, probable); BigInteger gcd = BigInteger.GreatestCommonDivisor(modPow - 1, probable); if (gcd == 1) { result = Environment.NewLine; result += string.Format("Certificate of primality({0})", probable) + Environment.NewLine; result += "{" + Environment.NewLine; result += " " + string.Format("{0} ^ {1}-1 mod {1} == 1{2}", witness, probable, Environment.NewLine); result += " " + string.Format("GCD({0}, {1}) == 1{2}", (modPow - 1), probable, Environment.NewLine); result += "}" + Environment.NewLine; } } return(result); }
public static bool IsProbablyPrime(BigInteger primeHopeful, int testCount) { bool result = false; using (outsideExecutionTime.StartTimer()) { Log.MethodEnter("MillerRabinPrimalityTest", nameof(primeHopeful), $"{primeHopeful}, {nameof(testCount)}: {testCount}"); if (primeHopeful == 2 || primeHopeful == 3 || primeHopeful == 5 || primeHopeful == 7) { FoundFactor(primeHopeful, BigInteger.GreatestCommonDivisor(primeHopeful, 210)); result = true; goto exit; } BigInteger remainder = primeHopeful & 1; // % Two; if (remainder == 0) { FoundFactor(primeHopeful, 2); result = false; goto exit; } BigInteger hopefulLess1 = primeHopeful - 1; BigInteger quotientD = hopefulLess1; remainder = quotientD & 1; // % Two; long divisionCountR = 0; while (remainder == 0) { quotientD = quotientD >> 1; // / Two; remainder = quotientD & 1; // % Two; divisionCountR++; } BigInteger hopefulLess2 = primeHopeful - Two; BigInteger residueX; BigInteger priorResidueX; BigInteger randomA = 0; // Tracks previous random values, prevents testing with the same value twice, // which would dramatically reduce our confidence in the primality of a probable prime. // Seems unlikely to happen, but its not a hit to performance to check. List <BigInteger> previousRandomValues = new List <BigInteger>(); int counter = 1; int modCount = 1; for (counter = 1; counter <= testCount; counter++) { do { randomA = CryptoRandomSingleton.RandomRange(Two, hopefulLess2); }while (previousRandomValues.Contains(randomA)); previousRandomValues.Add(randomA); Log.Message($"MillerRabin check prime hopeful (P) against random n: {randomA}"); using (insideExecutionTime.StartTimer()) { residueX = BigInteger.ModPow(randomA, quotientD, primeHopeful); Log.Message($"MillerRabin check a^d ≡ r (mod primeHopeful): {randomA}^{quotientD} ≡ {residueX} (mod {primeHopeful})"); } if (residueX == 1 || residueX == hopefulLess1) { BigInteger gcd = BigInteger.GreatestCommonDivisor(residueX - 1, primeHopeful); // Chance to factor: GCD((a^d mod n) − 1, n) Log.Message($"A) Chance to factor: GCD((a^d mod n) − 1, primeHopeful)"); if (gcd != 1 && gcd != primeHopeful) { FoundFactor(primeHopeful, gcd); result = false; goto exit; } continue; } modCount = 1; while (modCount <= divisionCountR) { priorResidueX = residueX; residueX = BigInteger.ModPow(residueX, 2, primeHopeful); Log.Message($"MillerRabin check a^2 ≡ r (mod primeHopeful): {priorResidueX}^2 ≡ {residueX} (mod {primeHopeful})"); if (residueX == 1) { // Chance to factor: a^(d*2^r)-1 BigInteger twoR = BigInteger.Pow(2, (int)divisionCountR); BigInteger dTwoR = BigInteger.Multiply(quotientD, twoR); if (dTwoR < int.MaxValue) { int pow = (int)dTwoR; BigInteger potentialFactor = BigInteger.Pow(randomA, pow) - 1; BigInteger gcd = BigInteger.GreatestCommonDivisor(potentialFactor, primeHopeful); Log.Message($"B) Chance to factor: GCD(a^(d*(2^r))-1, primeHopeful): GCD({randomA}^({quotientD}*2^{divisionCountR})-1 == {potentialFactor - 1}, primeHopeful) == {gcd}"); if (gcd != 1 && gcd != primeHopeful) { FoundFactor(primeHopeful, gcd); result = false; goto exit; } } Log.MethodLeave(); return(false); } if (residueX == hopefulLess1) { // Chance to factor: GCD((a^(d*2^r) mod n) − 1, n) BigInteger twoR = BigInteger.Pow(2, (int)divisionCountR); BigInteger dTwoR = BigInteger.Multiply(quotientD, twoR); BigInteger potentialFactor = BigInteger.ModPow(randomA, dTwoR, primeHopeful); BigInteger gcd = BigInteger.GreatestCommonDivisor(potentialFactor - 1, primeHopeful); Log.Message($"C) Chance to factor: GCD( a^(d*(2^r)) ≡ m−1 (mod primeHopeful) , primeHopeful): GCD( {randomA}^({quotientD}*(2^{divisionCountR})) ≡ {potentialFactor}-1 (mod primeHopeful) == {potentialFactor - 1}, primeHopeful) == {gcd}"); if (gcd != 1 && gcd != primeHopeful) { FoundFactor(primeHopeful, gcd); result = false; goto exit; } break; } modCount++; } if (residueX != hopefulLess1) { // ??? Chance to factor ??? BigInteger twoR = BigInteger.Pow(2, (int)divisionCountR); BigInteger dTwoR = BigInteger.Multiply(quotientD, twoR); BigInteger potentialFactor1 = BigInteger.ModPow(randomA, dTwoR, primeHopeful); BigInteger gcd1 = BigInteger.GreatestCommonDivisor(potentialFactor1 - 1, primeHopeful); Log.Message($"D) Chance to factor: GCD( a^(d*(2^r)) ≡ m−1 (mod primeHopeful) , primeHopeful): GCD( {randomA}^({quotientD}*(2^{divisionCountR})) ≡ {potentialFactor1}-1 (mod primeHopeful) == {potentialFactor1 - 1}, primeHopeful) == {gcd1}"); if (gcd1 != 1 && gcd1 != primeHopeful) { FoundFactor(primeHopeful, gcd1); result = false; goto exit; } result = false; goto exit; } } result = true; goto exit; } exit: Log.MethodLeave(); return(result); }
/// <summary> /// Returns a probable prime of size bits and will search n rounds for a composite /// </summary> /// <param name="bitSize">Size of prime, in bits</param> /// <param name="testCount">Number of different bases to use when testing probable prime for composites as evidence of primality</param> /// <returns></returns> public BigInteger GetProbablePrime(int bitSize, int testCount) { BigInteger result = 0; //Log.Message("."); Log.MethodEnter("ProvablePrime", nameof(bitSize), bitSize); if (cancelToken.IsCancellationRequested) { Log.Message("ProvablePrime.CancellationToken.IsCancellationRequested();"); Log.MethodLeave(); return(-1); } if (bitSize <= 20) { Log.Message("***MAXIMUM RECURSION DEPT REACHED"); result = TrialDivision.FindSmallPrimes(bitSize); Log.Message("***Hopeful prime: {0}", result); } else { //double c = 0.1; int m = 20; double r = 0.5; if (bitSize > 2 * m) { double rnd = 0; bool done = false; while (!done) { rnd = CryptoRandomSingleton.NextDouble(); r = Math.Pow(2, rnd - 1); done = (bitSize - r * bitSize) > m; } } int newBits = (int)Math.Floor(r * bitSize) + 1; BigInteger smallPrime = GetProbablePrime(newBits, testCount); if (smallPrime == -1) { Log.MethodLeave(); return(-1); } Log.Message("After Recursion: Length = {0}", smallPrime.ToString().Length); BigInteger pow = BigInteger.Pow(Two, bitSize - 1); BigInteger Q = Two * smallPrime; BigInteger I = pow / Q; bool success = false; while (!success) { if (cancelToken.IsCancellationRequested) { Log.Message("ProvablePrime.CancellationToken.IsCancellationRequested();"); Log.MethodLeave(); return(-1); } //LogMethod("Loop[{0}]: TestComposite({1})", _loopCount, result); BigInteger J = I + 1; BigInteger K = 2 * I; BigInteger rand1 = CryptoRandomSingleton.RandomRange(J, K); result = 2 * rand1; result = result * smallPrime; result = result + 1; bool isPrime = false; if (Eratosthenes.IsTooLarge(result)) { isPrime = true; } else { isPrime = Eratosthenes.IsPrime(result); } if (isPrime) { //LogMethod("ProvablePrime.RandomRange(J: {0}, K: {1}) = {2}", J, K, rand1); if (MillerRabin.IsProbablyPrime(result, testCount)) { success = true; string cert = MillerRabin.GetCertificateOfPrimality(result, rand1); if (cert != null) { Log.Message(cert); } } } } } //Log.Message("."); Log.MethodLeave(); return(result); }