Skip to content

boringcrypto: require CGO_ENABLED=1 when GOEXPERIMENT=boringcrypto #68588

Open
@aead

Description

@aead

Go version

go version go1.22.5 linux/amd64

Output of go env in your module/workspace:

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions