**Motivations :** - Corriger l'erreur de compilation dans block-sync.ts où services n'était plus défini - Supprimer les derniers imports commentés restants **Modifications :** - Restaurer l'initialisation de Services dans block-sync.ts après les vérifications de prérequis - Supprimer les imports commentés dans pairing.service.ts et iframe-pairing.service.ts **Pages affectées :** - src/pages/block-sync/block-sync.ts (restauration de l'initialisation Services) - src/services/pairing.service.ts (supprime imports commentés) - src/services/iframe-pairing.service.ts (supprime import commenté)
404 lines
12 KiB
TypeScript
404 lines
12 KiB
TypeScript
/**
|
|
* 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<PairingResult> {
|
|
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<PairingResult> {
|
|
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<PairingWords | null> {
|
|
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<PairingResult> {
|
|
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<PairingResult> {
|
|
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<CredentialData | null> {
|
|
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<boolean> {
|
|
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<PairingResult> {
|
|
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<string> {
|
|
// 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(' ');
|
|
}
|
|
}
|