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

crypto: mechanism to enable FIPS mode #70123

Open
FiloSottile opened this issue Oct 30, 2024 · 26 comments
Open

crypto: mechanism to enable FIPS mode #70123

FiloSottile opened this issue Oct 30, 2024 · 26 comments
Labels
Proposal Proposal-Accepted Proposal-Crypto Proposal related to crypto packages or other security issues
Milestone

Comments

@FiloSottile
Copy link
Contributor

FiloSottile commented Oct 30, 2024

As tracked in #69536 we are pursuing a FIPS 140-3 validation for the Go cryptography packages. This proposal is about the mechanism to selectively enable and disable FIPS mode.

All Linux binaries will be FIPS-capable, so there will be no need for a compile-time on-switch. We believe we can make the build-time overhead low enough that we can remove this entire layer of complexity, so there will be no need for a "FIPS build". Note however that the module will not be FIPS validated on all architectures1.

A run-time GODEBUG flag, fips140 defaults to off but can be set to on to operate the binary in FIPS mode. Primarily, this enables the slower and more complex RNG system, and the slow runtime self-tests. The GODEBUG can also be set to only to make any non-approved crypto return errors.2

A build tag, fips140, can be used to switch the GODEBUG default from off to on. It has no other effect. This allows building arbitrary programs so that they will run in FIPS mode by default. Note that //go:debug annotations can also be used to enable or disable the GODEBUG.

If fips140=on is set (in any way) and the binary can't operate in FIPS mode (because it was not built for Linux or it was built for an architecture that was not validated, for example), the binary will immediately panic at runtime.

A new package, crypto/fips140 will be added with a single function Enabled() bool which can be used to check at runtime if the binary is operating in FIPS mode.

Changing the GODEBUG after the binary started is not supported, it will not result in compliant FIPS operations, and may or may not cause the binary to panic. This is unlikely to change.

This proposal does not address how to select the FIPS module to compile against, which will require either a go build flag or a build tag. We are discussing this with @rsc. By default, binaries will build against the latest unvalidated version of the module3, but it will be possible to select both the latest In Process modules and the latest certified module. This will be transparent, although some newly introduced functionality may not be available when compiling against frozen versions of the module.

/cc @golang/security @cpu

Footnotes

  1. The current tentative plan is to validate on some Linux OSes on some amd64, arm64, mips64, ppc64le, and s390x. If you need specific OEs validated, reach out to me ASAP.

  2. We're aware this is usually less granularity than required by production applications, which sometimes need to use non-approved crypto for non-security purposes, as allowed by FIPS 140-3. We'll share precise plans later, but in short this only mode will be implemented in terms of an internal API that can be used by downstream forks (without explicit backward compatibility guarantees) to enforce such a fine-grained policy. We don't plan to provide an exposed upstream mechanism at this time.

  3. Running the latest unvalidated version in FIPS mode still makes sense in some settings, hopefully more and more over time.

@FiloSottile FiloSottile added Proposal Proposal-Crypto Proposal related to crypto packages or other security issues labels Oct 30, 2024
@FiloSottile FiloSottile added this to the Proposal milestone Oct 30, 2024
@gabyhelp
Copy link

Related Issues and Documentation

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

@qmuntal
Copy link
Contributor

qmuntal commented Oct 31, 2024

If changing the FIPS mode is not possible at runtime, it's a property fixed when building the binary, then IMO it would make sense to define fips140.Enabled as a const instead of as a func. This way it is clearer for that caller that the value can't change at runtime and that the compiler will strip any branch conditioned by fips140.Enabled.

Also, will FIPS mode affect the Go runtime behavior (or even other packages outside crypto)? If that's the case, then I would consider moving the fips140.Enabled flag to the runtime package.

@FiloSottile
Copy link
Contributor Author

If changing the FIPS mode is not possible at runtime, it's a property fixed when building the binary, then IMO it would make sense to define fips140.Enabled as a const instead of as a func. This way it is clearer for that caller that the value can't change at runtime and that the compiler will strip any branch conditioned by fips140.Enabled.

The idea is that FIPS mode will be selected only at runtime, by the GODEBUG env var. I think you might have been confused by my poorly worded "Changing the GODEBUG at runtime is not supported", edited to "Changing the GODEBUG after the binary started is not supported". I just meant that you can't do os.Setenv halfway through the run.

Also, will FIPS mode affect the Go runtime behavior (or even other packages outside crypto)? If that's the case, then I would consider moving the fips140.Enabled flag to the runtime package.

