Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/go: add fips140 module selection mechanism #70200

Open
rsc opened this issue Nov 5, 2024 · 52 comments
Open

cmd/go: add fips140 module selection mechanism #70200

rsc opened this issue Nov 5, 2024 · 52 comments

Comments

@rsc
Copy link
Contributor

rsc commented Nov 5, 2024

Important

Nov 20, 2024: The latest version of the proposal is here.

Update, Nov 11 2024: An updated version is at #70200 (comment). The change is to stop presenting the choice as anything like a module, and instead to use a GOFIPS140 environment variable, analogous to GOOS, GOARCH, CGO_ENABLED, and so on.


We are working toward getting Go crypto FIPS validated, to be able to remove BoringCrypto. The relevant code is going to be crypto/internal/fips/.... People are going to need to be able to select between a few different crypto/internal/fips trees when using any given Go distribution. We propose to treat the crypto/internal/fips tree as its own pseudo-module, with a new go build flag, -fips140, to choose the version.

In general, any build needs to choose between the actual latest source in $GOROOT/src/crypto/internal/fips and an earlier source snapshot stored in module zip form in $GOROOT/lib/fips140. (Earlier snapshots will have been through more validation processes and may be necessary for certain government-related use.)

The crypto/internal/fips module will use these version numbers:

  • the version in the actual Go 1.X.Y source tree will be called v1.X.Y.
  • snapshots taken ahead of Go 1.X.Y will be called v1.X.Y-fips.N for N starting at 1 and incrementing.

Note that the versions order correctly: v1.24.0-fips.1 < v1.24.0. The idea is that toward the end of the 1.24.0 cycle, a snapshot would be taken and sent to a lab for validation, so that by the time 1.24.0 is released, "v1.24.0-fips.1" will be an "in-process" FIPS module, meaning a lab has validated it but NIST has not yet signed off. (When NIST signs off, it becomes a "certified" module.)

The -fips140 flag can take one of the following possible values:

  • -fips140=off (the default) means to use the source in $GOROOT/src/crypto/internal/fips and set the default GODEBUG for compiled binaries to fips140=off. All other flag settings set the default GODEBUG to fips140=on.
  • -fips140=latest means to use the source in $GOROOT/src/crypto/internal/fips (and default binaries to fips140=on, which I will stop saying).
  • -fips140=v1.X.Y-fips.N means to use the snapshot in $GOROOT/lib/fips140/v1.X.Y-fips.N.zip.
  • -fips140=inprocess means to use the version listed in $GOROOT/lib/fips140/inprocess.txt (it will be v1.X.Y-fips.N for some X, Y, N).
  • -fips140=certified means to use the version listed in $GOROOT/lib/fips140/certified.txt (it too will be v1.X.Y-fips.N for some X, Y, N).

Note that the definitions of inprocess and certified are specific to the Go toolchain being used, to keep builds reproducible. (That is, NIST issuing a certification for a new version does not change the meaning of -fips140=certified in an older Go release.)

crypto/internal/fips will be like a module in the sense that:

  • When you run go list, packages in crypto/internal/fips/... will be reported as coming from a module named crypto/internal/fips.
  • crypto/internal/fips has a set of versions, module zips, and checksums.
  • Earlier snapshots maintained in $GOROOT/lib/fips140 will use the module zip format.
  • When using an earlier snapshot, the go command unpacks the module zip into the module cache and then uses that source in the build.
  • Programs that use go list or x/tools/go/packages will see the module information for crypto/internal/fips and will see source coming from the module cache when using earlier snapshots.
  • crypto/internal/fips will be listed in the go version -m output with its own version, in all builds using those packages. This will enable tools like govulncheck to report vulnerabilities accurately.

crypto/internal/fips will be special, or unlike a module, in these ways:

  • The module zips will only ever be loaded from $GOROOT/lib/fips140, never from the module proxy.
  • The checksums for the zips will only ever be loaded from $GOROOT/lib/fips140/fips140.sum, not from the checksum database.
  • crypto/internal/fips will not be listed in go.mod or participate in module version selection; the -fips140 build flag is the only way to choose the version.
  • The name "crypto/internal/fips" does not start with a domain name element, which violates golang.org/x/mod/module.CheckPath ("missing dot in first path element").
  • In -mod=vendor builds, fips code will come from the fips places ($GOROOT or else an unpacked zip), never from the vendor directory.

