36 lines
1.2 KiB
TypeScript
36 lines
1.2 KiB
TypeScript
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
|
|
const algorithm = "aes-256-gcm";
|
|
const version = "v1";
|
|
|
|
function getSecretKey() {
|
|
const secret = process.env.CRYPTO_SECRET?.toString();
|
|
if (!secret) {
|
|
throw Error("CRYPTO_SECRET env variable not set");
|
|
}
|
|
return createHash("sha256").update(secret).digest();
|
|
}
|
|
|
|
function encrypt(text: string) {
|
|
const iv = randomBytes(12);
|
|
const cipher = createCipheriv(algorithm, getSecretKey(), iv);
|
|
const ciphertext = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]);
|
|
const tag = cipher.getAuthTag();
|
|
return [version, iv.toString("base64url"), tag.toString("base64url"), ciphertext.toString("base64url")].join(":");
|
|
}
|
|
|
|
function decrypt(encryptedText: string) {
|
|
const [payloadVersion, iv, tag, ciphertext] = encryptedText.split(":");
|
|
if (payloadVersion !== version || !iv || !tag || !ciphertext) {
|
|
throw new Error("Invalid encrypted payload");
|
|
}
|
|
const decipher = createDecipheriv(algorithm, getSecretKey(), Buffer.from(iv, "base64url"));
|
|
decipher.setAuthTag(Buffer.from(tag, "base64url"));
|
|
return Buffer.concat([decipher.update(Buffer.from(ciphertext, "base64url")), decipher.final()]).toString("utf8");
|
|
}
|
|
|
|
export default {
|
|
encrypt,
|
|
decrypt,
|
|
};
|