There is a bit of runtime support, but I think logically the package makes more sense under crypto/. It will be a package with no dependencies anyway.

@aclements aclements moved this to Incoming in Proposals Nov 1, 2024
@aclements
Copy link
Member

By default, binaries will build against the latest unvalidated version of the module3, but it will be possible to select both the latest In Process modules and the latest certified module.

Isn't it going to cause confusion that, by default, when you set the fips140 build tag, what you get is not actually FIPS 140 certified?

@FiloSottile
Copy link
Contributor Author

Isn't it going to cause confusion that, by default, when you set the fips140 build tag, what you get is not actually FIPS 140 certified?

In general, "FIPS 140 certified" is more of a spectrum than a binary state, and for example with the new FedRAMP rules a binary that's in FIPS mode would be acceptable even if it's an updated version of the one that underwent validation.

Still, I agree that the build tag is confusing, also because people will end up assuming it does more than just set the default GODEBUG. Let's just drop it. Instead, we discussed with @rsc making the module version selection mechanism (which is out of scope for this proposal) also flip the GODEBUG default to on any time it's used.

@rsc
Copy link
Contributor

rsc commented Nov 5, 2024

The module details are a separate proposal, #70200.

@FiloSottile
Copy link
Contributor Author

We've been discussing with @rolandshoemaker how to let applications enforce the use of NIST-approved algorithms implemented within the module, and for now we want to offer a coarse binary choice, and then add more fine-grained mechanisms in the future based on feedback.

We propose adding a third documented GODEBUG=fips140 option, enforce which:

  • behaves like on
  • makes crypto/tls and golang.org/x/crypto/ssh silently consider only FIPS-compliant algorithms (like crypto/tls/fipsonly in Go+BoringCrypto)
  • makes non-approved cryptographic functions return errors or panic

The last point includes wholly non-approved algorithms implemented by the standard library or (an updated version of) golang.org/x/crypto, as well as approved algorithms used with non-approved parameters (such as HMAC with too short a key). This is to be considered a best-effort mechanism, since we can't for example detect applications implementing non-approved crypto with math/big or third-party modules, of course.

Here is a list of functions that would fail entirely:

  • crypto/cipher.NewGCM
  • crypto/cipher.NewGCMWithNonceSize
  • crypto/cipher.NewGCMWithTagSize
  • crypto/cipher.NewCFBDecrypter [p]
  • crypto/cipher.NewCFBEncrypter [p]
  • crypto/cipher.NewOFB [p]
  • crypto/des.NewCipher
  • crypto/des.NewTripleDESCipher
  • crypto/dsa.GenerateKey
  • crypto/dsa.GenerateParameters
  • crypto/dsa.Sign
  • crypto/dsa.Verify [p]
  • crypto/ecdh.X25519.ECDH
  • crypto/md5.New [p]
  • crypto/md5.Sum [p]
  • crypto/rand.Prime
  • crypto/rc4.NewCipher
  • crypto/rsa.EncryptPKCS1v15
  • crypto/rsa.DecryptPKCS1v15
  • crypto/sha1.New [p]
  • crypto/sha1.Sum [p]
  • golang.org/x/crypto/argon2.IDKey [p]
  • golang.org/x/crypto/argon2.Key [p]
  • golang.org/x/crypto/bcrypt.CompareHashAndPassword
  • golang.org/x/crypto/bcrypt.GenerateFromPassword
  • golang.org/x/crypto/blake2b.New*
  • golang.org/x/crypto/blake2b.Sum* [p]
  • golang.org/x/crypto/blake2s.New*
  • golang.org/x/crypto/blake2s.Sum* [p]
  • golang.org/x/crypto/blowfish.NewCipher
  • golang.org/x/crypto/blowfish.NewSaltedCipher
  • golang.org/x/crypto/cast5.NewCipher
  • golang.org/x/crypto/chacha20.NewUnauthenticatedCipher
  • golang.org/x/crypto/chacha20poly1305.New
  • golang.org/x/crypto/chacha20poly1305.NewX
  • golang.org/x/crypto/curve25519.ScalarBaseMult [p]
  • golang.org/x/crypto/curve25519.ScalarMult [p]
  • golang.org/x/crypto/curve25519.X25519
  • golang.org/x/crypto/md4.New [p]
  • golang.org/x/crypto/poly1305.New
  • golang.org/x/crypto/poly1305.Sum [p]
  • golang.org/x/crypto/poly1305.Verify [p]
  • golang.org/x/crypto/ripemd160.New [p]
  • golang.org/x/crypto/salsa20.XORKeyStream [p]
  • golang.org/x/crypto/sha3.NewLekacyKeccak256 [p]
  • golang.org/x/crypto/sha3.NewLekacyKeccak512 [p]
  • golang.org/x/crypto/tea.NewCipher
  • golang.org/x/crypto/tea.NewCipherWithRounds
  • golang.org/x/crypto/twofish.NewCipher
  • golang.org/x/crypto/xtea.NewCipher
  • golang.org/x/crypto/xts.NewCipher

