From 975bf6d3236118ad69e1555faa0d8264923770e8 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Fri, 28 Dec 2012 19:11:37 -0500 Subject: [PATCH] crypto/rsa: ensure that RSA keys use the full number of bits. While half of all numbers don't have their most-significant bit set, this is becoming increasingly impermissible for RSA moduli. In an attempt to exclude weak keys, several bits of software either do, or will, enforce that RSA moduli are >= 1024-bits. However, Go often generates 1023-bit RSA moduli which this software would then reject. This change causes crypto/rsa to regenerate the primes in the event that the result is shorter than requested. It also alters crypto/rand in order to remove the performance impact of this: The most important change to crypto/rand is that it will now set the top two bits in a generated prime (OpenSSL does the same thing). Multiplying two n/2 bit numbers, where each have the top two bits set, will always result in an n-bit product. (The effectively makes the crypto/rsa change moot, but that seems too fragile to depend on.) Also this change adds code to crypto/rand to rapidly eliminate some obviously composite numbers and reduce the number of Miller-Rabin tests needed to generate a prime. R=rsc, minux.ma CC=golang-dev https://golang.org/cl/7002050 --- src/pkg/crypto/rand/util.go | 61 ++++++++++++++++++++++++++++++++-- src/pkg/crypto/rsa/rsa.go | 5 +++ src/pkg/crypto/rsa/rsa_test.go | 3 ++ 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/pkg/crypto/rand/util.go b/src/pkg/crypto/rand/util.go index 5391c1829b9..50e5b162bdd 100644 --- a/src/pkg/crypto/rand/util.go +++ b/src/pkg/crypto/rand/util.go @@ -10,6 +10,21 @@ import ( "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. func Prime(rand io.Reader, bits int) (p *big.Int, err error) { @@ -25,6 +40,8 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err error) { bytes := make([]byte, (bits+7)/8) p = new(big.Int) + bigMod := new(big.Int) + for { _, err = io.ReadFull(rand, bytes) if err != nil { @@ -33,13 +50,51 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err error) { // 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 p.SetBytes(bytes) - if p.ProbablyPrime(20) { + + // 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(p, 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 { + continue NextDelta + } + } + + if delta > 0 { + bigMod.SetUint64(delta) + p.Add(p, bigMod) + } + 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 p.ProbablyPrime(20) && p.BitLen() == bits { return } } diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 6addd04bce1..543070f90fb 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -175,6 +175,11 @@ NextSetOfPrimes: pminus1.Sub(prime, bigOne) totient.Mul(totient, pminus1) } + if n.BitLen() != bits { + // This should never happen because crypto/rand should + // set the top two bits in each prime. + continue NextSetOfPrimes + } g := new(big.Int) priv.D = new(big.Int) diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go index f9fa56efe72..5fdf0b49146 100644 --- a/src/pkg/crypto/rsa/rsa_test.go +++ b/src/pkg/crypto/rsa/rsa_test.go @@ -21,6 +21,9 @@ func TestKeyGeneration(t *testing.T) { if err != nil { t.Errorf("failed to generate key") } + if bits := priv.N.BitLen(); bits != size { + t.Errorf("key too short (%d vs %d)", bits, size) + } testKeyBasics(t, priv) }