Files
mes-budgets-participatifs/src/lib/encryption.ts
2025-08-25 18:48:52 +02:00

74 lines
2.1 KiB
TypeScript

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));
}
};