/** * PairingService - Service spécialisé pour le pairing * Gère la logique métier du pairing sans couplage direct */ import { DeviceRepository } from '../repositories/device.repository'; import { ProcessRepository } from '../repositories/process.repository'; import { eventBus } from './event-bus'; import { secureLogger } from './secure-logger'; import { SecureCredentialsService } from './secure-credentials.service'; import { CredentialData } from './credentials/types'; export interface PairingResult { success: boolean; data?: any; error?: Error; } export interface PairingWords { words: string; address: string; timestamp: number; } export class PairingService { constructor( private deviceRepo: DeviceRepository, private processRepo: ProcessRepository, private sdkClient: any ) { // Use parameters console.log('Pairing service constructor:', { deviceRepo: this.deviceRepo, processRepo: this.processRepo, sdkClient: this.sdkClient }); } /** * Crée un nouveau processus de pairing avec credentials sécurisés */ async createPairing(password: string): Promise { try { secureLogger.info('Creating pairing process with secure credentials', { component: 'PairingService', operation: 'createPairing' }); // Vérifier qu'un appareil existe const device = await this.deviceRepo.getDevice(); if (!device) { throw new Error('No device found. Please initialize the device first.'); } const secureCredentialsService = SecureCredentialsService.getInstance(); // Valider la force du mot de passe const passwordValidation = secureCredentialsService.validatePasswordStrength(password); if (!passwordValidation.isValid) { throw new Error(`Password too weak: ${passwordValidation.feedback.join(', ')}`); } // Générer les credentials sécurisés const credentials = await secureCredentialsService.generateSecureCredentials(password); // Stocker les credentials de manière sécurisée await secureCredentialsService.storeCredentials(credentials, password); // Créer le processus de pairing via le SDK avec les clés sécurisées const result = await this.sdkClient.createPairing({ spendKey: credentials.spendKey, scanKey: credentials.scanKey }); if (result.success) { // Émettre l'événement de succès eventBus.emit('pairing:created', { result, credentials: true }); secureLogger.info('Pairing process created successfully with secure credentials', { component: 'PairingService', operation: 'createPairing' }); return { success: true, data: result }; } else { throw new Error(result.error || 'Failed to create pairing process'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Use errorMessage variable console.log('Pairing error:', errorMessage); secureLogger.error('Failed to create pairing process', error as Error, { component: 'PairingService', operation: 'createPairing' }); // Émettre l'événement d'erreur eventBus.emit('pairing:error', { error: errorMessage }); return { success: false, error: error as Error }; } } /** * Rejoint un processus de pairing avec des mots */ async joinPairing(words: string): Promise { try { secureLogger.info('Joining pairing process with words', { component: 'PairingService', operation: 'joinPairing', wordsLength: words.length }); // Valider les mots if (!words || words.trim().length === 0) { throw new Error('Words are required to join pairing'); } // Rejoindre le processus via le SDK const result = await this.sdkClient.joinPairing(words); if (result.success) { // Émettre l'événement de succès eventBus.emit('pairing:joined', { result }); secureLogger.info('Successfully joined pairing process', { component: 'PairingService', operation: 'joinPairing' }); return { success: true, data: result }; } else { throw new Error(result.error || 'Failed to join pairing process'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Use errorMessage variable console.log('Pairing error:', errorMessage); secureLogger.error('Failed to join pairing process', error as Error, { component: 'PairingService', operation: 'joinPairing' }); // Émettre l'événement d'erreur eventBus.emit('pairing:error', { error: errorMessage }); return { success: false, error: error as Error }; } } /** * Génère des mots de pairing */ async generatePairingWords(): Promise { try { const device = await this.deviceRepo.getDevice(); if (!device?.sp_wallet?.address) { throw new Error('No device address found'); } const words = await this.addressToWords(device.sp_wallet.address); const pairingWords: PairingWords = { words, address: device.sp_wallet.address, timestamp: Date.now() }; secureLogger.info('Pairing words generated', { component: 'PairingService', operation: 'generatePairingWords', address: device.sp_wallet.address }); // Émettre l'événement eventBus.emit('pairing:wordsGenerated', { pairingWords }); return pairingWords; } catch (error) { secureLogger.error('Failed to generate pairing words', error as Error, { component: 'PairingService', operation: 'generatePairingWords' }); return null; } } /** * Vérifie le statut du pairing */ async getPairingStatus(): Promise<{ isPaired: boolean; pairingId: string | null; deviceAddress: string | null; }> { try { const device = await this.deviceRepo.getDevice(); const isPaired = device ? await this.sdkClient.is_paired() : false; const pairingId = device ? await this.sdkClient.get_pairing_process_id() : null; const deviceAddress = device?.sp_wallet?.address || null; return { isPaired, pairingId, deviceAddress }; } catch (error) { secureLogger.error('Failed to get pairing status', error as Error, { component: 'PairingService', operation: 'getPairingStatus' }); return { isPaired: false, pairingId: null, deviceAddress: null }; } } /** * Confirme le pairing */ async confirmPairing(): Promise { try { secureLogger.info('Confirming pairing', { component: 'PairingService', operation: 'confirmPairing' }); const result = await this.sdkClient.confirmPairing(); if (result.success) { // Émettre l'événement de succès eventBus.emit('pairing:confirmed', { result }); secureLogger.info('Pairing confirmed successfully', { component: 'PairingService', operation: 'confirmPairing' }); return { success: true, data: result }; } else { throw new Error(result.error || 'Failed to confirm pairing'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Use errorMessage variable console.log('Pairing error:', errorMessage); secureLogger.error('Failed to confirm pairing', error as Error, { component: 'PairingService', operation: 'confirmPairing' }); // Émettre l'événement d'erreur eventBus.emit('pairing:error', { error: errorMessage }); return { success: false, error: error as Error }; } } /** * Annule le pairing */ async cancelPairing(): Promise { try { secureLogger.info('Cancelling pairing', { component: 'PairingService', operation: 'cancelPairing' }); const result = await this.sdkClient.cancelPairing(); // Émettre l'événement eventBus.emit('pairing:cancelled', { result }); secureLogger.info('Pairing cancelled', { component: 'PairingService', operation: 'cancelPairing' }); return { success: true, data: result }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Use errorMessage variable console.log('Pairing error:', errorMessage); secureLogger.error('Failed to cancel pairing', error as Error, { component: 'PairingService', operation: 'cancelPairing' }); return { success: false, error: error as Error }; } } /** * Récupère les credentials sécurisés */ async getSecureCredentials(password: string): Promise { try { const secureCredentialsService = SecureCredentialsService.getInstance(); const credentials = await secureCredentialsService.retrieveCredentials(password); if (credentials) { secureLogger.info('Secure credentials retrieved', { component: 'PairingService', operation: 'getSecureCredentials', hasSpendKey: !!credentials.spendKey, hasScanKey: !!credentials.scanKey }); } return credentials; } catch (error) { secureLogger.error('Failed to retrieve secure credentials', error as Error, { component: 'PairingService', operation: 'getSecureCredentials' }); return null; } } /** * Vérifie si des credentials sécurisés existent */ async hasSecureCredentials(): Promise { try { const secureCredentialsService = SecureCredentialsService.getInstance(); return await secureCredentialsService.hasCredentials(); } catch (error) { secureLogger.error('Failed to check secure credentials', error as Error, { component: 'PairingService', operation: 'hasSecureCredentials' }); return false; } } /** * Supprime les credentials sécurisés */ async deleteSecureCredentials(): Promise { try { const secureCredentialsService = SecureCredentialsService.getInstance(); await secureCredentialsService.deleteCredentials(); secureLogger.info('Secure credentials deleted', { component: 'PairingService', operation: 'deleteSecureCredentials' }); // Émettre l'événement eventBus.emit('credentials:deleted'); return { success: true }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Use errorMessage variable console.log('Pairing error:', errorMessage); secureLogger.error('Failed to delete secure credentials', error as Error, { component: 'PairingService', operation: 'deleteSecureCredentials' }); return { success: false, error: error as Error }; } } /** * Valide la force du mot de passe */ validatePassword(password: string): { isValid: boolean; score: number; feedback: string[]; } { const secureCredentialsService = SecureCredentialsService.getInstance(); return secureCredentialsService.validatePasswordStrength(password); } /** * Convertit une adresse en mots */ private async addressToWords(address: string): Promise { // Implémentation simplifiée - à remplacer par la vraie logique const words = [ 'abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid' ]; const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(address)); const hashArray = new Uint8Array(hash); const selectedWords = []; for (let i = 0; i < 4; i++) { const index = hashArray[i] % words.length; selectedWords.push(words[index]); } return selectedWords.join(' '); } }