Skip to main content

Cryptography

Recipient encryption is three libraries composed into one cascade. signal-protocol is the classical handshake and ratchet. ml-kem is ML-KEM-1024. pqxdh combines those two toward Signal’s post-quantum extended Diffie–Hellman.

This page is those libraries. Password sealing of your mailbox files lives in the crypto crate on Persistence. The product cascade — AES, then Signal, then PQXDH, then RSA hybrid, then ML-KEM — is specified on Signal and post-quantum.

It is research and development — subject to change. The Signal handshake library is open source. The rest of the messenger is not. These implementations are spec-faithful and educational. They are not wire-compatible with the Signal app.

signal-protocol​

The classical handshake and ratchet: X3DH key agreement, the Double Ratchet, identity keys, signed prekeys, one-time prekeys, and AES-256-GCM message encryption. Rust core, compiled to WebAssembly, with TypeScript bindings.

This is the piece Glitr publishes. You can inspect the source, the tests, and the proofs.

A second, local-only Dioxus gallery (signal-protocol-gallery) reuses whatsup-ui chrome and calls signal-protocol-core directly. It does not replace the React deploy, and it has no GitHub Pages URL.

Formal verification (hax/F*, Rocq, Lean, ProVerif) lives in that repository. Treat it as research-grade work on the handshake library, not an audit of the messenger.

Try path: Signal Protocol.

ml-kem​

Signal alone does not meet a post-quantum requirement. ml-kem is a pure-Rust ML-KEM-1024 (FIPS 203) wrapper for this stack.

PieceRole
ml-kem-coreThin wrapper over libcrux-ml-kem 0.0.10 — the same crate and pin as libsignal — plus hybrid AES-256-GCM.
ml-kem-wasmwasm-bindgen façade.
ml-kem-galleryDioxus demos on whatsup-ui chrome.

Lattice review lives in libcrux, not in this wrapper. The crate targets the 1024 parameter set (Signal PQXDH production): 1568-byte public key, 3168-byte expanded secret key, 1568-byte ciphertext, 32-byte shared secret. Secret keys are the expanded FIPS form.

Hybrid encrypt in core: encapsulate, HKDF-SHA256 with info ML-KEM-1024-AES-GCM, then AES-256-GCM. This crate is a different parameter set and key encoding from the older JS ML-KEM-768 layer.

Open the ML-KEM gallery

StoryLive
Key generationgui/ml-kem/key-generation
Encapsulategui/ml-kem/encapsulate
Hybrid encryptgui/ml-kem/hybrid-encrypt

pqxdh​

pqxdh is the composition step: signal-protocol-core (X25519 ECDH, HKDF-SHA-256, Ed25519, Double Ratchet) plus ml-kem-core (ML-KEM-1024), aimed at Signal PQXDH revision 3.

Documented deviations from the spec:

SpecThis crate
XEdDSA on a single X25519 identitySeparate X25519 (DH) and Ed25519 (signatures)
SHA-256 or SHA-512HKDF-SHA-256 via signal-protocol-core
libsignal encodings / protobufsSimple EncodeEC / EncodeKEM with type tags

Parameters: curve X25519, hash SHA-256, info PQXDH-v1, pqkem ML-KEM-1024, AEAD AES-256-GCM. It is a spec-faithful educational implementation. It is not libsignal wire-compatible.

Open the PQXDH gallery

StoryLive
Key generationgui/pqxdh/key-generation
Prekey bundlegui/pqxdh/prekey-bundle
Handshakegui/pqxdh/handshake
Initial messagegui/pqxdh/initial-message
Sessiongui/pqxdh/session
Errorsgui/pqxdh/errors

Try path: Post-quantum.

How the product uses them​

The shipped recipient path stacks these libraries as defense-in-depth: AES → Signal → PQXDH on the inside, then RSA hybrid → ML-KEM on the outside. PQXDH already includes X25519, ML-KEM-1024, and a ratchet. The extra Signal and standalone ML-KEM wraps are independent layers on the same payload — not a spec-recommended profile.

Full wording: Signal and post-quantum. The cascade manager and password envelopes are on Persistence.

Next: Persistence — git as storage, a crypto utility, and a GraphQL store over files.