Source file src/crypto/internal/fips140/check/check.go
1 // Copyright 2024 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package check implements the FIPS-140 load-time code+data verification. 6 // Every FIPS package providing cryptographic functionality except hmac and sha256 7 // must import crypto/internal/fips140/check, so that the verification happens 8 // before initialization of package global variables. 9 // The hmac and sha256 packages are used by this package, so they cannot import it. 10 // Instead, those packages must be careful not to change global variables during init. 11 // (If necessary, we could have check call a PostCheck function in those packages 12 // after the check has completed.) 13 package check 14 15 import ( 16 "crypto/internal/fips140/hmac" 17 "crypto/internal/fips140/sha256" 18 "crypto/internal/fips140deps/byteorder" 19 "crypto/internal/fips140deps/godebug" 20 "io" 21 "runtime" 22 "unsafe" 23 ) 24 25 // Enabled reports whether verification was enabled. 26 // If Enabled returns true, then verification succeeded, 27 // because if it failed the binary would have panicked at init time. 28 func Enabled() bool { 29 return enabled 30 } 31 32 var enabled bool // set when verification is enabled 33 var Verified bool // set when verification succeeds, for testing 34 35 // Supported reports whether the current GOOS/GOARCH is Supported at all. 36 func Supported() bool { 37 // See cmd/internal/obj/fips.go's EnableFIPS for commentary. 38 switch { 39 case runtime.GOARCH == "wasm", 40 runtime.GOOS == "windows" && runtime.GOARCH == "386", 41 runtime.GOOS == "windows" && runtime.GOARCH == "arm", 42 runtime.GOOS == "aix": 43 return false 44 } 45 return true 46 } 47 48 // Linkinfo holds the go:fipsinfo symbol prepared by the linker. 49 // See cmd/link/internal/ld/fips.go for details. 50 // 51 //go:linkname Linkinfo go:fipsinfo 52 var Linkinfo struct { 53 Magic [16]byte 54 Sum [32]byte 55 Self uintptr 56 Sects [4]struct { 57 // Note: These must be unsafe.Pointer, not uintptr, 58 // or else checkptr panics about turning uintptrs 59 // into pointers into the data segment during 60 // go test -race. 61 Start unsafe.Pointer 62 End unsafe.Pointer 63 } 64 } 65 66 // "\xff"+fipsMagic is the expected linkinfo.Magic. 67 // We avoid writing that explicitly so that the string does not appear 68 // elsewhere in normal binaries, just as a precaution. 69 const fipsMagic = " Go fipsinfo \xff\x00" 70 71 var zeroSum [32]byte 72 73 func init() { 74 v := godebug.Value("#fips140") 75 enabled = v != "" && v != "off" 76 if !enabled { 77 return 78 } 79 80 if asanEnabled { 81 // ASAN disapproves of reading swaths of global memory below. 82 // One option would be to expose runtime.asanunpoison through 83 // crypto/internal/fips140deps and then call it to unpoison the range 84 // before reading it, but it is unclear whether that would then cause 85 // false negatives. For now, FIPS+ASAN doesn't need to work. 86 // If this is made to work, also re-enable the test in check_test.go 87 // and in cmd/dist/test.go. 88 panic("fips140: cannot verify in asan mode") 89 } 90 91 switch v { 92 case "on", "only", "debug": 93 // ok 94 default: 95 panic("fips140: unknown GODEBUG setting fips140=" + v) 96 } 97 98 if !Supported() { 99 panic("fips140: unavailable on " + runtime.GOOS + "-" + runtime.GOARCH) 100 } 101 102 if Linkinfo.Magic[0] != 0xff || string(Linkinfo.Magic[1:]) != fipsMagic || Linkinfo.Sum == zeroSum { 103 panic("fips140: no verification checksum found") 104 } 105 106 h := hmac.New(sha256.New, make([]byte, 32)) 107 w := io.Writer(h) 108 109 /* 110 // Uncomment for debugging. 111 // Commented (as opposed to a const bool flag) 112 // to avoid import "os" in default builds. 113 f, err := os.Create("fipscheck.o") 114 if err != nil { 115 panic(err) 116 } 117 w = io.MultiWriter(h, f) 118 */ 119 120 w.Write([]byte("go fips object v1\n")) 121 122 var nbuf [8]byte 123 for _, sect := range Linkinfo.Sects { 124 n := uintptr(sect.End) - uintptr(sect.Start) 125 byteorder.BEPutUint64(nbuf[:], uint64(n)) 126 w.Write(nbuf[:]) 127 w.Write(unsafe.Slice((*byte)(sect.Start), n)) 128 } 129 sum := h.Sum(nil) 130 131 if [32]byte(sum) != Linkinfo.Sum { 132 panic("fips140: verification mismatch") 133 } 134 135 if v == "debug" { 136 println("fips140: verified code+data") 137 } 138 139 Verified = true 140 } 141