/// <summary> /// Determines whether a number is probably prime using the Rabin-Miller's test /// </summary> /// <remarks> /// Before applying the test, the number is tested for divisibility by X first primes. /// Different amounts X were determined for different bit lengths and are used as a constant. /// </remarks> /// <param name="T">BigInteger to check for primality</param> /// <param name="confidence">Number of chosen bases</param> /// <param name="rng">RandomNumberGenerator object</param> /// <param name="bitLength">Bit length of T (if not provided, the method calculates it)</param> /// <returns>True if this is probably prime</returns> public static bool IsProbablePrime(this BigInteger T, int confidence, RandomNumberGenerator rng, int bitLength = -1) { var thisVal = BigInteger.Abs(T); if (thisVal.IsZero || thisVal.IsOne) { return(false); } if (bitLength == -1) { bitLength = thisVal.BitCount(); } var take = PrimeFactorsToUse(bitLength); if (thisVal <= UInt64.MaxValue) { var uival = (UInt64)thisVal; foreach (var smallPrime in PrimesBelow1M.Take(take)) { if (smallPrime >= uival) { return(true); } if (uival % smallPrime == 0) { return(false); } } } else { foreach (var smallPrime in PrimesBelow1M_BI.Take(take)) { if ((thisVal % smallPrime).IsZero) { return(false); } } } return(thisVal.RabinMillerTest(confidence, rng)); }
/// <summary> /// Generates a random probable safe prime positive BigInteger of exactly the specified bit length using the provided RNG. /// Safe prime is a prime P such that P=2*Q+1, where Q is prime. Such Q is called a Sophie Germain prime. /// This method uses the Combined Sieve approach to improve performance as compared to naive algorithm. /// See Michael Wiener ``Safe Prime Generation with a Combined Sieve'', 2003 (https://eprint.iacr.org/2003/186) /// </summary> /// <param name="T">This parameter is irrelevant, required due to lack of static extension methods in C#</param> /// <param name="bits">Bit length of prime to generate</param> /// <param name="confidence">Number of chosen bases</param> /// <param name="rng">RandomNumberGenerator object</param> /// <returns>A probably prime number</returns> /// <exception cref="ArgumentOutOfRangeException">`bits` must be >= 3</exception> public static BigInteger GenSafePseudoPrime(this BigInteger T, int bits, int confidence, RandomNumberGenerator rng) { if (bits < 3) { throw new ArgumentOutOfRangeException(nameof(bits), bits, "GenSafePseudoPrime can only generate prime numbers of 3 bits or more"); } BigInteger result; var qbits = bits - 1; var take = PrimeFactorsToUse(bits); do { BigInteger q; var done = false; while (!done) { q = BigInteger.Zero.GenRandomBits(qbits, rng); q |= BigInteger.One; // make it odd var fail = false; if (q <= UInt64.MaxValue) { var uival = (UInt64)q; foreach (var curSmallPrime in PrimesBelow1M.Take(take)) { if (curSmallPrime >= uival) { fail = true; // not a fail but we skip Rabin-Miller test using this flag done = true; // and exit the outer while loop break; } var rem = uival % curSmallPrime; // Sieve: if rem=0 then Q is composite; // if second condition is true, then P will be divisible by curSmallPrime if (rem == 0 || rem == (curSmallPrime - 1) / 2) { fail = true; break; } } } else { foreach (var curSmallPrime in PrimesBelow1M_BI.Take(take)) { var rem = q % curSmallPrime; // Sieve: if rem=0 then Q is composite; // if second condition is true, then P will be divisible by curSmallPrime if (rem.IsZero || rem == (curSmallPrime - BigInteger.One) / Two) { fail = true; break; } } } if (fail) { continue; // try another Q } done = q.RabinMillerTest(confidence, rng); // returns true if Q is prime } result = Two * q + BigInteger.One; } while (!result.RabinMillerTest(confidence, rng)); // no need to check divisibility by small primes, can go straight to Rabin-Miller return(result); }