Note that scrypt is missing because it has been successfully described as approved due to its use of PBKDF2.

(Internally, we'll still let forks apply more fine-grained policy with a mechanism akin to #65265, but that's not part of this proposal.)

@ericlagergren
Copy link
Contributor

crypto/cipher.NewGCM
crypto/cipher.NewGCMWithNonceSize
crypto/cipher.NewGCMWithTagSize

Doesn't this preclude AES-GCM?

@FiloSottile
Copy link
Contributor Author

Doesn't this preclude AES-GCM?

It will require #69981, yes, or a special internal TLS mode we will probably not expose. That's FIPS 140-3 IG C.H for you.

@aclements
Copy link
Member

We propose adding a third documented GODEBUG=fips140 option, enforce which:

Why would someone chose enforce versus on?

@FiloSottile
Copy link
Contributor Author

Why would someone chose enforce versus on?

Having a FIPS module laying around is not enough to comply with anything, you also have to use it for all cryptographic operations, and use only its approved "services" (a NIST-ism for algorithms and modes). It's ok to do that by just being very careful not to use anything that's not approved, but enforce mode offers better assurance of it. Go+BoringCrypto doesn't have that mode (aside from crypto/tls/fipsonly) but some of the forks do.

@derekparker
Copy link
Contributor

Why would someone chose enforce versus on?

Having a FIPS module laying around is not enough to comply with anything, you also have to use it for all cryptographic operations, and use only its approved "services" (a NIST-ism for algorithms and modes). It's ok to do that by just being very careful not to use anything that's not approved, but enforce mode offers better assurance of it. Go+BoringCrypto doesn't have that mode (aside from crypto/tls/fipsonly) but some of the forks do.

This mode I think is very important. Within the golang-fips/go fork we have a strict FIPS mode we operate in by default which essentially matches the description of enforce. It takes the burden off the consumer of the toolchain to ensure they are using the crypto libraries in a FIPS compliant way by ensuring any violation is met with an error and ensuring that higher level packages such as tls are also operating in a FIPS compliant way.

@aclements
Copy link
Member

Okay, in that case why would someone chose on over enforce?

@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.

@FiloSottile
Copy link
Contributor Author

Okay, in that case why would someone chose on over enforce?

There's the concept of non-security purposes. E.g. the AWS APIs use MD5 as a checksum. That's not a security purpose, so it's allowed, but it involves the use of non-approved cryptography, so requires operating in on rather than enforce mode.

Ultimately, we'll want some finer-grained mechanism to make that coexist with the safety of enforce, but I don't think we can design it for Go 1.24, and having the two modes matches what's being used in the field at the moment.

@FiloSottile
Copy link
Contributor Author

We noticed I called enforce mode force in the proposal and enforce in #70123 (comment). Both are kind of confusing. Let's go with only since in that mode you can only use FIPS 140-3 cryptography.

@aclements
Copy link
Member

A new package, crypto/fips140 will be added with a single function Enabled() bool which can be used to check at runtime if the binary is operating in FIPS mode.

Does this need to distinguish on and only?

@aclements
Copy link
Member

It's worth noting that we believe this will have little effect on binary size, even though all binaries will be built with the ability to turn on FIPS mode. We're not going to be compiling in two different crypto trees. There will be a little extra code to support the dynamic selection between off and on, but not much.

Does this need to distinguish on and only?

To be clear, I think we would still want an Enabled() bool function since that's all people care about the vast majority of the time. That also means we can add functions to interrogate this more deeply in the future if we need to. @rolandshoemaker says people will let us know if they need this. :)

@aclements aclements moved this from Active to Likely Accept in Proposals Nov 13, 2024
@aclements
Copy link
Member

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

The proposal is to add a fips140 GODEBUG setting that can take the following three values:

  • fips140=off - The binary does not operate in FIPS mode.
  • fips140=on - The binary operates in FIPS mode. Primarily, this enables the slower and more complex RNG system, and the slow runtime self-tests. If for any reason the binary can't operate in FIPS mode, it will immediately panic during startup.
  • fips140=only - Like "on", but additionally any non-approved crypto will return errors.

