Aller au contenu

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-wrap avec IV par défaut A6A6A6A6A6A6A6A6
  • 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 les finally blocks
  • Transactions atomiques : rotateMasterEnvelope et revokeDevice avec queryRunner/rollback
  • Entités TypeORM : KeyEnvelope et DeviceBlacklist avec schéma vault_secure
  • ENUM EnvelopeType : master, device, recovery pour 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é : AesKwService séparé de KeyEnvelopeService
  • 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

  1. 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é.

  2. 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 sur deviceId seul crée un comportement cross-tenant.

  3. 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.

  4. 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.

  5. AES-KW avec Node.js crypto est robuste — L'algorithme id-aes256-wrap est stable depuis Node.js v15, l'implémentation native évite les dépendances tierces et garantit la conformité RFC 3394 au niveau cryptographique.