From b582ef385555f88d41679a621d34bc260de4eb68 Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=A9my=20Oudompheng?= Date: Sun, 24 Feb 2013 17:19:09 +0100 Subject: [PATCH] crypto/rsa: fix infinite loop in GenerateMultiPrimeKey for large nprimes The heuristics for BitLen of a product of randomly generated primes are wrong, and the generated candidates never match the required size for nprimes > 10. This corner case is not expected to be used in practice. R=agl CC=golang-dev https://golang.org/cl/7397052 --- src/pkg/crypto/rsa/rsa.go | 19 +++++++++++++++++-- src/pkg/crypto/rsa/rsa_test.go | 26 ++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 543070f90f..35a5f7c3c6 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -150,6 +150,20 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva NextSetOfPrimes: for { todo := bits + // crypto/rand should set the top two bits in each prime. + // Thus each prime has the form + // p_i = 2^bitlen(p_i) × 0.11... (in base 2). + // And the product is: + // P = 2^todo × α + // where α is the product of nprimes numbers of the form 0.11... + // + // If α < 1/2 (which can happen for nprimes > 2), we need to + // shift todo to compensate for lost bits: the mean value of 0.11... + // is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2 + // will give good results. + if nprimes >= 7 { + todo += (nprimes - 2) / 5 + } for i := 0; i < nprimes; i++ { primes[i], err = rand.Prime(random, todo/(nprimes-i)) if err != nil { @@ -176,8 +190,9 @@ NextSetOfPrimes: totient.Mul(totient, pminus1) } if n.BitLen() != bits { - // This should never happen because crypto/rand should - // set the top two bits in each prime. + // This should never happen for nprimes == 2 because + // crypto/rand should set the top two bits in each prime. + // For nprimes > 2 we hope it does not happen often. continue NextSetOfPrimes } diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go index 9be22a8f0b..f08cfe73c4 100644 --- a/src/pkg/crypto/rsa/rsa_test.go +++ b/src/pkg/crypto/rsa/rsa_test.go @@ -28,11 +28,11 @@ func TestKeyGeneration(t *testing.T) { } func Test3PrimeKeyGeneration(t *testing.T) { + size := 768 if testing.Short() { - return + size = 256 } - size := 768 priv, err := GenerateMultiPrimeKey(rand.Reader, 3, size) if err != nil { t.Errorf("failed to generate key") @@ -41,11 +41,11 @@ func Test3PrimeKeyGeneration(t *testing.T) { } func Test4PrimeKeyGeneration(t *testing.T) { + size := 768 if testing.Short() { - return + size = 256 } - size := 768 priv, err := GenerateMultiPrimeKey(rand.Reader, 4, size) if err != nil { t.Errorf("failed to generate key") @@ -53,6 +53,24 @@ func Test4PrimeKeyGeneration(t *testing.T) { testKeyBasics(t, priv) } +func TestNPrimeKeyGeneration(t *testing.T) { + primeSize := 64 + maxN := 24 + if testing.Short() { + primeSize = 16 + maxN = 16 + } + // Test that generation of N-prime keys works for N > 4. + for n := 5; n < maxN; n++ { + priv, err := GenerateMultiPrimeKey(rand.Reader, n, 64+n*primeSize) + if err == nil { + testKeyBasics(t, priv) + } else { + t.Errorf("failed to generate %d-prime key", n) + } + } +} + func TestGnuTLSKey(t *testing.T) { // This is a key generated by `certtool --generate-privkey --bits 128`. // It's such that de ≢ 1 mod φ(n), but is congruent mod the order of -- 2.48.1