fips

package
v2.34.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 1 Imported by: 0

README

fips

Package fips reports whether the running binary performs cryptographic operations through a FIPS 140-3 validated module, and which module that is.

Detection is gated on the fips build tag. Without it the package compiles to constants — ActiveBackend() is BackendNone and Enabled() is false — so ordinary builds carry no FIPS-related code and no extra dependencies. The package imports only the standard library.

Quick start

// Log the FIPS posture once during start-up.
fips.LogStatus(a.Logger())

// Branch on it where behaviour has to differ.
if fips.Enabled() {
    // ...
}

Backends

Built with -tags fips, two backends are recognised:

Backend Probe Active when
BackendBoringCrypto crypto/boring Built with the golang-fips toolchain (GOEXPERIMENT=boringcrypto, CGO_ENABLED=1, amd64 Linux) and a system OpenSSL in FIPS mode can be loaded
BackendNativeGo crypto/fips140 Built with GOFIPS140 set to a module version (e.g. v1.0.0), or run with GODEBUG=fips140=on — see Go FIPS 140-3 compliance
BackendNone Neither of the above

crypto/boring only has buildable files when the boringcrypto build tag is set, so that probe is isolated behind it (boring.go / notboring.go). This keeps -tags fips usable on a toolchain that ships the native Go Cryptographic Module instead of the golang-fips fork; importing crypto/boring unconditionally fails to compile there.

BackendBoringCrypto takes precedence: when the golang-fips toolchain routes the crypto packages through OpenSSL, OpenSSL is the validated module doing the work regardless of what crypto/fips140 reports.

Enabled() is derived from ActiveBackend() rather than probing a second time, so the two cannot disagree.

Logging

LogStatus records the posture at info level under the fips_backend field. It distinguishes four states, including "compiled with FIPS support but no backend activated" — a misconfigured FIPS deployment, which is very different from a build that never had FIPS support:

{"level":"INFO","msg":"FIPS mode is enabled, using the native Go Cryptographic Module","fips_backend":"native-go"}

A nil logger falls back to slog.Default(), so LogStatus is safe to call before a logger is wired up.

If you log the backend yourself, use the same fips_backend key so the field stays queryable across services:

logger.Info("crypto backend", "fips_backend", fips.ActiveBackend())

SSH algorithms

FIPS-compliant SSH algorithm sets live in the fips/sshalgo subpackage, which depends on golang.org/x/crypto. It is separate so that services which only need to report their posture do not take on that dependency.

import (
    "gitlab.com/gitlab-org/labkit/v2/fips/sshalgo"
    "golang.org/x/crypto/ssh"
)

config := &ssh.ServerConfig{}

algorithms := sshalgo.DefaultAlgorithms()
config.Ciphers = algorithms.Ciphers
config.MACs = algorithms.MACs
config.KeyExchanges = algorithms.KeyExchanges

ssh.NewServerConn calls SetDefaults() itself, so there is no need to call it here — and doing so before assigning these fields would have no effect anyway.

Without the tag both functions return golang.org/x/crypto/ssh's own sets unchanged, so callers can wire them in unconditionally and let the build tag decide the policy.

Policy, and what each backend adds

The exclusion lists below are GitLab's compliance determination and apply on every backend. They are a floor: a backend may narrow the result further, never widen it.

Field Dropped
KeyExchanges X25519 and its aliases, ML-KEM768/X25519, DH-group14-SHA1
Ciphers ChaCha20-Poly1305
MACs HMAC-SHA1, HMAC-SHA1-96
PublicKeyAuths Security-key ED25519 and ECDSA variants

This applies to DefaultAlgorithms() as much as to SupportedAlgorithms(): ssh.Config.SetDefaults() enables ChaCha20-Poly1305, HMAC-SHA1, HMAC-SHA1-96 and DH-group14-SHA1 by default, so the result is a subset of the default set rather than the default set itself — filtering only removes entries, it never reorders them.

Why X25519 is excluded even where Go allows it

Upstream Go approves the ML-KEM768/X25519 hybrid under fips140=on, and x/crypto/ssh registers it ahead of its own FIPS branch so it survives. GitLab excludes it anyway, for two independent reasons established when FIPS SSH support was first added:

  1. Compliance. The FIPS 140-3 Implementation Guidance states that curves in SP 800-186 but not in SP 800-56Arev3 are not approved for key agreement, naming ECDH X25519 explicitly — see !216. The hybrid still performs X25519 key agreement.
  2. Operation. These algorithms panic on the golang-fips toolchain — see commit c91581b, which introduced the filter for that reason.
What the native module adds

Under GOFIPS140 the result is narrowed further to what x/crypto/ssh can actually negotiate. Non-approved algorithms are never registered in its internal maps, and SetDefaults() — called unconditionally by NewServerConn and NewClientConn — drops anything absent from them, so a caller cannot negotiate one even by configuring it explicitly. Deriving that narrowing rather than hand-maintaining a second list keeps LabKit in step with the linked module, whose contents depend on GOFIPS140 and may change across Go versions.

Finite-field Diffie-Hellman is the visible case — the native module does not implement it, so advertising it would offer something unnegotiable:

native module : [ecdh-p256  ecdh-p384  ecdh-p521]
boringcrypto  : [ecdh-p256  ecdh-p384  ecdh-p521  dh-group14-sha256]

HostKeys is always left empty: those algorithms are derived from the host keys presented at handshake time.

Both functions also drop empty algorithm names, in every build configuration. This works around a bug in golang.org/x/crypto/ssh (present through v0.54.0) where defaultCiphers aliases the supportedCiphers backing array, so the in-place slices.DeleteFunc it runs under fips140.Enabled() leaves a zeroed tail behind — ssh.SupportedAlgorithms().Ciphers ends in "" on any GOFIPS140 build. See filter.go.

Testing

./scripts/test.sh runs both modules with $BUILD_TAGS, so the test-fips (golang-fips image, GOEXPERIMENT=boringcrypto) and test-fips-native (upstream image, GOFIPS140=v1.0.0) CI jobs both exercise this package. Two jobs are needed because cmd/go rejects GOFIPS140 combined with GOEXPERIMENT=boringcrypto.

Documentation

Overview

Package fips reports whether this binary performs cryptographic operations through a FIPS 140-3 validated module.

Detection is gated on the fips build tag. Without it, ActiveBackend always reports BackendNone and Enabled always reports false, so ordinary builds carry no FIPS-related code. Built with -tags fips, two backends are recognised:

  • BackendBoringCrypto: the FIPS Go compiler in https://github.com/golang-fips/go, which routes the crypto packages through a system OpenSSL and is probed via crypto/boring. It requires CGO_ENABLED=1, an amd64 Linux runtime, a kernel in FIPS mode (/proc/sys/crypto/fips_enabled is 1), and a system OpenSSL that can be loaded via dlopen().

  • BackendNativeGo: upstream Go's native Go Cryptographic Module, probed via crypto/fips140. It requires the binary to be built with GOFIPS140 set to a module version (for example v1.0.0), or to run with GODEBUG=fips140=on. See https://go.dev/doc/security/fips140.

crypto/boring only has buildable files when the boringcrypto build tag is set (GOEXPERIMENT=boringcrypto), so that probe is isolated behind the tag. This keeps the fips build tag usable on a toolchain that ships the native module instead of the golang-fips fork.

This package depends only on the standard library. FIPS-compliant SSH algorithm sets live in the gitlab.com/gitlab-org/labkit/v2/fips/sshalgo subpackage, which pulls in golang.org/x/crypto.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Enabled

func Enabled() bool

Enabled reports whether cryptographic operations are backed by a FIPS module. Callers that need to know which module should use ActiveBackend instead.

Enabled is derived from ActiveBackend rather than probing the backends a second time, so the two cannot disagree.

func LogStatus

func LogStatus(logger *slog.Logger)

LogStatus records this binary's FIPS posture at info level, under the fips_backend field. Services should call it once during start-up: the posture is fixed for the lifetime of the process, and an operator diagnosing a FIPS deployment needs it in the logs of every component.

A nil logger falls back to slog.Default.

Types

type Backend

type Backend string

Backend identifies the cryptographic backend satisfying FIPS 140-3 for this binary. It is a string so it renders usefully in logs and metrics labels.

const (
	// BackendNone indicates no FIPS backend is active. Either the binary was
	// not built with the fips build tag, or it was but neither backend
	// activated at runtime.
	BackendNone Backend = "none"

	// BackendBoringCrypto indicates a BoringCrypto-compatible backend is
	// handling crypto operations, i.e. the external OpenSSL library used by
	// the golang-fips toolchain.
	BackendBoringCrypto Backend = "boringcrypto"

	// BackendNativeGo indicates upstream Go's native Go Cryptographic Module
	// is handling crypto operations.
	BackendNativeGo Backend = "native-go"
)

func ActiveBackend

func ActiveBackend() Backend

ActiveBackend reports which cryptographic backend is satisfying FIPS 140-3 for this binary. Without the fips build tag no backend is compiled in, so this is always BackendNone.

func (Backend) String

func (b Backend) String() string

String implements fmt.Stringer.

Directories

Path Synopsis
Package sshalgo provides the SSH algorithm sets a server should offer, filtered for FIPS 140-3 compliance when the binary is built with the fips build tag.
Package sshalgo provides the SSH algorithm sets a server should offer, filtered for FIPS 140-3 compliance when the binary is built with the fips build tag.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL