**Motivations:** • Simplify wallet key extraction logic using restoreDevice API • Improve error handling and logging throughout the application • Remove unused scanner.js file • Enhance secure credential management service • Standardize logging format for better debugging • Fix storage service error handling **Modifications:** • Updated wallet-setup page to use proper wallet key extraction • Refactored secure-credentials service with better error handling • Improved storage service with proper error logging • Enhanced validation modal and device management components • Updated error utilities and logger with standardized formats • Removed deprecated scanner.js file • Fixed birthday-setup and block-sync page error handling • Updated security-setup page with improved error messages • Enhanced websocket manager error handling • Improved prerequisite utils and subscription utils • Fixed sp-address utils error handling • Updated encoder worker with better error logging **Pages affected:** • device-management component • validation-modal component • birthday-setup page • block-sync page • pairing page • security-setup page • wallet-setup page • router • All service files (secure-credentials, secure-key-manager, service, storage, token, websocket-manager) • All utility files (errors, logger, prerequisites, sp-address, subscription) • encoder worker
540 lines
30 KiB
TypeScript
540 lines
30 KiB
TypeScript
/**
|
|
* Page de génération du wallet
|
|
* Deuxième étape du processus d'initialisation
|
|
*/
|
|
|
|
import { DATABASE_CONFIG } from '../../services/database-config';
|
|
import { secureLogger } from '../services/secure-logger';
|
|
import { checkPBKDF2Key } from '../../utils/prerequisites.utils';
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
secureLogger.info('💰 Wallet setup page loaded', { component: 'WalletSetup' });
|
|
|
|
const status = document.getElementById('status') as HTMLDivElement;
|
|
const progressBar = document.getElementById('progressBar') as HTMLDivElement;
|
|
const continueBtn = document.getElementById('continueBtn') as HTMLButtonElement;
|
|
|
|
function updateStatus(message: string, type: 'loading' | 'success' | 'error') {
|
|
status.textContent = message;
|
|
status.className = `status ${type}`;
|
|
}
|
|
|
|
function updateProgress(percent: number) {
|
|
progressBar.style.width = `${percent}%`;
|
|
}
|
|
|
|
// Méthode pour sauvegarder directement en IndexedDB dans la base 4nk
|
|
async function saveCredentialsDirectly(credentials: any): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
|
|
|
|
request.onerror = () => reject(request.error);
|
|
request.onsuccess = () => {
|
|
const db = request.result;
|
|
const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readwrite');
|
|
const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name);
|
|
|
|
const putRequest = store.put(credentials, '/4nk/credentials');
|
|
putRequest.onsuccess = () => resolve();
|
|
putRequest.onerror = () => reject(putRequest.error);
|
|
};
|
|
|
|
request.onupgradeneeded = () => {
|
|
const db = request.result;
|
|
if (!db.objectStoreNames.contains(DATABASE_CONFIG.stores.wallet.name)) {
|
|
db.createObjectStore(DATABASE_CONFIG.stores.wallet.name, { keyPath: DATABASE_CONFIG.stores.wallet.keyPath as string });
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
try {
|
|
// Étape 1: Vérifier les prérequis AVANT d'initialiser Services
|
|
updateStatus('🔍 Vérification des prérequis...', 'loading');
|
|
updateProgress(10);
|
|
|
|
// CRITICAL: wallet-setup ne doit PAS générer de credentials
|
|
// Les credentials et clé PBKDF2 doivent être créés dans security-setup
|
|
// On vérifie que la clé PBKDF2 existe dans le store pbkdf2keys
|
|
secureLogger.debug('🔐 Checking for existing PBKDF2 key in pbkdf2keys store...', { component: 'WalletSetup' });
|
|
|
|
const pbkdf2KeyResult = await checkPBKDF2Key();
|
|
if (!pbkdf2KeyResult) {
|
|
secureLogger.warn('⚠️ No PBKDF2 key found in pbkdf2keys store, redirecting to security-setup...', { component: 'WalletSetup' });
|
|
updateStatus('⚠️ Redirection vers la configuration de sécurité...', 'loading');
|
|
setTimeout(() => {
|
|
window.location.href = '/src/pages/security-setup/security-setup.html';
|
|
}, 1000);
|
|
return;
|
|
}
|
|
|
|
const currentMode = pbkdf2KeyResult.mode;
|
|
secureLogger.info(`Prerequisites verified: PBKDF2 key found in pbkdf2keys store for mode: ${currentMode}`, { component: 'WalletSetup' });
|
|
|
|
// Étape 1.5: Vérifier si un wallet existe déjà
|
|
updateStatus('🔍 Vérification du wallet existant...', 'loading');
|
|
updateProgress(25);
|
|
|
|
const { DeviceReaderService } = await import('../../services/device-reader.service');
|
|
const deviceReader = DeviceReaderService.getInstance();
|
|
const existingDevice = await deviceReader.getDeviceFromDatabase();
|
|
|
|
if (existingDevice && existingDevice.sp_wallet) {
|
|
secureLogger.warn('✅ Wallet already exists, skipping creation', { component: 'WalletSetup' });
|
|
updateStatus('✅ Wallet existant trouvé', 'success');
|
|
updateProgress(100);
|
|
|
|
// Activer le bouton et rediriger
|
|
continueBtn.disabled = false;
|
|
continueBtn.textContent = 'Continuer';
|
|
continueBtn.onclick = () => {
|
|
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
|
};
|
|
|
|
// Auto-redirection après 2 secondes
|
|
setTimeout(() => {
|
|
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
|
}, 2000);
|
|
return;
|
|
}
|
|
|
|
secureLogger.info('🔧 No existing wallet found, creating new one...', { component: 'WalletSetup' });
|
|
updateStatus('🔧 Création du nouveau wallet...', 'loading');
|
|
updateProgress(30);
|
|
|
|
// Étape 2: Initialisation des services (uniquement si les prérequis sont OK)
|
|
updateStatus('🔄 Initialisation des services...', 'loading');
|
|
updateProgress(35);
|
|
|
|
// Vérifier la mémoire avant d'initialiser WebAssembly
|
|
if ((performance as any).memory) {
|
|
const memory = (performance as any).memory;
|
|
const usedPercent = (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100;
|
|
const usedMB = memory.usedJSHeapSize / 1024 / 1024;
|
|
const limitMB = memory.jsHeapSizeLimit / 1024 / 1024;
|
|
|
|
secureLogger.info('📊 Current memory usage: ${usedPercent.toFixed(1)}% (${usedMB.toFixed(1)}MB / ${limitMB.toFixed(1)}MB)', { component: 'WalletSetup' });
|
|
|
|
// Si la mémoire est très élevée (>75%), tenter un nettoyage agressif
|
|
if (usedPercent > 75) {
|
|
secureLogger.warn('⚠️ High memory usage detected, attempting aggressive cleanup...', { component: 'WalletSetup' });
|
|
updateStatus('🧹 Nettoyage de la mémoire en cours...', 'loading');
|
|
|
|
// Nettoyage agressif
|
|
if (window.gc) {
|
|
for (let i = 0; i < 3; i++) {
|
|
window.gc();
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
}
|
|
}
|
|
|
|
// Nettoyer les caches HTTP
|
|
if ('caches' in window) {
|
|
try {
|
|
const cacheNames = await caches.keys();
|
|
await Promise.all(cacheNames.map(name => caches.delete(name)));
|
|
secureLogger.info('🧹 Caches cleared', { component: 'WalletSetup' });
|
|
} catch (e) {
|
|
secureLogger.warn('Cache cleanup error', e as Error, { component: 'WalletSetup' });
|
|
}
|
|
}
|
|
|
|
// Vérifier la mémoire après nettoyage
|
|
const memoryAfter = (performance as any).memory;
|
|
const usedPercentAfter = (memoryAfter.usedJSHeapSize / memoryAfter.jsHeapSizeLimit) * 100;
|
|
secureLogger.info('📊 Memory after cleanup: ${usedPercentAfter.toFixed(1)}% (${(memoryAfter.usedJSHeapSize / 1024 / 1024).toFixed(1)}MB)', { component: 'WalletSetup' });
|
|
|
|
// Si toujours >90% après nettoyage, avertir l'utilisateur
|
|
if (usedPercentAfter > 90) {
|
|
secureLogger.error('❌ Memory still too high after cleanup', { component: 'WalletSetup' });
|
|
updateStatus('⚠️ Mémoire trop élevée. Fermez les autres onglets et actualisez la page.', 'error');
|
|
alert('⚠️ Mémoire insuffisante détectée.\n\nVeuillez :\n- Fermer les autres onglets du navigateur\n- Actualiser cette page\n\nSi le problème persiste, redémarrez le navigateur.');
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
let services: any; // Déclarer services au niveau supérieur
|
|
|
|
try {
|
|
secureLogger.info('🔄 Importing services...', { component: 'WalletSetup' });
|
|
const serviceModule = await import('../../services/service');
|
|
secureLogger.debug(`Service module imported: ${Object.keys(serviceModule)}`, { component: 'WalletSetup' });
|
|
|
|
// La classe Services est exportée par défaut
|
|
const Services = serviceModule.default;
|
|
|
|
if (!Services) {
|
|
throw new Error('Services class not found in default export');
|
|
}
|
|
secureLogger.info('🔄 Waiting for services to be ready...', { component: 'WalletSetup' });
|
|
|
|
// Attendre que les services soient initialisés avec plus de patience
|
|
let attempts = 0;
|
|
const maxAttempts = 30; // Plus de tentatives
|
|
const delayMs = 2000; // Délai plus long entre les tentatives
|
|
|
|
while (attempts < maxAttempts) {
|
|
try {
|
|
secureLogger.info('🔄 Attempting to get services (attempt ${attempts + 1}/${maxAttempts})...', { component: 'WalletSetup' });
|
|
services = await Services.getInstance();
|
|
secureLogger.info('✅ Services initialized successfully', { component: 'WalletSetup' });
|
|
break;
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
secureLogger.debug(`Services not ready yet (attempt ${attempts + 1}/${maxAttempts}): ${errorMessage}`, { component: 'WalletSetup' });
|
|
|
|
// Si c'est une erreur de mémoire, arrêter immédiatement
|
|
if (errorMessage.includes('Out of memory') || errorMessage.includes('insufficient memory')) {
|
|
secureLogger.error('🚫 Memory error detected - stopping retry attempts', { component: 'WalletSetup' });
|
|
updateStatus('❌ Erreur: Mémoire insuffisante. Veuillez actualiser la page.', 'error');
|
|
throw new Error('WebAssembly initialization failed due to insufficient memory. Please refresh the page.');
|
|
}
|
|
|
|
// Diagnostic plus détaillé
|
|
if (attempts === 5) {
|
|
secureLogger.debug('🔍 Diagnostic: Checking memory usage...', { component: 'WalletSetup' });
|
|
if ((performance as any).memory) {
|
|
const memory = (performance as any).memory;
|
|
secureLogger.info('📊 Memory usage: ${Math.round(memory.usedJSHeapSize / 1024 / 1024)}MB / ${Math.round(memory.totalJSHeapSize / 1024 / 1024)}MB', { component: 'WalletSetup' });
|
|
}
|
|
}
|
|
|
|
attempts++;
|
|
if (attempts >= maxAttempts) {
|
|
throw new Error(`Services failed to initialize after ${maxAttempts} attempts. This may be due to high memory usage or WebAssembly initialization issues.`);
|
|
}
|
|
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
secureLogger.error('Services not available', error as Error, { component: 'WalletSetup' });
|
|
throw error;
|
|
}
|
|
|
|
// Étape 3: Sauvegarde du wallet avec état birthday_waiting
|
|
updateStatus('💰 Sauvegarde du portefeuille...', 'loading');
|
|
updateProgress(60);
|
|
|
|
try {
|
|
secureLogger.info('🔐 Sauvegarde du wallet avec état birthday_waiting...', { component: 'WalletSetup' });
|
|
|
|
// Le mode de sécurité a déjà été trouvé dans la vérification des prérequis
|
|
// currentMode est déjà défini et vérifié
|
|
secureLogger.info(`Using security mode for wallet encryption: ${currentMode}`, { component: 'WalletSetup' });
|
|
|
|
// Générer un wallet temporaire avec état birthday_waiting
|
|
const { EncryptionService } = await import('../../services/encryption.service');
|
|
const encryptionService = EncryptionService.getInstance();
|
|
|
|
// Générer des clés temporaires pour le wallet
|
|
// Générer un wallet temporaire avec l'état birthday_waiting
|
|
const walletData = {
|
|
scan_sk: encryptionService.generateRandomKey(),
|
|
spend_key: encryptionService.generateRandomKey(),
|
|
network: 'signet',
|
|
state: 'birthday_waiting',
|
|
created_at: new Date().toISOString()
|
|
};
|
|
|
|
secureLogger.debug('Wallet data generated', { component: 'WalletSetup', data: walletData });
|
|
|
|
// Récupérer la clé PBKDF2 existante pour le mode détecté
|
|
// IMPORTANT: Ne PAS générer de nouvelle clé, utiliser celle qui existe
|
|
secureLogger.info(`Retrieving existing PBKDF2 key for security mode: ${currentMode}`, { component: 'WalletSetup' });
|
|
const pbkdf2Key = pbkdf2KeyResult.key;
|
|
if (!pbkdf2Key) {
|
|
secureLogger.error(`CRITICAL: Failed to retrieve PBKDF2 key for mode: ${currentMode}`, { component: 'WalletSetup' });
|
|
updateStatus('❌ Erreur: Impossible de récupérer la clé de chiffrement.', 'error');
|
|
throw new Error('CRITICAL: Failed to retrieve PBKDF2 key');
|
|
}
|
|
secureLogger.info('🔐 PBKDF2 key retrieved for wallet encryption', { component: 'WalletSetup' });
|
|
|
|
// Chiffrer le wallet avec la clé PBKDF2
|
|
const encryptedWallet = await encryptionService.encrypt(
|
|
JSON.stringify(walletData),
|
|
pbkdf2Key
|
|
);
|
|
secureLogger.info('🔐 Wallet encrypted with PBKDF2 key', { component: 'WalletSetup' });
|
|
secureLogger.debug('Encrypted wallet data', { component: 'WalletSetup', data: encryptedWallet });
|
|
|
|
// Ouvrir la base de données 4nk existante sans la modifier
|
|
secureLogger.info(`Opening IndexedDB database "${DATABASE_CONFIG.name}" version ${DATABASE_CONFIG.version}...`, { component: 'WalletSetup' });
|
|
const db = await new Promise<IDBDatabase>((resolve, reject) => {
|
|
const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version); // Utiliser la version centralisée
|
|
request.onerror = () => {
|
|
secureLogger.error('Failed to open IndexedDB', request.error as Error, { component: 'WalletSetup' });
|
|
reject(request.error);
|
|
};
|
|
request.onsuccess = () => {
|
|
secureLogger.info('✅ IndexedDB opened successfully', { component: 'WalletSetup' });
|
|
secureLogger.debug(`Database name: ${request.result.name}`, { component: 'WalletSetup' });
|
|
secureLogger.debug(`Database version: ${request.result.version}`, { component: 'WalletSetup' });
|
|
secureLogger.debug(`Available stores: ${Array.from(request.result.objectStoreNames)}`, { component: 'WalletSetup' });
|
|
resolve(request.result);
|
|
};
|
|
request.onupgradeneeded = () => {
|
|
const db = request.result;
|
|
secureLogger.debug('🔄 IndexedDB upgrade needed, checking wallet store...', { component: 'WalletSetup' });
|
|
|
|
// Créer le store wallet seulement s'il n'existe pas
|
|
if (!db.objectStoreNames.contains(DATABASE_CONFIG.stores.wallet.name)) {
|
|
const store = db.createObjectStore(DATABASE_CONFIG.stores.wallet.name, { keyPath: DATABASE_CONFIG.stores.wallet.keyPath as string });
|
|
secureLogger.info('✅ Wallet store created with keyPath: ${DATABASE_CONFIG.stores.wallet.keyPath}', { component: 'WalletSetup' });
|
|
} else {
|
|
secureLogger.info('✅ Wallet store already exists', { component: 'WalletSetup' });
|
|
}
|
|
};
|
|
});
|
|
|
|
// Étape 1: Sauvegarder le wallet dans le format attendu par getDeviceFromDatabase
|
|
// Utiliser le SDK directement pour éviter le service worker qui bloque
|
|
secureLogger.info('🔧 Creating device using SDK...', { component: 'WalletSetup' });
|
|
|
|
// Créer le device directement avec le SDK sans passer par saveDeviceInDatabase
|
|
if (!services.sdkClient) {
|
|
throw new Error('WebAssembly SDK not initialized');
|
|
}
|
|
|
|
// We set birthday later when we have the chain tip from relay
|
|
secureLogger.info('🔧 Creating new device with birthday 0...', { component: 'WalletSetup' });
|
|
const spAddress = await services.sdkClient.create_new_device(0, 'signet');
|
|
secureLogger.info(`Device created with address: ${spAddress}`, { component: 'WalletSetup' });
|
|
|
|
// Force wallet generation to ensure keys are created
|
|
secureLogger.info('🔧 Forcing wallet generation...', { component: 'WalletSetup' });
|
|
try {
|
|
const wallet = await services.sdkClient.dump_wallet();
|
|
secureLogger.debug('Wallet generated', { component: 'WalletSetup', data: wallet });
|
|
} catch (walletError) {
|
|
secureLogger.warn('Wallet generation failed', walletError as Error, { component: 'WalletSetup' });
|
|
}
|
|
|
|
// Récupérer le device créé par le SDK
|
|
const device = services.dumpDeviceFromMemory();
|
|
secureLogger.debug('Device structure from SDK', {
|
|
component: 'WalletSetup',
|
|
data: {
|
|
hasSpWallet: !!device.sp_wallet,
|
|
hasSpClient: !!device.sp_client,
|
|
spClientKeys: device.sp_client ? Object.keys(device.sp_client) : 'none'
|
|
}
|
|
});
|
|
|
|
secureLogger.debug(`Opening transaction for ${DATABASE_CONFIG.stores.wallet.name} store...`, { component: 'WalletSetup' });
|
|
|
|
// CRITICAL: Chiffrer TOUS les données du wallet avant stockage
|
|
// Le device contient des données sensibles (sp_wallet) qui ne doivent JAMAIS être en clair
|
|
secureLogger.info('🔐 Encrypting device data before storage...', { component: 'WalletSetup' });
|
|
const deviceString = JSON.stringify(device);
|
|
const encryptedDevice = await encryptionService.encrypt(deviceString, pbkdf2Key);
|
|
secureLogger.info('🔐 Device encrypted successfully', { component: 'WalletSetup' });
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readwrite');
|
|
const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name);
|
|
secureLogger.debug(`Store opened: ${store.name}`, { component: 'WalletSetup' });
|
|
|
|
// Stocker UNIQUEMENT des données chiffrées - aucun wallet en clair
|
|
const walletObject = {
|
|
pre_id: '1',
|
|
encrypted_device: encryptedDevice, // Device complètement chiffré
|
|
encrypted_wallet: encryptedWallet // Wallet chiffré
|
|
};
|
|
|
|
secureLogger.debug('🔍 Attempting to save encrypted wallet object', { component: 'WalletSetup' });
|
|
secureLogger.debug('Object contains only encrypted data', {
|
|
component: 'WalletSetup',
|
|
data: {
|
|
hasEncryptedDevice: !!walletObject.encrypted_device,
|
|
hasEncryptedWallet: !!walletObject.encrypted_wallet
|
|
// Ne pas logger le contenu chiffré
|
|
}
|
|
});
|
|
|
|
// Le store utilise des clés in-line (keyPath: 'pre_id'), ne pas fournir de clé explicite
|
|
const request = store.put(walletObject);
|
|
request.onsuccess = () => {
|
|
secureLogger.info('✅ Wallet saved in IndexedDB with correct format', { component: 'WalletSetup' });
|
|
secureLogger.debug('Saved wallet object', { component: 'WalletSetup', data: walletObject });
|
|
resolve();
|
|
};
|
|
request.onerror = () => {
|
|
secureLogger.error('Failed to save wallet in IndexedDB', request.error as Error, { component: 'WalletSetup' });
|
|
reject(request.error);
|
|
};
|
|
|
|
transaction.oncomplete = () => {
|
|
secureLogger.info('✅ Transaction completed successfully', { component: 'WalletSetup' });
|
|
secureLogger.debug(`Transaction completed for store: ${store.name}`, { component: 'WalletSetup' });
|
|
};
|
|
transaction.onerror = () => {
|
|
secureLogger.error('Transaction failed', transaction.error as Error, { component: 'WalletSetup' });
|
|
secureLogger.error('Transaction error details', new Error('Transaction failed'), {
|
|
component: 'WalletSetup',
|
|
data: {
|
|
error: transaction.error,
|
|
store: store.name,
|
|
mode: transaction.mode
|
|
}
|
|
});
|
|
reject(transaction.error);
|
|
};
|
|
});
|
|
|
|
// Étape 2: Vérifier que le wallet est bien stocké (nouvelle transaction)
|
|
await new Promise<void>((resolve, reject) => {
|
|
const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readonly');
|
|
const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name);
|
|
|
|
const verificationRequest = store.get('1');
|
|
verificationRequest.onsuccess = () => {
|
|
secureLogger.debug('Verification result structure', {
|
|
component: 'WalletSetup',
|
|
data: verificationRequest.result ? {
|
|
hasPreId: !!verificationRequest.result.pre_id,
|
|
hasEncryptedDevice: !!verificationRequest.result.encrypted_device,
|
|
hasEncryptedWallet: !!verificationRequest.result.encrypted_wallet,
|
|
hasDeviceInClear: !!verificationRequest.result.device // DEVRAIT ÊTRE NULL
|
|
} : 'null'
|
|
});
|
|
|
|
if (verificationRequest.result) {
|
|
secureLogger.info('Wallet verification: Found in IndexedDB with key "1"', { component: 'WalletSetup' });
|
|
|
|
// Vérifier qu'il n'y a AUCUN wallet en clair
|
|
if (verificationRequest.result.device) {
|
|
secureLogger.error('CRITICAL: Device in clear found in database! This should be encrypted.', { component: 'WalletSetup' });
|
|
reject(new Error('Security violation: wallet stored in clear'));
|
|
return;
|
|
}
|
|
|
|
// Vérifier que les données chiffrées sont présentes
|
|
if (!verificationRequest.result.encrypted_device || !verificationRequest.result.encrypted_wallet) {
|
|
secureLogger.error('❌ Wallet verification failed: encrypted data missing', { component: 'WalletSetup' });
|
|
reject(new Error('Encrypted data missing'));
|
|
return;
|
|
}
|
|
|
|
secureLogger.info('✅ Wallet stored correctly: only encrypted data present', { component: 'WalletSetup' });
|
|
resolve();
|
|
} else {
|
|
secureLogger.error('Wallet verification: Not found in IndexedDB with key "1"', { component: 'WalletSetup' });
|
|
reject(new Error('Wallet not found after save'));
|
|
}
|
|
};
|
|
verificationRequest.onerror = () => {
|
|
secureLogger.error('Wallet verification failed', verificationRequest.error as Error, { component: 'WalletSetup' });
|
|
reject(verificationRequest.error);
|
|
};
|
|
});
|
|
|
|
// Le mode de sécurité est déjà stocké via la clé PBKDF2 dans le store pbkdf2keys
|
|
// Pas besoin de le stocker séparément
|
|
secureLogger.info(`Security mode is implicitly stored via PBKDF2 key: ${currentMode}`, { component: 'WalletSetup' });
|
|
|
|
// Vérification finale : s'assurer que le wallet est bien dans IndexedDB
|
|
secureLogger.debug('🔍 Final verification: checking wallet in IndexedDB...', { component: 'WalletSetup' });
|
|
const finalVerification = await new Promise<any>((resolve, reject) => {
|
|
const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readonly');
|
|
const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name);
|
|
const request = store.get('1');
|
|
request.onsuccess = () => resolve(request.result);
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
|
|
if (finalVerification) {
|
|
// Vérifier qu'il n'y a PAS de device en clair
|
|
if (finalVerification.device) {
|
|
secureLogger.error('❌ CRITICAL: Final verification - Device in clear found!', { component: 'WalletSetup' });
|
|
throw new Error('Security violation: wallet stored in clear');
|
|
}
|
|
|
|
secureLogger.info('✅ Wallet saved exclusively in IndexedDB and verified', { component: 'WalletSetup' });
|
|
console.log('🔍 Wallet contains only encrypted data:', {
|
|
hasEncryptedDevice: !!finalVerification.encrypted_device,
|
|
hasEncryptedWallet: !!finalVerification.encrypted_wallet,
|
|
hasDeviceInClear: !!finalVerification.device // DEVRAIT ÊTRE FALSE
|
|
});
|
|
|
|
// TEST: Déchiffrer le wallet pour valider que ça fonctionne
|
|
secureLogger.info('🔐 TEST: Attempting to decrypt wallet to validate encryption...', { component: 'WalletSetup' });
|
|
try {
|
|
const pbkdf2KeyTest = pbkdf2KeyResult.key;
|
|
if (!pbkdf2KeyTest) {
|
|
secureLogger.error('❌ TEST: Failed to retrieve PBKDF2 key for decryption test', { component: 'WalletSetup' });
|
|
} else {
|
|
secureLogger.info('✅ TEST: PBKDF2 key retrieved for decryption test', { component: 'WalletSetup' });
|
|
|
|
// Déchiffrer le wallet chiffré
|
|
const decryptedWallet = await encryptionService.decrypt(
|
|
finalVerification.encrypted_wallet,
|
|
pbkdf2KeyTest
|
|
);
|
|
const parsedWallet = JSON.parse(decryptedWallet);
|
|
console.log('✅ TEST: Wallet decrypted successfully:', {
|
|
hasScanSk: !!parsedWallet.scan_sk,
|
|
hasSpendKey: !!parsedWallet.spend_key,
|
|
network: parsedWallet.network,
|
|
state: parsedWallet.state,
|
|
created_at: parsedWallet.created_at
|
|
});
|
|
|
|
// Déchiffrer le device chiffré
|
|
const decryptedDevice = await encryptionService.decrypt(
|
|
finalVerification.encrypted_device,
|
|
pbkdf2KeyTest
|
|
);
|
|
const parsedDevice = JSON.parse(decryptedDevice);
|
|
console.log('✅ TEST: Device decrypted successfully:', {
|
|
hasSpWallet: !!parsedDevice.sp_wallet,
|
|
network: parsedDevice.network
|
|
});
|
|
|
|
secureLogger.info('✅ TEST: Full decryption test passed - wallet and device decrypt correctly', { component: 'WalletSetup' });
|
|
}
|
|
} catch (decryptError) {
|
|
console.error('❌ TEST: Decryption test failed:', decryptError);
|
|
secureLogger.error('❌ This indicates an issue with encryption/decryption logic', { component: 'WalletSetup' });
|
|
}
|
|
} else {
|
|
secureLogger.error('❌ Final wallet verification failed - wallet not found in IndexedDB', { component: 'WalletSetup' });
|
|
throw new Error('Wallet verification failed - wallet not found');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error during wallet save:', error);
|
|
updateStatus('❌ Erreur: Échec de la sauvegarde du wallet', 'error');
|
|
throw error;
|
|
}
|
|
|
|
// Étape 4: Finalisation
|
|
updateStatus('✅ Wallet sauvegardé avec succès! Redirection automatique dans 3 secondes...', 'success');
|
|
updateProgress(100);
|
|
|
|
secureLogger.info('🎉 Wallet setup completed successfully - wallet saved with birthday_waiting state', { component: 'WalletSetup' });
|
|
secureLogger.info('🔗 Ready to proceed to network connection and birthday setup', { component: 'WalletSetup' });
|
|
|
|
// Activer le bouton continuer
|
|
continueBtn.disabled = false;
|
|
secureLogger.info('✅ Continue button enabled', { component: 'WalletSetup' });
|
|
|
|
// Redirection automatique après 3 secondes si l'utilisateur ne clique pas
|
|
setTimeout(() => {
|
|
secureLogger.info('🔄 Auto-redirecting to birthday setup after timeout...', { component: 'WalletSetup' });
|
|
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
|
}, 3000);
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error during wallet setup:', error);
|
|
updateStatus('❌ Erreur lors de la génération du wallet', 'error');
|
|
}
|
|
|
|
// Gestion du bouton continuer
|
|
continueBtn.addEventListener('click', async () => {
|
|
secureLogger.info('🔗 Redirecting to birthday setup...', { component: 'WalletSetup' });
|
|
secureLogger.info('💰 Wallet setup completed, redirecting to birthday configuration...', { component: 'WalletSetup' });
|
|
// Rediriger directement vers la page de configuration de la date anniversaire
|
|
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
|
});
|
|
}); |