Files
Calbook/lib/crypto.ts

49 lines
1.7 KiB
TypeScript

import crypto from "crypto";
const ALGORITHM = "aes-256-gcm";
function getKey() {
const env = process.env.CALDAV_ENCRYPTION_KEY;
if (!env || env.length < 32) {
throw new Error("CALDAV_ENCRYPTION_KEY muss mindestens 32 Zeichen lang sein");
}
return Buffer.from(env.slice(0, 32));
}
function encryptWithKey(value: string, key: Buffer): string {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
const authTag = cipher.getAuthTag();
return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted.toString("hex")}`;
}
function decryptWithKey(value: string, key: Buffer): string {
const [ivHex, tagHex, encryptedHex] = value.split(":");
if (!ivHex || !tagHex || !encryptedHex) {
throw new Error("Ungültiges Secret-Format");
}
const decipher = crypto.createDecipheriv(ALGORITHM, key, Buffer.from(ivHex, "hex"));
decipher.setAuthTag(Buffer.from(tagHex, "hex"));
const decrypted = Buffer.concat([
decipher.update(Buffer.from(encryptedHex, "hex")),
decipher.final()
]);
return decrypted.toString("utf8");
}
export function encryptSecret(value: string): string {
return encryptWithKey(value, getKey());
}
export function decryptSecret(value: string): string {
return decryptWithKey(value, getKey());
}
export function reEncryptWithNewKey(encrypted: string, oldKeyHex: string): string {
if (!encrypted || !oldKeyHex || oldKeyHex.length < 32) return encrypted;
const oldKey = Buffer.from(oldKeyHex.slice(0, 32));
const plaintext = decryptWithKey(encrypted, oldKey);
return encryptWithKey(plaintext, getKey());
}