Repository: go
Issue: boringcrypto: require `CGO_ENABLED=1` when `GOEXPERIMENT=boringcrypto`
Labels: NeedsInvestigation

### Go version

go version go1.22.5 linux/amd64

### Output of `go env` in your module/workspace:

```shell
O111MODULE=''
GOARCH='amd64'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/nix/store/16g3awzjv3341s27hkkkv1vk5dw4206m-go-1.22.5/share/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/nix/store/16g3awzjv3341s27hkkkv1vk5dw4206m-go-1.22.5/share/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.22.5'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='0'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2148692384=/tmp/go-build -gno-record-gcc-switches'
```


### What did you do?

The following Go program starts a simple HTTPS server on port `:4443` using a self-signed RSA certificate and writes a CPU profile to `cpu.pprof`: https://go.dev/play/p/6DzPHSlU1r2

Compile this program using the following `go build` commands on a linux/amd64 machine:
 1. `GOEXPERIMENT=boringcrypto CGO_ENABLED=0 go build -o fips-test main.go`
 2. `GOEXPERIMENT=boringcrypto CGO_ENABLED=1 go build -o fips-test main.go`

### What did you see happen?

Both commands produce a valid binary without any error. Inspecting both binaries with `rsc.io/goversion` results in:
  1. ```
      goversion -crypto -m fips-test
      fips-test go1.22.5 X:boringcrypto (standard crypto)
              path   command-line-arguments
              build  -buildmode=exe
              build  -compiler=gc
              build  CGO_ENABLED=0
              build  GOARCH=amd64
              build  GOEXPERIMENT=boringcrypto
              build  GOOS=linux
              build  GOAMD64=v1
      ```
  2. ```
     goversion -crypto -m fips-test
     fips-test go1.22.5 X:boringcrypto (boring crypto)
              path   command-line-arguments
              build  -buildmode=exe
              build  -compiler=gc
              build  CGO_ENABLED=1
              build  CGO_CFLAGS=
              build  CGO_CPPFLAGS=
              build  CGO_CXXFLAGS=
              build  CGO_LDFLAGS=
              build  GOARCH=amd64
              build  GOEXPERIMENT=boringcrypto
              build  GOOS=linux
              build  GOAMD64=v1
      ```

Looking at the `cpu.pprof` files also shows that the boringcrypto C implementation is only selected when `CGO_ENABLED=1`.
```
go tool pprof cpu.pprof
    File: fips-test
    Type: cpu
    Time: Jul 25, 2024 at 4:33pm (CEST)
    Duration: 13.37s, Total samples = 50ms ( 0.37%)
    Entering interactive mode (type "help" for commands, "o" for options)
    (pprof) top 10
    Showing nodes accounting for 50ms, 100% of 50ms total
     Showing top 10 nodes out of 15
          flat  flat%   sum%        cum   cum%
           20ms 40.00% 40.00%       40ms 80.00%  crypto/internal/bigmod.(*Nat).montgomeryMul
          20ms 40.00% 80.00%       20ms 40.00%  crypto/internal/bigmod.addMulVVW2048
          10ms 20.00%   100%       10ms 20.00%  crypto/internal/bigmod.(*Nat).shiftIn
             0     0%   100%       40ms 80.00%  crypto/internal/bigmod.(*Nat).Exp
             0     0%   100%       10ms 20.00%  crypto/internal/bigmod.(*Nat).Mod
             0     0%   100%       50ms   100%  crypto/rsa.(*PrivateKey).Sign
             0     0%   100%       50ms   100%  crypto/rsa.SignPSS
             0     0%   100%       50ms   100%  crypto/rsa.decrypt
             0     0%   100%       50ms   100%  crypto/rsa.signPSSWithSalt
             0     0%   100%       50ms   100%  crypto/tls.(*Conn).HandshakeContext
```

```
 File: fips-test
    Type: cpu
    Time: Jul 25, 2024 at 4:40pm (CEST)
    Duration: 12.57s, Total samples = 50ms (  0.4%)
    Entering interactive mode (type "help" for commands, "o" for options)
    (pprof) top 10
    Showing nodes accounting for 50ms, 100% of 50ms total
    Showing top 10 nodes out of 42
          flat  flat%   sum%        cum   cum%
          30ms 60.00% 60.00%       30ms 60.00%  runtime.cgocall
          10ms 20.00% 80.00%       10ms 20.00%  runtime.memclrNoHeapPointers
          10ms 20.00%   100%       10ms 20.00%  runtime/internal/syscall.Syscall6
             0     0%   100%       10ms 20.00%  bufio.(*Writer).Flush
             0     0%   100%       30ms 60.00%  crypto/internal/boring.(*PrivateKeyRSA).withKey
             0     0%   100%       30ms 60.00%  crypto/internal/boring.SignRSAPSS
             0     0%   100%       30ms 60.00%  crypto/internal/boring.SignRSAPSS.func1
             0     0%   100%       30ms 60.00%  crypto/internal/boring.SignRSAPSS.func1.2
             0     0%   100%       30ms 60.00%  crypto/internal/boring._Cfunc__goboringcrypto_RSA_sign_pss_mgf1
             0     0%   100%       30ms 60.00%  crypto/rsa.(*PrivateKey).Sign
```

***

Importing `crypto/tls/fipsonly` works in both cases and the Go TLS stack actually rejects TLS 1.3 connections (TLS 1.3 has to be disabled for boringcrypto). When obserbing the TLS stack behavior, it seems that the binary uses `boringcrypto` when `CGO_ENABLED=0` even though it actually uses the standard library crypto implementations.

### What did you expect to see?

This seems like a "footgun" in the sense that people might try to build a binary that uses a FIPS 140-2 certified module to meet compliance requirements but accidentally have set `CGO_ENABLED=0` in their build environment.

While it might seem obvious that `GOEXPERIMENT=boringcrypto` demands `CGO_ENABLED=1`, I was not able to find any documentation for this. Also there are blog posts not mentioning this in any way. For example: https://medium.com/cyberark-engineering/navigating-fips-compliance-for-go-applications-libraries-integration-and-security-42ac87eec40b

I'm aware that building Go binaries with boringcrypto as crypto backend is not officially supported. However, requiring `CGO_ENABLED=1` does not seem to have any downside.