ihm_client/docs/INITIALIZATION_FLOW.md
NicolasCantu a52baf07e0 docs: fix inconsistencies in documentation files
**Motivations :**
- La documentation doit refléter l'état actuel du code
- Corriger les incohérences entre le code et la documentation
- Ajouter les messages manquants dans INTEGRATION.md

**Modifications :**
- CODE_ANALYSIS_REPORT.md : Mise à jour de la taille de service.ts (2275 -> 3265 lignes)
- CODE_ANALYSIS_REPORT.md : Correction de la description du cache (désactivé au lieu de non limité)
- PAIRING_SYSTEM_ANALYSIS.md : Mise à jour des recommandations pour refléter l'état actuel (corrections implémentées)
- INTEGRATION.md : Ajout des messages manquants (TEST_RESPONSE, LISTENING)

**Pages affectées :**
- CODE_ANALYSIS_REPORT.md
- docs/PAIRING_SYSTEM_ANALYSIS.md
- INTEGRATION.md
2025-10-29 13:41:38 +01:00

23 KiB

Documentation de l'Initialisation LeCoffre.io

Vue d'ensemble

Le système LeCoffre.io suit un processus d'initialisation en plusieurs étapes pour créer et sécuriser un wallet Bitcoin. Ce document détaille chaque étape du processus, depuis le choix du mode de sécurité jusqu'au pairing réussi et à la récupération des processus.

Architecture des Stores IndexedDB

Stores utilisés :

  • pbkdf2keys : Stockage des clés PBKDF2 chiffrées par mode de sécurité
  • wallet : Stockage du wallet chiffré (device + wallet data)
  • credentials : Stockage des credentials de pairing (utilisé uniquement après pairing)
  • processes : Stockage des processus de communication
  • labels : Stockage des labels de transactions
  • shared_secrets : Stockage des secrets partagés
  • unconfirmed_secrets : Stockage des secrets non confirmés
  • diffs : Stockage des différences de synchronisation
  • data : Stockage des données générales

Flux d'Initialisation Complet

1. Démarrage de l'Application

Fichier : src/router.tscheckStorageStateAndNavigate()

L'application vérifie l'état du storage pour déterminer l'étape suivante :

// Logique de progression :
// - Si pairing → account
// - Si date anniversaire → pairing
// - Si wallet → birthday-setup
// - Si pbkdf2 → wallet-setup
// - Sinon → security-setup

États possibles :

  1. Appareil appairé → Redirection vers account
  2. Date anniversaire configurée → Redirection vers home (pairing)
  3. Wallet existe sans date anniversaire → Redirection vers birthday-setup
  4. Clé PBKDF2 existe → Redirection vers wallet-setup
  5. Aucune configuration → Redirection vers security-setup

2. Configuration du Mode de Sécurité

Fichier : src/pages/security-setup/security-setup.ts

2.1 Sélection du Mode

L'utilisateur choisit parmi les modes disponibles :

Mode Nom Description Niveau de Sécurité Clé de Chiffrement PBKDF2 Stockage de la Clé de Chiffrement
proton-pass Proton Pass Authentification biométrique via Proton Pass High Clé WebAuthn générée par le navigateur Stockée dans le navigateur (WebAuthn credential)
os Authentificateur OS Authentification biométrique du système High Clé WebAuthn générée par le système Stockée dans le système d'exploitation
otp OTP Code à usage unique (Google Authenticator, etc.) High Aucune (clé PBKDF2 stockée en clair) Secret OTP stocké dans l'application OTP
password Mot de passe Chiffrement par mot de passe (non sauvegardé) Low Mot de passe utilisateur Stocké dans le gestionnaire de mots de passe du navigateur
none Aucune sécurité Chiffrement avec clé en dur (non recommandé) Critical Clé en dur 4NK_DEFAULT_ENCRYPTION_KEY_NOT_SECURE Intégrée dans le code (non sécurisé)

2.2 Génération de la Clé PBKDF2

