11,039 karma · joined May 9, 2012
[ my public key: https://keybase.io/filippo; my proof: https://keybase.io/filippo/sigs/c51KtcfccPH0D3jG9PtQBZZh6AqhvB5MHIz2YmkupAc ]
We can solve the storage requirements, it’s fine.
The assertion is that the NSA is subverting standards processes to push pure ML-KEM key exchanges, the same algorithm they are requiring for their own TS//SCI information in CNSA 2.0.
There is mathematically no place for a NOBUS backdoor in ML-KEM (see https://keymaterial.net/2025/11/27/ml-kem-mythbusting/), so the assertion is that the NSA wants the US Government to use broken cryptography for its most sensitive information. Broken in the sense that an adversary or academic could discover how to break it tomorrow (or yesterday), throwing the government signals handling in disarray, or silently causing a counter-intelligence catastrophe.
That's... ridiculous? Bernstein wraps this in a lot of words and emotionally charged gish gallop, but the core technical point doesn't hold. And it's getting tiresome, and getting in the way of actually important PQ rollout work.
Before anyone claims there's precedent: there isn't. Dual_EC_DRBG was a NOBUS backdoor (and anyway was not authorized for TS//SCI as part of Suite B, the predecessor of CNSA 2.0), and export ciphers were for everyone else not for USG data.
(But also, it's easy to see how this is a problem for public keys and ciphertexts, and it would be weird to have an inconsistent API for private keys.)
Also, "an unambiguous key type that can be constructed from a []byte or responsibly generated on your behalf" is exactly what crypto/mlkem exposes.
I honestly thought it could not be done safely, but the runtime/secret proposal discussion proved me wrong.
These are actually very deliberate choices, based on maybe unintuitive experience.
We use []byte instead of e.g. [32]byte because generally you start with a []byte that's coming from somewhere: the network, a file format, a KDF.
Then you have two options to get a [32]byte: cast or copy. They both have bad failure modes. If you do a ([32]byte)(foo) cast, you risk a panic if the file/packet/whatever is not the size you expected (e.g. because it's actually attacker controlled). If you do a copy(seed, foo) it's WAY WORSE, because you risk copying only 5 bytes and leaving the rest to zero and not noticing.
Instead, we decided to move the length check into the library everywhere we take bytes, so at worst you get an error, which presumably you know how to handle.
> why we can't just pass in a seed value to a single unambiguous constructor when generating asymmetric keys
I am not sure what you are referring to here. For e.g. ML-KEM, you pass the seed to NewDecapsulationKey768 and you get an opaque *DecapsulationKey768 to pass around. We've been moving everything we can to that.
> Or how the constructor for a key pair could possibly return an error, when the algorithm is supposed to be deterministic.
Depends. If it takes a []byte, we want to return an error to force handling of incorrect lengths. If the key is not a seed (which is only an option for private keys), it can also be invalid, deterministic or not. (This is why I like seeds. https://words.filippo.io/ml-kem-seeds/)
> removing all dependencies on rand would make it obvious where the entropy must be coming from (the seed parameter)
Another place where experience taught us otherwise. Algorithms that take a well-specified seed should indeed just take that (like NewDecapsulationKey768 does!), but where the spec annoyingly takes "randomness from the sky" (https://words.filippo.io/avoid-the-randomness-from-the-sky/) in an unspecified way, taking a io.Reader gave folks the wrong impression that they could use that for deterministic key generation, which then breaks as soon as we change the internals.
There is only one place to get entropy from in a Go program, anyway: crypto/rand. Anything else is a testing need, and it can be handled with test affordances like the upcoming crypto/mlkem/mlkemtest or testing/cryptotest.SetGlobalRandom.
The public key algorithm has been frustratingly blocked on the IETF and CFRG, which are taking more than 15 months since FIPS 203 to stabilize a simple hash-based hybrid combiner.
You might want to read https://words.filippo.io/csrf.
Yes, even if Bluesky was down (as long as they have a backup) which is not the case for ActivityPub.
Worse, by the original definition http://foo.example.com and https://admin.example.com are same-site, and unless the site uses HSTS with includeSubDomains, any network attacker controls the former. Chrome changed that with Schemeful Same-Site in 2020, but Firefox and Safari never deployed it.
This doesn’t prove a negative, but is probably a good hint that this risk is not something worth prioritizing for Go applications from a security point of view.
Compare this with C/C++ where 60-75% of real world vulnerabilities are memory safety vulnerabilities. Memory safety is definitely a spectrum, and I’d argue there are diminishing returns.
We avoided the DIV by deliberately not using a modulus operation and doing Barrett reduction instead.
Porting the assembly to higher-level generators has nothing to do with lawyers (??), the goals are stated in https://go.dev/wiki/AssemblyPolicy.
The idea that one day we'll write All Of The Cryptography Code Once And For All In The Perfect Language and reuse that across languages comes up pretty regularly, and has never panned out.
Besides integrating properly with Go applications, this lets us optimize for readability and correctness, with IMHO excellent empirical results.
https://words.filippo.io/a-literate-go-implementation-of-pol...
It depends, but if you are targeting Security Level 1 (which is what most folks think about when they think about FIPS 140) you generally don't need your entire application to be validated, only the cryptographic module.
So (again, depending on your requirements and on the Operating Environment you deploy to and on what algorithms you use and how) setting GOFIPS140 might actually be all you need to do.
FIPS 140-3 mode is enabled by building with GOFIPS140=v1.0.0 (or similar, see https://go.dev/doc/security/fips140), but it shares 99% of the code with non-FIPS mode.
Still, your message is right, just GOFIPS140=off (the default!), not GOFIPS140=v1.0.0.
The system libraries approach used by Microsoft Go is cgo based IIUC, and I think derived from Go+BoringCrypto. I understand they are working on migrating their bindings to fit better downstream of the new native mode.
https://go.dev/doc/security/fips140
Yup.
https://github.com/FiloSottile/torchwood/tree/main/cmd/apt-t...
Edit: well https://webauthn-passkeys-prf-demo.explore.corbado.com/ works with an iCloud Keychain passkey on Safari on macOS 15.5, but Typage doesn't work with a YubiKey 5, so there is some support (the MDN data is out of date) but also something weird.
I’ll mail that one towards the end of the week.
It’s unfortunately an estimate, because right now we see 300 Mbps peaks, but as Tuscolo moves to Usable and more monitors implement Static CT, 5-10x is plausible.
It might turn out that 1 Gbps is enough and the P95 is 500 Mbps. Hard to tell right now, so I didn’t want to get people in trouble down the line.
Happy to discuss this further with anyone interested in running a log via email or Slack!
I still have high-value passwords and CLI credentials in passage + age-plugin-yubikey.
Once the module version is in GOMODCACHE, it's extremely fast, not even noticeable.