blob: 97552dc1ce10f1ee0d3568c5bea8874436dfc2c2 [file] [log] [blame] [edit]
// 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 fipsdeps
import (
"internal/testenv"
"strings"
"testing"
)
// AllowedInternalPackages are internal packages that can be imported from the
// FIPS module. The API of these packages ends up locked for the lifetime of the
// validated module, which can be years.
//
// DO NOT add new packages here just to make the tests pass.
var AllowedInternalPackages = map[string]bool{
// entropy.Depleted is the external passive entropy source, and sysrand.Read
// is the actual (but uncredited!) random bytes source.
"crypto/internal/entropy": true,
"crypto/internal/sysrand": true,
// impl.Register is how the packages expose their alternative
// implementations to tests outside the module.
"crypto/internal/impl": true,
// randutil.MaybeReadByte is used in non-FIPS mode by GenerateKey functions.
"crypto/internal/randutil": true,
}
func TestImports(t *testing.T) {
cmd := testenv.Command(t, testenv.GoToolPath(t), "list", "-f", `{{$path := .ImportPath -}}
{{range .Imports -}}
{{$path}} {{.}}
{{end -}}
{{range .TestImports -}}
{{$path}} {{.}}
{{end -}}
{{range .XTestImports -}}
{{$path}} {{.}}
{{end -}}`, "crypto/internal/fips140/...")
bout, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("go list: %v\n%s", err, bout)
}
out := string(bout)
// In a snapshot, all the paths are crypto/internal/fips140/v1.2.3/...
// Determine the version number and remove it for the test.
_, v, _ := strings.Cut(out, "crypto/internal/fips140/")
v, _, _ = strings.Cut(v, "/")
v, _, _ = strings.Cut(v, " ")
if strings.HasPrefix(v, "v") && strings.Count(v, ".") == 2 {
out = strings.ReplaceAll(out, "crypto/internal/fips140/"+v, "crypto/internal/fips140")
}
allPackages := make(map[string]bool)
// importCheck is the set of packages that import crypto/internal/fips140/check.
importCheck := make(map[string]bool)
for _, line := range strings.Split(out, "\n") {
if line == "" {
continue
}
pkg, importedPkg, _ := strings.Cut(line, " ")
allPackages[pkg] = true
if importedPkg == "crypto/internal/fips140/check" {
importCheck[pkg] = true
}
// Ensure we don't import any unexpected internal package from the FIPS
// module, since we can't change the module source after it starts
// validation. This locks in the API of otherwise internal packages.
if importedPkg == "crypto/internal/fips140" ||
strings.HasPrefix(importedPkg, "crypto/internal/fips140/") ||
strings.HasPrefix(importedPkg, "crypto/internal/fips140deps/") {
continue
}
if AllowedInternalPackages[importedPkg] {
continue
}
if strings.Contains(importedPkg, "internal") {
t.Errorf("unexpected import of internal package: %s -> %s", pkg, importedPkg)
}
}
// Ensure that all packages except check, check's dependencies, and the
// entropy source (which is used only from .../fips140/drbg) import check.
for pkg := range allPackages {
switch pkg {
case "crypto/internal/fips140/check":
case "crypto/internal/fips140":
case "crypto/internal/fips140/alias":
case "crypto/internal/fips140/subtle":
case "crypto/internal/fips140/hmac":
case "crypto/internal/fips140/sha3":
case "crypto/internal/fips140/sha256":
case "crypto/internal/fips140/sha512":
case "crypto/internal/fips140/entropy":
default:
if !importCheck[pkg] {
t.Errorf("package %s does not import crypto/internal/fips140/check", pkg)
}
}
}
}