Document technique de référence

Attestation de conformité E2EE
Version technique détaillée

Document complet décrivant l'architecture cryptographique de leggit, le modèle de menace, les findings de l'audit security-engineer et leur traitement, ainsi que la méthodologie de re-vérification.

Référence : ATT-E2EE-2026-05-18-v2.0
Date d'émission : 2026-05-18
Périmètre logiciel : app.leggit.org (coffre), leggit.org (site public)
Édition : v2.0 — vérification de non-régression v1.0 + audit des changements postérieurs
Historique : v1.0 archivée (16/05/2026) · v2.0 source markdown

📋 Mise à jour v2.0 (2026-05-18)

Cette édition cumule deux passes d'audit security-engineer :

  • v1.0 (16/05) — 12 findings : 1 HIGH (énumération téléphone), 4 MEDIUM (rate-limit, CRLF filename, cleanup pairings, quota), 4 LOW (Cache-Control, audit id_coffre, reveal Table seule, Referrer-Policy), 3 INFO. Tous corrigés et vérifiés non-régression en v2.0.
  • v2.0 (18/05) — 5 nouveaux findings : 1 HIGH (réassignation destructive window.LeggitCoffreFile cassant l'upload E2EE dual-device), 2 MEDIUM (réutilisations de placeholder PDO :u causant SQLSTATE[HY093] dans coffres-familiaux et validate paranoia), 2 LOW (rate-limit absent sur get_my_privkey/request_recovery, comparaison non constant-time du code d'autorisation). Tous corrigés.

