PD-97 — Retour d'expérience¶
📚 Navigation User Story
| Document | | | ---------- | -- | | 📋 [Spécification](PD-97-specification.md) | | | 🛠️ [Plan d'implémentation](PD-97-plan.md) | | | ✅ [Critères d'acceptation](PD-97-acceptability.md) | | | 📝 **Retour d'expérience** | *(ce document)* | [← Retour à mobile-ios](../PD-195-epic.md) · [↑ Index User Story](index.md)1. Résumé exécutif¶
La User Story PD-97 visait à implémenter un pipeline cryptographique client-side zero-knowledge pour iOS, incluant AES-256-GCM, gestion des enveloppes K_master, et effacement mémoire sécurisé. L'implémentation est fonctionnelle et fournit les primitives crypto nécessaires, mais présente 3 écarts MAJEURS (dépendance WASM/asm.js non validée en prod, opérations sur UI thread, zeroization incomplète) et 1 écart MINEUR (AAD optionnel). Verdict : ACCEPTÉ AVEC RÉSERVES.
2. Points fluides¶
- Architecture modulaire : structure
src/crypto/claire avec séparation aes-gcm, envelope, zeroize, constants, types, utils - @noble/ciphers : bibliothèque pure JavaScript sans dépendance WASM, fonctionne sur Hermes
- Polyfill WebAssembly : solution élégante qui définit un stub WASM pour forcer argon2-browser en mode asm.js
- Documentation zeroization : limitations JavaScript clairement documentées dans le code (strings immutables, GC)
- SecureBuffer class : wrapper avec destruction explicite et vérification d'état
- withZeroize patterns : helpers try/finally pour garantir cleanup même en cas d'erreur
- Tests Vitest : configuration dédiée pour modules ESM (@noble/ciphers)
- Envelope API complète : create, open, rewrap, validate, serialize/deserialize
3. Points difficiles¶
| Difficulté | Contexte |
|---|---|
| Hermes sans WASM | Le moteur JavaScript de React Native ne supporte pas WebAssembly, nécessitant un fallback asm.js |
| Strings JavaScript immutables | Impossible d'effacer les mots de passe/clés stockés en string, uniquement déréférencement |
| Argon2id 64 MiB | Allocation mémoire importante qui freeze l'UI sans worker offloading |
| Validation production | Le polyfill WASM n'est pas validé avec EAS builds, risque de régression en release |
| Dual test runner | Jest incompatible avec @noble/* (ESM), nécessitant Vitest séparé |
4. Hypothèses révélées tardivement¶
| Hypothèse initiale | Réalité découverte |
|---|---|
| Hermes supporte WebAssembly | FAUX — WASM non disponible, polyfill nécessaire pour asm.js fallback |
| JavaScript permet effacement mémoire | FAUX — Strings immutables, GC peut conserver des copies, "best-effort" seulement |
| Argon2id s'exécute sans blocage UI | FAUX — 64 MiB allocation synchrone, freeze perceptible sur devices lents |
| @noble/ciphers nécessite WASM | FAUX — Pure JavaScript, fonctionne parfaitement sur Hermes |
| Un seul test runner suffit | FAUX — Jest pour React Native, Vitest pour modules ESM-only |
5. Invariants complexes à implémenter¶
| Invariant | Complexité |
|---|---|
| Zero-knowledge strict | Aucune clé, password ou plaintext ne doit quitter l'appareil — vérifié par architecture |
| IV unique par chiffrement | Chaque encrypt() génère un nouvel IV via expo-crypto CSPRNG |
| Zeroization systématique | try/finally avec zeroize() sur tous les buffers sensibles après usage |
| Tag 16 bytes | GCM authentication tag fixé à 128 bits, non configurable |
| Envelope versioning | ENVELOPE_VERSION dans constants.ts pour migration future |
6. Dette technique¶
| Dette | Impact | Priorité |
|---|---|---|
| Argon2id sur UI thread | UX dégradée (freeze 2-5s sur devices lents) | HAUTE |
| Zeroization incomplète | K_master, K_doc, plaintexts fichiers non effacés systématiquement | HAUTE |
| Validation EAS manquante | Polyfill WASM non testé en production builds | HAUTE |
| AAD optionnel | Permet chiffrement sans contexte authentifié | MOYENNE |
| Strings jamais effacées | Passwords en mémoire jusqu'au GC | MOYENNE |
7. Risques résiduels¶
| Risque | Probabilité | Impact | Mitigation suggérée |
|---|---|---|---|
| Polyfill WASM échoue en prod | Moyenne | ÉLEVÉ | Tester avec EAS build --profile production |
| UI freeze Argon2id | Élevée | MOYEN | Migrer vers InteractionManager ou native module |
| Clés en mémoire après usage | Élevée | MOYEN | SecureBuffer systématique + audit usage |
| Régression @noble version | Faible | MOYEN | Lock versions dans package.json |
| Attaque memory dump | Faible | ÉLEVÉ | Documenter comme limitation connue |
8. Améliorations processus¶
| Amélioration | Bénéfice attendu |
|---|---|
| Tester polyfill avec EAS builds | Valider comportement asm.js en production |
| Migrer Argon2id vers native | Supprimer freeze UI via react-native-argon2 ou Turbo Module |
| SecureBuffer partout | Wrapper systématique pour toutes les clés dérivées |
| Rendre AAD obligatoire | Forcer contexte authentifié sur tous les chiffrements |
| Audit zeroization | Vérifier tous les points de sortie des clés et plaintexts |
| Worker crypto | Offloader opérations lourdes hors thread JS principal |
9. Enseignements clés¶
-
Hermes a des limitations critiques — Le moteur JavaScript de React Native ne supporte pas WebAssembly. Toute bibliothèque crypto dépendant de WASM doit avoir un fallback vérifié (asm.js ou pure JS).
-
JavaScript n'est pas conçu pour la sécurité mémoire — Les strings sont immutables, le GC est non déterministe, les buffers peuvent être copiés en interne. L'effacement mémoire est "best-effort" et doit être documenté comme tel.
-
Les opérations crypto lourdes bloquent l'UI — Sans worker ou InteractionManager, Argon2id avec 64 MiB freeze l'application. C'est un UX problem critique pour l'inscription/connexion.
-
@noble/ciphers est un excellent choix — Bibliothèque pure JavaScript, auditée, sans dépendance WASM. Parfaitement compatible avec Hermes et l'écosystème React Native.
-
Les polyfills doivent être validés en conditions réelles — Un polyfill qui fonctionne en développement (Expo Go) peut échouer en production (EAS builds). Toujours tester le chemin critique avec le profil de build final.