diff --git a/src/crypto/internal/fips140only/fips140only_test.go b/src/crypto/internal/fips140only/fips140only_test.go
new file mode 100644
index 0000000..ff5b4f5
--- /dev/null
+++ b/src/crypto/internal/fips140only/fips140only_test.go
@@ -0,0 +1,414 @@
+// Copyright 2024 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 fips140only_test
+
+import (
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/dsa"
+ "crypto/ecdh"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/hkdf"
+ "crypto/hmac"
+ "crypto/hpke"
+ "crypto/internal/fips140"
+ "crypto/internal/fips140only"
+ "crypto/md5"
+ "crypto/mlkem"
+ "crypto/mlkem/mlkemtest"
+ "crypto/pbkdf2"
+ "crypto/rand"
+ "crypto/rc4"
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/sha256"
+ _ "crypto/sha3"
+ _ "crypto/sha512"
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+ "internal/godebug"
+ "internal/testenv"
+ "io"
+ "math/big"
+ "os"
+ "strings"
+ "testing"
+
+ "golang.org/x/crypto/chacha20poly1305"
+)
+
+func TestFIPS140Only(t *testing.T) {
+ if !fips140only.Enforced() {
+ if err := fips140.Supported(); err != nil {
+ t.Skipf("skipping: %v", err)
+ }
+
+ cmd := testenv.Command(t, os.Args[0], "-test.v")
+ if !testing.Short() {
+ // In long mode we run "go test" with a clean GOCACHE instead of
+ // os.Args[0] to make sure that compilation also works under
+ // GODEBUG=fips140=only. See #70878.
+ cmd = testenv.Command(t, testenv.GoToolPath(t), "test", "-v", "crypto/internal/fips140only")
+ cmd.Env = append(cmd.Env, "GOCACHE="+t.TempDir())
+ }
+ cmd.Env = append(cmd.Environ(), "GODEBUG=fips140=only")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("GODEBUG=fips140=only %v failed: %v\n%s", cmd.Args, err, out)
+ }
+ t.Logf("exec'ed GODEBUG=fips140=only and succeeded:\n%s", out)
+ return
+ }
+ t.Run("cryptocustomrand=0", func(t *testing.T) {
+ t.Setenv("GODEBUG", os.Getenv("GODEBUG")+",cryptocustomrand=0")
+ testFIPS140Only(t)
+ })
+ t.Run("cryptocustomrand=1", func(t *testing.T) {
+ t.Setenv("GODEBUG", os.Getenv("GODEBUG")+",cryptocustomrand=1")
+ testFIPS140Only(t)
+ })
+}
+
+func testFIPS140Only(t *testing.T) {
+ if !fips140only.Enforced() {
+ t.Fatal("FIPS 140-only mode not enforced")
+ }
+ t.Logf("GODEBUG=fips140=only enabled")
+ fips140.ResetServiceIndicator()
+
+ aesBlock, err := aes.NewCipher(make([]byte, 16))
+ if err != nil {
+ t.Fatal(err)
+ }
+ notAESBlock := blockWrap{aesBlock}
+ iv := make([]byte, aes.BlockSize)
+
+ cipher.NewCBCEncrypter(aesBlock, iv)
+ expectPanic(t, func() { cipher.NewCBCEncrypter(notAESBlock, iv) })
+ cipher.NewCBCDecrypter(aesBlock, iv)
+ expectPanic(t, func() { cipher.NewCBCDecrypter(notAESBlock, iv) })
+
+ expectPanic(t, func() { cipher.NewCFBEncrypter(aesBlock, iv) })
+ expectPanic(t, func() { cipher.NewCFBDecrypter(aesBlock, iv) })
+
+ cipher.NewCTR(aesBlock, iv)
+ expectPanic(t, func() { cipher.NewCTR(notAESBlock, iv) })
+
+ expectPanic(t, func() { cipher.NewOFB(aesBlock, iv) })
+
+ expectErr(t, errRet2(cipher.NewGCM(aesBlock)))
+ expectErr(t, errRet2(cipher.NewGCMWithNonceSize(aesBlock, 12)))
+ expectErr(t, errRet2(cipher.NewGCMWithTagSize(aesBlock, 12)))
+ expectNoErr(t, errRet2(cipher.NewGCMWithRandomNonce(aesBlock)))
+
+ expectErr(t, errRet2(des.NewCipher(make([]byte, 8))))
+ expectErr(t, errRet2(des.NewTripleDESCipher(make([]byte, 24))))
+
+ expectErr(t, errRet2(rc4.NewCipher(make([]byte, 16))))
+
+ expectErr(t, errRet2(chacha20poly1305.New(make([]byte, chacha20poly1305.KeySize))))
+ expectErr(t, errRet2(chacha20poly1305.NewX(make([]byte, chacha20poly1305.KeySize))))
+
+ expectPanic(t, func() { md5.New().Sum(nil) })
+ expectErr(t, errRet2(md5.New().Write(make([]byte, 16))))
+ expectPanic(t, func() { md5.Sum([]byte("foo")) })
+
+ expectPanic(t, func() { sha1.New().Sum(nil) })
+ expectErr(t, errRet2(sha1.New().Write(make([]byte, 16))))
+ expectPanic(t, func() { sha1.Sum([]byte("foo")) })
+
+ withApprovedHash(func(h crypto.Hash) { h.New().Sum(nil) })
+ withNonApprovedHash(func(h crypto.Hash) { expectPanic(t, func() { h.New().Sum(nil) }) })
+
+ expectErr(t, errRet2(pbkdf2.Key(sha256.New, "password", make([]byte, 16), 1, 10)))
+ expectErr(t, errRet2(pbkdf2.Key(sha256.New, "password", make([]byte, 10), 1, 14)))
+ withNonApprovedHash(func(h crypto.Hash) {
+ expectErr(t, errRet2(pbkdf2.Key(h.New, "password", make([]byte, 16), 1, 14)))
+ })
+ withApprovedHash(func(h crypto.Hash) {
+ expectNoErr(t, errRet2(pbkdf2.Key(h.New, "password", make([]byte, 16), 1, 14)))
+ })
+
+ expectPanic(t, func() { hmac.New(sha256.New, make([]byte, 10)) })
+ withNonApprovedHash(func(h crypto.Hash) {
+ expectPanic(t, func() { hmac.New(h.New, make([]byte, 16)) })
+ })
+ withApprovedHash(func(h crypto.Hash) { hmac.New(h.New, make([]byte, 16)) })
+
+ expectErr(t, errRet2(hkdf.Key(sha256.New, make([]byte, 10), nil, "", 16)))
+ withNonApprovedHash(func(h crypto.Hash) {
+ expectErr(t, errRet2(hkdf.Key(h.New, make([]byte, 16), nil, "", 16)))
+ })
+ withApprovedHash(func(h crypto.Hash) {
+ expectNoErr(t, errRet2(hkdf.Key(h.New, make([]byte, 16), nil, "", 16)))
+ })
+
+ expectErr(t, errRet2(hkdf.Extract(sha256.New, make([]byte, 10), nil)))
+ withNonApprovedHash(func(h crypto.Hash) {
+ expectErr(t, errRet2(hkdf.Extract(h.New, make([]byte, 16), nil)))
+ })
+ withApprovedHash(func(h crypto.Hash) {
+ expectNoErr(t, errRet2(hkdf.Extract(h.New, make([]byte, 16), nil)))
+ })
+
+ expectErr(t, errRet2(hkdf.Expand(sha256.New, make([]byte, 10), "", 16)))
+ withNonApprovedHash(func(h crypto.Hash) {
+ expectErr(t, errRet2(hkdf.Expand(h.New, make([]byte, 16), "", 16)))
+ })
+ withApprovedHash(func(h crypto.Hash) {
+ expectNoErr(t, errRet2(hkdf.Expand(h.New, make([]byte, 16), "", 16)))
+ })
+
+ expectErr(t, errRet2(rand.Prime(rand.Reader, 10)))
+
+ expectErr(t, dsa.GenerateParameters(&dsa.Parameters{}, rand.Reader, dsa.L1024N160))
+ expectErr(t, dsa.GenerateKey(&dsa.PrivateKey{}, rand.Reader))
+ expectErr(t, errRet3(dsa.Sign(rand.Reader, &dsa.PrivateKey{}, make([]byte, 16))))
+ expectPanic(t, func() {
+ dsa.Verify(&dsa.PublicKey{}, make([]byte, 16), big.NewInt(1), big.NewInt(1))
+ })
+
+ expectErr(t, errRet2(ecdh.X25519().GenerateKey(rand.Reader)))
+ expectErr(t, errRet2(ecdh.X25519().NewPrivateKey(make([]byte, 32))))
+ expectErr(t, errRet2(ecdh.X25519().NewPublicKey(make([]byte, 32))))
+ for _, curve := range []ecdh.Curve{ecdh.P256(), ecdh.P384(), ecdh.P521()} {
+ expectErrIfCustomRand(t, errRet2(curve.GenerateKey(readerWrap{rand.Reader})))
+ k, err := curve.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expectNoErr(t, errRet2(curve.NewPrivateKey(k.Bytes())))
+ expectNoErr(t, errRet2(curve.NewPublicKey(k.PublicKey().Bytes())))
+ }
+
+ for _, curve := range []elliptic.Curve{elliptic.P256(), elliptic.P384(), elliptic.P521()} {
+ expectErrIfCustomRand(t, errRet2(ecdsa.GenerateKey(curve, readerWrap{rand.Reader})))
+ k, err := ecdsa.GenerateKey(curve, rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expectErrIfCustomRand(t, errRet2(k.Sign(readerWrap{rand.Reader}, make([]byte, 32), nil)))
+ expectErrIfCustomRand(t, errRet2(ecdsa.SignASN1(readerWrap{rand.Reader}, k, make([]byte, 32))))
+ expectErrIfCustomRand(t, errRet3(ecdsa.Sign(readerWrap{rand.Reader}, k, make([]byte, 32))))
+ expectNoErr(t, errRet2(k.Sign(rand.Reader, make([]byte, 32), nil)))
+ expectNoErr(t, errRet2(ecdsa.SignASN1(rand.Reader, k, make([]byte, 32))))
+ expectNoErr(t, errRet3(ecdsa.Sign(rand.Reader, k, make([]byte, 32))))
+
+ withNonApprovedHash(func(h crypto.Hash) {
+ expectErr(t, errRet2(k.Sign(nil, make([]byte, h.Size()), h)))
+ })
+ withApprovedHash(func(h crypto.Hash) {
+ expectNoErr(t, errRet2(k.Sign(nil, make([]byte, h.Size()), h)))
+ })
+ }
+ customCurve := &elliptic.CurveParams{Name: "custom", P: big.NewInt(1)}
+ expectErr(t, errRet2(ecdsa.GenerateKey(customCurve, rand.Reader)))
+
+ _, ed25519Key, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expectNoErr(t, errRet2(ed25519Key.Sign(nil, make([]byte, 32), crypto.Hash(0))))
+ expectNoErr(t, errRet2(ed25519Key.Sign(nil, make([]byte, 64), crypto.SHA512)))
+ // ed25519ctx is not allowed (but ed25519ph with context is).
+ expectErr(t, errRet2(ed25519Key.Sign(nil, make([]byte, 32), &ed25519.Options{
+ Context: "test",
+ })))
+ expectNoErr(t, errRet2(ed25519Key.Sign(nil, make([]byte, 64), &ed25519.Options{
+ Hash: crypto.SHA512, Context: "test",
+ })))
+ expectNoErr(t, errRet2(ed25519Key.Sign(nil, make([]byte, 64), &ed25519.Options{
+ Hash: crypto.SHA512,
+ })))
+
+ expectErr(t, errRet2(rsa.GenerateMultiPrimeKey(rand.Reader, 3, 2048)))
+ expectErr(t, errRet2(rsa.GenerateKey(rand.Reader, 1024)))
+ expectErr(t, errRet2(rsa.GenerateKey(rand.Reader, 2049)))
+ expectErrIfCustomRand(t, errRet2(rsa.GenerateKey(readerWrap{rand.Reader}, 2048)))
+ rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
+ expectNoErr(t, err)
+
+ smallKey := parseKey(testingKey(`-----BEGIN RSA TESTING KEY-----
+MIICXQIBAAKBgQDMrln6XoAa3Rjts+kRi5obbP86qSf/562RcuDO+yMXeTLHfi4M
+8ubyhoFY+UKBCGBLmmTO7ikbvQgdipkT3xVkU8nM3XTW4sxrnw0X5QXsl4PGlMo0
+5UufxYyQxe7bbjuwFz2XnN6Jz4orpOfO0s36/KVHj9lZRl+REpr/Jy+nJQIDAQAB
+AoGAJ9WEwGO01cWSzOwXH2mGX/EKCQ4TsUuS7XwogU/B6BcXyVhmuPFq/ecsdDbq
+ePc62mvdU6JpELNsyWcIXKQtYsRgJHxNS+KJkCQIq6YeiAWRG0XL6q+qVj+HtT8a
+1Qrmul9ZBd23Y9wLF8pg/xWDQYvb8DPAb/xJ0e/KEBZcWU8CQQDXFCFCGpCfwyxY
+Cq8G/3B94D9UYwk5mK6jRIH5m8LbaX9bKKetf8+If8TWVgeuiRjjN4WEQ78lPoSg
+3Fsz2qs3AkEA85/JCudNUf2FnY+T6h1c/2SWekZiZ1NS4lCh/C7iYuAN3oa8zGkf
+gjjR5e0+Z8rUAcZkTukxyLLaNqy6rs9GgwJAVR6pXvEGhcQHe7yWso1LpvWl+q7L
+StkrXIBTdEb54j4pYhl/6wFnUB1I+I7JsYCeseYaWFM7hfDtKoCrM6V6FwJBANxh
+KmfmnJcSkw/YlaEuNrYAs+6gRNvbEBsRfba2Yqu2qlUl5Ruz7IDMDXPEjLMvU2DX
+ql2HrTU0NRlIXwdLESkCQQDGJ54H6WK1eE1YvtxCaLm28zmogcFlvc21pym+PpM1
+bXVL8iKLrG91IYQByUHZIn3WVAd2bfi4MfKagRt0ggd4
+-----END RSA TESTING KEY-----`))
+
+ expectNoErr(t, errRet2(rsaKey.Sign(rand.Reader, make([]byte, 32), crypto.SHA256)))
+ expectErr(t, errRet2(smallKey.Sign(rand.Reader, make([]byte, 32), crypto.SHA256)))
+ expectErr(t, errRet2(rsaKey.Sign(rand.Reader, make([]byte, 20), crypto.SHA1)))
+ // rand is always ignored for PKCS1v15 signing
+ expectNoErr(t, errRet2(rsaKey.Sign(readerWrap{rand.Reader}, make([]byte, 32), crypto.SHA256)))
+
+ sigPKCS1v15, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, make([]byte, 32))
+ expectNoErr(t, err)
+ expectErr(t, errRet2(rsa.SignPKCS1v15(rand.Reader, smallKey, crypto.SHA256, make([]byte, 32))))
+ expectErr(t, errRet2(rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA1, make([]byte, 20))))
+ // rand is always ignored for PKCS1v15 signing
+ expectNoErr(t, errRet2(rsa.SignPKCS1v15(readerWrap{rand.Reader}, rsaKey, crypto.SHA256, make([]byte, 32))))
+
+ expectNoErr(t, rsa.VerifyPKCS1v15(&rsaKey.PublicKey, crypto.SHA256, make([]byte, 32), sigPKCS1v15))
+ expectErr(t, rsa.VerifyPKCS1v15(&smallKey.PublicKey, crypto.SHA256, make([]byte, 32), sigPKCS1v15))
+ expectErr(t, rsa.VerifyPKCS1v15(&rsaKey.PublicKey, crypto.SHA1, make([]byte, 20), sigPKCS1v15))
+
+ sigPSS, err := rsa.SignPSS(rand.Reader, rsaKey, crypto.SHA256, make([]byte, 32), nil)
+ expectNoErr(t, err)
+ expectErr(t, errRet2(rsa.SignPSS(rand.Reader, smallKey, crypto.SHA256, make([]byte, 32), nil)))
+ expectErr(t, errRet2(rsa.SignPSS(rand.Reader, rsaKey, crypto.SHA1, make([]byte, 20), nil)))
+ expectErr(t, errRet2(rsa.SignPSS(readerWrap{rand.Reader}, rsaKey, crypto.SHA256, make([]byte, 32), nil)))
+
+ expectNoErr(t, rsa.VerifyPSS(&rsaKey.PublicKey, crypto.SHA256, make([]byte, 32), sigPSS, nil))
+ expectErr(t, rsa.VerifyPSS(&smallKey.PublicKey, crypto.SHA256, make([]byte, 32), sigPSS, nil))
+ expectErr(t, rsa.VerifyPSS(&rsaKey.PublicKey, crypto.SHA1, make([]byte, 20), sigPSS, nil))
+
+ k, err := mlkem.GenerateKey768()
+ expectNoErr(t, err)
+ expectErr(t, errRet3(mlkemtest.Encapsulate768(k.EncapsulationKey(), make([]byte, 32))))
+ k1024, err := mlkem.GenerateKey1024()
+ expectNoErr(t, err)
+ expectErr(t, errRet3(mlkemtest.Encapsulate1024(k1024.EncapsulationKey(), make([]byte, 32))))
+
+ for _, kem := range []hpke.KEM{
+ hpke.DHKEM(ecdh.P256()),
+ hpke.DHKEM(ecdh.P384()),
+ hpke.DHKEM(ecdh.P521()),
+ hpke.MLKEM768(),
+ hpke.MLKEM1024(),
+ hpke.MLKEM768P256(),
+ hpke.MLKEM1024P384(),
+ hpke.MLKEM768X25519(), // allowed as hybrid
+ } {
+ t.Run(fmt.Sprintf("HKPE KEM %04x", kem.ID()), func(t *testing.T) {
+ k, err := kem.GenerateKey()
+ expectNoErr(t, err)
+ expectNoErr(t, errRet2(kem.DeriveKeyPair(make([]byte, 64))))
+ kb, err := k.Bytes()
+ expectNoErr(t, err)
+ expectNoErr(t, errRet2(kem.NewPrivateKey(kb)))
+ expectNoErr(t, errRet2(kem.NewPublicKey(k.PublicKey().Bytes())))
+ c, err := hpke.Seal(k.PublicKey(), hpke.HKDFSHA256(), hpke.AES128GCM(), nil, nil)
+ expectNoErr(t, err)
+ _, err = hpke.Open(k, hpke.HKDFSHA256(), hpke.AES128GCM(), nil, c)
+ expectNoErr(t, err)
+ })
+ }
+ expectErr(t, errRet2(hpke.DHKEM(ecdh.X25519()).GenerateKey()))
+ expectErr(t, errRet2(hpke.DHKEM(ecdh.X25519()).DeriveKeyPair(make([]byte, 64))))
+ expectErr(t, errRet2(hpke.DHKEM(ecdh.X25519()).NewPrivateKey(make([]byte, 32))))
+ expectErr(t, errRet2(hpke.DHKEM(ecdh.X25519()).NewPublicKey(make([]byte, 32))))
+ hpkeK, err := hpke.MLKEM768().GenerateKey()
+ expectNoErr(t, err)
+ expectErr(t, errRet2(hpke.Seal(hpkeK.PublicKey(), hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), nil, nil)))
+ expectErr(t, errRet2(hpke.Open(hpkeK, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), nil, make([]byte, 2000))))
+
+ // fips140=only mode should prevent any operation that would make the FIPS
+ // 140-3 module set its service indicator to false.
+ if !fips140.ServiceIndicator() {
+ t.Errorf("service indicator not set")
+ }
+}
+
+type blockWrap struct {
+ cipher.Block
+}
+
+type readerWrap struct {
+ io.Reader
+}
+
+func withApprovedHash(f func(crypto.Hash)) {
+ f(crypto.SHA224)
+ f(crypto.SHA256)
+ f(crypto.SHA384)
+ f(crypto.SHA512)
+ f(crypto.SHA3_224)
+ f(crypto.SHA3_256)
+ f(crypto.SHA3_384)
+ f(crypto.SHA3_512)
+ f(crypto.SHA512_224)
+ f(crypto.SHA512_256)
+}
+
+func withNonApprovedHash(f func(crypto.Hash)) {
+ f(crypto.MD5)
+ f(crypto.SHA1)
+}
+
+func expectPanic(t *testing.T, f func()) {
+ t.Helper()
+ defer func() {
+ t.Helper()
+ if err := recover(); err == nil {
+ t.Errorf("expected panic")
+ } else {
+ if s, ok := err.(string); !ok || !strings.Contains(s, "FIPS 140-only") {
+ t.Errorf("unexpected panic: %v", err)
+ }
+ }
+ }()
+ f()
+}
+
+var cryptocustomrand = godebug.New("cryptocustomrand")
+
+func expectErr(t *testing.T, err error) {
+ t.Helper()
+ if err == nil {
+ t.Errorf("expected error")
+ } else if !strings.Contains(err.Error(), "FIPS 140-only") {
+ t.Errorf("unexpected error: %v", err)
+ }
+}
+
+func expectNoErr(t *testing.T, err error) {
+ t.Helper()
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+}
+
+func expectErrIfCustomRand(t *testing.T, err error) {
+ t.Helper()
+ if cryptocustomrand.Value() == "1" {
+ expectErr(t, err)
+ } else {
+ expectNoErr(t, err)
+ }
+}
+
+func errRet2[T any](_ T, err error) error {
+ return err
+}
+
+func errRet3[T any](_, _ T, err error) error {
+ return err
+}
+
+func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
+
+func parseKey(s string) *rsa.PrivateKey {
+ p, _ := pem.Decode([]byte(s))
+ k, err := x509.ParsePKCS1PrivateKey(p.Bytes)
+ if err != nil {
+ panic(err)
+ }
+ return k
+}