Comment ça fonctionne

How it works

De la machine M-125 à votre poche — le parcours d'un message, du premier contact à la destruction automatique.

From the M-125 machine to your pocket — a message's journey, from first contact to automatic destruction.

1

Échange de clés

Key Exchange

Quand Alice et Bob se connectent pour la première fois, ils scannent leurs QR codes v2 contenant leurs clés publiques X25519 et ML-KEM-1024.

When Alice and Bob connect for the first time, they scan each other's v2 QR codes containing their X25519 and ML-KEM-1024 public keys.

Un PQXDH handshake (Post-Quantum Extended Diffie-Hellman) crée le secret partagé initial en combinant X25519 et ML-KEM-1024 (NIST FIPS 203). À partir de là, le Double Ratchet démarre.

A PQXDH handshake (Post-Quantum Extended Diffie-Hellman) creates the initial shared secret by combining X25519 and ML-KEM-1024 (NIST FIPS 203). From there, the Double Ratchet begins.

Alice→ génère (X25519_a, ML-KEM_a)
Bob→ génère (X25519_b, ML-KEM_b)
PQXDH Handshake :
DH1 = X25519(IK_a, SPK_b)
DH2 = X25519(EK_a, IK_b)
DH3 = X25519(EK_a, SPK_b)
DH4 = X25519(EK_a, OPK_b)
ct = ML-KEM.Encaps(pk_b) → (ss, ct)
SK = HKDF(DH1 ‖ DH2 ‖ DH3 ‖ DH4 ‖ ss)
Double Ratchet — visualisation
Double Ratchet — visualization
msg₁key = KDF(CK₁)
msg₂key = KDF(CK₂)
🔄 DH Ratchet → new CK
msg₃key = KDF(CK₃)
msg₄key = KDF(CK₄)
Chaque message a une clé unique. Les clés sont détruites après usage.
Every message has a unique key. Keys are destroyed after use.
2

Double Ratchet

À chaque tour de conversation (Alice parle, puis Bob répond), un nouvel échange X25519 est effectué — c'est le DH Ratchet. L'échange initial PQXDH garantit une résistance post-quantique.

Each conversation turn (Alice speaks, Bob replies) triggers a new X25519 exchange — the DH Ratchet. The initial PQXDH exchange ensures post-quantum resistance.

Pour chaque message individuel, une KDF Chain dérive une clé unique via HKDF. Résultat : même si un attaquant capture une clé, il ne peut lire ni les messages passés ni futurs.

For each individual message, a KDF Chain derives a unique key via HKDF. Result: even if an attacker captures one key, they can't read past or future messages.

🏛️ SPQR Triple Ratchet V3.5 — Tous les 10 messages, une ré-encapsulation ML-KEM-1024 injecte un nouveau secret post-quantique dans la rootKey via HKDF. La protection PQ est continue, pas seulement au handshake initial.

🏛️ SPQR Triple Ratchet V3.5 — Every 10 messages, an ML-KEM-1024 re-encapsulation injects a fresh post-quantum secret into rootKey via HKDF. PQ protection is continuous, not just at the initial handshake.

3

Chiffrement & Signature

Encryption & Signing

Le message est d'abord paddé à une taille fixe (256B/1KB/4KB/16KB) pour cacher sa longueur réelle.

The message is first padded to a fixed size (256B/1KB/4KB/16KB) to hide its actual length.

Ensuite : chiffrement AES-256-GCM (ou ChaCha20-Poly1305 sur les appareils sans accélération AES-NI) avec la clé du ratchet. Le tag 128-bit authentifie le message. Puis Ed25519 signe ct ‖ convId ‖ timestamp.

Then: AES-256-GCM (or ChaCha20-Poly1305 on devices without AES-NI hardware acceleration) encryption with the ratchet key. The 128-bit tag authenticates it. Then Ed25519 signs ct ‖ convId ‖ timestamp.

⚡ Sélection adaptative V3.5 — Le cipher est auto-détecté au démarrage via ARMv8 Crypto Extension probe. Les deux sont AEAD (authentification intégrée). Le champ cipherSuite dans le wire format garantit l'interopérabilité.

⚡ Adaptive selection V3.5 — Cipher is auto-detected at startup via ARMv8 Crypto Extension probe. Both are AEAD (built-in authentication). The cipherSuite field in the wire format ensures interoperability.

1. "Hello" → pad → [256 bytes]
2. AES-256-GCM | ChaCha20-Poly1305(key, iv, padded) ciphertext + tag₁₂₈
3. Ed25519.sign(ct ‖ convId ‖ ts) signature₅₁₂
4. senderUid = HMAC(convId, realUid) anonymous ID
→ Firebase reçoit un blob opaque. Rien n'est lisible côté serveur.
Blob chiffré + signature
Encrypted blob + signature
VPN TUN
3 relais Tor (Guard → Middle → Exit)
HTTPS
Firebase (blob opaque)
Firebase (opaque blob)
delete-after-delivery ✓
4

Transport Tor

Le blob chiffré est envoyé via un VPN TUN local qui route tout le trafic vers un proxy SOCKS5 → Tor.

The encrypted blob is sent via a local VPN TUN that routes all traffic through a SOCKS5 → Tor proxy.

Le message traverse 3 relais Tor (Guard, Middle, Exit). Firebase ne voit qu'une IP de sortie Tor — jamais la vôtre. Combiné au senderUid HMAC, l'anonymat est total.

The message traverses 3 Tor relays (Guard, Middle, Exit). Firebase only sees a Tor exit IP — never yours. Combined with senderUid HMAC, anonymity is complete.

5

Réception & Destruction

Receive & Destroy

Bob reçoit le blob. Il vérifie la signature Ed25519 (badge ✅ ou ⚠️), puis déchiffre avec sa clé AES-256-GCM ou ChaCha20-Poly1305 du ratchet (selon le champ cipherSuite).

Bob receives the blob. He verifies the Ed25519 signature (badge ✅ or ⚠️), then decrypts with his AES-256-GCM or ChaCha20-Poly1305 ratchet key (based on the cipherSuite field).

Le message déchiffré est stocké dans SQLCipher (base Room chiffrée AES-256). Si un timer éphémère est actif, le message s'autodétruit. Côté serveur, delete-after-delivery supprime le blob immédiatement.

The decrypted message is stored in SQLCipher (AES-256 encrypted Room DB). If an ephemeral timer is active, the message self-destructs. Server-side, delete-after-delivery removes the blob immediately.

Ed25519.verify(sig, ct ‖ convId ‖ ts) = VALID
AES-256-GCM.decrypt(key, iv, ct) → [padded]
unpad([padded]) → "Hello"
💾 Store → SQLCipher(AES-256)
⏱️ Timer → autodestruction
🗑️ Firebase → blob supprimé

Résultat : invisibilité totale

Result: total invisibility

Contenu chiffré
Content encrypted
IP masquée
IP hidden
Identité anonyme
Identity anonymous
Aucune trace
No trace left
Voir le code sourceView source code