Skip to main content
Tout ce qu’OxiMail stocke (blobs de messages, pièces jointes, contenu du chat, données de calendrier, et la base SQLite elle-même) est chiffré sur le disque. Le modèle à garder en tête est LUKS, pas du bout en bout : comme un disque chiffré, le serveur peut déchiffrer le contenu à la lecture pour un utilisateur authentifié ; contrairement au chiffrement de bout en bout, la perte du mot de passe d’un utilisateur ne rend jamais son courrier illisible, et IMAP, CalDAV et le webmail continuent tous de fonctionner.

Ce que le modèle protège, et ce qu’il ne protège pas

Protégé : un disque volé, une sauvegarde volée du répertoire de blobs ou du fichier de base, un disque déclassé, un attaquant avec accès hors ligne au stockage. Sans le matériel de clés, les blobs sont du chiffré et la base est un fichier SQLCipher. Non protégé : un serveur en marche entièrement compromis (il détient les clés pour faire son travail), ou un administrateur malveillant avec accès au matériel de clés. Ces menaces relèvent d’autres couches : le contrôle d’accès, le durcissement et la garde physique.

La hiérarchie de clés

Les clés enveloppent des clés : la rotation et l’effacement agissent sur de petits objets, jamais sur des téraoctets de contenu.
  1. La clé maîtresse d’organisation : une par organisation. Elle enveloppe tout ce qui suit.
  2. Les paires de clés par compte (X25519) : les clés de contenu de chaque compte sont enveloppées pour sa paire ; le contenu des blobs est chiffré en XSalsa20-Poly1305.
  3. Les clés symétriques à l’échelle de l’organisation (AES-256-GCM) : pour le contenu partagé par nature (pièces jointes de salons, blobs partagés) plutôt que possédé par un compte.
Deux propriétés sont délibérées :
  • Les clés de compte sont enveloppées avec la clé maîtresse de l’organisation, jamais avec le mot de passe de l’utilisateur. Une enveloppe dérivée du mot de passe rendrait les blobs indéchiffrables chaque fois que le serveur ne détient pas le mot de passe à la lecture, c’est-à-dire exactement le cas d’IMAP, de CalDAV et des workers d’arrière-plan. Un changement de mot de passe ne rechiffre donc jamais rien.
  • Les blobs sont auto-descriptifs (ADR-096) : chacun porte un en-tête de chiffrement nommant son schéma, le store ne devine jamais comment déchiffrer.
Chaque blob est adressé par le SHA-256 de son clair (la déduplication a lieu avant le chiffrement, et seulement au sein de l’organisation : la déduplication inter-organisations n’existe pas, à dessein). Dans une boîte partagée, la clé suit le compte effectif, pas l’appelant. Une clé secrète ne déchiffre jamais que les blobs de son propre compte : ancrer le choix sur le principal authentifié cassait donc le partage dans les deux sens. Un bénéficiaire pouvait lister une boîte partagée et en lire les sujets sans jamais ouvrir un corps, et un message qu’un bénéficiaire classait dans la boîte d’autrui était chiffré pour le bénéficiaire, laissant le propriétaire incapable de le lire dans sa propre boîte. Les deux se résolvent désormais sur le compte effectif, celui qu’a produit un grant validé, qui ne diffère de l’appelant qu’une fois le grant accepté par resolve_shared_context. Cela n’élargit aucune autorisation : l’usurpation n’est pas représentable, et le contrôle par objet dans les gestionnaires reste l’autorité sur ce qui est servi. Les capacités, elles, restent résolues sur l’appelant authentifié. Le chemin de lecture impose un discriminant de plus : un bénéficiaire d’un autre compte, ou un lien de partage anonyme, ne peut déchiffrer qu’un blob référencé par sa propre projection (fichiers, courrier ou avatar) sous le compte résolu. Même si le contrôle d’autorisation d’une route était contourné par un bug, la couche crypto refuse un blob d’une autre projection. Défense en profondeur au niveau des clés, pas seulement des ACL.

