import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync } from 'crypto'; // Clé de chiffrement dérivée de la clé Supabase const deriveKey = (): Buffer => { const salt = process.env.SUPABASE_ANON_KEY || 'default-salt'; return pbkdf2Sync(salt, 'mes-budgets-participatifs', 100000, 32, 'sha256'); }; export const encryptionService = { /** * Chiffre une valeur avec AES-256-GCM */ encrypt(value: string): string { if (!value) return ''; const key = deriveKey(); const iv = randomBytes(16); const cipher = createCipheriv('aes-256-gcm', key, iv); let encrypted = cipher.update(value, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); // Format: iv:authTag:encryptedData return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`; }, /** * Déchiffre une valeur chiffrée avec AES-256-GCM */ decrypt(encryptedValue: string): string { if (!encryptedValue) return ''; try { const parts = encryptedValue.split(':'); if (parts.length !== 3) { throw new Error('Format de chiffrement invalide'); } const [ivHex, authTagHex, encryptedData] = parts; const key = deriveKey(); const iv = Buffer.from(ivHex, 'hex'); const authTag = Buffer.from(authTagHex, 'hex'); const decipher = createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(authTag); let decrypted = decipher.update(encryptedData, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } catch (error) { console.error('Erreur lors du déchiffrement:', error); return ''; } }, /** * Vérifie si une valeur est chiffrée */ isEncrypted(value: string): boolean { return Boolean(value && value.includes(':') && value.split(':').length === 3); }, /** * Masque une valeur pour l'affichage */ mask(value: string, maskChar: string = '•'): string { if (!value) return ''; return maskChar.repeat(Math.min(value.length, 8)); } };