Post-quantum cryptography
New polls encrypt answers with X-Wing hybrid key encapsulation, and published results are signed with ML-DSA-65. Here is what uses which algorithm.
- Security reviewers
- 3 min read
- Updated Oct 14, 2026
- Reviewed by Security
Heads up
Why PQC (harvest now, decrypt later): ciphertext saved today could fall to a future quantum computer. Every new poll uses hybrid classical + post-quantum key encapsulation; pair this page with Encryption model overview for the full stack.
We use post-quantum hybrid cryptography so answers archived today stay hard to open later. Anonymity doesn't depend on the algorithm choice: nothing we store links an answer to its author, whatever cipher sealed it.
#What uses which algorithm
| Use | Algorithm |
|---|---|
| Sealing answers on new polls | X-Wing (X25519 + ML-KEM-768), then AES-256-GCM |
| Wrapping folder keys for each person | X-Wing |
| Signing published results documents and shared slices | ML-DSA-65 |
| Polls created before the post-quantum upgrade | RSA-OAEP 2048 (unchanged) |
| One-vote ballots on personal invites | RSA-2048 blind signatures |
Answers themselves aren't signed. A signature goes on the aggregate document an admin publishes, so a reader can check the numbers weren't changed afterwards. See Verifying a published results document.
Personal invites use a separate, classical primitive on purpose: a blind RSA signature is what lets us sign a ballot without seeing it, so we can enforce one vote per invite without being able to match the answer to the invite. There's no standard post-quantum blind signature yet. A future quantum attacker could forge ballot signatures for a poll, but that's a question of vote counting after the fact, not of reading answers.
What this buys you
Hybrid encapsulation combines X25519 with ML-KEM-768, so an attacker has to break both to open an answer.
#How an answer is sealed
- Publish the poll's public key. The poll's X-Wing public key is served with its questions.
- Encapsulate with X-Wing. The respondent's browser runs the hybrid key encapsulation against that key.
- Derive and seal. HKDF-SHA-256 derives an AES-256-GCM key, and the answers are sealed under it.
- Store ciphertext. We store a versioned blob that opens only with the poll's private key.
const { ciphertext: kem, sharedSecret } = xwingEncapsulate(pollPublicKey);
const aesKey = hkdfSha256(sharedSecret, randomSalt, "response-v2");
const sealed = aesGcmEncrypt(aesKey, randomIv, payload);
// stored as { version: 2, salt, xwingCiphertext: kem, iv, ciphertext: sealed }
#Browser constraints
| Library | @noble/post-quantum, open-source, pure TypeScript |
| Runtime | No WebAssembly for the post-quantum code |
| Storage | Key material in IndexedDB |
The library is open-source and auditable. It hasn't had an independent third-party audit, so treat it as you would any young implementation of new standards.
#Upgrades and versioning
The post-quantum upgrade happens per admin account, not per poll or workspace. The first time an account signs in after the upgrade, its browser creates an X-Wing collaboration key and re-wraps the folder keys and poll keys it holds, then tells us the account has moved to the new version. Polls created before that keep their original RSA-OAEP keypair, so their answers stay readable; every poll created afterwards gets an X-Wing keypair.
Each stored answer carries a version field, so the admin's browser knows which algorithm opens it.
Next
Return to Encryption model overview for the product-level story, or Zero-knowledge architecture for confidentiality mapped to behaviour.