← Back to list

Obelisk now supports post-quantum direct messages

Built with Nostr WoT and QuantaKrypto. The messages ride inside ordinary NIP-17 gift wrap and no relay had to change anything.

Leon Acosta in Nostr WoT · 2026-08-19 14:36 · 0 claps · 6.8 min read
#nostr #cryptography #post-quantum-cryptography #privacy #cybersecurity
Open on Medium ↗
Wiki topics: CRY · Crypto & Web3 🔒 · Cybersecurity ⚛️ · Physics

Obelisk now supports post-quantum direct messages

Built with Nostr WoT and QuantaKrypto. The messages ride inside ordinary NIP-17 gift wrap and no relay had to change anything. Here is how it works, who can use it today, and the two things we got wrong.

They are running now, not behind a flag. If you use Obelisk and the person you are writing to has published post-quantum keys, the message that leaves your browser is protected against somebody who records it today and breaks elliptic-curve cryptography in fifteen years.

Three separate projects had to line up for that: the Nostr WoT extension, which holds your keys and does the signing; Obelisk, a relay-only chat client; and QuantaKrypto, which did the cryptographic design and review.

What is worth writing down is not that it works. It is the single constraint that decided which team could do what, because it is not obvious, several people assumed the opposite, and it will apply to anyone else attempting this.

The constraint

Post-quantum encryption here is hybrid. A shared secret from ML-KEM-1024 is combined with the ordinary NIP-44 conversation key through HKDF, and both go into the final key. If the lattice half is ever broken, you fall back to exactly the security you have today rather than to nothing.

That construction needs two things:

  • To encrypt, the raw NIP-44 conversation key.
  • To decrypt, the ML-KEM secret key.

Now look at what a signer actually exposes. NIP-07, the browser extension interface, hands back finished ciphertext and nothing else. It never returns the conversation key, because handing a web page the key would defeat the point of a signer. It certainly never returns a secret key.

So neither input is available to the caller. The cryptography can only be performed by the component that holds the key material, which is the extension. No client library can add post-quantum support on top of a signer’s public interface, no matter how it is layered. An SDK that tries will end up either exporting the conversation key, which is unacceptable, or reimplementing the signer, which is worse.

This is the part people get wrong, and the direction of the mistake is always the same: assuming this is a client feature. It is a signer feature that clients ask for.

Who owned what

Once that is clear the division falls out on its own.

Nostr WoT owns the keys. It derives an ML-KEM-1024 encryption key and an ML-DSA-87 signing key from a 24-word BIP-39 seed where one exists, or takes an independently generated pair where one does not, publishes them in an attestation event, and performs the actual encryption and decryption. Nothing leaves the extension.

QuantaKrypto owns the cryptography and the specification: the derivation scheme, the envelope format, the proof of possession, the review, and the four public drafts.

Obelisk owns the decision and the explanation. It decides when to ask for post-quantum encryption, fetches the recipient’s keys, and tells the user in plain words what protection a conversation has. That last job is harder than it sounds and nobody else could do it, because only the client knows what the user is looking at.

Where the keys come from

The derivation is the piece with the sharpest constraint, so it is worth being precise.

Post-quantum keys are derived from the same NIP-06 BIP-39 seed as your Nostr identity, as siblings of the secp256k1 key and deliberately never from it.

That direction is the entire security property. If a post-quantum key could be computed from your Nostr private key, then Shor’s algorithm recovers the private key from your published pubkey, an attacker reruns the derivation, and the post-quantum key falls with it. The scheme would be circular and worth nothing.

Where a 24-word phrase exists, the practical consequence is the good part: nothing new to back up, one mnemonic still restores everything.

Most Nostr identities are not that, and this is the part worth being straight about. Almost every mobile client generates a raw key and shows you an nsec, with no mnemonic at all, and nostr-tools' own NIP-06 helper produces 12 words rather than 24. A 12-word phrase is refused rather than quietly downgraded, because 128 bits of seed entropy would make the seed the weakest link rather than the lattice.

Those accounts generate an independent post-quantum key instead. It works identically once published, and its attestation is tagged origin: independent so a reader can tell the provenance apart. The honest cost: it cannot be recovered from a mnemonic, so it needs its own backup. Remote-signer and watch-only accounts cannot hold post-quantum keys at all yet.

Because the key cannot be derived from an identity, it has to be announced. That is a kind:10203 replaceable event carrying both public keys, signed by your ordinary Nostr key, with an ML-DSA proof of possession binding all three together. ML-KEM cannot sign, so binding it into a signed message is what gives it a possession proof at all.

It rides inside gift wrap, so relays never noticed

