Comment ça fonctionne

How it works

Le parcours d'un message, du premier contact à la destruction automatique.

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 contenant leurs clés publiques X25519.

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

Un X3DH handshake (Extended Triple Diffie-Hellman) crée le secret partagé initial. À partir de là, le Double Ratchet démarre.

An X3DH handshake (Extended Triple Diffie-Hellman) creates the initial shared secret. From there, the Double Ratchet begins.

Alice→ génère (SK_a, PK_a)
Bob→ génère (SK_b, PK_b)
X3DH 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)
SK = HKDF(DH1 ‖ DH2 ‖ DH3 ‖ DH4)
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.

Each conversation turn (Alice speaks, Bob replies) triggers a new X25519 exchange — the DH Ratchet.

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.

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 avec la clé du ratchet. Le tag 128-bit authentifie le message. Puis Ed25519 signe ct ‖ convId ‖ timestamp.

Then: AES-256-GCM encryption with the ratchet key. The 128-bit tag authenticates it. Then Ed25519 signs ct ‖ convId ‖ timestamp.

1. "Hello" → pad → [256 bytes]
2. AES-256-GCM(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 du ratchet.

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

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