PD-243 — Plan d'implémentation¶
Navigation User Story
| Document | | | ---------- | -- | | [Expression de besoin](PD-243-besoin.md) | | | [Specification](PD-243-specification.md) | | | [Tests](PD-243-tests.md) | | | **Plan** | *(ce document)* | | [Retour d'experience](PD-243-rex.md) | | [← Retour a crypto](../PD-189-epic.md)1. Références¶
| Document | Version |
|---|---|
| PD-243-specification.md | v2 (post Gate 3) |
| PD-243-tests.md | v2 (post Gate 3) |
| Architecture cryptographique | v4.1 |
| Anomalie PD-34 | DONE_WITH_ANOMALY |
Décision PO Gate 3 v1 : Breaking change label K_doc accepté. Migration documents existants incluse.
2. Découpage en composants¶
2.1 Composant CONST — Centralisation des labels HKDF¶
Fichier : src/crypto/constants.ts Responsable : agent-constants
| Tâche | Description | REQ | INV |
|---|---|---|---|
| CONST-01 | Ajouter constante CONTEXT_K_AUTH | REQ-243-05 | INV-243-02 |
| CONST-02 | Vérifier que CONTEXT_KDOC existe avec underscore | REQ-243-06 | INV-243-07 |
Code cible :
Observable : Grep "K_auth" dans constants.ts retourne la constante.
2.2 Composant HKDF — Service de dérivation¶
Fichier : src/services/hkdf.ts Responsable : agent-crypto
| Tâche | Description | REQ | INV |
|---|---|---|---|
| HKDF-01 | Supprimer HKDF_CONTEXTS local | REQ-243-07 | INV-243-02 |
| HKDF-02 | Importer labels depuis constants.ts | REQ-243-07 | INV-243-02 |
| HKDF-03 | Corriger label KDOC → K_DOC (breaking change) | REQ-243-06, REQ-243-11 | INV-243-07 |
| HKDF-04 | Implémenter deriveAuthKey() | REQ-243-01..05 | INV-243-01 |
| HKDF-05 | Ajouter pattern try/finally pour zeroization | PROP-243-05 | INV-243-04 |
Signature deriveAuthKey() :
/**
* Dérive K_auth depuis K_master via HKDF-SHA3-256
*
* @param kMaster - Clé maître (32 bytes)
* @returns K_auth (32 bytes)
* @throws Error si kMaster invalide
*/
export async function deriveAuthKey(kMaster: Uint8Array): Promise<Uint8Array>;
Paramètres HKDF : - Hash : SHA3-256 - IKM : kMaster (32 bytes) - Salt : empty (new Uint8Array(0)) - Info : CONTEXT_K_AUTH ("ProbatioVault::K_auth::v1") - Output : 32 bytes
Observable : deriveAuthKey(kMaster).length === 32
2.3 Composant KEYS — Re-export¶
Fichier : src/crypto/keys.ts Responsable : agent-crypto
| Tâche | Description | REQ | INV |
|---|---|---|---|
| KEYS-01 | Re-exporter deriveAuthKey | REQ-243-01 | - |
Observable : import { deriveAuthKey } from '../crypto/keys' fonctionne.
2.4 Composant TESTS — Tests unitaires¶
Fichier : src/__tests__/services/hkdf.test.ts Responsable : agent-tests
| Tâche | Description | TC |
|---|---|---|
| TEST-01 | Test TC-CRYPTO-01 (32 bytes) | TC-CRYPTO-01 |
| TEST-02 | Test TC-CRYPTO-02 (depuis K_master pas password) | TC-CRYPTO-02 |
| TEST-03 | Test TC-CRYPTO-03 (HKDF-SHA3-256 vecteur) | TC-CRYPTO-03 |
| TEST-04 | Test TC-DET-01 (déterminisme) | TC-DET-01 |
| TEST-05 | Test TC-DET-02 (K_master différent) | TC-DET-02 |
| TEST-06 | Test TC-ISO-01 (K_auth !== K_encryption) | TC-ISO-01 |
| TEST-07 | Test TC-ISO-02 (K_auth !== K_doc) | TC-ISO-02 |
| TEST-08 | Test TC-LABEL-02 (K_doc avec underscore) | TC-LABEL-02 |
| TEST-09 | Test TC-LABEL-03 (K_auth label exact) | TC-LABEL-03 |
| TEST-10 | Test TC-REG-03 (K_doc breaking change) | TC-REG-03 |
| TEST-11 | Test TC-REG-01 (K_encryption inchangée) | TC-REG-01 |
| TEST-12 | Test TC-REG-02 (K_share inchangée) | TC-REG-02 |
| TEST-13 | Test TC-LABEL-01 (grep absence "Kdoc") | TC-LABEL-01 |
Tâche VECTOR-01 : Produire vecteurs de test HKDF-SHA3-256 - Responsable : agent-crypto (étape 6) - Deadline : Avant TEST-03 et TEST-09 - Méthode : Calcul Python (référence externe) + validation @noble/hashes
Observable : npm test -- --testPathPattern=hkdf passe.
3. Flux de données¶
┌─────────────────────────────────────────────────────────────────┐
│ K_master (32 bytes) │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ HKDF-SHA3-256 ││
│ │ ┌───────────────┬───────────────┬───────────────┐ ││
│ │ │ info=K_auth │ info=K_enc │ info=K_share │ ││
│ │ │ salt=empty │ salt=empty │ salt=SHARE │ ││
│ │ └───────┬───────┴───────┬───────┴───────┬───────┘ ││
│ │ ↓ ↓ ↓ ││
│ │ K_auth K_encryption K_share ││
│ │ (32 bytes) (32 bytes) (32 bytes) ││
│ │ │ ││
│ │ ┌─────┴─────┐ ││
│ │ │ HKDF │ ││
│ │ │ salt=docId│ ││
│ │ │ info=K_doc│ ││
│ │ └─────┬─────┘ ││
│ │ ↓ ││
│ │ K_doc ││
│ │ (32 bytes) ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
Propriété d'isolation : Tous les labels sont différents → toutes les clés sont distinctes pour un même K_master.
3bis. Diagrammes Mermaid¶
3bis.1 Graphe de dependances des composants¶
graph TD
CONST["CONST<br/>src/crypto/constants.ts<br/>(CONTEXT_K_AUTH, CONTEXT_KDOC)"]
HKDF["HKDF<br/>src/services/hkdf.ts<br/>(deriveAuthKey, deriveKey)"]
KEYS["KEYS<br/>src/crypto/keys.ts<br/>(re-export public API)"]
TESTS["TESTS<br/>src/__tests__/services/hkdf.test.ts<br/>(TC-CRYPTO, TC-DET, TC-ISO, TC-LABEL, TC-REG)"]
NOBLE["@noble/hashes<br/>(hkdf, sha3_256)"]
HKDF -->|importe labels| CONST
HKDF -->|utilise| NOBLE
KEYS -->|re-exporte deriveAuthKey| HKDF
TESTS -->|teste| HKDF
TESTS -->|verifie labels| CONST
TESTS -->|importe via| KEYS
style CONST fill:#e8f5e9,stroke:#2e7d32
style HKDF fill:#e3f2fd,stroke:#1565c0
style KEYS fill:#fff3e0,stroke:#ef6c00
style TESTS fill:#fce4ec,stroke:#c62828
style NOBLE fill:#f3e5f5,stroke:#7b1fa2 3bis.2 Sequence d'implementation¶
sequenceDiagram
participant C as CONST<br/>constants.ts
participant H as HKDF<br/>hkdf.ts
participant K as KEYS<br/>keys.ts
participant T as TESTS<br/>hkdf.test.ts
Note over C: Phase 1 — Labels
C->>C: CONST-01 Ajouter CONTEXT_K_AUTH
C->>C: CONST-02 Verifier CONTEXT_KDOC (underscore)
Note over H: Phase 2 — Refactor imports
H->>C: HKDF-01 Supprimer HKDF_CONTEXTS local
H->>C: HKDF-02 Importer labels depuis constants.ts
Note over H: Phase 3 — Breaking change
H->>H: HKDF-03 Corriger label Kdoc → K_doc
Note over H: Phase 4 — Nouvelle derivation
H->>H: HKDF-04 Implementer deriveAuthKey()
H->>H: HKDF-05 Ajouter try/finally zeroization
Note over K: Phase 5 — API publique
K->>H: KEYS-01 Re-exporter deriveAuthKey
Note over T: Phase 6 — Validation
T->>H: TEST-01..06 Tests crypto + determinisme + isolation
T->>C: TEST-08..09 Tests labels (K_doc, K_auth)
T->>H: TEST-10..12 Tests regression (K_encryption, K_share, K_doc breaking)
T->>C: TEST-13 Grep absence "Kdoc" 4. Mapping Invariants → Mécanismes¶
| Invariant | Mécanisme | Observable |
|---|---|---|
| INV-243-01 | deriveAuthKey prend kMaster comme IKM | Signature + test |
| INV-243-02 | Import CONTEXT_* depuis constants.ts | Grep inline literals = 0 |
| INV-243-03 | Labels différents → sorties différentes | Tests TC-ISO-01, TC-ISO-02 |
| INV-243-04 | try/finally + fill(0) | Revue code + test erreur |
| INV-243-05 | Tests de régression K_encryption, K_share | TC-REG-01, TC-REG-02 |
| INV-243-06 | Vecteurs de test communs | TC-INTEROP-01 (CI) |
| INV-243-07 | KDOC → K_DOC + grep absence Kdoc | TC-LABEL-01, TC-LABEL-02 |
5. Mapping Critères → Tests¶
| TA | TC couvrant | Automatisable |
|---|---|---|
| TA-243-01 | TC-CRYPTO-01, TC-CRYPTO-03 | Oui |
| TA-243-02 | TC-LABEL-03 | Oui |
| TA-243-03 | TC-DET-01, TC-DET-02 | Oui |
| TA-243-04 | TC-ISO-01, TC-ISO-02 | Oui |
| TA-243-05 | TC-LABEL-01, TC-LABEL-02 | Oui |
| TA-243-06 | TC-REG-01, TC-REG-02, TC-REG-03 | Oui |
| TA-243-07 | TC-ARCH-01, TC-ARCH-02 | Partiel (grep) |
| TA-243-08 | TC-INTEROP-01 | Oui (CI) |
| TA-243-09 | TC-SEC-01 | Revue + test |
| TA-243-10 | Plan migration | Document |
6. Gestion des erreurs¶
| Erreur | Comportement | Test |
|---|---|---|
| kMaster null/undefined | Throw "K_master must be exactly 32 bytes" | TC-ERR-01 |
| kMaster.length !== 32 | Throw "K_master must be exactly 32 bytes" | TC-ERR-02 |
| Exception HKDF | Propagation + zeroization finally | TC-SEC-01 |
7. Contraintes techniques¶
7.1 Dépendances inter-PD¶
| Story | Statut | Dépendance |
|---|---|---|
| PD-34 | DONE_WITH_ANOMALY | Correction label K_doc |
| PD-97 | DONE | @noble/hashes installé |
| PD-33 | DONE | Pattern HKDF établi |
| PD-242 | DONE | Pattern try/finally zeroization |
7.2 Framework de test¶
- Runner : Jest (existant, ESM-compatible via ts-jest)
- Tests d'intégration : Non requis (crypto pure, pas de services externes)
- Variables CI : Aucune requise
7.3 Compatibilité ESM/CJS¶
- @noble/hashes : ESM-only mais Jest configuré pour le supporter (moduleResolution: node16)
- Hermes : Pure JS, pas de WASM
7.4 Production des vecteurs de test¶
Responsable : Claude (étape 6) Deadline : Avant TC-CRYPTO-03, TC-LABEL-03
Méthode : 1. Calculer avec Python hashlib + hkdf (référence externe) 2. Valider que @noble/hashes produit les mêmes résultats 3. Documenter les vecteurs dans PD-243-tests.md
Vecteur K_auth :
IKM: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f (32 bytes)
salt: (empty)
info: "ProbatioVault::K_auth::v1" → 50726f626174696f5661756c743a3a4b5f617574683a3a7631
output_length: 32
expected: [À CALCULER]
8. Plan de migration K_doc (Breaking Change)¶
8.1 Impact¶
La correction du label Kdoc → K_doc change la sortie HKDF. Tous les documents existants ont des K_doc calculées avec l'ancien label.
8.2 Stratégie¶
Phase 1 (PD-243) : Correction du code - Modifier HKDF_CONTEXTS.KDOC → CONTEXT_KDOC (depuis constants.ts) - Le nouveau label ProbatioVault::K_doc::v1 est utilisé
Phase 2 (Story future) : Migration des documents existants - Script de migration pour recalculer K_doc avec le nouveau label - Re-chiffrement des métadonnées si nécessaire
8.3 Tests de breaking change¶
TC-REG-03 vérifie explicitement que K_doc change : - Calculer K_doc avec ancien label → valeur A - Calculer K_doc avec nouveau label → valeur B - Assert A !== B
9. Sécurité¶
9.1 Zeroization pattern¶
export async function deriveAuthKey(kMaster: Uint8Array): Promise<Uint8Array> {
let derivedKey: Uint8Array | null = null;
try {
// Validation
if (kMaster?.length !== 32) {
throw new Error("K_master must be exactly 32 bytes");
}
const info = new TextEncoder().encode(CONTEXT_K_AUTH);
derivedKey = hkdf(sha3_256, kMaster, new Uint8Array(0), info, 32);
// Retourner une copie pour permettre zeroization du buffer local
return new Uint8Array(derivedKey);
} finally {
// Best-effort zeroization
if (derivedKey) {
derivedKey.fill(0);
}
}
}
Limitations documentées : - JavaScript strings immutables (label non zeroizable) - GC non contrôlable - Best-effort uniquement
9.2 Checklist sécurité¶
| Vérification | Statut |
|---|---|
| Pas de littéraux inline pour labels | TC-ARCH-01 |
| try/finally présent | TC-SEC-01 |
| fill(0) dans finally | TC-SEC-01 |
| Validation inputs | TC-ERR-01, TC-ERR-02 |
10. Hypothèses¶
| ID | Hypothèse | Impact si faux |
|---|---|---|
| HYP-243-01 | K_master disponible en Uint8Array 32 bytes | Adapter interface |
| HYP-243-02 | Backend accepte label K_auth format :: | Story backend séparée |
| HYP-243-03 | Vecteurs produits avant TC-INTEROP-01 | Bloquer tests interop |
11. Séquence d'implémentation¶
1. CONST-01, CONST-02 (constants.ts)
↓
2. HKDF-01, HKDF-02 (imports centralisés)
↓
3. HKDF-03 (correction K_doc label)
↓
4. HKDF-04 (deriveAuthKey)
↓
5. HKDF-05 (zeroization pattern)
↓
6. KEYS-01 (re-export)
↓
7. TEST-01..10 (tests unitaires)
↓
8. TC-LABEL-01 (grep absence Kdoc)
12. Definition of Done Plan¶
- Tous les invariants mappés à des mécanismes
- Tous les TA mappés à des TC
- Contraintes techniques documentées
- Plan de migration K_doc documenté
- Hypothèses explicitées
- Code contracts produits (ci-dessous)
13. Vigilance¶
| Point | Risque | Mitigation |
|---|---|---|
| Breaking change K_doc | Incompatibilité documents existants | Migration planifiée (phase 2) |
| Vecteurs non calculés | Tests non exécutables | Produire avant tests |
| ESM compatibility | Jest failures | Confirmer config existante |