Knowledge base Talking to people
Quantum-resistant encryption
Private messages and group chats between Nymchat users can be encrypted so that recording them today buys an attacker nothing, even once quantum computers arrive.
The problem it solves
Ordinary Nostr encryption — NIP-44, which is what private messages and group chats are built on — has two halves. The half that actually scrambles the text (ChaCha20, HMAC-SHA256, HKDF) holds up fine against a quantum computer. The half that agrees on the key does not.
That key agreement is elliptic-curve Diffie-Hellman on secp256k1, the same maths Bitcoin uses. Shor's algorithm solves it outright. Anyone who runs it against a public key recovers the private key, and with it every shared secret that key ever produced.
This is not a problem that starts when quantum computers arrive. An adversary with enough storage can record encrypted traffic today, sit on it, and decrypt it years later. Anything you send now that still matters then is already exposed. That is the specific attack this feature defeats.
How it works
Nymchat leaves the NIP-44 message format completely alone. The key agreement it already uses is untouched; a second, independent one is added around it. So there are two key exchanges per message instead of one:
- the standard secp256k1 ECDH, exactly as before;
- ML-KEM-768, the post-quantum algorithm NIST standardized as FIPS 203. Its security rests on lattice problems, which Shor's algorithm does not touch.
The two are layered, not mixed. The message is encrypted with NIP-44 exactly as it always was, and that finished ciphertext is then sealed a second time inside the post-quantum layer — ChaCha20-Poly1305, keyed from the ML-KEM shared secret. The sender's key, the recipient's key, the encapsulation ciphertext and the recipient's ML-KEM key are bound into that derivation as context, so a layer cannot be lifted off one exchange and replayed onto another.
A combiner such as X-Wing or Signal's PQXDH mixes both secrets into one key instead. Nymchat does not, and the reason is practical rather than cryptographic: mixing needs the raw ECDH output, and a browser extension or remote signer will never hand that back — it returns a finished ciphertext, which is the whole point of holding the key where the app cannot reach it. Layering keeps NIP-44 whole and produced by whatever holds the identity key, signer included, so those logins are ordinary participants rather than excluded ones.
The important property is the same either way: the result is at least as strong as the stronger half. Reading the message means getting through both layers. If lattice cryptography turns out to be weaker than hoped, breaking ML-KEM only strips the outer layer and leaves today's NIP-44 underneath. If secp256k1 falls to a quantum computer, the inner layer opens but the outer one does not. An attacker has to break both.
Every message gets a fresh post-quantum exchange, so recovering one message's key reveals nothing about the others.
The post-quantum half needs a key of its own, and where that key comes from is what decides
whether the whole thing is worth anything. It is deliberately not worked out from
your nsec. A quantum computer that breaks the classical half recovers your
nsec from your public key, and anything derived from the nsec by a
published formula comes out with it — two locks opened by one break. So Nymchat
generates a separate post-quantum code for each account, once, from random
bytes that no public value exposes. It reads as nympq1… and sits beside
your nsec in your Nym's details, revealed and copied the same way.
It is not a password and not a second login: it is key material you can save, exactly like
the nsec.
There is nothing to turn on
This is not a setting. Post-quantum encryption is simply how Nymchat talks to Nymchat, the same way ordinary encryption is how it talks to everything else. It applies automatically to every private message and group chat with another Nymchat user.
Settings › Privacy & Security shows whether it is active and how many of your contacts it currently reaches, but only as information — there is no switch.
This works whichever way you log in. A browser-extension (NIP-07) or remote-signer (NIP-46) login never hands your identity key to the app, which is the whole point of using one — and it does not have to. The two halves of the encryption are kept apart: your signer does the part it has always done, and the app does the post-quantum part with your recovery code, which it holds directly. Extension and remote-signer logins are ordinary participants in both directions. The status line in Settings says what applies to you.
Nothing you already have is affected. Every message in your history stays readable on every
device with nothing but your nsec, exactly as before, and stays that way for the
life of the account. What needs the nympq1… recovery code is the quantum-resistant
material written from now
on: new messages, and the synced settings and archive that travel with them.
So a device has to hold that code before it can open those, and adding a device now takes one
extra step. In the same panel on the new device you paste the nympq1…
code from a device that already has it. It is once per device, not once per login. That step is not friction we failed to remove: a code that could arrive
on a new device by itself would have to travel there inside an ordinary message, and that
message is exactly what the attacker this feature exists to stop is recording. The step is
the reason the protection is real.
The app generates the nympq1… code for you, so there is nothing to choose and
nothing to invent.
To see it: tap your nym to open View or Edit Nym's Details, then
Reveal this nym's private key and recovery code. It sits directly below
your nsec, with the same show and copy buttons. You never have to think about
it until the day you add a second device.
An unlinked device is not locked out in the meantime. It keeps reading everything written under the older encryption and keeps sending normally, and once you link it, it goes back and opens the quantum-resistant messages it could not read before. Nothing is lost by linking late.
A second device needs two things: a current version of the app, and the
nympq1… code. An older build keeps working — it is not cut off
— but it cannot read the current format, so while it is still on the account the
copies you keep for yourself fall back to the older one that it can. An up-to-date device
that has not been linked yet reads your older history and asks for the code. Neither can
tell us it exists, which is why this is a notice rather than something we detect for
you.
Who you get it with
Both sides need it. Every Nymchat client publishes a small signed, expiring announcement saying so, and includes its post-quantum key when it has one. Holding a valid announcement for someone is what makes the app use the stronger exchange with them. There is no negotiation to interfere with: either the key is there or it is not, and because the announcement is signed by its author, a substituted key would have to be forged rather than merely served.
What an attacker who controls a relay can do is withhold the announcement, so the sender never sees a key and falls back to ordinary encryption. That is worth stating plainly. It costs post-quantum protection for those messages; it cannot make one unreadable, and it cannot make you accept a key that is not yours. Clients ask several relays, so suppressing it takes control of all of them.
Those announcements last a week and refresh themselves daily while you use the app. If you are away longer than that, yours lapses and you are read as an ordinary Nostr client for a while: messages sent to you in that gap are encrypted the normal way rather than the quantum-resistant way. They still arrive, and you can still read them — and nothing you received earlier is affected. Opening the app republishes your announcement, and the stronger encryption resumes on its own. There is nothing to redo.
Everyone else keeps working exactly as before. Bitchat users and any other Nostr client receive standard NIP-17, unchanged. Nothing breaks when you message someone outside Nymchat.
A post-quantum message replaces the standard one rather than being sent alongside it. Sending both would be pointless: an attacker would simply break the weaker copy.
The copies addressed to you get it as well — the one your other devices read, the archive behind it, and your synced settings, which travel the same way and carry your conversation list, your group keys and your message history. Leaving any of those on the old encryption would have made them the weakest thing you store: readable by anyone who breaks today's cryptography, no matter how carefully the messages themselves were sealed.
Staying readable by Bitchat
Bitchat cannot read Nymchat's message format, so historically Nymchat sent a second copy of every message in Bitchat's format to anyone who might be using it — which, for someone you had never heard from, meant everyone.
The announcement above makes that guessing unnecessary. If someone has published one, they are running Nymchat, so the Bitchat copy is dropped and only the stronger message is sent. If they have not, nothing has changed: they still receive both formats, exactly as before, so nothing you send can become unreadable because the app guessed wrong about which client someone uses.
Like the encryption itself, this is automatic and has no setting.
Nymchat could infer who is running what from public channel activity, and it would spot people sooner. But that inference is occasionally wrong, and being wrong here means sending somebody a message their app cannot open, with no error and no retry. A published announcement is signed by its author and cannot be faked, so it is used instead.
In group chats
Group messages are already encrypted separately for each member, so a group can mix both kinds freely — members whose clients support it get the stronger encryption, the rest get the standard one, and the group works normally either way.
Because of that, a group message is only marked quantum-resistant when every member received it that way. If even one copy used the standard exchange, that copy carries the same message, so the message as a whole is only as protected as its weakest copy. The app says "quantum-resistant to 8 of 10 members" rather than rounding up.
Group chats also keep their rotating one-time keys, which hide who is in a group from anyone watching a relay. That protection is unaffected and works exactly as it did.
Reading the badge
Every private message and group message carries a small shield next to the padlock. Tapping either mark explains what it is showing.
| Mark | Meaning |
|---|---|
| Padlock | Who sent it — whether the signature matches the claimed author. |
| Shield | Quantum-resistant. Both sides held a post-quantum code of their own, and the hybrid exchange was used for every copy of this message. |
| Dashed shield | Partly quantum-resistant — a group message only some members received a protected copy of. Because the others carry the same text, treat the message as conventionally encrypted overall. |
| Struck-through shield | Not quantum-resistant. Standard NIP-44 encryption — still end-to-end encrypted, and still unreadable to anyone but the participants today. |
| No shield | Not an encrypted message. Public channel posts are readable on the relay by design, so no encryption mark applies. |
The shield is always present on an encrypted message, and says plainly when the answer is no. A missing mark would be ambiguous — it could equally mean “not quantum-resistant”, “this build is too old”, or “the indicator is broken” — and a security indicator that cannot be distinguished from a broken one is not telling you anything.
It sits beside the padlock rather than replacing it because the two answer different questions, and a message can be one without the other — quantum-resistant but from an unverified sender, or verified but conventionally encrypted.
Older messages keep the struck-through shield permanently. Anything sent before both sides were on a quantum-resistant build was sealed with the conventional exchange, and that ciphertext already exists on the relays — it cannot be re-sealed after the fact. This is exactly the harvest-now-decrypt-later exposure the upgrade closes going forward, so the badge showing it is the honest answer rather than a fault.
What it does not do
This protects the contents of your messages. It does not make every part of Nostr quantum-proof, and it is worth being precise about the difference.
- Signatures are still secp256k1. Nostr identities sign with Schnorr on the same curve. An adversary who already had a working quantum computer could forge a signature and impersonate someone in real time. What the hybrid exchange stops is reading recorded traffic — confidentiality, not authentication.
- There is no forward secrecy. Both keys are long-lived: NIP-44 derives
its key from your
nsec, and the post-quantum layer encapsulates to the ML-KEM key behind yournympq1…code. Every message gets a fresh encapsulation, so one message's key does not open the next — but whoever ends up holding those long-term keys can read everything they ever recorded, not only what came after. That is what expiring messages reduce, and it is why this feature is aimed at harvest-now-decrypt-later rather than at a key that gets taken. - Metadata is unchanged. A relay still sees that an event was addressed to a key, and roughly when. That is handled by gift wrapping and one-time keys, not by this.
- The Bluetooth mesh is not covered. A message carried over the mesh travels inside a Noise XX session, which is strong, current encryption but a classical key exchange — so it does not carry the post-quantum layer, even between two Nymchat users. The badge reflects this: a message delivered over the mesh is not marked quantum-resistant. Adding it there means changing the handshake itself, which both peers would have to do at the same moment, with no relay to fall back through and while staying readable by Bitchat.
- Your recovery code is a second thing to keep. It starts with
nympq1and you will find it in your Nym's details, in the same panel as yournsec— tap your nym, then “Reveal this nym's private key and recovery code”. Yournsecon its own does not rebuild your post-quantum key — that is precisely why it works — so if no device holds that code and you have no copy of it, what was sealed to it cannot be opened again. There is no reset and nobody to ask. Save it wherever you saved yournsec. - It cannot protect what the other side does. A recipient can still screenshot, copy or keep a message.
This is the same position Signal takes with its own post-quantum upgrade: fix the key exchange now, because that is the part an attacker can act on retroactively, and treat signatures as a separate problem for when the standards catch up.
Checking the work
The ML-KEM implementation is tested against the official test vectors NIST publishes for FIPS 203, so it is checked against the standard itself rather than only against itself. The web app and the mobile app are additionally held to a shared set of test vectors, which is what guarantees a message written by one can be read by the other.
Both are open source under the AGPL-3.0, and the web app's builds are reproducible — see verifying a build.
Going deeper: Post-Quantum Key Agreement in Nymchat — a technical whitepaper on how the ML-KEM key is distributed without a directory, the exact construction that combines the two key exchanges, and the designs that were rejected.