Fichier : src/services/secure-credentials.service.tsgeneratePBKDF2Key()

Pour chaque mode, une clé PBKDF2 est générée et stockée différemment :

Mode proton-pass et os (WebAuthn)
// Stockage avec WebAuthn (authentification biométrique)
await webAuthnService.storeKeyWithWebAuthn(pbkdf2Key, securityMode);
  • Store : pbkdf2keys
  • Clé : security_mode (ex: "proton-pass")
  • Valeur : Clé PBKDF2 chiffrée avec WebAuthn
  • Authentification : Biométrique (empreinte, visage, etc.)
Mode otp
// Génération du secret OTP
const otpSecret = await this.generateOTPSecret();
// Stockage de la clé PBKDF2 en clair
await this.storePBKDF2KeyInStore(pbkdf2Key, securityMode);
// Affichage du QR code
this.displayOTPQRCode(otpSecret);
  • Store : pbkdf2keys
  • Clé : security_mode ("otp")
  • Valeur : Clé PBKDF2 en clair
  • Authentification : Code OTP généré par l'application
Mode password
// Demande du mot de passe utilisateur
const userPassword = await this.promptForPasswordWithBrowser();
// Chiffrement de la clé PBKDF2
const encryptedKey = await encryptionService.encrypt(pbkdf2Key, userPassword);
// Stockage chiffré
await this.storePBKDF2KeyInStore(encryptedKey, securityMode);
  • Store : pbkdf2keys
  • Clé : security_mode ("password")
  • Valeur : Clé PBKDF2 chiffrée avec le mot de passe utilisateur
  • Authentification : Mot de passe utilisateur (non sauvegardé)
Mode none
// Clé de chiffrement en dur
const hardcodedKey = '4NK_DEFAULT_ENCRYPTION_KEY_NOT_SECURE';
// Chiffrement avec la clé en dur
const encryptedKeyNone = await encryptionService.encrypt(pbkdf2Key, hardcodedKey);
// Stockage chiffré
await this.storePBKDF2KeyInStore(encryptedKeyNone, securityMode);
  • Store : pbkdf2keys
  • Clé : security_mode ("none")
  • Valeur : Clé PBKDF2 chiffrée avec clé en dur
  • Authentification : Aucune (non sécurisé)

3. Création du Wallet

Fichier : src/pages/wallet-setup/wallet-setup.ts

3.1 Récupération de la Clé PBKDF2

Le système teste tous les modes de sécurité pour trouver la clé PBKDF2 valide :

const allSecurityModes = ['none', 'otp', 'password', 'os', 'proton-pass'];
for (const mode of allSecurityModes) {
    const hasKey = await secureCredentialsService.hasPBKDF2Key(mode);
    if (hasKey) {
        const key = await secureCredentialsService.retrievePBKDF2Key(mode);
        if (key) {
            currentMode = mode;
            break;
        }
    }
}

3.2 Génération des Clés du Wallet

// Génération des clés temporaires
const walletData = {
    scan_sk: encryptionService.generateRandomKey(),
    spend_key: encryptionService.generateRandomKey(),
    network: 'signet',
    state: 'birthday_waiting',
    created_at: new Date().toISOString()
};

3.3 Création du Device via SDK

// Création du device avec birthday = 0
const spAddress = await services.sdkClient.create_new_device(0, 'signet');
// Génération forcée du wallet
const wallet = await services.sdkClient.dump_wallet();

3.4 Stockage Chiffré du Wallet

// Chiffrement du device
const encryptedDevice = await encryptionService.encrypt(deviceString, pbkdf2Key);
// Chiffrement du wallet
const encryptedWallet = await encryptionService.encrypt(walletString, pbkdf2Key);

// Stockage dans le store wallet
const walletObject = {
    pre_id: '1',
    encrypted_device: encryptedDevice,
    encrypted_wallet: encryptedWallet
};

Store : wallet Clé : pre_id ("1") Valeur : Objet contenant uniquement des données chiffrées

