Comment fonctionne notre chiffrement de bout en bout
Mise à jour : juin 2026
Bref résumé
- Tes données sont chiffrées sur ton appareil et ne le quittent que sous forme de texte chiffré illisible. Notre serveur ne stocke que des blobs chiffrés et n'a accès à aucun texte en clair.
- Toutes les clés sont dérivées d'un code de récupération de 128 bits qui ne quitte jamais ton appareil. Nous ne le connaissons pas et ne pouvons pas le restaurer.
- Chaque enregistrement est chiffré avec sa propre clé et authentifié avant déchiffrement (Verify-before-Decrypt avec Ed25519).
- E2EE ne protège pas tout : certaines métadonnées (volume de données, moments d'accès) restent visibles, et un appareil compromis ou un code de récupération perdu ne peuvent pas être sauvés par la cryptographie.
Ce que « bout en bout » signifie pour nous
Le chiffrement de bout en bout signifie : le texte en clair de tes données n'existe que sur tes propres appareils. Le chiffrement et le déchiffrement se font exclusivement en local. Ce qui est synchronisé entre les appareils et stocké sur notre serveur est toujours déjà chiffré avant de quitter l'appareil.
Concrètement : une ligne de données est codée canoniquement en JSON côté client, puis chiffrée avec un procédé AEAD. Le serveur ne reçoit qu'un blob d'octets opaque plus quelques champs d'administration. Il ne possède aucune des clés nécessaires pour déchiffrer. C'est une propriété architecturale, pas une promesse : comme les clés ne parviennent jamais au serveur, ce dernier ne peut techniquement pas lire le contenu.
Cliquez sur les composants pour voir les détails :
Appareil A (Expéditeur)
Les données sont chiffrées localement sur l'appareil avec AES-256-GCM. Le code de récupération et les clés ne quittent jamais l'appareil.
Comment tes données sont chiffrées
Nous utilisons des composants éprouvés et standardisés des bibliothèques @noble/ciphers, @noble/hashes et @noble/curves :
- AES-256-GCM chiffre tes enregistrements. AES-256-GCM est un procédé AEAD qui assure à la fois la confidentialité (personne ne lit) et l'intégrité (la manipulation est détectée). Chaque enregistrement utilise un Nonce aléatoire de 12 octets et un code d'authentification de 16 octets (MAC). Format : nonce(12) ‖ ciphertext ‖ mac(16).
- XChaCha20-Poly1305 chiffre le matériel clé lors de l'appairage des appareils (Key-Wrapping). C'est également un procédé AEAD, mais il utilise un Nonce plus long de 24 octets, ce qui rend les Nonces aléatoires sans risque.
- AAD (Associated Data) lie chaque texte chiffré à ses métadonnées — en-tête, Bucket, UUID de l'enregistrement, révision, Key-Epoch, version du schéma et longueur remplie. Ces champs sont authentifiés : si un seul d'entre eux est modifié, le déchiffrement échoue. Un AAD vide est strictement rejeté (protection Confused-Deputy).
- Padding dissimule la taille exacte : chaque enregistrement est rempli à une taille de Bucket (256, 1024, 4096, 16384 ou 65536 octets ; au-delà, multiples de 65536). Ainsi, la taille du blob ne révèle qu'une classe approximative, pas la quantité exacte de données.
- Signature Ed25519 (blob_sig) signe chaque blob. Avant chaque déchiffrement, le client vérifie cette signature contre les clés d'appareil autorisées (Verify-before-Decrypt). La vérification s'exécute strictement selon RFC-8032 (zip215:false), ce qui exclut les contrefaçons via des points de faible ordre. Les données manipulées génèrent immédiatement une erreur, pas un défaut silencieux.
Tes clés restent sur ton appareil
La racine de tout est une entropie de récupération de 128 bits — le code de récupération. À partir de ces 16 octets aléatoires, HKDF-SHA256 (une fonction de dérivation de clé qui génère de nombreuses clés séparées par domaine à partir d'un secret) dérive de manière déterministe un Master-Secret, dont proviennent d'autres clés : un Account-ID, une Key-Encryption-Key, la clé Auth (authSeed) et la clé de routage. Chaque dérivation utilise son propre label versionné (par exemple mypep/master/v1), de sorte que les clés ne se chevauchent pas.
Par collection, il existe une Collection-Key propre, et par enregistrement et révision, une Record-Key propre (rec/<uuid>/<rev>). Cette clé unique par (enregistrement, révision) assure une séparation complète des clés : aucune clé AES-GCM n'est jamais utilisée deux fois — une forte protection contre les attaques de réutilisation de Nonce.
Le Master-Secret est délibérément non stocké. Seules les valeurs dérivées et les clés de signature/wrapping propres à l'appareil résident localement — dans le navigateur en IndexedDB.
Cliquez sur une clé pour voir les détails :
Code de récupération
Entropie aléatoire de 128 bits. La clé racine absolue. Générée lors de la création du compte. Ne quitte jamais l'appareil.
Récupération — fonctionnalité et responsabilité : l'arbre entier des clés dépend du code de récupération. Nous ne le stockons pas ; une trappe, un Key-Escrow ou une réinitialisation côté serveur sont architecturalement exclus — non seulement par politique, mais parce que les clés ne parviennent techniquement jamais au serveur. C'est le cœur d'une véritable E2EE : personne d'autre que toi — pas même nous — ne peut restaurer tes données sans le code de récupération ou un appareil appairé. Si le code est perdu et qu'aucun appareil couplé n'existe plus, les données sont définitivement inaccessibles.
Ce que notre serveur voit — et ce qu'il ne voit pas
Le serveur ne stocke que des blobs chiffrés. Il ne voit pas : le contenu de tes enregistrements, les noms de tes collections (ils n'apparaissent que comme un Bucket HMAC-SHA256 opaque) ou tes clés.
Pour être honnête, il voit cependant certaines métadonnées : les valeurs de Bucket opaques, les révisions d'enregistrement, la classe de taille approximative via le Padding, la fréquence de synchronisation, les ID d'appareil (comme SHA-256 du hash des clés d'appareil) et la position du curseur lors de la synchronisation. On peut en déduire qu'un compte est actif et approximativement combien et comment des données de quelle taille sont déplacées à quel moment — mais pas de quoi il s'agit en substance.
La résolution des conflits se fait uniquement par les révisions (révision plus élevée gagne, « Last-Write-Wins »), car le serveur ne connaît pas le contenu et ne peut pas fusionner en substance. L'authentification se fait par Challenge-Response avec Ed25519 et un token JWT suivant ; la liste des appareils est signée avec ta clé Auth et vérifiée exclusivement côté client.
Plusieurs appareils (Appairage)
Un nouvel appareil, comme le navigateur, est appairé avec le téléphone via code QR. Le code QR transmet les clés d'appareil publiques du navigateur directement (hors bande), pas via le serveur — cela protège l'échange de clés contre les attaques de l'intercepteur. Le téléphone empaquette le bundle Identity (Account-Data-Key, Auth-Key, Account-ID, Routing-Key) par X25519-ECDH et XChaCha20-Poly1305 exclusivement pour le navigateur et le transmet de manière chiffrée.
Après déchiffrement, le navigateur affiche une empreinte digitale (SHA-256 de l'Account-ID, en groupes hexadécimaux). S'il correspond à l'affichage sur le téléphone, il est garanti que tu as reçu le bon bundle. Les clients Web n'ont pas leur propre code de récupération : en cas de perte du stockage du navigateur, tu te rapparille simplement avec le téléphone.
Flux de couplage
Le navigateur génère des clés temporaires et les affiche sous forme de QR code. Le smartphone le scanne. Ce canal direct (hors bande) immunise l'échange de clés contre les attaques de l'homme du milieu.
Ce que cela NE protège PAS
E2EE est puissant, mais pas une panacée. Honnêtement sur les limites :
- Appareil compromis : les malwares dans le contexte du navigateur ou l'accès direct à IndexedDB peuvent lire les clés stockées localement et tout déchiffrer. La base de données locale est en texte clair.
- Code de récupération perdu : sans code et sans appareil appairé, il n'y a pas de retour. Il n'existe pas de rotation de clés et pas de mécanisme de sauvegarde.
- Métadonnées : les volumes de données, les classes de taille, les motifs d'accès et les moments restent visibles pour le serveur.
- Blobs de photos : les photos sont adressées par contenu (
blob_id = sha256(blob)). Quiconque possède déjà un texte en clair peut vérifier si exactement cette photo a été téléchargée. - Serveur malveillant : il ne peut ni lire ni falsifier le contenu (Verify-before-Decrypt protège), mais il pourrait retenir des données ou redemander d'anciennes listes d'appareils signées (Replay). Les dégâts d'un tel Replay sont limités : l'appareil s'enregistre de manière idempotente, donc une ancienne liste rejouée ne remplace pas l'état actuel. Le serveur ne peut pas inventer de nouvelles données valides.
- Clé Auth : quiconque obtient la clé Auth peut modifier la liste des appareils et se ré-authentifier. Elle doit rester secrète.
Les composants cryptographiques en bref
- AES-256-GCM — chiffrement d'enregistrement (AEAD) ; Nonce de 12 octets, MAC de 16 octets.
- XChaCha20-Poly1305 — Key-Wrapping lors de l'appairage (AEAD) ; Nonce de 24 octets, MAC de 16 octets.
- Argon2id v1.3 — dérivation basée sur un mot de passe, résiliente en mémoire (standard : 64 MiB, 3 itérations, 4 voies) ; disponible pour les futures fonctionnalités basées sur les mots de passe, mais NON pour le code de récupération — celui-ci est déjà une entropie aléatoire de 128 bits et n'a pas besoin de résilience en mémoire.
- HKDF-SHA256 — dérivation de toutes les clés à partir du code de récupération, séparation par domaine via des labels versionnés ; output de 32 octets.
- HMAC-SHA256 — routage opaque des collections (calcul du Bucket).
- SHA-256 — hachage pour les empreintes digitales, les ID d'appareil et l'adressage des photos.
- Ed25519 (RFC-8032-strikt, zip215:false) — signatures pour les enregistrements, la liste des appareils et l'authentification.
- X25519-ECDH — échange de clés lors de l'appairage des appareils.
- Padding-Buckets (256 B à 65536 B, au-delà multiples de 65536) — dissimulation de la taille.
- Longueur de clé uniformément 32 octets (256 bits).