The default value of fips140 will depend on the GOFIPS140 build-time environment variable (see #70200), but if no GOFIPS140 is set, it defaults to off.

The fips140 GODEBUG cannot be changed at runtime. Attempting to do so may or may not panic.

A new package, crypto/fips140 will be added with a single function Enabled() bool which can be used to check at runtime if the binary is operating in FIPS mode.

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/629816 mentions this issue: cmd/go: add GOFIPS140 to 'go help environment'

gopherbot pushed a commit that referenced this issue Nov 20, 2024
Also re-sort the various lists. (All lists should be sorted.)
The linked page https://go.dev/security/fips140 has yet
to be written, but soon.

For #70123.

Change-Id: Ica5bbf5bd29e9136c2a49d3099d6e0e018c36145
Reviewed-on: https://go-review.googlesource.com/c/go/+/629816
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/630115 mentions this issue: all: rename crypto/internal/fips to crypto/internal/fips140

gopherbot pushed a commit that referenced this issue Nov 20, 2024
Sometimes we've used the 140 suffix (GOFIPS140, crypto/fips140)
and sometimes not (crypto/internal/fips, cmd/go/internal/fips).
Use it always, to avoid having to remember which is which.

Also, there are other FIPS standards, like AES (FIPS 197), SHA-2 (FIPS 180),
and so on, which have nothing to do with FIPS 140. Best to be clear.

For #70123.

Change-Id: I33b29dabd9e8b2703d2af25e428f88bc81c7c307
Reviewed-on: https://go-review.googlesource.com/c/go/+/630115
Reviewed-by: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
@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 fips140 GODEBUG setting that can take the following three values:

  • fips140=off - The binary does not operate in FIPS mode.
  • fips140=on - The binary operates in FIPS mode. Primarily, this enables the slower and more complex RNG system, and the slow runtime self-tests. If for any reason the binary can't operate in FIPS mode, it will immediately panic during startup.
  • fips140=only - Like "on", but additionally any non-approved crypto will return errors.

The default value of fips140 will depend on the GOFIPS140 build-time environment variable (see #70200), but if no GOFIPS140 is set, it defaults to off.

The fips140 GODEBUG cannot be changed at runtime. Attempting to do so may or may not panic.

A new package, crypto/fips140 will be added with a single function Enabled() bool which can be used to check at runtime if the binary is operating in FIPS mode.

@aclements aclements moved this from Likely Accept to Accepted in Proposals Nov 21, 2024
@aclements aclements changed the title proposal: crypto: mechanism to enable FIPS mode crypto: mechanism to enable FIPS mode Nov 21, 2024
@aclements aclements modified the milestones: Proposal, Backlog Nov 21, 2024
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/631017 mentions this issue: crypto/fips140: new package

@dmitshur dmitshur modified the milestones: Backlog, Go1.24 Nov 22, 2024
gopherbot pushed a commit that referenced this issue Nov 22, 2024
This package holds only the Enabled() function.

Updates #70123

Change-Id: If0e731724d9997001fa52002fa6ae72df4eb16ff
Reviewed-on: https://go-review.googlesource.com/c/go/+/631017
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/631018 mentions this issue: crypto: implement fips140=only mode

gopherbot pushed a commit that referenced this issue Nov 22, 2024
Running the test suite in this mode is definitely not an option. Testing
this will probably look like a very long test that tries all functions.
Filed #70514 to track the tests.

For #70123

Change-Id: I6f67de83da37dd1e94e620b7f4f4f6aabe040c41
Reviewed-on: https://go-review.googlesource.com/c/go/+/631018
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
@liggitt
Copy link
Contributor

liggitt commented Dec 5, 2024

The description says:

A run-time GODEBUG flag, fips140 defaults to off but can be set to on to operate the binary in FIPS mode
...
Changing the GODEBUG after the binary started is not supported

The accepted summary says:

The fips140 GODEBUG cannot be changed at runtime. Attempting to do so may or may not panic.

For clarity, is the description still accurate, that the fips140 GODEBUG can be set at runtime but not changed after the binary started? Or did the proposal shift to make this a build-time decision only?

@FiloSottile
Copy link
Contributor Author

the fips140 GODEBUG can be set at runtime but not changed after the binary started

Correct. The summary probably left "after the binary started" implied.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Proposal Proposal-Accepted Proposal-Crypto Proposal related to crypto packages or other security issues
Projects
Status: Accepted
Development

No branches or pull requests

10 participants