Skip to content
All articles
Audit report

From the audit floor: replay-attackable post-quantum prekeys

By qproof Audit5 min
TL;DR

The finding

Post-quantum prekeys were issued one-shot, but the server had no mechanism to retire them after use. The same ML-KEM encapsulation could be replayed, defeating forward secrecy. The fix is the same discipline as classical one-time prekeys: atomically consume on use.

Adding post-quantum protection to a messaging protocol is rarely a single change. Identity keys, signed prekeys, one-time prekeys, the initial root-key derivation, the message ratchet, group key derivation, backups, multi-device sync — each is a place where asymmetric primitives live, and each is its own migration decision.

What we found

In this engagement, the team had correctly added an ML-KEM encapsulation against a post-quantum prekey. But the prekey was treated as reusable: the server returned it, and returned it again, without removing it from the pool when it was consumed.

From the audit floor

Why it matters

An attacker with access to the server — or to a single compromised prekey response — could replay the same ML-KEM encapsulation against the same prekey. The shared secret is no longer fresh, and the forward-secrecy property the prekey was supposed to provide is gone.

The fix is not novel. One-time prekeys, classical or post-quantum, must be consumed atomically: the server removes the prekey at the moment it is handed out for a session, and never serves it twice. The post-quantum case is identical — the discipline simply has to be carried across to the new primitive rather than reinvented.

Pitfall

Ignoring the prekey distribution channel

ML-KEM prekeys are ~1184 bytes each. A pool that previously distributed 100 classical prekeys per user now moves over 100 KB per user. Plan the distribution channel — bandwidth, pool sizing, and battery on mobile clients that fetch and verify them.

Have a system that needs this?

Secure my organization