--- /dev/null
+// 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 fips
+
+import (
+ "errors"
+ "internal/godebug"
+ "strings"
+ _ "unsafe" // for go:linkname
+)
+
+// fatal is [runtime.fatal], pushed via linkname.
+//
+//go:linkname fatal
+func fatal(string)
+
+// failfipscast is a GODEBUG key allowing simulation of a Cryptographic Algorithm
+// Self-Test (CAST) failure, as required during FIPS 140-3 functional testing.
+// The value is a substring of the target CAST name.
+var failfipscast = godebug.New("#failfipscast")
+
+// testingOnlyCASTHook is called during tests with each CAST name.
+var testingOnlyCASTHook func(string)
+
+// CAST runs the named Cryptographic Algorithm Self-Test (if compiled and
+// operated in FIPS mode) and aborts the program (stopping the module
+// input/output and entering the "error state") if the self-test fails.
+//
+// These are mandatory self-checks that must be performed by FIPS 140-3 modules
+// before the algorithm is used. See Implementation Guidance 10.3.A.
+//
+// The name must not contain commas, colons, hashes, or equal signs.
+//
+// When calling this function, also add the calling package to cast_test.go.
+func CAST(name string, f func() error) {
+ if strings.ContainsAny(name, ",#=:") {
+ panic("fips: invalid self-test name: " + name)
+ }
+ if testingOnlyCASTHook != nil {
+ testingOnlyCASTHook(name)
+ }
+
+ err := f()
+ if failfipscast.Value() != "" && strings.Contains(name, failfipscast.Value()) {
+ err = errors.New("simulated CAST failure")
+ }
+ if err != nil {
+ fatal("FIPS 140-3 self-test failed: " + name + ": " + err.Error())
+ panic("unreachable")
+ }
+}
--- /dev/null
+// 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 fips_test
+
+import (
+ "crypto/internal/fips"
+ "fmt"
+ "internal/testenv"
+ "strings"
+ "testing"
+
+ // Import packages that define CASTs to test them.
+ _ "crypto/internal/fips/hmac"
+ _ "crypto/internal/fips/sha256"
+ _ "crypto/internal/fips/sha3"
+ _ "crypto/internal/fips/sha512"
+)
+
+func TestCAST(t *testing.T) {
+ if len(fips.AllCASTs) == 0 {
+ t.Errorf("no CASTs to test")
+ }
+
+ for _, name := range fips.AllCASTs {
+ t.Logf("CAST %s completed successfully", name)
+ }
+
+ t.Run("SimulateFailures", func(t *testing.T) {
+ testenv.MustHaveExec(t)
+ for _, name := range fips.AllCASTs {
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+ cmd := testenv.Command(t, testenv.Executable(t), "-test.run=TestCAST", "-test.v")
+ cmd = testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s", name))
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Error(err)
+ } else {
+ t.Logf("CAST %s failed and caused the program to exit", name)
+ t.Logf("%s", out)
+ }
+ if strings.Contains(string(out), "completed successfully") {
+ t.Errorf("CAST %s failure did not stop the program", name)
+ }
+ })
+ }
+ })
+}
--- /dev/null
+// 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 fips
+
+var AllCASTs []string
+
+func init() {
+ testingOnlyCASTHook = func(name string) {
+ AllCASTs = append(AllCASTs, name)
+ }
+}
--- /dev/null
+// 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 hmac
+
+import (
+ "bytes"
+ "crypto/internal/fips"
+ "crypto/internal/fips/sha256"
+ "errors"
+)
+
+func init() {
+ fips.CAST("HMAC-SHA2-256", func() error {
+ input := []byte{
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ }
+ want := []byte{
+ 0xf0, 0x8d, 0x82, 0x8d, 0x4c, 0x9e, 0xad, 0x3d,
+ 0xdc, 0x12, 0x9c, 0x4e, 0x70, 0xc4, 0x19, 0x2a,
+ 0x4f, 0x12, 0x73, 0x23, 0x73, 0x77, 0x66, 0x05,
+ 0x10, 0xee, 0x57, 0x6b, 0x3a, 0xc7, 0x14, 0x41,
+ }
+ h := New(sha256.New, input)
+ h.Write(input)
+ h.Write(input)
+ if got := h.Sum(nil); !bytes.Equal(got, want) {
+ return errors.New("unexpected result")
+ }
+ return nil
+ })
+}
--- /dev/null
+// 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 sha256
+
+import (
+ "bytes"
+ "crypto/internal/fips"
+ "errors"
+)
+
+func init() {
+ fips.CAST("SHA2-256", func() error {
+ input := []byte{
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ }
+ want := []byte{
+ 0x5d, 0xfb, 0xab, 0xee, 0xdf, 0x31, 0x8b, 0xf3,
+ 0x3c, 0x09, 0x27, 0xc4, 0x3d, 0x76, 0x30, 0xf5,
+ 0x1b, 0x82, 0xf3, 0x51, 0x74, 0x03, 0x01, 0x35,
+ 0x4f, 0xa3, 0xd7, 0xfc, 0x51, 0xf0, 0x13, 0x2e,
+ }
+ h := New()
+ h.Write(input)
+ if got := h.Sum(nil); !bytes.Equal(got, want) {
+ return errors.New("unexpected result")
+ }
+ return nil
+ })
+}
--- /dev/null
+// 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 sha3
+
+import (
+ "bytes"
+ "crypto/internal/fips"
+ "errors"
+)
+
+func init() {
+ fips.CAST("cSHAKE128", func() error {
+ input := []byte{
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ }
+ want := []byte{
+ 0xd2, 0x17, 0x37, 0x39, 0xf6, 0xa1, 0xe4, 0x6e,
+ 0x81, 0xe5, 0x70, 0xe3, 0x1b, 0x10, 0x4c, 0x82,
+ 0xc5, 0x48, 0xee, 0xe6, 0x09, 0xf5, 0x89, 0x52,
+ 0x52, 0xa4, 0x69, 0xd4, 0xd0, 0x76, 0x68, 0x6b,
+ }
+ h := NewCShake128(input, input)
+ h.Write(input)
+ if got := h.Sum(nil); !bytes.Equal(got, want) {
+ return errors.New("unexpected result")
+ }
+ return nil
+ })
+}
--- /dev/null
+// 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 sha512
+
+import (
+ "bytes"
+ "crypto/internal/fips"
+ "errors"
+)
+
+func init() {
+ fips.CAST("SHA2-512", func() error {
+ input := []byte{
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ }
+ want := []byte{
+ 0xb4, 0xc4, 0xe0, 0x46, 0x82, 0x6b, 0xd2, 0x61,
+ 0x90, 0xd0, 0x97, 0x15, 0xfc, 0x31, 0xf4, 0xe6,
+ 0xa7, 0x28, 0x20, 0x4e, 0xad, 0xd1, 0x12, 0x90,
+ 0x5b, 0x08, 0xb1, 0x4b, 0x7f, 0x15, 0xc4, 0xf3,
+ 0x8e, 0x29, 0xb2, 0xfc, 0x54, 0x26, 0x5a, 0x12,
+ 0x63, 0x26, 0xc5, 0xbd, 0xea, 0x66, 0xc1, 0xb0,
+ 0x8e, 0x9e, 0x47, 0x72, 0x3b, 0x2d, 0x70, 0x06,
+ 0x5a, 0xc1, 0x26, 0x2e, 0xcc, 0x37, 0xbf, 0xb1,
+ }
+ h := New()
+ h.Write(input)
+ if got := h.Sum(nil); !bytes.Equal(got, want) {
+ return errors.New("unexpected result")
+ }
+ return nil
+ })
+}
fatal(s)
}
+//go:linkname fips_fatal crypto/internal/fips.fatal
+func fips_fatal(s string) {
+ fatal(s)
+}
+
// throw triggers a fatal error that dumps a stack trace and exits.
//
// throw should be used for runtime-internal fatal errors where Go itself,