La base : SQLCipher

Les métadonnées structurées (dossiers, en-têtes, drapeaux, journal de changements) vivent dans SQLite, chiffré avec SQLCipher :
La clé de la base vit dans le matériel de clés sous /etc/oximail ; l’assistant la provisionne. Un data.db copié sans cette clé est du bruit. Quel SQLCipher, et comment le vérifier. La ligne actuelle embarque SQLCipher 4.14.0 sur SQLite 3.51.3, lié statiquement dans le binaire. Le point compte plus qu’il n’y paraît : les CVE du moteur lui-même n’atteignent une base chiffrée que par une publication SQLCipher. Un build qui en épingle une ancienne traîne donc en silence tous les correctifs SQLite qu’il a manqués. Interrogez le binaire qui tourne, pas un manifeste :
Le binaire livré ne dépend d’aucune bibliothèque de cryptographie du système. Il porte la sienne, et le chemin de déploiement le prouve désormais au lieu de le supposer : un build dont la sortie ldd mentionne une bibliothèque TLS, de cryptographie ou SQLite du système est refusé avant d’atteindre un hôte. La propriété tenait auparavant par un accident d’ordre de liaison, c’est-à-dire précisément le genre de chose qui se casse en silence au premier changement de dépendance sans rapport.

Les sauvegardes ne portent pas de clés

À dessein (ADR-102), une sauvegarde de la base et du répertoire de blobs n’inclut pas le matériel de clés : une sauvegarde volée n’est donc pas une fuite de données. Le corollaire est opérationnel, et absolu :
Sauvegardez /etc/oximail (configuration, clés DKIM, matériel de clés) séparément des données, et conservez-le avec le même soin qu’une clé de récupération de chiffrement de disque. Des sauvegardes de données sans la sauvegarde des clés sont du chiffré pour toujours. oximail verify-backup contrôle vos sauvegardes avant que vous en ayez besoin.

Les opérations sur les clés

  • La rotation ré-enveloppe les petits objets de clés sous une nouvelle clé maîtresse ; les blobs de contenu ne sont pas touchés, la rotation reste donc bon marché quelle que soit la taille des boîtes (séparation clés/contenu).
  • Le séquestre produit une copie scellée du matériel de clés pour une garde hors de la machine. rotate et escrow sont tous deux conditionnés à un exercice de séquestre : la commande refuse d’avancer tant que le séquestre sur disque n’a pas été prouvé restaurable. Un séquestre jamais testé n’est pas un séquestre.
  • account regenerate-key frappe une paire de clés neuve pour un compte et rend définitivement illisible chaque blob chiffré sous l’ancienne. Elle existe pour des scénarios de récupération délibérés ; la phase d’audit compte et affiche les blobs concernés avant de demander confirmation. Une clé manquante à l’exécution échoue toujours bruyamment (ADR-050) et renvoie l’opérateur ici : le serveur ne sert ni ne stocke jamais du clair en silence à la place.

La crypto-suppression

Supprimer un compte détruit son matériel de clés : chaque blob du compte devient indéchiffrable à cet instant, sans toucher aux fichiers eux-mêmes. C’est ce qui rend l’effacement RGPD praticable à l’échelle d’une boîte : le balayage supprime les lignes et les documents d’index, et le chiffré resté sur le disque est du bruit. Le seul stockage sans clés à détruire, l’index de recherche plein texte, est balayé explicitement.

Ce que ce n’est pas

  • Pas du chiffrement de bout en bout. Le serveur déchiffre pour les lectures authentifiées. Le bout en bout pour des surfaces précises est un sujet distinct, avec des compromis distincts (la recherche, les passerelles DAV et le filtrage côté serveur cessent tous de fonctionner sous E2E).
  • Pas du chiffrement de système de fichiers. Le modèle se compose très bien avec LUKS/dm-crypt en dessous, mais ne l’exige pas : le schéma applicatif protège les sauvegardes et les fichiers copiés même hors du volume chiffré.