diff --git a/src/components/device-management/device-management.ts b/src/components/device-management/device-management.ts
index f18b285..6d1d351 100644
--- a/src/components/device-management/device-management.ts
+++ b/src/components/device-management/device-management.ts
@@ -30,7 +30,7 @@ export class DeviceManagementComponent extends HTMLElement {
}
async loadDeviceData() {
- if (!this.service) return;
+ if (!this.service) {return;}
try {
// Get current device address and generate 4 words
@@ -601,12 +601,12 @@ export class DeviceManagementComponent extends HTMLElement {
const saveBtn = this.shadowRoot!.getElementById('saveChangesBtn') as HTMLButtonElement;
const cancelBtn = this.shadowRoot!.getElementById('cancelChangesBtn') as HTMLButtonElement;
- if (saveBtn) saveBtn.disabled = false;
- if (cancelBtn) cancelBtn.disabled = false;
+ if (saveBtn) {saveBtn.disabled = false;}
+ if (cancelBtn) {cancelBtn.disabled = false;}
}
async saveChanges() {
- if (!this.service) return;
+ if (!this.service) {return;}
try {
// Update the pairing process with new devices
@@ -634,8 +634,8 @@ export class DeviceManagementComponent extends HTMLElement {
const saveBtn = this.shadowRoot!.getElementById('saveChangesBtn') as HTMLButtonElement;
const cancelBtn = this.shadowRoot!.getElementById('cancelChangesBtn') as HTMLButtonElement;
- if (saveBtn) saveBtn.disabled = true;
- if (cancelBtn) cancelBtn.disabled = true;
+ if (saveBtn) {saveBtn.disabled = true;}
+ if (cancelBtn) {cancelBtn.disabled = true;}
}
async importAccount() {
@@ -692,12 +692,12 @@ export class DeviceManagementComponent extends HTMLElement {
const confirm1 = confirm(
'đš EXPORT CRITIQUE: Cette action va exposer votre CLĂ PRIVĂE.\n\nCette clĂ© permet de signer des transactions sans interaction sur le 2Ăšme device.\n\nĂtes-vous sĂ»r de vouloir continuer ?'
);
- if (!confirm1) return;
+ if (!confirm1) {return;}
const confirm2 = confirm(
'â ïž SĂCURITĂ: Votre clĂ© privĂ©e sera visible en clair.\n\nAssurez-vous que personne ne peut voir votre Ă©cran.\n\nContinuer ?'
);
- if (!confirm2) return;
+ if (!confirm2) {return;}
const confirm3 = prompt(
'đ DERNIĂRE CONFIRMATION: Cette clĂ© privĂ©e donne un accĂšs TOTAL Ă votre compte.\n\nTapez "EXPORTER" pour confirmer:'
@@ -711,7 +711,7 @@ export class DeviceManagementComponent extends HTMLElement {
// Get the device's private key
// @ts-ignore - deviceRaw is guaranteed to be non-null after the check below
const deviceRaw = await this.service.getDeviceFromDatabase();
- if (!deviceRaw || !deviceRaw.sp_wallet) {
+ if (!deviceRaw?.sp_wallet) {
throw new Error('Device ou clé privée non trouvée');
}
// TypeScript assertion: deviceRaw is guaranteed to be non-null after the check
diff --git a/src/components/secure-credentials/secure-credentials.ts b/src/components/secure-credentials/secure-credentials.ts
index a8a71b6..085c022 100644
--- a/src/components/secure-credentials/secure-credentials.ts
+++ b/src/components/secure-credentials/secure-credentials.ts
@@ -325,7 +325,7 @@ export class SecureCredentialsComponent {
*/
private showMessage(message: string, type: 'success' | 'error' | 'warning' | 'info'): void {
const messagesContainer = document.getElementById('credentials-messages');
- if (!messagesContainer) return;
+ if (!messagesContainer) {return;}
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
diff --git a/src/components/security-mode-selector/security-mode-selector.ts b/src/components/security-mode-selector/security-mode-selector.ts
index f659b9a..954c1ea 100644
--- a/src/components/security-mode-selector/security-mode-selector.ts
+++ b/src/components/security-mode-selector/security-mode-selector.ts
@@ -309,7 +309,7 @@ export class SecurityModeSelector {
}
private confirmSelection(): void {
- if (!this.selectedMode) return;
+ if (!this.selectedMode) {return;}
const modeConfig = this.getSecurityModes().find(m => m.mode === this.selectedMode);
diff --git a/src/components/validation-modal/validation-modal.ts b/src/components/validation-modal/validation-modal.ts
index 51b89f0..e8a55b6 100755
--- a/src/components/validation-modal/validation-modal.ts
+++ b/src/components/validation-modal/validation-modal.ts
@@ -42,13 +42,13 @@ export async function initValidationModal(processDiffs: any) {
`;
const box = document.querySelector('.validation-box');
- if (box) box.innerHTML += state;
+ if (box) {box.innerHTML += state;}
}
document.querySelectorAll('.expansion-panel-header').forEach(header => {
header.addEventListener('click', function (event) {
const target = event.target as HTMLElement;
const body = target.nextElementSibling as HTMLElement;
- if (body?.style) body.style.display = body.style.display === 'block' ? 'none' : 'block';
+ if (body?.style) {body.style.display = body.style.display === 'block' ? 'none' : 'block';}
});
});
}
diff --git a/src/pages/home/home-component.ts b/src/pages/home/home-component.ts
index fc6efed..133e712 100644
--- a/src/pages/home/home-component.ts
+++ b/src/pages/home/home-component.ts
@@ -32,7 +32,7 @@ export class LoginComponent extends HTMLElement {
render() {
if (this.shadowRoot)
- this.shadowRoot.innerHTML = `
+ {this.shadowRoot.innerHTML = `
${loginHtml}
@@ -40,7 +40,7 @@ export class LoginComponent extends HTMLElement {
${loginScript}
- `;
+ `;}
}
}
diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts
index 4e24fc4..51b461e 100755
--- a/src/pages/home/home.ts
+++ b/src/pages/home/home.ts
@@ -461,7 +461,7 @@ function setupUserInteractionListener(): void {
let hasTriggered = false;
const triggerWebAuthn = async (event: Event) => {
- if (hasTriggered) return;
+ if (hasTriggered) {return;}
hasTriggered = true;
console.log('đ User interaction detected:', event.type, 'triggering WebAuthn...');
@@ -812,7 +812,7 @@ async function waitForCredentialsAvailability(): Promise {
// Vérifier que les credentials sont réellement disponibles et accessibles
const credentials = await secureCredentialsService.retrieveCredentials('');
- if (credentials && credentials.spendKey && credentials.scanKey) {
+ if (credentials?.spendKey && credentials.scanKey) {
console.log('â
Credentials confirmés comme disponibles');
return;
} else {
@@ -938,7 +938,7 @@ async function handleMainPairing(): Promise {
const credentialData = await secureCredentialsService.generateSecureCredentials('4nk-secure-password');
- if (!credentialData || !credentialData.spendKey || !credentialData.scanKey) {
+ if (!credentialData?.spendKey || !credentialData.scanKey) {
throw new Error('Failed to generate valid credentials - missing spendKey or scanKey');
}
@@ -999,7 +999,7 @@ async function handleMainPairing(): Promise {
try {
// Vérifier que les credentials sont réellement disponibles
const credentials = await secureCredentialsService.retrieveCredentials('');
- if (!credentials || !credentials.spendKey || !credentials.scanKey) {
+ if (!credentials?.spendKey || !credentials.scanKey) {
throw new Error('Credentials not properly available');
}
credentialsReady = true;
diff --git a/src/pages/security-setup/security-setup.ts b/src/pages/security-setup/security-setup.ts
index 27563eb..800fd90 100644
--- a/src/pages/security-setup/security-setup.ts
+++ b/src/pages/security-setup/security-setup.ts
@@ -4,12 +4,24 @@
*/
import { SecurityMode } from '../../services/security-mode.service';
+import { DATABASE_CONFIG, openDatabase } from '../../services/database-config';
let selectedMode: SecurityMode | null = null;
-document.addEventListener('DOMContentLoaded', () => {
+document.addEventListener('DOMContentLoaded', async () => {
console.log('đ Security setup page loaded');
+ // Initialiser la base de données avec la configuration complÚte
+ try {
+ console.log(`đ Initializing database ${DATABASE_CONFIG.name} version ${DATABASE_CONFIG.version}...`);
+ await openDatabase();
+ console.log('â
Database initialized successfully');
+ } catch (error) {
+ console.error('â Failed to initialize database:', error);
+ alert('Erreur lors de l\'initialisation de la base de données. Veuillez réessayer.');
+ return;
+ }
+
const options = document.querySelectorAll('.security-option');
const continueBtn = document.getElementById('continueBtn') as HTMLButtonElement;
const warning = document.getElementById('warning') as HTMLDivElement;
@@ -72,6 +84,7 @@ document.addEventListener('DOMContentLoaded', () => {
console.log('đ Generating PBKDF2 key with security mode:', selectedMode);
// Générer la clé PBKDF2 et la stocker selon le mode
+ // IMPORTANT: Cette opĂ©ration doit ĂȘtre directe (pas de dĂ©lai) pour que WebAuthn fonctionne
const pbkdf2Key = await secureCredentialsService.generatePBKDF2Key(selectedMode);
console.log('â
PBKDF2 key generated and stored securely');
diff --git a/src/pages/wallet-setup/wallet-setup.ts b/src/pages/wallet-setup/wallet-setup.ts
index 7da7643..9ad7c3d 100644
--- a/src/pages/wallet-setup/wallet-setup.ts
+++ b/src/pages/wallet-setup/wallet-setup.ts
@@ -3,6 +3,8 @@
* DeuxiÚme étape du processus d'initialisation
*/
+import { DATABASE_CONFIG } from '../../services/database-config';
+
document.addEventListener('DOMContentLoaded', async () => {
console.log('đ° Wallet setup page loaded');
@@ -22,13 +24,13 @@ document.addEventListener('DOMContentLoaded', async () => {
// Méthode pour sauvegarder directement en IndexedDB dans la base 4nk
async function saveCredentialsDirectly(credentials: any): Promise {
return new Promise((resolve, reject) => {
- const request = indexedDB.open('4nk', 2);
+ 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(['wallet'], 'readwrite');
- const store = transaction.objectStore('wallet');
+ 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();
@@ -37,8 +39,8 @@ document.addEventListener('DOMContentLoaded', async () => {
request.onupgradeneeded = () => {
const db = request.result;
- if (!db.objectStoreNames.contains('wallet')) {
- db.createObjectStore('wallet');
+ if (!db.objectStoreNames.contains(DATABASE_CONFIG.stores.wallet.name)) {
+ db.createObjectStore(DATABASE_CONFIG.stores.wallet.name, { keyPath: DATABASE_CONFIG.stores.wallet.keyPath as string });
}
};
});
@@ -49,7 +51,7 @@ document.addEventListener('DOMContentLoaded', async () => {
updateStatus('đ Initialisation des services...', 'loading');
updateProgress(10);
- let services; // Déclarer services au niveau supérieur
+ let services: any; // Déclarer services au niveau supérieur
try {
console.log('đ Importing services...');
@@ -76,7 +78,7 @@ document.addEventListener('DOMContentLoaded', async () => {
console.log('â
Services initialized successfully');
break;
} catch (error) {
- console.log(`âł Services not ready yet (attempt ${attempts + 1}/${maxAttempts}):`, error.message);
+ console.log(`âł Services not ready yet (attempt ${attempts + 1}/${maxAttempts}):`, error instanceof Error ? error.message : String(error));
// Diagnostic plus détaillé
if (attempts === 5) {
@@ -198,9 +200,9 @@ document.addEventListener('DOMContentLoaded', async () => {
console.log('đ Encrypted wallet data:', encryptedWallet);
// Ouvrir la base de données 4nk existante sans la modifier
- console.log('đ Opening IndexedDB database "4nk" version 2...');
+ console.log(`đ Opening IndexedDB database "${DATABASE_CONFIG.name}" version ${DATABASE_CONFIG.version}...`);
const db = await new Promise((resolve, reject) => {
- const request = indexedDB.open('4nk', 2); // Utiliser la version existante
+ const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version); // Utiliser la version centralisée
request.onerror = () => {
console.error('â Failed to open IndexedDB:', request.error);
reject(request.error);
@@ -227,37 +229,48 @@ document.addEventListener('DOMContentLoaded', async () => {
});
// Ătape 1: Sauvegarder le wallet dans le format attendu par getDeviceFromDatabase
- console.log('đ Opening transaction for wallet store...');
+ // Utiliser le SDK directement pour éviter le service worker qui bloque
+ console.log('đ§ Creating device using SDK...');
+
+ // 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
+ console.log('đ§ Creating new device with birthday 0...');
+ const spAddress = await services.sdkClient.create_new_device(0, 'signet');
+ console.log('â
Device created with address:', spAddress);
+
+ // Force wallet generation to ensure keys are created
+ console.log('đ§ Forcing wallet generation...');
+ try {
+ const wallet = await services.sdkClient.dump_wallet();
+ console.log('â
Wallet generated:', JSON.stringify(wallet));
+ } catch (walletError) {
+ console.warn('â ïž Wallet generation failed:', walletError);
+ }
+
+ // Récupérer le device créé par le SDK
+ const device = services.dumpDeviceFromMemory();
+ console.log('đ Device structure from SDK:', {
+ hasSpWallet: !!device.sp_wallet,
+ hasSpClient: !!device.sp_client,
+ spClientKeys: device.sp_client ? Object.keys(device.sp_client) : 'none'
+ });
+
+ console.log(`đ Opening transaction for ${DATABASE_CONFIG.stores.wallet.name} store...`);
await new Promise((resolve, reject) => {
- const transaction = db.transaction(['wallet'], 'readwrite');
- const store = transaction.objectStore('wallet');
+ const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readwrite');
+ const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name);
console.log('đ Store opened:', store.name);
- // Créer un device temporaire avec l'état birthday_waiting
- // Utiliser le wallet chiffré au lieu des données en clair
- const device = {
- sp_wallet: {
- // Stocker le wallet chiffré au lieu des clés en clair
- encrypted_data: encryptedWallet,
- network: walletData.network,
- birthday: 0, // Sera mis Ă jour dans birthday-setup
- last_scan: 0,
- state: 'birthday_waiting'
- },
- sp_client: {
- // Structure complÚte pour éviter l'erreur "missing field sp_client"
- initialized: false,
- // Ajouter d'autres champs nécessaires
- version: 1,
- capabilities: []
- },
- created_at: walletData.created_at
- };
-
- // Stocker dans le format attendu par getDeviceFromDatabase
+ // Stocker le wallet chiffré séparément et laisser le SDK gérer sp_wallet
const walletObject = {
pre_id: '1',
- device: device
+ device: device,
+ encrypted_wallet: encryptedWallet, // Stocker le wallet chiffré séparément
+ security_mode: currentMode // Stocker le mode de sécurité
};
console.log('đ Attempting to save wallet object:', walletObject);
@@ -290,8 +303,8 @@ document.addEventListener('DOMContentLoaded', async () => {
// Ătape 2: VĂ©rifier que le wallet est bien stockĂ© (nouvelle transaction)
await new Promise((resolve, reject) => {
- const transaction = db.transaction(['wallet'], 'readonly');
- const store = transaction.objectStore('wallet');
+ 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 = () => {
@@ -328,25 +341,26 @@ document.addEventListener('DOMContentLoaded', async () => {
// Vérification finale : s'assurer que le wallet est bien dans IndexedDB
console.log('đ Final verification: checking wallet in IndexedDB...');
const finalVerification = await new Promise((resolve, reject) => {
- const transaction = db.transaction(['wallet'], 'readonly');
- const store = transaction.objectStore('wallet');
+ 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 && finalVerification.device) {
- console.log('â
Wallet saved exclusively in IndexedDB and verified');
- console.log('đ Wallet contains:', {
- hasSpWallet: !!finalVerification.device.sp_wallet,
- hasSpClient: !!finalVerification.device.sp_client,
- state: finalVerification.device.sp_wallet?.state,
- hasEncryptedData: !!finalVerification.device.sp_wallet?.encrypted_data
- });
- } else {
- console.error('â Final wallet verification failed - wallet not found in IndexedDB');
- throw new Error('Wallet verification failed - wallet not found');
- }
+ if (finalVerification && finalVerification.device) {
+ console.log('â
Wallet saved exclusively in IndexedDB and verified');
+ console.log('đ Wallet contains:', {
+ hasSpWallet: !!finalVerification.device.sp_wallet,
+ hasSpClient: !!finalVerification.device.sp_client,
+ hasEncryptedWallet: !!finalVerification.encrypted_wallet,
+ securityMode: finalVerification.security_mode,
+ spClientKeys: finalVerification.device.sp_client ? Object.keys(finalVerification.device.sp_client) : 'none'
+ });
+ } else {
+ console.error('â Final wallet verification failed - wallet not found in IndexedDB');
+ throw new Error('Wallet verification failed - wallet not found');
+ }
} catch (error) {
console.error('â Error during wallet save:', error);
diff --git a/src/router.ts b/src/router.ts
index a7e56eb..2869a57 100755
--- a/src/router.ts
+++ b/src/router.ts
@@ -526,7 +526,7 @@ export async function registerAllListeners() {
await services.checkConnections(process, stateId);
- let res: Record = {};
+ const res: Record = {};
if (state) {
// Decrypt all the data we have the key for
for (const attribute of Object.keys(state.pcd_commitment)) {
@@ -621,7 +621,7 @@ export async function registerAllListeners() {
};
const handleGetPairingId = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.GET_PAIRING_ID) return;
+ if (event.data.type !== MessageType.GET_PAIRING_ID) {return;}
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
@@ -655,7 +655,7 @@ export async function registerAllListeners() {
};
const handleCreateProcess = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.CREATE_PROCESS) return;
+ if (event.data.type !== MessageType.CREATE_PROCESS) {return;}
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
@@ -704,7 +704,7 @@ export async function registerAllListeners() {
};
const handleNotifyUpdate = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.NOTIFY_UPDATE) return;
+ if (event.data.type !== MessageType.NOTIFY_UPDATE) {return;}
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
@@ -742,7 +742,7 @@ export async function registerAllListeners() {
};
const handleValidateState = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.VALIDATE_STATE) return;
+ if (event.data.type !== MessageType.VALIDATE_STATE) {return;}
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
@@ -777,7 +777,7 @@ export async function registerAllListeners() {
};
const handleUpdateProcess = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.UPDATE_PROCESS) return;
+ if (event.data.type !== MessageType.UPDATE_PROCESS) {return;}
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
@@ -861,7 +861,7 @@ export async function registerAllListeners() {
}
}
- if (privateData[field]) continue;
+ if (privateData[field]) {continue;}
// We've get back all the way to the first state without seeing it, it's a new public field
publicData[field] = newData[field];
@@ -888,7 +888,7 @@ export async function registerAllListeners() {
};
const handleDecodePublicData = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.DECODE_PUBLIC_DATA) return;
+ if (event.data.type !== MessageType.DECODE_PUBLIC_DATA) {return;}
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
@@ -922,7 +922,7 @@ export async function registerAllListeners() {
};
const handleHashValue = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.HASH_VALUE) return;
+ if (event.data.type !== MessageType.HASH_VALUE) {return;}
console.log('handleHashValue', event.data);
@@ -951,7 +951,7 @@ export async function registerAllListeners() {
};
const handleGetMerkleProof = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.GET_MERKLE_PROOF) return;
+ if (event.data.type !== MessageType.GET_MERKLE_PROOF) {return;}
try {
const { accessToken, processState, attributeName } = event.data;
@@ -978,7 +978,7 @@ export async function registerAllListeners() {
};
const handleValidateMerkleProof = async (event: MessageEvent) => {
- if (event.data.type !== MessageType.VALIDATE_MERKLE_PROOF) return;
+ if (event.data.type !== MessageType.VALIDATE_MERKLE_PROOF) {return;}
try {
const { accessToken, merkleProof, documentHash } = event.data;
@@ -1149,7 +1149,7 @@ async function handlePairing4WordsJoin(event: MessageEvent) {
async function cleanPage() {
const container = document.querySelector('#containerId');
- if (container) container.innerHTML = '';
+ if (container) {container.innerHTML = '';}
}
// Essential functions are now handled directly in the application
@@ -1191,7 +1191,7 @@ document.addEventListener('navigate', (e: Event) => {
const event = e as CustomEvent<{ page: string; processId?: string }>;
if (event.detail.page === 'chat') {
const container = document.querySelector('.container');
- if (container) container.innerHTML = '';
+ if (container) {container.innerHTML = '';}
//initChat();
diff --git a/src/service-workers/database.worker.js b/src/service-workers/database.worker.js
index cfa304e..91ca37c 100755
--- a/src/service-workers/database.worker.js
+++ b/src/service-workers/database.worker.js
@@ -101,9 +101,14 @@ async function scanMissingData(processesToScan) {
return Array.from(toDownload);
}
+const DATABASE_CONFIG = {
+ name: '4nk',
+ version: 3
+};
+
async function openDatabase() {
return new Promise((resolve, reject) => {
- const request = indexedDB.open('4nk', 1);
+ const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
request.onerror = event => {
reject(request.error);
};
@@ -115,6 +120,12 @@ async function openDatabase() {
if (!db.objectStoreNames.contains('wallet')) {
db.createObjectStore('wallet', { keyPath: 'pre_id' });
}
+ if (!db.objectStoreNames.contains('credentials')) {
+ db.createObjectStore('credentials');
+ }
+ if (!db.objectStoreNames.contains('pbkdf2keys')) {
+ db.createObjectStore('pbkdf2keys');
+ }
};
});
}
diff --git a/src/services/credentials/storage.service.ts b/src/services/credentials/storage.service.ts
index 9f5718a..0b91770 100644
--- a/src/services/credentials/storage.service.ts
+++ b/src/services/credentials/storage.service.ts
@@ -3,12 +3,13 @@
*/
import { secureLogger } from '../secure-logger';
import { CredentialData } from './types';
+import { DATABASE_CONFIG } from '../database-config';
export class StorageService {
private static instance: StorageService;
- private dbName = '4nk';
- private storeName = 'credentials'; // Store séparé pour les clés PBKDF2
- private dbVersion = 2;
+ private dbName = DATABASE_CONFIG.name;
+ private storeName = DATABASE_CONFIG.stores.credentials.name; // Store séparé pour les clés PBKDF2
+ private dbVersion = DATABASE_CONFIG.version;
private constructor() {}
@@ -150,20 +151,29 @@ export class StorageService {
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
- if (!db.objectStoreNames.contains(this.storeName)) {
- db.createObjectStore(this.storeName);
- secureLogger.info('IndexedDB upgrade needed, creating credentials store', {
- component: 'StorageService',
- operation: 'openDatabase'
- });
- }
- if (!db.objectStoreNames.contains('wallet')) {
- db.createObjectStore('wallet');
- secureLogger.info('IndexedDB upgrade needed, creating wallet store', {
- component: 'StorageService',
- operation: 'openDatabase'
- });
- }
+ console.log(`đ StorageService: Database upgrade needed for ${this.dbName} version ${this.dbVersion}`);
+
+ // Créer tous les stores définis dans DATABASE_CONFIG
+ Object.values(DATABASE_CONFIG.stores).forEach(storeConfig => {
+ if (!db.objectStoreNames.contains(storeConfig.name)) {
+ const options: IDBObjectStoreParameters = {};
+ if (storeConfig.keyPath) {
+ options.keyPath = storeConfig.keyPath;
+ }
+ if ('autoIncrement' in storeConfig && storeConfig.autoIncrement) {
+ options.autoIncrement = true;
+ }
+
+ const store = db.createObjectStore(storeConfig.name, options);
+
+ // Créer les index
+ storeConfig.indices.forEach(indexConfig => {
+ store.createIndex(indexConfig.name, indexConfig.keyPath, { unique: indexConfig.unique || false });
+ });
+
+ console.log(`â
Created store: ${storeConfig.name}`);
+ }
+ });
};
});
}
diff --git a/src/services/credentials/webauthn.service.ts b/src/services/credentials/webauthn.service.ts
index 814924e..f24a1d0 100644
--- a/src/services/credentials/webauthn.service.ts
+++ b/src/services/credentials/webauthn.service.ts
@@ -4,6 +4,7 @@
import { secureLogger } from '../secure-logger';
import { SecurityMode } from '../security-mode.service';
import { WebAuthnCredential, EncryptionResult } from './types';
+import { DATABASE_CONFIG } from '../database-config';
export class WebAuthnService {
private static instance: WebAuthnService;
@@ -18,35 +19,36 @@ export class WebAuthnService {
}
/**
- * Stocke une clé avec WebAuthn
+ * Stocke une clé PBKDF2 avec WebAuthn
+ * WebAuthn est utilisé uniquement pour l'authentification, pas pour le chiffrement
*/
async storeKeyWithWebAuthn(key: string, securityMode: SecurityMode): Promise {
try {
- secureLogger.info('Storing key with WebAuthn', {
+ secureLogger.info('Storing PBKDF2 key with WebAuthn encryption', {
component: 'WebAuthnService',
operation: 'storeKeyWithWebAuthn',
securityMode
});
- // Créer des credentials WebAuthn pour stocker la clé
- const credential = await this.createCredentials('4nk-secure-password', securityMode);
+ // Créer des credentials WebAuthn pour l'authentification
+ const credential = await this.createCredentials('4nk-pbkdf2-key', securityMode);
- // Stocker la clé chiffrée avec les credentials WebAuthn
+ // Chiffrer la clé PBKDF2 avec la réponse WebAuthn
const encryptedKey = await this.encryptKeyWithWebAuthn(key, credential);
- // Sauvegarder dans IndexedDB
- await this.saveEncryptedKey(encryptedKey, credential.id, securityMode);
+ // Stocker la clé PBKDF2 chiffrée dans IndexedDB
+ // La clé est stockée avec le securityMode comme clé
+ await this.savePBKDF2Key(encryptedKey, credential.id, securityMode);
- secureLogger.info('Key stored with WebAuthn successfully', {
+ secureLogger.info('PBKDF2 key encrypted and stored with WebAuthn successfully', {
component: 'WebAuthnService',
operation: 'storeKeyWithWebAuthn'
});
} catch (error) {
- secureLogger.error('Failed to store key with WebAuthn', {
+ secureLogger.error('Failed to store PBKDF2 key with WebAuthn', error as Error, {
component: 'WebAuthnService',
- operation: 'storeKeyWithWebAuthn',
- error: error instanceof Error ? error.message : String(error)
+ operation: 'storeKeyWithWebAuthn'
});
throw error;
}
@@ -197,16 +199,18 @@ export class WebAuthnService {
console.log('đ WebAuthn credential created successfully:', credential);
+ const response = credential.response as AuthenticatorAttestationResponse;
return {
id: Array.from(new Uint8Array(credential.rawId)).map(b => b.toString(16).padStart(2, '0')).join(''),
- publicKey: Array.from(new Uint8Array((credential.response as AuthenticatorAttestationResponse).publicKey!))
+ publicKey: Array.from(new Uint8Array(response.getPublicKey()!))
};
} catch (error) {
console.error('â WebAuthn credential creation failed:', error);
// Message d'erreur spécifique pour Proton Pass
if (mode === 'proton-pass') {
- throw new Error(`Proton Pass authentication failed: ${error.message}. Please ensure Proton Pass is installed and enabled.`);
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ throw new Error(`Proton Pass authentication failed: ${errorMessage}. Please ensure Proton Pass is installed and enabled.`);
}
throw error;
@@ -229,10 +233,9 @@ export class WebAuthnService {
console.log('đ Key encrypted with WebAuthn credential');
return encryptedKey;
} catch (error) {
- secureLogger.error('Failed to encrypt key with WebAuthn', {
+ secureLogger.error('Failed to encrypt key with WebAuthn', error as Error, {
component: 'WebAuthnService',
- operation: 'encryptKeyWithWebAuthn',
- error: error instanceof Error ? error.message : String(error)
+ operation: 'encryptKeyWithWebAuthn'
});
throw error;
}
@@ -241,31 +244,34 @@ export class WebAuthnService {
/**
* Sauvegarde une clé chiffrée dans IndexedDB
*/
- private async saveEncryptedKey(encryptedKey: string, credentialId: string, securityMode: SecurityMode): Promise {
+ /**
+ * Sauvegarde la clé PBKDF2 chiffrée dans IndexedDB
+ * NE PAS stocker credentialId avec la clé chiffrée
+ */
+ private async savePBKDF2Key(encryptedKey: string, _credentialId: string, securityMode: SecurityMode): Promise {
try {
const db = await this.openDatabase();
- const transaction = db.transaction(['webauthn-keys'], 'readwrite');
- const store = transaction.objectStore('webauthn-keys');
+ console.log(`đ Available stores in ${DATABASE_CONFIG.name}:`, Array.from(db.objectStoreNames));
+ const transaction = db.transaction([DATABASE_CONFIG.stores.pbkdf2keys.name], 'readwrite');
+ const store = transaction.objectStore(DATABASE_CONFIG.stores.pbkdf2keys.name);
await new Promise((resolve, reject) => {
- // Stocker seulement la clĂ© chiffrĂ©e (pas de credentialId au mĂȘme endroit)
+ // Utiliser le securityMode comme clé d'enregistrement
+ // NE PAS stocker credentialId avec la clé chiffrée
const request = store.put({
- encryptedKey,
- securityMode: securityMode, // Mode de sécurité dynamique
+ encryptedKey, // Clé PBKDF2 chiffrée avec WebAuthn
timestamp: Date.now()
- }, 'pbkdf2-key');
+ }, securityMode);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
- // Ne pas stocker credentialId - il sera récupéré dynamiquement via WebAuthn
- console.log('đ CredentialId will be retrieved dynamically via WebAuthn');
+ console.log(`đ PBKDF2 key stored with security mode: ${securityMode}`);
} catch (error) {
- secureLogger.error('Failed to save encrypted key', {
+ secureLogger.error('Failed to save PBKDF2 key', error as Error, {
component: 'WebAuthnService',
- operation: 'saveEncryptedKey',
- error: error instanceof Error ? error.message : String(error)
+ operation: 'savePBKDF2Key'
});
throw error;
}
@@ -273,46 +279,88 @@ export class WebAuthnService {
/**
- * RécupÚre une clé chiffrée avec WebAuthn (récupération dynamique)
+ * RécupÚre et déchiffre la clé PBKDF2 avec WebAuthn
*/
- async retrieveKeyWithWebAuthn(): Promise {
+ async retrieveKeyWithWebAuthn(securityMode: SecurityMode): Promise {
try {
- // Récupérer la clé chiffrée depuis IndexedDB
+ // Récupérer la clé PBKDF2 chiffrée depuis IndexedDB avec le securityMode comme clé
const db = await this.openDatabase();
- const transaction = db.transaction(['webauthn-keys'], 'readonly');
- const store = transaction.objectStore('webauthn-keys');
+ const transaction = db.transaction([DATABASE_CONFIG.stores.pbkdf2keys.name], 'readonly');
+ const store = transaction.objectStore(DATABASE_CONFIG.stores.pbkdf2keys.name);
const result = await new Promise((resolve, reject) => {
- const request = store.get('pbkdf2-key');
+ const request = store.get(securityMode);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
if (!result || !result.encryptedKey) {
- console.log('đ No encrypted key found in WebAuthnKeysDB');
+ console.log(`đ No PBKDF2 key found for security mode: ${securityMode}`);
return null;
}
- // Récupérer credentialId dynamiquement via WebAuthn
+ // Récupérer le credentialId dynamiquement via WebAuthn
const credentialId = await this.getCurrentCredentialId();
if (!credentialId) {
console.log('đ No WebAuthn credential available');
return null;
}
- // Déchiffrer la clé avec credentialId
- const { EncryptionService } = await import('./encryption.service');
- const encryptionService = EncryptionService.getInstance();
+ // Déchiffrer la clé avec le credentialId WebAuthn
+ const encrypted = atob(result.encryptedKey);
+ const combined = new Uint8Array(encrypted.length);
+ for (let i = 0; i < encrypted.length; i++) {
+ combined[i] = encrypted.charCodeAt(i);
+ }
- const decryptedKey = await encryptionService.decryptWithPassword(result.encryptedKey, credentialId);
- console.log('đ Key decrypted with WebAuthn credential');
+ // Extraire salt (16 bytes), iv (12 bytes) et données chiffrées
+ const salt = combined.slice(0, 16);
+ const iv = combined.slice(16, 28);
+ const encryptedData = combined.slice(28);
+
+ // Dériver la clé avec PBKDF2
+ const keyMaterial = await crypto.subtle.importKey(
+ 'raw',
+ new TextEncoder().encode(credentialId),
+ 'PBKDF2',
+ false,
+ ['deriveBits']
+ );
+
+ const derivedKey = await crypto.subtle.deriveBits(
+ {
+ name: 'PBKDF2',
+ salt: salt,
+ iterations: 100000,
+ hash: 'SHA-256'
+ },
+ keyMaterial,
+ 256
+ );
+
+ const cryptoKey = await crypto.subtle.importKey(
+ 'raw',
+ derivedKey,
+ { name: 'AES-GCM' },
+ false,
+ ['decrypt']
+ );
+
+ // Déchiffrer
+ const decrypted = await crypto.subtle.decrypt(
+ { name: 'AES-GCM', iv: iv },
+ cryptoKey,
+ encryptedData
+ );
+
+ const decryptedKey = new TextDecoder().decode(decrypted);
+ console.log('đ PBKDF2 key decrypted with WebAuthn');
return decryptedKey;
} catch (error) {
- secureLogger.error('Failed to retrieve key with WebAuthn', {
+ secureLogger.error('Failed to retrieve PBKDF2 key with WebAuthn', error as Error, {
component: 'WebAuthnService',
- operation: 'retrieveKeyWithWebAuthn',
- error: error instanceof Error ? error.message : String(error)
+ operation: 'retrieveKeyWithWebAuthn'
});
return null;
}
@@ -348,10 +396,10 @@ export class WebAuthnService {
*/
private async openDatabase(): Promise {
return new Promise((resolve, reject) => {
- const request = indexedDB.open('WebAuthnKeysDB', 1);
+ const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
request.onerror = () => {
- secureLogger.error('Failed to open WebAuthn database', new Error('Database open failed'), {
+ secureLogger.error('Failed to open database', new Error('Database open failed'), {
component: 'WebAuthnService',
operation: 'openDatabase'
});
@@ -364,8 +412,12 @@ export class WebAuthnService {
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
- if (!db.objectStoreNames.contains('webauthn-keys')) {
- db.createObjectStore('webauthn-keys');
+ console.log(`đ ${DATABASE_CONFIG.name} database upgrade needed, creating stores...`);
+ if (!db.objectStoreNames.contains(DATABASE_CONFIG.stores.pbkdf2keys.name)) {
+ db.createObjectStore(DATABASE_CONFIG.stores.pbkdf2keys.name);
+ console.log(`â
${DATABASE_CONFIG.stores.pbkdf2keys.name} store created`);
+ } else {
+ console.log(`â
${DATABASE_CONFIG.stores.pbkdf2keys.name} store already exists`);
}
};
});
diff --git a/src/services/database-config.ts b/src/services/database-config.ts
new file mode 100644
index 0000000..cfef725
--- /dev/null
+++ b/src/services/database-config.ts
@@ -0,0 +1,111 @@
+/**
+ * Database Configuration - Configuration centralisée de la base de données IndexedDB
+ *
+ * Ce fichier centralise toutes les définitions de la base de données pour éviter
+ * les erreurs de version et d'incohérence entre les différents fichiers.
+ */
+
+export const DATABASE_CONFIG = {
+ name: '4nk',
+ version: 3,
+ stores: {
+ wallet: {
+ name: 'wallet',
+ keyPath: 'pre_id',
+ indices: []
+ },
+ credentials: {
+ name: 'credentials',
+ keyPath: null,
+ indices: []
+ },
+ pbkdf2keys: {
+ name: 'pbkdf2keys',
+ keyPath: null,
+ indices: []
+ },
+ labels: {
+ name: 'labels',
+ keyPath: 'emoji',
+ indices: []
+ },
+ processes: {
+ name: 'processes',
+ keyPath: null,
+ indices: []
+ },
+ shared_secrets: {
+ name: 'shared_secrets',
+ keyPath: null,
+ indices: []
+ },
+ unconfirmed_secrets: {
+ name: 'unconfirmed_secrets',
+ keyPath: null,
+ autoIncrement: true,
+ indices: []
+ },
+ diffs: {
+ name: 'diffs',
+ keyPath: 'value_commitment',
+ indices: [
+ { name: 'byStateId', keyPath: 'state_id', unique: false },
+ { name: 'byNeedValidation', keyPath: 'need_validation', unique: false },
+ { name: 'byStatus', keyPath: 'validation_status', unique: false }
+ ]
+ },
+ data: {
+ name: 'data',
+ keyPath: null,
+ indices: []
+ }
+ }
+} as const;
+
+/**
+ * Ouvrir la base de données IndexedDB avec la configuration centralisée
+ */
+export async function openDatabase(): Promise {
+ return new Promise((resolve, reject) => {
+ const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
+
+ request.onerror = () => {
+ console.error(`â Failed to open database ${DATABASE_CONFIG.name}:`, request.error);
+ reject(request.error);
+ };
+
+ request.onsuccess = () => {
+ console.log(`â
Database ${DATABASE_CONFIG.name} opened successfully`);
+ resolve(request.result);
+ };
+
+ request.onupgradeneeded = () => {
+ const db = request.result;
+ console.log(`đ Database upgrade needed for ${DATABASE_CONFIG.name} version ${DATABASE_CONFIG.version}`);
+
+ // Créer les stores manquants
+ Object.values(DATABASE_CONFIG.stores).forEach(storeConfig => {
+ if (!db.objectStoreNames.contains(storeConfig.name)) {
+ const options: IDBObjectStoreParameters = {};
+ if (storeConfig.keyPath) {
+ options.keyPath = storeConfig.keyPath;
+ }
+ if ('autoIncrement' in storeConfig && storeConfig.autoIncrement) {
+ options.autoIncrement = true;
+ }
+
+ const store = db.createObjectStore(storeConfig.name, options);
+
+ // Créer les index
+ storeConfig.indices.forEach(indexConfig => {
+ store.createIndex(indexConfig.name, indexConfig.keyPath, { unique: indexConfig.unique || false });
+ });
+
+ console.log(`â
Created store: ${storeConfig.name}`);
+ } else {
+ console.log(`â
Store already exists: ${storeConfig.name}`);
+ }
+ });
+ };
+ });
+}
diff --git a/src/services/database.service.ts b/src/services/database.service.ts
index 8281326..67e555b 100755
--- a/src/services/database.service.ts
+++ b/src/services/database.service.ts
@@ -1,10 +1,11 @@
import Services from './service';
+import { DATABASE_CONFIG } from './database-config';
export class Database {
private static instance: Database;
private db: IDBDatabase | null = null;
- private dbName: string = '4nk';
- private dbVersion: number = 2;
+ private dbName: string = DATABASE_CONFIG.name;
+ private dbVersion: number = DATABASE_CONFIG.version;
private serviceWorkerRegistration: ServiceWorkerRegistration | null = null;
private messageChannel: MessageChannel | null = null;
private messageChannelForGet: MessageChannel | null = null;
@@ -49,6 +50,11 @@ export class Database {
options: {},
indices: [],
},
+ AnkPBKDF2Keys: {
+ name: 'pbkdf2keys',
+ options: {},
+ indices: [],
+ },
};
// Private constructor to prevent direct instantiation from outside
diff --git a/src/services/event-bus.ts b/src/services/event-bus.ts
index 95aa394..0c964c2 100644
--- a/src/services/event-bus.ts
+++ b/src/services/event-bus.ts
@@ -36,7 +36,7 @@ export class EventBus {
* Ajoute un écouteur d'événement
*/
on(event: string, listener: EventListener): void {
- if (this.isDestroyed) return;
+ if (this.isDestroyed) {return;}
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
@@ -60,7 +60,7 @@ export class EventBus {
* Ajoute un écouteur d'événement qui ne se déclenche qu'une fois
*/
once(event: string, listener: EventListener): void {
- if (this.isDestroyed) return;
+ if (this.isDestroyed) {return;}
const onceListener = (data?: EventData) => {
listener(data);
@@ -74,10 +74,10 @@ export class EventBus {
* Supprime un écouteur d'événement
*/
off(event: string, listener: EventListener): void {
- if (this.isDestroyed) return;
+ if (this.isDestroyed) {return;}
const eventListeners = this.listeners.get(event);
- if (!eventListeners) return;
+ if (!eventListeners) {return;}
const index = eventListeners.indexOf(listener);
if (index > -1) {
@@ -94,10 +94,10 @@ export class EventBus {
* Ămet un Ă©vĂ©nement
*/
emit(event: string, data?: EventData): void {
- if (this.isDestroyed) return;
+ if (this.isDestroyed) {return;}
const eventListeners = this.listeners.get(event);
- if (!eventListeners || eventListeners.length === 0) return;
+ if (!eventListeners || eventListeners.length === 0) {return;}
// Créer une copie pour éviter les modifications pendant l'itération
const listeners = [...eventListeners];
@@ -115,10 +115,10 @@ export class EventBus {
* Ămet un Ă©vĂ©nement de maniĂšre asynchrone
*/
async emitAsync(event: string, data?: EventData): Promise {
- if (this.isDestroyed) return;
+ if (this.isDestroyed) {return;}
const eventListeners = this.listeners.get(event);
- if (!eventListeners || eventListeners.length === 0) return;
+ if (!eventListeners || eventListeners.length === 0) {return;}
const listeners = [...eventListeners];
const promises = listeners.map(listener => {
@@ -138,7 +138,7 @@ export class EventBus {
* Supprime tous les écouteurs d'un événement ou tous les écouteurs
*/
removeAllListeners(event?: string): void {
- if (this.isDestroyed) return;
+ if (this.isDestroyed) {return;}
if (event) {
this.listeners.delete(event);
@@ -191,7 +191,7 @@ export class EventBus {
* Nettoie les écouteurs orphelins
*/
cleanup(): void {
- if (this.isDestroyed) return;
+ if (this.isDestroyed) {return;}
// Supprimer les écouteurs qui ne sont plus dans les souscriptions
this.listeners.forEach((listeners, event) => {
diff --git a/src/services/memory-manager.ts b/src/services/memory-manager.ts
index 198e5f6..9484463 100644
--- a/src/services/memory-manager.ts
+++ b/src/services/memory-manager.ts
@@ -41,7 +41,7 @@ export class MemoryManager {
* Démarre le monitoring de la mémoire
*/
startMonitoring(): void {
- if (this.isMonitoring) return;
+ if (this.isMonitoring) {return;}
this.isMonitoring = true;
this.logMemoryStats();
@@ -92,10 +92,10 @@ export class MemoryManager {
*/
getCache(cacheName: string, key: string): T | null {
const cache = this.caches.get(cacheName);
- if (!cache) return null;
+ if (!cache) {return null;}
const entry = cache.get(key);
- if (!entry) return null;
+ if (!entry) {return null;}
// Vérifier l'ùge de l'entrée
if (Date.now() - entry.timestamp > this.maxCacheAge) {
@@ -115,7 +115,7 @@ export class MemoryManager {
*/
deleteCache(cacheName: string, key: string): boolean {
const cache = this.caches.get(cacheName);
- if (!cache) return false;
+ if (!cache) {return false;}
return cache.delete(key);
}
@@ -142,7 +142,7 @@ export class MemoryManager {
*/
getCacheStats(cacheName: string): { size: number; entries: any[] } {
const cache = this.caches.get(cacheName);
- if (!cache) return { size: 0, entries: [] };
+ if (!cache) {return { size: 0, entries: [] };}
const entries = Array.from(cache.entries()).map(([key, entry]) => ({
key,
@@ -162,7 +162,7 @@ export class MemoryManager {
*/
getMemoryStats(): MemoryStats | null {
const memory = (performance as any).memory;
- if (!memory) return null;
+ if (!memory) {return null;}
return {
usedJSHeapSize: memory.usedJSHeapSize,
@@ -177,7 +177,7 @@ export class MemoryManager {
*/
private checkMemoryUsage(): void {
const stats = this.getMemoryStats();
- if (!stats) return;
+ if (!stats) {return;}
if (stats.usedJSHeapSize > this.memoryThreshold) {
this.performMemoryCleanup();
@@ -226,7 +226,7 @@ export class MemoryManager {
*/
private evictLeastUsedEntries(): void {
this.caches.forEach(cache => {
- if (cache.size <= this.maxCacheSize) return;
+ if (cache.size <= this.maxCacheSize) {return;}
// Trier par nombre d'accĂšs et derniĂšre utilisation
const entries = Array.from(cache.entries()).sort((a, b) => {
diff --git a/src/services/modal.service.ts b/src/services/modal.service.ts
index 2706fb0..0aed620 100755
--- a/src/services/modal.service.ts
+++ b/src/services/modal.service.ts
@@ -37,9 +37,9 @@ export default class ModalService {
let html = modalHtml;
html = html.replace('{{device1}}', myAddress);
html = html.replace('{{device2}}', receiverAddress);
- if (container) container.innerHTML += html;
+ if (container) {container.innerHTML += html;}
const modal = document.getElementById('login-modal');
- if (modal) modal.style.display = 'flex';
+ if (modal) {modal.style.display = 'flex';}
const newScript = document.createElement('script');
newScript.setAttribute('type', 'module');
@@ -117,7 +117,7 @@ export default class ModalService {
// Modal injection methods removed - using confirmation modal instead
this.modal = document.getElementById('confirmation-modal');
- if (this.modal) this.modal.style.display = 'flex';
+ if (this.modal) {this.modal.style.display = 'flex';}
// Close modal when clicking outside of it
window.onclick = event => {
@@ -130,7 +130,7 @@ export default class ModalService {
console.log('=============> Confirm Login');
}
async closeLoginModal() {
- if (this.modal) this.modal.style.display = 'none';
+ if (this.modal) {this.modal.style.display = 'none';}
}
async showConfirmationModal(
@@ -190,6 +190,6 @@ export default class ModalService {
async closeConfirmationModal() {
const service = await Services.getInstance();
await service.unpairDevice();
- if (this.modal) this.modal.style.display = 'none';
+ if (this.modal) {this.modal.style.display = 'none';}
}
}
diff --git a/src/services/pairing.service.ts b/src/services/pairing.service.ts
index 5c80927..10eb0a0 100644
--- a/src/services/pairing.service.ts
+++ b/src/services/pairing.service.ts
@@ -151,7 +151,7 @@ export class PairingService {
async generatePairingWords(): Promise {
try {
const device = await this.deviceRepo.getDevice();
- if (!device || !device.sp_wallet?.address) {
+ if (!device?.sp_wallet?.address) {
throw new Error('No device address found');
}
diff --git a/src/services/performance-monitor.ts b/src/services/performance-monitor.ts
index 743e71c..89d811c 100644
--- a/src/services/performance-monitor.ts
+++ b/src/services/performance-monitor.ts
@@ -51,7 +51,7 @@ export class PerformanceMonitor {
* Démarre le monitoring des performances
*/
startMonitoring(): void {
- if (this.isMonitoring) return;
+ if (this.isMonitoring) {return;}
this.isMonitoring = true;
console.log('đ Performance monitoring started');
@@ -73,7 +73,7 @@ export class PerformanceMonitor {
recordMetric(name: string, value: number, unit: string = 'ms'): void {
// Use unit parameter
console.log('Performance metric unit:', unit);
- if (!this.isMonitoring) return;
+ if (!this.isMonitoring) {return;}
if (!this.metrics.has(name)) {
this.metrics.set(name, []);
@@ -130,7 +130,7 @@ export class PerformanceMonitor {
*/
getMetricStats(name: string): PerformanceStats | null {
const values = this.metrics.get(name);
- if (!values || values.length === 0) return null;
+ if (!values || values.length === 0) {return null;}
const sorted = [...values].sort((a, b) => a - b);
const sum = values.reduce((acc, val) => acc + val, 0);
@@ -173,7 +173,7 @@ export class PerformanceMonitor {
// Analyser chaque métrique
Object.entries(metrics).forEach(([name, stats]) => {
const threshold = this.thresholds[name];
- if (!threshold) return;
+ if (!threshold) {return;}
const percentage = (stats.average / threshold.critical) * 100;
healthScore -= Math.max(0, 100 - percentage);
@@ -217,7 +217,7 @@ export class PerformanceMonitor {
*/
private checkThresholds(name: string, value: number): void {
const threshold = this.thresholds[name];
- if (!threshold) return;
+ if (!threshold) {return;}
if (value > threshold.critical) {
console.warn(`đš Critical performance threshold exceeded for ${name}: ${value}ms`);
@@ -230,7 +230,7 @@ export class PerformanceMonitor {
* Configure les observateurs de performance
*/
private setupPerformanceObservers(): void {
- if (!window.PerformanceObserver) return;
+ if (!window.PerformanceObserver) {return;}
// Observer les mesures de performance
const measureObserver = new PerformanceObserver((list) => {
diff --git a/src/services/secure-credentials.service.ts b/src/services/secure-credentials.service.ts
index f5472a1..26d70eb 100644
--- a/src/services/secure-credentials.service.ts
+++ b/src/services/secure-credentials.service.ts
@@ -110,8 +110,8 @@ export class SecureCredentialsService {
switch (securityMode) {
case 'proton-pass':
case 'os':
- // Récupérer la clé chiffrée avec WebAuthn (récupération dynamique)
- return await webAuthnService.retrieveKeyWithWebAuthn();
+ // Récupérer la clé chiffrée avec WebAuthn
+ return await webAuthnService.retrieveKeyWithWebAuthn(securityMode);
case 'browser':
// Récupérer la clé du gestionnaire de mots de passe
diff --git a/src/services/secure-key-manager.ts b/src/services/secure-key-manager.ts
index fecb54f..6f65b29 100644
--- a/src/services/secure-key-manager.ts
+++ b/src/services/secure-key-manager.ts
@@ -65,7 +65,7 @@ export class SecureKeyManager {
async getPrivateKey(password: string): Promise {
try {
const encryptedData = await this.getEncryptedData();
- if (!encryptedData) return null;
+ if (!encryptedData) {return null;}
const derivedKey = await this.deriveKey(password);
const iv = encryptedData.slice(0, 12);
diff --git a/src/services/secure-logger.ts b/src/services/secure-logger.ts
index 88f68cc..0ffc4fe 100644
--- a/src/services/secure-logger.ts
+++ b/src/services/secure-logger.ts
@@ -91,7 +91,7 @@ export class SecureLogger {
* Sanitise le contexte pour supprimer les données sensibles
*/
private sanitizeContext(context?: LogContext): LogContext | undefined {
- if (!context) return undefined;
+ if (!context) {return undefined;}
const sanitized: LogContext = {};
const sensitiveKeys = [
diff --git a/src/services/security-mode.service.ts b/src/services/security-mode.service.ts
index 122ecd4..164f567 100644
--- a/src/services/security-mode.service.ts
+++ b/src/services/security-mode.service.ts
@@ -1,10 +1,10 @@
/**
* SecurityModeService - Gestion des modes de sécurisation
* GÚre le stockage et la récupération du mode de sécurisation choisi par l'utilisateur
+ * NOTE: Le mode de sécurité est stocké uniquement avec la clé PBKDF2, pas dans un store séparé
*/
import { secureLogger } from './secure-logger';
-import Database from './database.service';
export type SecurityMode = 'proton-pass' | 'os' | 'browser' | 'otp' | '2fa' | 'password' | 'none';
@@ -25,11 +25,10 @@ export interface SecurityModeConfig {
export class SecurityModeService {
private static instance: SecurityModeService;
- private database: Database;
private currentMode: SecurityMode | null = null;
private constructor() {
- this.database = new Database();
+ // N'instancier pas Database ici, il sera récupéré via getInstance() quand nécessaire
}
public static getInstance(): SecurityModeService {
@@ -41,78 +40,32 @@ export class SecurityModeService {
/**
* RécupÚre le mode de sécurisation actuel
+ * NOTE: Le mode est stocké uniquement en mémoire, dérivé de la clé PBKDF2 stockée
*/
public async getCurrentMode(): Promise {
if (this.currentMode) {
return this.currentMode;
}
- try {
- // Vérifier que la base de données est disponible
- if (!this.database || typeof this.database.getObject !== 'function') {
- secureLogger.warn('Database not available, returning null mode', {
- component: 'SecurityModeService',
- operation: 'getCurrentMode'
- });
- return null;
- }
-
- const storedMode = await this.database.getObject('security_settings', 'current_mode');
- this.currentMode = storedMode?.mode || null;
-
- secureLogger.info('Current security mode retrieved', {
- component: 'SecurityModeService',
- operation: 'getCurrentMode',
- mode: this.currentMode
- });
-
- return this.currentMode;
- } catch (error) {
- // Si l'erreur est "object store not found", c'est normal pour un premier lancement
- if (error instanceof Error && (error.name === 'NotFoundError' || error.message.includes('object stores was not found'))) {
- secureLogger.info('No security mode set yet (first launch)', {
- component: 'SecurityModeService',
- operation: 'getCurrentMode'
- });
- return null;
- }
-
- secureLogger.error('Failed to retrieve current security mode', error as Error, {
- component: 'SecurityModeService',
- operation: 'getCurrentMode'
- });
- return null;
- }
+ // Le mode de sécurité n'est pas stocké en base, il est dérivé de la clé PBKDF2
+ // Ici on retourne null si aucun mode n'est en mémoire
+ // Le mode sera défini lors de la génération de la clé PBKDF2
+ secureLogger.info('No security mode in memory (will be set when PBKDF2 key is generated)', {
+ component: 'SecurityModeService',
+ operation: 'getCurrentMode'
+ });
+ return null;
}
/**
* Définit le mode de sécurisation
+ * NOTE: Le mode de sécurité est stocké uniquement avec la clé PBKDF2, pas dans un store séparé
*/
public async setSecurityMode(mode: SecurityMode): Promise {
try {
const modeConfig = this.getSecurityModeConfig(mode);
- // Vérifier que la base de données est disponible
- if (!this.database || typeof this.database.setObject !== 'function') {
- secureLogger.warn('Database not available, setting mode in memory only', {
- component: 'SecurityModeService',
- operation: 'setSecurityMode',
- mode
- });
- this.currentMode = mode;
- return;
- }
-
- // Stocker le mode en base
- await this.database.setObject('security_settings', 'current_mode', {
- mode,
- name: modeConfig.name,
- description: modeConfig.description,
- securityLevel: modeConfig.securityLevel,
- timestamp: Date.now(),
- implementation: modeConfig.implementation
- });
-
+ // Stocker uniquement en mémoire
this.currentMode = mode;
secureLogger.info('Security mode set successfully', {
@@ -133,13 +86,8 @@ export class SecurityModeService {
operation: 'setSecurityMode',
mode
});
- // En cas d'erreur, définir le mode en mémoire seulement
- this.currentMode = mode;
- secureLogger.warn('Security mode set in memory only due to database error', {
- component: 'SecurityModeService',
- operation: 'setSecurityMode',
- mode
- });
+ // NE PAS FAIRE DE FALLBACK - échouer complÚtement
+ throw error;
}
}
@@ -286,7 +234,7 @@ export class SecurityModeService {
*/
public async isCurrentModeSecure(): Promise {
const currentMode = await this.getCurrentMode();
- if (!currentMode) return false;
+ if (!currentMode) {return false;}
const config = this.getSecurityModeConfig(currentMode);
return config.securityLevel === 'high' || config.securityLevel === 'medium';
@@ -350,7 +298,8 @@ export class SecurityModeService {
*/
public async resetSecurityMode(): Promise {
try {
- await this.database.deleteObject('security_settings', 'current_mode');
+ const database = await Database.getInstance();
+ await database.deleteObject('security_settings', 'current_mode');
this.currentMode = null;
secureLogger.info('Security mode reset', {
@@ -371,7 +320,8 @@ export class SecurityModeService {
*/
public async getSecurityModeHistory(): Promise {
try {
- const history = await this.database.getObject('security_settings', 'mode_history') || [];
+ const database = await Database.getInstance();
+ const history = await database.getObject('security_settings', 'mode_history') || [];
return history;
} catch (error) {
secureLogger.error('Failed to retrieve security mode history', error as Error, {
@@ -399,7 +349,12 @@ export class SecurityModeService {
history.splice(10);
}
- await this.database.setObject('security_settings', 'mode_history', history);
+ const database = await Database.getInstance();
+ await database.addObject({
+ storeName: 'security_settings',
+ key: 'mode_history',
+ object: history
+ });
} catch (error) {
secureLogger.error('Failed to add to security mode history', error as Error, {
component: 'SecurityModeService',
diff --git a/src/services/service.ts b/src/services/service.ts
index 0f7fb76..7f75784 100755
--- a/src/services/service.ts
+++ b/src/services/service.ts
@@ -448,7 +448,7 @@ export default class Services {
// Use processId parameter
console.log('Getting cached process:', processId);
const process = this.processesCache[processId];
- if (!process) return null;
+ if (!process) {return null;}
const now = Date.now();
if (now - (process as any).timestamp > this.cacheExpiry) {
@@ -704,7 +704,7 @@ export default class Services {
if (!roles) {
throw new Error('No roles found');
}
- let members: Set = new Set();
+ const members: Set = new Set();
for (const role of Object.values(roles!)) {
for (const member of role.members) {
// Check if we know the member that matches this id
@@ -726,7 +726,7 @@ export default class Services {
}
// If pairedAddresses is not in the current state, look in previous states
- if (!publicData || !publicData['pairedAddresses']) {
+ if (!publicData?.['pairedAddresses']) {
// Look for pairedAddresses in previous states
for (let i = process.states.length - 1; i >= 0; i--) {
const state = process.states[i];
@@ -737,7 +737,7 @@ export default class Services {
}
}
- if (!publicData || !publicData['pairedAddresses']) {
+ if (!publicData?.['pairedAddresses']) {
throw new Error('Not a pairing process');
}
const decodedAddresses = this.decodeValue(publicData['pairedAddresses']);
@@ -749,14 +749,14 @@ export default class Services {
// Ensure the amount is available before proceeding
await this.getTokensFromFaucet();
- let unconnectedAddresses = new Set();
+ const unconnectedAddresses = new Set();
const myAddress = this.getDeviceAddress();
for (const member of Array.from(members)) {
const sp_addresses = member.sp_addresses;
- if (!sp_addresses || sp_addresses.length === 0) continue;
+ if (!sp_addresses || sp_addresses.length === 0) {continue;}
for (const address of sp_addresses) {
// For now, we ignore our own device address, although there might be use cases for having a secret with ourselves
- if (address === myAddress) continue;
+ if (address === myAddress) {continue;}
if ((await this.getSecretForAddress(address)) === null) {
unconnectedAddresses.add(address);
}
@@ -1271,7 +1271,7 @@ export default class Services {
try {
// Get current device to check birthday
const device = await this.getDeviceFromDatabase();
- if (device && device.sp_wallet) {
+ if (device?.sp_wallet) {
console.log('đ Device wallet state:', {
birthday: device.sp_wallet.birthday,
last_scan: device.sp_wallet.last_scan,
@@ -1332,7 +1332,7 @@ export default class Services {
// Check wallet state in SDK
try {
const device = await this.getDeviceFromDatabase();
- if (device && device.sp_wallet) {
+ if (device?.sp_wallet) {
console.log('đ Wallet state:');
console.log('- Last scan:', device.sp_wallet.last_scan);
console.log('- Current block:', this.currentBlockHeight);
@@ -1719,7 +1719,7 @@ export default class Services {
private ensureWalletKeysAvailable(): void {
try {
const device = this.dumpDeviceFromMemory();
- if (!device || !device.sp_wallet) {
+ if (!device?.sp_wallet) {
throw new Error('â Wallet not initialized - WebAuthn decryption required');
}
@@ -1749,7 +1749,7 @@ export default class Services {
// Additional debugging: check wallet state
try {
const device = this.dumpDeviceFromMemory();
- if (device && device.sp_wallet) {
+ if (device?.sp_wallet) {
console.log(`đ Wallet debugging info:`, {
birthday: device.sp_wallet.birthday,
last_scan: device.sp_wallet.last_scan,
@@ -2021,7 +2021,7 @@ export default class Services {
try {
const device = await this.getDeviceFromDatabase();
- if (!device || !device.sp_wallet) {
+ if (!device?.sp_wallet) {
throw new Error('Device not found or wallet not initialized');
}
@@ -2600,14 +2600,14 @@ export default class Services {
const existing = await this.getProcess(processId);
if (existing) {
// Look for state id we don't know yet
- let newStates: string[] = [];
- let newRoles: Record[] = [];
+ const newStates: string[] = [];
+ const newRoles: Record[] = [];
if (!Array.isArray(process.states)) {
console.warn('process.states is not an array:', process.states);
continue;
}
for (const state of process.states) {
- if (!state || !state.state_id) {
+ if (!state?.state_id) {
continue;
} // shouldn't happen
if (state.state_id === EMPTY32BYTES) {
@@ -2944,7 +2944,7 @@ export default class Services {
}
public getLastCommitedState(process: Process): ProcessState | null {
- if (process.states.length === 0) return null;
+ if (process.states.length === 0) {return null;}
const processTip = process.states[process.states.length - 1].commited_in;
const lastCommitedState = process.states.findLast(state => state.commited_in !== processTip);
if (lastCommitedState) {
@@ -2955,7 +2955,7 @@ export default class Services {
}
public getLastCommitedStateIndex(process: Process): number | null {
- if (process.states.length === 0) return null;
+ if (process.states.length === 0) {return null;}
const processTip = process.states[process.states.length - 1].commited_in;
for (let i = process.states.length - 1; i >= 0; i--) {
if (process.states[i].commited_in !== processTip) {
@@ -2966,14 +2966,14 @@ export default class Services {
}
public getUncommitedStates(process: Process): ProcessState[] {
- if (process.states.length === 0) return [];
+ if (process.states.length === 0) {return [];}
const processTip = process.states[process.states.length - 1].commited_in;
const res = process.states.filter(state => state.commited_in === processTip);
return res.filter(state => state.state_id !== EMPTY32BYTES);
}
public getStateFromId(process: Process, stateId: string): ProcessState | null {
- if (process.states.length === 0) return null;
+ if (process.states.length === 0) {return null;}
const state = process.states.find(state => state.state_id === stateId);
if (state) {
return state;
@@ -2983,7 +2983,7 @@ export default class Services {
}
public getNextStateAfterId(process: Process, stateId: string): ProcessState | null {
- if (process.states.length === 0) return null;
+ if (process.states.length === 0) {return null;}
const index = process.states.findIndex(state => state.state_id === stateId);
diff --git a/src/services/websocket-manager.ts b/src/services/websocket-manager.ts
index 1277b0a..61894b7 100644
--- a/src/services/websocket-manager.ts
+++ b/src/services/websocket-manager.ts
@@ -182,7 +182,7 @@ export class WebSocketManager {
* Configure les écouteurs d'événements WebSocket
*/
private setupEventListeners(resolve: Function, _reject: Function): void {
- if (!this.ws) return;
+ if (!this.ws) {return;}
this.ws.onopen = () => {
this.status.isConnected = true;
diff --git a/src/utils/logger.ts b/src/utils/logger.ts
index 515c9d2..3754e12 100644
--- a/src/utils/logger.ts
+++ b/src/utils/logger.ts
@@ -63,7 +63,7 @@ class Logger {
}
private log(level: LogLevel, message: string, context?: LogContext): void {
- if (!this.shouldLog(level)) return;
+ if (!this.shouldLog(level)) {return;}
const entry = this.createLogEntry(level, message, context);
this.logs.push(entry);
diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts
index 3d19edb..ebba5f8 100755
--- a/src/utils/sp-address.utils.ts
+++ b/src/utils/sp-address.utils.ts
@@ -2344,11 +2344,11 @@ function showLoadingState() {
// Update loading message
const loadingText = loadingFlow.querySelector('h2');
const loadingDesc = loadingFlow.querySelector('p');
- if (loadingText) loadingText.textContent = 'Initializing...';
- if (loadingDesc) loadingDesc.textContent = 'Setting up secure pairing interface';
+ if (loadingText) {loadingText.textContent = 'Initializing...';}
+ if (loadingDesc) {loadingDesc.textContent = 'Setting up secure pairing interface';}
}
- if (creatorFlow) (creatorFlow as HTMLElement).style.display = 'none';
- if (joinerFlow) (joinerFlow as HTMLElement).style.display = 'none';
+ if (creatorFlow) {(creatorFlow as HTMLElement).style.display = 'none';}
+ if (joinerFlow) {(joinerFlow as HTMLElement).style.display = 'none';}
}
// Detect flow and show appropriate interface
@@ -2364,16 +2364,16 @@ async function detectAndShowFlow() {
if (hasWords) {
// Joiner flow
- if (loadingFlow) (loadingFlow as HTMLElement).style.display = 'none';
- if (creatorFlow) (creatorFlow as HTMLElement).style.display = 'none';
- if (joinerFlow) (joinerFlow as HTMLElement).style.display = 'block';
+ if (loadingFlow) {(loadingFlow as HTMLElement).style.display = 'none';}
+ if (creatorFlow) {(creatorFlow as HTMLElement).style.display = 'none';}
+ if (joinerFlow) {(joinerFlow as HTMLElement).style.display = 'block';}
updateJoinerStatus('Ready to join pairing');
} else {
// Creator flow
- if (loadingFlow) (loadingFlow as HTMLElement).style.display = 'none';
- if (creatorFlow) (creatorFlow as HTMLElement).style.display = 'block';
- if (joinerFlow) (joinerFlow as HTMLElement).style.display = 'none';
+ if (loadingFlow) {(loadingFlow as HTMLElement).style.display = 'none';}
+ if (creatorFlow) {(creatorFlow as HTMLElement).style.display = 'block';}
+ if (joinerFlow) {(joinerFlow as HTMLElement).style.display = 'none';}
updateCreatorStatus('Ready to create pairing');
}
diff --git a/src/utils/subscription.utils.ts b/src/utils/subscription.utils.ts
index 1a34143..8579ef3 100755
--- a/src/utils/subscription.utils.ts
+++ b/src/utils/subscription.utils.ts
@@ -21,7 +21,7 @@ export function addSubscription(
event: any,
eventHandler: EventListenerOrEventListenerObject
): void {
- if (!element) return;
+ if (!element) {return;}
subscriptions.push({ element, event, eventHandler });
element.addEventListener(event, eventHandler);
}
diff --git a/src/websockets.ts b/src/websockets.ts
index fc2c6a5..e4d4dcc 100755
--- a/src/websockets.ts
+++ b/src/websockets.ts
@@ -4,7 +4,7 @@ import { messageValidator } from './services/message-validator';
import { secureLogger } from './services/secure-logger';
let ws: WebSocket;
-let messageQueue: string[] = [];
+const messageQueue: string[] = [];
export async function initWebsocket(url: string) {
ws = new WebSocket(url);