import { createCipheriv, createDecipheriv, createHash, randomBytes } from "crypto"; const PREFIX = "gcm1:"; function encryptionKey(): Buffer { const secret = process.env.SETTINGS_ENCRYPTION_KEY ?? ""; if (secret.length < 32) { throw new Error("SETTINGS_ENCRYPTION_KEY fehlt oder ist zu kurz (min. 32 Zeichen)"); } return createHash("sha256").update(secret, "utf8").digest(); } /** Verschlüsselt einen Geheimnis-Wert mit AES-256-GCM. Format: gcm1: */ export function encryptSecret(plain: string): string { const iv = randomBytes(12); const cipher = createCipheriv("aes-256-gcm", encryptionKey(), iv); const enc = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]); const tag = cipher.getAuthTag(); return PREFIX + Buffer.concat([iv, tag, enc]).toString("base64"); } /** Entschlüsselt einen mit encryptSecret erzeugten Wert. Wirft bei Manipulation/falschem Key. */ export function decryptSecret(stored: string): string { if (!stored.startsWith(PREFIX)) throw new Error("Unbekanntes Geheimnis-Format"); const raw = Buffer.from(stored.slice(PREFIX.length), "base64"); if (raw.length < 12 + 16 + 1) throw new Error("Beschädigter Geheimnis-Wert"); const iv = raw.subarray(0, 12); const tag = raw.subarray(12, 28); const enc = raw.subarray(28); const decipher = createDecipheriv("aes-256-gcm", encryptionKey(), iv); decipher.setAuthTag(tag); return Buffer.concat([decipher.update(enc), decipher.final()]).toString("utf8"); } /** true, wenn ein Wert verschlüsselt ablegbar ist (Key vorhanden). */ export function canEncrypt(): boolean { return (process.env.SETTINGS_ENCRYPTION_KEY ?? "").length >= 32; }