4. Configuration de la Date Anniversaire

Fichier : src/pages/birthday-setup/birthday-setup.ts

4.1 Vérification des Prérequis

Avant de procéder, la page vérifie que tous les prérequis sont remplis :

// Vérification de la clé PBKDF2 dans le store pbkdf2keys
const securityModes = ['none', 'otp', 'password', 'os', 'proton-pass'];
let pbkdf2KeyFound = false;
for (const mode of securityModes) {
    const hasKey = await secureCredentials.hasPBKDF2Key(mode);
    if (hasKey) {
        const key = await secureCredentials.retrievePBKDF2Key(mode);
        if (key) {
            pbkdf2KeyFound = true;
            break;
        }
    }
}

// Vérification du wallet en base de données (avec retry pour synchronisation)
let wallet = await services.getDeviceFromDatabase();
if (!wallet) {
    // Retry jusqu'à 5 tentatives avec délai de 500ms
    for (let attempt = 0; attempt < 5; attempt++) {
        await new Promise(resolve => setTimeout(resolve, 500));
        wallet = await services.getDeviceFromDatabase();
        if (wallet) break;
    }
}

// Vérification que le wallet contient les données attendues
if (wallet.sp_wallet && wallet.sp_wallet.birthday !== undefined) {
    console.log('✅ Wallet found in database with birthday:', wallet.sp_wallet.birthday);
} else {
    throw new Error('Wallet found but missing required data');
}

Points importants :

  • Vérification réelle de la présence de la clé PBKDF2 dans le store pbkdf2keys
  • Vérification réelle du wallet en base avec retry pour gérer les problèmes de synchronisation
  • Validation que le wallet contient bien les données attendues (sp_wallet, birthday)

4.2 Connexion aux Relais

// Connexion aux relais Bitcoin
await services.connectAllRelays();

// Vérification que les relais sont connectés en vérifiant chain_tip
const currentBlockHeight = services.getCurrentBlockHeight();
if (currentBlockHeight !== -1) {
    console.log('✅ Relays connected successfully, chain_tip:', currentBlockHeight);
} else {
    throw new Error('Relays connected but chain_tip not set');
}

// Vérification que le handshake a été reçu
if (currentBlockHeight > 0) {
    console.log('✅ Communication handshake completed, chain_tip:', currentBlockHeight);
} else {
    throw new Error('Handshake not received or chain_tip not set');
}

Vérifications réelles :

  • Vérification que chain_tip est défini (valeur != -1)
  • Vérification que chain_tip est positif (indique que le handshake a été reçu)

4.3 Mise à Jour de la Date Anniversaire

Fichier : src/services/service.tsupdateDeviceBlockHeight()

// Mise à jour du birthday du device
await services.updateDeviceBlockHeight();

Processus interne avec vérifications réelles :

  1. Restauration en mémoire :
this.sdkClient.restore_device(device);
// Vérification que le device a été restauré en mémoire
const restoredDevice = this.dumpDeviceFromMemory();
if (restoredDevice?.sp_wallet?.birthday === device.sp_wallet.birthday) {
    console.log('✅ Device restored in memory with updated birthday:', device.sp_wallet.birthday);
} else {
    throw new Error('Device restoration failed');
}
  1. Sauvegarde en base de données :
await this.saveDeviceInDatabase(device);
// Vérification que le device a été sauvegardé en base de données
const savedDevice = await this.getDeviceFromDatabase();
if (savedDevice?.sp_wallet?.birthday === device.sp_wallet.birthday) {
    console.log('✅ Device saved to database with updated birthday:', device.sp_wallet.birthday);
} else {
    throw new Error('Device save verification failed');
}
  1. Vérification du scan initial :
await this.sdkClient.scan_blocks(this.currentBlockHeight, BLINDBITURL);
// Vérification que le scan est terminé en vérifiant last_scan
const deviceAfterScan = this.dumpDeviceFromMemory();
if (deviceAfterScan?.sp_wallet?.last_scan === this.currentBlockHeight) {
    console.log('✅ Initial scan completed for new wallet');
} else {
    console.warn('⚠️ Initial scan may not be complete');
}
  1. Vérification finale :
