feat: encrypt wallet completely and detect security mode from PBKDF2 key

**Motivations :**
- Encrypt all wallet data with PBKDF2 key, never store in clear
- Detect security mode from available PBKDF2 key instead of using fallback
- Stop page without fallback if no PBKDF2 key is available
- Use existing PBKDF2 keys only, no generation

**Modifications :**
- wallet-setup.ts: Encrypt device data before storing, store only encrypted_device and encrypted_wallet
- wallet-setup.ts: Detect security mode by testing all modes to find a working PBKDF2 key
- wallet-setup.ts: Stop without fallback if no PBKDF2 key is found
- wallet-setup.ts: Retrieve existing PBKDF2 key (no generation)
- wallet-setup.ts: Remove separate security mode storage (already stored via PBKDF2 key in pbkdf2keys store)
- wallet-setup.ts: Add verification to reject any wallet stored in clear
- wallet-setup.ts: Fix IndexedDB inline key usage (remove explicit key parameter)

**Pages affected :**
- wallet-setup.html: Encrypts and stores only encrypted wallet data, detects security mode from PBKDF2 key
This commit is contained in:
NicolasCantu 2025-10-26 02:28:33 +01:00
parent aa913ef930
commit 3eae4f0210

View File

@ -152,15 +152,39 @@ document.addEventListener('DOMContentLoaded', async () => {
try { try {
console.log('🔐 Sauvegarde du wallet avec état birthday_waiting...'); console.log('🔐 Sauvegarde du wallet avec état birthday_waiting...');
// Récupérer le mode de sécurité pour le chiffrement // DÉTECTER le mode de sécurité via la clé PBKDF2 disponible
const { SecurityModeService } = await import('../../services/security-mode.service'); // Le mode de sécurité est identifié par la clé PBKDF2 qui fonctionne
const securityModeService = SecurityModeService.getInstance(); const { SecureCredentialsService } = await import('../../services/secure-credentials.service');
let currentMode = await securityModeService.getCurrentMode(); const secureCredentialsService = SecureCredentialsService.getInstance();
// Si aucun mode n'est trouvé, utiliser le mode par défaut console.log('🔍 Testing all security modes to find a PBKDF2 key...');
// Tester tous les modes de sécurité pour trouver une clé PBKDF2 valide
const allSecurityModes: Array<'browser' | 'otp' | 'password' | 'none' | 'os' | 'proton-pass'> =
['browser', 'otp', 'password', 'none', 'os', 'proton-pass'];
let currentMode: string | null = null;
for (const mode of allSecurityModes) {
console.log(`🔍 Testing security mode: ${mode}`);
try {
const key = await secureCredentialsService.retrievePBKDF2Key(mode);
if (key) {
currentMode = mode;
console.log(`✅ PBKDF2 key found for security mode: ${mode}`);
break;
}
} catch (error) {
console.log(`⚠️ No PBKDF2 key found for mode ${mode}`);
}
}
// CRITICAL: Si aucune clé PBKDF2 n'est disponible, arrêter immédiatement
if (!currentMode) { if (!currentMode) {
console.log('⚠️ No security mode found, using browser mode as fallback'); console.error('❌ CRITICAL: No PBKDF2 key found for any security mode.');
currentMode = 'browser'; console.error('❌ Cannot proceed with wallet creation without encryption key.');
updateStatus('❌ Erreur: Aucune clé de chiffrement trouvée. Veuillez configurer la sécurité.', 'error');
throw new Error('CRITICAL: No PBKDF2 key found. Cannot create wallet without encryption key.');
} }
console.log('🔐 Using security mode for wallet encryption:', currentMode); console.log('🔐 Using security mode for wallet encryption:', currentMode);
@ -183,12 +207,15 @@ document.addEventListener('DOMContentLoaded', async () => {
console.log('🔐 Wallet data generated:', walletData); console.log('🔐 Wallet data generated:', walletData);
// Récupérer la clé PBKDF2 générée par le service de sécurité // Récupérer la clé PBKDF2 existante pour le mode détecté
const { SecureCredentialsService } = await import('../../services/secure-credentials.service'); // IMPORTANT: Ne PAS générer de nouvelle clé, utiliser celle qui existe
const secureCredentialsService = SecureCredentialsService.getInstance(); console.log('🔐 Retrieving existing PBKDF2 key for security mode:', currentMode);
const pbkdf2Key = await secureCredentialsService.retrievePBKDF2Key(currentMode as any);
// Générer la clé PBKDF2 avec le mode de sécurité choisi if (!pbkdf2Key) {
const pbkdf2Key = await secureCredentialsService.generatePBKDF2Key(currentMode); console.error('❌ CRITICAL: Failed to retrieve PBKDF2 key for mode:', currentMode);
updateStatus('❌ Erreur: Impossible de récupérer la clé de chiffrement.', 'error');
throw new Error('CRITICAL: Failed to retrieve PBKDF2 key');
}
console.log('🔐 PBKDF2 key retrieved for wallet encryption'); console.log('🔐 PBKDF2 key retrieved for wallet encryption');
// Chiffrer le wallet avec la clé PBKDF2 // Chiffrer le wallet avec la clé PBKDF2
@ -260,22 +287,37 @@ document.addEventListener('DOMContentLoaded', async () => {
}); });
console.log(`🔍 Opening transaction for ${DATABASE_CONFIG.stores.wallet.name} store...`); console.log(`🔍 Opening transaction for ${DATABASE_CONFIG.stores.wallet.name} store...`);
// 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
console.log('🔐 Encrypting device data before storage...');
const deviceString = JSON.stringify(device);
const encryptedDevice = await encryptionService.encryptWithPassword(deviceString, pbkdf2Key);
console.log('🔐 Device encrypted successfully');
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readwrite'); const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readwrite');
const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name); const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name);
console.log('🔍 Store opened:', store.name); console.log('🔍 Store opened:', store.name);
// Stocker le wallet chiffré séparément et laisser le SDK gérer sp_wallet // Stocker UNIQUEMENT des données chiffrées - aucun wallet en clair
const walletObject = { const walletObject = {
pre_id: '1', pre_id: '1',
device: device, encrypted_device: encryptedDevice, // Device complètement chiffré
encrypted_wallet: encryptedWallet, // Stocker le wallet chiffré séparément encrypted_wallet: encryptedWallet, // Wallet chiffré
security_mode: currentMode // Stocker le mode de sécurité security_mode: currentMode // Mode de sécurité utilisé
}; };
console.log('🔍 Attempting to save wallet object:', walletObject); console.log('🔍 Attempting to save encrypted wallet object');
// Le store utilise des clés out-of-line, fournir une clé explicite console.log('🔐 Object contains only encrypted data:', {
const request = store.put(walletObject, '1'); hasEncryptedDevice: !!walletObject.encrypted_device,
hasEncryptedWallet: !!walletObject.encrypted_wallet,
securityMode: walletObject.security_mode,
// 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 = () => { request.onsuccess = () => {
console.log('✅ Wallet saved in IndexedDB with correct format'); console.log('✅ Wallet saved in IndexedDB with correct format');
console.log('🔍 Saved wallet object:', walletObject); console.log('🔍 Saved wallet object:', walletObject);
@ -308,11 +350,32 @@ document.addEventListener('DOMContentLoaded', async () => {
const verificationRequest = store.get('1'); const verificationRequest = store.get('1');
verificationRequest.onsuccess = () => { verificationRequest.onsuccess = () => {
console.log('🔍 Verification result:', verificationRequest.result); console.log('🔍 Verification result structure:', verificationRequest.result ? {
hasPreId: !!verificationRequest.result.pre_id,
hasEncryptedDevice: !!verificationRequest.result.encrypted_device,
hasEncryptedWallet: !!verificationRequest.result.encrypted_wallet,
hasSecurityMode: !!verificationRequest.result.security_mode,
hasDeviceInClear: !!verificationRequest.result.device // DEVRAIT ÊTRE NULL
} : 'null');
if (verificationRequest.result) { if (verificationRequest.result) {
console.log('✅ Wallet verification: Found in IndexedDB with key "1"'); console.log('✅ Wallet verification: Found in IndexedDB with key "1"');
console.log('🔍 Device state:', verificationRequest.result.device.sp_wallet.state);
console.log('🔍 Encrypted data present:', !!verificationRequest.result.device.sp_wallet.encrypted_data); // Vérifier qu'il n'y a AUCUN wallet en clair
if (verificationRequest.result.device) {
console.error('❌ CRITICAL: Device in clear found in database! This should be encrypted.');
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) {
console.error('❌ Wallet verification failed: encrypted data missing');
reject(new Error('Encrypted data missing'));
return;
}
console.log('✅ Wallet stored correctly: only encrypted data present');
resolve(); resolve();
} else { } else {
console.error('❌ Wallet verification: Not found in IndexedDB with key "1"'); console.error('❌ Wallet verification: Not found in IndexedDB with key "1"');
@ -325,18 +388,9 @@ document.addEventListener('DOMContentLoaded', async () => {
}; };
}); });
// Sauvegarder le mode de sécurité choisi // Le mode de sécurité est déjà stocké via la clé PBKDF2 dans le store pbkdf2keys
console.log('🔐 Saving security mode:', currentMode); // Pas besoin de le stocker séparément
await securityModeService.setSecurityMode(currentMode); console.log('🔐 Security mode is implicitly stored via PBKDF2 key:', currentMode);
// Vérifier que le mode de sécurité est bien sauvegardé
const savedMode = await securityModeService.getCurrentMode();
if (savedMode === currentMode) {
console.log('✅ Security mode saved and verified:', savedMode);
} else {
console.error('❌ Security mode verification failed. Expected:', currentMode, 'Got:', savedMode);
throw new Error('Security mode verification failed');
}
// Vérification finale : s'assurer que le wallet est bien dans IndexedDB // Vérification finale : s'assurer que le wallet est bien dans IndexedDB
console.log('🔍 Final verification: checking wallet in IndexedDB...'); console.log('🔍 Final verification: checking wallet in IndexedDB...');
@ -348,14 +402,19 @@ document.addEventListener('DOMContentLoaded', async () => {
request.onerror = () => reject(request.error); request.onerror = () => reject(request.error);
}); });
if (finalVerification && finalVerification.device) { if (finalVerification) {
// Vérifier qu'il n'y a PAS de device en clair
if (finalVerification.device) {
console.error('❌ CRITICAL: Final verification - Device in clear found!');
throw new Error('Security violation: wallet stored in clear');
}
console.log('✅ Wallet saved exclusively in IndexedDB and verified'); console.log('✅ Wallet saved exclusively in IndexedDB and verified');
console.log('🔍 Wallet contains:', { console.log('🔍 Wallet contains only encrypted data:', {
hasSpWallet: !!finalVerification.device.sp_wallet, hasEncryptedDevice: !!finalVerification.encrypted_device,
hasSpClient: !!finalVerification.device.sp_client,
hasEncryptedWallet: !!finalVerification.encrypted_wallet, hasEncryptedWallet: !!finalVerification.encrypted_wallet,
securityMode: finalVerification.security_mode, securityMode: finalVerification.security_mode,
spClientKeys: finalVerification.device.sp_client ? Object.keys(finalVerification.device.sp_client) : 'none' hasDeviceInClear: !!finalVerification.device // DEVRAIT ÊTRE FALSE
}); });
} else { } else {
console.error('❌ Final wallet verification failed - wallet not found in IndexedDB'); console.error('❌ Final wallet verification failed - wallet not found in IndexedDB');