import crypto from 'crypto'; // Get encryption key from environment or generate a default one const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY || 'phosphat-report-default-key-32b'; // Must be 32 characters const ALGORITHM = 'aes-256-cbc'; const IV_LENGTH = 16; // For AES, this is always 16 // Ensure the key is exactly 32 bytes function getEncryptionKey(): Buffer { const key = ENCRYPTION_KEY.padEnd(32, '0').substring(0, 32); return Buffer.from(key, 'utf8'); } /** * Encrypts a plain text string * @param text - The plain text to encrypt * @returns Encrypted string in format: iv:encryptedData */ export function encrypt(text: string): string { if (!text) return ''; try { const key = getEncryptionKey(); const iv = crypto.randomBytes(IV_LENGTH); const cipher = crypto.createCipher(ALGORITHM, key); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); // Return iv and encrypted data separated by ':' return iv.toString('hex') + ':' + encrypted; } catch (error) { console.error('Encryption error:', error); throw new Error('Failed to encrypt data'); } } /** * Decrypts an encrypted string * @param encryptedText - The encrypted text in format: iv:encryptedData * @returns Decrypted plain text string */ export function decrypt(encryptedText: string): string { if (!encryptedText) return ''; try { const key = getEncryptionKey(); const textParts = encryptedText.split(':'); if (textParts.length !== 2) { throw new Error('Invalid encrypted text format'); } const iv = Buffer.from(textParts[0], 'hex'); const encryptedData = textParts[1]; const decipher = crypto.createDecipher(ALGORITHM, key); let decrypted = decipher.update(encryptedData, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } catch (error) { console.error('Decryption error:', error); throw new Error('Failed to decrypt data'); } } /** * Encrypts sensitive mail settings * @param settings - Mail settings object * @returns Mail settings with encrypted password */ export function encryptMailSettings(settings: { host: string; port: number; secure: boolean; username: string; password: string; fromName: string; fromEmail: string; }) { return { ...settings, password: encrypt(settings.password) }; } /** * Decrypts sensitive mail settings * @param settings - Mail settings object with encrypted password * @returns Mail settings with decrypted password */ export function decryptMailSettings(settings: { host: string; port: number; secure: boolean; username: string; password: string; fromName: string; fromEmail: string; }) { return { ...settings, password: decrypt(settings.password) }; } /** * Validates if a string is encrypted (contains ':' separator) * @param text - Text to validate * @returns True if text appears to be encrypted */ export function isEncrypted(text: string): boolean { return text.includes(':') && text.split(':').length === 2; } /** * Safely encrypts a password only if it's not already encrypted * @param password - Password to encrypt * @returns Encrypted password */ export function safeEncryptPassword(password: string): string { if (isEncrypted(password)) { return password; // Already encrypted } return encrypt(password); } /** * Test encryption/decryption functionality * @param testText - Text to test with * @returns Test results */ export function testEncryption(testText: string = 'test-password-123') { try { const encrypted = encrypt(testText); const decrypted = decrypt(encrypted); return { success: decrypted === testText, original: testText, encrypted: encrypted, decrypted: decrypted, isValid: decrypted === testText }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } }