import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto'; const ALGORITHM = 'aes-256-gcm'; const IV_LENGTH = 12; const TAG_LENGTH = 16; const ENCODING = 'base64'; function getKey(): Buffer { const raw = process.env.ENCRYPTION_KEY; if (!raw) { throw new Error('ENCRYPTION_KEY environment variable is not set'); } const buf = Buffer.from(raw, 'hex'); if (buf.length !== 32) { throw new Error( 'ENCRYPTION_KEY must be a 64-character hex string (32 bytes)' ); } return buf; } export function encrypt(plaintext: string): string { const key = getKey(); const iv = randomBytes(IV_LENGTH); const cipher = createCipheriv(ALGORITHM, key, iv); const encrypted = Buffer.concat([ cipher.update(plaintext, 'utf8'), cipher.final(), ]); const tag = cipher.getAuthTag(); // Format: base64(iv + tag + ciphertext) return Buffer.concat([iv, tag, encrypted]).toString(ENCODING); } export function decrypt(ciphertext: string): string { const key = getKey(); const buf = Buffer.from(ciphertext, ENCODING); const iv = buf.subarray(0, IV_LENGTH); const tag = buf.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH); const encrypted = buf.subarray(IV_LENGTH + TAG_LENGTH); const decipher = createDecipheriv(ALGORITHM, key, iv); decipher.setAuthTag(tag); return decipher.update(encrypted) + decipher.final('utf8'); }