// 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 or Pairwise Consistency
// Test (if operated in FIPS mode) and aborts the program (stopping the module
// input/output and entering the "error state") if the self-test fails.
//
// CASTs are mandatory self-checks that must be performed by FIPS 140-3 modules
-// before the algorithm is used. See Implementation Guidance 10.3.A. PCTs are
+// before the algorithm is used. See Implementation Guidance 10.3.A. PCTs are
// mandatory for every key pair that is generated/imported, including ephemeral
// keys (which effectively doubles the cost of key establishment). See
// Implementation Guidance 10.3.A Additional Comment 1.
// The name must not contain commas, colons, hashes, or equal signs.
//
// When calling this function from init(), also import the calling package from
-// cast_external_test.go, while if calling it from key generation/importing, add
-// an invocation to TestCAST.
+// crypto/internal/fipstest, while if calling it from key generation/importing, add
+// an invocation to fipstest.TestPCTs.
func CAST(name string, f func() error) {
if strings.ContainsAny(name, ",#=:") {
panic("fips: invalid self-test name: " + name)
}
- if testingOnlyCASTHook != nil {
- testingOnlyCASTHook(name)
- }
if !Enabled {
return
}
fatal("FIPS 140-3 self-test failed: " + name + ": " + err.Error())
panic("unreachable")
}
+ if debug {
+ println("FIPS 140-3 self-test passed:", 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_test
-
-import (
- "crypto/internal/fips"
- "fmt"
- "internal/testenv"
- "strings"
- "testing"
-
- // Import packages that define CASTs to test them.
- _ "crypto/internal/fips/aes"
- _ "crypto/internal/fips/aes/gcm"
- _ "crypto/internal/fips/drbg"
- _ "crypto/internal/fips/hkdf"
- _ "crypto/internal/fips/hmac"
- "crypto/internal/fips/mlkem"
- _ "crypto/internal/fips/sha256"
- _ "crypto/internal/fips/sha3"
- _ "crypto/internal/fips/sha512"
- _ "crypto/internal/fips/tls12"
- _ "crypto/internal/fips/tls13"
-)
-
-func TestCAST(t *testing.T) {
- if len(fips.AllCASTs) == 0 {
- t.Errorf("no CASTs to test")
- }
-
- // Cause PCTs to be invoked.
- mlkem.GenerateKey768()
-
- if fips.Enabled {
- 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,fips140=on", 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)
- }
-}
}
var enabled bool // set when verification is enabled
-var verified bool // set when verification succeeds, for testing
+var Verified bool // set when verification succeeds, for testing
-// supported reports whether the current GOOS/GOARCH is supported at all.
-func supported() bool {
+// Supported reports whether the current GOOS/GOARCH is Supported at all.
+func Supported() bool {
// See cmd/internal/obj/fips.go's EnableFIPS for commentary.
switch {
case runtime.GOARCH == "wasm",
return true
}
-// linkinfo holds the go:fipsinfo symbol prepared by the linker.
+// Linkinfo holds the go:fipsinfo symbol prepared by the linker.
// See cmd/link/internal/ld/fips.go for details.
//
-//go:linkname linkinfo go:fipsinfo
-var linkinfo struct {
+//go:linkname Linkinfo go:fipsinfo
+var Linkinfo struct {
Magic [16]byte
Sum [32]byte
Self uintptr
panic("fips140: unknown GODEBUG setting fips140=" + v)
}
- if !supported() {
+ if !Supported() {
panic("fips140: unavailable on " + runtime.GOOS + "-" + runtime.GOARCH)
}
- if linkinfo.Magic[0] != 0xff || string(linkinfo.Magic[1:]) != fipsMagic || linkinfo.Sum == zeroSum {
+ if Linkinfo.Magic[0] != 0xff || string(Linkinfo.Magic[1:]) != fipsMagic || Linkinfo.Sum == zeroSum {
panic("fips140: no verification checksum found")
}
w.Write([]byte("go fips object v1\n"))
var nbuf [8]byte
- for _, sect := range linkinfo.Sects {
+ for _, sect := range Linkinfo.Sects {
n := uintptr(sect.End) - uintptr(sect.Start)
byteorder.BePutUint64(nbuf[:], uint64(n))
w.Write(nbuf[:])
}
sum := h.Sum(nil)
- if [32]byte(sum) != linkinfo.Sum {
+ if [32]byte(sum) != Linkinfo.Sum {
panic("fips140: verification mismatch")
}
println("fips140: verified code+data")
}
- verified = true
+ Verified = true
}
+++ /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 check
-
-var Verified = &verified
-var Linkinfo = &linkinfo
-var Supported = supported
+++ /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 drbg_test
-
-import (
- "bytes"
- "crypto/internal/fips/drbg"
- "crypto/internal/fips/subtle"
- "encoding/hex"
- "testing"
-)
-
-func TestCounter(t *testing.T) {
- // https://github.com/usnistgov/ACVP-Server/blob/fb44dce/gen-val/json-files/ctrDRBG-1.0/prompt.json#L4447-L4482
-
- entropyInput := decodeHex("9FCBB4CCC0135C484BDED061DA9FD70748682FE84166B97FF53F9AA1909B2E95D3D529C0F453B3AC575D12AA441CC5CD")
- persoString := decodeHex("2C9FED0B39556CDBE699EBCA2A0EC7EECB287E8744475050C572FA8AE9ED0A4A7D6F1CABF1C4278532FB20AF7D64BD32")
- reseedEntropy := decodeHex("913C0DA19B010EDDD55A7A4F3F713EEF5B1534D34360A7EC376AE71A6B340043CC7726F762CB853453F399B3A645062A")
- reseedAdditional := decodeHex("2D9D4EC141A22E6CD2F6EE4F6719CF6BDF95CFE50B8D5EA6C87D38B4B872706FFF80B0380BB90E9C42D11D6526E56C29")
- additional1 := decodeHex("A642F06D327828F3E84564A3E37D60C157073B95864CA07981B0189668A0D978CD5DC68F06801CEFF0DC839A312B028E")
- additional2 := decodeHex("9DB14BABFA9107C88BA92073C0B4A65E89147EA06D74B894142979482F452915B35B5636F9B8A951759735ADE7C8D5D1")
- returnedBits := decodeHex("F10C645683FF0131254052ED4C698122B46B563654C29D728AC191CA4AAEFE649EEFE4C6FC33B25BB739294DD5CF578099F856C98D98000CBF971F1E6EA900822FF8C110118F6520471744D3F8A3F5C7D568494240E57F5488AF9C9F9F4E7322F56CCD843C0DBFCE9170C02E205389420527F23EDB3369D9FCC5E34901B5BA4EB71B973FC7982FFE0899FF7FE53EE0C4F51A3EF93EF9C6D4D279DD7536F8776BE94AAA05E89EF6E6AEE8832B4B42FFCA5FB91EC0273F9EF945865512889B0C5EE141D1B38DF827D2A694835561628C6F9B093A01A835F07ADBB9E03FEBF93389E8F3B86E1E0ABF1F9958FA286AD995289C2F606D1A9043A166C1AFE8D00769C712650819C9068A4BD22717C98338395A7BA6E95B5178BFBF4EFB0F05A91713BA8BF2127A6BA1EDFA6D1CAB05C03EE0D2AFE1DA4EB8F2C579EC872FF4B602027EF4BDCF2F4B01423F8E600A13D7CACB6AB83263BA58F907694AF614A6724FD0E4C627A0D91DDC6716C697FACE6F4808A4F37B731DE4E0CD4766CEADAAAF47992505299C72AC1A6E9A8335B8D7E501B3841188D0DA4DE5267674444DC2B0CF9F010756FA865A25CA3F1B24C34E845B2259926B6A867A7684DE68A6137C4FB0F47A2E54AE9E6455BEBA0B0A9629644FE9E378EE95386443BA977124FFD1192E9F460684C7B09FA99F5F93F04F56FD7955E042187887CE696F1934017E458B16B5C9")
-
- // We don't support personalization strings, but the pre-generated JSON
- // vectors always use them, so just pre-mix them.
- var seed [drbg.SeedSize]byte
- subtle.XORBytes(seed[:], entropyInput, persoString)
- c := drbg.NewCounter(&seed)
-
- c.Reseed((*[48]byte)(reseedEntropy), (*[48]byte)(reseedAdditional))
-
- buf := make([]byte, len(returnedBits))
- c.Generate(buf, (*[48]byte)(additional1))
-
- c.Generate(buf, (*[48]byte)(additional2))
- if !bytes.Equal(buf, returnedBits) {
- t.Errorf("unexpected output:\n%x\n%x", buf, returnedBits)
- }
-}
-
-func decodeHex(s string) []byte {
- b, err := hex.DecodeString(s)
- if err != nil {
- panic(err)
- }
- return b
-}
var Enabled bool
+var debug bool
+
func init() {
switch godebug.New("#fips140").Value() {
- case "on", "debug", "only":
+ case "on", "only":
+ Enabled = true
+ case "debug":
Enabled = true
+ debug = true
}
}
dk := &DecapsulationKey768{}
kemKeyGen(dk, d, z)
ek := dk.EncapsulationKey()
- c, Ke := kemEncaps(nil, ek, m)
+ c, Ke := ek.EncapsulateInternal(m)
Kd, err := dk.Decapsulate(c)
if err != nil {
return err
"kemDecaps": "kemDecaps1024",
"pkeDecrypt": "pkeDecrypt1024",
- "GenerateKey768": "GenerateKey1024",
- "generateKey": "generateKey1024",
+ "GenerateKey768": "GenerateKey1024",
+ "GenerateKeyInternal768": "GenerateKeyInternal1024",
+ "generateKey": "generateKey1024",
"kemKeyGen": "kemKeyGen1024",
"kemPCT": "kemPCT1024",
drbg.Read(z[:])
kemKeyGen1024(dk, &d, &z)
fips.CAST("ML-KEM PCT", func() error { return kemPCT1024(dk) })
+ fips.RecordApproved()
return dk, nil
}
+// GenerateKeyInternal1024 is a derandomized version of GenerateKey1024,
+// exclusively for use in tests.
+func GenerateKeyInternal1024(d, z *[32]byte) *DecapsulationKey1024 {
+ dk := &DecapsulationKey1024{}
+ kemKeyGen1024(dk, d, z)
+ return dk
+}
+
// NewDecapsulationKey1024 parses a decapsulation key from a 64-byte
// seed in the "d || z" form. The seed must be uniformly random.
func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) {
z := (*[32]byte)(seed[32:])
kemKeyGen1024(dk, d, z)
fips.CAST("ML-KEM PCT", func() error { return kemPCT1024(dk) })
+ fips.RecordApproved()
return dk, nil
}
// K-PKE.KeyGen according to FIPS 203, Algorithm 13. The two are merged to save
// copies and allocations.
func kemKeyGen1024(dk *DecapsulationKey1024, d, z *[32]byte) {
- fips.RecordApproved()
-
dk.d = *d
dk.z = *z
drbg.Read(m[:])
// Note that the modulus check (step 2 of the encapsulation key check from
// FIPS 203, Section 7.2) is performed by polyByteDecode in parseEK1024.
+ fips.RecordApproved()
return kemEncaps1024(cc, ek, &m)
}
+// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for
+// use in tests.
+func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (ciphertext, sharedKey []byte) {
+ cc := &[CiphertextSize1024]byte{}
+ return kemEncaps1024(cc, ek, m)
+}
+
// kemEncaps1024 generates a shared key and an associated ciphertext.
//
// It implements ML-KEM.Encaps_internal according to FIPS 203, Algorithm 17.
func kemEncaps1024(cc *[CiphertextSize1024]byte, ek *EncapsulationKey1024, m *[messageSize]byte) (c, K []byte) {
- fips.RecordApproved()
- if cc == nil {
- cc = &[CiphertextSize1024]byte{}
- }
-
g := sha3.New512()
g.Write(m[:])
g.Write(ek.h[:])
drbg.Read(z[:])
kemKeyGen(dk, &d, &z)
fips.CAST("ML-KEM PCT", func() error { return kemPCT(dk) })
+ fips.RecordApproved()
return dk, nil
}
+// GenerateKeyInternal768 is a derandomized version of GenerateKey768,
+// exclusively for use in tests.
+func GenerateKeyInternal768(d, z *[32]byte) *DecapsulationKey768 {
+ dk := &DecapsulationKey768{}
+ kemKeyGen(dk, d, z)
+ return dk
+}
+
// NewDecapsulationKey768 parses a decapsulation key from a 64-byte
// seed in the "d || z" form. The seed must be uniformly random.
func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) {
z := (*[32]byte)(seed[32:])
kemKeyGen(dk, d, z)
fips.CAST("ML-KEM PCT", func() error { return kemPCT(dk) })
+ fips.RecordApproved()
return dk, nil
}
// K-PKE.KeyGen according to FIPS 203, Algorithm 13. The two are merged to save
// copies and allocations.
func kemKeyGen(dk *DecapsulationKey768, d, z *[32]byte) {
- fips.RecordApproved()
-
dk.d = *d
dk.z = *z
drbg.Read(m[:])
// Note that the modulus check (step 2 of the encapsulation key check from
// FIPS 203, Section 7.2) is performed by polyByteDecode in parseEK.
+ fips.RecordApproved()
return kemEncaps(cc, ek, &m)
}
+// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for
+// use in tests.
+func (ek *EncapsulationKey768) EncapsulateInternal(m *[32]byte) (ciphertext, sharedKey []byte) {
+ cc := &[CiphertextSize768]byte{}
+ return kemEncaps(cc, ek, m)
+}
+
// kemEncaps generates a shared key and an associated ciphertext.
//
// It implements ML-KEM.Encaps_internal according to FIPS 203, Algorithm 17.
func kemEncaps(cc *[CiphertextSize768]byte, ek *EncapsulationKey768, m *[messageSize]byte) (c, K []byte) {
- fips.RecordApproved()
- if cc == nil {
- cc = &[CiphertextSize768]byte{}
- }
-
g := sha3.New512()
g.Write(m[:])
g.Write(ek.h[:])
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+package fipstest
+
// A module wrapper adapting the Go FIPS module to the protocol used by the
// BoringSSL project's `acvptool`.
//
// and module wrappers.
//
// [0]:https://boringssl.googlesource.com/boringssl/+/refs/heads/master/util/fipstools/acvp/ACVP.md#testing-other-fips-modules
-package fips_test
import (
"bufio"
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package alias
+package fipstest
-import "testing"
+import (
+ "crypto/internal/fips/alias"
+ "testing"
+)
var a, b [100]byte
}
func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
- any := AnyOverlap(x, y)
+ any := alias.AnyOverlap(x, y)
if any != anyOverlap {
t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
}
- inexact := InexactOverlap(x, y)
+ inexact := alias.InexactOverlap(x, y)
if inexact != inexactOverlap {
t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
}
--- /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 fipstest
+
+import (
+ "fmt"
+ "internal/testenv"
+ "io/fs"
+ "os"
+ "regexp"
+ "strings"
+ "testing"
+
+ // Import packages that define CASTs to test them.
+ _ "crypto/internal/fips/aes"
+ _ "crypto/internal/fips/aes/gcm"
+ _ "crypto/internal/fips/drbg"
+ _ "crypto/internal/fips/hkdf"
+ _ "crypto/internal/fips/hmac"
+ "crypto/internal/fips/mlkem"
+ _ "crypto/internal/fips/sha256"
+ _ "crypto/internal/fips/sha3"
+ _ "crypto/internal/fips/sha512"
+ _ "crypto/internal/fips/tls12"
+ _ "crypto/internal/fips/tls13"
+)
+
+func findAllCASTs(t *testing.T) map[string]struct{} {
+ testenv.MustHaveSource(t)
+
+ // Ask "go list" for the location of the crypto/internal/fips tree, as it
+ // might be the unpacked frozen tree selected with GOFIPS140.
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "list", "-f", `{{.Dir}}`, "crypto/internal/fips")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("go list: %v\n%s", err, out)
+ }
+ fipsDir := strings.TrimSpace(string(out))
+ t.Logf("FIPS module directory: %s", fipsDir)
+
+ // Find all invocations of fips.CAST.
+ allCASTs := make(map[string]struct{})
+ castRe := regexp.MustCompile(`fips\.CAST\("([^"]+)"`)
+ if err := fs.WalkDir(os.DirFS(fipsDir), ".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() || !strings.HasSuffix(path, ".go") {
+ return nil
+ }
+ data, err := os.ReadFile(fipsDir + "/" + path)
+ if err != nil {
+ return err
+ }
+ for _, m := range castRe.FindAllSubmatch(data, -1) {
+ allCASTs[string(m[1])] = struct{}{}
+ }
+ return nil
+ }); err != nil {
+ t.Fatalf("WalkDir: %v", err)
+ }
+
+ return allCASTs
+}
+
+// TestPCTs causes the conditional PCTs to be invoked.
+func TestPCTs(t *testing.T) {
+ mlkem.GenerateKey768()
+ t.Log("completed successfully")
+}
+
+func TestCASTFailures(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ allCASTs := findAllCASTs(t)
+ if len(allCASTs) == 0 {
+ t.Fatal("no CASTs found")
+ }
+
+ for name := range allCASTs {
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+ cmd := testenv.Command(t, testenv.Executable(t), "-test.run=TestPCTs", "-test.v")
+ cmd = testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s,fips140=on", 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)
+ }
+ })
+ }
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package check_test
+package fipstest
import (
. "crypto/internal/fips/check"
"internal/abi"
"internal/asan"
"internal/godebug"
+ "internal/testenv"
"os"
- "os/exec"
"runtime"
"testing"
"unicode"
const enableFIPSTest = true
-func TestVerify(t *testing.T) {
- if *Verified {
+func TestFIPSCheckVerify(t *testing.T) {
+ if Verified {
t.Logf("verified")
return
}
return
}
- cmd := exec.Command(os.Args[0], "-test.v")
+ cmd := testenv.Command(t, os.Args[0], "-test.v", "-test.run=TestFIPSCheck")
cmd.Env = append(cmd.Environ(), "GODEBUG=fips140=on")
out, err := cmd.CombinedOutput()
if err != nil {
t.Logf("exec'ed GODEBUG=fips140=on and succeeded:\n%s", out)
}
-func TestInfo(t *testing.T) {
+func TestFIPSCheckInfo(t *testing.T) {
if !enableFIPSTest {
return
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gcm_test
+package fipstest
import (
"bytes"
"crypto/internal/fips/aes"
"crypto/internal/fips/aes/gcm"
- "encoding/hex"
- "strings"
"testing"
)
}
}
}
-
-func decodeHex(t *testing.T, s string) []byte {
- t.Helper()
- s = strings.ReplaceAll(s, " ", "")
- b, err := hex.DecodeString(s)
- if err != nil {
- t.Fatal(err)
- }
- return b
-}
--- /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 fipstest
+
+import (
+ "bytes"
+ "crypto/internal/fips/drbg"
+ "crypto/internal/fips/subtle"
+ "testing"
+)
+
+func TestCounterDRBG(t *testing.T) {
+ // https://github.com/usnistgov/ACVP-Server/blob/fb44dce/gen-val/json-files/ctrDRBG-1.0/prompt.json#L4447-L4482
+
+ entropyInput := decodeHex(t, "9FCBB4CCC0135C484BDED061DA9FD70748682FE84166B97FF53F9AA1909B2E95D3D529C0F453B3AC575D12AA441CC5CD")
+ persoString := decodeHex(t, "2C9FED0B39556CDBE699EBCA2A0EC7EECB287E8744475050C572FA8AE9ED0A4A7D6F1CABF1C4278532FB20AF7D64BD32")
+ reseedEntropy := decodeHex(t, "913C0DA19B010EDDD55A7A4F3F713EEF5B1534D34360A7EC376AE71A6B340043CC7726F762CB853453F399B3A645062A")
+ reseedAdditional := decodeHex(t, "2D9D4EC141A22E6CD2F6EE4F6719CF6BDF95CFE50B8D5EA6C87D38B4B872706FFF80B0380BB90E9C42D11D6526E56C29")
+ additional1 := decodeHex(t, "A642F06D327828F3E84564A3E37D60C157073B95864CA07981B0189668A0D978CD5DC68F06801CEFF0DC839A312B028E")
+ additional2 := decodeHex(t, "9DB14BABFA9107C88BA92073C0B4A65E89147EA06D74B894142979482F452915B35B5636F9B8A951759735ADE7C8D5D1")
+ returnedBits := decodeHex(t, "F10C645683FF0131254052ED4C698122B46B563654C29D728AC191CA4AAEFE649EEFE4C6FC33B25BB739294DD5CF578099F856C98D98000CBF971F1E6EA900822FF8C110118F6520471744D3F8A3F5C7D568494240E57F5488AF9C9F9F4E7322F56CCD843C0DBFCE9170C02E205389420527F23EDB3369D9FCC5E34901B5BA4EB71B973FC7982FFE0899FF7FE53EE0C4F51A3EF93EF9C6D4D279DD7536F8776BE94AAA05E89EF6E6AEE8832B4B42FFCA5FB91EC0273F9EF945865512889B0C5EE141D1B38DF827D2A694835561628C6F9B093A01A835F07ADBB9E03FEBF93389E8F3B86E1E0ABF1F9958FA286AD995289C2F606D1A9043A166C1AFE8D00769C712650819C9068A4BD22717C98338395A7BA6E95B5178BFBF4EFB0F05A91713BA8BF2127A6BA1EDFA6D1CAB05C03EE0D2AFE1DA4EB8F2C579EC872FF4B602027EF4BDCF2F4B01423F8E600A13D7CACB6AB83263BA58F907694AF614A6724FD0E4C627A0D91DDC6716C697FACE6F4808A4F37B731DE4E0CD4766CEADAAAF47992505299C72AC1A6E9A8335B8D7E501B3841188D0DA4DE5267674444DC2B0CF9F010756FA865A25CA3F1B24C34E845B2259926B6A867A7684DE68A6137C4FB0F47A2E54AE9E6455BEBA0B0A9629644FE9E378EE95386443BA977124FFD1192E9F460684C7B09FA99F5F93F04F56FD7955E042187887CE696F1934017E458B16B5C9")
+
+ // We don't support personalization strings, but the pre-generated JSON
+ // vectors always use them, so just pre-mix them.
+ var seed [drbg.SeedSize]byte
+ subtle.XORBytes(seed[:], entropyInput, persoString)
+ c := drbg.NewCounter(&seed)
+
+ c.Reseed((*[48]byte)(reseedEntropy), (*[48]byte)(reseedAdditional))
+
+ buf := make([]byte, len(returnedBits))
+ c.Generate(buf, (*[48]byte)(additional1))
+
+ c.Generate(buf, (*[48]byte)(additional2))
+ if !bytes.Equal(buf, returnedBits) {
+ t.Errorf("unexpected output:\n%x\n%x", buf, returnedBits)
+ }
+}
--- /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 fipstest collects external tests that would ordinarily live in
+// crypto/internal/fips/... packages. That tree gets snapshot at each
+// validation, while we want tests to evolve and still apply to all versions of
+// the module. Also, we can't fix failing tests in a module snapshot, so we need
+// to either minimize, skip, or remove them. Finally, the module needs to avoid
+// importing internal packages like testenv and cryptotest to avoid locking in
+// their APIs.
+package fipstest
+
+import (
+ "encoding/hex"
+ "strings"
+ "testing"
+)
+
+func decodeHex(t *testing.T, s string) []byte {
+ t.Helper()
+ s = strings.ReplaceAll(s, " ", "")
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return b
+}
// Copyright 2014 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 hkdf_test
+
+package fipstest_test
+
+// TODO(fips, #61477): move this to crypto/hkdf once it exists.
import (
"bytes"
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package fips_test
+package fipstest
import (
"crypto/internal/fips"
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package mlkem
+package fipstest_test
+
+// TODO(fips, #70122): move this to crypto/mlkem once it exists.
import (
"bytes"
+ . "crypto/internal/fips/mlkem"
"crypto/internal/fips/sha3"
"crypto/rand"
_ "embed"
s := sha3.NewShake128()
o := sha3.NewShake128()
seed := make([]byte, SeedSize)
- var msg [messageSize]byte
+ var msg [32]byte
ct1 := make([]byte, CiphertextSize768)
for i := 0; i < n; i++ {
o.Write(ek.Bytes())
s.Read(msg[:])
- ct, k := kemEncaps(nil, ek, &msg)
+ ct, k := ek.EncapsulateInternal(&msg)
o.Write(ct)
o.Write(k)
var sink byte
func BenchmarkKeyGen(b *testing.B) {
- var dk DecapsulationKey768
var d, z [32]byte
rand.Read(d[:])
rand.Read(z[:])
b.ResetTimer()
for i := 0; i < b.N; i++ {
- kemKeyGen(&dk, &d, &z)
+ dk := GenerateKeyInternal768(&d, &z)
sink ^= dk.EncapsulationKey().Bytes()[0]
}
}
func BenchmarkEncaps(b *testing.B) {
seed := make([]byte, SeedSize)
rand.Read(seed)
- var m [messageSize]byte
+ var m [32]byte
rand.Read(m[:])
dk, err := NewDecapsulationKey768(seed)
if err != nil {
b.Fatal(err)
}
ekBytes := dk.EncapsulationKey().Bytes()
- var c [CiphertextSize768]byte
b.ResetTimer()
for i := 0; i < b.N; i++ {
ek, err := NewEncapsulationKey768(ekBytes)
if err != nil {
b.Fatal(err)
}
- c, K := kemEncaps(&c, ek, &m)
+ c, K := ek.EncapsulateInternal(&m)
sink ^= c[0] ^ K[0]
}
}
c, _ := ek.Encapsulate()
b.ResetTimer()
for i := 0; i < b.N; i++ {
- K := kemDecaps(dk, (*[CiphertextSize768]byte)(c))
+ K, _ := dk.Decapsulate(c)
sink ^= K[0]
}
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package sha3_test
+package fipstest_test
+
+// TODO(fips, #69982): move to the crypto/sha3 package once it exists.
import (
"bytes"
"testing"
)
-// TODO(fips): move tests to the stdlib crypto/sha3 package.
-
// Sum224 returns the SHA3-224 digest of the data.
func Sum224(data []byte) (digest [28]byte) {
h := New224()
}
}
-var sink byte
+var sinkSHA3 byte
func TestAllocations(t *testing.T) {
cryptotest.SkipTestAllocations(t)
h.Write(b)
out := make([]byte, 0, 32)
out = h.Sum(out)
- sink ^= out[0]
+ sinkSHA3 ^= out[0]
}); allocs > 0 {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
h.Write(b)
out := make([]byte, 0, 32)
out = h.Sum(out)
- sink ^= out[0]
+ sinkSHA3 ^= out[0]
h.Read(out)
- sink ^= out[0]
+ sinkSHA3 ^= out[0]
}); allocs > 0 {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
if allocs := testing.AllocsPerRun(10, func() {
b := []byte("ABC")
out := Sum256(b)
- sink ^= out[0]
+ sinkSHA3 ^= out[0]
}); allocs > 0 {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package ssh_test
+package fipstest
import (
"bytes"
"testing"
)
-func TestACVPVector(t *testing.T) {
+func TestSSHACVPVector(t *testing.T) {
// https://github.com/usnistgov/ACVP-Server/blob/3a7333f638/gen-val/json-files/kdf-components-ssh-1.0/prompt.json#L910-L915
K := fromHex("0000010100E534CD9780786AF19994DD68C3FD7FE1E1F77C3938B2005C49B080CF88A63A44079774A36F23BA4D73470CB318C30524854D2F36BAB9A45AD73DBB3BC5DD39A547F62BC921052E102E37F3DD0CD79A04EB46ACC14B823B326096A89E33E8846624188BB3C8F16B320E7BB8F5EB05F080DCEE244A445DBED3A9F3BA8C373D8BE62CDFE2FC5876F30F90F01F0A55E5251B23E0DBBFCFB1450715E329BB00FB222E850DDB11201460B8AEF3FC8965D3B6D3AFBB885A6C11F308F10211B82EA2028C7A84DD0BB8D5D6AC3A48D0C2B93609269C585E03889DB3621993E7F7C09A007FB6B5C06FFA532B0DBF11F71F740D9CD8FAD2532E21B9423BF3D85EE4E396BE32")
H := fromHex("8FB22F0864960DA5679FD377248E41C2D0390E5AB3BB7955A3B6C588FB75B20D")
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gcm_test
+package fipstest
import (
"bytes"
"testing"
)
-func TestAllocations(t *testing.T) {
+func TestXAESAllocations(t *testing.T) {
if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
t.Skip("Test reports non-zero allocation count. See issue #70448")
}
return g.Open(dst, n, ciphertext, additionalData)
}
-func TestVectors(t *testing.T) {
+func TestXAESVectors(t *testing.T) {
key := bytes.Repeat([]byte{0x01}, 32)
nonce := []byte("ABCDEFGHIJKLMNOPQRSTUVWX")
plaintext := []byte("XAES-256-GCM")
}
}
-func TestAccumulated(t *testing.T) {
+func TestXAESAccumulated(t *testing.T) {
iterations := 10_000
expected := "e6b9edf2df6cec60c8cbd864e2211b597fb69a529160cd040d56c0c210081939"