2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00
2026-07-16 20:36:25 -04:00

Post-Quantum Nostr

A migration strategy for bringing post-quantum security to Nostr without breaking the social graph, without requiring consensus on a single post-quantum algorithm, and without forcing existing users to abandon their identities.

Table of Contents


The Problem

Nostr's cryptography is built entirely on secp256k1:

  • Identity: Your pubkey IS your identity — it's in every event, it's your npub, it's what filters use, it's what NIP-05 verifies, it's what the social graph is built on.
  • Authentication: Every event is signed with a Schnorr signature over secp256k1. Signature verification requires the public key, which is published in plaintext on every event.
  • Encryption (NIP-04): Uses secp256k1 ECDH to derive a shared secret, then AES-256-CBC. The shared X coordinate is used directly as the AES key. The IV is published in plaintext.
  • Encryption (NIP-44): Uses secp256k1 ECDH to derive a shared secret, then HKDF → ChaCha20 + HMAC-SHA256. The nonce is published in plaintext inside the payload.

Shor's algorithm breaks the elliptic curve discrete logarithm problem on secp256k1 in polynomial time. With a sufficiently large fault-tolerant quantum computer, an attacker who has recorded any Nostr event can:

  1. Extract the public key from the event.
  2. Run Shor's algorithm to recover the private key.
  3. Forge signatures, decrypt all NIP-04/NIP-44 messages, and impersonate the user.

NIP-04 vs NIP-44: No Quantum Advantage

NIP-44 is a substantial classical-security upgrade over NIP-04 (authenticated encryption, key separation, per-message keys, better cipher, length-hiding padding), but it offers zero post-quantum security advantage. Both derive their shared secret from secp256k1 ECDH. Everything NIP-44 adds (HKDF, per-message nonces, ChaCha20, HMAC-SHA256, custom padding) sits downstream of the broken ECDH step.

The NIP-44 spec itself acknowledges this:

No post-quantum security: a powerful quantum computer would be able to decrypt the messages

