PD-242 — Plan d'implémentation¶
Story : Enveloppe cryptographique de récupération (K_recovery) Date : 2026-02-19 Complexité : Medium Estimé : 8 tâches, ~12 fichiers
Analyse de l'existant¶
Dépendances résolues (DONE)¶
| Story | Module | Exports utilisables |
|---|---|---|
| PD-97 | /src/services/crypto.ts | computeFileHash() (SHA3-256) |
| PD-97 | /src/services/hkdf.ts | hkdfExtract(), hkdfExpand(), domain separation |
| PD-97 | /src/crypto/aes-gcm.ts | encrypt(), decrypt() (AES-256-GCM) |
| PD-98 | /src/services/keychainStorage.ts | storeMasterKey(), getMasterKey() |
| PD-97 | /src/crypto/utils.ts | bytesToHex(), hexToBytes(), bytesToBase64() |
| PD-97 | /src/crypto/zeroize.ts | zeroize() |
Dépendances à ajouter¶
| Package | Version | Usage |
|---|---|---|
@noble/bip39 | ^2.0.0 | Génération mnemonic 24 mots, validation, seed derivation |
Justification : @noble/bip39 est du même auteur que @noble/hashes et @noble/ciphers déjà utilisés. Audit complet, pure JavaScript, compatible Hermes.
Architecture cible¶
src/
├── crypto/
│ ├── bip39.ts # NEW — Wrapper BIP-39 avec types stricts
│ └── constants.ts # UPDATE — Ajouter constantes recovery
├── services/
│ └── recoveryService.ts # NEW — Service principal K_recovery
├── screens/
│ └── recovery/
│ ├── RecoveryCreateScreen.tsx # NEW — Flux onboarding
│ ├── RecoveryRestoreScreen.tsx # NEW — Flux nouveau device
│ └── RecoveryRegenerateScreen.tsx # NEW — Flux régénération
├── components/
│ └── recovery/
│ ├── MnemonicDisplay.tsx # NEW — Affichage phrase avec timeout
│ ├── MnemonicInput.tsx # NEW — Saisie 24 mots avec validation
│ └── RecoveryProgress.tsx # NEW — Indicateur progression
└── __tests__/
├── services/
│ └── recoveryService.test.ts # NEW — Tests unitaires + vecteurs
└── crypto/
└── bip39.test.ts # NEW — Vecteurs BIP-39 officiels
Contraintes techniques¶
Dépendances inter-PD¶
| Story | Statut | Nature |
|---|---|---|
| PD-97 | DONE | HKDF-SHA3-256, AES-256-GCM, SHA3-256 |
| PD-98 | DONE | Keychain storage K_master |
Framework de test¶
- Runner : Jest (existant)
- Tests d'intégration : Mocks SecureStore, pas de services réels
- Vecteurs : BIP-39 officiels (TREZOR) dans
__tests__/fixtures/
Compatibilité ESM/CJS¶
@noble/bip39est ESM-only mais compatible Hermes via Metro bundler- Pas de WebAssembly requis (pure JavaScript)
Tâches¶
TASK-1 — Ajout dépendance @noble/bip39¶
Fichiers : - package.json
Actions : 1. npm install @noble/bip39 2. Vérifier compatibilité Metro bundler 3. Test import basique
Contrat : CC-01
TASK-2 — Module BIP-39 wrapper¶
Fichiers : - src/crypto/bip39.ts (CREATE) - src/crypto/constants.ts (UPDATE)
Interface :
// bip39.ts
export function generateMnemonic(): string[];
export function validateMnemonic(words: string[]): boolean;
export function mnemonicToSeed(words: string[], passphrase?: string): Uint8Array;
export function getWordlist(): readonly string[];
Contrat : CC-02
TASK-3 — RecoveryService principal¶
Fichiers : - src/services/recoveryService.ts (CREATE)
Interface :
export interface RecoveryEnvelope {
envelope: string; // base64
nonce: string; // base64
tag: string; // base64
}
export function deriveRecoveryKey(mnemonic: string[], userId: string): Uint8Array;
export function createRecoveryEnvelope(kRecovery: Uint8Array, kMaster: Uint8Array): RecoveryEnvelope;
export function restoreFromEnvelope(kRecovery: Uint8Array, envelope: RecoveryEnvelope): Uint8Array;
export function computeVerificationHash(kRecovery: Uint8Array, userId: string): Uint8Array;
export function zeroizeRecoveryKey(kRecovery: Uint8Array): void;
Contrat : CC-03
TASK-4 — Composants UI Recovery¶
Fichiers : - src/components/recovery/MnemonicDisplay.tsx (CREATE) - src/components/recovery/MnemonicInput.tsx (CREATE) - src/components/recovery/RecoveryProgress.tsx (CREATE)
Propriétés MnemonicDisplay : - words: string[] - timeoutSeconds: number (default 120) - onTimeout: () => void - secureDisplay: boolean (protection screenshot)
Propriétés MnemonicInput : - onComplete: (words: string[]) => void - onError: (error: string) => void - expectedWordCount: 24
Contrat : CC-04
TASK-5 — Écran RecoveryCreateScreen (onboarding)¶
Fichiers : - src/screens/recovery/RecoveryCreateScreen.tsx (CREATE) - src/navigation/types.ts (UPDATE)
Flux : 1. Générer phrase → afficher MnemonicDisplay 2. Timeout 120s ou "Continuer" → masquer phrase 3. Re-saisie complète 24 mots via MnemonicInput 4. Validation → dériver K_recovery → créer envelope 5. Envoyer envelope + H_verify au backend 6. Zeroize K_recovery → navigation vers home
Contrat : CC-05
TASK-6 — Écran RecoveryRestoreScreen (nouveau device)¶
Fichiers : - src/screens/recovery/RecoveryRestoreScreen.tsx (CREATE)
Flux : 1. Détection absence K_master (automatique) 2. Saisie 24 mots via MnemonicInput 3. Dériver K_recovery → calculer H_verify_candidate 4. GET /recovery/h_verify/:user_id → comparer 5. Si match : GET /recovery/envelope → déchiffrer 6. Stocker K_master dans Keychain 7. Zeroize K_recovery → navigation vers home
Contrat : CC-06
TASK-7 — Écran RecoveryRegenerateScreen¶
Fichiers : - src/screens/recovery/RecoveryRegenerateScreen.tsx (CREATE)
Flux : 1. Authentification forte (biométrie + password via useReauth) 2. DELETE /recovery/envelope/:user_id (invalider ancienne) 3. Générer nouvelle phrase → afficher 4. Re-saisie complète 5. Créer nouvelle envelope → POST /recovery/envelope 6. Zeroize → confirmation succès
Contrat : CC-07
TASK-8 — Tests unitaires et E2E¶
Fichiers : - src/__tests__/crypto/bip39.test.ts (CREATE) - src/__tests__/services/recoveryService.test.ts (CREATE) - src/__tests__/e2e/recovery.test.ts (CREATE)
Couverture : - Vecteurs BIP-39 officiels (TREZOR) - Round-trip envelope create/restore - Rejet phrase incorrecte - Timeout affichage - Rate limiting mock
Contrat : CC-08
Matrice de couverture INV/CA → Tâche → Test¶
| Invariant/CA | Tâche couvrant | Test couvrant |
|---|---|---|
| INV-242-01 (K_recovery jamais réseau) | TASK-3 | TC-SEC-01, TC-SEC-02 |
| INV-242-02 (phrase jamais stockée) | TASK-3, TASK-5 | TC-SEC-03, TC-SEC-04 |
| INV-242-03 (zeroize K_recovery) | TASK-3 | TC-SEC-05 |
| INV-242-04 (backend cannot decrypt) | TASK-3 | TC-SEC-06 |
| INV-242-05 (256 bits entropy) | TASK-2 | TC-BIP-01, TC-BIP-02 |
| INV-242-06 (HKDF-SHA3-256 domain) | TASK-3 | TC-CRYPTO-01, TC-CRYPTO-02, TC-CRYPTO-03 |
| INV-242-07 (AES-256-GCM AEAD) | TASK-3 | TC-CRYPTO-04, TC-CRYPTO-05 |
| INV-242-08 (H_verify sans exposer K) | TASK-3 | TC-VERIFY-01, TC-VERIFY-02 |
| INV-242-09 (confirmation 24 mots) | TASK-5 | TC-UX-01, TC-UX-02 |
| INV-242-10 (detection nouveau device) | TASK-6 | TC-ARCH-01 |
| INV-242-11 (protection screenshot) | TASK-4 | TC-SEC-07 |
| INV-242-12 (timeout 120s) | TASK-4 | TC-SEC-08 |
| INV-242-13 (rate limiting) | TASK-6 | TC-SEC-09, TC-SEC-10 |
| CA-1 (TA-1 validité BIP-39) | TASK-2 | TC-BIP-01, TC-BIP-02, TC-BIP-03 |
| CA-2 (TA-2 déterminisme) | TASK-3 | TC-CRYPTO-02 |
| CA-3 (TA-3 round-trip) | TASK-3 | TC-CRYPTO-04, TC-CRYPTO-05 |
| CA-4 (TA-4 rejet phrase incorrecte) | TASK-3 | TC-NEG-01, TC-NEG-02 |
| CA-5 (TA-5 vérification pré-déchiffrement) | TASK-3 | TC-VERIFY-01, TC-VERIFY-02 |
| CA-6 (TA-6 absence logs) | TASK-3 | TC-LOG-01 |
| CA-7 (TA-7 re-saisie obligatoire) | TASK-5 | TC-UX-01, TC-UX-02 |
| CA-8 (TA-8 flux E2E) | TASK-6 | TC-E2E-01, TC-E2E-02 |
| CA-9 (TA-9 invalidation ancienne) | TASK-7 | TC-E2E-REGEN |
| CA-10 (TA-10 performance) | TASK-3 | TC-PERF-01, TC-PERF-02 |
| CA-11 (TA-11 screenshot) | TASK-4 | TC-SEC-07 |
| CA-12 (TA-12 timeout) | TASK-4 | TC-SEC-08 |
| CA-13 (TA-13 rate limiting) | TASK-6 | TC-SEC-09, TC-SEC-10 |
| CA-14 (TA-14 vecteurs BIP-39) | TASK-2 | TC-BIP-03 |
| CA-15 (TA-15 portabilité) | TASK-3 | TC-COMPAT-01 |
| CA-16 (TA-16 observabilité) | TASK-5, TASK-6, TASK-7 | TC-API-01, TC-API-02, TC-API-03 |
Code Contracts¶
Voir fichier associé : PD-242-code-contracts.yaml
Séquence d'implémentation¶
gantt
title PD-242 Séquence
dateFormat YYYY-MM-DD
section Setup
TASK-1 Dépendance :t1, 2026-02-20, 1h
TASK-2 BIP-39 wrapper :t2, after t1, 2h
section Core
TASK-3 RecoveryService :t3, after t2, 4h
section UI
TASK-4 Composants :t4, after t2, 3h
TASK-5 CreateScreen :t5, after t3 t4, 3h
TASK-6 RestoreScreen :t6, after t5, 2h
TASK-7 RegenerateScreen :t7, after t6, 2h
section Tests
TASK-8 Tests :t8, after t7, 3h Chemin critique : TASK-1 → TASK-2 → TASK-3 → TASK-5 → TASK-6 → TASK-7 → TASK-8
Risques et mitigations¶
| Risque | Probabilité | Impact | Mitigation |
|---|---|---|---|
| @noble/bip39 incompatible Hermes | Faible | Bloquant | Fallback vers bip39 package classique |
| Performance PBKDF2 2048 iter trop lent | Faible | Mineur | Async avec indicateur loading |
| Protection screenshot iOS insuffisante | Moyenne | Majeur | Combiner FLAG_SECURE + overlay |
| Zeroize inefficace (GC JS) | Connue | Accepté | Best-effort documenté, risque résiduel accepté |
Définition of Done (pré-Gate 8)¶
- Tous les tests TC-* passent
- Vecteurs BIP-39 officiels validés
- Coverage > 80% sur recoveryService.ts
- Aucun log de K_recovery (audit console)
- Performance < 3s sur iPhone 12+
- Protection screenshot vérifiée
- Timeout 120s fonctionnel
- Pipeline CI vert
- Revue code approuvée