The specialness seems unavoidable. We could go all the way and make fips completely different and not look anything like a module, but it is necessary to be able to say what version of fips a given program uses, and modules are the vocabulary we use for talking about versions. So it makes sense to reuse that vocabulary. In general the special cases seem like they will not cause much trouble. In particular, programs that use x/tools/go/packages or go list to find information about packages in a build will see a Dir set to a directory in the module cache when the alternate fips locations are being used. Since they already handle seeing other packages in the module cache, they should keep working without any changes.

The only special case that might cause trouble is if clients of module.CheckPath try to check "crypto/internal/fips". If that turns out to be a problem, we can update module.CheckPath to allow crypto/internal/fips as a special case. In my prototype of this functionality, I skipped the calls to module.CheckPath in the few places where it mattered, avoiding any changes to x/mod.

I have a prototype of this code working. It needs some cleanup and tests, but the necessary changes are quite small:

 src/cmd/go/internal/cfg/cfg.go               |   1 +
 src/cmd/go/internal/load/pkg.go              |   5 +-
 src/cmd/go/internal/modfetch/cache.go        |  11 +-
 src/cmd/go/internal/modfetch/codehost/git.go |   2 +
 src/cmd/go/internal/modfetch/coderepo.go     |  14 +-
 src/cmd/go/internal/modfetch/fetch.go        |   2 +-
 src/cmd/go/internal/modfetch/fips.go         | 239 +++++++++++++++++++++++++++
 src/cmd/go/internal/modfetch/repo.go         |  11 +-
 src/cmd/go/internal/modfetch/sumdb.go        |   4 +
 src/cmd/go/internal/modload/build.go         |  10 +-
 src/cmd/go/internal/modload/import.go        |  10 +-
 src/cmd/go/internal/modload/init.go          |   8 +
 src/cmd/go/internal/modload/load.go          |   2 +-
 src/cmd/go/internal/modload/query.go         |   2 +-
 src/cmd/go/internal/work/build.go            |   7 +-
 15 files changed, 306 insertions(+), 22 deletions(-)

In summary, the proposal is to establish the crypto/internal/fips pseudo-module with the version scheme and properties described above, and to add the -fips140 go build flag. (Note that “build flags” apply not just to go build but to most other go commands, including go install, go test, and go list.)

@gopherbot gopherbot added this to the Proposal milestone Nov 5, 2024
@rsc rsc added this to Proposals Nov 5, 2024
@rsc rsc moved this to Incoming in Proposals Nov 5, 2024
@prattmic
Copy link
Member

prattmic commented Nov 5, 2024

-fips140=certified means to use the version listed in $GOROOT/lib/fips140/certified.txt (it too will be v1.X.Y-fips.N for some X, Y, N).

Am I understanding correctly that if v1.24.0-fips.1 is "in-process" when 1.24.0 is released, but certified before 1.24.1 is released, then 1.24.1 will include a backport CL to change $GOROOT/lib/fips140/certified.txt to v1.24.0-fips.1?

@prattmic
Copy link
Member

prattmic commented Nov 5, 2024

How many "earlier snapshots" are kept in $GOROOT/lib/fips140? e.g., will I be able to use v1.24.0-fips.1 with Go 1.30.0?

I wonder mainly due to thoughts of (a) distribution size with keeping many versions and (b) there may be a maintenance burden with keeping compatibility with very old modules, particularly if there are dependencies on internals in std.

@mateusz834
Copy link
Member

mateusz834 commented Nov 5, 2024

What is the use-case for -fips140=inprocess? Why someone would need to use it instead of latest/certified. Is there any benefit/requirement for using a in process FIPS module?

@mateusz834
Copy link
Member

mateusz834 commented Nov 5, 2024

