54
54
// of crypto/internal/fips with an earlier snapshot. The reason to do
55
55
// this is to use a copy that has been through additional lab validation
56
56
// (an "in-process" module) or NIST certification (a "certified" module).
57
- // This functionality is not yet implemented.
57
+ // The snapshots are stored in GOROOT/lib/fips140 in module zip form.
58
+ // When a snapshot is being used, Init unpacks it into the module cache
59
+ // and then uses that directory as the source location.
60
+ //
61
+ // A FIPS snapshot like v1.2.3 is integrated into the build in two different ways.
62
+ //
63
+ // First, the snapshot's fips140 directory replaces crypto/internal/fips
64
+ // using fsys.Bind. The effect is to appear to have deleted crypto/internal/fips
65
+ // and everything below it, replacing it with the single subdirectory
66
+ // crypto/internal/fips/v1.2.3, which now has the FIPS packages.
67
+ // This virtual file system replacement makes patterns like std and crypto...
68
+ // automatically see the snapshot packages instead of the original packages
69
+ // as they walk GOROOT/src/crypto/internal/fips.
70
+ //
71
+ // Second, ResolveImport is called to resolve an import like crypto/internal/fips/sha256.
72
+ // When snapshot v1.2.3 is being used, ResolveImport translates that path to
73
+ // crypto/internal/fips/v1.2.3/sha256 and returns the actual source directory
74
+ // in the unpacked snapshot. Using the actual directory instead of the
75
+ // virtual directory GOROOT/src/crypto/internal/fips/v1.2.3 makes sure
76
+ // that other tools using go list -json output can find the sources,
77
+ // as well as making sure builds have a real directory in which to run the
78
+ // assembler, compiler, and so on. The translation of the import path happens
79
+ // in the same code that handles mapping golang.org/x/mod to
80
+ // cmd/vendor/golang.org/x/mod when building commands.
81
+ //
82
+ // It is not strictly required to include v1.2.3 in the import path when using
83
+ // a snapshot - we could make things work without doing that - but including
84
+ // the v1.2.3 gives a different version of the code a different name, which is
85
+ // always a good general rule. In particular, it will mean that govulncheck need
86
+ // not have any special cases for crypto/internal/fips at all. The reports simply
87
+ // need to list the relevant symbols in a given Go version. (For example, if a bug
88
+ // is only in the in-tree copy but not the snapshots, it doesn't list the snapshot
89
+ // symbols; if it's in any snapshots, it has to list the specific snapshot symbols
90
+ // in addition to the “normal” symbol.)
91
+ //
92
+ // TODO: crypto/internal/fips is going to move to crypto/internal/fips140,
93
+ // at which point all the crypto/internal/fips references need to be updated.
58
94
package fips
59
95
60
96
import (
61
97
"cmd/go/internal/base"
62
98
"cmd/go/internal/cfg"
99
+ "cmd/go/internal/fsys"
100
+ "cmd/go/internal/modfetch"
101
+ "cmd/go/internal/str"
102
+ "context"
103
+ "os"
104
+ "path"
105
+ "path/filepath"
106
+ "strings"
107
+
108
+ "golang.org/x/mod/module"
109
+ "golang.org/x/mod/semver"
63
110
)
64
111
65
112
// Init initializes the FIPS settings.
@@ -71,6 +118,10 @@ func Init() {
71
118
}
72
119
initDone = true
73
120
initVersion ()
121
+ initDir ()
122
+ if Snapshot () {
123
+ fsys .Bind (Dir (), filepath .Join (cfg .GOROOT , "src/crypto/internal/fips" ))
124
+ }
74
125
}
75
126
76
127
var initDone bool
@@ -120,5 +171,85 @@ func initVersion() {
120
171
return
121
172
}
122
173
174
+ // Otherwise version must exist in lib/fips140, either as
175
+ // a .zip (a source snapshot like v1.2.0.zip)
176
+ // or a .txt (a redirect like inprocess.txt, containing a version number).
177
+ if strings .Contains (v , "/" ) || strings .Contains (v , `\` ) || strings .Contains (v , ".." ) {
178
+ base .Fatalf ("go: malformed GOFIPS140 version %q" , cfg .GOFIPS140 )
179
+ }
180
+ if cfg .GOROOT == "" {
181
+ base .Fatalf ("go: missing GOROOT for GOFIPS140" )
182
+ }
183
+
184
+ file := filepath .Join (cfg .GOROOT , "lib" , "fips140" , v )
185
+ if data , err := os .ReadFile (file + ".txt" ); err == nil {
186
+ v = strings .TrimSpace (string (data ))
187
+ file = filepath .Join (cfg .GOROOT , "lib" , "fips140" , v )
188
+ if _ , err := os .Stat (file + ".zip" ); err != nil {
189
+ base .Fatalf ("go: unknown GOFIPS140 version %q (from %q)" , v , cfg .GOFIPS140 )
190
+ }
191
+ }
192
+
193
+ if _ , err := os .Stat (file + ".zip" ); err == nil {
194
+ // Found version. Add a build tag.
195
+ cfg .BuildContext .BuildTags = append (cfg .BuildContext .BuildTags , "fips140" + semver .MajorMinor (v ))
196
+ version = v
197
+ return
198
+ }
199
+
123
200
base .Fatalf ("go: unknown GOFIPS140 version %q" , v )
124
201
}
202
+
203
+ // Dir reports the directory containing the crypto/internal/fips source code.
204
+ // If Snapshot() is false, Dir returns GOROOT/src/crypto/internal/fips.
205
+ // Otherwise Dir ensures that the snapshot has been unpacked into the
206
+ // module cache and then returns the directory in the module cache
207
+ // corresponding to the crypto/internal/fips directory.
208
+ func Dir () string {
209
+ checkInit ()
210
+ return dir
211
+ }
212
+
213
+ var dir string
214
+
215
+ func initDir () {
216
+ v := version
217
+ if v == "latest" || v == "off" {
218
+ dir = filepath .Join (cfg .GOROOT , "src/crypto/internal/fips" )
219
+ return
220
+ }
221
+
222
+ mod := module.Version {Path : "golang.org/fips140" , Version : v }
223
+ file := filepath .Join (cfg .GOROOT , "lib/fips140" , v + ".zip" )
224
+ zdir , err := modfetch .Unzip (context .Background (), mod , file )
225
+ if err != nil {
226
+ base .Fatalf ("go: unpacking GOFIPS140=%v: %v" , v , err )
227
+ }
228
+ dir = filepath .Join (zdir , "fips140" )
229
+ return
230
+ }
231
+
232
+ // ResolveImport resolves the import path imp.
233
+ // If it is of the form crypto/internal/fips/foo
234
+ // (not crypto/internal/fips/v1.2.3/foo)
235
+ // and we are using a snapshot, then LookupImport
236
+ // rewrites the path to crypto/internal/fips/v1.2.3/foo
237
+ // and returns that path and its location in the unpacked
238
+ // FIPS snapshot.
239
+ func ResolveImport (imp string ) (newPath , dir string , ok bool ) {
240
+ checkInit ()
241
+ const fips = "crypto/internal/fips"
242
+ if ! Snapshot () || ! str .HasPathPrefix (imp , fips ) {
243
+ return "" , "" , false
244
+ }
245
+ fipsv := path .Join (fips , version )
246
+ var sub string
247
+ if str .HasPathPrefix (imp , fipsv ) {
248
+ sub = "." + imp [len (fipsv ):]
249
+ } else {
250
+ sub = "." + imp [len (fips ):]
251
+ }
252
+ newPath = path .Join (fips , version , sub )
253
+ dir = filepath .Join (Dir (), version , sub )
254
+ return newPath , dir , true
255
+ }
0 commit comments