Aller au contenu

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/bip39 est 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