Aller au contenu

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 :

// Ajouter après CONTEXT_KDOC (ligne 26)
export const CONTEXT_K_AUTH = "ProbatioVault::K_auth::v1";

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 KdocK_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