← back to Govarbitrage
src/lib/crypto.ts
41 lines
import crypto from "node:crypto";
// AES-256-GCM encryption for secrets at rest (e.g. provider API keys stored in
// ApiCredential). ENCRYPTION_KEY must be 32 bytes as 64 hex chars.
export interface Encrypted {
ciphertext: string; // base64
iv: string; // base64
authTag: string; // base64
}
function key(): Buffer {
const hex = process.env.ENCRYPTION_KEY || "";
const buf = Buffer.from(hex, "hex");
if (buf.length !== 32) {
throw new Error("ENCRYPTION_KEY must be 32 bytes (64 hex chars) for AES-256-GCM");
}
return buf;
}
export function encryptSecret(plaintext: string): Encrypted {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv("aes-256-gcm", key(), iv);
const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
return {
ciphertext: ct.toString("base64"),
iv: iv.toString("base64"),
authTag: cipher.getAuthTag().toString("base64"),
};
}
export function decryptSecret(enc: Encrypted): string {
const decipher = crypto.createDecipheriv("aes-256-gcm", key(), Buffer.from(enc.iv, "base64"));
decipher.setAuthTag(Buffer.from(enc.authTag, "base64"));
const pt = Buffer.concat([
decipher.update(Buffer.from(enc.ciphertext, "base64")),
decipher.final(),
]);
return pt.toString("utf8");
}