
Critique of a hybrid identity scheme: Ed25519 today + truncated commitment to Falcon/Dilithium for post-quantum migration
I'm designing the identity scheme for hashiverse, an open-source peer-to-peer twitter/x replacement that I want to remain robust against a future quantum adversary, without paying the size cost of shipping full PQ public keys today. I'd appreciate a teardown to find flaws before I'm stuck with it in deployed code.
The scheme
At identity creation, the user generates three keypairs locally (based on a long keyphrase or some other locally generated entropy):
- Ed25519 (classical signing).
- ML-DSA-44 (Dilithium, FIPS 204).
- Falcon-512 (FN-DSA, FIPS 205).
The public identity, called ClientId, is:
ClientId = {
verification_key: <Ed25519 public key, 32 bytes>
pq_commitment: <16 bytes Blake3(Falcon512_pubkey)[..16]
|| 16 bytes Blake3(MLDSA44_pubkey)[..16]>
id: Blake3(verification_key || pq_commitment)
}
Public identity material is 64 bytes (verification_key + pq_commitment), plus a 32-byte derived id used for routing.
The full PQ public keys (~897 B Falcon-512, ~1312 B ML-DSA-44) are NOT shipped or published today. The user holds them privately. Today, every signed artefact in the network is signed with Ed25519 only; the PQ private keys sit unused.
The future-proofing claim
If Ed25519 becomes practically forgeable (e.g. a CRQC capable of solving discrete log on Curve25519), the network performs a coordinated migration:
- Each user reveals their Falcon-512 public key.
- Verifiers check the revealed key against the user's 16-byte Falcon commitment.
- Subsequent signatures must be Falcon-512.
- If Falcon is also subsequently broken (e.g. a structural attack on NTRU lattices), fall through to ML-DSA, verified against the user's own committed 16 bytes.
Cost today: 32 bytes per identity for the commitment, versus ~2,200 bytes for shipping both PQ public keys directly.
Specific concerns I'd appreciate critique on
- Truncated commitment security. Each PQ public key is committed via the first 16 bytes (128 bits) of Blake3. Classical pre-image security is ~2^(128,) which I read as acceptable. Under Grover it drops to ~2^(64,) which feels uncomfortably close to feasible by the time a CRQC actually exists. Is 16 bytes enough, or should I expand to 32 bytes per algorithm?
- Multi-target attack scaling. With N visible identities, a quantum attacker willing to forge any one of them (rather than a specific target) needs roughly 2^(64) / sqrt(N) work under Grover for a pre-image. For N around 2^(20,) that's around 2^(54) work. At what scale does this stop being theoretical?
- Transition window vulnerability. Between the day a CRQC becomes practical and the day the network completes migration to PQ verification, all existing Ed25519-secured identities are forgeable. There's no automatic switchover; some governance step has to happen. Are there cleaner designs that let verification be "the latest still-unbroken algorithm in the committed set" without requiring a coordinated fork?
- Independence of the two PQ commitments. Falcon and Dilithium are committed independently. If only Falcon is broken (e.g. as a function of its specific lattice structure), I want verification to fall back to Dilithium. Is this fall-back-to-the-next-algo pattern sound, or does the independence of commitments hide a subtlety I'm missing?
- Identity immutability. Once created, the
ClientIdis permanent. No rotation, no algorithm swap, no cross-signed succession. A "please follow me on my new identity" social protocol is documented as future work but not built. If a user's Ed25519 private key is compromised today (pre-quantum), they have no in-protocol recovery and must abandon the identity. Is permanent identity defensible as a design choice, or is this a fundamental design wart? - Pre-release primitive. I'm using the
ml-dsaRust crate at version 0.1.0-rc.7. The crate authors note that generated keys may change before v1.0. Any identities I let users create now risk invalidating their Dilithium commitment when the crate stabilises. Practical concern, or am I overthinking the lifecycle?
I'd value any teardowns. The scheme isn't deployed at scale yet, so a critical flaw caught now is still cheap to fix.
Code: https://github.com/hashiverse/hashiverse, specifically hashiverse-rust/hashiverse-lib/src/tools/keys_post_quantum.rs and client_id.rs.