// Sauvegarde finale avec last_scan mis à jour
await this.saveDeviceInDatabase(device);
// Vérification que le device a été sauvegardé avec last_scan mis à jour
const finalDevice = await this.getDeviceFromDatabase();
if (finalDevice?.sp_wallet?.last_scan === this.currentBlockHeight) {
    console.log('✅ New wallet initial scan completed and saved');
} else {
    throw new Error('Final save verification failed');
}

Vérification dans birthday-setup.ts :

// Vérifier que le birthday a bien été mis à jour en récupérant le wallet depuis la base
const updatedWallet = await services.getDeviceFromDatabase();
if (updatedWallet?.sp_wallet?.birthday && updatedWallet.sp_wallet.birthday > 0) {
    console.log('✅ Birthday updated successfully:', updatedWallet.sp_wallet.birthday);
} else {
    throw new Error('Birthday update verification failed');
}

4.4 Sauvegarde du Device avec Vérification

Fichier : src/services/service.tssaveDeviceInDatabase()

La méthode saveDeviceInDatabase() effectue maintenant une vérification réelle après la sauvegarde :

// Sauvegarde du wallet chiffré
const putRequest = store.put(walletObject);
putRequest.onsuccess = () => {
    console.log('✅ Device saved to database with encryption');
    // La vérification se fera dans transaction.oncomplete
};

transaction.oncomplete = async () => {
    // Vérifier que le wallet a bien été sauvegardé en le récupérant depuis la base
    const verificationDb = await new Promise<IDBDatabase>((resolve, reject) => {
        const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
        request.onsuccess = () => resolve(request.result);
        request.onerror = () => reject(request.error);
    });

    const verificationTx = verificationDb.transaction([walletStore], 'readonly');
    const verifyRequest = verificationStore.get('1');

    verifyRequest.onsuccess = () => {
        const savedData = verifyRequest.result;
        if (savedData && savedData.encrypted_device === encryptedDevice) {
            console.log('✅ Verified: Device correctly saved in database');
            resolve();
        } else {
            throw new Error('Device save verification failed');
        }
    };
};

Points importants :

  • Vérification réelle après la transaction pour confirmer que les données sont bien sauvegardées
  • Comparaison de encrypted_device pour s'assurer que les données sont correctes
  • Logs de succès uniquement après vérification réelle

4.5 Redirection vers la Synchronisation des Blocs

Après la mise à jour réussie du birthday, l'application redirige vers la page de synchronisation des blocs :

// Redirection vers la page de synchronisation des blocs
window.location.href = '/src/pages/block-sync/block-sync.html';

Page : src/pages/block-sync/block-sync.html

  • Interface dédiée pour la synchronisation des blocs
  • Affichage de la progression de la synchronisation
  • Gestion de la synchronisation initiale du wallet avec le réseau Bitcoin

5. Processus de Pairing

Fichier : src/pages/home/home.tshandleMainPairing()

5.1 Vérification du Mode de Sécurité

const currentMode = await securityModeService.getCurrentMode();
if (!currentMode) {
    // Afficher le sélecteur de mode de sécurité
    await showSecurityModeSelector();
    return;
}

5.2 Authentification selon le Mode

Mode proton-pass et os
// Authentification WebAuthn
const credential = await navigator.credentials.get({
    publicKey: {
        challenge: new Uint8Array(32),
        allowCredentials: [{
            id: credentialId,
            type: 'public-key'
        }]
    }
});
Mode otp
// Demande du code OTP
const otpCode = await this.promptForOTPCode();
// Vérification du code OTP
const isValid = await this.verifyOTPCode(otpCode, otpSecret);
Mode password
// Demande du mot de passe
const password = await this.promptForPassword();
// Déchiffrement de la clé PBKDF2
const pbkdf2Key = await encryptionService.decrypt(encryptedKey, password);
Mode none
// Déchiffrement avec la clé en dur
const pbkdf2Key = await encryptionService.decrypt(encryptedKey, hardcodedKey);