The symmetric primitives in both NIPs (AES-256, ChaCha20, HMAC-SHA256, HKDF-SHA256) are adequately post-quantum (~128-bit PQ security via Grover's quadratic speedup). The weakness is exclusively the secp256k1 ECDH key agreement, which has zero post-quantum security in both NIPs.


Why Bitcoin's Hash-the-Pubkey Trick Doesn't Work for Nostr

Bitcoin has limited quantum resistance because addresses are hashes of pubkeys (RIPEMD160(SHA256(pubkey))), and pubkeys are only revealed at spend time. This works because of three properties that Nostr does not have:

Property Bitcoin Nostr
Identifier Address (hash of pubkey) Pubkey directly
Pubkey revealed At spend time only On every event
After reveal UTXO consumed, key discarded Identity persists, key reused

In Nostr, the pubkey is load-bearing in ways it isn't in Bitcoin:

  1. The pubkey IS the identity. It's in every event, it's your npub, it's what filters use, it's what NIP-05 verifies, it's what the social graph is built on.
  2. The pubkey must be revealed for every event. Schnorr signature verification requires the public key. You can't verify a signature with a hash of the pubkey.
  3. Identity is never consumed. You publish thousands of events over years with the same pubkey. There's no "consume and rotate" moment.

If Nostr published hash(pubkey) instead, you'd get quantum resistance for exactly zero seconds — the time between key generation and your first event publication, at which point the pubkey must be revealed for signature verification.

The only ways to avoid revealing the pubkey (ZK-SNARKs, giving up public verifiability, one-time keys per event) are impractical or fundamentally change what Nostr is.

The real fix is post-quantum signatures and post-quantum key agreement, not hashing the pubkey. This document describes how to get there without breaking Nostr.


The Strategy: Overview

flowchart TD
    subgraph SECURITY["Security Layers - All Quantum-Resistant"]
        L1[Layer 1: Seed phrase - PQ-safe root of trust]
        L2[Layer 2: Key-link event + OTS - PQ-safe identity migration]
        L3[Layer 3: PQ signatures - PQ-safe future authentication]
        L4[Layer 4: OTS on important events - PQ-safe historical provenance]
        L5[Layer 5: OTP or PQ encryption - PQ-safe confidentiality]
    end

    L1 --> L2 --> L3
    L1 --> L5
    L2 --> L4

The strategy uses existing Nostr infrastructure (NIP-06, NIP-03) plus one new event type (a key-link NIP). No new cryptographic primitives are needed for the migration itself — PQ algorithms are used additively, and the community doesn't need to agree on which one to use.

Design Principles

  1. No social graph loss — existing follows and identity carry forward
  2. No user re-onboarding — the seed phrase becomes the new root of trust
  3. Backward compatible — legacy clients continue working; PQ is additive
  4. No algorithm consensus required — link to all viable PQ schemes now, converge later
  5. Pre-quantum timestamping — establish links now, before quantum computers exist
  6. Transparent to users — the client handles everything at seed-phrase login

Component 1: Seed Phrase as Algorithm-Agnostic Root of Trust

The BIP39 seed is a 64-byte entropy string derived from a mnemonic via PBKDF2-HMAC-SHA512 (2048 iterations). This is a symmetric KDF — no public-key cryptography involved. A quantum computer provides no advantage beyond Grover's quadratic speedup.

The seed is algorithm-agnostic. It's just entropy. You can derive any key type from it:

flowchart TD
    SEED[BIP39 Seed - 64 bytes, quantum-safe]
    SEED --> BIP32[BIP32 derivation - secp256k1 keypair]
    SEED --> PQKEM[HKDF derivation - ML-KEM keypair]
    SEED --> PQDSA[HKDF derivation - ML-DSA keypair]
    SEED --> SLHDSA[HKDF derivation - SLH-DSA keypair]
    SEED --> SYMKEY[HKDF derivation - symmetric encryption key]

    style SEED fill:#cfc
    style BIP32 fill:#fcc
    style PQKEM fill:#cfc
    style PQDSA fill:#cfc
    style SLHDSA fill:#cfc
    style SYMKEY fill:#cfc

PQ Key Derivation from BIP39 Seeds

BIP32 is specific to secp256k1. For PQ keys, use HKDF-SHA256 (quantum-resistant) to derive key material from the BIP39 seed:

For each PQ algorithm A:
  pq_seed_A = HKDF-Extract(salt="nostr-pq-v1", ikm=bip39_seed)
  pq_key_material_A = HKDF-Expand(pq_seed_A, info=A.identifier, L=A.key_size)
  pq_keypair_A = A.keygen(pq_key_material_A)

The identifier string ("ml-dsa-65", "slh-dsa-128s", "ml-kem-768", etc.) is the only algorithm-specific part. New algorithms can be added later without changing the derivation scheme.

Seed Phrase Entropy Considerations

Mnemonic length Entropy Post-quantum security (Grover's) Assessment
12 words 128 bits ~64 bits Borderline
24 words 256 bits ~128 bits Adequate

For users concerned about quantum attacks on the seed entropy itself, 24-word mnemonics are recommended. The bigger risk to seed phrases is classical (theft, phishing), not quantum.


This is the core innovation. Instead of choosing one PQ algorithm, cross-sign with all viable PQ schemes now. Let consensus emerge later.

The Standardization Uncertainty Problem

The PQ standardization landscape is still evolving:

Scheme NIST Status Concern
ML-DSA (Dilithium) FIPS 204, finalized 2024 Lattice-based — could have undiscovered structural weaknesses
SLH-DSA (SPHINCS+) FIPS 205, finalized 2024 Hash-based, very conservative, but huge signatures (8-50 KB)
FN-DSA (Falcon) FIPS 206, still draft Compact but complex FFT, implementation concerns
ML-KEM (Kyber) FIPS 203, finalized 2024 Lattice-based — same class as Dilithium

History gives reason to be cautious: SIKE was a NIST PQ finalist that was broken in 2022 using a classical algorithm that ran in an hour on a laptop. If the Nostr community had picked SIKE early, we'd be back to square one.

The Multi-Scheme Solution

flowchart TD
    SEED[BIP39 Seed] --> SECP[secp256k1 keypair]
    SEED --> MLDSA[ML-DSA keypair]
    SEED --> SLHDSA[SLH-DSA keypair]
    SEED --> FALCON[FN-DSA keypair]
    SEED --> MLKEM[ML-KEM keypair]

    SECP --> SIGN[Schnorr sign]
    MLDSA --> SIGN2[ML-DSA sign]
    SLHDSA --> SIGN3[SLH-DSA sign]
    FALCON --> SIGN4[FN-DSA sign]

    SIGN --> EVENT[Key-Link Event]
    SIGN2 --> EVENT
    SIGN3 --> EVENT
    SIGN4 --> EVENT

    EVENT --> OTS[OpenTimestamp via NIP-03]
    OTS --> RELAY[Published to relays NOW]

    RELAY --> CLIENT1[PQ-aware client - picks ML-DSA]
    RELAY --> CLIENT2[PQ-aware client - picks SLH-DSA]
    RELAY --> CLIENT3[Legacy client - ignores all PQ keys]

    style EVENT fill:#ffa
    style OTS fill:#cfc

A new event kind (to be assigned) containing multiple cross-signed links:

{
  "kind": <new key-link kind>,
  "pubkey": "<secp256k1 npub>",
  "content": {
    "links": [
      {
        "algorithm": "ml-dsa-65",
        "public_key": "<ML-DSA-65 public key, base64>",
        "signature": "<ML-DSA signature over the link statement, base64>"
      },
      {
        "algorithm": "slh-dsa-128s",
        "public_key": "<SLH-DSA-128s public key, base64>",
        "signature": "<SLH-DSA signature over the link statement, base64>"
      },
      {
        "algorithm": "fn-dsa-512",
        "public_key": "<FN-DSA-512 public key, base64>",
        "signature": "<FN-DSA signature over the link statement, base64>"
      },
      {
        "algorithm": "ml-kem-768",
        "public_key": "<ML-KEM-768 public key, base64>",
        "note": "KEM key for encryption; ownership asserted by secp256k1 signature over this content"
      }
    ],
    "statement": "All keys in this event are derived from the same seed and controlled by the same entity as secp256k1 key <npub>. This link is established pre-quantum.",
    "secp256k1_signature": "<Schnorr signature over the entire content>"
  }
}

Each PQ signature scheme independently signs the same link statement. The secp256k1 Schnorr signature covers the entire content (including all PQ public keys and signatures), binding them together.

ML-KEM Cannot Sign

ML-KEM (Kyber) is a KEM (Key Encapsulation Mechanism), not a signature scheme. It cannot sign the link statement. Instead, its binding is proven indirectly: the secp256k1 signature covers the ML-KEM public key in the content, and the ML-KEM key is derived from the same seed. The proof is: "the entity that controls the secp256k1 key (proven by Schnorr signature) asserts that this ML-KEM key is also theirs."

This is weaker than a cross-signature but sufficient because:

  • The secp256k1 signature is valid NOW (pre-quantum), establishing the link while we can trust secp256k1
  • The ML-KEM key is used for encryption, not authentication — a false claim of ownership would just mean someone can't decrypt messages sent to you, which is self-correcting

Why This Is Better Than Picking One Scheme

Concern Pick-one approach Multi-scheme approach
Scheme gets broken later Migration fails, need new link Other links still valid
Standardization changes May need to re-link Already covered
Consensus not reached Can't proceed Proceed immediately
Event size Small Larger (~20-30 KB), but one-time cost
Client complexity Support one scheme Support any subset
Race condition risk Must publish before quantum Published now, all schemes

Event size is a non-issue because this is a one-time publication, not per-event overhead.

Revocation: If a Scheme Is Broken Later

If a specific PQ scheme is later found to be weak (like SIKE was):

  1. Clients simply stop trusting the broken scheme — no revocation event needed; clients just ignore links to algorithms on a known-broken list
  2. The remaining links are still valid
  3. Optionally, publish a new key-link event without the broken scheme, signed by the remaining valid PQ keys

Component 3: OpenTimestamps for Pre-Quantum Anchoring

NIP-03 (OpenTimestamps Attestations for Events) already exists and is implemented in nostr_core_lib. It anchors Nostr event hashes to the Bitcoin blockchain via OpenTimestamps.

Why Bitcoin Timestamping Is Quantum-Resistant

The Bitcoin proof-of-work chain is quantum-resistant — not because Bitcoin's signatures are PQ-safe (they're not for revealed pubkeys), but because re-mining historical blocks is infeasible even with a quantum computer. Grover's algorithm gives only a quadratic speedup on mining, which doesn't help retroactively alter past blocks.

An attacker who breaks your secp256k1 key via Shor's can forge new events but cannot backdate an event to before a specific Bitcoin block — they'd need to produce a valid OTS proof anchoring it to a past block, which requires either:

  • Re-mining that Bitcoin block (impossible, even with a quantum computer, due to cumulative proof-of-work)
  • Finding a SHA256 preimage for the event hash (still infeasible with Grover's quadratic speedup on 256-bit hashes)

The key-link event itself MUST be timestamped via NIP-03. This prevents a race condition attack:

Without OTS on the key-link event:

  • Attacker breaks secp256k1 key via Shor's
  • Attacker publishes a fraudulent key-link event linking your secp256k1 key to their PQ key
  • Both real and fraudulent events have valid secp256k1 signatures (key is compromised)
  • Clients can't distinguish them — created_at is forgeable

With OTS on the key-link event:

  • Your real key-link event is anchored to Bitcoin block N (before quantum computers)
  • Attacker's fraudulent event is published later and cannot produce an OTS proof for block N or earlier
  • Clients verify: "the key-link event with the earliest valid OTS proof wins"
  • The real key-link is cryptographically distinguishable from the fraudulent one

What Should Be OpenTimestamped

Not every event needs OTS. Priority list:

  1. Key-link events — absolutely critical, foundation of PQ migration
  2. Identity-defining events — NIP-05 verification, profile events (kind 0)
  3. Important content — contracts, announcements, evidence
  4. High-value events — financial events (NIP-60 Cashu wallets, zaps)

Routine events (regular posts, reactions) probably don't need OTS — the impact of retroactive forgery is low.


Component 4: Migrating Existing Users with Raw nsec

Most Nostr users today have a raw nsec (random private key) with no seed phrase. Asking them to abandon their identity and social graph to adopt a seed phrase is a non-starter. This component addresses how to migrate them.

The Challenge

A secp256k1 private key is 32 bytes (256 bits). BIP39 encodes 11 bits per word. Encoding the old nsec requires 24 extra words. The old nsec was generated randomly — it cannot be "derived" from a seed (that's a preimage problem). It must be encoded (directly or encrypted).

Approach A: Extended Phrase (36 words)

flowchart LR
    subgraph CREATION["One-time migration"]
        OLD[Old nsec - 32 bytes]
        NEW[Generate 12-word BIP39 phrase]
        NEW --> SEED[BIP39 seed]
        SEED --> ENCKEY[Derive encryption key via HKDF]
        ENCKEY --> ENC[Encrypt old nsec: ChaCha20]
        OLD --> ENC
        ENC --> CIPHER[32 bytes encrypted nsec]
        CIPHER --> WORDS[Encode as 24 BIP39 words + checksum]
        NEW --> PHRASE[Full phrase: 12 + 24 = 36 words]
        WORDS --> PHRASE
    end

    subgraph RECOVERY["Recovery with full phrase"]
        FULL[36 words entered]
        FULL --> FIRST[First 12 words]
        FULL --> LAST[Last 24 words]
        FIRST --> SEED2[BIP39 seed]
        SEED2 --> ENCKEY2[Derive encryption key]
        LAST --> CIPHER2[Decode to 32 bytes]
        ENCKEY2 --> DEC[Decrypt]
        CIPHER2 --> DEC
        DEC --> OLDRECOV[Old nsec recovered]
        SEED2 --> PQKEYS[PQ keys derived]
    end

    subgraph FUTURE["After migration - drop old key"]
        FIRST12[First 12 words only]
        FIRST12 --> SEED3[BIP39 seed]
        SEED3 --> PQKEYS2[PQ keys only]
    end

Design:

  1. Generate a standard 12-word BIP39 phrase (128 bits entropy)
  2. Derive a 32-byte encryption key from the BIP39 seed: enc_key = HKDF-Extract(salt="nostr-legacy-key", ikm=bip39_seed)
  3. Encrypt the old nsec: encrypted_nsec = ChaCha20(enc_key, nonce=0, old_nsec) → 32 bytes
  4. Encode the 32-byte encrypted nsec as 24 BIP39 words (with its own checksum)
  5. Full phrase: 12 words (new seed) + 24 words (encrypted old nsec) = 36 words

Recovery with 36 words: First 12 → BIP39 seed → PQ keys + encryption key. Last 24 → decode → decrypt → old nsec. Both keys recovered.

Recovery with 12 words (after migration): First 12 → PQ keys only. Old nsec no longer needed.

Why encrypt the old nsec? The extra 24 words are meaningless without the first 12 (partial compromise protection). If someone steals only the last 24 words: useless. If someone steals only the first 12 words: they get PQ keys but not the old nsec.

flowchart TD
    subgraph SETUP["One-time migration"]
        OLD[Old nsec]
        NEW[Generate 12-word BIP39 phrase]
        NEW --> SEED[BIP39 seed]
        SEED --> NEWSECP[New secp256k1 key via NIP-06]
        SEED --> PQKEYS[PQ keys via HKDF]
        SEED --> ENCKEY[Symmetric encryption key via HKDF]
        ENCKEY --> ENC[Encrypt old nsec]
        OLD --> ENC
        ENC --> EVENT30078[kind 30078: encrypted old nsec]
        NEWSECP --> SIGN1[Sign event with new secp256k1 key]
        SIGN1 --> EVENT30078
        EVENT30078 --> RELAY[Publish to relays]

        OLD --> KEYLINK[Key-link event: cross-signed]
        NEWSECP --> KEYLINK
        PQKEYS --> KEYLINK
        KEYLINK --> OTS[OpenTimestamp via NIP-03]
        OTS --> BTC[Anchored in Bitcoin]
        KEYLINK --> RELAY
    end

    subgraph RECOVER["Recovery from 12 words only"]
        WORDS[12 words entered]
        WORDS --> SEED2[BIP39 seed]
        SEED2 --> NEWSECP2[New secp256k1 key]
        SEED2 --> PQKEYS2[PQ keys]
        SEED2 --> ENCKEY2[Encryption key]
        NEWSECP2 --> FETCH[Fetch kind 30078 from relays]
        FETCH --> CIPHER[Encrypted old nsec]
        ENCKEY2 --> DEC[Decrypt]
        CIPHER --> DEC
        DEC --> OLDRECOV[Old nsec recovered]
    end

Design:

  1. Generate a 12-word BIP39 phrase
  2. Derive from seed: new secp256k1 key (NIP-06 path), PQ keys (HKDF), symmetric encryption key (HKDF)
  3. Encrypt old nsec with the symmetric key: encrypted_nsec = ChaCha20-Poly1305(enc_key, old_nsec)
  4. Publish as kind 30078 event, signed with the new secp256k1 key
  5. Publish key-link event (cross-signed, OpenTimestamped via NIP-03)
  6. User backs up only 12 words

Recovery: 12 words → derive new key + encryption key → fetch kind 30078 from relays → decrypt → old nsec. Both keys recovered from 12 words + relay access.

No circular dependency: The kind 30078 event is found by the new pubkey (derived from the 12-word seed). The new secp256k1 key is the "storage identity." The old nsec is just data inside the event.

Approach C: Hybrid (Best of Both)

  • Primary backup: 12-word phrase (PQ keys + relay recovery of old nsec)
  • Optional fallback: also write down 24 extra words (encrypted old nsec) in case relays are unavailable
  • If relays available: 12 words suffice
  • If relays down: 12 + 24 = 36 words recover everything

Comparison

Property 36-word phrase 12-word + relay backup Hybrid
Backup size 36 words 12 words 12 or 36 words
Relay dependency None Yes Optional
Custom format Yes No Optional
Self-contained Yes No Yes (with 36)
Quantum-safe Yes Yes Yes

Component 5: Quantum-Safe Self-Storage

For self-storage use cases (e.g., kind 30078 application data), NIP-04 and NIP-44 are unnecessary and quantum-vulnerable. Two quantum-safe alternatives:

Option A: One-Time Pad (Information-Theoretic Security)

A one-time pad provides information-theoretic security — the strongest security guarantee in cryptography, stronger than any computational security (including post-quantum). It's a mathematical proof of perfect secrecy: no computer that could ever exist, in any universe governed by the laws of physics, can break it.

Requirements:

  • Pad must be truly random (hardware RNG recommended, e.g., TrueRNG)
  • Pad must be as long as the data
  • Pad must never be reused
  • Pad must be stored securely (e.g., USB drive)

For self-storage, OTP is a natural fit because you sidestep OTP's biggest limitation (key distribution between parties) — you're encrypting for yourself, so the pad lives on your device.

What a quantum attacker can and cannot do with OTP-encrypted kind 30078:

  • ❌ Read your stored data — OTP is information-theoretically secure
  • ✅ Forge your signature — Shor's breaks secp256k1 (identity compromised)
  • ✅ Impersonate you, delete/replace your events
  • ❌ Decrypt your stored data — even with your private key, the data is protected by the pad

This gives clean separation: quantum breakage of your secp256k1 key compromises your identity/authentication but NOT your stored data's confidentiality.

Critical warning: pad reuse is fatal. If any pad section is used twice, XOR of the two ciphertexts equals XOR of the two plaintexts — the encryption breaks. Multi-device sync requires an atomic reservation protocol for pad sections.

Option B: Symmetric Key from Seed (Computational PQ Security)

Derive a 256-bit symmetric key from the BIP39 seed (independent of secp256k1) and encrypt with ChaCha20-Poly1305 or AES-256-GCM:

storage_key = HKDF-Extract(salt="nostr-storage-v1", ikm=bip39_seed)
ciphertext = ChaCha20-Poly1305(storage_key, nonce, plaintext)

This is post-quantum because:

  • HKDF-SHA256 is symmetric (~128-bit PQ security)
  • ChaCha20 / AES-256 are symmetric (~128-bit PQ security)
  • The secp256k1 key is never part of the encryption path

Tradeoff vs OTP: Simpler (no pad management, no USB drive), but computationally secure rather than information-theoretically secure. Adequate for most use cases.

Comparison

Property OTP Symmetric key from seed
Security level Information-theoretic (perfect secrecy) Computational, ~128-bit PQ
Key material Pad as long as all data Single 256-bit key
Storage overhead Pad on USB drive Just the seed phrase
Quantum resistance Unconditional Conditional (Grover's is optimal)
Future-proofing Permanent Could theoretically fall to new quantum algorithms
Multi-device Requires pad sync + offset coordination Just share the seed
Practicality More complex Simpler

Component 6: Deterministic Wallet Compartmentalization

BIP32/BIP39 deterministic wallets (NIP-06) provide meaningful quantum compartmentalization through their tree structure.

The Core Protection: Chain Codes Are Secret

When Shor's algorithm recovers a leaf private key from its published public key, the attacker gets only that key. To climb the tree (recover the parent private key), they need the parent chain code:

parent_private_key = child_private_key - IL mod n
where IL = HMAC-SHA512(parent_chain_code, parent_public_key || index)[:32]

The parent chain code is NOT published in normal Nostr usage. HMAC-SHA512 is a symmetric primitive — a quantum computer provides no advantage. Without chain codes, the attacker is stuck at the leaf level.

The Firewall Effect of Hardened Derivation

The NIP-06 path m/44'/1237'/account'/0/0 has hardened derivation at three levels. This means:

  • Breaking all keys in Account 0 does NOT compromise Account 1 (hardened at account' level)
  • The seed/master key remains secure even if all account keys are broken
  • Hardened derivation requires the parent private key, not just the public key

The Critical Vulnerability: Chain Code Leakage

If an attacker obtains a chain code at any level AND breaks one child key at that level via Shor's, they can:

  1. Compute IL using the chain code + child public key + index
  2. Recover the parent private key
  3. Derive ALL children at that level
  4. Continue climbing if parent chain codes are also known

Where chain codes leak: published xpubs (extended public keys), wallet software that exports xpubs, backup exposure. In standard Nostr usage, chain codes are not published.

Summary

Scenario Compromised?
One leaf key broken via Shor's Only that key
Multiple leaf keys from same account broken Only those keys
Leaf key broken + chain code at that level known Parent + all siblings
Account key broken + chain code at account level known Parent + all accounts
Seed phrase stolen (classical) Everything
Seed safe, quantum attacker, no chain codes leaked Only published keys

Recommendations

  1. Use separate accounts for separate Nostr identities — hardened derivation contains the damage
  2. Never publish xpubs — chain codes are the keys to the kingdom
  3. Use 24-word mnemonics for adequate PQ security on the seed entropy
  4. Don't reuse keys across services — each identity gets its own account index
  5. Assume published keys WILL be broken — plan for containment, not prevention

The Complete Migration Flow

flowchart TD
    subgraph NOW["Pre-quantum - NOW"]
        USER[User with raw nsec OR seed phrase]
        USER --> MIGRATE[Client: Enable PQ migration]
        MIGRATE --> SEED[Generate or use 12-word BIP39 phrase]
        SEED --> DERIVE[Derive: new secp256k1 + all PQ keys + storage key]
        DERIVE --> ENCRYPT[Encrypt old nsec if migrating from raw nsec]
        ENCRYPT --> STORE30078[Publish kind 30078: encrypted old nsec]
        DERIVE --> KEYLINK[Publish key-link event: cross-signed by all PQ schemes]
        KEYLINK --> OTS[OpenTimestamp the key-link event via NIP-03]
        OTS --> BTC[Anchored in Bitcoin block]
        KEYLINK --> RELAY[Published to relays]
        STORE30078 --> RELAY
        SEED --> BACKUP[User writes down 12-word phrase]
    end

    subgraph TRANSITION["Transition period - dual signing"]
        DUAL[Client signs events with both secp256k1 and PQ keys]
        DUAL --> LEGACY[Legacy clients verify secp256k1 signature]
        DUAL --> PQAWARE[PQ-aware clients verify PQ signature]
    end

    subgraph FUTURE["Post-quantum - when quantum computers arrive"]
        ATTACK[Attacker breaks secp256k1 via Shors]
        ATTACK --> FORGE[Can forge secp256k1 signatures]
        ATTACK --> CANNOT1[Cannot forge PQ signatures]
        ATTACK --> CANNOT2[Cannot backdate events - OTS proofs anchor history]
        ATTACK --> CANNOT3[Cannot break OTP-encrypted storage]

        PQAWARE --> VERIFY[Verify key-link event via OTS]
        VERIFY --> USEPQ[Switch to PQ signatures]
        USEPQ --> REJECT[Reject forged secp256k1-only events]
    end

    subgraph RETIRE["Old key retirement"]
        DROP[User drops extra backup, keeps 12 words only]
        DROP --> PQONLY[Client uses PQ keys only]
        PQONLY --> DONE[Migration complete]
    end

    NOW --> TRANSITION --> FUTURE --> RETIRE

    style NOW fill:#cfc
    style TRANSITION fill:#ffa
    style FUTURE fill:#fcc
    style RETIRE fill:#cfc

User Experience

  1. User clicks "Enable quantum-safe migration" in their client
  2. Client generates a 12-word phrase, shows it to the user to write down
  3. Client handles everything else automatically:
    • Derives new secp256k1 key + all PQ keys from seed
    • Encrypts old nsec and publishes as kind 30078 (if migrating from raw nsec)
    • Publishes cross-signed key-link event
    • OpenTimestamps the key-link event via NIP-03
  4. During transition: client signs with both old and new keys
  5. After quantum migration: client uses PQ keys only, old key retired
  6. User drops extra backup, keeps 12 words

What Remains Unsolved

Historical Event Authenticity (Partially Solved)

Past secp256k1-signed events that were NOT OpenTimestamped remain forgeable after quantum break. An attacker can create fake old events that appear to be from you.

Mitigation: OpenTimestamp important events now (NIP-03). Events with valid OTS proofs are cryptographically anchored to a pre-quantum timestamp and cannot be forged retroactively. Events without OTS proofs become untrustworthy after quantum break.

Not fully solved: Routine events (regular posts, reactions) that aren't worth OpenTimestamping will become forgeable. The impact is low for most content, but high for events used as evidence, contracts, or historical records.

Relay Trust for Recovery

Approach B (12 words + relay backup) depends on relays retaining the kind 30078 event containing the encrypted old nsec. If all relays delete it, the old nsec is lost.

Mitigations:

  • Publish to multiple relays
  • Use relays you control or trust
  • Use the hybrid approach (Approach C) with 36-word fallback
  • The event is small and signed, so it's easy to preserve

PQ Signature Size

PQ signatures are much larger than secp256k1 Schnorr signatures:

Scheme Signature size
secp256k1 Schnorr 64 bytes
ML-DSA-65 ~3.3 KB
SLH-DSA-128s ~8 KB
FN-DSA-512 ~0.7 KB

This increases event sizes during the transition period (dual signing). After migration, events can be signed with PQ only. FN-DSA (Falcon) offers the most compact PQ signatures.

PQ Algorithm Risk

Any PQ algorithm could theoretically be broken by a new classical or quantum algorithm (as SIKE was). The multi-scheme approach hedges against this — if one scheme falls, the others remain linked. But if ALL lattice-based schemes are broken simultaneously, only SLH-DSA (hash-based, very conservative) remains as a fallback.


Implementation Status

This is a design document. The following building blocks already exist:

Existing Infrastructure

Component Status Location
NIP-06 (key derivation from mnemonic) Implemented nostr_core_lib: nip006.c
NIP-03 (OpenTimestamps) Implemented nostr_core_lib: nip003.c
NIP-44 (encrypted payloads) Implemented nostr_core_lib: nip044.c
NIP-04 (encrypted DMs, deprecated) Implemented nostr_core_lib: nip004.c
OTP cipher (information-theoretic encryption) Implemented ~/lt/otp

To Be Built

Component Description
Key-link NIP New NIP defining the cross-signed key-link event format
PQ key derivation from BIP39 seeds HKDF-based derivation of ML-DSA, SLH-DSA, FN-DSA, ML-KEM keys
PQ signature support Integration of liboqs or similar for PQ signature verification
Migration tooling Client-side tooling for migrating raw nsec users to seed phrases
Kind 30078 encrypted storage Symmetric-key or OTP encryption for self-storage
Extended phrase encoding 36-word phrase format (Approach A/C)

Dependencies

  • liboqs — Open Quantum Safe library for PQ algorithms
  • OpenTimestamps — already integrated via NIP-03
  • BIP39/BIP32 — already integrated via NIP-06

References


License

This document is released into the public domain. The migration strategy described herein is intended as a community resource for the Nostr ecosystem.

S
Description
No description provided
Readme
3.7 MiB
Languages
JavaScript 87.5%
CSS 5.9%
HTML 5.7%
Shell 0.9%