Skip to content

crypto/fips140: selective policy enforcement framework #74630

Closed

Description

@rolandshoemaker

Go 1.24 introduced the fips140=only GODEBUG setting, which strictly enforces
that only FIPS 140-3 compliant cryptography is used, either panicking in or
returning an error from non-compliant functions.

For the vast majority of use cases, this is likely to be overly strict, as
programs will likely need to use non-compliant functions in code paths that do
not require FIPS 140-3 compliant cryptography (for instance, when using
non-compliant hashes for non-cryptographic purposes).

More complex programs would likely benefit from a more selective policy
enforcement framework that either allows marking non-compliant function calls as
acceptable or allows selectively enforcing compliance requirements (or both).

For instance, one possible approach would be to add a function to
crypto/fips140 which takes an options struct and a function. Within this
function, enforcement decisions would be modified by the specified options
(e.g., “allow calls to x, y, and z non-compliant functions” or “strictly enforce
FIPS 140-3 compliance”).

This issue doesn’t propose any explicit changes; it is mainly intended, for
now, to collect ideas from the community. We’d especially like to hear from
users who have experience developing applications that would benefit from this
type of selective enforcement and from those who have experience using similar
frameworks in other languages.

Update: we now have a concrete proposal, in #74630 (comment).

Activity

  1. added
    NeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.
    on Jul 15, 2025
  2. added
    LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a tool
    on Jul 15, 2025
  3. qmuntal commented on Jul 15, 2025

    @qmuntal
    Member

    More than a runtime enforcement I would find more useful to have a static analysis tool that reports which non-FIPS compliant algorithms are used. It could be a special govulncheck mode, for example.

    But that might be food for another proposal.

  4. CanobbioE commented on Aug 5, 2025

    @CanobbioE

    Just wanted to share my experience migrating to GODEBUG=fips140=only at the company I work for.

    We encountered only two issues during the process. Sharing them here in case it helps others.

    Issue 1
    For unit testing, we were using a fakekms client with a aesgcm fake key.
    The aesgcm implementation of NewAEAD internally calls NewGCM, which, under fips140=only, returns an error suggesting the use of NewGCMWithRandomNonce.

    Solution 1
    We resolved this by generating a new fake key with aesgcmsiv, which is allowed in FIPS mode.

    Issue 2
    Redis script caching uses SHA-1 to generate the cache key.
    Due to legacy code, we were generating the SHA-1 hash ourselves, which resulted in the error: use of SHA-1 is not allowed.

    Solution 2
    We updated the code to delegate SHA-1 generation to Redis by calling SCRIPT LOAD, avoiding the explicit use of SHA-1 in Go.

    In conclusion, the migration to the strictest configuration (fips140=only) was quick and relatively straightforward, kudos!

  5. FiloSottile commented on Aug 22, 2025

    @FiloSottile
    Contributor

    An interesting problem of the current fips140=only that we should fix in the next design is that it will disallow ML-KEM/X25519 hybrids, which are FIPS 140 compliant by disregarding the X25519 part.

  6. xduwyy commented on Aug 26, 2025

    @xduwyy

    An interesting problem of the current fips140=only that we should fix in the next design is that it will disallow ML-KEM/X25519 hybrids, which are FIPS 140 compliant by disregarding the X25519 part.

    Hi @FiloSottile , is this the root cause of #75148 ?

  7. martinhartig commented on Aug 26, 2025

    @martinhartig

    We’d especially like to hear from users who have experience developing applications ...

    A practical problem is the usage of Websockets with fips140=only. Websocket's standard defines the usage SHA-1 usage but in a non-crypto context, see https://datatracker.ietf.org/doc/html/rfc6455#section-10.8.
    With Fips140=only, web sockets become unusable, which in turn make fips140=only impossible to apply.

  8. rolandshoemaker commented on Aug 26, 2025

    @rolandshoemaker
    MemberAuthor

    An interesting problem of the current fips140=only that we should fix in the next design is that it will disallow ML-KEM/X25519 hybrids, which are FIPS 140 compliant by disregarding the X25519 part.

    Hi @FiloSottile , is this the root cause of #75148 ?

    SP 800-186 does not allow X25519 for key agreement, so it cannot be used in FIPS mode. Because ML-KEM/X25519 is a hybrid construction, it is allowed, because it does not (necessarily) rely on the X25519 component for it's security. Since it is allowed we should be exempting this construction in the fips140=only mode, but we cannot allow for standalone usage.

    This is a rather complex corner of policy enforcement.

  9. rolandshoemaker commented on Aug 27, 2025

    @rolandshoemaker
    MemberAuthor

    We had a conversation about this at GopherCon. With that discussion, in addition to other conversations we've had, and the discussion here, I think we've landed on a reasonable proposal here.

    It seems as if the consensus has moved towards not needing a highly configurable enforcement framework, and that we can solve perhaps 90+% of peoples issues with a rather blunt enforcement API which simply allows turn off and on all enforcement for a specific scope.

    Concretely the proposed API addition in crypto/fips140 would be (obviously with names up for discussion):

    // WithEnforcementDisabled disables strict FIPS 140-3 enforcement while
    // executing f. Calling WithEnforcementDisabled without strict enforcement
    // enabled (GODEBUG=fips140=only is not set or already inside of a call to
    // WithEnforcementDisabled) is a no-op.
    //
    // Any goroutines created by f will also have strict enforcement disabled for
    // their lifetime, even if f returns before they complete.
    //
    // As this disables enforcement, it should be applied carefully to tightly
    // scoped functions.
    func WithEnforcementDisabled(f func())
    
    // EnforcementDisabled indicates if strict FIPS 140-3 enforcement is enabled.
    // Strict enforcement is enabled when a program is run with GODEBUG=fips140=only
    // and not inside of a call to WithEnforcementDisabled.
    func EnforcementEnabled() bool
    

    We would then use these APIs in the standard library to disable enforcement for non-security relevant usages of cryptographic primitives in the runtime and our own packages (such as usages of hashes, like SHA-256, for checksums, etc.) or for acceptable usages of unapproved primitives in safe constructions (such as the usage of X25519 in the ML-KEM/X25519 hybrid key agreement.)

  10. changed the title [-]crypto/fips140: selective policy enforcement framework[/-] [+]proposal: crypto/fips140: selective policy enforcement framework[/+] on Aug 28, 2025
  11. added this to the Proposal milestone on Aug 28, 2025
  12. removed
    NeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.
    on Aug 28, 2025
  13. FiloSottile commented on Sep 7, 2025

    @FiloSottile
    Contributor

    I think we agreed to make the goroutine propagation semantics equivalent to runtime/secret and WithDataIndependentTiming, and they both don't propagate, so I think this should not propagate either.

    From #21865 (comment):

    4. secret.Do(f) does not propagate its properties to goroutines created by f's call graph. It is the programmer's responsibility to run secret.Do again as needed from new goroutines.

    As for naming, I think I like Enforced() for parallel to Enabled() and WithoutEnforcement() to avoid sounding like we are enforcing disabling FIPS 140 mode.

    // WithoutEnforcement disables strict FIPS 140-3 enforcement while executing f.
    // Calling WithoutEnforcement without strict enforcement enabled
    // (GODEBUG=fips140=only is not set or already inside of a call to
    // WithoutEnforcement) is a no-op.
    //
    // WithoutEnforcement does not affect any goroutines started within it.
    //
    // As this disables enforcement, it should be applied carefully to tightly
    // scoped functions.
    func WithoutEnforcement(f func())
    
    // Enforced indicates if strict FIPS 140-3 enforcement is enabled. Strict
    // enforcement is enabled when a program is run with GODEBUG=fips140=only and
    // not inside of a call to [WithoutEnforcement].
    func Enforced() bool
    

    Aside from that, +1!

  14. 36 remaining items

  15. modified the milestones: Backlog, Go1.26 on Nov 26, 2025
  16. added a commit that references this issue on Nov 26, 2025
    86bbea0
  17. gopherbot commented on Dec 9, 2025

    @gopherbot
    Contributor

    Change https://go.dev/cl/728505 mentions this issue: crypto/hpke: apply fips140.WithoutEnforcement to ML-KEM+X25519 hybrid

  18. xnox commented on Feb 19, 2026

    @xnox
    Contributor

    Is the intent here to use the above as potentially a dynamic service indicators, and ability to access "non-approved, allowed", "legacy use", "deprecated" and so on algorithms?

    Now that there is such access to algorithms that are not "Approved", some of the messages have now become potentially misleading and ambigious to the developers unfamiliar with NIST terminology. Various cryptographic algorithms and services have various NIST statuses:

    • "Approved"
    • "Non-Approved, Allowed Algorithms"
    • "Non-Approved, Allowed Algorithms with No Security Claimed"
    • "Non-Approved, Not Allowed Algorithms"
    • "Legacy use"
    • "Deprecated"
    • "Deprecated until DATE" and "Disallowed after DATE"
    • "For blockchain usage only"
    • "For storage usage only"
    • "Only as part of TLS"
    • "Disallowed"

    I did complain to NIST that they should stop inventing more "statuses" for algorithms.

    And above definitions are not fixed or set in stone for everyone at any given point in time - as there is freedom for each module to choose which algorithms to implement; and each vendor can choose not to test certain algorithms and downgrade them to non-approved. And often timing of the module submission defines what was "Approved" at the time. Hence often there are fun discrepancies that modules mere days appart have widely different sets of Approved algorithms.

    Thus brushing all error messages with "is not allowed" is too broad, and likely in many cases conflicts with what the future geomys module policy will say. And importantly sounds way to similar to "Non-approved Not Allowed Algorithms" - which are often in fact implemented and are intended to be used. And currently the message is the same for both "legacy" and "deprecated" algorithms, as well as "Non-Approved, Allowed Algorithms with No Security Claimed" which is what many Hybrids are for submissions prior to latest hybrids I.G. Expectation is that some of the hybrids will upgrade to "Approved" once ACVP testing for them becomes available.

    In Bouncycastle FIPS (popular for Java), and very wide spread term is "approved-only" which is more accurate - as it tells the user this is not the the universal "Approved" algorithm/service, and one has to read the security policy and IGs to figure out if their intended use case is ok or not.

    Would we be open to change the error message wording slightly?

    - "crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode"
    + "crypto/sha1: use of SHA-1 is blocked in FIPS 140 approved-only mode"

    Would significantly improve end developer experience, and will help them to guide themselves to see if they need to redesign their code, or if they have a choice to use "deprecated / allowed / no-security claim" algorithms instead.

    In essence avoiding using the term "not allowed" due to its special meaning. And using the term "blocked" which is widely used by Security Labs to indicate blocking algorithms and having opt-in/opt-out indicators to access algorithms without the universal "Approved" status.

    It is very annoying that the opposite of "approved" is 10 different terms, including "allowed" in NIST terms.

    Specifically about SHA-1 it is often legacy for signature verification only, deprecated for all other use cases, disallowed after 2030 - and of course is treated as redundant cryptography equivalent to plaintext for non-security purposes as per FIPS 140-3 I.G.2.4.A for any other use cases (typically as a one way compression function; non-security relevant identifier and so on).

    b130dab is accurate example of "dissallowed" algorithm, being allowed in an "approved" higher level service for hybrid TLS which is still "FIPS approved-only".

  19. FiloSottile commented on Feb 21, 2026

    @FiloSottile
    Contributor

    Is the intent here to use the above as potentially a dynamic service indicators, and ability to access "non-approved, allowed", "legacy use", "deprecated" and so on algorithms?

    No, the concept of service indicator and all the various NIST states you enumerate (which I wholeheartedly agree with you are getting ridiculous) are FIPS 140-3 compliance concepts that need to be enforced or anyway defined by the FIPS 140-3 module. For those I refer you to the forthcoming Security Policy and to #69536 (comment).

    fips140=only is a best-effort mechanism implemented outside the FIPS 140-3 module, which I would recommend more for debugging and testing than for production. Anyway, I'm happy to switch "not allowed" to "blocked", feel free to open an issue so we don't forget. I don't want to call it "FIPS 140 approved-only mode" though, precisely because "approved" has a very specific meaning I don't want fips140=only to commit to.

  20. xnox commented on Feb 21, 2026

    @xnox
    Contributor

    I bet whichever unused term I pick, will be precisely the next term used by the next SP draft.

    And yes, there are a lot of policy choices people make - afterall even if something is approved one may have a unique opinion if one wants to use it or not.

    CNSA in this sense at least is a lot better - use this one cipher, and you are done.

  21. panga commented on Mar 23, 2026

    @panga

    @FiloSottile I see you’re working on FIPS compatibility with MLKEM+X25519. Is it in the short term plan to fix the tls issue described in #78298 ?

    If I understand correctly it requires adding WithoutEnforcement in that code path.

    Thank you in advance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolProposalProposal-Accepted

Type

No type

Projects

  • Status
    Accepted

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions