]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/fips140: add WithoutEnforcement
authorDaniel Morsing <daniel.morsing@gmail.com>
Mon, 24 Nov 2025 13:08:10 +0000 (13:08 +0000)
committerGopher Robot <gobot@golang.org>
Wed, 26 Nov 2025 22:26:06 +0000 (14:26 -0800)
WithoutEnforcement lets programs running under GODEBUG=fips140=only
selectively opt out of strict enforcement. This is especially helpful
for non-critical uses of cryptography routines like SHA-1 for content
addressable storage backends (E.g. git).

Fixes #74630

Change-Id: Iabba1f5eb63498db98047aca45e09c5dccf2fbdf
Reviewed-on: https://go-review.googlesource.com/c/go/+/723720
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
32 files changed:
api/next/74630.txt [new file with mode: 0644]
doc/next/6-stdlib/99-minor/crypto/fips140/74630.md [new file with mode: 0644]
src/crypto/cipher/cbc.go
src/crypto/cipher/cfb.go
src/crypto/cipher/ctr.go
src/crypto/cipher/gcm.go
src/crypto/cipher/ofb.go
src/crypto/des/cipher.go
src/crypto/dsa/dsa.go
src/crypto/ecdh/nist.go
src/crypto/ecdh/x25519.go
src/crypto/ecdsa/ecdsa.go
src/crypto/ecdsa/ecdsa_legacy.go
src/crypto/ed25519/ed25519.go
src/crypto/fips140/enforcement.go [new file with mode: 0644]
src/crypto/fips140/enforcement_test.go [new file with mode: 0644]
src/crypto/fips140/testdata/enforcement_test.go [new file with mode: 0644]
src/crypto/hkdf/hkdf.go
src/crypto/hmac/hmac.go
src/crypto/internal/fips140only/fips140only.go
src/crypto/md5/md5.go
src/crypto/pbkdf2/pbkdf2.go
src/crypto/rand/util.go
src/crypto/rc4/rc4.go
src/crypto/rsa/fips.go
src/crypto/rsa/pkcs1v15.go
src/crypto/rsa/rsa.go
src/crypto/sha1/sha1.go
src/go/build/deps_test.go
src/runtime/fipsbypass.go [new file with mode: 0644]
src/runtime/proc.go
src/runtime/runtime2.go

diff --git a/api/next/74630.txt b/api/next/74630.txt
new file mode 100644 (file)
index 0000000..2a2a82f
--- /dev/null
@@ -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 (file)
index 0000000..6a56aad
--- /dev/null
@@ -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.
index 8e6140629699545b35b0f65f4e87eebac56a43b9..87bafee08ade4f7eb55ee5eaab21366ef6c8df5e 100644 (file)
@@ -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 {
index 7de682fac36d98ad9637131f2ae4dd59c559abb0..8ede955b9171a22cff17da52a832f48b03dc2a12 100644 (file)
@@ -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)
index 49512ca5dd8b8eb37be0f55ef62c67b6daf46eb5..8e63ed7e668a4cbf19482ae21c774a18e58dfa84 100644 (file)
@@ -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 {
index 73493f6cd2311b02fdc5edc813579fe17c6171c7..88892e155551b3ed73bf1913275d2fc7b98e2dd7 100644 (file)
@@ -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)
index 8db5659f7a25ca9c57baa70db267c0d09d5c90d1..ee5dfaf5c0dd62c068afaedb365c791a4dbdacf7 100644 (file)
@@ -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")
        }
 
index 21303b384cf7577b38f6bf9cc1e862024deae884..81ba766b2527d21c645517b9e541901acfe6bade 100644 (file)
@@ -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")
        }
 
index 000becc82dfb793a3310d2df4bb132b723b0f274..ecc4c82bb524fd4acc47076d24b2aa51b8ba2ca6 100644 (file)
@@ -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")
        }
 
index acef8298943c2bb07ed395367e39005efd343d2b..0d58196842a7ae73cf0a4ba6940735c9fdb2e590 100644 (file)
@@ -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")
        }
 
index 81dca5d3a423e056df1ef6712e5115fcd5905c5c..3ad13f3e73a21cb7c455e98b155add9f5e32ea34 100644 (file)
@@ -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 {
index 38f0b564ac98e0a6ecc1fcec9a8c7239ddbd29ed..9d965c4e7b8eebfd43c6d57e41506997823431cd 100644 (file)
@@ -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)
index 74f27b74885e0dff3c7f773965959f60df7ba417..f6b4401bd758f96a69ba0edd024e165c98fb257f 100644 (file)
@@ -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")
        }
 
index 0e26813958707515cf09050bc14a3f7c709aec23..26b4882b13280e8521407b33453899ac07e67678 100644 (file)
@@ -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 (file)
index 0000000..5dae32a
--- /dev/null
@@ -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 (file)
index 0000000..708c7f7
--- /dev/null
@@ -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 (file)
index 0000000..a8fde3b
--- /dev/null
@@ -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")
+       }
+}
index 6b02522866d57fb66bb9755c976345082b12dbc5..88439922a5032e3ca25ee4dc352732b4a243c66a 100644 (file)
@@ -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 {
index 554c8c9b78940bef180649b8e5f18278f8c788fc..e7976e25193dfe3d57718966984fa62de9293153 100644 (file)
@@ -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")
                }
index 147877a34fcb5fab8b5db371f046d0e362609395..1b0a4be6ba58978e9bc547e21facb99d05dca9b7 100644 (file)
@@ -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) {
index 9274f89d3e2c1559b5c282c4881b3d2947f50b9e..f1287887ff5e250bd9980f146d436e1384e181a4 100644 (file)
@@ -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")
        }
 
index 01fd12e40e341ef9a3475329f1baa0973a34be64..0bc14be888d9d6335f8230087473c938b3593817 100644 (file)
@@ -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")
                }
index 10c2284a9b5fc430282eae659afd150fcf80e69f..8c9285197511ce65b15fe302dc6234daae148ec5 100644 (file)
@@ -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 {
index eebc1c04cbfedb52b0f54321df68cf948866ab72..c4d2b0d382e7fc6f095cf986c51917a67096a160 100644 (file)
@@ -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)
index ba92659193fb978dd85808285f7a6582d9cbf3a0..75aa3d3d725757c0bef36881a7f5ab711a9e435c 100644 (file)
@@ -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 {
index 76853a94453541343fc314f28721bd8a595fdc56..caf68957e2d35a95dc54d29f4eae106702e560e0 100644 (file)
@@ -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")
        }
 
index 7e1bf3d7a56bd98d0f95fe7d0e2bc6770c1d3801..5680d8b54159dbdccacbed881bb2dc8d140121fd 100644 (file)
@@ -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")
        }
 
index 3acc5b11fb789e32756b6aaad48431450560cecf..46e47df1d32cf233e34f58f788736dbb1469ebd8 100644 (file)
@@ -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
index 295c69425ee52a58befa428597e62e9490d6a22c..5466f025e1b11d4c021b645954b6f37453dbea98 100644 (file)
@@ -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 (file)
index 0000000..12df9c6
--- /dev/null
@@ -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
+}
index 58fb4bd6810606e871969db46c8361ec7c85d7da..3b98be107483112213dfbe00b8e29ad20e55e706 100644 (file)
@@ -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)
index 3175ee55f543681e4c48c7009d011934d2b868dc..58eaf80237234b41e91a50cd3f956ec502ef58c8 100644 (file)
@@ -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