diff --git a/safe_prime_generator.go b/safe_prime_generator.go index 8d9e2fa..c947041 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// The code is the original Go implementation of rand.Prime optimized for +// generating safe (Sophie Germain) primes. +// A safe prime is a prime number of the form 2p + 1, where p is also a prime. + package paillier import ( @@ -30,11 +34,31 @@ var smallPrimes = []uint8{ // operations. var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365) -type safePrime struct { - p *big.Int - q *big.Int -} - +// GenerateSafePrime tries to find a safe prime concurrently. +// The returned result is a safe prime `p` and prime `q` such that `p=2q+1`. +// Concurrency level can be controlled with the `concurrencyLevel` parameter. +// If a safe prime could not be found in the specified `timeout`, the error +// is returned. Also, if at least one search process failed, error is returned +// as well. +// +// How fast we generate a prime number is mostly a matter of luck and it depends +// on how lucky we are with drawing the first bytes. +// With today's multicore processors, we can execute the process on multiple +// cores concurrently, accept the first valid result and cancel the rest of +// work. This way, with the same finding algorithm, we can get the result +// faster. +// +// Concurrency level should be set depending on what `bitLen` of prime is +// expected. For example, as of today, on a typical workstation, for 512-bit +// safe prime, `concurrencyLevel` should be set to `1` as generating the prime +// of this length is a matter of milliseconds for a single core. +// For 1024-bit safe prime, `concurrencyLevel` should be usually set to at least +// `2` and for 2048-bit safe prime, `concurrencyLevel` must be set to at least +// `4` to get the result in a reasonable time. +// +// This function generates safe primes of at least 6 `bitLen`. For every +// generated safe prime, the two most significant bits are always set to `1` +// - we don't want the generated number to be too small. func GenerateSafePrime( bitLen int, concurrencyLevel int, @@ -58,6 +82,7 @@ func GenerateSafePrime( runGenPrimeRoutine(ctx, primeChan, errChan, mutex, rand.Reader, bitLen) } + // Cancel after the specified timeout. go func() { time.Sleep(timeout) mutex.Lock() @@ -81,6 +106,46 @@ func GenerateSafePrime( } } +type safePrime struct { + p *big.Int // p = 2q + 1 + q *big.Int +} + +// Starts a Goroutine searching for a safe prime of the specified `pBitLen`. +// If succeeds, writes prime `p` and prime `q` such that `p = 2q+1` to the +// `primeChan`. Prime `p` has a bit length equal to `pBitLen` and prime `q` has +// a bit length equal to `pBitLen-1`. +// +// The algorithm is as follows: +// 1. Generate a random odd number `q` of length `pBitLen-1` with two the most +// significant bytes set to `1`. +// 2. Execute preliminary primality test on `q` checking whether it is coprime +// to all the elements of `smallPrimes`. It allows to eliminate trivial +// cases quickly, when `q` is obviously no prime, without running an +// expensive final primality tests. +// If `q` is coprime to all of the `smallPrimes`, then go to the point 3. +// If not, add `2` and try again. Do it at most 10 times. +// 3. Check the potentially prime `q`, whether `q = 1 (mod 3)`. This will +// happen for 50% of cases. +// If it is, then `p = 2q+1` will be a multiple of 3, so it will be obviously +// not a prime number. In this case, add `2` and try again. Do it at most 10 +// times. If `q != 1 (mod 3)`, go to the point 4. +// 4. Now we know `q` is potentially prime and `p = 2q+1` is not a multiple of +// 3. We execute a preliminary primality test on `p`, checking whether +// it is coprime to all the elements of `smallPrimes` just like we did for +// `q` in point 2. If `p` is not coprime to at least one element of the +// `smallPrimes`, then go back to point 1. +// If `p` is coprime to all the elements of `smallPrimes`, go to point 5. +// 5. At this point, we know `q` is potentially prime, and `p=q+1` is also +// potentially prime. We need to execute a final primality test for `q`. +// We apply Miller-Rabin and Baillie-PSW tests. If they succeeds, it means +// that `q` is prime with a very high probability. Knowing `q` is prime, +// we use Pocklington's criterion to prove the primality of `p=2q+1`, that +// is, we execute Fermat primality test to base 2 checking whether +// `2^{p-1} = 1 (mod p)`. It's significantly faster than running full +// Miller-Rabin and Baillie-PSW for `p`. +// If `q` and `p` are found to be prime, return them as a result. If not, go +// back to the point 1. func runGenPrimeRoutine( ctx context.Context, primeChan chan safePrime, @@ -113,9 +178,11 @@ func runGenPrimeRoutine( return } - // Clear bits in the first byte to make sure the candidate has a size <= bits. + // Clear bits in the first byte to make sure the candidate has + // a size <= bits. bytes[0] &= uint8(int(1<