cpns / packages /api /src /lib /encryption.ts
rogasper's picture
feat: add new skills and enhance server configuration. Introduce 'security-review' and 'test-driven-development' skills in skills-lock.json. Update turbo.json to include a test dependency. Create .env.example files for server and web applications to standardize environment variables. Implement security headers in server index.ts for improved security. Enhance ResultSection component to support onClear callback and update generation job handling for better session management. Add encryption for API keys in the backend and implement rate limiting across various routers to prevent abuse.
f2d1fea
Raw
History Blame Contribute Delete
1.85 kB
import crypto from "node:crypto";
const ALGORITHM = "aes-256-gcm";
const IV_LENGTH = 16;
const SALT_LENGTH = 16;
const KEY_LENGTH = 32;
const ITERATIONS = 100_000;
function getKey(secret: Buffer, salt: Buffer): Buffer {
return crypto.pbkdf2Sync(secret, salt, ITERATIONS, KEY_LENGTH, "sha256");
}
function getEncryptionKey(): Buffer {
const key = process.env.API_KEY_ENCRYPTION_KEY;
if (!key || key.length < 32) {
throw new Error(
"API_KEY_ENCRYPTION_KEY environment variable must be set (minimum 32 characters)",
);
}
return Buffer.from(key, "utf8");
}
export function encryptApiKey(plaintext: string): string {
const secret = getEncryptionKey();
const salt = crypto.randomBytes(SALT_LENGTH);
const iv = crypto.randomBytes(IV_LENGTH);
const key = getKey(secret, salt);
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
const authTag = cipher.getAuthTag();
// Format: salt:iv:authTag:ciphertext (all base64)
return [
salt.toString("base64"),
iv.toString("base64"),
authTag.toString("base64"),
encrypted.toString("base64"),
].join(":");
}
export function decryptApiKey(encrypted: string): string {
const secret = getEncryptionKey();
const parts = encrypted.split(":");
if (parts.length !== 4) {
throw new Error("Invalid encrypted API key format");
}
const salt = Buffer.from(parts[0]!, "base64");
const iv = Buffer.from(parts[1]!, "base64");
const authTag = Buffer.from(parts[2]!, "base64");
const ciphertext = Buffer.from(parts[3]!, "base64");
const key = getKey(secret, salt);
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
decipher.setAuthTag(authTag);
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
}