1
2
3
4
5 package fipstest
6
7 import (
8 "bytes"
9 "crypto/internal/cryptotest"
10 . "crypto/internal/fips140/check"
11 "crypto/internal/fips140/check/checktest"
12 "fmt"
13 "internal/abi"
14 "internal/godebug"
15 "internal/testenv"
16 "os"
17 "os/exec"
18 "path/filepath"
19 "runtime"
20 "testing"
21 "unicode"
22 "unsafe"
23 )
24
25 func TestIntegrityCheck(t *testing.T) {
26 if Verified {
27 t.Logf("verified")
28 return
29 }
30
31 if godebug.New("fips140").Value() == "on" {
32 t.Fatalf("GODEBUG=fips140=on but verification did not run")
33 }
34
35 cryptotest.RerunWithFIPS140Enabled(t)
36 }
37
38 func TestIntegrityCheckFailure(t *testing.T) {
39 moduleStatus(t)
40 cryptotest.MustSupportFIPS140(t)
41
42 t.Logf("running modified binary...")
43 cmd := reexecCommand(t, "fips140=on", true, "-test.v", "-test.run=^TestIntegrityCheck$")
44 out, err := cmd.CombinedOutput()
45 t.Logf("running with GODEBUG=fips140=on:\n%s", out)
46 if err == nil {
47 t.Errorf("modified binary did not fail as expected")
48 }
49 if !bytes.Contains(out, []byte("fips140: verification mismatch")) {
50 t.Errorf("modified binary did not fail with expected message")
51 }
52 if bytes.Contains(out, []byte("verified")) {
53 t.Errorf("modified binary did not exit")
54 }
55 }
56
57
58
59
60 var browserBridge = os.Getenv("GOBROWSERBRIDGE")
61
62 func reexecCommand(t *testing.T, godebug string, corrupt bool, args ...string) *exec.Cmd {
63 if browserBridge == "" {
64 exe := testenv.Executable(t)
65 if corrupt {
66 exe = corruptExecutable(t)
67 }
68 cmd := testenv.Command(t, exe, args...)
69 cmd.Env = append(cmd.Environ(), "GODEBUG="+godebug)
70 return cmd
71 }
72
73 bridgeArgs := []string{"-run"}
74 if corrupt {
75 bridgeArgs = append(bridgeArgs, "-corrupt")
76 }
77 bridgeArgs = append(append(bridgeArgs, "--"), args...)
78 cmd := testenv.Command(t, browserBridge, bridgeArgs...)
79 cmd.Env = append(cmd.Environ(), "GODEBUG="+godebug)
80 return cmd
81 }
82
83 func corruptExecutable(t *testing.T) string {
84 bin, err := os.ReadFile(testenv.Executable(t))
85 if err != nil {
86 t.Fatal(err)
87 }
88
89
90 bin = bytes.ReplaceAll(bin, Linkinfo.Sum[:], bytes.Repeat([]byte("X"), len(Linkinfo.Sum)))
91
92 binPath := filepath.Join(t.TempDir(), "fips140test.exe")
93 if err := os.WriteFile(binPath, bin, 0o755); err != nil {
94 t.Fatal(err)
95 }
96
97 if runtime.GOOS == "darwin" {
98
99 cmd := testenv.Command(t, "codesign", "-s", "-", "-f", binPath)
100 out, err := cmd.CombinedOutput()
101 if err != nil {
102 t.Fatalf("codesign failed: %v\n%s", err, out)
103 }
104 }
105
106 return binPath
107 }
108
109 func TestIntegrityCheckInfo(t *testing.T) {
110 cryptotest.MustSupportFIPS140(t)
111
112
113 if checktest.NOPTRDATA != 1 {
114 t.Errorf("checktest.NOPTRDATA = %d, want 1", checktest.NOPTRDATA)
115 }
116 if checktest.RODATA != 2 {
117 t.Errorf("checktest.RODATA = %d, want 2", checktest.RODATA)
118 }
119 if checktest.DATA.P != &checktest.NOPTRDATA {
120 t.Errorf("checktest.DATA.P = %p, want &checktest.NOPTRDATA (%p)", checktest.DATA.P, &checktest.NOPTRDATA)
121 }
122 if checktest.DATA.X != 3 {
123 t.Errorf("checktest.DATA.X = %d, want 3", checktest.DATA.X)
124 }
125 if checktest.NOPTRBSS != 0 {
126 t.Errorf("checktest.NOPTRBSS = %d, want 0", checktest.NOPTRBSS)
127 }
128 if checktest.BSS != nil {
129 t.Errorf("checktest.BSS = %p, want nil", checktest.BSS)
130 }
131 if p := checktest.PtrStaticData(); p != nil && *p != 10 {
132 t.Errorf("*checktest.PtrStaticData() = %d, want 10", *p)
133 }
134
135
136 sect := func(i int, name string, p unsafe.Pointer) {
137 s := Linkinfo.Sects[i]
138 if !(uintptr(s.Start) <= uintptr(p) && uintptr(p) < uintptr(s.End)) {
139 t.Errorf("checktest.%s (%#x) not in section #%d (%#x..%#x)", name, p, i, s.Start, s.End)
140 }
141 }
142 sect(0, "TEXT", unsafe.Pointer(abi.FuncPCABIInternal(checktest.TEXT)))
143 if p := checktest.PtrStaticText(); p != nil {
144 sect(0, "StaticText", p)
145 }
146 sect(1, "RODATA", unsafe.Pointer(&checktest.RODATA))
147 sect(2, "NOPTRDATA", unsafe.Pointer(&checktest.NOPTRDATA))
148 if p := checktest.PtrStaticData(); p != nil {
149 sect(2, "StaticData", unsafe.Pointer(p))
150 }
151 sect(3, "DATA", unsafe.Pointer(&checktest.DATA))
152
153
154 no := func(name string, p unsafe.Pointer, ix ...int) {
155 for _, i := range ix {
156 s := Linkinfo.Sects[i]
157 if uintptr(s.Start) <= uintptr(p) && uintptr(p) < uintptr(s.End) {
158 t.Errorf("%s (%#x) unexpectedly in section #%d (%#x..%#x)", name, p, i, s.Start, s.End)
159 }
160 }
161 }
162
163
164 no("checktest.TEXT", unsafe.Pointer(abi.FuncPCABIInternal(checktest.TEXT)), 1, 2, 3)
165 no("checktest.RODATA", unsafe.Pointer(&checktest.RODATA), 0, 2, 3)
166 no("checktest.NOPTRDATA", unsafe.Pointer(&checktest.NOPTRDATA), 0, 1, 3)
167 no("checktest.DATA", unsafe.Pointer(&checktest.DATA), 0, 1, 2)
168
169
170 no("fmt.Printf", unsafe.Pointer(abi.FuncPCABIInternal(fmt.Printf)), 0, 1, 2, 3)
171 no("unicode.Categories", unsafe.Pointer(&unicode.Categories), 0, 1, 2, 3)
172 no("unicode.ASCII_Hex_Digit", unsafe.Pointer(&unicode.ASCII_Hex_Digit), 0, 1, 2, 3)
173
174
175
176 n := uintptr(0)
177 for _, s := range Linkinfo.Sects {
178 n += uintptr(s.End) - uintptr(s.Start)
179 }
180 if n < 16*1024 {
181 t.Fatalf("fips sections not big enough: %d, want at least 16 kB", n)
182 }
183 }
184
View as plain text