From 7fe8c8225e7e69aeadfd83a59daa23d02aa124b7 Mon Sep 17 00:00:00 2001 From: fenos Date: Tue, 23 Jul 2024 10:27:44 +0200 Subject: [PATCH] feat: remove crypto-js in favour of crypto native dependency --- package-lock.json | 24 ----------------- package.json | 2 -- src/internal/auth/crypto.ts | 54 +++++++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6fe225b2..384e5bc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,6 @@ "axios-retry": "^3.9.1", "connection-string": "^4.3.6", "conventional-changelog-conventionalcommits": "^5.0.0", - "crypto-js": "^4.2.0", "dotenv": "^16.0.0", "fastify": "^4.8.1", "fastify-metrics": "^10.2.0", @@ -68,7 +67,6 @@ "devDependencies": { "@types/async-retry": "^1.4.5", "@types/busboy": "^1.3.0", - "@types/crypto-js": "^4.1.1", "@types/fs-extra": "^9.0.13", "@types/jest": "^29.2.1", "@types/js-yaml": "^4.0.5", @@ -5173,12 +5171,6 @@ "@types/node": "*" } }, - "node_modules/@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", - "dev": true - }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -6514,11 +6506,6 @@ "node": ">= 8" } }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, "node_modules/custom-error-instance": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz", @@ -15487,12 +15474,6 @@ "@types/node": "*" } }, - "@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", - "dev": true - }, "@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -16521,11 +16502,6 @@ "which": "^2.0.1" } }, - "crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, "custom-error-instance": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz", diff --git a/package.json b/package.json index cadfb705..532b2563 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "axios-retry": "^3.9.1", "connection-string": "^4.3.6", "conventional-changelog-conventionalcommits": "^5.0.0", - "crypto-js": "^4.2.0", "dotenv": "^16.0.0", "fastify": "^4.8.1", "fastify-metrics": "^10.2.0", @@ -81,7 +80,6 @@ "devDependencies": { "@types/async-retry": "^1.4.5", "@types/busboy": "^1.3.0", - "@types/crypto-js": "^4.1.1", "@types/fs-extra": "^9.0.13", "@types/jest": "^29.2.1", "@types/js-yaml": "^4.0.5", diff --git a/src/internal/auth/crypto.ts b/src/internal/auth/crypto.ts index ca826b65..c5382047 100644 --- a/src/internal/auth/crypto.ts +++ b/src/internal/auth/crypto.ts @@ -1,21 +1,53 @@ -import AES from 'crypto-js/aes' -import Utf8 from 'crypto-js/enc-utf8' +import { createCipheriv, createDecipheriv, createHash, randomBytes } from 'crypto' import { getConfig } from '../../config' - const { encryptionKey } = getConfig() /** - * Decrypts a text with the configured encryption key via ENCRYPTION_KEY env - * @param ciphertext - */ + * Generate CryptoJs.AES key from passphrase + * https://github.com/brix/crypto-js/issues/468 + * */ +function convertPassphraseToAesKeyBuffer(key: string, salt: Buffer): Buffer { + const password = Buffer.concat([Buffer.from(key, 'binary'), salt]) + const hash: Buffer[] = [] + let digest = password + for (let i = 0; i < 3; i++) { + hash[i] = createHash('md5').update(digest).digest() + digest = Buffer.concat([hash[i]!, password]) + } + return Buffer.concat(hash) +} + +/** + * Replicate CryptoJs.AES.decrypt method + * */ export function decrypt(ciphertext: string): string { - return AES.decrypt(ciphertext, encryptionKey).toString(Utf8) + try { + const cipherBuffer = Buffer.from(ciphertext, 'base64') + const salt = cipherBuffer.subarray(8, 16) + const keyDerivation = convertPassphraseToAesKeyBuffer(encryptionKey, salt) + const [key, iv] = [keyDerivation.subarray(0, 32), keyDerivation.subarray(32)] + const contents = cipherBuffer.subarray(16) + const decipher = createDecipheriv('aes-256-cbc', key, iv) + const decrypted = Buffer.concat([decipher.update(contents), decipher.final()]) + return decrypted.toString('utf8') + } catch (e) { + throw e + } } /** - * Encrypts a text with the configured encryption key via ENCRYPTION_KEY env - * @param plaintext - */ + * Replicate CryptoJs.AES.encrypt method + * */ export function encrypt(plaintext: string): string { - return AES.encrypt(plaintext, encryptionKey).toString() + try { + const salt = randomBytes(8) + const keyDerivation = convertPassphraseToAesKeyBuffer(encryptionKey, salt) + const [key, iv] = [keyDerivation.subarray(0, 32), keyDerivation.subarray(32)] + const cipher = createCipheriv('aes-256-cbc', key, iv) + const contents = Buffer.concat([cipher.update(plaintext), cipher.final()]) + const encrypted = Buffer.concat([Buffer.from('Salted__', 'utf8'), salt, contents]) + return encrypted.toString('base64') + } catch (e) { + throw e + } }