diff --git a/api/next/74630.txt b/api/next/74630.txt new file mode 100644 index 00000000000..2a2a82fc43b --- /dev/null +++ b/api/next/74630.txt @@ -0,0 +1,2 @@ +pkg crypto/fips140, func Enforced() bool #74630 +pkg crypto/fips140, func WithoutEnforcement(func()) #74630 diff --git a/doc/next/6-stdlib/99-minor/crypto/fips140/74630.md b/doc/next/6-stdlib/99-minor/crypto/fips140/74630.md new file mode 100644 index 00000000000..6a56aad3d15 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/crypto/fips140/74630.md @@ -0,0 +1,2 @@ +The new [WithoutEnforcement] and [Enforced] functions now allow running +in `GODEBUG=fips140=only` mode while selectively disabling the strict FIPS 140-3 checks. diff --git a/src/crypto/cipher/cbc.go b/src/crypto/cipher/cbc.go index 8e614062969..87bafee08ad 100644 --- a/src/crypto/cipher/cbc.go +++ b/src/crypto/cipher/cbc.go @@ -54,7 +54,7 @@ func NewCBCEncrypter(b Block, iv []byte) BlockMode { if b, ok := b.(*aes.Block); ok { return aes.NewCBCEncrypter(b, [16]byte(iv)) } - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/cipher: use of CBC with non-AES ciphers is not allowed in FIPS 140-only mode") } if cbc, ok := b.(cbcEncAble); ok { @@ -133,7 +133,7 @@ func NewCBCDecrypter(b Block, iv []byte) BlockMode { if b, ok := b.(*aes.Block); ok { return aes.NewCBCDecrypter(b, [16]byte(iv)) } - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/cipher: use of CBC with non-AES ciphers is not allowed in FIPS 140-only mode") } if cbc, ok := b.(cbcDecAble); ok { diff --git a/src/crypto/cipher/cfb.go b/src/crypto/cipher/cfb.go index 7de682fac36..8ede955b917 100644 --- a/src/crypto/cipher/cfb.go +++ b/src/crypto/cipher/cfb.go @@ -61,7 +61,7 @@ func (x *cfb) XORKeyStream(dst, src []byte) { // CFB is also unoptimized and not validated as part of the FIPS 140-3 module. // If an unauthenticated [Stream] mode is required, use [NewCTR] instead. func NewCFBEncrypter(block Block, iv []byte) Stream { - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/cipher: use of CFB is not allowed in FIPS 140-only mode") } return newCFB(block, iv, false) @@ -77,7 +77,7 @@ func NewCFBEncrypter(block Block, iv []byte) Stream { // CFB is also unoptimized and not validated as part of the FIPS 140-3 module. // If an unauthenticated [Stream] mode is required, use [NewCTR] instead. func NewCFBDecrypter(block Block, iv []byte) Stream { - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/cipher: use of CFB is not allowed in FIPS 140-only mode") } return newCFB(block, iv, true) diff --git a/src/crypto/cipher/ctr.go b/src/crypto/cipher/ctr.go index 49512ca5dd8..8e63ed7e668 100644 --- a/src/crypto/cipher/ctr.go +++ b/src/crypto/cipher/ctr.go @@ -42,7 +42,7 @@ func NewCTR(block Block, iv []byte) Stream { if block, ok := block.(*aes.Block); ok { return aesCtrWrapper{aes.NewCTR(block, iv)} } - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/cipher: use of CTR with non-AES ciphers is not allowed in FIPS 140-only mode") } if ctr, ok := block.(ctrAble); ok { diff --git a/src/crypto/cipher/gcm.go b/src/crypto/cipher/gcm.go index 73493f6cd23..88892e15555 100644 --- a/src/crypto/cipher/gcm.go +++ b/src/crypto/cipher/gcm.go @@ -28,7 +28,7 @@ const ( // An exception is when the underlying [Block] was created by aes.NewCipher // on systems with hardware support for AES. See the [crypto/aes] package documentation for details. func NewGCM(cipher Block) (AEAD, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce") } return newGCM(cipher, gcmStandardNonceSize, gcmTagSize) @@ -42,7 +42,7 @@ func NewGCM(cipher Block) (AEAD, error) { // cryptosystem that uses non-standard nonce lengths. All other users should use // [NewGCM], which is faster and more resistant to misuse. func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce") } return newGCM(cipher, size, gcmTagSize) @@ -57,7 +57,7 @@ func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) { // cryptosystem that uses non-standard tag lengths. All other users should use // [NewGCM], which is more resistant to misuse. func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce") } return newGCM(cipher, gcmStandardNonceSize, tagSize) @@ -66,7 +66,7 @@ func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) { func newGCM(cipher Block, nonceSize, tagSize int) (AEAD, error) { c, ok := cipher.(*aes.Block) if !ok { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode") } return newGCMFallback(cipher, nonceSize, tagSize) diff --git a/src/crypto/cipher/ofb.go b/src/crypto/cipher/ofb.go index 8db5659f7a2..ee5dfaf5c0d 100644 --- a/src/crypto/cipher/ofb.go +++ b/src/crypto/cipher/ofb.go @@ -29,7 +29,7 @@ type ofb struct { // OFB is also unoptimized and not validated as part of the FIPS 140-3 module. // If an unauthenticated [Stream] mode is required, use [NewCTR] instead. func NewOFB(b Block, iv []byte) Stream { - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/cipher: use of OFB is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/des/cipher.go b/src/crypto/des/cipher.go index 21303b384cf..81ba766b252 100644 --- a/src/crypto/des/cipher.go +++ b/src/crypto/des/cipher.go @@ -29,7 +29,7 @@ type desCipher struct { // NewCipher creates and returns a new [cipher.Block]. func NewCipher(key []byte) (cipher.Block, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/des: use of DES is not allowed in FIPS 140-only mode") } @@ -77,7 +77,7 @@ type tripleDESCipher struct { // NewTripleDESCipher creates and returns a new [cipher.Block]. func NewTripleDESCipher(key []byte) (cipher.Block, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/des: use of TripleDES is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/dsa/dsa.go b/src/crypto/dsa/dsa.go index 000becc82df..ecc4c82bb52 100644 --- a/src/crypto/dsa/dsa.go +++ b/src/crypto/dsa/dsa.go @@ -64,7 +64,7 @@ const numMRTests = 64 // GenerateParameters puts a random, valid set of DSA parameters into params. // This function can take many seconds, even on fast machines. func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes) error { - if fips140only.Enabled { + if fips140only.Enforced() { return errors.New("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode") } @@ -162,7 +162,7 @@ GeneratePrimes: // GenerateKey generates a public&private key pair. The Parameters of the // [PrivateKey] must already be valid (see [GenerateParameters]). func GenerateKey(priv *PrivateKey, rand io.Reader) error { - if fips140only.Enabled { + if fips140only.Enforced() { return errors.New("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode") } @@ -212,7 +212,7 @@ func fermatInverse(k, P *big.Int) *big.Int { // Be aware that calling Sign with an attacker-controlled [PrivateKey] may // require an arbitrary amount of CPU. func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, nil, errors.New("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode") } @@ -284,7 +284,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err // to the byte-length of the subgroup. This function does not perform that // truncation itself. func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/ecdh/nist.go b/src/crypto/ecdh/nist.go index acef8298943..0d58196842a 100644 --- a/src/crypto/ecdh/nist.go +++ b/src/crypto/ecdh/nist.go @@ -44,7 +44,7 @@ func (c *nistCurve) GenerateKey(rand io.Reader) (*PrivateKey, error) { return k, nil } - if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + if fips140only.Enforced() && !fips140only.ApprovedRandomReader(rand) { return nil, errors.New("crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-only mode") } diff --git a/src/crypto/ecdh/x25519.go b/src/crypto/ecdh/x25519.go index 81dca5d3a42..3ad13f3e73a 100644 --- a/src/crypto/ecdh/x25519.go +++ b/src/crypto/ecdh/x25519.go @@ -35,7 +35,7 @@ func (c *x25519Curve) String() string { } func (c *x25519Curve) GenerateKey(rand io.Reader) (*PrivateKey, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode") } key := make([]byte, x25519PrivateKeySize) @@ -47,7 +47,7 @@ func (c *x25519Curve) GenerateKey(rand io.Reader) (*PrivateKey, error) { } func (c *x25519Curve) NewPrivateKey(key []byte) (*PrivateKey, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode") } if len(key) != x25519PrivateKeySize { @@ -67,7 +67,7 @@ func (c *x25519Curve) NewPrivateKey(key []byte) (*PrivateKey, error) { } func (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode") } if len(key) != x25519PublicKeySize { diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index 38f0b564ac9..9d965c4e7b8 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -358,7 +358,7 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) { } func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], rand io.Reader) (*PrivateKey, error) { - if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + if fips140only.Enforced() && !fips140only.ApprovedRandomReader(rand) { return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") } privateKey, err := ecdsa.GenerateKey(c, rand) @@ -403,7 +403,7 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) { } func signFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey, rand io.Reader, hash []byte) ([]byte, error) { - if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + if fips140only.Enforced() && !fips140only.ApprovedRandomReader(rand) { return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") } k, err := privateKeyToFIPS(c, priv) @@ -448,7 +448,7 @@ func signFIPSDeterministic[P ecdsa.Point[P]](c *ecdsa.Curve[P], hashFunc crypto. return nil, err } h := fips140hash.UnwrapNew(hashFunc.New) - if fips140only.Enabled && !fips140only.ApprovedHash(h()) { + if fips140only.Enforced() && !fips140only.ApprovedHash(h()) { return nil, errors.New("crypto/ecdsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } sig, err := ecdsa.SignDeterministic(c, h, k, hash) diff --git a/src/crypto/ecdsa/ecdsa_legacy.go b/src/crypto/ecdsa/ecdsa_legacy.go index 74f27b74885..f6b4401bd75 100644 --- a/src/crypto/ecdsa/ecdsa_legacy.go +++ b/src/crypto/ecdsa/ecdsa_legacy.go @@ -20,7 +20,7 @@ import ( // deprecated custom curves. func generateLegacy(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/ecdsa: use of custom curves is not allowed in FIPS 140-only mode") } @@ -81,7 +81,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err } func signLegacy(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/ecdsa: use of custom curves is not allowed in FIPS 140-only mode") } @@ -153,7 +153,7 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { } func verifyLegacy(pub *PublicKey, hash []byte, sig []byte) bool { - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/ecdsa: use of custom curves is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/ed25519/ed25519.go b/src/crypto/ed25519/ed25519.go index 0e268139587..26b4882b132 100644 --- a/src/crypto/ed25519/ed25519.go +++ b/src/crypto/ed25519/ed25519.go @@ -110,7 +110,7 @@ func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOp case hash == crypto.SHA512: // Ed25519ph return ed25519.SignPH(k, message, context) case hash == crypto.Hash(0) && context != "": // Ed25519ctx - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/ed25519: use of Ed25519ctx is not allowed in FIPS 140-only mode") } return ed25519.SignCtx(k, message, context) @@ -230,7 +230,7 @@ func VerifyWithOptions(publicKey PublicKey, message, sig []byte, opts *Options) case opts.Hash == crypto.SHA512: // Ed25519ph return ed25519.VerifyPH(k, message, sig, opts.Context) case opts.Hash == crypto.Hash(0) && opts.Context != "": // Ed25519ctx - if fips140only.Enabled { + if fips140only.Enforced() { return errors.New("crypto/ed25519: use of Ed25519ctx is not allowed in FIPS 140-only mode") } return ed25519.VerifyCtx(k, message, sig, opts.Context) diff --git a/src/crypto/fips140/enforcement.go b/src/crypto/fips140/enforcement.go new file mode 100644 index 00000000000..5dae32a3a79 --- /dev/null +++ b/src/crypto/fips140/enforcement.go @@ -0,0 +1,47 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fips140 + +import ( + "internal/godebug" + _ "unsafe" // for linkname +) + +// WithoutEnforcement disables strict FIPS 140-3 enforcement while executing f. +// Calling WithoutEnforcement without strict enforcement enabled +// (GODEBUG=fips140=only is not set or already inside of a call to +// WithoutEnforcement) is a no-op. +// +// WithoutEnforcement is inherited by any goroutines spawned while executing f. +// +// As this disables enforcement, it should be applied carefully to tightly +// scoped functions. +func WithoutEnforcement(f func()) { + if !Enabled() || !Enforced() { + f() + return + } + setBypass() + defer unsetBypass() + f() +} + +var enabled = godebug.New("fips140").Value() == "only" + +// Enforced indicates if strict FIPS 140-3 enforcement is enabled. Strict +// enforcement is enabled when a program is run with GODEBUG=fips140=only and +// enforcement has not been disabled by a call to [WithoutEnforcement]. +func Enforced() bool { + return enabled && !isBypassed() +} + +//go:linkname setBypass +func setBypass() + +//go:linkname isBypassed +func isBypassed() bool + +//go:linkname unsetBypass +func unsetBypass() diff --git a/src/crypto/fips140/enforcement_test.go b/src/crypto/fips140/enforcement_test.go new file mode 100644 index 00000000000..708c7f7a15e --- /dev/null +++ b/src/crypto/fips140/enforcement_test.go @@ -0,0 +1,46 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fips140_test + +import ( + "crypto/internal/cryptotest" + "internal/testenv" + "path/filepath" + "strings" + "testing" +) + +func TestWithoutEnforcement(t *testing.T) { + testenv.MustHaveExec(t) + testenv.MustHaveGoBuild(t) + cryptotest.MustSupportFIPS140(t) + + tool, _ := testenv.GoTool() + tmpdir := t.TempDir() + binFile := filepath.Join(tmpdir, "fips140.test") + cmd := testenv.Command(t, tool, "test", "-c", "-o", binFile, "./testdata") + out, err := cmd.CombinedOutput() + if err != nil { + t.Log(string(out)) + t.Errorf("Could not build enforcement tests") + } + cmd = testenv.Command(t, binFile, "-test.list", ".") + list, err := cmd.CombinedOutput() + if err != nil { + t.Log(string(out)) + t.Errorf("Could not get enforcement test list") + } + for test := range strings.Lines(string(list)) { + test = strings.TrimSpace(test) + t.Run(test, func(t *testing.T) { + cmd = testenv.Command(t, binFile, "-test.run", "^"+test+"$") + cmd.Env = append(cmd.Env, "GODEBUG=fips140=only") + out, err := cmd.CombinedOutput() + if err != nil { + t.Error(string(out)) + } + }) + } +} diff --git a/src/crypto/fips140/testdata/enforcement_test.go b/src/crypto/fips140/testdata/enforcement_test.go new file mode 100644 index 00000000000..a8fde3b5bc6 --- /dev/null +++ b/src/crypto/fips140/testdata/enforcement_test.go @@ -0,0 +1,65 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fips140_test + +import ( + "crypto/des" + "crypto/fips140" + "testing" +) + +func expectAllowed(t *testing.T, why string, expected bool) { + t.Helper() + result := isAllowed() + if result != expected { + t.Fatalf("%v: expected: %v, got: %v", why, expected, result) + } +} + +func isAllowed() bool { + _, err := des.NewCipher(make([]byte, 8)) + return err == nil +} + +func TestDisabled(t *testing.T) { + expectAllowed(t, "before enforcement disabled", false) + fips140.WithoutEnforcement(func() { + expectAllowed(t, "inside WithoutEnforcement", true) + }) + // make sure that bypass doesn't live on after returning + expectAllowed(t, "after WithoutEnforcement", false) +} + +func TestNested(t *testing.T) { + expectAllowed(t, "before enforcement bypass", false) + fips140.WithoutEnforcement(func() { + fips140.WithoutEnforcement(func() { + expectAllowed(t, "inside nested WithoutEnforcement", true) + }) + expectAllowed(t, "inside nested WithoutEnforcement", true) + }) + expectAllowed(t, "after enforcement bypass", false) +} + +func TestGoroutineInherit(t *testing.T) { + ch := make(chan bool, 2) + expectAllowed(t, "before enforcement bypass", false) + fips140.WithoutEnforcement(func() { + go func() { + ch <- isAllowed() + }() + }) + allowed := <-ch + if !allowed { + t.Fatal("goroutine didn't inherit enforcement bypass") + } + go func() { + ch <- isAllowed() + }() + allowed = <-ch + if allowed { + t.Fatal("goroutine inherited bypass after WithoutEnforcement return") + } +} diff --git a/src/crypto/hkdf/hkdf.go b/src/crypto/hkdf/hkdf.go index 6b02522866d..88439922a50 100644 --- a/src/crypto/hkdf/hkdf.go +++ b/src/crypto/hkdf/hkdf.go @@ -71,7 +71,7 @@ func Key[Hash hash.Hash](h func() Hash, secret, salt []byte, info string, keyLen } func checkFIPS140Only[Hash hash.Hash](h func() Hash, key []byte) error { - if !fips140only.Enabled { + if !fips140only.Enforced() { return nil } if len(key) < 112/8 { diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go index 554c8c9b789..e7976e25193 100644 --- a/src/crypto/hmac/hmac.go +++ b/src/crypto/hmac/hmac.go @@ -45,7 +45,7 @@ func New(h func() hash.Hash, key []byte) hash.Hash { // BoringCrypto did not recognize h, so fall through to standard Go code. } h = fips140hash.UnwrapNew(h) - if fips140only.Enabled { + if fips140only.Enforced() { if len(key) < 112/8 { panic("crypto/hmac: use of keys shorter than 112 bits is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/internal/fips140only/fips140only.go b/src/crypto/internal/fips140only/fips140only.go index 147877a34fc..1b0a4be6ba5 100644 --- a/src/crypto/internal/fips140only/fips140only.go +++ b/src/crypto/internal/fips140only/fips140only.go @@ -5,18 +5,20 @@ package fips140only import ( + "crypto/fips140" "crypto/internal/fips140/drbg" "crypto/internal/fips140/sha256" "crypto/internal/fips140/sha3" "crypto/internal/fips140/sha512" "hash" - "internal/godebug" "io" ) -// Enabled reports whether FIPS 140-only mode is enabled, in which non-approved +// Enforced reports whether FIPS 140-only mode is enabled and enforced, in which non-approved // cryptography returns an error or panics. -var Enabled = godebug.New("fips140").Value() == "only" +func Enforced() bool { + return fips140.Enforced() +} func ApprovedHash(h hash.Hash) bool { switch h.(type) { diff --git a/src/crypto/md5/md5.go b/src/crypto/md5/md5.go index 9274f89d3e2..f1287887ff5 100644 --- a/src/crypto/md5/md5.go +++ b/src/crypto/md5/md5.go @@ -124,7 +124,7 @@ func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { - if fips140only.Enabled { + if fips140only.Enforced() { return 0, errors.New("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") } // Note that we currently call block or blockGeneric @@ -173,7 +173,7 @@ func (d *digest) Sum(in []byte) []byte { } func (d *digest) checkSum() [Size]byte { - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/pbkdf2/pbkdf2.go b/src/crypto/pbkdf2/pbkdf2.go index 01fd12e40e3..0bc14be888d 100644 --- a/src/crypto/pbkdf2/pbkdf2.go +++ b/src/crypto/pbkdf2/pbkdf2.go @@ -39,7 +39,7 @@ import ( // Setting keyLength to a value outside of this range will result in an error. func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) { fh := fips140hash.UnwrapNew(h) - if fips140only.Enabled { + if fips140only.Enforced() { if keyLength < 112/8 { return nil, errors.New("crypto/pbkdf2: use of keys shorter than 112 bits is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/rand/util.go b/src/crypto/rand/util.go index 10c2284a9b5..8c928519751 100644 --- a/src/crypto/rand/util.go +++ b/src/crypto/rand/util.go @@ -15,7 +15,7 @@ import ( // Prime returns a number of the given bit length that is prime with high probability. // Prime will return error for any error returned by rand.Read or if bits < 2. func Prime(rand io.Reader, bits int) (*big.Int, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/rand: use of Prime is not allowed in FIPS 140-only mode") } if bits < 2 { diff --git a/src/crypto/rc4/rc4.go b/src/crypto/rc4/rc4.go index eebc1c04cbf..c4d2b0d382e 100644 --- a/src/crypto/rc4/rc4.go +++ b/src/crypto/rc4/rc4.go @@ -31,7 +31,7 @@ func (k KeySizeError) Error() string { // NewCipher creates and returns a new [Cipher]. The key argument should be the // RC4 key, at least 1 byte and at most 256 bytes. func NewCipher(key []byte) (*Cipher, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/rc4: use of RC4 is not allowed in FIPS 140-only mode") } k := len(key) diff --git a/src/crypto/rsa/fips.go b/src/crypto/rsa/fips.go index ba92659193f..75aa3d3d725 100644 --- a/src/crypto/rsa/fips.go +++ b/src/crypto/rsa/fips.go @@ -84,10 +84,10 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, if err := checkFIPS140OnlyPrivateKey(priv); err != nil { return nil, err } - if fips140only.Enabled && !fips140only.ApprovedHash(h) { + if fips140only.Enforced() && !fips140only.ApprovedHash(h) { return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } - if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + if fips140only.Enforced() && !fips140only.ApprovedRandomReader(rand) { return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") } @@ -97,7 +97,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, } saltLength := opts.saltLength() - if fips140only.Enabled && saltLength > h.Size() { + if fips140only.Enforced() && saltLength > h.Size() { return nil, errors.New("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode") } switch saltLength { @@ -149,7 +149,7 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts if err := checkFIPS140OnlyPublicKey(pub); err != nil { return err } - if fips140only.Enabled && !fips140only.ApprovedHash(h) { + if fips140only.Enforced() && !fips140only.ApprovedHash(h) { return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } @@ -159,7 +159,7 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts } saltLength := opts.saltLength() - if fips140only.Enabled && saltLength > h.Size() { + if fips140only.Enforced() && saltLength > h.Size() { return errors.New("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode") } switch saltLength { @@ -234,10 +234,10 @@ func encryptOAEP(hash hash.Hash, mgfHash hash.Hash, random io.Reader, pub *Publi if err := checkFIPS140OnlyPublicKey(pub); err != nil { return nil, err } - if fips140only.Enabled && !fips140only.ApprovedHash(hash) { + if fips140only.Enforced() && !fips140only.ApprovedHash(hash) { return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } - if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) { + if fips140only.Enforced() && !fips140only.ApprovedRandomReader(random) { return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") } @@ -291,7 +291,7 @@ func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l if err := checkFIPS140OnlyPrivateKey(priv); err != nil { return nil, err } - if fips140only.Enabled { + if fips140only.Enforced() { if !fips140only.ApprovedHash(hash) || !fips140only.ApprovedHash(mgfHash) { return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } @@ -341,7 +341,7 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ if err := checkFIPS140OnlyPrivateKey(priv); err != nil { return nil, err } - if fips140only.Enabled && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { + if fips140only.Enforced() && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } @@ -387,7 +387,7 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) if err := checkFIPS140OnlyPublicKey(pub); err != nil { return err } - if fips140only.Enabled && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { + if fips140only.Enforced() && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } @@ -415,7 +415,7 @@ func fipsError2[T any](x T, err error) (T, error) { } func checkFIPS140OnlyPublicKey(pub *PublicKey) error { - if !fips140only.Enabled { + if !fips140only.Enforced() { return nil } if pub.N == nil { @@ -437,7 +437,7 @@ func checkFIPS140OnlyPublicKey(pub *PublicKey) error { } func checkFIPS140OnlyPrivateKey(priv *PrivateKey) error { - if !fips140only.Enabled { + if !fips140only.Enforced() { return nil } if err := checkFIPS140OnlyPublicKey(&priv.PublicKey); err != nil { diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go index 76853a94453..caf68957e2d 100644 --- a/src/crypto/rsa/pkcs1v15.go +++ b/src/crypto/rsa/pkcs1v15.go @@ -49,7 +49,7 @@ type PKCS1v15DecryptOptions struct { // // [draft-irtf-cfrg-rsa-guidance-05]: https://www.ietf.org/archive/id/draft-irtf-cfrg-rsa-guidance-05.html#name-rationale func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, error) { - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/rsa: use of PKCS#1 v1.5 encryption is not allowed in FIPS 140-only mode") } @@ -212,7 +212,7 @@ func DecryptPKCS1v15SessionKey(random io.Reader, priv *PrivateKey, ciphertext [] // access patterns. If the plaintext was valid then index contains the index of // the original message in em, to allow constant time padding removal. func decryptPKCS1v15(priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) { - if fips140only.Enabled { + if fips140only.Enforced() { return 0, nil, 0, errors.New("crypto/rsa: use of PKCS#1 v1.5 encryption is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index 7e1bf3d7a56..5680d8b5415 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -350,13 +350,13 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { return key, nil } - if fips140only.Enabled && bits < 2048 { + if fips140only.Enforced() && bits < 2048 { return nil, errors.New("crypto/rsa: use of keys smaller than 2048 bits is not allowed in FIPS 140-only mode") } - if fips140only.Enabled && bits%2 == 1 { + if fips140only.Enforced() && bits%2 == 1 { return nil, errors.New("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode") } - if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) { + if fips140only.Enforced() && !fips140only.ApprovedRandomReader(random) { return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") } @@ -424,7 +424,7 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey if nprimes == 2 { return GenerateKey(random, bits) } - if fips140only.Enabled { + if fips140only.Enforced() { return nil, errors.New("crypto/rsa: multi-prime RSA is not allowed in FIPS 140-only mode") } diff --git a/src/crypto/sha1/sha1.go b/src/crypto/sha1/sha1.go index 3acc5b11fb7..46e47df1d32 100644 --- a/src/crypto/sha1/sha1.go +++ b/src/crypto/sha1/sha1.go @@ -126,7 +126,7 @@ func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { - if fips140only.Enabled { + if fips140only.Enforced() { return 0, errors.New("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") } boring.Unreachable() @@ -161,7 +161,7 @@ func (d *digest) Sum(in []byte) []byte { } func (d *digest) checkSum() [Size]byte { - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") } @@ -205,7 +205,7 @@ func (d *digest) ConstantTimeSum(in []byte) []byte { } func (d *digest) constSum() [Size]byte { - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") } @@ -274,7 +274,7 @@ func Sum(data []byte) [Size]byte { if boring.Enabled { return boring.SHA1(data) } - if fips140only.Enabled { + if fips140only.Enforced() { panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") } var d digest diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 295c69425ee..5466f025e1b 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -537,7 +537,7 @@ var depsRules = ` < crypto/internal/fips140/edwards25519 < crypto/internal/fips140/ed25519 < crypto/internal/fips140/rsa - < FIPS < crypto/fips140; + < crypto/fips140 < FIPS; crypto !< FIPS; diff --git a/src/runtime/fipsbypass.go b/src/runtime/fipsbypass.go new file mode 100644 index 00000000000..12df9c6b6a2 --- /dev/null +++ b/src/runtime/fipsbypass.go @@ -0,0 +1,22 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import _ "unsafe" + +//go:linkname fips140_setBypass crypto/fips140.setBypass +func fips140_setBypass() { + getg().fipsOnlyBypass = true +} + +//go:linkname fips140_unsetBypass crypto/fips140.unsetBypass +func fips140_unsetBypass() { + getg().fipsOnlyBypass = false +} + +//go:linkname fips140_isBypassed crypto/fips140.isBypassed +func fips140_isBypassed() bool { + return getg().fipsOnlyBypass +} diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 58fb4bd6810..3b98be10748 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -4481,6 +4481,7 @@ func gdestroy(gp *g) { gp.labels = nil gp.timer = nil gp.bubble = nil + gp.fipsOnlyBypass = false if gcBlackenEnabled != 0 && gp.gcAssistBytes > 0 { // Flush assist credit to the global pool. This gives @@ -5325,6 +5326,9 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr, parked bool, waitreaso traceRelease(trace) } + // fips140 bubble + newg.fipsOnlyBypass = callergp.fipsOnlyBypass + // Set up race context. if raceenabled { newg.racectx = racegostart(callerpc) diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 3175ee55f54..58eaf802372 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -545,6 +545,7 @@ type g struct { runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking lockedm muintptr fipsIndicator uint8 + fipsOnlyBypass bool syncSafePoint bool // set if g is stopped at a synchronous safe point. runningCleanups atomic.Bool sig uint32