PD-35 — Retour d'expérience¶
📚 Navigation User Story
| Document | | | ---------- | -- | | 📋 [Spécification](PD-35-specification.md) | | | 🛠️ [Plan d'implémentation](PD-35-plan.md) | | | ✅ [Critères d'acceptation](PD-35-acceptability.md) | | | 📝 **Retour d'expérience** | *(ce document)* | [← Retour à crypto-proof](../PD-189-epic.md) · [↑ Index User Story](index.md)1. Résumé exécutif¶
La User Story PD-35 visait à implémenter un module d'enveloppes de clés (Key Wrapping) permettant de protéger K_master_user via AES-256-KW (RFC 3394) avec support de trois types d'enveloppes : master (changement password), device (multi-appareils), et recovery (récupération BIP39). L'implémentation fournit un AesKwService conforme avec zeroization, un KeyEnvelopeService avec transactions atomiques, et des entités TypeORM. Cependant, 4 écarts MAJEURS sont identifiés : API non protégée (JWT désactivé), route GET recovery absente, vérification blacklist globale au lieu de par utilisateur, et journalisation probatoire non implémentée. Verdict : ACCEPTÉ AVEC RÉSERVES.
2. Points fluides¶
- AES-256-KW conforme RFC 3394 : utilisation de
id-aes256-wrapavec IV par défautA6A6A6A6A6A6A6A6 - Node.js crypto natif : pas de dépendance externe pour AES Key Wrap
- Validation longueurs stricte : KEK 32 bytes, plaintext 32 bytes, wrapped 40 bytes
- Zeroization systématique :
aesKwService.zeroize()appelé dans tous lesfinallyblocks - Transactions atomiques :
rotateMasterEnvelopeetrevokeDeviceavec queryRunner/rollback - Entités TypeORM :
KeyEnvelopeetDeviceBlacklistavec schémavault_secure - ENUM EnvelopeType :
master,device,recoverypour typage strict - lastUsedAt tracking : mise à jour automatique à chaque accès Device Envelope
- DTOs avec validation : class-validator pour format base64, UUID, longueurs
- Documentation JSDoc : architecture Key Wrapping documentée avec exemples
- Swagger/OpenAPI : annotations @ApiOperation, @ApiResponse complètes
- Service découplé :
AesKwServiceséparé deKeyEnvelopeService - generateKey() helper : génération CSPRNG intégrée
3. Points difficiles¶
| Difficulté | Contexte |
|---|---|
| JWT guard désactivé | Commenté pour développement, userId hardcodé 'test-user-123' |
| Route GET recovery absente | Seule la création est exposée, pas la récupération |
| Blacklist globale | isDeviceBlacklisted(deviceId) ignore userId, bloque device pour tous users |
| Journalisation TODO | Audit log commenté dans rotateMasterEnvelope et revokeDevice |
| Tests RFC 3394 | Aucun vecteur normatif, validation longueurs uniquement |
4. Hypothèses révélées tardivement¶
| Hypothèse initiale | Réalité découverte |
|---|---|
| Auth JWT disponible | FAUX — JwtAuthGuard et @CurrentUser n'existent pas, scaffolding DEV ONLY |
| Blacklist par (user, device) | FAUX — Query filtre uniquement sur deviceId, ignore userId |
| Récupération compte via API | FAUX — Création recovery envelope OK mais GET non implémenté |
| Audit log dispo (PD-XX) | FAUX — Aucune US dépendante créée, TODO laissés |
| Tests conformité RFC inclus | FAUX — Tests limités aux validations de format |
5. Invariants complexes à implémenter¶
| Invariant | Complexité |
|---|---|
| K_master_user identique | Ne doit JAMAIS changer durant la vie du compte |
| Zeroization après usage | Toutes les clés (KEK, K_master_user) effacées dans finally |
| Transaction atomique rotation | Unwrap/Re-wrap/Save dans même transaction |
| Unicité master/recovery | Une seule enveloppe active par type par utilisateur |
| Blacklist permanente | Révocation définitive, device ne peut plus créer envelope |
| Integrity check AES-KW | 8 bytes IV vérifient intégrité à l'unwrap |
6. Dette technique¶
| Dette | Impact | Priorité |
|---|---|---|
| JWT guard désactivé | Isolation utilisateur non assurée, toutes routes accessibles | HAUTE |
| Route GET recovery absente | Récupération compte impossible via API | HAUTE |
| Blacklist ignore userId | Device révoqué pour user A bloqué pour user B | HAUTE |
| Journalisation absente | Non-conformité NF Z42-013 / ISO 14641 | HAUTE |
| Tests RFC 3394 | Conformité non prouvée par vecteurs officiels | MOYENNE |
| @CurrentUser decorator | Dépendance AuthModule non résolue | MOYENNE |
7. Risques résiduels¶
| Risque | Probabilité | Impact | Mitigation suggérée |
|---|---|---|---|
| Accès non autorisé | Élevée | CRITIQUE | Implémenter JwtAuthGuard avant production |
| Isolation utilisateur | Élevée | ÉLEVÉ | Extraire userId du JWT, pas hardcodé |
| Blacklist cross-user | Moyenne | MOYEN | Ajouter userId dans query isDeviceBlacklisted |
| Récupération bloquée | Moyenne | ÉLEVÉ | Ajouter GET /crypto/keys/recovery-envelope |
| Audit manquant | Élevée | MOYEN | Implémenter service audit avec signatures HSM |
| Memory dump | Faible | ÉLEVÉ | Zeroization déjà en place, vérifier exhaustivité |
8. Améliorations processus¶
| Amélioration | Bénéfice attendu |
|---|---|
| Définir dépendances inter-US | Clarifier que PD-35 dépend de JwtAuthGuard (PD-XX) |
| Tests de conformité RFC dès la spec | Inclure vecteurs RFC 3394 dans critères d'acceptation |
| Scaffolding explicite | Documenter clairement que "DEV ONLY" = écart MAJEUR accepté |
| CRUD complet dans spec | Si "récupération compte", alors GET recovery requis |
| Revue sécurité pre-merge | Détecter blacklist globale avant acceptation |
9. Enseignements clés¶
-
Le scaffolding "DEV ONLY" génère des écarts MAJEURS — Les guards commentés et userId hardcodés constituent des failles de sécurité documentées. Ce pattern de développement doit être explicitement accepté comme dette technique ou bloqué.
-
La vérification blacklist doit respecter le modèle de données — La contrainte UNIQUE sur
(user_id, device_id)implique que la query doit filtrer sur les deux colonnes. Une query surdeviceIdseul crée un comportement cross-tenant. -
Un mécanisme de récupération nécessite une route de récupération — Créer une Recovery Envelope sans exposer de route GET rend le mécanisme inutilisable via API. La spec doit préciser le flux complet.
-
La journalisation probatoire doit être une dépendance explicite — Les TODO "audit log (PD-XX)" sans US créée garantissent que cette contrainte ne sera pas satisfaite. Créer la dépendance au backlog.
-
AES-KW avec Node.js crypto est robuste — L'algorithme
id-aes256-wrapest stable depuis Node.js v15, l'implémentation native évite les dépendances tierces et garantit la conformité RFC 3394 au niveau cryptographique.