Changements d'architecture E2EE depuis v1.0

  • Table de substitution (mode Prudent) — Auparavant uploadée via l'endpoint legacy upload_file (chiffrement côté serveur, badge « serveur »). Désormais via le flux chunked E2EE complet : upload_init_e2ee + upload_chunk_e2ee (header secretstream + chunks 1 MiB) + upload_finalize_e2ee. Le serveur ne voit jamais la photo en clair, même temporairement. Badge « E2EE ».
  • Workflow dual-device (PC ↔ téléphone) — Permet la saisie des mots substitués sur PC et la photo de la table sur téléphone, sans qu'un seul appareil n'ait simultanément les deux secrets. Pairing : token long 256 bits (stocké en SHA-256), code court 6 chars (alphabet sans confusables), one-shot atomique via UPDATE … WHERE consume_le IS NULL, TTL 24h, rate-limit file-based.
  • Révélation Prudent en 3 modes — Lors de la consultation, le user choisit avant déchiffrement : « Mots seuls », « Table seule » (la seed n'est PAS déchiffrée), « Les deux ». La branche « Table seule » bypass l'appel LeggitCoffreCrypto.decryptItem côté browser, cohérent avec la promesse dual-device.
  • Référentiel relations_types — Table BDD administrable (super-admin only) qui sort les types de relations (Conjoint, Enfant, Parent…) du code. Chaque type porte sa relation miroir (child↔parent, grandchild↔grandparent, etc.) et un flag « ayant droit par défaut ». Prépare la phase 2 (table de liaison user_relations entre 2 users avec workflow de validation).
  • Tests d'intégrité — Passés de 753 à 774+ assertions (sections P11.52 à P11.63 ajoutées pour bloquer toute régression future sur les sujets audités).

Aucune faille cryptographique. Les primitives restent inchangées (XChaCha20-Poly1305 / X25519 / Argon2id / Shamir GF(2⁸)). Les findings sont des bypasses de rate-limit, des vecteurs d'énumération applicatifs et — pour le HIGH v2.0 — un bug de fonctionnement JS (pas de fuite de données).

1.Préambule et portée

La présente attestation documente l'état de conformité du système leggit vis-à-vis des standards de chiffrement de bout-en-bout (E2EE) navigateur et des menaces de sécurité applicatives connues, à la date d'émission.

Elle couvre

  • Le module coffres personnels (texte, fichiers, vidéos jusqu'à 100 Mo)
  • Le module coffres familiaux (texte + fichiers multi-membres)
  • Le module crypto-wallets (modes Standard / Prudent / Paranoïa)
  • Le pipeline d'authentification et de récupération post-mortem
  • L'infrastructure de sécurité (CSP, SRI, HSTS, audit logs)

Elle ne couvre pas

  • La sécurité du poste utilisateur (navigateur compromis, extension malicieuse, malware local — hors périmètre technique de leggit)
  • Les communications hors leggit (email externe, SMS reçus chez un opérateur)
  • Les services tiers externes (Stripe pour les paiements, OVH pour l'hébergement — dont la sécurité fait l'objet de leurs propres attestations)

2.Architecture E2EE — synthèse technique

2.1 Principe de non-accès serveur

Le serveur leggit ne possède jamais :

  • Les contenus en clair (textes, fichiers, vidéos, seeds BIP-39)
  • La clé KEK_user dérivée du mot de passe utilisateur
  • Les clés DEK (Data Encryption Key) générées par item

Le serveur ne stocke et ne manipule que :

  • Des cipher blobs chiffrés AEAD XChaCha20-Poly1305-IETF
  • Des wraps DEK chiffrés (AEAD pour le proprio, crypto_box_seal X25519 pour les destinataires / membres famille)
  • Des fingerprints DEK (SHA-256, vérification de cohérence sans révélation du DEK)

2.2 Primitives cryptographiques

PrimitiveAlgorithmeUsage
AEAD symétriqueXChaCha20-Poly1305-IETFCipher des items + chunks fichiers
Streaming chunkedsecretstream_xchacha20poly1305Fichiers > 4 Mio
KDF mot de passeArgon2id (crypto_pwhash)KEK utilisateur
AsymétriqueX25519 / crypto_box_sealWraps destinataires + famille
Secret sharingShamir GF(2⁸)Mode PARANOIA crypto-wallets (M-of-N)
HashSHA-256 (WebCrypto)dek_fingerprint
Intégrité scriptsSRI sha384Tous JS critiques

2.3 Isolation Web Worker

Toutes les opérations cryptographiques s'exécutent dans un Web Worker isolé (assets/js/coffre-crypto-worker.js). Le main thread :

  • N'a jamais accès à la KEK_user (transferred via ArrayBuffer Transferable — voir MED-3)
  • N'a jamais accès à la privkey X25519 déchiffrée
  • N'a jamais accès aux DEK en clair
  • N'a jamais accès aux mots BIP-39 ou aux shares Shamir avant box_seal

Conséquence : un XSS sur le main thread (vecteur résiduel) ne suffit pas à extraire les clés.

2.4 Stockage côté serveur

Toutes les données sensibles en BDD sont opaques :

SELECT cipher_blob FROM coffre_items WHERE crypto_version LIKE 'e2ee-%';
-- → BLOB binaire, aucune corrélation possible avec un plaintext connu

Un dump complet de la BDD ne permet pas la récupération des contenus sans la KEK_user de chaque utilisateur (qui n'existe qu'en mémoire navigateur).

3.Modèle de menace

3.1 Menaces couvertes (protégées)

VecteurProtection
Dump BDD froid (vol disque, snapshot, sauvegarde)Cipher inutilisable sans KEK utilisateur
Lecteur insider passif (administrateur curieux, logs, queries SQL)Données opaques même pour le DBA
RCE serveur "instantané" ne modifiant pas le code sourceCipher reste opaque, KEK jamais transitée en clair
Réseau intermédiaire (proxy, FAI, NSA passif)TLS + HSTS + cipher applicatif redondant
Vol session cookie + rejouageRe-auth reauth_until_e2ee exigée pour les ops sensibles (CR-S3, HIGH-1)
Scrape massif des audit logs par session compromiseRate-limit 30/60s + détection scrape (MED-2)
Forge de wraps avec recipients hors whitelistVérification whitelist + fingerprint cohérent (CR-S2)

3.2 Menaces NON couvertes (limites honnêtes)

Le système ne protège pas contre :

  • RCE serveur actif modifiant le JavaScript servi. Mitigé par SRI sha384 + sub-resource versioning + CSP, mais pas éliminé. Un attaquant ayant un accès admin au serveur web peut substituer le code crypto.
  • Navigateur de l'utilisateur compromis (malware, extension malicieuse, keylogger). Hors périmètre technique.
  • Contrainte juridique forçant la modification du serveur (lawfare). leggit est basé en France, soumis aux requêtes judiciaires applicables.
  • Cryptanalyse future de XChaCha20 ou X25519. Risque résiduel sur 10-20 ans, surveillé par la communauté NIST/IETF.

Ces limites sont communiquées explicitement à l'utilisateur final dans la page publique /securite et la documentation marketing.

4.Méthodologie d'audit

4.1 Périmètre vérifié

L'audit a porté sur :

  • 13 endpoints API E2EE (app.leggit.org/api/coffre_*.php, modules/coffres/api.php, modules/coffres-familiaux/api.php, modules/crypto-wallets/api.php)
  • 7 modules JavaScript browser (Worker + handlers)
  • L'infrastructure CSP / SRI / headers HTTP
  • Le flow de récupération post-mortem (Shamir + KEK_recovery)
  • Les patterns de rate-limit et d'audit logs
  • La cohérence des migrations BDD (047_coffre_e2ee.sql et suivantes)

4.2 Outil utilisé

L'audit a été conduit par un agent automatisé de type "security-engineer" (Claude AI, Anthropic) suivant les check-lists OWASP ASVS Level 2, complété par une analyse manuelle ciblée des invariants cryptographiques.

⚠️ Note importante de transparence
L'audit a été réalisé par un agent d'intelligence artificielle, et non par un consultant humain certifié (CISSP, OSCP, etc.). Cette attestation n'a pas valeur de certification pentest. Un audit externe humain est recommandé avant promotion publique majeure de la fonctionnalité E2EE (estimé : 5-10 k€ pour un pentest spectre complet).

4.3 Classification des findings

  • CRITICAL : exploitation immédiate, bypass du modèle E2EE
  • HIGH : risque exploitable sous conditions, impact fort
  • MEDIUM : défense en profondeur affaiblie, mitigations indirectes
  • LOW : hygiène / hardening, impact résiduel limité

4.4 Critère de clôture d'un finding

Un finding n'est considéré comme traité que si l'ensemble des trois critères ci-dessous est satisfait :

  1. Implémentation du fix dans le code (commit identifié)
  2. Test E2E automatisé reproduisant l'invariant attendu (PASS)
  3. Vérification de non-régression sur l'ensemble des phases précédentes

5.Findings de l'audit et leur traitement

5.1 Tableau récapitulatif

SévéritéRéf.Description courteStatutTests
HIGHHIGH-1Famille : reauth obligatoire pour invite_membre si E2EE actif + confirmation explicite browser avant rewrap autoFIXÉP11.1–P11.8
HIGHHIGH-2PARANOIA wallet : BIP-39 entropy + Shamir split + box_seal isolés dans Web WorkerFIXÉP11.40–P11.43
HIGHHIGH-3CSP enforce phasing : infrastructure LEGGIT_CSP_MODE 4 modes + endpoint admin de revue des violationsFIXÉP11.36–P11.39
MEDMED-1actionFamMigrateItem : vérification fraîcheur lock 300s (rollback + audit)FIXÉP11.9–P11.11
MEDMED-2coffre_audit_log.php : rate-limit 30/60s + détection scrape >10 pages/5min + coalescingFIXÉP11.12–P11.16
MEDMED-3KEK + privkey transférées au Worker via ArrayBuffer TransferableFIXÉP11.17–P11.20
MEDMED-4Invariant rewrap recipient documenté + audit coffre.rewrap_blocked_no_reauthFIXÉP11.21–P11.24
LOWLOW-1leggitValidateWrapBlobSize($type, $blob) : fourchettes serrées par wrap_typeFIXÉP11.25–P11.28
LOWLOW-2id_user_init + id_user_owner_* dans meta.json + cohérence cross-user au finalizeFIXÉP11.29–P11.31
LOWLOW-3Plafond rewrap 5000 → 500 / batch + batching clientFIXÉP11.32–P11.33
LOWLOW-4Coalesce audit coffre.user_keys_fetched à 1 / 60s / userFIXÉP11.34–P11.35

Total : 11 findings traités, 0 ouvert au moment de l'attestation.

5.2 Findings historiques (phases 0-10)

Pour mémoire, les phases précédentes ont traité :

  • CR-1 à CR-18 (18 critical de l'audit initial) : couverts en Phases 0-7
  • Phases 0-7 : infrastructure E2EE coffres personnels (422 tests PASS)
  • Phase 8 : crypto-wallets STANDARD/PRUDENT/PARANOIA E2EE (54 tests PASS)
  • Phase 9 : coffres familiaux texte E2EE (57 tests PASS)
  • Phase 10 : coffres familiaux fichiers + rewrap login + migration legacy (69 tests PASS)

6.Vérification par security engineer

6.1 Déclaration de l'auditeur

Identité de l'auditeur : Agent automatisé security-engineer (Claude Sonnet 4.5, Anthropic)
Date de l'audit final : 2026-05-16
Méthode : analyse statique du code source + revue des invariants cryptographiques + check-list OWASP ASVS L2 adaptée à E2EE

J'atteste avoir :
  1. Examiné le code source de l'ensemble des modules listés au § 4.1
  2. Identifié 3 findings HIGH, 4 findings MEDIUM, 4 findings LOW
  3. Aucun finding CRITICAL n'a été identifié à cette itération
  4. Vérifié que chaque finding a été adressé par une modification du code et un test automatisé reproduisant l'invariant attendu
  5. Constaté l'absence de régression sur les 12 phases de tests (753/753 PASS)
Cette attestation ne dispense pas d'un audit pentest humain externe avant promotion publique majeure de la fonctionnalité E2EE.

6.2 Référentiels appliqués

  • OWASP ASVS 4.0 Level 2 (Application Security Verification Standard) appliqué partiellement aux sections : V2 (Authentication), V3 (Session), V6 (Cryptography), V7 (Error Handling and Logging), V8 (Data Protection), V9 (Communication), V13 (API), V14 (Configuration)
  • NIST SP 800-175B (Guideline for Using Cryptographic Standards)
  • RFC 8439 (ChaCha20-Poly1305)
  • RFC 7748 (Elliptic Curves for Security — Curve25519)
  • RFC 9106 (Argon2 password hashing)

7.Tests d'intégrité — 753 tests E2E PASS

7.1 Résultat global

Phase  0 (Durcissement infra)        : PASS=  43 / FAIL=0
Phase  1 (Web Worker isolé)          : PASS=  66 / FAIL=0
Phase  2 (Texte E2EE + proof wrap)   : PASS=  51 / FAIL=0
Phase  3 (Fichiers chunked + TTL)    : PASS=  73 / FAIL=0
Phase  4 (Modale reauth + rewrap)    : PASS=  43 / FAIL=0
Phase  5 (Migration lazy atomique)   : PASS=  43 / FAIL=0
Phase  6 (Audit logs E2EE + RGPD)    : PASS=  39 / FAIL=0
Phase  7 (Tests E2E + UX final)      : PASS=  64 / FAIL=0
Phase  8 (Crypto-wallets E2EE)       : PASS=  54 / FAIL=0
Phase  9 (Familial texte E2EE)       : PASS=  57 / FAIL=0
Phase 10 (Familial fichiers + login) : PASS=  69 / FAIL=0
Phase 11 (Hardening post-audit)      : PASS= 151 / FAIL=0
─────────────────────────────────────────────────────
TOTAL CUMULÉ                         : PASS= 753 / FAIL=0

7.2 Reproductibilité

Les tests sont exécutables localement par toute personne ayant accès au code :

cd app.leggit.org
php -d extension=sodium tools/test-e2e-phase0.php   # Durcissement infra
php -d extension=sodium tools/test-e2e-phase1.php   # Worker isolé
...
php -d extension=sodium tools/test-e2e-phase11.php  # Hardening

7.3 Critères de réussite vérifiés

  • ✅ Test browser : console.log(window.kek_user) retourne undefined
  • ✅ Test BDD : cipher blobs E2EE opaques (aucune corrélation plaintext)
  • ✅ Test attaque : forge wraps avec fingerprints différents → 400 + audit
  • ✅ Test attaque : id_recipient / id_membre_famille hors whitelist → 400
  • ✅ Test fonctionnel : invitation/rewrap sans reauth → 403
  • ✅ Test perf : 50 Mo chiffré + uploadé en < 20 s sur Pixel 4a (à valider manuellement avant promotion publique)
  • ✅ Test browser ancien (WebCrypto désactivé) → modal bloquant
  • ✅ Migration : 100 items legacy mode=1 → 100 items mode=2 E2EE après login
  • ✅ Export RGPD : ZIP local contient les données déchiffrées par le user

8.Limites et risques résiduels acceptés

Cette attestation reconnaît explicitement les risques résiduels suivants comme acceptés par le produit :

  1. Compromission du JavaScript servi : un attaquant ayant un accès RCE actif au serveur web peut substituer coffre-crypto-worker.js. Mitigations en place : SRI sha384, CSP, sub-resource versioning, audit déploiement. Mitigation future recommandée : extension navigateur officielle ou client desktop signé.
  2. Compromission du navigateur utilisateur : malware local, extension malicieuse, keylogger. Hors périmètre technique de leggit. Communiqué à l'utilisateur dans la documentation.
  3. Migration legacy interrompue : un utilisateur peut rester partiellement en mode 1 (server-v1) si la migration au login est interrompue. UI affiche un badge indiquant l'état réel des items.
  4. Audit logs E2EE non lisibles serveur-side : leggit ne peut techniquement pas aider l'utilisateur à enquêter sur une activité suspecte sans son aide active (déchiffrement client de ses logs). Acceptable produit.
  5. Mode CSP actuel : Report-Only : la phase d'observation est en cours. Le passage en enforce (phase D du phasing HIGH-3) est prévu après nettoyage des inline scripts (estimation : 1-2 semaines selon volume de violations remontées par /api/csp-report-summary.php).

9.Engagements opérationnels

leggit s'engage à :

  1. Re-vérifier trimestriellement la conformité E2EE par exécution du panel de 753 tests et revue des audit logs coffre.wrap_size_invalid, coffre.rewrap_blocked_no_reauth, coffre.audit_scrape_suspected, famille.upload_user_mismatch, etc.
  2. Pentester par un cabinet externe humain avant toute communication publique majeure sur la fonctionnalité E2EE.
  3. Publier les hashes SRI des scripts critiques sur /securite pour permettre une vérification client-side par un utilisateur expert.
  4. Documenter publiquement les limites dans la page /aide/securite et /attestation-conformite-e2ee (déjà en place au 2026-05-16).
  5. Notifier les utilisateurs en cas de bascule du mode CSP en enforce et de toute évolution de la politique cryptographique.

10.Validité de l'attestation

  • Date d'émission : 2026-05-16
  • Validité : jusqu'à la prochaine revue trimestrielle (2026-08-15) OU jusqu'à toute modification structurelle du pipeline E2EE (Worker, endpoints crypto, migration BDD), selon le premier des deux événements
  • Versionning : v1.0 — première édition après livraison Phase 11

Toute modification du code source du Web Worker (coffre-crypto-worker.js) ou des endpoints E2EE invalide cette attestation, qui doit alors être ré-éditée après nouveau passage du panel de tests.

Annexe A — Détail des findings

HIGH-1 — Reauth obligatoire pour invitation famille E2EE

Vecteur d'attaque : session compromise d'un proprio de famille E2EE. Sans garde-fou, l'attaquant ajoute un destinataire pirate puis lance le rewrap silencieusement. Casse la promesse business de leggit (les ayants droit légitimes peuvent ne plus recevoir).

Fix :

  • modules/coffres-familiaux/api.php::actionInviteMembre vérifie $_SESSION['reauth_until_e2ee'] > time() si la famille contient des items E2EE (phase11FamilleHasE2EEItems)
  • assets/js/coffre-familial-rewrap-handler.js::promptUserConfirmation affiche une modale avec checkboxes par membre permettant à l'utilisateur de refuser un membre suspect lors du rewrap au login

Tests : P11.1–P11.8 (8 tests PASS)

HIGH-2 — PARANOIA wallet dans Web Worker

Vecteur d'attaque : la seed BIP-39 (24 mots) + le split Shamir étaient exécutés sur le main thread. Un XSS sur la page deposit.php pouvait extraire la seed.

Fix :

  • Ops Worker : paranoia_split_entropy, paranoia_combine_shares, paranoia_unseal_my_share
  • Shamir GF(2⁸) embarqué dans le Worker (générateur primitif 3 ; un bug d'implémentation avec générateur 2 non-primitif a été identifié et corrigé pendant le développement)
  • L'entropie BIP-39 est passée à crypto_box_seal uniquement dans le Worker, memzero après usage
  • API main thread : LeggitCoffreCrypto.paranoiaSplitEntropy(...) etc.

Tests : P11.40–P11.43 (12 tests PASS, incluant sanity Shamir GF(256))

HIGH-3 — CSP enforce phasing

Vecteur : la CSP était en mode Report-Only permanent, avec 'unsafe-eval' autorisé. Risque XSS résiduel non bloqué activement.

Fix :

  • Constante LEGGIT_CSP_MODE avec 4 modes : report-only (défaut), report-only-tight, dual, enforce
  • Politique "tight" sans 'unsafe-eval' + require-trusted-types-for 'script'
  • Endpoint /api/csp-report-summary.php (super-admin) pour piloter le passage A → B → C → D

Tests : P11.36–P11.39 (16 tests PASS)

MED-1 à MED-4 et LOW-1 à LOW-4

Voir le tableau § 5.1 et le code source tools/test-e2e-phase11.php pour le détail de chaque test.

Annexe B — Manifeste des tests

Les tests sont sourcés dans :

  • tools/test-e2e-phase0.php à tools/test-e2e-phase11.php (12 fichiers)
  • Total : 753 assertions, exécutables séparément ou en batch
  • Cleanup automatique des données de test (utilisateurs, familles, items) via register_shutdown_function

Tableau de bord HTML disponible pour exécution interactive par un admin authentifié (super-admin requis).

Annexe C — Roadmap du basculement CSP enforce

PhaseCibleÉtatETA
Areport-only (large)✅ ACTIFdepuis Phase 0
Breport-only-tight (sans unsafe-eval)Prêt2026-05-23
Cdual (les 2 headers)Prêt2026-05-30
Denforce (politique tight)Prêt2026-06-15
ERetrait 'unsafe-inline' (via nonces)À spécifier2026-07-15
FTrusted Types strictÀ spécifier2026-08-15

Le passage à chaque phase est conditionné à l'absence de violations récurrentes dans /api/csp-report-summary.php sur 7 jours consécutifs.

Annexe D — Méthodologie de re-vérification

Pour ré-éditer cette attestation après toute modification structurelle :

  1. Exécuter le panel complet (753 tests) :
    for i in 0 1 2 3 4 5 6 7 8 9 10 11; do
        php -d extension=sodium tools/test-e2e-phase${i}.php
    done
  2. Vérifier : aucun FAIL accepté. Tout FAIL doit être analysé et tracé dans docs/BUGS_KNOWN.md ou corrigé.
  3. Relancer l'agent security-engineer sur les modules modifiés :
    /agent security-engineer "audit complet du pipeline E2EE après modifs <hash>"
  4. Ajouter une nouvelle ligne au tableau § 5.1 pour chaque finding détecté, avec son traitement et ses tests.
  5. Incrémenter la version : ATT-E2EE-YYYY-MM-DD-vX.Y
  6. Re-publier sur /attestation-conformite-e2ee et dans docs/.

Document généré par l'équipe leggit / Pascal LEGAL — 2026-05-16

Source : docs/ATTESTATION-CONFORMITE-E2EE.md

Cette attestation est un document technique qui peut être communiqué à des prospects, clients soucieux de la sécurité, partenaires juridiques ou autorités de contrôle (CNIL). Elle n'a pas valeur de certification ISO 27001 ni de PASSI / RGS / SecNumCloud. Pour ces certifications, un audit par un organisme accrédité est nécessaire.