import crypto from "crypto"; import { env } from "../env"; const ENCRYPTION_KEY: string | undefined = env.ENCRYPTION_KEY; // Must be 256 bits (32 bytes, 64 hex characters) const IV_LENGTH: number = 12; // For AES-GCM, this is always 12 // Alternatively: openssl rand -hex 32 export function keyGen() { return crypto.randomBytes(32).toString("hex"); } /** * Encrypts the given plain text using AES-256-GCM algorithm. * * @param {string} plainText - The text to encrypt. * @returns {string} The encrypted data in hex format, including IV and authentication tag. */ export function encrypt(plainText: string): string { if (!ENCRYPTION_KEY) { throw new Error("Missing environment variable: `ENCRYPTION_KEY`"); } const iv = crypto.randomBytes(IV_LENGTH); // Directly use Buffer returned by randomBytes const cipher = crypto.createCipheriv( "aes-256-gcm", new Uint8Array(Buffer.from(ENCRYPTION_KEY, "hex")), new Uint8Array(iv), ); let encrypted = cipher.update(plainText, "utf8", "hex"); encrypted += cipher.final("hex"); const authTag = cipher.getAuthTag(); // Return iv, encrypted data, and authTag as hex, combined in one line return iv.toString("hex") + ":" + encrypted + ":" + authTag.toString("hex"); } export function decrypt(text: string): string { if (!ENCRYPTION_KEY) { throw new Error("Missing environment variable: `ENCRYPTION_KEY`"); } const [ivHex, encryptedHex, authTagHex] = text.split(":"); if (!ivHex || !encryptedHex || !authTagHex) { throw new Error("Invalid or corrupted cipher format"); } const iv = Buffer.from(ivHex, "hex"); const encryptedText = Buffer.from(encryptedHex, "hex"); const authTag = Buffer.from(authTagHex, "hex"); const decipher = crypto.createDecipheriv( "aes-256-gcm", new Uint8Array(Buffer.from(ENCRYPTION_KEY, "hex")), new Uint8Array(iv), ); decipher.setAuthTag(new Uint8Array(authTag)); let decrypted = decipher.update( new Uint8Array(encryptedText), undefined, "utf8", ); decrypted += decipher.final("utf8"); return decrypted.toString(); }