// 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")
	}
}