The message itself is an ordinary NIP-17 gift wrap over NIP-59: a rumor, sealed, wrapped with a throwaway key that is fresh for every message. The post-quantum payload sits inside the seal.

So a relay stores an ordinary kind:1059 event of a slightly unusual size. No relay changed anything. Clients that have not implemented this are unaffected, because the new part lives inside a layer they could not read anyway. It costs about 3 KB per message and roughly a millisecond to encrypt.

The specifications

An implementation nobody else can talk to is a private protocol wearing Nostr’s clothes, so the formats are written up as four public drafts. None has a NIP number and none is submitted yet:

  • Key derivation: deriving ML-KEM and ML-DSA keys from the BIP-39 seed as siblings, the seed-strength rule, and a test vector
  • Key attestation: the kind:10203 event, its tags, the proof of possession, and the verification a reader must perform in full or not at all
  • The post-quantum NIP-44 envelope: the wire format, the hybrid KDF, and why decryption must produce one indistinguishable error
  • Signer capability: how a client asks a signer what it supports instead of guessing

The reference implementation is on npm as [@nostr-wot/pq](https://www.npmjs.com/package/@nostr-wot/pq).

Two things we got wrong

Writing the capability draft turned up a real bug in our own signer. A post-quantum request on a remote-signer account was being routed to the bunker before the post-quantum code ran, and a bunker answers an encryption request with ordinary NIP-44. The caller got classic ciphertext in response to a post-quantum request, with no way to tell the difference. That is precisely the silent downgrade the design exists to prevent, arriving through a path nothing was watching. Writing the spec is what found it, which is an argument for writing specs.

The second was more instructive, and it was not in the cryptography at all. Every seal, signature and envelope was correct throughout.

Each message publishes two gift wraps: one to the recipient, one to yourself so your own history survives a reload. The relay-selection code was unioning the recipient’s published inbox with a fallback that always included our own relay, so both copies landed on a socket already authenticated as the sender. The relay did not need to decrypt anything. The connection identified us, and it then saw two same-sized envelopes seconds apart, one addressed to the recipient and one to ourselves. That states the pair outright.

It is the equivalent of posting two anonymous letters while showing the postman your ID, and addressing one of them to yourself. Four review rounds read that branch and missed it, because all four were checking cryptography. The fix is an ordered ladder of relay targets, used one rung at a time and never unioned.

The transferable lesson: gift wrap protects the social graph, and the routing underneath it can throw that away without touching a single cryptographic primitive.

What it looks like to a user

Obelisk shows each conversation in one of three states rather than a binary, because most conversations sit in the middle and the middle is real protection.

Extra safe is unreadable, nobody can see who you are talking to, and a future quantum computer could not open it. Safe is the same minus the quantum part. Basic is a legacy NIP-04 thread where the servers can see who you are talking to.

Collapsing that into quantum-safe or not would erase the protection gift wrap already provides and turn the ordinary case into a permanent warning that people stop reading.

Where this is honest about its limits

Event signatures are still secp256k1. This does not stop a future adversary forging notes in your name, and it cannot: that is a protocol-wide change no client can make alone.

What is protected is confidentiality against somebody recording traffic today and decrypting it later. That is the half of the problem that cannot be fixed retroactively, and the half already accruing damage. “Quantum secure” is the wrong claim to make to a user. “The messages being recorded today stay unreadable” is the right one.

It is also opt-in, which it should not be. A Nostr identity is a secp256k1 keypair and nothing else, with no algorithm identifier and no key set, so post-quantum can only be added beside it rather than built into it. We wrote about why that makes opt-in the only available shape and what would have to change.

Try it, or argue with it

If you use the Nostr WoT extension, open Menu, Security, Post-quantum key. A 24-word account has keys derived and waiting, and publishing them is one button. Anything else is offered the independent-key path instead. Obelisk’s guide covers it end to end.

If you build on Nostr, the drafts are the thing to read. Reading post-quantum messages needs no negotiation at all, because the envelope is self-describing. Sending needs one cacheable relay query. For most clients this is an afternoon.

And if the wire format is wrong, now is much cheaper than after a second client ships against it. That is what a draft is for.

The implementation write-up with measured message sizes is on QuantaKrypto.


메타데이터
post_id
76a2b9ede5c8
slug
obelisk-now-supports-post-quantum-direct-messages-76a2b9ede5c8
url
https://medium.com/nostr-wot/obelisk-now-supports-post-quantum-direct-messages-76a2b9ede5c8
canonical_url
https://medium.com/nostr-wot/obelisk-now-supports-post-quantum-direct-messages-76a2b9ede5c8
author_url
https://medium.com/@leonacosta
status
ok
fetched_at
2026-08-23 08:05:43