diff --git a/src/lib/secret.ts b/src/lib/secret.ts index d10be458..f7d35727 100644 --- a/src/lib/secret.ts +++ b/src/lib/secret.ts @@ -1,10 +1,16 @@ -import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto"; +import { hkdfSync, createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto"; import { config } from "./config"; const PREFIX = "enc:v1:"; const IV_LENGTH = 12; function deriveKey(): Buffer { + return Buffer.from( + hkdfSync("sha256", config.sessionSecret, "", "caddy-proxy-manager:secret:v1", 32) + ); +} + +function deriveKeyLegacy(): Buffer { return createHash("sha256").update(config.sessionSecret).digest(); } @@ -29,16 +35,23 @@ export function decryptSecret(value: string): string { if (!value) return ""; if (!isEncryptedSecret(value)) return value; + // Try new HKDF key first, fall back to old SHA-256 key for existing data + try { + return _decryptWithKey(value, deriveKey()); + } catch { + return _decryptWithKey(value, deriveKeyLegacy()); + } +} + +function _decryptWithKey(value: string, key: Buffer): string { const payload = value.slice(PREFIX.length); const [ivB64, tagB64, dataB64] = payload.split(":"); if (!ivB64 || !tagB64 || !dataB64) { throw new Error("Invalid encrypted secret format"); } - const iv = Buffer.from(ivB64, "base64"); const tag = Buffer.from(tagB64, "base64"); const data = Buffer.from(dataB64, "base64"); - const key = deriveKey(); const decipher = createDecipheriv("aes-256-gcm", key, iv); decipher.setAuthTag(tag); const plaintext = Buffer.concat([decipher.update(data), decipher.final()]);