Aller au contenu

PD-35 — Plan d'implémentation


📚 Navigation User Story | Document | | | ---------- | -- | | 📋 [Spécification](PD-35-specification.md) | | | 🛠️ **Plan d'implémentation** | *(ce document)* | | ✅ [Critères d'acceptation](PD-35-acceptability.md) | | | 📝 [Retour d'expérience](PD-35-rex.md) | | [← Retour à crypto-proof](../PD-189-epic.md) · [↑ Index User Story](index.md)

Objectif

Implémenter le module d'enveloppes de clés (Key Wrapping) pour protéger K_master_user sans re-chiffrer les documents.

Choix techniques retenus

  • Algorithme : AES-256-KW (RFC 3394)
  • Bibliothèque : Node.js crypto (aes-256-wrap)
  • Types : master, device, recovery
  • Stockage : Table vault_secure.key_envelopes

Architecture ciblée

src/modules/crypto/
├── services/
│   ├── aes-kw.service.ts           # AES Key Wrap
│   └── key-envelope.service.ts     # Gestion enveloppes
├── controllers/
│   └── key-envelope.controller.ts  # API endpoints
├── entities/
│   ├── key-envelope.entity.ts
│   └── device-blacklist.entity.ts
└── dto/
    ├── create-master-envelope.dto.ts
    ├── create-device-envelope.dto.ts
    └── create-recovery-envelope.dto.ts

Découpage technique

Phase 1 : AES-256-KW Service

  1. Implémenter wrap(kek, plaintext) :
  2. Valider KEK 32 bytes
  3. Valider plaintext 32 bytes
  4. Retourner 40 bytes (32 + 8 IV)

  5. Implémenter unwrap(kek, wrapped) :

  6. Valider wrapped 40 bytes
  7. Vérifier IV A6A6A6A6A6A6A6A6
  8. Retourner plaintext 32 bytes

  9. Utiliser crypto.createCipheriv('aes-256-wrap')

Phase 2 : Entités

  1. Créer KeyEnvelope entity :
@Entity('key_envelopes', { schema: 'vault_secure' })
export class KeyEnvelope {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column('uuid')
  userId: string;

  @Column({ type: 'enum', enum: ['master', 'device', 'recovery'] })
  type: EnvelopeType;

  @Column('bytea')
  wrappedKey: Buffer;

  @Column({ nullable: true })
  deviceId?: string;

  @CreateDateColumn()
  createdAt: Date;

  @Column({ nullable: true })
  revokedAt?: Date;
}
  1. Créer DeviceBlacklist entity

Phase 3 : Key Envelope Service

  1. createMasterEnvelope(userId, wrappedKey) :
  2. Valider format wrapped key
  3. Créer enveloppe type=master
  4. Une seule active par user

  5. createDeviceEnvelope(userId, deviceId, wrappedKey) :

  6. Vérifier device non blacklisté
  7. Créer enveloppe type=device

  8. createRecoveryEnvelope(userId, wrappedKey) :

  9. Créer enveloppe type=recovery
  10. Une seule active par user

  11. revokeDeviceEnvelope(userId, deviceId, reason) :

  12. Marquer enveloppe révoquée
  13. Ajouter à blacklist

Phase 4 : Controller

  1. POST /crypto/envelopes/master :
  2. Créer enveloppe master

  3. POST /crypto/envelopes/device :

  4. Créer enveloppe device

  5. GET /crypto/envelopes :

  6. Lister enveloppes actives

  7. DELETE /crypto/envelopes/device/:id :

  8. Révoquer device

Phase 5 : Transactions

  1. Rotation password :
  2. Transaction atomique
  3. Créer nouvelle enveloppe master
  4. Marquer ancienne révoquée

  5. Révocation device :

  6. Transaction atomique
  7. Révoquer + blacklist

Phase 6 : Tests

  1. Tests wrap/unwrap conformité RFC 3394
  2. Tests unicité enveloppe master
  3. Tests révocation device
  4. Tests blacklist device

Points de vigilance

  • KEK : Jamais stockée, dérivée du password
  • wrapped_key : Seul élément persisté
  • Blacklist : Permanent, pas de réactivation
  • Transactions : Atomiques pour cohérence

Hors périmètre

  • Dérivation KEK (→ PD-33 côté client)
  • Chiffrement documents (→ PD-97 côté client)
  • HSM pour clés (→ PD-36)