PD-34 — Retour d'expérience¶
📚 Navigation User Story
| Document | | | ---------- | -- | | 📋 [Spécification](PD-34-specification.md) | | | 🛠️ [Plan d'implémentation](PD-34-plan.md) | | | ✅ [Critères d'acceptation](PD-34-acceptability.md) | | | 📝 **Retour d'expérience** | *(ce document)* | [← Retour à crypto](../PD-189-epic.md) · [↑ Index User Story](index.md)1. Résumé exécutif¶
La User Story PD-34 visait à implémenter la dérivation HKDF-SHA3-256 pour produire K_doc (clé unique par document) depuis K_master_user, avec domain separation via info string normative. L'implémentation est fonctionnelle et fournit l'isolation cryptographique attendue, mais présente 4 écarts : info string non conforme (Kdoc vs K_doc), architecture à 2 niveaux (K_master → K_share → K_doc) au lieu de 1 (K_master → K_doc), validation docId permissive, et hkdfExtract non conforme RFC 5869. Verdict : ACCEPTÉ AVEC RÉSERVES.
2. Points fluides¶
- Bibliothèque @noble/hashes : intégration simple, API claire, SHA3-256 natif
- Architecture modulaire : séparation nette
hkdf.ts(primitives) etkeys.ts(API hex) - Wrappers hex API :
deriveKShare(),deriveKDoc()facilitent l'intégration - Zeroization systématique : clés intermédiaires effacées après usage
- Fonction utilitaire
uuidToBytes(): conversion UUID → 16 bytes centralisée danscrypto/utils - Domain separation effective : K_share ≠ K_doc via salt et info différents
- Tests de déterminisme : même input → même output validé
- Chaînage complet :
deriveKDocFromMaster()simplifie l'usage courant
3. Points difficiles¶
| Difficulté | Contexte |
|---|---|
| Ambiguïté spec vs plan | Spec dit IKM=K_master_user direct, plan introduit K_share intermédiaire |
| Format info string | Hésitation entre :: et _ (constants.ts vs hkdf.ts) non résolue |
| @noble/hashes API | hkdf() fait Extract+Expand ensemble, impossible de séparer proprement |
| Validation UUID vs string | Choix de fallback TextEncoder pour flexibilité mais perd garantie 16 bytes |
| Re-exports multiples | Chaîne hkdf.ts → keys.ts → consumers complexifie le debug |
4. Hypothèses révélées tardivement¶
| Hypothèse initiale | Réalité découverte |
|---|---|
| Spec et plan sont alignés | FAUX — Plan introduit K_share absent de la spec |
| IKM = K_master_user directement | FAUX — Implémentation utilise K_share intermédiaire |
| Info string normative respectée | FAUX — Kdoc au lieu de K_doc (underscore manquant) |
| hkdf() permet Extract seul | FAUX — @noble/hashes combine Extract+Expand |
| constants.ts utilisé partout | FAUX — hkdf.ts définit ses propres HKDF_CONTEXTS |
5. Invariants complexes à implémenter¶
| Invariant | Complexité |
|---|---|
| Unicité K_doc par document | Salt = docId impose conversion UUID → 16 bytes cohérente |
| Hiérarchie 2 niveaux vs 1 | Plan demande K_share intermédiaire, spec ne le mentionne pas |
| Domain separation stricte | Contextes différents K_share/K_doc avec même K_master source |
| Déterminisme cross-platform | Même docId → même K_doc sur iOS/Android/Web |
| Effacement clés intermédiaires | K_share doit être zeroize après dérivation K_doc |
6. Dette technique¶
| Dette | Impact | Priorité |
|---|---|---|
| Info string divergente | HKDF_CONTEXTS.KDOC = "...::Kdoc::..." vs spec "...::K_doc::..." | HAUTE |
| K_share non documentée dans spec | Niveau intermédiaire absent de PD-34-specification.md | HAUTE |
| hkdfExtract non conforme | Ne produit pas PRK seule, appelle hkdf() complet | MOYENNE |
| Fallback TextEncoder | docId non-UUID accepté, perd garantie 16 bytes | BASSE |
| constants.ts ignoré | CONTEXT_KDOC correct mais non utilisé par hkdf.ts | MOYENNE |
7. Risques résiduels¶
| Risque | Probabilité | Impact | Mitigation suggérée |
|---|---|---|---|
Interop failure si autre client utilise spec exacte (K_doc) | Moyenne | ÉLEVÉ | Aligner hkdf.ts sur constants.ts |
| Audit RFC 5869 échoue sur hkdfExtract | Moyenne | MOYEN | Documenter comportement ou fix |
| K_share exposée via API sans nécessité | Faible | FAIBLE | Masquer dans module privé |
| docId non-UUID produit salt variable | Faible | MOYEN | Logger warning si fallback |
8. Améliorations processus¶
| Amélioration | Bénéfice attendu |
|---|---|
| Aligner spec et plan AVANT implémentation | Détecter K_share manquant dans spec plus tôt |
| Importer constants.ts au lieu de redéfinir | Éviter divergences info string |
| Test d'interop avec vecteurs spec | Valider output exact avec IKM/salt/info normatifs |
| Revue croisée spec ↔ code | Identifier écarts de naming (Kdoc vs K_doc) |
| Wrapper hkdfExtract conforme RFC | Implémenter via HMAC direct si nécessaire pour audit |
9. Enseignements clés¶
-
Les divergences plan/spec créent de la dette — Le plan a introduit K_share sans mise à jour de la spec, causant l'écart E-02.
-
Les constantes centralisées doivent être utilisées —
constants.tsdéfinitCONTEXT_KDOC = "ProbatioVault::K_doc::v1"(correct) maishkdf.tsdéfinitHKDF_CONTEXTS.KDOC = "ProbatioVault::Kdoc::v1"(incorrect). Pattern identique à PD-33. -
Les bibliothèques ont leurs contraintes — @noble/hashes combine Extract+Expand dans
hkdf(), rendant hkdfExtract non conforme RFC 5869 telle qu'implémentée. -
La flexibilité peut nuire — Le fallback TextEncoder pour docId non-UUID facilite les tests mais introduit un risque d'incohérence en production.
-
La hiérarchie de clés doit être documentée — L'ajout de K_share (niveau intermédiaire) améliore l'architecture mais doit être reflété dans la spec pour éviter confusion lors d'audits.