import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto"; import { config } from "@devpulse/config"; const ENCRYPTION_SALT = process.env.ENCRYPTION_SALT || "devpulse-secure-credential-salt-v1"; let _derivedKey: Buffer | null = null; function getDerivedKey(): Buffer { if (_derivedKey) return _derivedKey; if (!config.ENCRYPTION_KEY) { throw new Error( "Failed to initialize encryption: ENCRYPTION_KEY must be set in environment variables. Generate one with: openssl rand -hex 32", ); } _derivedKey = scryptSync(config.ENCRYPTION_KEY, ENCRYPTION_SALT, 32); return _derivedKey; } export function encryptSecret(plainText: string): string { const key = getDerivedKey(); const iv = randomBytes(12); const cipher = createCipheriv("aes-256-gcm", key, iv); let encrypted = cipher.update(plainText, "utf8", "hex"); encrypted += cipher.final("hex"); const authTag = cipher.getAuthTag().toString("hex"); return `${iv.toString("hex")}:${authTag}:${encrypted}`; } export function decryptSecret(encryptedPayload: string): string { const key = getDerivedKey(); const parts = encryptedPayload.split(":"); if (parts.length !== 3) { throw new Error("Failed to decrypt data: invalid encrypted payload format"); } const [ivHex, authTagHex, encryptedHex] = parts; 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(encryptedHex, "hex", "utf8"); decrypted += decipher.final("utf8"); return decrypted; }