Skip to content

Security overview

Zentalk implements the Signal Protocol (X3DH + Double Ratchet) with a post-quantum hybrid handshake (enabled by default), layered metadata protection, and a publicly auditable key transparency log. This page maps the full stack; each layer has a dedicated page with wire-level detail.

Layer Mechanism Primitives Details
Key agreement X3DH (Extended Triple Diffie-Hellman) X25519, Ed25519, HKDF-SHA256 X3DH key agreement
Post-quantum hardening Hybrid X3DH X25519 + ML-KEM-768 (Kyber-768), HKDF combiner Post-quantum hybrid encryption
Session encryption Double Ratchet X25519 DH ratchet, HMAC-SHA256 chains, AES-256-GCM Double Ratchet
Groups and channels Epoch-based symmetric key ratchet HKDF-SHA256, AES-256-GCM Group and channel encryption
Metadata protection Sealed sender envelopes, address hashing, padding X25519 ECDH, HKDF-SHA256, AES-256-GCM, HMAC-SHA256 Sealed sender
Key transparency Append-only Merkle log with signed roots SHA-256 Merkle tree, inclusion proofs Key Transparency Log
User verification Safety numbers and SAS codes Iterated SHA-512, HKDF-SHA256 Contact verification

The layer pages build on one another; the table lists them in recommended reading order.

Two interoperable implementations exist:

  • TypeScript (web client): zentalk/src/services/e2ee/ — the reference for everything documented in this section.
  • Go (mesh node): zentalk-node/pkg/protocol/x3dh.go and ratchet.go — parity implementation sharing the same protocol structure and HKDF info strings.

For programmatic access to the APIs referenced throughout, see the REST API and the TypeScript SDK.

Purpose Primitive Parameters
Diffie-Hellman X25519 32-byte keys
Signatures Ed25519 32-byte public keys, 64-byte signatures
Post-quantum KEM ML-KEM-768 (Kyber-768, NIST FIPS 203) 1184-byte public key, 1088-byte ciphertext, 32-byte shared secret
Authenticated encryption AES-256-GCM 32-byte key, 12-byte IV, 16-byte tag
Key derivation HKDF-SHA256 (RFC 5869) Protocol-specific salts and info strings
Chain advancement / MACs HMAC-SHA256 32-byte output

Encrypted with keys the server never holds:

  • Direct message content (Double Ratchet, AES-256-GCM)
  • Group and channel message content (epoch keys distributed over pairwise E2EE)
  • Group key distribution messages
  • Sender identity inside sealed-sender envelopes (X25519-sealed sender certificate)

Zentalk’s metadata posture is summarized below; the relay-visibility details are documented in Sealed sender. In summary:

Metadata Current state
Sender address (sealed envelopes) Hidden — encrypted inside the sender certificate
Recipient address (sealed envelopes) Hashed (32 bytes) in v2; HMAC-bucketed into 1024 buckets in v3
Mesh routing addresses Salted SHA-256 hashes (zentalk-mesh-v1 salt) — obfuscation, not anonymity: anyone who knows an address can recompute its hash
Payload size Bucketed by relay padding
Timing Exact (v2); coarser timing planned
Presence, typing indicators, read receipts Encrypted variants (encrypted_presence_update, encrypted_typing, encrypted_read_receipt) are implemented; mesh-only deployments enforce them by blocking the plaintext variants. Plaintext is visible to backend and relays only on non-mesh-only deployments
X3DH initial messages Contain senderAddress in plaintext for new sessions only; established sessions do not leak it

The project maintains a full threat model defining nine adversary classes and six trust boundaries. In summary:

  • Passive network observers (A1) — mitigated by TLS 1.3, relay padding, and E2EE. A dedicated “store now, decrypt later” sub-class (A1*) is addressed by the hybrid post-quantum handshake.
  • Active MITM (A2) — mitigated by signed-prekey verification in X3DH, the Key Transparency Log, and out-of-band SAS verification. KTL is tamper-evident, not tamper-proof; a split-view attack requires out-of-band verification to detect.
  • Malicious mesh validators (A3) — content is E2EE; membership tokens fail closed; replication factor of 3 or more tolerates Byzantine drop. Residual: routing tags reveal partial social-graph information until v3 bucketing is fully deployed.
  • Compromised relay (A4)sealed sender hides sender identity; sender certificates expire after 1 hour, limiting the impact of a compromise.
  • Malicious clients (A5) — DPoP-bound refresh tokens, per-endpoint rate limits, and Double Ratchet replay protection (persistent IV tracking).
  • Compelled access and insiders (A6–A8) — the operator can only produce ciphertext and hashed identifiers; message content keys never leave user devices.
  • Device compromise (A9) — key material is wrapped with AES-256-GCM in secure browser storage; an unlocked-device adversary is outside the threat envelope, as is the case for messaging applications generally.

All long-term and session key material is generated client-side and stored in IndexedDB via a secure key storage layer. The server stores only public key bundles (identity keys, signed prekeys, one-time prekeys) published through the keys API — see X3DH key agreement for the publish/claim flow and Authentication for how requests are authorized.