From f5a107a36f06924ad4616db554754f7845c1af23 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Wed, 25 Jul 2018 10:55:11 +0200 Subject: [PATCH 01/16] Draft implementation of a safe prime generator I've adapted a Go code for generating random numbers by inserting some optimisations that will allow us to generate safe primes faster than with the previous, naive approach. First of all, having q which can be prime, we first check whether q%3=1. If that's true, there is no chance p=2q+1 is prime. It lets us to reject candidate numbers quicker without running an expensive primality tests. Also, before we run a primality test for q, we may check p=2q+1 against the primes between 3-53 (We are limited by Go's uint64 range). If all those conditions are met and we know p is prime, it's enough to check Pocklington criterion for q instead of running an expensive primality test for it. --- safe_prime_generator.go | 146 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 safe_prime_generator.go diff --git a/safe_prime_generator.go b/safe_prime_generator.go new file mode 100644 index 0000000..4266472 --- /dev/null +++ b/safe_prime_generator.go @@ -0,0 +1,146 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package paillier + +import ( + "errors" + "io" + "math/big" +) + +// smallPrimes is a list of small, prime numbers that allows us to rapidly +// exclude some fraction of composite candidates when searching for a random +// prime. This list is truncated at the point where smallPrimesProduct exceeds +// a uint64. It does not include two because we ensure that the candidates are +// odd by construction. +var smallPrimes = []uint8{ + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, +} + +// smallPrimesProduct is the product of the values in smallPrimes and allows us +// to reduce a candidate prime by this number and then determine whether it's +// coprime to all the elements of smallPrimes without further big.Int +// operations. +var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365) + +// Prime returns a number, p, of the given size, such that p is prime +// with high probability. +// Prime will return error for any error returned by rand.Read or if bits < 2. +func SafePrimes(rand io.Reader, bits int) (p *big.Int, q *big.Int, err error) { + qbits := bits - 1 + + if qbits < 2 { + err = errors.New("crypto/rand: prime size must be at least 2-bit") + return + } + + b := uint(qbits % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, (qbits+7)/8) + p = new(big.Int) + q = new(big.Int) + + bigMod := new(big.Int) + + //NextRand: + for { + _, err = io.ReadFull(rand, bytes) + if err != nil { + return nil, nil, err + } + + // Clear bits in the first byte to make sure the candidate has a size <= bits. + bytes[0] &= uint8(int(1<= 2 { + bytes[0] |= 3 << (b - 2) + } else { + // Here b==1, because b cannot be zero. + bytes[0] |= 1 + if len(bytes) > 1 { + bytes[1] |= 0x80 + } + } + // Make the value odd since an even number this large certainly isn't prime. + bytes[len(bytes)-1] |= 1 + + q.SetBytes(bytes) + + p.Mul(q, big.NewInt(2)) + p.Add(p, big.NewInt(1)) + + // Calculate the value mod the product of smallPrimes. If it's + // a multiple of any of these primes we add two until it isn't. + // The probability of overflowing is minimal and can be ignored + // because we still perform Miller-Rabin tests on the result. + bigMod.Mod(q, smallPrimesProduct) + mod := bigMod.Uint64() + + NextDelta: + for delta := uint64(0); delta < 1<<20; delta += 2 { + m := mod + delta + for _, prime := range smallPrimes { + if m%uint64(prime) == 0 && (qbits > 6 || m != uint64(prime)) { + continue NextDelta + } + } + + if delta > 0 { + bigMod.SetUint64(delta) + q.Add(q, bigMod) + } + + qMod3 := new(big.Int).Mod(q, big.NewInt(3)) + if qMod3.Cmp(big.NewInt(1)) == 0 { + continue NextDelta + } + + p.Mul(q, big.NewInt(2)) + p.Add(p, big.NewInt(1)) + if !isPrimeCandidate(p) { + continue NextDelta + } + + break + } + + // There is a tiny possibility that, by adding delta, we caused + // the number to be one bit too long. Thus we check BitLen + // here. + if q.ProbablyPrime(20) && + isPocklingtonCriterionSatisfied(p) && + q.BitLen() == qbits { + return + } + } +} + +func isPocklingtonCriterionSatisfied(p *big.Int) bool { + return new(big.Int).Exp( + big.NewInt(2), + new(big.Int).Sub(p, big.NewInt(1)), + p, + ).Cmp(big.NewInt(1)) == 0 +} + +func isPrimeCandidate(number *big.Int) bool { + bigMod := new(big.Int) + bigMod.Mod(number, smallPrimesProduct) + m := bigMod.Uint64() + + for _, prime := range smallPrimes { + if m%uint64(prime) == 0 && m != uint64(prime) { + return false + } + } + + return true +} From e0b836a4bc5c07e52e83e913cc8665976a5f1b67 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Thu, 26 Jul 2018 08:53:23 +0200 Subject: [PATCH 02/16] Generate safe prime concurrently How fast we generate a prime number is mostly a matter of luck. It varies from miliseconds to seconds and depends how lucky we are with drawing 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 algorithm, we get the result faster. --- safe_prime_generator.go | 224 +++++++++++++++++++++++------------ safe_prime_generator_test.go | 98 +++++++++++++++ 2 files changed, 244 insertions(+), 78 deletions(-) create mode 100644 safe_prime_generator_test.go diff --git a/safe_prime_generator.go b/safe_prime_generator.go index 4266472..8d9e2fa 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -5,9 +5,14 @@ package paillier import ( + "context" + "crypto/rand" "errors" + "fmt" "io" "math/big" + "sync" + "time" ) // smallPrimes is a list of small, prime numbers that allows us to rapidly @@ -25,102 +30,165 @@ var smallPrimes = []uint8{ // operations. var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365) -// Prime returns a number, p, of the given size, such that p is prime -// with high probability. -// Prime will return error for any error returned by rand.Read or if bits < 2. -func SafePrimes(rand io.Reader, bits int) (p *big.Int, q *big.Int, err error) { - qbits := bits - 1 +type safePrime struct { + p *big.Int + q *big.Int +} - if qbits < 2 { - err = errors.New("crypto/rand: prime size must be at least 2-bit") - return +func GenerateSafePrime( + bitLen int, + concurrencyLevel int, + timeout time.Duration, +) (*big.Int, *big.Int, error) { + if bitLen < 6 { + return nil, nil, errors.New("safe prime size must be at least 6 bits") } - b := uint(qbits % 8) + primeChan := make(chan safePrime, 1) + errChan := make(chan error, 1) + + defer close(primeChan) + defer close(errChan) + + mutex := &sync.Mutex{} + + ctx, cancel := context.WithCancel(context.Background()) + + for i := 0; i < concurrencyLevel; i++ { + runGenPrimeRoutine(ctx, primeChan, errChan, mutex, rand.Reader, bitLen) + } + + go func() { + time.Sleep(timeout) + mutex.Lock() + cancel() + mutex.Unlock() + }() + + select { + case result := <-primeChan: + mutex.Lock() + cancel() + mutex.Unlock() + return result.p, result.q, nil + case err := <-errChan: + mutex.Lock() + cancel() + mutex.Unlock() + return nil, nil, err + case <-ctx.Done(): + return nil, nil, fmt.Errorf("generator timed out after %v", timeout) + } +} + +func runGenPrimeRoutine( + ctx context.Context, + primeChan chan safePrime, + errChan chan error, + mutex *sync.Mutex, + rand io.Reader, + pBitLen int, +) { + qBitLen := pBitLen - 1 + b := uint(qBitLen % 8) if b == 0 { b = 8 } - bytes := make([]byte, (qbits+7)/8) - p = new(big.Int) - q = new(big.Int) + bytes := make([]byte, (qBitLen+7)/8) + p := new(big.Int) + q := new(big.Int) bigMod := new(big.Int) - //NextRand: - for { - _, err = io.ReadFull(rand, bytes) - if err != nil { - return nil, nil, err - } + go func() { + for { + select { + case <-ctx.Done(): + return + default: + _, err := io.ReadFull(rand, bytes) + if err != nil { + errChan <- err + return + } - // Clear bits in the first byte to make sure the candidate has a size <= bits. - bytes[0] &= uint8(int(1<= 2 { - bytes[0] |= 3 << (b - 2) - } else { - // Here b==1, because b cannot be zero. - bytes[0] |= 1 - if len(bytes) > 1 { - bytes[1] |= 0x80 - } - } - // Make the value odd since an even number this large certainly isn't prime. - bytes[len(bytes)-1] |= 1 + // Clear bits in the first byte to make sure the candidate has a size <= bits. + bytes[0] &= uint8(int(1<= 2 { + bytes[0] |= 3 << (b - 2) + } else { + // Here b==1, because b cannot be zero. + bytes[0] |= 1 + if len(bytes) > 1 { + bytes[1] |= 0x80 + } + } + // Make the value odd since an even number this large certainly isn't prime. + bytes[len(bytes)-1] |= 1 - q.SetBytes(bytes) + q.SetBytes(bytes) - p.Mul(q, big.NewInt(2)) - p.Add(p, big.NewInt(1)) + p.Mul(q, big.NewInt(2)) + p.Add(p, big.NewInt(1)) - // Calculate the value mod the product of smallPrimes. If it's - // a multiple of any of these primes we add two until it isn't. - // The probability of overflowing is minimal and can be ignored - // because we still perform Miller-Rabin tests on the result. - bigMod.Mod(q, smallPrimesProduct) - mod := bigMod.Uint64() + // Calculate the value mod the product of smallPrimes. If it's + // a multiple of any of these primes we add two until it isn't. + // The probability of overflowing is minimal and can be ignored + // because we still perform Miller-Rabin tests on the result. + bigMod.Mod(q, smallPrimesProduct) + mod := bigMod.Uint64() - NextDelta: - for delta := uint64(0); delta < 1<<20; delta += 2 { - m := mod + delta - for _, prime := range smallPrimes { - if m%uint64(prime) == 0 && (qbits > 6 || m != uint64(prime)) { - continue NextDelta + NextDelta: + for delta := uint64(0); delta < 1<<20; delta += 2 { + m := mod + delta + for _, prime := range smallPrimes { + if m%uint64(prime) == 0 && (qBitLen > 6 || m != uint64(prime)) { + continue NextDelta + } + } + + if delta > 0 { + bigMod.SetUint64(delta) + q.Add(q, bigMod) + } + + qMod3 := new(big.Int).Mod(q, big.NewInt(3)) + if qMod3.Cmp(big.NewInt(1)) == 0 { + continue NextDelta + } + + p.Mul(q, big.NewInt(2)) + p.Add(p, big.NewInt(1)) + if !isPrimeCandidate(p) { + continue NextDelta + } + + break + } + + // There is a tiny possibility that, by adding delta, we caused + // the number to be one bit too long. Thus we check BitLen + // here. + if q.ProbablyPrime(20) && + isPocklingtonCriterionSatisfied(p) && + q.BitLen() == qBitLen { + + mutex.Lock() + if ctx.Err() == nil { + primeChan <- safePrime{p, q} + } + mutex.Unlock() + + return } } - - if delta > 0 { - bigMod.SetUint64(delta) - q.Add(q, bigMod) - } - - qMod3 := new(big.Int).Mod(q, big.NewInt(3)) - if qMod3.Cmp(big.NewInt(1)) == 0 { - continue NextDelta - } - - p.Mul(q, big.NewInt(2)) - p.Add(p, big.NewInt(1)) - if !isPrimeCandidate(p) { - continue NextDelta - } - - break } - - // There is a tiny possibility that, by adding delta, we caused - // the number to be one bit too long. Thus we check BitLen - // here. - if q.ProbablyPrime(20) && - isPocklingtonCriterionSatisfied(p) && - q.BitLen() == qbits { - return - } - } + }() } func isPocklingtonCriterionSatisfied(p *big.Int) bool { diff --git a/safe_prime_generator_test.go b/safe_prime_generator_test.go new file mode 100644 index 0000000..a0d1ba9 --- /dev/null +++ b/safe_prime_generator_test.go @@ -0,0 +1,98 @@ +package paillier + +import ( + "errors" + "fmt" + "math/big" + "reflect" + "testing" + "time" +) + +func TestAsyncGenerator(t *testing.T) { + concurrencyLevel := 4 + + var tests = map[string]struct { + bitLen int + timeout time.Duration + expectedError error + }{ + "primes successfully generated": { + bitLen: 512, + timeout: 60 * time.Second, + expectedError: nil, + }, + "generator timed out": { + bitLen: 8192, + timeout: 1 * time.Second, + expectedError: errors.New("generator timed out after 1s"), + }, + "bit length is 5": { + bitLen: 5, + timeout: 1 * time.Second, + expectedError: errors.New("safe prime size must be at least 6 bits"), + }, + "bit length is 6": { + bitLen: 6, + timeout: 60 * time.Second, + expectedError: nil, + }, + } + + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + p, q, err := GenerateSafePrime( + test.bitLen, + concurrencyLevel, + test.timeout, + ) + + if test.expectedError != nil { + if !reflect.DeepEqual(test.expectedError, err) { + t.Fatalf( + "Unexpected error\nActual: %v\nExpected: %v", + err, + test.expectedError, + ) + } + } else { + if err != nil { + t.Fatal(err) + } + + if !p.ProbablyPrime(20) { + t.Errorf("p is not prime; p = %v", p) + } + + if !q.ProbablyPrime(20) { + t.Errorf("q is not prime; q = %v", q) + } + + // p = 2q + 1 ? + expectedP := new(big.Int) + expectedP.Mul(big.NewInt(2), q) + expectedP.Add(expectedP, big.NewInt(1)) + + if expectedP.Cmp(p) != 0 { + t.Errorf("2q + 1 != p") + } + + if len(p.Bytes()) != test.bitLen { + fmt.Errorf( + "Unexpected p bit length\nActual: %v\nExpected: %v", + len(p.Bytes()), + test.bitLen, + ) + } + + if len(q.Bytes()) != test.bitLen-1 { + fmt.Errorf( + "Unexpected q bit length\nActual: %v\nExpected: %v", + len(q.Bytes()), + test.bitLen-1, + ) + } + } + }) + } +} From 21d6230b20aac0bb3ef1cb04fed1d92890ac39a8 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Thu, 26 Jul 2018 10:19:20 +0200 Subject: [PATCH 03/16] Improved documentation for the safe prime generator --- safe_prime_generator.go | 85 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 8 deletions(-) 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< Date: Thu, 26 Jul 2018 11:12:23 +0200 Subject: [PATCH 04/16] Wait for all goroutines to finish Initially, we did not wait for all goroutines to finish which was fine as long as we did not run all tests with `go test` and observe deadlock in 50% of cases. This is interesting, since deadlock never occurred when running safe_prime_generator_test.go. Anyway, waiting for all go routines to finish shouldn't harm the performance a lot and it gives a guarantee we did not miss anything. --- safe_prime_generator.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index c947041..8fa65a2 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -63,7 +63,7 @@ func GenerateSafePrime( bitLen int, concurrencyLevel int, timeout time.Duration, -) (*big.Int, *big.Int, error) { +) (p *big.Int, q *big.Int, err error) { if bitLen < 6 { return nil, nil, errors.New("safe prime size must be at least 6 bits") } @@ -75,11 +75,15 @@ func GenerateSafePrime( defer close(errChan) mutex := &sync.Mutex{} + waitGroup := &sync.WaitGroup{} + waitGroup.Add(concurrencyLevel) ctx, cancel := context.WithCancel(context.Background()) for i := 0; i < concurrencyLevel; i++ { - runGenPrimeRoutine(ctx, primeChan, errChan, mutex, rand.Reader, bitLen) + runGenPrimeRoutine( + ctx, primeChan, errChan, mutex, waitGroup, rand.Reader, bitLen, + ) } // Cancel after the specified timeout. @@ -95,15 +99,18 @@ func GenerateSafePrime( mutex.Lock() cancel() mutex.Unlock() - return result.p, result.q, nil + p, q, err = result.p, result.q, nil case err := <-errChan: mutex.Lock() cancel() mutex.Unlock() - return nil, nil, err + p, q, err = nil, nil, err case <-ctx.Done(): - return nil, nil, fmt.Errorf("generator timed out after %v", timeout) + p, q, err = nil, nil, fmt.Errorf("generator timed out after %v", timeout) } + + waitGroup.Wait() + return } type safePrime struct { @@ -151,6 +158,7 @@ func runGenPrimeRoutine( primeChan chan safePrime, errChan chan error, mutex *sync.Mutex, + waitGroup *sync.WaitGroup, rand io.Reader, pBitLen int, ) { @@ -170,6 +178,7 @@ func runGenPrimeRoutine( for { select { case <-ctx.Done(): + waitGroup.Done() return default: _, err := io.ReadFull(rand, bytes) @@ -253,6 +262,7 @@ func runGenPrimeRoutine( } mutex.Unlock() + waitGroup.Done() return } } From ab52193fbff7e7dc119d2d1afcc4a3dea5ad82ae Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Thu, 26 Jul 2018 11:22:12 +0200 Subject: [PATCH 05/16] Use the new, faster way for generating safe primes everywhere --- utils.go | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/utils.go b/utils.go index c25ff3a..23944d4 100644 --- a/utils.go +++ b/utils.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "io" "math/big" + "time" ) var ZERO = big.NewInt(0) @@ -22,18 +23,11 @@ func Factorial(n int) *big.Int { // Returns 2 primes such that p = 2 * q + 1 and that the length of // p is nbits. `p` is called a safe prime +// +// Deprecated: This function has been left here just for backward compatibility. +// Please use `GenerateSafePrime` from the `safe_prime_generator.go` directly. func GenerateSafePrimes(nbits int, random io.Reader) (p, q *big.Int, err error) { - for { - q, err = rand.Prime(random, nbits-1) - if err != nil { - return - } - p = (new(big.Int)).Mul(q, big.NewInt(2)) - p = p.Add(p, big.NewInt(1)) - if p.ProbablyPrime(50) { //a probability of 2**-100 of not being prime - return - } - } + return GenerateSafePrime(nbits, 4, 120*time.Second) } // Generate a random element in the group of all the elements in Z/nZ that From 7ab24f06419078cd13d4886aae5628f0f3f15d0d Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Thu, 26 Jul 2018 15:39:58 +0200 Subject: [PATCH 06/16] Drop support for Go 1.3, 1,4. Support Go 1.7+ Go 1.7 moved the `golang.org/x/net/context` package into the standard library as `context`. Code importing `context` is no longer compiling for Go versions earlier than 1.7. Since 1.7 was released two years ago and we don't want to sacrifice quality, we decided to drop support for 1.4 and 1.3 and support 1.7+ now. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0fff33c..9ed898b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ --- language: go go: - - 1.3 - - 1.4 + - 1.7 - tip From ee7e96eaf6569f45845c13f5d5ee54f7ee8baa53 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Fri, 27 Jul 2018 16:45:29 +0200 Subject: [PATCH 07/16] Fixed bit lenght assertions for safe prime generator tests --- safe_prime_generator_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/safe_prime_generator_test.go b/safe_prime_generator_test.go index a0d1ba9..b4e58a5 100644 --- a/safe_prime_generator_test.go +++ b/safe_prime_generator_test.go @@ -2,7 +2,6 @@ package paillier import ( "errors" - "fmt" "math/big" "reflect" "testing" @@ -77,18 +76,18 @@ func TestAsyncGenerator(t *testing.T) { t.Errorf("2q + 1 != p") } - if len(p.Bytes()) != test.bitLen { - fmt.Errorf( + if p.BitLen() != test.bitLen { + t.Fatalf( "Unexpected p bit length\nActual: %v\nExpected: %v", - len(p.Bytes()), + p.BitLen(), test.bitLen, ) } - if len(q.Bytes()) != test.bitLen-1 { - fmt.Errorf( + if q.BitLen() != test.bitLen-1 { + t.Fatalf( "Unexpected q bit length\nActual: %v\nExpected: %v", - len(q.Bytes()), + q.BitLen(), test.bitLen-1, ) } From 7f7e390737b9df12f5a87e93b1b138089847b4a3 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Fri, 27 Jul 2018 16:56:48 +0200 Subject: [PATCH 08/16] Removed not needed code --- safe_prime_generator.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index 8fa65a2..ade754c 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -210,9 +210,6 @@ func runGenPrimeRoutine( q.SetBytes(bytes) - p.Mul(q, big.NewInt(2)) - p.Add(p, big.NewInt(1)) - // Calculate the value mod the product of smallPrimes. If it's // a multiple of any of these primes we add two until it isn't. // The probability of overflowing is minimal and can be ignored @@ -279,9 +276,7 @@ func isPocklingtonCriterionSatisfied(p *big.Int) bool { } func isPrimeCandidate(number *big.Int) bool { - bigMod := new(big.Int) - bigMod.Mod(number, smallPrimesProduct) - m := bigMod.Uint64() + m := new(big.Int).Mod(number, smallPrimesProduct).Uint64() for _, prime := range smallPrimes { if m%uint64(prime) == 0 && m != uint64(prime) { From 78ea6668f84609083972c34a9f1f6af82e00e302 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Fri, 27 Jul 2018 17:31:27 +0200 Subject: [PATCH 09/16] Improved safe prime generator documentation --- safe_prime_generator.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index ade754c..e1a7799 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -125,7 +125,7 @@ type safePrime struct { // // 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`. +// significant bits 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 @@ -231,6 +231,19 @@ func runGenPrimeRoutine( q.Add(q, bigMod) } + // If `q = 1 (mod 3)`, then `p` is a multiple of `3` so it's + // obviously no prime and such `q` should be rejected. + // This will happen in 50% of cases and we should detect + // and eliminate them early. + // + // Explanation: + // If q = 1 (mod 3) then there exists a q' such that: + // q = 3q' + 1 + // + // Since p = 2q + 1: + // p = 2q + 1 = 2(3q' + 1) + 1 = 6q' + 2 + 1 = 6q' + 3 = + // = 3(2q' + 1) + // So `p` is a multiple of `3`. qMod3 := new(big.Int).Mod(q, big.NewInt(3)) if qMod3.Cmp(big.NewInt(1)) == 0 { continue NextDelta From ca756b481b0e61afe8680f6109a3737b18a5ea78 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Fri, 27 Jul 2018 17:42:05 +0200 Subject: [PATCH 10/16] Removed deprecated GenerateSafePrimes function The new function should be now called directly. --- safe_prime_generator.go | 4 ++-- safe_prime_generator_test.go | 36 +++--------------------------------- thresholdkey_generator.go | 3 ++- utils.go | 10 ---------- utils_test.go | 10 ---------- 5 files changed, 7 insertions(+), 56 deletions(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index e1a7799..7e87275 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -10,7 +10,6 @@ package paillier import ( "context" - "crypto/rand" "errors" "fmt" "io" @@ -63,6 +62,7 @@ func GenerateSafePrime( bitLen int, concurrencyLevel int, timeout time.Duration, + random io.Reader, ) (p *big.Int, q *big.Int, err error) { if bitLen < 6 { return nil, nil, errors.New("safe prime size must be at least 6 bits") @@ -82,7 +82,7 @@ func GenerateSafePrime( for i := 0; i < concurrencyLevel; i++ { runGenPrimeRoutine( - ctx, primeChan, errChan, mutex, waitGroup, rand.Reader, bitLen, + ctx, primeChan, errChan, mutex, waitGroup, random, bitLen, ) } diff --git a/safe_prime_generator_test.go b/safe_prime_generator_test.go index b4e58a5..4b57efe 100644 --- a/safe_prime_generator_test.go +++ b/safe_prime_generator_test.go @@ -1,8 +1,8 @@ package paillier import ( + "crypto/rand" "errors" - "math/big" "reflect" "testing" "time" @@ -44,6 +44,7 @@ func TestAsyncGenerator(t *testing.T) { test.bitLen, concurrencyLevel, test.timeout, + rand.Reader, ) if test.expectedError != nil { @@ -59,38 +60,7 @@ func TestAsyncGenerator(t *testing.T) { t.Fatal(err) } - if !p.ProbablyPrime(20) { - t.Errorf("p is not prime; p = %v", p) - } - - if !q.ProbablyPrime(20) { - t.Errorf("q is not prime; q = %v", q) - } - - // p = 2q + 1 ? - expectedP := new(big.Int) - expectedP.Mul(big.NewInt(2), q) - expectedP.Add(expectedP, big.NewInt(1)) - - if expectedP.Cmp(p) != 0 { - t.Errorf("2q + 1 != p") - } - - if p.BitLen() != test.bitLen { - t.Fatalf( - "Unexpected p bit length\nActual: %v\nExpected: %v", - p.BitLen(), - test.bitLen, - ) - } - - if q.BitLen() != test.bitLen-1 { - t.Fatalf( - "Unexpected q bit length\nActual: %v\nExpected: %v", - q.BitLen(), - test.bitLen-1, - ) - } + AreSafePrimes(p, q, test.bitLen, t) } }) } diff --git a/thresholdkey_generator.go b/thresholdkey_generator.go index 1dab199..755745d 100644 --- a/thresholdkey_generator.go +++ b/thresholdkey_generator.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "io" "math/big" + "time" ) // Generates a threshold Paillier key with an algorithm based on [DJN 10], @@ -58,7 +59,7 @@ func GetThresholdKeyGenerator(nbits, TotalNumberOfDecryptionServers, Threshold i } func (tkg *ThresholdKeyGenerator) generateSafePrimes() (*big.Int, *big.Int, error) { - return GenerateSafePrimes(tkg.nbits, tkg.Random) + return GenerateSafePrime(tkg.nbits, 4, 120*time.Second, tkg.Random) } func (tkg *ThresholdKeyGenerator) initPandP1() error { diff --git a/utils.go b/utils.go index 23944d4..98cca45 100644 --- a/utils.go +++ b/utils.go @@ -4,7 +4,6 @@ import ( "crypto/rand" "io" "math/big" - "time" ) var ZERO = big.NewInt(0) @@ -21,15 +20,6 @@ func Factorial(n int) *big.Int { return ret } -// Returns 2 primes such that p = 2 * q + 1 and that the length of -// p is nbits. `p` is called a safe prime -// -// Deprecated: This function has been left here just for backward compatibility. -// Please use `GenerateSafePrime` from the `safe_prime_generator.go` directly. -func GenerateSafePrimes(nbits int, random io.Reader) (p, q *big.Int, err error) { - return GenerateSafePrime(nbits, 4, 120*time.Second) -} - // Generate a random element in the group of all the elements in Z/nZ that // has a multiplicative inverse. func GetRandomNumberInMultiplicativeGroup(n *big.Int, random io.Reader) (*big.Int, error) { diff --git a/utils_test.go b/utils_test.go index 759e21a..cd86b9b 100644 --- a/utils_test.go +++ b/utils_test.go @@ -87,16 +87,6 @@ func GetEntireRQn(n int) map[int]bool { return ret } -func TestGenerateSafePrimes(t *testing.T) { - for i := 0; i < 10; i++ { - p, q, err := GenerateSafePrimes(20, rand.Reader) - if err != nil { - t.Error(err) - } - AreSafePrimes(p, q, 20, t) - } -} - func TestGetRandomGeneratorOfTheQuadraticResidue(t *testing.T) { tooSmallPrime1, tooSmallPrime2 := b(347), b(359) m := new(big.Int).Mul(tooSmallPrime1, tooSmallPrime2) From 65d7ad4e73817147cc4b5d2f6a5cb962cdfaf75a Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Fri, 27 Jul 2018 17:43:46 +0200 Subject: [PATCH 11/16] Fixed typo in the documentation --- safe_prime_generator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index 7e87275..ea677eb 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -145,7 +145,7 @@ type safePrime struct { // 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 +// We apply Miller-Rabin and Baillie-PSW tests. If they succeed, 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 From 530e40c1d41af56662b1e07487325f00e496261d Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Mon, 30 Jul 2018 09:56:40 +0200 Subject: [PATCH 12/16] Deadlock fixed - result channel buffer increased Although the result channel send operation and context cancel are guarded by the same mutex and context is cancelled by the main thread when it receives a result message from the channnel, there is absolutely no guarantee that one goroutine can't write result to the channel after the first goroutine did the same and before the main thread cancelled the context. This is the following scenario: - Worker #1 acquires mutex, writes to the result channel - Worker #2 acquires mutex, writes to the result channel - Main thread reads from the result channel, acquires mutex, cancels the context Since our result channel (and error channel) had buffer of size 1, the Worker #2 was hanging indefinitely. Setting the buffer size to the number of goroutines fixed the problem. Alternative scenario was: - Worker #1 acquires mutex, writes to the result channel - Main thread acquires mutex, reads from the result channel, cancels the context - Worker #2 receives cancel notification so it completes without yielding the result That's why deadlock was happening only from time to time. --- safe_prime_generator.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index ea677eb..3a3355f 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -68,19 +68,19 @@ func GenerateSafePrime( return nil, nil, errors.New("safe prime size must be at least 6 bits") } - primeChan := make(chan safePrime, 1) - errChan := make(chan error, 1) + primeChan := make(chan safePrime, concurrencyLevel) + errChan := make(chan error, concurrencyLevel) defer close(primeChan) defer close(errChan) mutex := &sync.Mutex{} waitGroup := &sync.WaitGroup{} - waitGroup.Add(concurrencyLevel) ctx, cancel := context.WithCancel(context.Background()) for i := 0; i < concurrencyLevel; i++ { + waitGroup.Add(1) runGenPrimeRoutine( ctx, primeChan, errChan, mutex, waitGroup, random, bitLen, ) From b21e0d7d9706dffb3fa1072e9a66ac6788f96046 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Mon, 30 Jul 2018 12:27:59 +0200 Subject: [PATCH 13/16] Name parameters for the safe prime generator call `4` and `120*time.Seconds` were just some magic numbers in the code. We decided to move them to separate value declarations to make the code more self-explanatory. --- thresholdkey_generator.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/thresholdkey_generator.go b/thresholdkey_generator.go index 755745d..96948a0 100644 --- a/thresholdkey_generator.go +++ b/thresholdkey_generator.go @@ -59,7 +59,9 @@ func GetThresholdKeyGenerator(nbits, TotalNumberOfDecryptionServers, Threshold i } func (tkg *ThresholdKeyGenerator) generateSafePrimes() (*big.Int, *big.Int, error) { - return GenerateSafePrime(tkg.nbits, 4, 120*time.Second, tkg.Random) + concurrencyLevel := 4 + timeout := 120 * time.Second + return GenerateSafePrime(tkg.nbits, concurrencyLevel, timeout, tkg.Random) } func (tkg *ThresholdKeyGenerator) initPandP1() error { From 82f050d7b2bbfc3d7e235b074a4283952000930c Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Mon, 30 Jul 2018 15:46:16 +0200 Subject: [PATCH 14/16] Simplified concurrency mechanisms for goroutines It does not longer make sense to have a mutex and cancelled context check since we have a wait group and channels with buffers with the size equal the number of workers. Without the wait group we had to make sure no goroutine tries to write to the closed channel. Now, channel is closed after all goroutines completed (either by finding prime or cancelling the work), so there is no chance someone tries to write to closed channel. --- safe_prime_generator.go | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index 3a3355f..351c602 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -74,7 +74,6 @@ func GenerateSafePrime( defer close(primeChan) defer close(errChan) - mutex := &sync.Mutex{} waitGroup := &sync.WaitGroup{} ctx, cancel := context.WithCancel(context.Background()) @@ -82,28 +81,22 @@ func GenerateSafePrime( for i := 0; i < concurrencyLevel; i++ { waitGroup.Add(1) runGenPrimeRoutine( - ctx, primeChan, errChan, mutex, waitGroup, random, bitLen, + ctx, primeChan, errChan, waitGroup, random, bitLen, ) } // Cancel after the specified timeout. go func() { time.Sleep(timeout) - mutex.Lock() cancel() - mutex.Unlock() }() select { case result := <-primeChan: - mutex.Lock() cancel() - mutex.Unlock() p, q, err = result.p, result.q, nil case err := <-errChan: - mutex.Lock() cancel() - mutex.Unlock() p, q, err = nil, nil, err case <-ctx.Done(): p, q, err = nil, nil, fmt.Errorf("generator timed out after %v", timeout) @@ -157,7 +150,6 @@ func runGenPrimeRoutine( ctx context.Context, primeChan chan safePrime, errChan chan error, - mutex *sync.Mutex, waitGroup *sync.WaitGroup, rand io.Reader, pBitLen int, @@ -175,10 +167,11 @@ func runGenPrimeRoutine( bigMod := new(big.Int) go func() { + defer waitGroup.Done() + for { select { case <-ctx.Done(): - waitGroup.Done() return default: _, err := io.ReadFull(rand, bytes) @@ -266,13 +259,7 @@ func runGenPrimeRoutine( isPocklingtonCriterionSatisfied(p) && q.BitLen() == qBitLen { - mutex.Lock() - if ctx.Err() == nil { - primeChan <- safePrime{p, q} - } - mutex.Unlock() - - waitGroup.Done() + primeChan <- safePrime{p, q} return } } From 1200243127402a07a4e878ef99f750dc9df80b4a Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Mon, 30 Jul 2018 16:04:00 +0200 Subject: [PATCH 15/16] Simplified how values are returned from the GenerateSafePrime We can use `defer waitGroup.Wait()` to make sure it's called regardless which path we go. This we, we can call `return ...` directly from all select cases and we don't need named parameters. --- safe_prime_generator.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index 351c602..c905172 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -63,7 +63,7 @@ func GenerateSafePrime( concurrencyLevel int, timeout time.Duration, random io.Reader, -) (p *big.Int, q *big.Int, err error) { +) (*big.Int, *big.Int, error) { if bitLen < 6 { return nil, nil, errors.New("safe prime size must be at least 6 bits") } @@ -71,10 +71,11 @@ func GenerateSafePrime( primeChan := make(chan safePrime, concurrencyLevel) errChan := make(chan error, concurrencyLevel) + waitGroup := &sync.WaitGroup{} + defer close(primeChan) defer close(errChan) - - waitGroup := &sync.WaitGroup{} + defer waitGroup.Wait() ctx, cancel := context.WithCancel(context.Background()) @@ -94,16 +95,13 @@ func GenerateSafePrime( select { case result := <-primeChan: cancel() - p, q, err = result.p, result.q, nil + return result.p, result.q, nil case err := <-errChan: cancel() - p, q, err = nil, nil, err + return nil, nil, err case <-ctx.Done(): - p, q, err = nil, nil, fmt.Errorf("generator timed out after %v", timeout) + return nil, nil, fmt.Errorf("generator timed out after %v", timeout) } - - waitGroup.Wait() - return } type safePrime struct { From be670a086c2d88a4d0b65a3b22808e9eda5780cc Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Mon, 30 Jul 2018 16:34:59 +0200 Subject: [PATCH 16/16] Added documentation to isPoclingtonCriterionSatisfied --- safe_prime_generator.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index c905172..f0e70a6 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -265,6 +265,10 @@ func runGenPrimeRoutine( }() } +// Pocklington's criterion can be used to prove the primality of `p = 2q + 1` +// once one has proven the primality of `q`. +// With `q` prime, `p = 2q + 1`, and `p` passing Fermat's primality test to base +// `2` that `2^{p-1} = 1 (mod p)` then `p` is prime as well. func isPocklingtonCriterionSatisfied(p *big.Int) bool { return new(big.Int).Exp( big.NewInt(2),