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')); } diff --git a/package-lock.json b/package-lock.json index afa477c..3ad5944 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "kuzzle-vault", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a6fa925..d7a8cf5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kuzzle-vault", - "version": "1.0.0", + "version": "1.0.1", "description": "Encrypt and decrypt secrets with Kuzzle Vault", "main": "./index.js", "scripts": { 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', () => {