check.mx raw

   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"
  17  	"crypto/internal/fips140/hmac"
  18  	"crypto/internal/fips140/sha256"
  19  	"crypto/internal/fips140deps/byteorder"
  20  	"crypto/internal/fips140deps/godebug"
  21  	"io"
  22  	"unsafe"
  23  )
  24  
  25  // Verified is set when verification succeeded. It can be expected to always be
  26  // true when [fips140.Enabled] is true, or init would have panicked.
  27  var Verified bool
  28  
  29  // Linkinfo holds the go:fipsinfo symbol prepared by the linker.
  30  // See cmd/link/internal/ld/fips.go for details.
  31  //
  32  //go:linkname Linkinfo go:fipsinfo
  33  var Linkinfo struct {
  34  	Magic [16]byte
  35  	Sum   [32]byte
  36  	Self  uintptr
  37  	Sects [4]struct {
  38  		// Note: These must be unsafe.Pointer, not uintptr,
  39  		// or else checkptr panics about turning uintptrs
  40  		// into pointers into the data segment during
  41  		// go test -race.
  42  		Start unsafe.Pointer
  43  		End   unsafe.Pointer
  44  	}
  45  }
  46  
  47  // "\xff"+fipsMagic is the expected linkinfo.Magic.
  48  // We avoid writing that explicitly so that the string does not appear
  49  // elsewhere in normal binaries, just as a precaution.
  50  const fipsMagic = " Go fipsinfo \xff\x00"
  51  
  52  var zeroSum [32]byte
  53  
  54  func init() {
  55  	if !fips140.Enabled {
  56  		return
  57  	}
  58  
  59  	if err := fips140.Supported(); err != nil {
  60  		var msg []byte
  61  		msg = append(msg, "fips140: "...)
  62  		msg = append(msg, err.Error()...)
  63  		panic(string(msg))
  64  	}
  65  
  66  	if Linkinfo.Magic[0] != 0xff || Linkinfo.Magic[1:] != fipsMagic || Linkinfo.Sum == zeroSum {
  67  		panic("fips140: no verification checksum found")
  68  	}
  69  
  70  	h := hmac.New(sha256.New, []byte{:32})
  71  	w := io.Writer(h)
  72  
  73  	/*
  74  		// Uncomment for debugging.
  75  		// Commented (as opposed to a const bool flag)
  76  		// to avoid import "os" in default builds.
  77  		f, err := os.Create("fipscheck.o")
  78  		if err != nil {
  79  			panic(err)
  80  		}
  81  		w = io.MultiWriter(h, f)
  82  	*/
  83  
  84  	w.Write([]byte("go fips object v1\n"))
  85  
  86  	var nbuf [8]byte
  87  	for _, sect := range Linkinfo.Sects {
  88  		n := uintptr(sect.End) - uintptr(sect.Start)
  89  		byteorder.BEPutUint64(nbuf[:], uint64(n))
  90  		w.Write(nbuf[:])
  91  		w.Write(unsafe.Slice((*byte)(sect.Start), n))
  92  	}
  93  	sum := h.Sum(nil)
  94  
  95  	if [32]byte(sum) != Linkinfo.Sum {
  96  		panic("fips140: verification mismatch")
  97  	}
  98  
  99  	// "The temporary value(s) generated during the integrity test of the
 100  	// module’s software or firmware shall [05.10] be zeroised from the module
 101  	// upon completion of the integrity test"
 102  	clear(sum)
 103  	clear(nbuf[:])
 104  	h.Reset()
 105  
 106  	if godebug.Value("fips140") == "debug" {
 107  		println("fips140: verified code+data")
 108  	}
 109  
 110  	Verified = true
 111  }
 112