ZAP Protocol

Post-Quantum

ZAP's post-quantum options — IETF X-Wing KEM at the transport, hybrid Ed25519+ML-DSA-65 identities, PQ-bound names via RNS.

Post-Quantum

ZAP is PQ-ready at the wire, the identity, and the name-service layer. This page lays out what's standardized, what's optional, and where to look in the SDKs.

Layers

┌──────────────────────────────────────────────────────────────┐
│ Name service       PQ-RNS         identity bound to KEM pk    │
│ Identity / sig     hybrid sigs    Ed25519 + ML-DSA-65         │
│ Transport (KEM)    IETF X-Wing    X25519 + ML-KEM-768         │
│ AEAD              ChaCha20-Poly1305 with seq-numbered nonces  │
└──────────────────────────────────────────────────────────────┘

Each layer is independent. You can pick PQ at the transport without PQ at the name service, or vice versa, or both. Default for the reference SDKs is all three.

KEM — IETF X-Wing

ZAP's wire-level key agreement is IETF X-Wing: X25519 ⊕ ML-KEM-768, combined via SHA3-256 with a fixed label. The combined shared secret is safe as long as either X25519 or ML-KEM-768 is. Bit-for-bit interop with any X-Wing peer (same construction Cloudflare, Anthropic, iMessage PQ3 have adopted).

shared_secret = SHA3-256( "\./" || "X-Wing" || ss_M || ss_X || ct_X || pk_X )
InputBytesSource
"\./"3label prefix
"X-Wing"6ASCII protocol name (IETF spec)
ss_M32ML-KEM-768 shared secret
ss_X32X25519 shared secret
ct_X32X25519 ephemeral pk from encapsulator
pk_X32X25519 static pk of recipient

Cross-language conformance

Every SDK ships a KAT (known-answer test) pinning the combiner output for fixed inputs:

ss_M = 32 × 0x01
ss_X = 32 × 0x02
ct_X = 32 × 0x03
pk_X = 32 × 0x04
expected SHA3-256 = 72df2088314a73de80c21d9593f13fcd5629c800c70b1507f0dd918fde5fe4ed

A Python or TypeScript SDK that disagrees on the label, hash, or input order fails this test immediately — that's how we prevent silent divergence.

Identity — hybrid signatures

Long-term identities are hybrid keypairs: Ed25519 (classical) + ML-DSA-65 (FIPS 204). Both signatures must verify; either-or compromise still fails forgery. This protects identity claims even under a quantum-capable adversary.

IdentityPublic (3200 bytes):
  [Ed25519 pk: 32][ML-DSA-65 pk: 1952][X-Wing pk: 1216]

The X-Wing pk in the identity is the static key — used as the recipient pk in the combiner above. Forward secrecy comes from the X25519 ephemeral inside X-Wing.

Name service — PQ-RNS

RNS (Resource Name Service) resolves names to identity records signed by the owning identity itself. The record holds the PQ-bound identity + current endpoints, signed under the identity's hybrid key.

record {
  name        : "acme.payments"
  identity    : IdentityPublic       ← hybrid keypair
  endpoints   : [zap://...]
  signed_at   : UnixSeconds
  signature   : hybrid signature over the canonical encoding
}

The resolver doesn't need to be trusted — anyone can verify a returned record. There is no CA, no DNS provider, no registrar in the trust path.

A ZAP client that resolves acme.payments:

  1. Pulls the signed record from any RNS resolver
  2. Verifies the hybrid signature (Ed25519 + ML-DSA-65)
  3. Picks an endpoint, dials it
  4. The handshake re-uses the identity's X-Wing static key as pk_X — the connection is bound to the same identity the record resolved to

Bearer tokens, API keys, and CA-signed certificates never enter the picture.

Full guide: RNS over ZAP.

Migration paths

Where you areWhat to enable
Classical-only legacyKeep TLS in front of ZAP; opt into PQ-RNS for name service (no transport change)
Want PQ transport, keep DNSEnable X-Wing in your SDK config; keep existing name service
Greenfield deploymentAll three: X-Wing KEM, hybrid sigs, PQ-RNS — the reference SDK default

What's standardized vs. ZAP-specific

StandardizedZAP-specific
X-Wing KEMIETF draft-connolly-cfrg-xwing-kembit-for-bit
ML-KEMNIST FIPS 203bit-for-bit
ML-DSANIST FIPS 204bit-for-bit
Composite signatures (Ed25519 + ML-DSA)Composite-ML-DSA (IETF draft)bit-for-bit
RNS record formatcanonical CBOR-tagged ZAP schema
RNS resolution protocolspec-only today, see zap-proto/rns

ZAP doesn't invent crypto. It composes IETF / NIST primitives in a published, auditable way.

In the browser & at the edge

WherePath
BrowserThe browser hop is TLS 1.3 (Chrome / Safari handle the KEM negotiation; recent versions ship X25519MLKEM768). Inside that TLS hop, the application can do PQ-RNS resolution against an HTTP-bridge RNS resolver and pass through to a ZAP origin. End-to-end PQ requires a server-side terminator.
Cloudflare Worker / Deno / BunV8 isolates can run the WASM build of the SDK once published. Today, point the browser at a server-side terminator and proxy.
Lux network (validator ↔ validator)Native PQ ZAP on TCP — production mandatory, all three layers on.
Service ↔ service inside a clusterNative PQ ZAP — same as above, no TLS in front.

Per-language status

SDKX-Wing KATHybrid sigsPQ-RNS resolver
Go (zap-proto/go)reference impl
TypeScript (zap-proto/ts)spec-only
Python (zap-proto/py)spec-only
Rust (zap-proto/rust)spec-only
C++ (zap-proto/cpp)spec-only
Java (zap-proto/java)spec-only
C# (zap-proto/cs)spec-only

"Spec-only" means the schema is published but the resolver / publisher is not yet implemented in that SDK. The KAT vectors and the canonical record encoding are language-neutral — you can implement against the spec without waiting for a per-language client.

Further reading

On this page