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