5.3 Génération des Credentials de Pairing

// Génération des credentials sécurisés
const credentialData = await secureCredentialsService.generateSecureCredentials('4nk-secure-password');

// Stockage des credentials dans le store credentials
await secureCredentialsService.storeCredentials(credentialData, '');

// Récupération et déchiffrement des credentials
const retrievedCredentials = await secureCredentialsService.retrieveCredentials('');

5.4 Création du Processus de Pairing

// Création du processus via le SDK
const pairingResult = await services.createPairingProcess({
    spendKey: retrievedCredentials.spendKey,
    scanKey: retrievedCredentials.scanKey
});

6. Récupération des Processus

Fichier : src/services/service.tsrestoreProcessesFromDB()

6.1 Synchronisation des Processus

// Récupération des processus depuis la base de données
const processes = await processRepo.getAllProcesses();

// Synchronisation avec le réseau
for (const process of processes) {
    await services.syncProcess(process);
}

6.2 Mise à Jour de l'État de Pairing

// Vérification de l'état de pairing
const isPaired = services.isPaired();
if (isPaired) {
    // Redirection vers la page account
    await navigate('account');
}

Diagramme de Flux

graph TD
    A[Démarrage Application] --> B{Vérification État Storage}
    B -->|Aucune config| C[Security Setup]
    B -->|PBKDF2 existe| D[Wallet Setup]
    B -->|Wallet existe| E[Birthday Setup]
    B -->|Birthday configuré| F[Block Sync]
    B -->|Appareil appairé| G[Account]

    C --> C1[Sélection Mode Sécurité]
    C1 --> C2[Génération Clé PBKDF2]
    C2 --> C3[Stockage selon Mode]
    C3 --> D

    D --> D1[Récupération Clé PBKDF2]
    D1 --> D2[Création Device SDK]
    D2 --> D3[Génération Wallet]
    D3 --> D4[Stockage Chiffré avec Vérification]
    D4 --> E

    E --> E1[Vérification Prérequis]
    E1 --> E2{Prérequis OK?}
    E2 -->|Non| E3[Redirection vers Setup Précédent]
    E2 -->|Oui| E4[Connexion Relais]
    E4 --> E5[Vérification Handshake]
    E5 --> E6[Mise à Jour Birthday]
    E6 --> E7[Vérification Sauvegarde]
    E7 --> E8[Vérification Birthday]
    E8 --> F

    F --> F1[Synchronisation Blocs]
    F1 --> F2[Initialisation Services]
    F2 --> F3[Scan des Blocs]
    F3 --> F4[Mise à Jour last_scan]
    F4 --> G

    G --> G1[Pairing]
    G1 --> G2[Authentification Mode]
    G2 --> G3[Génération Credentials]
    G3 --> G4[Création Processus Pairing]
    G4 --> G5[Récupération Processus]

Sécurité par Mode

Mode proton-pass et os

  • Stockage : Clé PBKDF2 chiffrée avec WebAuthn
  • Authentification : Biométrique (empreinte, visage)
  • Sécurité : Élevée (clé matérielle)

Mode otp

  • Stockage : Clé PBKDF2 en clair
  • Authentification : Code OTP temporaire
  • Sécurité : Élevée (authentification à deux facteurs)

Mode password

  • Stockage : Clé PBKDF2 chiffrée avec mot de passe utilisateur
  • Authentification : Mot de passe utilisateur
  • Sécurité : Faible (dépend de la force du mot de passe)

Mode none

  • Stockage : Clé PBKDF2 chiffrée avec clé en dur
  • Authentification : Aucune
  • Sécurité : Critique (non recommandé)

Gestion des Erreurs

