From 0bd4e742da0c93c967bfd51d921f5b9b1310a9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Fri, 3 Nov 2023 13:26:20 +0100 Subject: [PATCH] refactor(core): Migrate from crypto-js to native crypto (#7556) [`crypto-js` has been discontinued](https://github.com/brix/crypto-js/commit/1da3dabf93f0a0435c47627d6f171ad25f452012) PS: We'll remove `crypto-js` usage from `n8n-workflow` and `@n8n_io/license-sdk` in separate PRs. --- packages/core/package.json | 2 -- packages/core/src/Cipher.ts | 36 +++++++++++++++++++++++++------ packages/core/test/Cipher.test.ts | 5 +++++ pnpm-lock.yaml | 6 ------ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 655432424c..729abe3e20 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -37,7 +37,6 @@ "@types/aws4": "^1.5.1", "@types/concat-stream": "^2.0.0", "@types/cron": "~1.7.1", - "@types/crypto-js": "^4.1.3", "@types/express": "^4.17.6", "@types/lodash": "^4.14.195", "@types/mime-types": "^2.1.0", @@ -54,7 +53,6 @@ "axios": "^0.21.1", "concat-stream": "^2.0.0", "cron": "~1.7.2", - "crypto-js": "^4.2.0", "fast-glob": "^3.2.5", "file-type": "^16.5.4", "flatted": "^3.2.4", diff --git a/packages/core/src/Cipher.ts b/packages/core/src/Cipher.ts index 88e25e00ab..4e1b649bed 100644 --- a/packages/core/src/Cipher.ts +++ b/packages/core/src/Cipher.ts @@ -1,21 +1,43 @@ import { Service } from 'typedi'; -import { AES, enc } from 'crypto-js'; +import { createHash, createCipheriv, createDecipheriv, randomBytes } from 'crypto'; import { InstanceSettings } from './InstanceSettings'; +// Data encrypted by CryptoJS always starts with these bytes +const RANDOM_BYTES = Buffer.from('53616c7465645f5f', 'hex'); + @Service() export class Cipher { constructor(private readonly instanceSettings: InstanceSettings) {} encrypt(data: string | object) { - const { encryptionKey } = this.instanceSettings; - return AES.encrypt( - typeof data === 'string' ? data : JSON.stringify(data), - encryptionKey, - ).toString(); + const salt = randomBytes(8); + const [key, iv] = this.getKeyAndIv(salt); + const cipher = createCipheriv('aes-256-cbc', key, iv); + const encrypted = cipher.update(typeof data === 'string' ? data : JSON.stringify(data)); + return Buffer.concat([RANDOM_BYTES, salt, encrypted, cipher.final()]).toString('base64'); } decrypt(data: string) { + const input = Buffer.from(data, 'base64'); + if (input.length < 16) return ''; + const salt = input.subarray(8, 16); + const [key, iv] = this.getKeyAndIv(salt); + const contents = input.subarray(16); + const decipher = createDecipheriv('aes-256-cbc', key, iv); + return Buffer.concat([decipher.update(contents), decipher.final()]).toString('utf-8'); + } + + private getKeyAndIv(salt: Buffer): [Buffer, Buffer] { const { encryptionKey } = this.instanceSettings; - return AES.decrypt(data, encryptionKey).toString(enc.Utf8); + const password = Buffer.concat([Buffer.from(encryptionKey, 'binary'), salt]); + const hash1 = createHash('md5').update(password).digest(); + const hash2 = createHash('md5') + .update(Buffer.concat([hash1, password])) + .digest(); + const iv = createHash('md5') + .update(Buffer.concat([hash2, password])) + .digest(); + const key = Buffer.concat([hash1, hash2]); + return [key, iv]; } } diff --git a/packages/core/test/Cipher.test.ts b/packages/core/test/Cipher.test.ts index 23e0bf4ab4..1b7c0de944 100644 --- a/packages/core/test/Cipher.test.ts +++ b/packages/core/test/Cipher.test.ts @@ -26,5 +26,10 @@ describe('Cipher', () => { const decrypted = cipher.decrypt('U2FsdGVkX194VEoX27o3+y5jUd1JTTmVwkOKjVhB6Jg='); expect(decrypted).toEqual('random-string'); }); + + it('should not try to decrypt if the input is shorter than 16 bytes', () => { + const decrypted = cipher.decrypt('U2FsdGVkX194VEo'); + expect(decrypted).toEqual(''); + }); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ca668d3c8..61c36655b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -582,9 +582,6 @@ importers: cron: specifier: ~1.7.2 version: 1.7.2 - crypto-js: - specifier: ^4.2.0 - version: 4.2.0 fast-glob: specifier: ^3.2.5 version: 3.2.12 @@ -640,9 +637,6 @@ importers: '@types/cron': specifier: ~1.7.1 version: 1.7.3 - '@types/crypto-js': - specifier: ^4.1.3 - version: 4.1.3 '@types/express': specifier: ^4.17.6 version: 4.17.14