Source file src/crypto/internal/fips/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/fips/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/fips/hmac"
    17  	"crypto/internal/fips/sha256"
    18  	"internal/byteorder"
    19  	"internal/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 == "windows" && runtime.GOARCH == "arm64",
    43  		runtime.GOOS == "aix":
    44  		return false
    45  	}
    46  	return true
    47  }
    48  
    49  // linkinfo holds the go:fipsinfo symbol prepared by the linker.
    50  // See cmd/link/internal/ld/fips.go for details.
    51  //
    52  //go:linkname linkinfo go:fipsinfo
    53  var linkinfo struct {
    54  	Magic [16]byte
    55  	Sum   [32]byte
    56  	Self  uintptr
    57  	Sects [4]struct {
    58  		// Note: These must be unsafe.Pointer, not uintptr,
    59  		// or else checkptr panics about turning uintptrs
    60  		// into pointers into the data segment during
    61  		// go test -race.
    62  		Start unsafe.Pointer
    63  		End   unsafe.Pointer
    64  	}
    65  }
    66  
    67  // "\xff"+fipsMagic is the expected linkinfo.Magic.
    68  // We avoid writing that explicitly so that the string does not appear
    69  // elsewhere in normal binaries, just as a precaution.
    70  const fipsMagic = " Go fipsinfo \xff\x00"
    71  
    72  var zeroSum [32]byte
    73  
    74  func init() {
    75  	v := godebug.New("#fips140").Value()
    76  	enabled = v != "" && v != "off"
    77  	if !enabled {
    78  		return
    79  	}
    80  
    81  	switch v {
    82  	case "on", "only", "debug":
    83  		// ok
    84  	default:
    85  		panic("fips140: unknown GODEBUG setting fips140=" + v)
    86  	}
    87  
    88  	if !supported() {
    89  		panic("fips140: unavailable on " + runtime.GOOS + "-" + runtime.GOARCH)
    90  	}
    91  
    92  	if linkinfo.Magic[0] != 0xff || string(linkinfo.Magic[1:]) != fipsMagic || linkinfo.Sum == zeroSum {
    93  		panic("fips140: no verification checksum found")
    94  	}
    95  
    96  	h := hmac.New(sha256.New, make([]byte, 32))
    97  	w := io.Writer(h)
    98  
    99  	/*
   100  		// Uncomment for debugging.
   101  		// Commented (as opposed to a const bool flag)
   102  		// to avoid import "os" in default builds.
   103  		f, err := os.Create("fipscheck.o")
   104  		if err != nil {
   105  			panic(err)
   106  		}
   107  		w = io.MultiWriter(h, f)
   108  	*/
   109  
   110  	w.Write([]byte("go fips object v1\n"))
   111  
   112  	var nbuf [8]byte
   113  	for _, sect := range linkinfo.Sects {
   114  		n := uintptr(sect.End) - uintptr(sect.Start)
   115  		byteorder.BePutUint64(nbuf[:], uint64(n))
   116  		w.Write(nbuf[:])
   117  		w.Write(unsafe.Slice((*byte)(sect.Start), n))
   118  	}
   119  	sum := h.Sum(nil)
   120  
   121  	if [32]byte(sum) != linkinfo.Sum {
   122  		panic("fips140: verification mismatch")
   123  	}
   124  
   125  	if v == "debug" {
   126  		println("fips140: verified code+data")
   127  	}
   128  
   129  	verified = true
   130  }
   131  

View as plain text