From 6376f8d1732175dabe4f09b8b21cf9991984b960 Mon Sep 17 00:00:00 2001 From: ycombes Date: Mon, 16 Sep 2019 18:04:41 +0200 Subject: [PATCH 1/2] add sha256 support with old md5 support --- lib/vault.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/vault.js b/lib/vault.js index 30a92b4..3a4e37d 100644 --- a/lib/vault.js +++ b/lib/vault.js @@ -24,7 +24,10 @@ class Vault { return; } - this._vaultKeyHash = crypto.createHash('md5') + this._vaultKeyHash = crypto.createHash('sha256') + .update(this._vaultKey, 'utf-8') + .digest(); + this._vaultKeyHashMD5 = crypto.createHash('md5') .update(this._vaultKey, 'utf-8') .digest('hex') .toUpperCase(); @@ -154,18 +157,30 @@ class Vault { } /** - * Decrypts data with AES CBC using the secret key and the initialization vector + * Decrypts data with AES CBC using the given secret key and the initialization vector * This function keeps compatibility with old IV size (8 bytes) from 1.8.0 to 1.8.1 */ - _decryptData (data) { + _decryptDataWithKey(data, key) { const [ encryptedData, ivHex ] = data.split('.'), iv = ivHex.length === 16 ? ivHex : Buffer.from(ivHex, 'hex'), - decipher = crypto.createDecipheriv('aes-256-cbc', this._vaultKeyHash, iv); + decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); return decipher.update(encryptedData, 'hex', 'utf8') + decipher.final('utf8'); } + /** + * Decrypts data with AES CBC using the initialization vector and the sha256 hashed secret key + * or the md5 hashed secrets key if it fails with sha256 + */ + _decryptData (data) { + try { + return this._decryptDataWithKey(data, this._vaultKeyHash); + } catch (e) { + return this._decryptDataWithKey(data, this._vaultKeyHashMD5); + } + } + _readJsonFile (file) { return JSON.parse(fs.readFileSync(file, 'utf8')); } From 8828ae508add0c427bbaec2c0deda7d8fb21773d Mon Sep 17 00:00:00 2001 From: ycombes Date: Tue, 17 Sep 2019 10:01:21 +0200 Subject: [PATCH 2/2] add unit tests --- test/vault.test.js | 49 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/test/vault.test.js b/test/vault.test.js index bdec18c..fc1b8fe 100644 --- a/test/vault.test.js +++ b/test/vault.test.js @@ -9,6 +9,8 @@ describe('Test: vault core component', () => { clearSecrets, encryptedSecrets, encryptedSecretsIv8, + encryptedSecretsSha256, + encryptedSecretsSha256Iv8, Vault, vault; @@ -21,6 +23,7 @@ describe('Test: vault core component', () => { deep: { nested: { value: 'nested value' } } }; + // Encrypted secrets with MD5 hashed password encryptedSecrets = { aws: { keyId: 'ac2560ec6b05098a843cdc5ab106bf99.f79775f7fd8fb7c8f456b68741414fcc', @@ -33,7 +36,7 @@ describe('Test: vault core component', () => { } }; - // Encrypted secrets with 8 bytes IV + // Encrypted secrets with 8 bytes IV and MD5 hashed password encryptedSecretsIv8 = { aws: { keyId: '5f71b9bc33a6aea5b0263c9be88c1c4f.786ff771e2760258', @@ -46,6 +49,32 @@ describe('Test: vault core component', () => { } }; + // Encrypted secrets with SHA256 hashed password + encryptedSecretsSha256 = { + aws: { + keyId: 'da4e9dcf3b20ae3901211764c97b954b.4000c14b024ca76b48d922d666624eab', + secretKey: '7160329ef751a3377354586db9515173991276f5216f73b0789af214c8298f877b08ef6305c1b670c48687d5f2867bb0.d8027bacfeedb64ce54b2d13dd5558bc' + }, + deep: { + nested: { + value: 'be2698fc2840a5d4eec839c5a5963b98.97492d612828b6a1974827a9781c6d35' + } + } + }; + + // Encrypted secrets with 8 bytes IV and SHA256 hashed password + encryptedSecretsSha256Iv8 = { + aws: { + keyId: 'b6d4f001f8825802cc550bc1b959e99c.00c8ac5ec21a6f6e', + secretKey: '1bfd3a80c7bae345b48076914f351522e2bea4dab7572c63342eb5d3a95db2e35d9d66d42d58e2ec057d264725d594ed.9c529964fbb80be1' + }, + deep: { + nested: { + value: '57767336893841dec88ee5babfe591b4.1765cd733e639217' + } + } + }; + fsMock = { existsSync: sinon.stub().returns(true), readFileSync: sinon.stub().returns(JSON.stringify(encryptedSecrets)), @@ -86,17 +115,31 @@ describe('Test: vault core component', () => { } }); - it('reads the secret file and store decrypted secrets', () => { + it('reads the secret file and store decrypted secrets with MD5 hashed password', () => { vault.decrypt(); should(vault.secrets).match(clearSecrets); }); - it('reads the secret file and store decrypted secrets with old IV size of 8 bytes', () => { + it('reads the secret file and store decrypted secrets with old IV size of 8 bytes and MD5 hashed password', () => { fsMock.readFileSync.returns(JSON.stringify(encryptedSecretsIv8)); vault.decrypt(); should(vault.secrets).match(clearSecrets); }); + + it('reads the secret file and store decrypted secrets with SHA256 hashed password', () => { + fsMock.readFileSync.returns(JSON.stringify(encryptedSecretsSha256)); + + vault.decrypt(); + should(vault.secrets).match(clearSecrets); + }); + + it('reads the secret file and store decrypted secrets with old IV size of 8 bytes and SHA256 hashed password', () => { + fsMock.readFileSync.returns(JSON.stringify(encryptedSecretsSha256Iv8)); + + vault.decrypt(); + should(vault.secrets).match(clearSecrets); + }); }); describe('#encrypt', () => {