const crypto = require("crypto"); const { getSetting } = require("./settings"); function encryptionKey() { const secret = getSetting("session_secret"); if (!secret) { throw new Error("Lumi's session secret is not initialized."); } return crypto.createHash("sha256").update(`lumi-overlay:${secret}`).digest(); } function generateToken() { return crypto.randomBytes(32).toString("base64url"); } function tokenHash(token) { return crypto.createHash("sha256").update(String(token || "")).digest("hex"); } function encryptSecret(value) { if (value === null || value === undefined || value === "") return null; const iv = crypto.randomBytes(12); const cipher = crypto.createCipheriv("aes-256-gcm", encryptionKey(), iv); const encrypted = Buffer.concat([cipher.update(String(value), "utf8"), cipher.final()]); const tag = cipher.getAuthTag(); return `v1.${iv.toString("base64url")}.${tag.toString("base64url")}.${encrypted.toString("base64url")}`; } function decryptSecret(value) { if (!value) return ""; const [version, ivValue, tagValue, encryptedValue] = String(value).split("."); if (version !== "v1" || !ivValue || !tagValue || !encryptedValue) { throw new Error("Stored secret has an unsupported format."); } const decipher = crypto.createDecipheriv( "aes-256-gcm", encryptionKey(), Buffer.from(ivValue, "base64url") ); decipher.setAuthTag(Buffer.from(tagValue, "base64url")); return Buffer.concat([ decipher.update(Buffer.from(encryptedValue, "base64url")), decipher.final() ]).toString("utf8"); } function createStoredToken() { const token = generateToken(); return { token, hash: tokenHash(token), encrypted: encryptSecret(token) }; } module.exports = { createStoredToken, decryptSecret, encryptSecret, generateToken, tokenHash };