Skip to content

Commit a505ff8

Browse files
authored
Use GCD to check RSA moduli for small primes (letsencrypt#4883)
The existing checkSmallPrimes function maintains a table of primes, converts them to an array of *big.Int and uses the resulting values for comparing against a RSA modulus as follows: for _, prime := range smallPrimes { if modulus % prime != 0 { return invalid } } return valid This incurs substantial overhead as each prime is checked individually, invoking QuoRem(...) each time. By multiplying the primes together into a single *big.Int, we can utilize a single library call, GCD(...), and check all the values at once. While a single GCD invocation is slower than a single QuoRem, 133 such invocations of QuoRem are together slower than the single GCD. BenchmarkSmallPrimeGCD BenchmarkSmallPrimeGCD-4 72759 16240 ns/op BenchmarkSmallPrimeIndividualMods BenchmarkSmallPrimeIndividualMods-4 8866 165265 ns/op This gives us room to later increase the number of smallPrimes, if desired, while keeping the same timing profile. Currently the product of the 133 primes in smallPrimes fits within 1040 bits. Signed-off-by: Alexander Scheel <alexander.m.scheel@gmail.com>
1 parent e600b9e commit a505ff8

File tree

1 file changed

+16
-10
lines changed

1 file changed

+16
-10
lines changed

goodkey/good_key.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ var smallPrimeInts = []int64{
3636
// singleton defines the object of a Singleton pattern
3737
var (
3838
smallPrimesSingleton sync.Once
39-
smallPrimes []*big.Int
39+
smallPrimesProduct *big.Int
4040
)
4141

4242
// BlockedKeyCheckFunc is used to pass in the sa.BlockedKey method to KeyPolicy,
@@ -316,20 +316,26 @@ func (policy *KeyPolicy) goodKeyRSA(key *rsa.PublicKey) (err error) {
316316
//
317317
// Short circuits; execution time is dependent on i. Do not use this on secret
318318
// values.
319+
//
320+
// Rather than checking each prime individually (invoking Mod on each),
321+
// multiply the primes together and let GCD do our work for us: if the
322+
// GCD between <key> and <product of primes> is not one, we know we have
323+
// a bad key. This is substantially faster than checking each prime
324+
// individually.
319325
func checkSmallPrimes(i *big.Int) bool {
320326
smallPrimesSingleton.Do(func() {
327+
smallPrimesProduct = big.NewInt(1)
321328
for _, prime := range smallPrimeInts {
322-
smallPrimes = append(smallPrimes, big.NewInt(prime))
329+
smallPrimesProduct.Mul(smallPrimesProduct, big.NewInt(prime))
323330
}
324331
})
325332

326-
for _, prime := range smallPrimes {
327-
var result big.Int
328-
result.Mod(i, prime)
329-
if result.Sign() == 0 {
330-
return true
331-
}
332-
}
333+
// When the GCD is 1, i and smallPrimesProduct are coprime, meaning they
334+
// share no common factors. When the GCD is not one, it is the product of
335+
// all common factors, meaning we've identified at least one small prime
336+
// which invalidates i as a valid key.
333337

334-
return false
338+
var result big.Int
339+
result.GCD(nil, nil, i, smallPrimesProduct)
340+
return result.Cmp(big.NewInt(1)) != 0
335341
}

0 commit comments

Comments
 (0)