Erreurs de Chiffrement

  • Clé PBKDF2 introuvable → Redirection vers security-setup
  • Échec de déchiffrement → Demande de réauthentification
  • Wallet corrompu → Recréation du wallet

Erreurs de Réseau

  • Connexion relais échouée → Retry automatique
  • Synchronisation échouée → Mode hors ligne
  • Pairing échoué → Nouvelle tentative

Erreurs d'Authentification

  • WebAuthn échoué → Fallback vers autre mode
  • OTP invalide → Nouvelle demande
  • Mot de passe incorrect → Nouvelle tentative

Erreurs de Vérification

  • Vérification des prérequis échouée → Redirection vers l'étape appropriée
  • Vérification de sauvegarde échouée → Retry de la sauvegarde avec logs détaillés
  • Vérification de restauration échouée → Retry de la restauration avec logs détaillés
  • Vérification de handshake échouée → Retry de la connexion avec logs détaillés

Système de Vérification Réelle des Logs

Tous les logs de succès sont maintenant émis uniquement après vérification réelle des résultats. Cela permet de :

  1. Détecter les échecs silencieux : Les opérations qui échouent sans erreur sont détectées par les vérifications
  2. Avoir des logs fiables : Les logs reflètent la réalité et non juste des déclarations
  3. Faciliter le diagnostic : Les logs indiquent précisément où et pourquoi un processus échoue

Vérifications Implémentées

Dans birthday-setup.ts

  • Vérification réelle de la présence de la clé PBKDF2 dans le store pbkdf2keys
  • Vérification réelle du wallet en base avec retry pour gérer les problèmes de synchronisation
  • Validation que le wallet contient bien les données attendues (sp_wallet, birthday)
  • Vérification que les relais sont connectés en vérifiant chain_tip
  • Vérification que le handshake a été reçu (chain_tip > 0)
  • Vérification que le birthday a bien été mis à jour en récupérant le wallet depuis la base

Dans updateDeviceBlockHeight()

  • Vérification que le device est restauré en mémoire en comparant le birthday
  • Vérification que le device est sauvegardé en base en le récupérant après l'opération
  • Vérification que le scan est terminé en vérifiant last_scan
  • Vérification que la sauvegarde finale est réussie

Dans saveDeviceInDatabase()

  • Vérification que le wallet est bien sauvegardé en le récupérant depuis la base après la transaction
  • Comparaison de encrypted_device pour confirmer que les données sont correctes
  • Logs de succès uniquement après vérification réelle

Avantages

  • Fiabilité : Les logs reflètent la réalité et non juste des déclarations
  • Diagnostic : Facilite le diagnostic en cas de problème
  • Détection : Détecte les échecs silencieux qui passeraient inaperçus
  • Traçabilité : Chaque étape est vérifiée et tracée avec des logs détaillés

Points d'Attention

  1. Ordre des modes testés : ['none', 'otp', 'password', 'os', 'proton-pass']
  2. Store credentials : Utilisé uniquement après pairing
  3. Clé PBKDF2 : Toujours stockée dans pbkdf2keys avec security_mode comme clé
  4. Wallet : Toujours stocké chiffré dans le store wallet
  5. Redirection automatique : 3 secondes après création du wallet vers birthday-setup
  6. Vérifications réelles : Tous les logs de succès sont émis uniquement après vérification réelle des résultats
  7. Block Sync : Nouvelle page intermédiaire entre birthday-setup et pairing pour la synchronisation des blocs
  8. Prérequis : Chaque page vérifie ses prérequis en base de données avant de procéder
  9. Synchronisation IndexedDB : Utilisation directe d'IndexedDB pour éviter les problèmes de synchronisation avec le service worker
  10. Retry automatique : Retry automatique jusqu'à 5 tentatives avec délai de 500ms pour les vérifications de wallet en base

Cette documentation couvre l'ensemble du processus d'initialisation du système LeCoffre.io, depuis la configuration de sécurité jusqu'au pairing réussi et à la récupération des processus.