Skip to content

Commit acd54c9

Browse files
FiloSottilegopherbot
authored andcommitted
crypto/rsa: move key generation to crypto/internal/fips140/rsa
It's about 2x slower, but we'll recover that by implementing trial divisions in a follow-up CL. Updates #69799 For #69536 Change-Id: Icc02f5a268b658d629bbe7fdaf2a42ad3b259e2c Reviewed-on: https://go-review.googlesource.com/c/go/+/632477 Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Russ Cox <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 7d7192e commit acd54c9

File tree

4 files changed

+192
-28
lines changed

4 files changed

+192
-28
lines changed

src/crypto/internal/fips140/bigmod/nat.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ func (x *Nat) resetToBytes(b []byte) *Nat {
102102
if err := x.setBytes(b); err != nil {
103103
panic("bigmod: internal error: bad arithmetic")
104104
}
105+
return x.trim()
106+
}
107+
108+
// trim reduces the size of x to match its value.
109+
func (x *Nat) trim() *Nat {
105110
// Trim most significant (trailing in little-endian) zero limbs.
106111
// We assume comparison with zero (but not the branch) is constant time.
107112
for i := len(x.limbs) - 1; i >= 0; i-- {
@@ -475,8 +480,24 @@ func minusInverseModW(x uint) uint {
475480
// The number of significant bits and whether the modulus is even is leaked
476481
// through timing side-channels.
477482
func NewModulus(b []byte) (*Modulus, error) {
478-
m := &Modulus{}
479-
m.nat = NewNat().resetToBytes(b)
483+
n := NewNat().resetToBytes(b)
484+
return newModulus(n)
485+
}
486+
487+
// NewModulusProduct creates a new Modulus from the product of two numbers
488+
// represented as big-endian byte slices. The result must be greater than one.
489+
func NewModulusProduct(a, b []byte) (*Modulus, error) {
490+
x := NewNat().resetToBytes(a)
491+
y := NewNat().resetToBytes(b)
492+
n := NewNat().reset(len(x.limbs) + len(y.limbs))
493+
for i := range y.limbs {
494+
n.limbs[i+len(x.limbs)] = addMulVVW(n.limbs[i:i+len(x.limbs)], x.limbs, y.limbs[i])
495+
}
496+
return newModulus(n.trim())
497+
}
498+
499+
func newModulus(n *Nat) (*Modulus, error) {
500+
m := &Modulus{nat: n}
480501
if m.nat.IsZero() == yes || m.nat.IsOne() == yes {
481502
return nil, errors.New("modulus must be > 1")
482503
}

src/crypto/internal/fips140/rsa/keygen.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,115 @@
55
package rsa
66

77
import (
8+
"crypto/internal/fips140"
89
"crypto/internal/fips140/bigmod"
910
"crypto/internal/fips140/drbg"
11+
"crypto/internal/randutil"
1012
"errors"
13+
"io"
1114
)
1215

16+
// GenerateKey generates a new RSA key pair of the given bit size.
17+
// bits must be at least 128.
18+
//
19+
// When operating in FIPS mode, rand is ignored.
20+
func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) {
21+
if bits < 128 {
22+
return nil, errors.New("rsa: key too small")
23+
}
24+
fips140.RecordApproved()
25+
if bits < 2048 || bits > 16384 {
26+
fips140.RecordNonApproved()
27+
}
28+
29+
for {
30+
p, err := randomPrime(rand, (bits+1)/2)
31+
if err != nil {
32+
return nil, err
33+
}
34+
q, err := randomPrime(rand, bits/2)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
P, err := bigmod.NewModulus(p)
40+
if err != nil {
41+
return nil, err
42+
}
43+
Q, err := bigmod.NewModulus(q)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
N, err := bigmod.NewModulusProduct(p, q)
49+
if err != nil {
50+
return nil, err
51+
}
52+
if N.BitLen() != bits {
53+
return nil, errors.New("rsa: internal error: modulus size incorrect")
54+
}
55+
56+
φ, err := bigmod.NewModulusProduct(P.Nat().SubOne(N).Bytes(N),
57+
Q.Nat().SubOne(N).Bytes(N))
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
e := bigmod.NewNat().SetUint(65537)
63+
d, ok := bigmod.NewNat().InverseVarTime(e, φ)
64+
if !ok {
65+
continue
66+
}
67+
68+
if e.ExpandFor(φ).Mul(d, φ).IsOne() == 0 {
69+
return nil, errors.New("rsa: internal error: e*d != 1 mod φ(N)")
70+
}
71+
72+
return newPrivateKey(N, 65537, d, P, Q)
73+
}
74+
}
75+
76+
// randomPrime returns a random prime number of the given bit size.
77+
// rand is ignored in FIPS mode.
78+
func randomPrime(rand io.Reader, bits int) ([]byte, error) {
79+
if bits < 64 {
80+
return nil, errors.New("rsa: prime size must be at least 32-bit")
81+
}
82+
83+
b := make([]byte, (bits+7)/8)
84+
for {
85+
if fips140.Enabled {
86+
drbg.Read(b)
87+
} else {
88+
randutil.MaybeReadByte(rand)
89+
if _, err := io.ReadFull(rand, b); err != nil {
90+
return nil, err
91+
}
92+
}
93+
if excess := len(b)*8 - bits; excess != 0 {
94+
b[0] >>= excess
95+
}
96+
97+
// Don't let the value be too small: set the most significant two bits.
98+
// Setting the top two bits, rather than just the top bit, means that
99+
// when two of these values are multiplied together, the result isn't
100+
// ever one bit short.
101+
if excess := len(b)*8 - bits; excess < 7 {
102+
b[0] |= 0b1100_0000 >> excess
103+
} else {
104+
b[0] |= 0b0000_0001
105+
b[1] |= 0b1000_0000
106+
}
107+
108+
// Make the value odd since an even number certainly isn't prime.
109+
b[len(b)-1] |= 1
110+
111+
if isPrime(b) {
112+
return b, nil
113+
}
114+
}
115+
}
116+
13117
// isPrime runs the Miller-Rabin Probabilistic Primality Test from
14118
// FIPS 186-5, Appendix B.3.1.
15119
//

src/crypto/rsa/rsa.go

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
"crypto/internal/boring/bbig"
4747
"crypto/internal/fips140/bigmod"
4848
"crypto/internal/fips140/rsa"
49+
"crypto/internal/fips140only"
4950
"crypto/internal/randutil"
5051
"crypto/rand"
5152
"crypto/subtle"
@@ -278,32 +279,8 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) {
278279
if err := checkKeySize(bits); err != nil {
279280
return nil, err
280281
}
281-
return GenerateMultiPrimeKey(random, 2, bits)
282-
}
283-
284-
// GenerateMultiPrimeKey generates a multi-prime RSA keypair of the given bit
285-
// size and the given random source.
286-
//
287-
// Table 1 in "[On the Security of Multi-prime RSA]" suggests maximum numbers of
288-
// primes for a given bit size.
289-
//
290-
// Although the public keys are compatible (actually, indistinguishable) from
291-
// the 2-prime case, the private keys are not. Thus it may not be possible to
292-
// export multi-prime private keys in certain formats or to subsequently import
293-
// them into other code.
294-
//
295-
// This package does not implement CRT optimizations for multi-prime RSA, so the
296-
// keys with more than two primes will have worse performance.
297-
//
298-
// Deprecated: The use of this function with a number of primes different from
299-
// two is not recommended for the above security, compatibility, and performance
300-
// reasons. Use [GenerateKey] instead.
301-
//
302-
// [On the Security of Multi-prime RSA]: http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
303-
func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey, error) {
304-
randutil.MaybeReadByte(random)
305282

306-
if boring.Enabled && random == boring.RandReader && nprimes == 2 &&
283+
if boring.Enabled && random == boring.RandReader &&
307284
(bits == 2048 || bits == 3072 || bits == 4096) {
308285
bN, bE, bD, bP, bQ, bDp, bDq, bQinv, err := boring.GenerateKeyRSA(bits)
309286
if err != nil {
@@ -339,6 +316,68 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey
339316
return key, nil
340317
}
341318

319+
if fips140only.Enabled && bits < 2048 {
320+
return nil, errors.New("crypto/rsa: use of keys smaller than 2048 bits is not allowed in FIPS 140-only mode")
321+
}
322+
if fips140only.Enabled && bits > 16384 {
323+
return nil, errors.New("crypto/rsa: use of keys larger than 16384 bits is not allowed in FIPS 140-only mode")
324+
}
325+
326+
k, err := rsa.GenerateKey(random, bits)
327+
if err != nil {
328+
return nil, err
329+
}
330+
N, e, d, p, q, dP, dQ, qInv := k.Export()
331+
key := &PrivateKey{
332+
PublicKey: PublicKey{
333+
N: new(big.Int).SetBytes(N),
334+
E: e,
335+
},
336+
D: new(big.Int).SetBytes(d),
337+
Primes: []*big.Int{
338+
new(big.Int).SetBytes(p),
339+
new(big.Int).SetBytes(q),
340+
},
341+
Precomputed: PrecomputedValues{
342+
fips: k,
343+
Dp: new(big.Int).SetBytes(dP),
344+
Dq: new(big.Int).SetBytes(dQ),
345+
Qinv: new(big.Int).SetBytes(qInv),
346+
CRTValues: make([]CRTValue, 0), // non-nil, to match Precompute
347+
},
348+
}
349+
return key, nil
350+
}
351+
352+
// GenerateMultiPrimeKey generates a multi-prime RSA keypair of the given bit
353+
// size and the given random source.
354+
//
355+
// Table 1 in "[On the Security of Multi-prime RSA]" suggests maximum numbers of
356+
// primes for a given bit size.
357+
//
358+
// Although the public keys are compatible (actually, indistinguishable) from
359+
// the 2-prime case, the private keys are not. Thus it may not be possible to
360+
// export multi-prime private keys in certain formats or to subsequently import
361+
// them into other code.
362+
//
363+
// This package does not implement CRT optimizations for multi-prime RSA, so the
364+
// keys with more than two primes will have worse performance.
365+
//
366+
// Deprecated: The use of this function with a number of primes different from
367+
// two is not recommended for the above security, compatibility, and performance
368+
// reasons. Use [GenerateKey] instead.
369+
//
370+
// [On the Security of Multi-prime RSA]: http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
371+
func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey, error) {
372+
if nprimes == 2 {
373+
return GenerateKey(random, bits)
374+
}
375+
if fips140only.Enabled {
376+
return nil, errors.New("crypto/rsa: multi-prime RSA is not allowed in FIPS 140-only mode")
377+
}
378+
379+
randutil.MaybeReadByte(random)
380+
342381
priv := new(PrivateKey)
343382
priv.E = 65537
344383

src/crypto/rsa/rsa_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func TestEverything(t *testing.T) {
195195
t.Parallel()
196196
priv, err := GenerateKey(rand.Reader, size)
197197
if err != nil {
198-
t.Errorf("GenerateKey(%d): %v", size, err)
198+
t.Fatalf("GenerateKey(%d): %v", size, err)
199199
}
200200
if bits := priv.N.BitLen(); bits != size {
201201
t.Errorf("key too short (%d vs %d)", bits, size)

0 commit comments

Comments
 (0)