Security

Every app that integrates MeshWhisper gets post-quantum E2EE without having to understand or implement cryptography. These are the primitives underneath.

Security levels

Level classification matches Apple's iMessage PQ3 security levels. Signal is Level 2. Apple iMessage is Level 3.

Level 1 Done

Classical E2EE

AES-256-GCM per message. Every message encrypted and authenticated before it leaves the device.

Level 2 Done

Post-quantum session establishment

PQXDH — hybrid X3DH + ML-KEM-768. Protects against "harvest now, decrypt later" quantum attacks. Matches Signal's PQXDH specification.

Level 3 Roadmap

Post-quantum ratchet healing

Periodic ML-KEM injection into the live Double Ratchet. Even if ratchet state is compromised, PQ forward secrecy is restored at the next injection point.

Key exchange and session encryption

The Signal protocol, extended with post-quantum key encapsulation.

Level 2

PQXDH

Session establishment uses a hybrid of X3DH and ML-KEM-768. Alice encapsulates to Bob's ML-KEM public key during the handshake. The final shared secret mixes all X25519 DH outputs with the ML-KEM shared secret under a BLAKE3 domain separation context.

An adversary recording encrypted traffic today cannot decrypt it with a future quantum computer. The limitation: if ratchet state is compromised after establishment, future traffic can be decrypted classically. That is what Level 3 addresses.

Level 1 + 2

Double Ratchet

Every message uses a fresh key via a symmetric-key ratchet. When a new message arrives from the other side, a DH ratchet step advances the root chain using fresh X25519 — providing forward secrecy and break-in recovery simultaneously.

Out-of-order delivery is handled via a skipped-message-key store with a MAX_SKIP cap of 2,000 — sized to cover the relay's 72-hour store-and-forward TTL.

Roadmap — Level 3

PQ3 ratchet healing

Every 50 messages — or every 24 hours — one side advertises a new ML-KEM-768 public key in the message header. The other side encapsulates to it and includes the ciphertext in the next reply. Both sides mix the resulting shared secret into the root key.

The exposure window is bounded by the injection cadence rather than the session lifetime. The spec is written — docs/pq3-ratchet-spec.md.

Implemented

Safety numbers

Both parties can verify a 60-digit safety number out-of-band to confirm no man-in-the-middle. Computed as a sorted BLAKE3 hash of both Ed25519 identity keys, formatted as 12 groups of 5 decimal digits. Identical regardless of which party calls it first.

mw.getSafetyNumber(peerId)
→ "12345 67890 12345 67890 ..."

What the node knows

The threat model is explicit. The node is an ISP carrying encrypted traffic, not a party to the conversation.

What the node can see

  • Source IPs of connected devices
  • Push tokens for offline delivery
  • Destination hashes (rotating hourly)
  • Traffic timing and volume

What the node cannot see

  • Message content
  • Sender or recipient identity
  • Which application a packet belongs to
  • Session keys or any key material

Primitives

@noble/curves
X25519, Ed25519

Elliptic curve key exchange and signing

@noble/ciphers
AES-256-GCM

Authenticated encryption

@noble/hashes
BLAKE3

KDF, destination hash derivation, safety numbers

@noble/post-quantum
ML-KEM-768

NIST FIPS 203 — PQXDH encapsulation

Audited pure JavaScript — no WASM, no native bindings. Identical in browser, Node.js, and React Native.