From e06556ac2a9bc6007731fea49915cc70d75b578f Mon Sep 17 00:00:00 2001 From: CS041 Faatih <22oo1cso41faatih@gmail.com> Date: Sun, 21 Jun 2026 18:36:03 +0530 Subject: [PATCH 1/3] fix: strengthen encryption key derivation with PBKDF2 --- lib/crypto.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/crypto.ts b/lib/crypto.ts index 9cfc241ac..b3113bec6 100644 --- a/lib/crypto.ts +++ b/lib/crypto.ts @@ -3,25 +3,26 @@ import crypto from 'node:crypto'; const ALGO = 'aes-256-gcm'; -function key(): Buffer { +function key(salt: Buffer): Buffer { const k = process.env.ENCRYPTION_KEY; if (!k || k.length < 32) { throw new Error('ENCRYPTION_KEY must be at least 32 characters'); } - return crypto.createHash('sha256').update(k).digest(); + return crypto.pbkdf2Sync(k, salt, 100000, 32, 'sha512'); } export function encryptToken(plain: string): string { + const salt = crypto.randomBytes(16); const iv = crypto.randomBytes(12); - const cipher = crypto.createCipheriv(ALGO, key(), iv); + const cipher = crypto.createCipheriv(ALGO, key(salt), iv); const enc = Buffer.concat([cipher.update(plain, 'utf8'), cipher.final()]); const tag = cipher.getAuthTag(); - return [iv, tag, enc].map((b) => b.toString('base64')).join('.'); + return [salt, iv, tag, enc].map((b) => b.toString('base64')).join('.'); } export function decryptToken(payload: string): string { - const [iv, tag, enc] = payload.split('.').map((p) => Buffer.from(p, 'base64')); - const decipher = crypto.createDecipheriv(ALGO, key(), iv); + const [salt, iv, tag, enc] = payload.split('.').map((p) => Buffer.from(p, 'base64')); + const decipher = crypto.createDecipheriv(ALGO, key(salt), iv); decipher.setAuthTag(tag); return Buffer.concat([decipher.update(enc), decipher.final()]).toString('utf8'); } From 5761a49875b05201b05136d270de56f88cd6679a Mon Sep 17 00:00:00 2001 From: CS041 Faatih <22oo1cso41faatih@gmail.com> Date: Sun, 21 Jun 2026 19:20:33 +0530 Subject: [PATCH 2/3] test: update crypto tests for salted payload format --- lib/crypto.empty-fallback.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crypto.empty-fallback.test.ts b/lib/crypto.empty-fallback.test.ts index 4a5f35689..d2c27500d 100644 --- a/lib/crypto.empty-fallback.test.ts +++ b/lib/crypto.empty-fallback.test.ts @@ -15,13 +15,13 @@ describe('crypto empty / missing inputs verification', () => { const encrypted = encryptToken(plain); expect(encrypted).toBeDefined(); expect(encrypted).not.toBe(plain); - expect(encrypted.split('.')).toHaveLength(3); + expect(encrypted.split('.')).toHaveLength(4); expect(decryptToken(encrypted)).toBe(plain); }); it('handles empty string encryption and decryption', () => { const encrypted = encryptToken(''); - expect(encrypted.split('.')).toHaveLength(3); + expect(encrypted.split('.')).toHaveLength(4); expect(decryptToken(encrypted)).toBe(''); }); From 580071387fb7c6ea2fd695d39961f1f440b853a2 Mon Sep 17 00:00:00 2001 From: CS041 Faatih <22oo1cso41faatih@gmail.com> Date: Sun, 21 Jun 2026 20:45:11 +0530 Subject: [PATCH 3/3] chore: retrigger ci