Skip to content

Commit b2df703

Browse files
FiloSottileAravindhKudiyarasan-ET
authored andcommitted
crypto: implement fips140=only mode
Running the test suite in this mode is definitely not an option. Testing this will probably look like a very long test that tries all functions. Filed golang#70514 to track the tests. For golang#70123 Change-Id: I6f67de83da37dd1e94e620b7f4f4f6aabe040c41 Reviewed-on: https://go-review.googlesource.com/c/go/+/631018 Reviewed-by: Dmitri Shuralyov <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Reviewed-by: Daniel McCarney <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 194de8f commit b2df703

File tree

19 files changed

+904
-57
lines changed

19 files changed

+904
-57
lines changed

src/crypto/cipher/cfb.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
package cipher
88

99
import (
10-
"crypto/internal/alias"
10+
"crypto/internal/fips140/alias"
11+
"crypto/internal/fips140only"
1112
"crypto/subtle"
1213
)
1314

@@ -54,13 +55,19 @@ func (x *cfb) XORKeyStream(dst, src []byte) {
5455
// using the given [Block]. The iv must be the same length as the [Block]'s block
5556
// size.
5657
func NewCFBEncrypter(block Block, iv []byte) Stream {
58+
if fips140only.Enabled {
59+
panic("crypto/cipher: use of CFB is not allowed in FIPS 140-only mode")
60+
}
5761
return newCFB(block, iv, false)
5862
}
5963

6064
// NewCFBDecrypter returns a [Stream] which decrypts with cipher feedback mode,
6165
// using the given [Block]. The iv must be the same length as the [Block]'s block
6266
// size.
6367
func NewCFBDecrypter(block Block, iv []byte) Stream {
68+
if fips140only.Enabled {
69+
panic("crypto/cipher: use of CFB is not allowed in FIPS 140-only mode")
70+
}
6471
return newCFB(block, iv, true)
6572
}
6673

src/crypto/cipher/gcm.go

+158-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
package cipher
66

77
import (
8-
"crypto/internal/alias"
8+
"crypto/internal/fips140/aes"
9+
"crypto/internal/fips140/aes/gcm"
10+
"crypto/internal/fips140/alias"
11+
"crypto/internal/fips140only"
912
"crypto/subtle"
1013
"errors"
1114
"internal/byteorder"
@@ -83,7 +86,10 @@ type gcm struct {
8386
// An exception is when the underlying [Block] was created by aes.NewCipher
8487
// on systems with hardware support for AES. See the [crypto/aes] package documentation for details.
8588
func NewGCM(cipher Block) (AEAD, error) {
86-
return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, gcmTagSize)
89+
if fips140only.Enabled {
90+
return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
91+
}
92+
return newGCM(cipher, gcmStandardNonceSize, gcmTagSize)
8793
}
8894

8995
// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
@@ -94,7 +100,10 @@ func NewGCM(cipher Block) (AEAD, error) {
94100
// cryptosystem that uses non-standard nonce lengths. All other users should use
95101
// [NewGCM], which is faster and more resistant to misuse.
96102
func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
97-
return newGCMWithNonceAndTagSize(cipher, size, gcmTagSize)
103+
if fips140only.Enabled {
104+
return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
105+
}
106+
return newGCM(cipher, size, gcmTagSize)
98107
}
99108

100109
// NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois
@@ -106,10 +115,154 @@ func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
106115
// cryptosystem that uses non-standard tag lengths. All other users should use
107116
// [NewGCM], which is more resistant to misuse.
108117
func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) {
109-
return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, tagSize)
118+
if fips140only.Enabled {
119+
return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
120+
}
121+
return newGCM(cipher, gcmStandardNonceSize, tagSize)
122+
}
123+
124+
func newGCM(cipher Block, nonceSize, tagSize int) (AEAD, error) {
125+
c, ok := cipher.(*aes.Block)
126+
if !ok {
127+
if fips140only.Enabled {
128+
return nil, errors.New("crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode")
129+
}
130+
return newGCMFallback(cipher, nonceSize, tagSize)
131+
}
132+
// We don't return gcm.New directly, because it would always return a non-nil
133+
// AEAD interface value with type *gcm.GCM even if the *gcm.GCM is nil.
134+
g, err := gcm.New(c, nonceSize, tagSize)
135+
if err != nil {
136+
return nil, err
137+
}
138+
return g, nil
139+
}
140+
141+
// NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter
142+
// Mode, with randomly-generated nonces. The cipher must have been created by
143+
// [aes.NewCipher].
144+
//
145+
// It generates a random 96-bit nonce, which is prepended to the ciphertext by Seal,
146+
// and is extracted from the ciphertext by Open. The NonceSize of the AEAD is zero,
147+
// while the Overhead is 28 bytes (the combination of nonce size and tag size).
148+
//
149+
// A given key MUST NOT be used to encrypt more than 2^32 messages, to limit the
150+
// risk of a random nonce collision to negligible levels.
151+
func NewGCMWithRandomNonce(cipher Block) (AEAD, error) {
152+
c, ok := cipher.(*aes.Block)
153+
if !ok {
154+
return nil, errors.New("cipher: NewGCMWithRandomNonce requires aes.Block")
155+
}
156+
g, err := gcm.New(c, gcmStandardNonceSize, gcmTagSize)
157+
if err != nil {
158+
return nil, err
159+
}
160+
return gcmWithRandomNonce{g}, nil
161+
}
162+
163+
type gcmWithRandomNonce struct {
164+
*gcm.GCM
165+
}
166+
167+
func (g gcmWithRandomNonce) NonceSize() int {
168+
return 0
169+
}
170+
171+
func (g gcmWithRandomNonce) Overhead() int {
172+
return gcmStandardNonceSize + gcmTagSize
173+
}
174+
175+
func (g gcmWithRandomNonce) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
176+
if len(nonce) != 0 {
177+
panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce")
178+
}
179+
180+
ret, out := sliceForAppend(dst, gcmStandardNonceSize+len(plaintext)+gcmTagSize)
181+
if alias.InexactOverlap(out, plaintext) {
182+
panic("crypto/cipher: invalid buffer overlap of output and input")
183+
}
184+
if alias.AnyOverlap(out, additionalData) {
185+
panic("crypto/cipher: invalid buffer overlap of output and additional data")
186+
}
187+
nonce = out[:gcmStandardNonceSize]
188+
ciphertext := out[gcmStandardNonceSize:]
189+
190+
// The AEAD interface allows using plaintext[:0] or ciphertext[:0] as dst.
191+
//
192+
// This is kind of a problem when trying to prepend or trim a nonce, because the
193+
// actual AES-GCTR blocks end up overlapping but not exactly.
194+
//
195+
// In Open, we write the output *before* the input, so unless we do something
196+
// weird like working through a chunk of block backwards, it works out.
197+
//
198+
// In Seal, we could work through the input backwards or intentionally load
199+
// ahead before writing.
200+
//
201+
// However, the crypto/internal/fips140/aes/gcm APIs also check for exact overlap,
202+
// so for now we just do a memmove if we detect overlap.
203+
//
204+
// ┌───────────────────────────┬ ─ ─
205+
// │PPPPPPPPPPPPPPPPPPPPPPPPPPP│ │
206+
// └▽─────────────────────────▲┴ ─ ─
207+
// ╲ Seal ╲
208+
// ╲ Open ╲
209+
// ┌───▼─────────────────────────△──┐
210+
// │NN|CCCCCCCCCCCCCCCCCCCCCCCCCCC|T│
211+
// └────────────────────────────────┘
212+
//
213+
if alias.AnyOverlap(out, plaintext) {
214+
copy(ciphertext, plaintext)
215+
plaintext = ciphertext[:len(plaintext)]
216+
}
217+
218+
gcm.SealWithRandomNonce(g.GCM, nonce, ciphertext, plaintext, additionalData)
219+
return ret
220+
}
221+
222+
func (g gcmWithRandomNonce) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
223+
if len(nonce) != 0 {
224+
panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce")
225+
}
226+
if len(ciphertext) < gcmStandardNonceSize+gcmTagSize {
227+
return nil, errOpen
228+
}
229+
230+
ret, out := sliceForAppend(dst, len(ciphertext)-gcmStandardNonceSize-gcmTagSize)
231+
if alias.InexactOverlap(out, ciphertext) {
232+
panic("crypto/cipher: invalid buffer overlap of output and input")
233+
}
234+
if alias.AnyOverlap(out, additionalData) {
235+
panic("crypto/cipher: invalid buffer overlap of output and additional data")
236+
}
237+
// See the discussion in Seal. Note that if there is any overlap at this
238+
// point, it's because out = ciphertext, so out must have enough capacity
239+
// even if we sliced the tag off. Also note how [AEAD] specifies that "the
240+
// contents of dst, up to its capacity, may be overwritten".
241+
if alias.AnyOverlap(out, ciphertext) {
242+
nonce = make([]byte, gcmStandardNonceSize)
243+
copy(nonce, ciphertext)
244+
copy(out[:len(ciphertext)], ciphertext[gcmStandardNonceSize:])
245+
ciphertext = out[:len(ciphertext)-gcmStandardNonceSize]
246+
} else {
247+
nonce = ciphertext[:gcmStandardNonceSize]
248+
ciphertext = ciphertext[gcmStandardNonceSize:]
249+
}
250+
251+
_, err := g.GCM.Open(out[:0], nonce, ciphertext, additionalData)
252+
if err != nil {
253+
return nil, err
254+
}
255+
return ret, nil
256+
}
257+
258+
// gcmAble is an interface implemented by ciphers that have a specific optimized
259+
// implementation of GCM. crypto/aes doesn't use this anymore, and we'd like to
260+
// eventually remove it.
261+
type gcmAble interface {
262+
NewGCM(nonceSize, tagSize int) (AEAD, error)
110263
}
111264

