Skip to main content

Research Notes

Receiving is PQ-safe, spending is not (yet)​

March 2026

SPECTER's security model has a deliberate split: the receiving/discovery layer uses ML-KEM-768 (post-quantum), while the spending path uses secp256k1 (classical). This isn't a bug. It's a pragmatic design choice.

Why the split exists​

Ethereum wallets only understand secp256k1 signatures. To make stealth addresses spendable in existing wallets, SPECTER shifts the recipient's secp256k1 spending key by a tweak derived from the ML-KEM shared secret — an ERC-5564-style additive construction. The derivation path:

ML-KEM shared secret
→ t = H_to_scalar("SPECTER_STEALTH_TWEAK_V2" || shared_secret || counter) // secp256k1 scalar, rejection-sampled
→ stealth address P = spending_pub + t·G (sender, public data only)
→ stealth private p = spending_sk + t (mod n) (recipient, needs the secret spending key)
→ Ethereum private key + address (keccak256), reused for the Sui address

This gives you a wallet-compatible output today while protecting the discovery layer against future quantum attacks.

Why receiving matters more​

The receiving path's artifacts (ciphertexts, announcements) are permanent and public. Anyone can collect them. If they become breakable later, all historical privacy is retroactively lost.

The spending path is different: by the time a quantum computer could attack the secp256k1 key, the funds are typically already moved. The exposure window is much smaller.

The closing path​

ERC-4337 smart accounts can use custom verification logic. A SPECTER smart account could verify ML-DSA signatures instead of ECDSA. That's the near-term path to fully post-quantum spending, without waiting for Ethereum consensus changes.

See the ERC Proposal for the formal specification, and Security Boundaries for the full analysis.


Design note: Why SHAKE-256 for key derivation​

SPECTER uses SHAKE-256 (a NIST-standardized extendable-output function) for all key derivations with domain separation:

  • "SPECTER_VIEW_TAG_V1" for view tag computation
  • "SPECTER_STEALTH_TWEAK_V2" for the secp256k1 stealth-address tweak t — the same scalar produces the address (P = spending_pub + t·G) and the spend key (p = spending_sk + t)

The version suffix versions each label, leaving room to revise a derivation without ambiguity. The tweak label is at _V2: it replaced a pre-2.0 hash-only stealth derivation (SPECTER_STEALTH_PK_V1 / SK_V1 / ETH_KEY_V1) under which the sender could reconstruct the stealth private key.

Domain separation prevents cross-protocol attacks where the same shared secret might produce colliding outputs in different contexts. SHAKE-256 provides 128-bit post-quantum security at 256-bit output length.


Design note: Why deterministic rekeying, not additive offsets​

Classical stealth systems use additive key offsets: sk_stealth = sk_spend + hash. This works because secp256k1 keys are scalars in a cyclic group.

Lattice-based keys (ML-DSA, ML-KEM) are structured polynomial matrices. Adding an arbitrary scalar doesn't preserve the keypair relationship. There's no lattice equivalent of the "add a scalar to the private key" trick.

SPECTER instead uses deterministic rekeying:

seed = SHAKE-256(domain || PK_spend || shared_secret_hash)
(sk_stealth, PK_stealth) = KeyGen(seed)

This produces a fresh, valid keypair that's:

  • Unlinkable to the original spending key
  • Deterministically reproducible by the recipient
  • Not derivable by the sender (who doesn't know sk_spend)

Reference: Key research papers​

PaperRelevance
FIPS 203 (ML-KEM)The NIST standard SPECTER implements
ERC-5564The stealth address standard SPECTER extends
ERC-6538Stealth meta-address registry
Stealth Addresses Research (2501.13733)Post-quantum stealth address analysis
ERC-4337Account abstraction for PQ spending path

ERC Proposal

The formal specification for schemeId 2 (ML-KEM extension of ERC-5564).