**Motivations :** - Implémentation du système de sélection de mode de sécurité - Séparation claire entre les données de sécurité et les données du wallet - Suppression des duplications entre 'encrypted-pbkdf2-key' et 'pbkdf2-key' - Architecture modulaire pour la gestion des credentials **Modifications :** - Ajout du composant security-mode-selector pour la sélection du mode de sécurité - Création des pages séquentielles : security-setup, wallet-setup, birthday-setup - Implémentation des services de credentials (encryption, storage, webauthn) - Ajout du service security-mode pour la gestion des modes de sécurité - Correction du stockage des clés PBKDF2 avec le securityMode dynamique - Suppression des méthodes redondantes dans StorageService - Nettoyage des appels redondants dans secure-credentials.service.ts **Pages affectées :** - src/components/security-mode-selector/ (nouveau composant) - src/pages/security-setup/ (nouvelle page) - src/pages/wallet-setup/ (nouvelle page) - src/pages/birthday-setup/ (nouvelle page) - src/services/credentials/ (nouveaux services) - src/services/security-mode.service.ts (nouveau service) - src/services/secure-credentials.service.ts (modifié) - src/services/database.service.ts (modifié) - src/router.ts (modifié) - src/pages/home/home.ts (modifié)
412 lines
12 KiB
TypeScript
412 lines
12 KiB
TypeScript
/**
|
|
* 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
|
|
*/
|
|
|
|
import { secureLogger } from './secure-logger';
|
|
import Database from './database.service';
|
|
|
|
export type SecurityMode = 'proton-pass' | 'os' | 'browser' | 'otp' | '2fa' | 'password' | 'none';
|
|
|
|
export interface SecurityModeConfig {
|
|
mode: SecurityMode;
|
|
name: string;
|
|
description: string;
|
|
securityLevel: 'high' | 'medium' | 'low' | 'critical';
|
|
requiresConfirmation: boolean;
|
|
warnings: string[];
|
|
implementation: {
|
|
useWebAuthn: boolean;
|
|
useEncryption: boolean;
|
|
usePlatformAuth: boolean;
|
|
storageType: 'encrypted' | 'plain' | 'hybrid';
|
|
};
|
|
}
|
|
|
|
export class SecurityModeService {
|
|
private static instance: SecurityModeService;
|
|
private database: Database;
|
|
private currentMode: SecurityMode | null = null;
|
|
|
|
private constructor() {
|
|
this.database = new Database();
|
|
}
|
|
|
|
public static getInstance(): SecurityModeService {
|
|
if (!SecurityModeService.instance) {
|
|
SecurityModeService.instance = new SecurityModeService();
|
|
}
|
|
return SecurityModeService.instance;
|
|
}
|
|
|
|
/**
|
|
* Récupère le mode de sécurisation actuel
|
|
*/
|
|
public async getCurrentMode(): Promise<SecurityMode | null> {
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Définit le mode de sécurisation
|
|
*/
|
|
public async setSecurityMode(mode: SecurityMode): Promise<void> {
|
|
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
|
|
});
|
|
|
|
this.currentMode = mode;
|
|
|
|
secureLogger.info('Security mode set successfully', {
|
|
component: 'SecurityModeService',
|
|
operation: 'setSecurityMode',
|
|
mode,
|
|
securityLevel: modeConfig.securityLevel
|
|
});
|
|
|
|
// Émettre un événement pour notifier les autres services
|
|
window.dispatchEvent(new CustomEvent('securityModeChanged', {
|
|
detail: { mode, config: modeConfig }
|
|
}));
|
|
|
|
} catch (error) {
|
|
secureLogger.error('Failed to set security mode', error as Error, {
|
|
component: '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
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Récupère la configuration d'un mode de sécurisation
|
|
*/
|
|
public getSecurityModeConfig(mode: SecurityMode): SecurityModeConfig {
|
|
const configs: Record<SecurityMode, SecurityModeConfig> = {
|
|
'proton-pass': {
|
|
mode: 'proton-pass',
|
|
name: 'Proton Pass',
|
|
description: 'Utilise Proton Pass pour l\'authentification biométrique et la gestion des clés',
|
|
securityLevel: 'high',
|
|
requiresConfirmation: false,
|
|
warnings: [],
|
|
implementation: {
|
|
useWebAuthn: true,
|
|
useEncryption: true,
|
|
usePlatformAuth: true,
|
|
storageType: 'encrypted'
|
|
}
|
|
},
|
|
'os': {
|
|
mode: 'os',
|
|
name: 'Authentificateur OS',
|
|
description: 'Utilise l\'authentificateur intégré de votre système d\'exploitation',
|
|
securityLevel: 'high',
|
|
requiresConfirmation: false,
|
|
warnings: [],
|
|
implementation: {
|
|
useWebAuthn: true,
|
|
useEncryption: true,
|
|
usePlatformAuth: true,
|
|
storageType: 'encrypted'
|
|
}
|
|
},
|
|
'browser': {
|
|
mode: 'browser',
|
|
name: 'Navigateur',
|
|
description: 'Utilise les fonctionnalités de sécurité du navigateur',
|
|
securityLevel: 'medium',
|
|
requiresConfirmation: false,
|
|
warnings: [],
|
|
implementation: {
|
|
useWebAuthn: true,
|
|
useEncryption: true,
|
|
usePlatformAuth: false,
|
|
storageType: 'encrypted'
|
|
}
|
|
},
|
|
'otp': {
|
|
mode: 'otp',
|
|
name: 'OTP (Proton Pass Compatible)',
|
|
description: 'Utilise un code OTP généré par une application compatible (Proton Pass, Google Authenticator, etc.)',
|
|
securityLevel: 'high',
|
|
requiresConfirmation: false,
|
|
warnings: [],
|
|
implementation: {
|
|
useWebAuthn: false,
|
|
useEncryption: true,
|
|
usePlatformAuth: false,
|
|
storageType: 'encrypted',
|
|
requiresOTP: true
|
|
}
|
|
},
|
|
'2fa': {
|
|
mode: '2fa',
|
|
name: 'Application 2FA',
|
|
description: 'Stockage en clair avec authentification par application 2FA',
|
|
securityLevel: 'low',
|
|
requiresConfirmation: true,
|
|
warnings: [
|
|
'⚠️ Clés stockées en clair',
|
|
'⚠️ Risque de compromission',
|
|
'⚠️ Non recommandé pour des données sensibles'
|
|
],
|
|
implementation: {
|
|
useWebAuthn: false,
|
|
useEncryption: false,
|
|
usePlatformAuth: false,
|
|
storageType: 'plain'
|
|
}
|
|
},
|
|
'password': {
|
|
mode: 'password',
|
|
name: 'Mot de Passe (Non Sauvegardé)',
|
|
description: 'Vos clés sont chiffrées avec un mot de passe que vous devez saisir à chaque utilisation',
|
|
securityLevel: 'low',
|
|
requiresConfirmation: true,
|
|
warnings: [
|
|
'⚠️ Le mot de passe n\'est PAS sauvegardé',
|
|
'⚠️ NON récupérable en cas d\'oubli',
|
|
'⚠️ À saisir à chaque utilisation'
|
|
],
|
|
implementation: {
|
|
useWebAuthn: false,
|
|
useEncryption: true,
|
|
usePlatformAuth: false,
|
|
storageType: 'encrypted',
|
|
requiresPassword: true
|
|
}
|
|
},
|
|
'none': {
|
|
mode: 'none',
|
|
name: 'Aucune Sécurité',
|
|
description: 'Stockage en clair sans aucune protection',
|
|
securityLevel: 'critical',
|
|
requiresConfirmation: true,
|
|
warnings: [
|
|
'🚨 Clés stockées en clair',
|
|
'🚨 Accès non protégé',
|
|
'🚨 RISQUE ÉLEVÉ'
|
|
],
|
|
implementation: {
|
|
useWebAuthn: false,
|
|
useEncryption: false,
|
|
usePlatformAuth: false,
|
|
storageType: 'plain'
|
|
}
|
|
}
|
|
};
|
|
|
|
return configs[mode];
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un mode nécessite une confirmation
|
|
*/
|
|
public requiresConfirmation(mode: SecurityMode): boolean {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.requiresConfirmation;
|
|
}
|
|
|
|
/**
|
|
* Récupère les avertissements pour un mode
|
|
*/
|
|
public getWarnings(mode: SecurityMode): string[] {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.warnings;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si le mode actuel est sécurisé
|
|
*/
|
|
public async isCurrentModeSecure(): Promise<boolean> {
|
|
const currentMode = await this.getCurrentMode();
|
|
if (!currentMode) return false;
|
|
|
|
const config = this.getSecurityModeConfig(currentMode);
|
|
return config.securityLevel === 'high' || config.securityLevel === 'medium';
|
|
}
|
|
|
|
/**
|
|
* Récupère tous les modes disponibles
|
|
*/
|
|
public getAvailableModes(): SecurityMode[] {
|
|
return ['proton-pass', 'os', 'browser', '2fa', 'none'];
|
|
}
|
|
|
|
/**
|
|
* Récupère les modes recommandés (sécurisés)
|
|
*/
|
|
public getRecommendedModes(): SecurityMode[] {
|
|
return ['proton-pass', 'os', 'browser'];
|
|
}
|
|
|
|
/**
|
|
* Récupère les modes non recommandés (non sécurisés)
|
|
*/
|
|
public getNonRecommendedModes(): SecurityMode[] {
|
|
return ['2fa', 'none'];
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un mode utilise WebAuthn
|
|
*/
|
|
public usesWebAuthn(mode: SecurityMode): boolean {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.implementation.useWebAuthn;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un mode utilise le chiffrement
|
|
*/
|
|
public usesEncryption(mode: SecurityMode): boolean {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.implementation.useEncryption;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un mode utilise l'authentificateur de plateforme
|
|
*/
|
|
public usesPlatformAuth(mode: SecurityMode): boolean {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.implementation.usePlatformAuth;
|
|
}
|
|
|
|
/**
|
|
* Récupère le type de stockage pour un mode
|
|
*/
|
|
public getStorageType(mode: SecurityMode): 'encrypted' | 'plain' | 'hybrid' {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.implementation.storageType;
|
|
}
|
|
|
|
/**
|
|
* Réinitialise le mode de sécurisation
|
|
*/
|
|
public async resetSecurityMode(): Promise<void> {
|
|
try {
|
|
await this.database.deleteObject('security_settings', 'current_mode');
|
|
this.currentMode = null;
|
|
|
|
secureLogger.info('Security mode reset', {
|
|
component: 'SecurityModeService',
|
|
operation: 'resetSecurityMode'
|
|
});
|
|
} catch (error) {
|
|
secureLogger.error('Failed to reset security mode', error as Error, {
|
|
component: 'SecurityModeService',
|
|
operation: 'resetSecurityMode'
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Récupère l'historique des modes de sécurisation
|
|
*/
|
|
public async getSecurityModeHistory(): Promise<any[]> {
|
|
try {
|
|
const history = await this.database.getObject('security_settings', 'mode_history') || [];
|
|
return history;
|
|
} catch (error) {
|
|
secureLogger.error('Failed to retrieve security mode history', error as Error, {
|
|
component: 'SecurityModeService',
|
|
operation: 'getSecurityModeHistory'
|
|
});
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ajoute une entrée à l'historique des modes
|
|
*/
|
|
private async addToHistory(mode: SecurityMode): Promise<void> {
|
|
try {
|
|
const history = await this.getSecurityModeHistory();
|
|
history.unshift({
|
|
mode,
|
|
timestamp: Date.now(),
|
|
config: this.getSecurityModeConfig(mode)
|
|
});
|
|
|
|
// Garder seulement les 10 dernières entrées
|
|
if (history.length > 10) {
|
|
history.splice(10);
|
|
}
|
|
|
|
await this.database.setObject('security_settings', 'mode_history', history);
|
|
} catch (error) {
|
|
secureLogger.error('Failed to add to security mode history', error as Error, {
|
|
component: 'SecurityModeService',
|
|
operation: 'addToHistory',
|
|
mode
|
|
});
|
|
}
|
|
}
|
|
}
|