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  

View as plain text