112-
func newGCMWithNonceAndTagSize(cipher Block, nonceSize, tagSize int) (AEAD, error) {
265+
func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) {
113266
if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize {
114267
return nil, errors.New("cipher: incorrect tag size given to GCM")
115268
}

src/crypto/cipher/ofb.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
package cipher
88

99
import (
10-
"crypto/internal/alias"
10+
"crypto/internal/fips140/alias"
11+
"crypto/internal/fips140only"
1112
"crypto/subtle"
1213
)
1314

@@ -22,6 +23,10 @@ type ofb struct {
2223
// in output feedback mode. The initialization vector iv's length must be equal
2324
// to b's block size.
2425
func NewOFB(b Block, iv []byte) Stream {
26+
if fips140only.Enabled {
27+
panic("crypto/cipher: use of OFB is not allowed in FIPS 140-only mode")
28+
}
29+
2530
blockSize := b.BlockSize()
2631
if len(iv) != blockSize {
2732
panic("cipher.NewOFB: IV length must equal block size")

src/crypto/des/cipher.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package des
66

77
import (
88
"crypto/cipher"
9-
"crypto/internal/alias"
9+
"crypto/internal/fips140/alias"
10+
"crypto/internal/fips140only"
11+
"errors"
1012
"internal/byteorder"
1113
"strconv"
1214
)
@@ -27,6 +29,10 @@ type desCipher struct {
2729

2830
// NewCipher creates and returns a new [cipher.Block].
2931
func NewCipher(key []byte) (cipher.Block, error) {
32+
if fips140only.Enabled {
33+
return nil, errors.New("crypto/des: use of DES is not allowed in FIPS 140-only mode")
34+
}
35+
3036
if len(key) != 8 {
3137
return nil, KeySizeError(len(key))
3238
}
@@ -71,6 +77,10 @@ type tripleDESCipher struct {
7177

7278
// NewTripleDESCipher creates and returns a new [cipher.Block].
7379
func NewTripleDESCipher(key []byte) (cipher.Block, error) {
80+
if fips140only.Enabled {
81+
return nil, errors.New("crypto/des: use of TripleDES is not allowed in FIPS 140-only mode")
82+
}
83+
7484
if len(key) != 24 {
7585
return nil, KeySizeError(len(key))
7686
}

src/crypto/dsa/dsa.go

+17
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"io"
1919
"math/big"
2020

21+
"crypto/internal/fips140only"
2122
"crypto/internal/randutil"
2223
)
2324

@@ -63,6 +64,10 @@ const numMRTests = 64
6364
// GenerateParameters puts a random, valid set of DSA parameters into params.
6465
// This function can take many seconds, even on fast machines.
6566
func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes) error {
67+
if fips140only.Enabled {
68+
return errors.New("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode")
69+
}
70+
6671
// This function doesn't follow FIPS 186-3 exactly in that it doesn't
6772
// use a verification seed to generate the primes. The verification
6873
// seed doesn't appear to be exported or used by other code and
@@ -157,6 +162,10 @@ GeneratePrimes:
157162
// GenerateKey generates a public&private key pair. The Parameters of the
158163
// [PrivateKey] must already be valid (see [GenerateParameters]).
159164
func GenerateKey(priv *PrivateKey, rand io.Reader) error {
165+
if fips140only.Enabled {
166+
return errors.New("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode")
167+
}
168+
160169
if priv.P == nil || priv.Q == nil || priv.G == nil {
161170
return errors.New("crypto/dsa: parameters not set up before generating key")
162171
}
@@ -203,6 +212,10 @@ func fermatInverse(k, P *big.Int) *big.Int {
203212
// Be aware that calling Sign with an attacker-controlled [PrivateKey] may
204213
// require an arbitrary amount of CPU.
205214
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
215+
if fips140only.Enabled {
216+
return nil, nil, errors.New("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode")
217+
}
218+
206219
randutil.MaybeReadByte(rand)
207220

208221
// FIPS 186-3, section 4.6
@@ -271,6 +284,10 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
271284
// to the byte-length of the subgroup. This function does not perform that
272285
// truncation itself.
273286
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
287+
if fips140only.Enabled {
288+
panic("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode")
289+
}
290+
274291
// FIPS 186-3, section 4.7
275292

276293
if pub.P.Sign() == 0 {

src/crypto/ecdh/x25519.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
package ecdh
66

77
import (
8-
"crypto/internal/edwards25519/field"
8+
"bytes"
9+
"crypto/internal/fips140/edwards25519/field"
10+
"crypto/internal/fips140only"
911
"crypto/internal/randutil"
1012
"errors"
1113
"io"
@@ -33,6 +35,9 @@ func (c *x25519Curve) String() string {
3335
}
3436

3537
func (c *x25519Curve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
38+
if fips140only.Enabled {
39+
return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode")
40+
}
3641
key := make([]byte, x25519PrivateKeySize)
3742
randutil.MaybeReadByte(rand)
3843
if _, err := io.ReadFull(rand, key); err != nil {
@@ -42,6 +47,9 @@ func (c *x25519Curve) GenerateKey(rand io.Reader) (*PrivateKey, error) {
4247
}
4348

4449
func (c *x25519Curve) NewPrivateKey(key []byte) (*PrivateKey, error) {
50+
if fips140only.Enabled {
51+
return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode")
52+
}
4553
if len(key) != x25519PrivateKeySize {
4654
return nil, errors.New("crypto/ecdh: invalid private key size")
4755
}
@@ -65,6 +73,9 @@ func (c *x25519Curve) privateKeyToPublicKey(key *PrivateKey) *PublicKey {
6573
}
6674

6775
func (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) {
76+
if fips140only.Enabled {
77+
return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode")
78+
}
6879
if len(key) != x25519PublicKeySize {
6980
return nil, errors.New("crypto/ecdh: invalid public key")
7081
}

0 commit comments

Comments
 (0)