Microsoft Go 1.24 FIPS changes
devblogs.microsoft.com
devblogs.microsoft.com
For a variety of reasons - including "CGo is not Go" (https://dave.cheney.net/2016/01/18/cgo-is-not-go) - using boringcrypto and requiring CGO_ENABLED=1 could be a blocker. Not using system libraries meant that getting all of the software to agree on internal certificate chains was a chore. Go was in a pretty weird place.
Whether FIPS 140 is actually a good target for cryptography is another question. My understanding is that FIPS 140-1 and 140-2 cipher suites were considered by many experts to be outdated when those standards were approved, and that FIPS 140 still doesn't encompass post quantum crypto, and the algorithms chosen don't help mitigate misuse (e.g.: nonce reuse).
Answer to that question: it is not. It's a thing you only do when you are forced to by some requisitions process.
We're going through a FIPS compliance project right now and it's nothing more than a miserably expensive set of check boxes. Contractor they hired is incompetent so that isn't helping either.
We created software and we wanted it to be secure (by ourselves). When it came to FIPS compliance, we had to pay for certification, but we were already compliant anyway, so it was only procedure.
It's definitely not as strict as the latest developments in crypto would demand, but it does cover the product in its entirety. Like, oh, you have encryption, but you have a backdoor here: not FIPS-2 compliant.
So, yes, plenty of software and products are FIPS compliant. And if it's mandated, then the provider is not chosen.
If you're already compliant (implying you are using FIPS compliant crypto in all situations), the matter is tautological.
My experience lies mostly in C and embedded applications, where the physical security was also in question and that is where the most of the effort to certify went. Software was relatively easy by using the right versions. So, maybe not just a certification, but non-fretting one.
Golang projects however don't compile against OpenSSL instead using the internal cryptography. In many cases rebuilding and repackaging a Golang based tool is a no-no since now you are accepting ownership of it in an audit, when instead you want to point to an upstream vendor, or source code is not available.
So in many cases in production while the system itself is in FIPS mode (Linux kernel), and applications using system crypto libraries (OpenSSL primarily) are in FIPS mode, Go binaries are not in FIPS mode and may end up using algorithms that are explicitly disallowed by the FIPS standards.
It's just unfortunate for the many orgs delivering software that eventually customers start demanding this and it was really painful in Go.
With OpenSSL installed system wide you can turn on a flag and the library will force it into FIPS mode internally, with Go unless you build your binaries to link against OpenSSL like this Microsoft Go release seems to be doing you don't get that instant on system wide.
The latest version is FIPS 140-3 and is more up to date.
Note, binaries made for FIPS 140 with OpenSSL dynamically linked do not run on Alpine. While I've not looked at the Microsoft go/openssl compiler, it's worth testing its output across distros/versions as different distros have different versions of glibc and openssl.
GOLANG_FIPS=1
From https://news.ycombinator.com/item?id=42265927 :> Chrome switching to NIST-approved ML-KEM quantum encryption" (2024) https://www.bleepingcomputer.com/news/security/chrome-switch...
From https://news.ycombinator.com/item?id=41535866 :
>> Someday there will probably be a TLS1.4/2.0 with PQ, and also FIPS-140-4?
If you don't need FIPS for compliance you're better off investing in much more useful things for security (more effective linting, test coverage, keeping dependencies up to date, etc)
But it is a miserable exercise all in all. As you’re implementing it, you think “this is making things worse, less robust or secure, but oh well, that’s the only way it will sell”.
Why couldn't the crypto changes be rolled into mainstream Go?
Upstream Go tricks Windows into enabling long path support by setting an undocumented flag in the PEB. The Microsoft Go fork can't use undocumented APIs, so this commit removes the hack.
There is no documented way to enable long path support from within the process, so this this is a breaking change for the Microsoft Go fork. Note that the Go standard library makes a best effort to support long paths by using the `\\?\` prefix when possible, so this change should only affect long relative paths, which can't be used with the `\\?\`.
lol?
Ahh, I'm inferring (from [2]) even with the manifest entry, a system wide registry key is also required to get long paths working:
> I'm working with the Windows security team to find a way to enable long path support without having to modify the registry, just by using the embedded manifest, but the chances of this happening soon are quite low.
[1] https://learn.microsoft.com/en-us/windows/win32/fileio/maxim... [2] https://github.com/golang/go/issues/69853
Yeah, it's really sad. Go basically became like any other language.
Now even fun stuff like net.IP, and a netip, with netip.Addr. Lots of way to write the same thing. And an even further shift from Go initially being something along the lines of "You shouldn't control or even think of the garbage collector" to now having runtime.AddCleanup) as well as more fun hacks to technically stay compatible.
Now, I am not saying that the changes are bad per se, but more that they simply aren't matching the original design/claims/goals. I remember Rob Pike saying things like "The language likely won't change much" other than generics which have been said to likely come since pre-1.0 basically since Go got a website. The idea clearly was to stay simple, but that seems to have been thrown overboard. I wished Go's predecessors (Limo, Alef, et al) or something similar would have made it out of the more researchy state.
But hey looks like some more stable languages get traction. To my complete surprise Ada is making a comeback with [Alire](https://alire.ada.dev/) and such. Not really a Go competitor, but nice to see it's still being worked on.
https://words.filippo.io/dispatches/fips-hkdf/
Thought one of the Filippo/Go/FIPS posts had a big thread but doesn't look like it and/or can't find it.
I like the reference.
I've managed and implemented—along with Daniel McCarney, Roland Shoemaker, and Russ Cox—the native upstream Go validation mentioned in the intro, which is shipping in Go 1.24 and will be certified on Linux (amd64, arm64, ppc64le, s390x), Windows (amd64, arm64), macOS (arm64), and FreeBSD (amd64). The Linux operating environments were funded by various stakeholders, the rest were funded by Geomys for the benefit of the Go community.
There are some details now at https://go.dev/doc/security/fips140, but we're going to write a proper blog post once the module gets on the CMVP In Process list.
tl;dr is that it should soon take a single environment variable to transparently build against a FIPS 140-3 validated module which is just a slightly out of date version of the same Go standard library everyone else is using. (AFAIK this is the first non-JVM memory safe FIPS 140 module!)
If we ever meet in person, I own you a beer!
> When GODEBUG=fips140=only is used, in addition to the above, cryptographic algorithms that are not FIPS 140-3 compliant will return an error or panic. Note that this mode is a best effort and can’t guarantee compliance with all FIPS 140-3 requirements.
Will need to be more complete/certain in order to pass certification? My gut says it would based on the requirements, but I don't know how the auditors would respond in practice.
Even now it has been a lot of trouble trying to get vendors to provide us a way to rebuild/repackage their Golang binaries using the FIPS-140 support from RHEL, and if it is still the case that they need to build one-offs, teams will still need to rebuild/repackage a lot of Golang tools just to be FIPS compliant because it's not as simple as setting a flag like it is for OpenSSL.
We had to buy what felt like bootleg Canonical OpenSSL binaries, and Go looked like building some speculative forks that clearly had not been designed to be released.
Isn't this the entire FIPS scam? You have to do whatever your auditor says, even if it's ridiculous, and they are getting paid under the table by vendors.
In order to get that you need to hire a certification lab to verify what you did and from there the government processes it. That processing, reading paperwork and asking a couple questions, takes over a year at this point which is where a significant issue is. This is also a prereq to FedRAMP, which is a whole new set of paperwork and checkbox compliance that adds debatable quality.
I can understand the need to prevent unsafe solutions, but the current process is so slow and expensive that it makes selling useful things to customers that require FIPS a huge PITA.
One thing I do appreciate is how the relevant issue in the upstream Go repo says: "A number of companies must comply with them, for example as part of a broader FedRAMP compliance posture. (If that's not you, you can ignore this. Run!)" [1]
Apparently I'm just not the target audience. Sounds like I'm correct in assuming it's security theater at best, and an avenue for new backdoors at worst.
As for why not a library? Go has a pretty big "batteries included" implementation for networking. For customers demanding FIPS compliance it isn't enough to say, write all of your own application code to use a package that does FIPS-compliant TLS, but you have to make sure all of your libraries also use those algorithms. No rational person is going to vendor their entire dependency tree and maintain forks of the entire universe to swap `crypto/tls` and `net/http`. Well, some of these contracts are big enough to justify it, but whew, what a waste.
The FIPS compliant Go builds from Microsoft and now Go 1.24 make it a lot easier to check that box and unless a package implemented their own TLS stack it's easy to attest that the software uses FIPS validated crypto modules. Without that, good luck trying to sell to gov and highly regulated institutions in financial and health.
Maybe that works? But many of the standard library packages use linker shenanigans, if I recall, like //go:linkname pragmas. Maybe that's an issue, maybe not?
It would be interesting if the fork was unnecessary and it was possible to implement FIPS via package replacements! That would be a lot simpler.
I think at the time that would have been the only way to make it so any in-the-wild 3rd party code you want to use would use the FIPS-compliant libraries anytime someone imported "crypto/whatever" from the standard library.
I haven't tested the workspaces functionality [1] to see if it allows you to override standard library functionality, but in principle something either very like that, or slightly tweaked, would be enough that you could just use standard Go with a particular text file dropped into place and some libraries to override the standard library now.
But yeah, if you didn't even know this fork existed, you're not in the target audience. tptacek or someone else who really knows their stuff can Cunningham's Law me if I'm wrong, but my impression is that FIPS has a track record of rigidly demanding very medium levels of security, possibly including some rigidly poor security choices, and if you don't have a need to be in compliance, you can and should do much better by using newer and better options than what it mandates.
Any good modern conspiracy theories about Windows system libraries for cryptography?
While rebuilding your fleet of Go apps may be trivial in a technical sense, organizing and tracking deployment across an enterprise is less trivial. Tracking that your nodes are all running updated versions of Windows isn't trivial, either, but much easier.
FIPS is a PITA largely because of the change management burden. But that's precisely one of the biggest gaps in enterprise security. Keeping most of your installations up-to-date is easy; it's the long tail of stragglers and misfits that is the most difficult part. In that light, quibbling about algorithms is almost like bike shedding, though it's difficult to overestimate how much bad crypto is out there (see, e.g., last year's Okta bcrypt issue that is making the rounds again on HN). When people complain about having to use ECDSA P-256 instead Ed25519[1], I want to roll my eyes.
[1] Ed25519 finally became FIPS compliant only last year, there aren't many (any?) certified implementations, and in any event P-256 is just much more widely supported, especially in niches like HSMs, etc.
> This repository produces a modified version of Go that can be used to build FIPS 140-2 compliant applications. Our goal is to share this implementation with others in the Go community who have the same requirement, and to merge this capability into upstream Go as soon as possible.
It is also the default 'golang' package on Azure Linux 3.0.
Microsoft Go fork: https://github.com/microsoft/go
Issue discussing FIPS implementation: https://github.com/golang/go/issues/69536
But 3.0 is an actual new version of the distro with newer packages (like going from Ubuntu 22.04 to 24.04, as an example).