// 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 check implements the FIPS-140 load-time code+data verification. // Every FIPS package providing cryptographic functionality except hmac and sha256 // must import crypto/internal/fips140/check, so that the verification happens // before initialization of package global variables. // The hmac and sha256 packages are used by this package, so they cannot import it. // Instead, those packages must be careful not to change global variables during init. // (If necessary, we could have check call a PostCheck function in those packages // after the check has completed.)
package check import ( ) // Enabled reports whether verification was enabled. // If Enabled returns true, then verification succeeded, // because if it failed the binary would have panicked at init time. func () bool { return enabled } var enabled bool // set when verification is enabled var Verified bool // set when verification succeeds, for testing // Supported reports whether the current GOOS/GOARCH is Supported at all. func () bool { // See cmd/internal/obj/fips.go's EnableFIPS for commentary. switch { case runtime.GOARCH == "wasm", runtime.GOOS == "windows" && runtime.GOARCH == "386", runtime.GOOS == "windows" && runtime.GOARCH == "arm", runtime.GOOS == "aix": return false } return true } // Linkinfo holds the go:fipsinfo symbol prepared by the linker. // See cmd/link/internal/ld/fips.go for details. // //go:linkname Linkinfo go:fipsinfo var Linkinfo struct { Magic [16]byte Sum [32]byte Self uintptr Sects [4]struct { // Note: These must be unsafe.Pointer, not uintptr, // or else checkptr panics about turning uintptrs // into pointers into the data segment during // go test -race. Start unsafe.Pointer End unsafe.Pointer } } // "\xff"+fipsMagic is the expected linkinfo.Magic. // We avoid writing that explicitly so that the string does not appear // elsewhere in normal binaries, just as a precaution. const fipsMagic = " Go fipsinfo \xff\x00" var zeroSum [32]byte func init() { := godebug.Value("#fips140") enabled = != "" && != "off" if !enabled { return } if asanEnabled { // ASAN disapproves of reading swaths of global memory below. // One option would be to expose runtime.asanunpoison through // crypto/internal/fips140deps and then call it to unpoison the range // before reading it, but it is unclear whether that would then cause // false negatives. For now, FIPS+ASAN doesn't need to work. // If this is made to work, also re-enable the test in check_test.go // and in cmd/dist/test.go. panic("fips140: cannot verify in asan mode") } switch { case "on", "only", "debug": // ok default: panic("fips140: unknown GODEBUG setting fips140=" + ) } if !Supported() { panic("fips140: unavailable on " + runtime.GOOS + "-" + runtime.GOARCH) } if Linkinfo.Magic[0] != 0xff || string(Linkinfo.Magic[1:]) != fipsMagic || Linkinfo.Sum == zeroSum { panic("fips140: no verification checksum found") } := hmac.New(sha256.New, make([]byte, 32)) := io.Writer() /* // Uncomment for debugging. // Commented (as opposed to a const bool flag) // to avoid import "os" in default builds. f, err := os.Create("fipscheck.o") if err != nil { panic(err) } w = io.MultiWriter(h, f) */ .Write([]byte("go fips object v1\n")) var [8]byte for , := range Linkinfo.Sects { := uintptr(.End) - uintptr(.Start) byteorder.BEPutUint64([:], uint64()) .Write([:]) .Write(unsafe.Slice((*byte)(.Start), )) } := .Sum(nil) if [32]byte() != Linkinfo.Sum { panic("fips140: verification mismatch") } if == "debug" { println("fips140: verified code+data") } Verified = true }