Hi Nick,

Thank you for raising this up from the deeper sub-thread discussion. I appreciate the clarity of your summary.

On 7/13/26 11:59, Nick Sullivan wrote:
Hi TLSWG,

Pulling this up from a deep subthread so it doesn't get lost. There are two distinct attacks against a state-recoverable DRBG worth naming separately. Both work the same way: one exposure of RNG output lets the attacker walk state forward and predict every subsequent draw from the same DRBG.

Scenario 1: passive eavesdropper. Recovery vector is a wire nonce like ServerHello.Random. Applies to both KEMs and DH.

Scenario 2: adversary client. Recovery vector is `m`, which the adversary recovers by decapsulating the server's ciphertext. Applies
to KEMs only, not to DH.

This is right, and both reproduce end-to-end -- the PoCs aren't just
constructible, they're built... Scenario 1 (ServerHello.Random / TLS 1.3
legacy_session_id) and Scenario 2 (the `m`-oracle) both run against real
builds of OpenSSL, LibreSSL and other libraries when the RNGs have a
trapdoor construction.

Scenario 2 applies to KEMs but not DH because the FO transform requires the decapsulator to recover the encapsulator's `m`. DH's peer sees only `a*G` behind ECDLP. No raw randomness ever crosses. This is a KEM- abstraction property, not ML-KEM-specific.

Agreed, and worth stressing exactly as you frame it: the oracle falls
out of FO requiring the decapsulator to recover `m`, so it works on any
FO-KEM that exposes raw coins -- not just ML-KEM.

But where should be careful is that in practice this actually can impact
DH as well. Hybrid constructions without the `m` oracle are just as
vulnerable.

The attacker in Scenario 2 needs to make enough connections to your server to accumulate the `m` values needed to recover DRBG state. For a Dual_EC-shape DRBG a single 32-byte `m` is enough. Other state- recoverable constructions may require more.

Correct -- one 32-byte block recovers the state, and recovery is
forward-only from there. If the block is truncated as was shown in the
past with Dual_EC_DRBG, then it is trivial to bruteforce the truncated
bits for guessing and confirmation.

What both scenarios share: once DRBG state is recovered, the attacker passively decrypts every subsequent connection to that server from just the network flow. Every user, every session,
decrypted from the wire without touching the server again. This
continues until the DRBG reseeds. For a stack that does not
explicitly reseed, that means for the life of the process. Predicted
`m` plus observed `ek` gives the KEM shared secret directly. This is
the mechanism behind the 2015 Juniper ScreenOS Dual_EC incident,
which put passive VPN decryption within reach of whoever held the> trapdoor to 
the substituted Q constant. Nothing about ML-KEM makes
it immune to the same mechanism. That cascade is the actual concern.

One point that sharpens the urgency: this needs no nation-state and no
substituted constant. Anyone who supplies their own P, Q holds the
trapdoor secret(s), and at least one shipping library still exposes
caller-chosen P, Q - so the same cascade is reproducible by anyone, not
only by whoever swapped Q as in the ScreenOS case.

How the server arranges its DRBGs determines which scenario the attacker would take. Under a shared DRBG (ServerHello.Random and `m` from the same source), Scenario 1 is available to any passive observer without an active connection. This is Ben Kaduk's point. Splitting the DRBGs closes Scenario 1 but leaves Scenario 2 as the primary path against the DRBG that feeds `m`.
Yes -- with one addition: because recovery is forward-only, a *single*
late leak from the `m`-DRBG also seeds every subsequent session until
reseed. So splitting alone isn't sufficient for the `m` path; that DRBG
still has to be state-non-revealing (or `m` hashed).

David Benjamin has argued that the fix belongs at the RNG layer, not
at ML-KEM or inside TLS. That seems right. Use a DRBG whose output
does not reveal its state (duh), and which provides post- compromise
recovery (via reseeding from fresh entropy by design, or via per-
connection reinitialization at the caller). Either way, any state
exposure is bounded in time. This approach is KEM-generic and applies beyond ML-KEM.

I can't entirely agree with David or your conclusion here. ML-KEM was made qualitatively weaker by removing the hash without a valid technical justification that also expressly mitigates this class of issues. Falling back to the same justification as NIST (requirement: "use a NIST-approved DRBG" or similar) isn't robust. More importantly it isn't responsiveness to the threat of security failures in those DRBGs which is unrelated to entropy concerns.

One observation I would offer is that when speaking with a BSI person recently at a conference, they were very focused on the math and the purity of the formulation but not at all concerned about the stuff outside of the pure math issues. I fully understand that perspective as it is a fair separation of concerns. Adversaries don't care, they want to win. So if an Adversary can't beat the lattice hardness assumptions, they can expect the mathematicians to ignore something that isn't mathematically interesting. This is exactly the pattern we see here and so we should just avoid falling into that trap.

We know many people will simply read random bytes into `m`, in practice, and they we won't meet the requirements that ensure that the lattice problems are the hardness assumption that matters. Thus, defense-in-depth helps with the actual end user scenarios.

Another core issue with simply delegating to a DRBG is that it isn't possible to be sure that the output doesn't reveal its state without a more precise threat model.

Doing both is the durable and robust answer when we can't be certain about possible intentional hidden structure. Sometimes it is obvious (e.g., software Dual_EC_DRBG ) but sometimes the raw sample can reveal hidden structure from the DRBG unintentionally in specific attack scenarios. Hashing `m` at the KEM layer is a cheap, interop-transparent defense-in-depth barrier that blunts Scenario 2 specifically. It is complementary to the suggestion of requiring RNG fixes, not a substitute.

Runnable PoCs for both scenarios are trivial to construct.

Yes, I am happy to share implementations for generating your own parameters and carrying out each of these attacks. I have also looked at many libraries and the software using them in various protocols. ML-KEM, TLS, and other related IETF security protocols all will require some changes to be robust against this class of attacks.

Let me know if I have this summary right.

You have it mostly right, I think. There are additional details such as how ECH is impacted, and there are other protocols where this is similarly problematic. IETF should protect all of those by ensuring ML-KEM means ML-KEM with a hashed `m` as a basic start. Systematically eliminating all other issues is related and important work.

Kind regards,
Jacob Appelbaum

Nick


_______________________________________________ TLS mailing list -- [email protected] To unsubscribe send an email to [email protected]

_______________________________________________
TLS mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to