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(''); }); 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'); }