I also wonder how a process of adding any new public API to the crypto packages would look like after this change. Before adding any new API we would have to wait for it to be available in N amount of latest fips modules (#70200 (comment))? Or is the -fips140 flag going to define implicitly a build tag such that it can be detected in crypto packages for conditional compilation with //go:build.

@cpu
Copy link
Contributor

cpu commented Nov 5, 2024

What is the use-case for -fips140=inprocess? Why someone would need to use it instead of latest/certified. Is there any benefit/requirement for using a in process FIPS module?

AIUI submissions to the FIPS validation queue take a long time to become certified with the current NIST backlog. Some policy regimes (I think notably fedramp) may allow using modules that are in the process of becoming certified since they've had lab scrutiny if not yet final sign off. If a dependent user's requirements allow it then -fips140=inprocess would let you benefit from improvements made since the last certification process ended without blocking on NIST to finish the approval of the new module. Stricter policy regimes may not allow this so it has to be a separate option.

@ianlancetaylor
Copy link
Member

  1. At this point there are no FIPS versions in process or certified. So presumably -fips140=inprocess and -fips140=certified will fail.
  2. You say that go list or x/tools/go/packages will see module information for crypto/internal/fips. For go list that will presumably be based on the -fips140 command line flag. How will x/tools/go/packages determine the FIPS version to use?
  3. I imagine that organizations will want to ensure consistent use of a FIPS version across their builds. Today they can get boringcrypto with a single consistent environment variable GOEXPERIMENT. I guess the corresponding environment variable would be GOFLAGS=-fips140=certified. But how will that be applied to x/tools/go/packages? Or (perhaps the same question) for Bazel or other build systems?
  4. It sounds like the certification process is going to be based on a particular version of the source code. What will be the schedule for further certifications? How will this affect ordinary maintenance of the crypto/internal/fips packages? Should we do typo fixes for comments? Should we do routine updates to use new standard library functionality, such as the recent changes from sort.Sort to slices.SortFunc (for cases with special-purpose implementations of sort.Interface)?
  5. When using Go 1.25, will it be possible to use -fips140=v1.24.1-fips.1? (That is, can 1.25 use FIPS for 1.24.) When using Go 1.25.3 will be possible to use -fpis140=v1.25.1-fips.1? (That is, can 1.25.3 use FIPS for 1.25.1.)

@FiloSottile
Copy link
Contributor

How many "earlier snapshots" are kept in $GOROOT/lib/fips140? e.g., will I be able to use v1.24.0-fips.1 with Go 1.30.0?

I wonder mainly due to thoughts of (a) distribution size with keeping many versions and (b) there may be a maintenance burden with keeping compatibility with very old modules, particularly if there are dependencies on internals in std.

The idea is to keep only the latest certified and the latest in-process modules as snapshots. Sometimes we might need to have a third snapshot that is going to become in-process but isn't yet, but hopefully we can time things well enough that won't happen.

Indeed, the contact surface between the standard library and the modules is a burden in both directions: the modules might use internal APIs (although I tried to minimize that) and the standard library uses the module APIs (see below).

I also wonder how a process of adding any new public API to the crypto packages would look like after this change. Before adding any new API we would have to wait for it to be available in N amount of latest fips modules (#70200 (comment))? Or is the -fips140 flag going to define implicitly a build tag such that it can be detected in crypto packages for conditional compilation with //go:build.

We discussed this in abstract and we're happy with new APIs returning an error if -fips140 is not off or latest. Concretely, we haven't discussed how to implement that, but an implicit build tag is an option. As long as we're confident we can make it work, we don't need to decide the specifics until we add new module-dependent APIs.

  1. At this point there are no FIPS versions in process or certified. So presumably -fips140=inprocess and -fips140=certified will fail.

Indeed.

  1. It sounds like the certification process is going to be based on a particular version of the source code. What will be the schedule for further certifications? How will this affect ordinary maintenance of the crypto/internal/fips packages? Should we do typo fixes for comments? Should we do routine updates to use new standard library functionality, such as the recent changes from sort.Sort to slices.SortFunc (for cases with special-purpose implementations of sort.Interface)?

We'll maintain the crypto/internal/fips tree as usual, with an eye to not breaking the stdlib-facing API too often (see above). At least every year we'll kick off a revalidation to keep a healthy pipeline of module versions moving to in-process and certified.

  1. When using Go 1.25, will it be possible to use -fips140=v1.24.1-fips.1? (That is, can 1.25 use FIPS for 1.24.) When using Go 1.25.3 will be possible to use -fpis140=v1.25.1-fips.1? (That is, can 1.25.3 use FIPS for 1.25.1.)

Yes. Consider that modules take up to a couple years to reach certification at the moment, so the latest certified for Go 1.26 might still be v1.24.1-fips.1. However, only the versions that are snapshotted in lib/fips140 will be available, of which we'll only keep up to three usually (see above).

(Left out the x/tools/go/packages questions that I might not be the best person to answer.)

@mateusz834
Copy link
Member

We discussed this in abstract and we're happy with new APIs returning an error if -fips140 is not off or latest. Concretely, we haven't discussed how to implement that, but an implicit build tag is an option. As long as we're confident we can make it work, we don't need to decide the specifics until we add new module-dependent APIs.

Ok, not everything can/should return an error, but i assume panic is also fine, for new APIs, to avoid build tag we can also add a version const into the fips module.

@ianthehat
Copy link

Why not double down on the pseudo module idea, and instead of having a flag at all, just use replace directives.
That way you can set it in the go.mod for things that really should build in fips, or in a go.work if you want to build temporarily in fips mode without touching the go.mod
replace crypto/internal/fips => crypto/internal/fips/inprocess

@seankhliao
Copy link
Member

#30241 was an accepted but unimplemented proposal for standard library vendoring, would it make sense to have the fips module work like that?

@n2vi
Copy link

n2vi commented Nov 5, 2024

Inevitably, there will come a day when the latest in-process and certified version will be found to contain a vulnerability. What -fips flag is used to update to the patched version?

In OpenBSD land, we talk of versions 7.6-release (which would correspond to your certified) and 7.6-current (which is approximately your gotip) and 7.6-stable (which is the release version with only critical security and reliability patches). So another way of asking my question is what is your name for "FIPS-stable"?

I got verbal assurance in public from Donna Dodson, back when she was in charge of all this, that NIST intends that even the most compliance-sensitive government clients should run "FIPS-stable" and not wait around for re-certification. It would be good to get this in writing from the current administration, but also plan in advance for version naming.

There ought to be some associated public logs maintained at NIST for recording such patches.

@FiloSottile
Copy link
Contributor

Inevitably, there will come a day when the latest in-process and certified version will be found to contain a vulnerability. What -fips flag is used to update to the patched version?

Good point, we discussed this but forgot to mention it in the proposal. The plan if a vulnerability is found in v1.24.0-fips.1 while it's the certified module is to issue v1.24.0-fips.2 and update the certified pointer in a regular Go patch release, because indeed even the most compliance-sensistive deployment is expected to weight in favor of mitigating a security vulnerability.

We can keep the previous module, too, if nonetheless we get complaints about diverging from the certified source. This can make the number of modules in lib/fips140 grow, but we did a retrospective of the security vulnerabilities in the Go crypto packages of the last five years, and only 2-4 (depending how you count) would have had to be fixed in a FIPS module, and all of them could be mitigated from the standard library. We could also be clever and compress them one against the other, if it becomes necessary.

@aclements
Copy link
Member

Are there any values of the -fips140 that would set the GODEBUG to enforce?

The interaction between the -fips140 flag and the GODEBUG seem pretty complicated. Do we actually need the GODEBUG? Is there a significant need for changing that at runtime rather than compile time?

You say that go list or x/tools/go/packages will see module information for crypto/internal/fips. For go list that will presumably be based on the -fips140 command line flag. How will x/tools/go/packages determine the FIPS version to use?

Ping @adonovan to comment on the interaction with go/packages here.

@adonovan
Copy link
Member

adonovan commented Nov 6, 2024

cc: @timothy-king @findleyr for x/tools implications. Seems like most of the cruft is handled by go list:

Programs that use go list or x/tools/go/packages will see the module information for crypto/internal/fips and will see source coming from the module cache when using earlier snapshots.

The GODEBUG implications are also mostly internal to go list.

There may be tools consequences (likely minor) of these rules:

  • The name "crypto/internal/fips" does not start with a domain name element, which violates golang.org/x/mod/module.CheckPath ("missing dot in first path element").
  • In -mod=vendor builds, fips code will come from the fips places ($GOROOT or else an unpacked zip), never from the vendor directory.

Also, it will be a(nother) module with no go.mod file.

@FiloSottile
Copy link
Contributor

Are there any values of the -fips140 that would set the GODEBUG to enforce?

No, I expect enforce to be less popular, and applications can opt-in with the regular GODEBUG mechanisms.

The interaction between the -fips140 flag and the GODEBUG seem pretty complicated. Do we actually need the GODEBUG? Is there a significant need for changing that at runtime rather than compile time?

We've heard from a few folks that would like to make a single build and then choose to operate it in FIPS mode or not at runtime. The benefits of GODEBUG=fips140=off are faster crypto/rand and faster key generation, because FIPS 140-3 has some pretty heavy (and not generally useful) requirements on those. Another nice property of this design is that any Go binary can be operated in FIPS mode by just switching the GODEBUG at runtime, if your compliance regime is ok with "updated" modules.

I think build flag and GODEBUG are pretty orthogonal: the former selects the module version, the latter whether the binary operates in FIPS mode. They only interact in that if you used the -fips140 build flag we assume you want a GODEBUG default of on instead of off.

@derekparker
Copy link
Contributor

Are there any values of the -fips140 that would set the GODEBUG to enforce?

No, I expect enforce to be less popular, and applications can opt-in with the regular GODEBUG mechanisms.

The interaction between the -fips140 flag and the GODEBUG seem pretty complicated. Do we actually need the GODEBUG? Is there a significant need for changing that at runtime rather than compile time?

We've heard from a few folks that would like to make a single build and then choose to operate it in FIPS mode or not at runtime. The benefits of GODEBUG=fips140=off are faster crypto/rand and faster key generation, because FIPS 140-3 has some pretty heavy (and not generally useful) requirements on those. Another nice property of this design is that any Go binary can be operated in FIPS mode by just switching the GODEBUG at runtime, if your compliance regime is ok with "updated" modules.

This aspect is import to us at Red Hat for how we build and ship our binaries. Likely, we will still have to carry a patch downstream because we prefer detection to be automatic (e.g. if the host is in FIPS mode, the binary should automatically operate in FIPS mode). However, having the mechanism be chosen at runtime from the initial design will make it easier to implement and carry this specific patch on our end. Additionally, overall it simplifies building and distributing binaries with FIPS requirements such that it won't require 2 builds for each component (FIPS / non-FIPS).

@aclements aclements moved this from Incoming to Active in Proposals Nov 6, 2024
@aclements
Copy link
Member

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.

@timothy-king
Copy link
Contributor

crypto/internal/fips will not be listed in go.mod or participate in module version selection; the -fips140 build flag is the only way to choose the version.

I am not sure I understand the motivation behind this. Why would this pseudo-module be so special compared to other modules? My naive take is that the decision to use fips (and which version) should be checked into a file like go.mod or go.work somewhere. Is there a compliance reason?

@ianlancetaylor
Copy link
Member

@timothy-king Are you suggesting that the possible values of the -fips140 option should instead appear in a go.mod file? It would still be a special case, because the actual source code lives in the standard Go repo. That might mean that everything that deals with go.mod files would have to know about it. Is there a big advantage one way or the other?

@adonovan Maybe this question doesn't make sense, but if something like Bazel uses go/packages uses "go list", how do pass a -fips140 option down to "go list"?

@timothy-king
Copy link
Contributor

Are you suggesting that the possible values of the -fips140 option should instead appear in a go.mod file?

I am not yet suggesting this. I am trying to understand why the pseudo-module selection is not more like a normal module selection. Why not write require crypto/internal/fips v1.24.0-fips.1 or something similar in a go.mod?

Given the proposed flag, I presume that this cannot work for some reason beyond how it is distributed/where it is fetched from. But I don't understand this reason yet.

@rsc
Copy link
Contributor Author

rsc commented Nov 7, 2024

I am not yet suggesting this. I am trying to understand why the pseudo-module selection is not more like a normal module selection. Why not write require crypto/internal/fips v1.24.0-fips.1 or something similar in a go.mod?

@timothy-king Because this is not the kind of requirement that makes sense for a single dependency or package to enforce. For example some people need to build Kubernetes with FIPS but most people don't. It doesn't make sense to put this requirement in Kubernetes's go.mod. Even among the people who want Kubernetes with FIPS, the specific version of the FIPS code (inprocess vs certified vs latest) that those people want will differ from context to context (mostly company to company), based on their chosen compliance levels. It's very much a build-time decision not a global dependency decision.

(Edit: Same answer to @ianthehat's question about replace directives. We really don't want to allow the ecosystem as a whole to manipulate this setting.)

@ianlancetaylor
Copy link
Member

GOOS and GOARCH are documented, among other places, at https://pkg.go.dev/runtime from runtime/extern.go.

@ianlancetaylor
Copy link
Member

If I understand correctly, you are suggesting that setting GOFIPS140=off when building a program means "build with $GOROOT/src/crypto/internal/fips and act as though the program specifies //go:debug fips140=off in the main package.

When running make.bash, not setting GOFIPS140 at all is exactly the same as setting GOFIPS140=off.

When not running make.bash, not setting GOFIPS140 at all means to use the value used when make.bash was run.

If GOFIPS140 was not set when running make.bash, or if GOFIPS140=off was set when running make.bash, and GOFIPS140 is not set when building a program, then we build with $GOROOT/src/crypto/internal/fips and act as though the program specifies //go:debug fips140=off in the main package.

At run time in all cases we can set GODEBUG=fips140=on to get whatever version of fips140 was used to build.

@aclements
Copy link
Member

The proposal committee's main sense is just that this all seems complicated. On the other hand, based on my understanding, I think this is as complicated as necessary to support the needs of FIPS. This is also, to a significant extent, an expert interface. If you have to use FIPS mode, you're going to take the time to figure out what you're doing.

@aclements
Copy link
Member

Have all remaining concerns about this proposal been addressed?

The proposal details are in #70200 (comment) (and the meanings of the GOFIPS140 values are in #70200 (comment)). The documentation plan is in #70200 (comment).

@rsc
Copy link
Contributor Author

rsc commented Nov 13, 2024

@ianlancetaylor

If I understand correctly, you are suggesting that setting GOFIPS140=off when building a program means "build with $GOROOT/src/crypto/internal/fips and act as though the program specifies //go:debug fips140=off in the main package.

Yes, and that is the default.

When running make.bash, not setting GOFIPS140 at all is exactly the same as setting GOFIPS140=off.

Yes.

When not running make.bash, not setting GOFIPS140 at all means to use the value used when make.bash was run.

Yes.

If GOFIPS140 was not set when running make.bash, or if GOFIPS140=off was set when running make.bash, and GOFIPS140 is not set when building a program, then we build with $GOROOT/src/crypto/internal/fips and act as though the program specifies //go:debug fips140=off in the main package.

Yes.

At run time in all cases we can set GODEBUG=fips140=on to get whatever version of fips140 was used to build.

Yes. It changes various behaviors, not the cryptographic code itself. It will mean that, as an incomplete list, (1) the init-time fips code+data hashing check runs, (2) crypto/rand.Reader runs the fips randomness generator, which seeds itself from /dev/random but then does more instead of using those bytes directly, and (3) crypto/tls changes its default permitted algorithms to be from the fips allowlist.

@rsc
Copy link
Contributor Author

rsc commented Nov 13, 2024

@aclements

The proposal committee's main sense is just that this all seems complicated. On the other hand, based on my understanding, I think this is as complicated as necessary to support the needs of FIPS. This is also, to a significant extent, an expert interface. If you have to use FIPS mode, you're going to take the time to figure out what you're doing.

I agree with this, although I think the new approach is less complicated than the module approach; thanks to @ianthehat in particular for that pushing back on that.

@ianthehat
Copy link

I agree, I think reading the whole conversation it all feels very complicated, but the actual final solution is actually only moderately complicated, no more so than it needs to be, and much cleaner than some of the intermediate steps might have you believe. Thanks for taking in to account my point of view, I am happy with where we ended up!

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/629198 mentions this issue: cmd/go: add basic GOFIPS140 support

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/629196 mentions this issue: cmd/dist: add GOFIPS140 setting

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/629201 mentions this issue: cmd/go: add GOFIPS140 snapshot support

gopherbot pushed a commit that referenced this issue Nov 19, 2024
GOFIPS140 will be used to control whether to build binaries that
run in FIPS-140 mode by default, as well as which version of
crypto/internal/fips is used during a given build.
It is a target configuration variable analogous to
GOOS, GOARCH, CGO_ENABLED, and the like, so the
default value is recorded in the toolchain during make.bash.

This CL adds the GOFIPS140 setting to the build process
and records the default for use by cmd/go.

For #70200.

Change-Id: Iafcb5a4207f00fae8bcd93e0184a63c72526abea
Reviewed-on: https://go-review.googlesource.com/c/go/+/629196
Reviewed-by: Michael Matloob <matloob@golang.org>
Auto-Submit: Russ Cox <rsc@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
gopherbot pushed a commit that referenced this issue Nov 19, 2024
GOFIPS140 does two things: (1) control whether to build binaries that
run in FIPS-140 mode by default, and (2) control which version of the
crypto/internal/fips source tree to use during a build.

This CL implements part (1). It recognizes the GOFIPS140 settings
"off" and "latest" and uses them to set the default GODEBUG=fips140
setting to "off" or "on" accordingly.

The documentation for GOFIPS140 is in a follow-up CL.

See cmd/go/internal/fips/fips.go for an overview.

For #70200.

Change-Id: I045f8ae0f19778a1e72a5cd2b6a7b0c88934fc30
Reviewed-on: https://go-review.googlesource.com/c/go/+/629198
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
gopherbot pushed a commit that referenced this issue Nov 20, 2024
GOFIPS140 does two things: (1) control whether to build binaries that
run in FIPS-140 mode by default, and (2) control which version of the
crypto/internal/fips source tree to use during a build.

This CL implements part (2). The older snapshot source trees are
stored in GOROOT/lib/fips140 in module-formatted zip files,
even though crypto/internal/fips is not technically a module.
(Reusing the module packing and unpacking code avoids reinventing it.)

See cmd/go/internal/fips/fips.go for an overview.

The documentation for GOFIPS140 is in a follow-up CL.

For #70200.

Change-Id: I73a610fd2c9ff66d0cced37d51acd8053497238e
Reviewed-on: https://go-review.googlesource.com/c/go/+/629201
Reviewed-by: Michael Matloob <matloob@golang.org>
Auto-Submit: Russ Cox <rsc@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/629996 mentions this issue: cmd/go: fix -changed dont print when GOFIPS140 is non-default`

@aclements
Copy link
Member

Based on the discussion above, this proposal seems like a likely accept.

The proposal is to add a new GOFIPS140 target configuration environment variable that controls the source used for the crypto/internal/fips package and the default value of the fips140 GODEBUG. It can take the following possible values:

GOFIPS140 crypto/internal/fips source Default fips140 GODEBUG
off $GOROOT/src/crypto/internal/fips off
latest $GOROOT/src/crypto/internal/fips on
vX.Y $GOROOT/lib/fips140/vX.Y.zip on
inprocess Use vX.Y listed in $GOROOT/lib/fips140/inprocess.txt on
certified Use vX.Y listed in $GOROOT/lib/fips140/certified.txt on

Note that the definitions of inprocess and certified are specific to the Go toolchain being used, to keep builds reproducible. (That is, NIST issuing a certification for a new version does not change the meaning of GOFIPS140=certified in an older Go release.)

Like all the other target configuration variables (for example, GOOS, GOARCH, GOARM, CC, CGO_ENABLED, ...), the GOFIPS140 setting during make.bash is "baked" into the toolchain as the default setting for builds with that toolchain. If unset, the default for standard Go toolchains will be "off". The baked-in default can be overridden by setting GOFIPS140 to a non-empty value when invoking go build or related commands.

When you run go version -m or fetch a debug.BuildInfo, there will be a new BuildSetting with key “GOFIPS140” and value "off" or "devel" or else a version. (That is, instead of "inprocess" you would see the specific version that "inprocess" resolved to. For "latest" you would see the Go version if this is a release, or else "devel" if that's the Go version.)

There is no user-visible API or tooling that would refer to the crypto/internal/fips code as a module.

When you run go list, packages in crypto/internal/fips/... may have a Dir in $GOROOT/src/crypto/internal/fips/... or they may come from directories in the module cache. Either way, they will have a nil Module field, matching the rest of the standard library.

While crypto/internal/fips does have a set of versions in semver syntax, they are entirely separate from the Go module system.

The files in lib/fips140 will have an implementation-defined format. That format will coincidentally use the module zip and checksum file formats, but that is an implementation detail.

GOFIPS140 will be documented at the top of make.bash, in runtime/extern.go and in any other places where GOOS, GOARCH, GOAMD64, etc are documented. There will also be a go.dev/doc/fips140 (or perhaps go.dev/security/fips140) that gives the bigger picture overview.

@aclements aclements moved this from Active to Likely Accept in Proposals Nov 21, 2024
gopherbot pushed a commit that referenced this issue Nov 21, 2024
See #70200 (comment),
GOFIPS140 value when building the toolchain (off when not set)
is the default value for GOFIPS140,
it is buildcfg.defaultGOFIPS140,
export as buildcfg.DefaultGOFIPS140 that can be used in the cmd/go.

For #70200

Change-Id: I5a4873a718eeefda8e65bfab51d9d3d5ad2c21b6
Reviewed-on: https://go-review.googlesource.com/c/go/+/629996
Reviewed-by: Michael Matloob <matloob@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
@aclements aclements moved this from Likely Accept to Accepted in Proposals Nov 27, 2024
@aclements
Copy link
Member

No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.

The proposal is to add a new GOFIPS140 target configuration environment variable that controls the source used for the crypto/internal/fips package and the default value of the fips140 GODEBUG. It can take the following possible values:

GOFIPS140 crypto/internal/fips source Default fips140 GODEBUG
off $GOROOT/src/crypto/internal/fips off
latest $GOROOT/src/crypto/internal/fips on
vX.Y $GOROOT/lib/fips140/vX.Y.zip on
inprocess Use vX.Y listed in $GOROOT/lib/fips140/inprocess.txt on
certified Use vX.Y listed in $GOROOT/lib/fips140/certified.txt on

Note that the definitions of inprocess and certified are specific to the Go toolchain being used, to keep builds reproducible. (That is, NIST issuing a certification for a new version does not change the meaning of GOFIPS140=certified in an older Go release.)

Like all the other target configuration variables (for example, GOOS, GOARCH, GOARM, CC, CGO_ENABLED, ...), the GOFIPS140 setting during make.bash is "baked" into the toolchain as the default setting for builds with that toolchain. If unset, the default for standard Go toolchains will be "off". The baked-in default can be overridden by setting GOFIPS140 to a non-empty value when invoking go build or related commands.

When you run go version -m or fetch a debug.BuildInfo, there will be a new BuildSetting with key “GOFIPS140” and value "off" or "devel" or else a version. (That is, instead of "inprocess" you would see the specific version that "inprocess" resolved to. For "latest" you would see the Go version if this is a release, or else "devel" if that's the Go version.)

There is no user-visible API or tooling that would refer to the crypto/internal/fips code as a module.

When you run go list, packages in crypto/internal/fips/... may have a Dir in $GOROOT/src/crypto/internal/fips/... or they may come from directories in the module cache. Either way, they will have a nil Module field, matching the rest of the standard library.

While crypto/internal/fips does have a set of versions in semver syntax, they are entirely separate from the Go module system.

The files in lib/fips140 will have an implementation-defined format. That format will coincidentally use the module zip and checksum file formats, but that is an implementation detail.

GOFIPS140 will be documented at the top of make.bash, in runtime/extern.go and in any other places where GOOS, GOARCH, GOAMD64, etc are documented. There will also be a go.dev/doc/fips140 (or perhaps go.dev/security/fips140) that gives the bigger picture overview.

@aclements aclements changed the title proposal: cmd/go: add fips140 module selection mechanism cmd/go: add fips140 module selection mechanism Nov 27, 2024
@aclements aclements modified the milestones: Proposal, Backlog Nov 27, 2024
@dmitshur
Copy link
Contributor

Given #69536 and #70123 are in Go1.24 milestone, it seems this should be too, right?

@dmitshur dmitshur modified the milestones: Backlog, Go1.24 Dec 12, 2024
@FiloSottile
Copy link
Contributor

This is mostly done. The only thing left to do will be freezing the zip file of v1.0 once we reach source freeze with the lab, and populating inprocess.txt once the lab signs off on it. Hopefully these will both happen before Go 1.24.0, but updating inprocess.txt may also slip to a minor version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Accepted
Development

No branches or pull requests