mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
refactor(core): Migrate from crypto-js to native crypto (#7556)
[`crypto-js` has been
discontinued](1da3dabf93
)
PS: We'll remove `crypto-js` usage from `n8n-workflow` and
`@n8n_io/license-sdk` in separate PRs.
This commit is contained in:
parent
135f9214f5
commit
0bd4e742da
|
@ -37,7 +37,6 @@
|
||||||
"@types/aws4": "^1.5.1",
|
"@types/aws4": "^1.5.1",
|
||||||
"@types/concat-stream": "^2.0.0",
|
"@types/concat-stream": "^2.0.0",
|
||||||
"@types/cron": "~1.7.1",
|
"@types/cron": "~1.7.1",
|
||||||
"@types/crypto-js": "^4.1.3",
|
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/lodash": "^4.14.195",
|
"@types/lodash": "^4.14.195",
|
||||||
"@types/mime-types": "^2.1.0",
|
"@types/mime-types": "^2.1.0",
|
||||||
|
@ -54,7 +53,6 @@
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"concat-stream": "^2.0.0",
|
"concat-stream": "^2.0.0",
|
||||||
"cron": "~1.7.2",
|
"cron": "~1.7.2",
|
||||||
"crypto-js": "^4.2.0",
|
|
||||||
"fast-glob": "^3.2.5",
|
"fast-glob": "^3.2.5",
|
||||||
"file-type": "^16.5.4",
|
"file-type": "^16.5.4",
|
||||||
"flatted": "^3.2.4",
|
"flatted": "^3.2.4",
|
||||||
|
|
|
@ -1,21 +1,43 @@
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import { AES, enc } from 'crypto-js';
|
import { createHash, createCipheriv, createDecipheriv, randomBytes } from 'crypto';
|
||||||
import { InstanceSettings } from './InstanceSettings';
|
import { InstanceSettings } from './InstanceSettings';
|
||||||
|
|
||||||
|
// Data encrypted by CryptoJS always starts with these bytes
|
||||||
|
const RANDOM_BYTES = Buffer.from('53616c7465645f5f', 'hex');
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class Cipher {
|
export class Cipher {
|
||||||
constructor(private readonly instanceSettings: InstanceSettings) {}
|
constructor(private readonly instanceSettings: InstanceSettings) {}
|
||||||
|
|
||||||
encrypt(data: string | object) {
|
encrypt(data: string | object) {
|
||||||
const { encryptionKey } = this.instanceSettings;
|
const salt = randomBytes(8);
|
||||||
return AES.encrypt(
|
const [key, iv] = this.getKeyAndIv(salt);
|
||||||
typeof data === 'string' ? data : JSON.stringify(data),
|
const cipher = createCipheriv('aes-256-cbc', key, iv);
|
||||||
encryptionKey,
|
const encrypted = cipher.update(typeof data === 'string' ? data : JSON.stringify(data));
|
||||||
).toString();
|
return Buffer.concat([RANDOM_BYTES, salt, encrypted, cipher.final()]).toString('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
decrypt(data: string) {
|
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;
|
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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,5 +26,10 @@ describe('Cipher', () => {
|
||||||
const decrypted = cipher.decrypt('U2FsdGVkX194VEoX27o3+y5jUd1JTTmVwkOKjVhB6Jg=');
|
const decrypted = cipher.decrypt('U2FsdGVkX194VEoX27o3+y5jUd1JTTmVwkOKjVhB6Jg=');
|
||||||
expect(decrypted).toEqual('random-string');
|
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('');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -582,9 +582,6 @@ importers:
|
||||||
cron:
|
cron:
|
||||||
specifier: ~1.7.2
|
specifier: ~1.7.2
|
||||||
version: 1.7.2
|
version: 1.7.2
|
||||||
crypto-js:
|
|
||||||
specifier: ^4.2.0
|
|
||||||
version: 4.2.0
|
|
||||||
fast-glob:
|
fast-glob:
|
||||||
specifier: ^3.2.5
|
specifier: ^3.2.5
|
||||||
version: 3.2.12
|
version: 3.2.12
|
||||||
|
@ -640,9 +637,6 @@ importers:
|
||||||
'@types/cron':
|
'@types/cron':
|
||||||
specifier: ~1.7.1
|
specifier: ~1.7.1
|
||||||
version: 1.7.3
|
version: 1.7.3
|
||||||
'@types/crypto-js':
|
|
||||||
specifier: ^4.1.3
|
|
||||||
version: 4.1.3
|
|
||||||
'@types/express':
|
'@types/express':
|
||||||
specifier: ^4.17.6
|
specifier: ^4.17.6
|
||||||
version: 4.17.14
|
version: 4.17.14
|
||||||
|
|
Loading…
Reference in a new issue