Skip to content

Commit 24d31ef

Browse files
committed
crypto/rsa: reimplement GenerateKey per FIPS 186-5
Submitting on behalf of @archanaravindar. This patch implements RSA Key Generation per FIPS 186-5. The implementation uses math/big despite eventually needing to port to bigmod in order to not have to validate the math/big package in the FIPS certification. The port to bigmod will be committed as a follow-up patch. For golang#69799
1 parent bea9b91 commit 24d31ef

File tree

2 files changed

+161
-2
lines changed

2 files changed

+161
-2
lines changed

src/crypto/rsa/rsa.go

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"crypto/rand"
3434
"crypto/subtle"
3535
"errors"
36+
"fmt"
3637
"hash"
3738
"io"
3839
"math"
@@ -272,7 +273,165 @@ func (priv *PrivateKey) Validate() error {
272273
// returned key does not depend deterministically on the bytes read from rand,
273274
// and may change between calls and/or between versions.
274275
func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
275-
return GenerateMultiPrimeKey(random, 2, bits)
276+
if bits < 2048 {
277+
// Fall back to old implementation for smaller keys.
278+
return GenerateMultiPrimeKey(random, 2, bits)
279+
}
280+
281+
priv := new(PrivateKey)
282+
priv.E = 65537
283+
// p and q
284+
primes := make([]*big.Int, 2)
285+
priv.Primes = primes
286+
287+
if priv.rsaFipsGeneratePrimeFactors(bits) != nil {
288+
return nil, errors.New("crypto/rsa: could not generate prime factors p,q")
289+
}
290+
n := new(big.Int).Set(bigOne)
291+
totient := new(big.Int).Set(bigOne)
292+
pminus1 := new(big.Int)
293+
for _, prime := range primes {
294+
n.Mul(n, prime)
295+
pminus1.Sub(prime, bigOne)
296+
totient.Mul(totient, pminus1)
297+
}
298+
priv.D = new(big.Int)
299+
e := big.NewInt(int64(priv.E))
300+
ok := priv.D.ModInverse(e, totient)
301+
302+
if ok != nil {
303+
priv.N = n
304+
} else {
305+
return nil, errors.New("crypto/rsa: modulus error with public key exponent")
306+
}
307+
priv.N = n
308+
priv.Precomputed = PrecomputedValues{Dp: nil, Dq: nil, Qinv: nil, CRTValues: make([]CRTValue, 0), n: nil, p: nil, q: nil}
309+
priv.Precompute()
310+
return priv, nil
311+
}
312+
313+
func diffCheck(p *big.Int, q *big.Int, bits int) bool {
314+
// 2^(nlen/2)-100
315+
limit := new(big.Int).Lsh(bigOne, (uint)(bits>>1)-99)
316+
z := new(big.Int).Sub(p, q)
317+
z = z.Abs(z)
318+
if z.Cmp(limit) <= 0 {
319+
return true
320+
} else {
321+
return false
322+
}
323+
}
324+
325+
func rsaFipsAuxPrimeMRRounds(bits int) int {
326+
if bits >= 4096 {
327+
return 44
328+
}
329+
if bits >= 3072 {
330+
return 41
331+
}
332+
if bits >= 2048 {
333+
return 38
334+
}
335+
return 0
336+
}
337+
338+
func (priv *PrivateKey) rsaFipsGeneratePrimeFactors(bits int) error {
339+
rounds := rsaFipsAuxPrimeMRRounds(bits)
340+
bytes := ((bits >> 1) + 7) >> 3
341+
342+
E := new(big.Int).SetInt64(int64(priv.E))
343+
344+
// 1/sqrt(2) * 2^256
345+
base, ok := new(big.Int).SetString("0xB504F333F9DE6484597D89B3754ABE9F1D6F60BA893BA84CED17AC8583339916", 0)
346+
if !ok {
347+
panic("crypto/rsa: overflow of static constant sqrt2inv")
348+
}
349+
if (bits >> 1) < 257 {
350+
return errors.New("crypto/rsa: Number of bits too small")
351+
}
352+
sqrtinv := new(big.Int).Lsh(base, (uint)((bits>>1)-257))
353+
354+
i := 0
355+
pbuf := make([]byte, bytes)
356+
var p, q *big.Int
357+
for {
358+
// Generate p
359+
if _, err := rand.Read(pbuf); err != nil {
360+
panic("crypto/rsa: RNG failure")
361+
}
362+
pbuf[bytes-1] |= 1
363+
pbuf[0] |= 0xe0
364+
pbuf[1] |= 0xa0
365+
p = new(big.Int).SetBytes(pbuf)
366+
367+
// check if p < 1/sqrt(2)*(2^(bits/2)-1)
368+
for p.Cmp(sqrtinv) < 0 {
369+
if _, err := rand.Read(pbuf); err != nil {
370+
return fmt.Errorf("crypto/rsa: error reading from random number generator: %s", err)
371+
}
372+
pbuf[bytes-1] |= 1
373+
pbuf[0] |= 0xe0
374+
pbuf[1] |= 0xa0
375+
376+
p = new(big.Int).SetBytes(pbuf)
377+
}
378+
diff := new(big.Int).Sub(p, bigOne)
379+
ret := new(big.Int).GCD(nil, nil, diff, E)
380+
if ret.Cmp(bigOne) == 0 {
381+
isPrime := p.ProbablyPrime(rounds)
382+
if isPrime {
383+
goto genq
384+
}
385+
}
386+
i++
387+
if i >= 5*bits {
388+
priv.Primes[0] = nil
389+
priv.Primes[1] = nil
390+
return errors.New("crypto/rsa: number of tries to find prime factor exceeded limit")
391+
}
392+
}
393+
genq:
394+
// Generate q
395+
i = 0
396+
for {
397+
if _, err := rand.Read(pbuf); err != nil {
398+
return fmt.Errorf("crypto/rsa: error reading from random number generator: %s", err)
399+
}
400+
pbuf[bytes-1] |= 1
401+
pbuf[0] |= 0xe0
402+
pbuf[1] |= 0x80
403+
404+
q = new(big.Int).SetBytes(pbuf)
405+
406+
// check if q < 1/sqrt(2)*(2^(bits/2)-1)
407+
for q.Cmp(sqrtinv) < 0 || diffCheck(p, q, bits) {
408+
if _, err := rand.Read(pbuf); err != nil {
409+
return fmt.Errorf("crypto/rsa: error reading from random number generator: %s", err)
410+
}
411+
pbuf[bytes-1] |= 1
412+
pbuf[0] |= 0xe0
413+
pbuf[1] |= 0x80
414+
415+
q = new(big.Int).SetBytes(pbuf)
416+
}
417+
diff := new(big.Int).Sub(q, bigOne)
418+
ret := new(big.Int).GCD(nil, nil, diff, E)
419+
if ret.Cmp(bigOne) == 0 {
420+
isPrime := q.ProbablyPrime(rounds)
421+
if isPrime {
422+
break
423+
}
424+
}
425+
i++
426+
if i >= 10*bits {
427+
priv.Primes[0] = nil
428+
priv.Primes[1] = nil
429+
return errors.New("crypto/rsa: number of tries to find prime factor exceeded limit")
430+
}
431+
}
432+
priv.Primes[0] = p
433+
priv.Primes[1] = q
434+
return nil
276435
}
277436

278437
// GenerateMultiPrimeKey generates a multi-prime RSA keypair of the given bit

src/crypto/rsa/rsa_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
)
2525

2626
func TestKeyGeneration(t *testing.T) {
27-
for _, size := range []int{128, 1024, 2048, 3072} {
27+
for _, size := range []int{128, 1024, 2048, 3072, 4096, 8192} {
2828
priv, err := GenerateKey(rand.Reader, size)
2929
if err != nil {
3030
t.Errorf("GenerateKey(%d): %v", size, err)

0 commit comments

Comments
 (0)