44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
|
|
const ALGORITHM = "aes-256-gcm";
|
|
const KEY_LENGTH = 32;
|
|
|
|
function getKey(): Buffer {
|
|
const raw =
|
|
process.env.NUXT_ENCRYPTION_KEY || process.env.ENCRYPTION_KEY || "";
|
|
if (!raw || raw.length < 32) {
|
|
// Fallback: pad with zeros (only for dev without key)
|
|
return Buffer.alloc(
|
|
KEY_LENGTH,
|
|
raw.padEnd(KEY_LENGTH, "0").slice(0, KEY_LENGTH),
|
|
);
|
|
}
|
|
return Buffer.from(raw.slice(0, KEY_LENGTH), "utf8");
|
|
}
|
|
|
|
export function encrypt(text: string): string {
|
|
const key = getKey();
|
|
const iv = randomBytes(12);
|
|
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
const encrypted = Buffer.concat([
|
|
cipher.update(text, "utf8"),
|
|
cipher.final(),
|
|
]);
|
|
const tag = cipher.getAuthTag();
|
|
// Format: iv(24hex):tag(32hex):encrypted(hex)
|
|
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted.toString("hex")}`;
|
|
}
|
|
|
|
export function decrypt(stored: string): string {
|
|
const key = getKey();
|
|
const parts = stored.split(":");
|
|
if (parts.length !== 3) throw new Error("Invalid encrypted format");
|
|
const [ivHex, tagHex, dataHex] = parts;
|
|
const iv = Buffer.from(ivHex, "hex");
|
|
const tag = Buffer.from(tagHex, "hex");
|
|
const data = Buffer.from(dataHex, "hex");
|
|
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
decipher.setAuthTag(tag);
|
|
return decipher.update(data) + decipher.final("utf8");
|
|
}
|