import crypto from 'crypto'; import { config } from '../config'; const ALGORITHM = 'aes-256-gcm'; const IV_LENGTH = 16; const TAG_LENGTH = 16; function getKey(): Buffer { const key = config.encryptionKey; if (!key || key.length < 32) { throw new Error('ENCRYPTION_KEY must be at least 32 characters long'); } return Buffer.from(key.padEnd(32, 'x').slice(0, 32), 'utf8'); } export function encrypt(text: string): { encrypted: string; iv: string; tag: string } { const key = getKey(); const iv = crypto.randomBytes(IV_LENGTH); const cipher = crypto.createCipheriv(ALGORITHM, key, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); const tag = cipher.getAuthTag().toString('hex'); return { encrypted, iv: iv.toString('hex'), tag, }; } export function encryptCombined(text: string): string { const result = encrypt(text); return `${result.encrypted}:${result.iv}:${result.tag}`; } export function decryptCombined(stored: string): string { const parts = stored.split(':'); if (parts.length < 3) throw new Error('Invalid encrypted data format'); const tag = parts.pop()!; const iv = parts.pop()!; const encrypted = parts.join(':'); return decrypt(encrypted, iv, tag); } export function decrypt(encrypted: string, ivHex: string, tagHex: string): string { const key = getKey(); const iv = Buffer.from(ivHex, 'hex'); const tag = Buffer.from(tagHex, 'hex'); const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); decipher.setAuthTag(tag); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } export function hashToken(token: string): string { return crypto.createHash('sha256').update(token).digest('hex'); }