@tsmx/secure-config-tool
Advanced tools
Comparing version 1.0.2 to 1.1.0
const crypt = require('../utils/crypt'); | ||
module.exports = function (options) { | ||
const key = crypt.genkey(); | ||
console.log(key); | ||
console.log(crypt.genkey(options && options.base64)); | ||
}; |
@@ -6,3 +6,3 @@ const crypt = require('../utils/crypt'); | ||
const verbose = options && options.verbose; | ||
var key = null; | ||
let key = null; | ||
try { | ||
@@ -15,3 +15,3 @@ key = crypt.retrieveKey(verbose); | ||
} | ||
var secret = null; | ||
let secret = null; | ||
if (options && options.secret) { | ||
@@ -18,0 +18,0 @@ secret = options.secret; |
{ | ||
"name": "@tsmx/secure-config-tool", | ||
"version": "1.0.2", | ||
"version": "1.1.0", | ||
"description": "Tool for generating encrypted secure-config entries.", | ||
@@ -5,0 +5,0 @@ "main": "secure-config-tool.js", |
@@ -5,4 +5,9 @@ # [**secure-config-tool**](https://github.com/tsmx/secure-config-tool) | ||
Generating encrypted secrets and keys for [secure-config](https://www.npmjs.com/package/@tsmx/secure-config). | ||
Supporting command-line tool for [secure-config](https://www.npmjs.com/package/@tsmx/secure-config). | ||
Features: | ||
- generating keys | ||
- encrypting secrets | ||
- decrypting secrets (for validation/testing purposes) | ||
## Usage | ||
@@ -22,4 +27,4 @@ | ||
[tsmx@localhost ]$ secure-config-tool genkey | ||
iC771qNLe+OGVcduw8fqpDIIK7lK0T5p | ||
[tsmx@localhost ]$ export CONFIG_ENCRYPTION_KEY=iC771qNLe+OGVcduw8fqpDIIK7lK0T5p | ||
9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f | ||
[tsmx@localhost ]$ export CONFIG_ENCRYPTION_KEY=9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f | ||
``` | ||
@@ -34,2 +39,9 @@ | ||
### Decrypt values | ||
``` | ||
[tsmx@localhost ]$ secure-config-tool decrypt "ENCRYPTED|82da1c22e867d68007d66a23b7b748b3|452a2ed1105ec5607576b820b90aa49f" | ||
MySecret | ||
``` | ||
## Test | ||
@@ -36,0 +48,0 @@ |
@@ -6,2 +6,3 @@ #!/usr/bin/env node | ||
const createKey = require('./functions/create-key'); | ||
const decryptSecret = require('./functions/decrypt-secret'); | ||
@@ -11,3 +12,3 @@ program | ||
.description('creates an encrypted entry for secure-config') | ||
.option('-s, --secret <secret>', 'the secret value to be encryoted, asked if not provided') | ||
.option('-s, --secret <secret>', 'the secret value to be encrypted, asked if not provided') | ||
.option('-v, --verbose', 'verbose output') | ||
@@ -24,3 +25,4 @@ .action(createSecret).on('--help', function () { | ||
.command('genkey') | ||
.description('generates a 32 bytes AES key for encrypting/decrypting values for secure-config') | ||
.description('generates a 32 byte AES key for encrypting/decrypting values for secure-config and returns the hex string') | ||
.option('-b, --base64', 'the generated key is a base64 string') | ||
.action(createKey).on('--help', function () { | ||
@@ -31,5 +33,17 @@ console.log(''); | ||
console.log(' $ secure-config-tool genkey'); | ||
console.log(' $ secure-config-tool genkey --export'); | ||
console.log(' $ secure-config-tool genkey --base64'); | ||
}); | ||
program | ||
.command('decrypt <secret>') | ||
.description('decrypts an encrypted entry for secure-config') | ||
.option('-v, --verbose', 'verbose output') | ||
.action(decryptSecret).on('--help', function () { | ||
console.log(''); | ||
console.log('Examples:'); | ||
console.log(''); | ||
console.log(' $ secure-config-tool decrypt "ENCRYPTED|82da1c22e867d68007d66a23b7b748b3|452a2ed1105ec5607576b820b90aa49f"'); | ||
console.log(' $ secure-config-tool decrypt --verbose "ENCRYPTED|82da1c22e867d68007d66a23b7b748b3|452a2ed1105ec5607576b820b90aa49f"'); | ||
}); | ||
program.parse(process.argv); |
@@ -7,5 +7,12 @@ describe('secure-config-tool test suite', () => { | ||
const TEST_KEY = 'iC771qNLe+OGVcduw8fqpDIIK7lK0T5p'; | ||
const TEST_KEY_BROKEN = 'iC771qNLe+OGVcduw8fqpDIIK7lK0T'; | ||
const TEST_SECRET = 'MySecret123$'; | ||
const TEST_SECRET_ENCRYPTED = 'ENCRYPTED|f43fda7e3486b77a46b77b1c0b35e3db|9d329de17378813ffe21117360dfe3fa'; | ||
const DECRYPT_ERROR = 'Decryption failed. Please check that the encrypted secret is valid and has the form "ENCRYPTED|IV|DATA"\n' + | ||
'Please see the docs under: https://github.com/tsmx/secure-config'; | ||
beforeEach(() => { | ||
delete process.env['CONFIG_ENCRYPTION_KEY']; | ||
jest.resetModules(); | ||
delete process.env['CONFIG_ENCRYPTION_KEY']; | ||
console.log = testConsoleLog; | ||
@@ -22,5 +29,3 @@ testOutput = []; | ||
const crypt = require('../utils/crypt'); | ||
const text = 'TestSecret-123!'; | ||
const key = 'iC771qNLe+OGVcduw8fqpDIIK7lK0T5p'; | ||
const encrypted = crypt.encrypt(text, key); | ||
const encrypted = crypt.encrypt(TEST_SECRET, TEST_KEY); | ||
expect(encrypted).toBeDefined(); | ||
@@ -31,13 +36,60 @@ let parts = encrypted.split('|'); | ||
expect(parts[0]).toBe('ENCRYPTED'); | ||
const decrypted = crypt.decrypt(encrypted, key); | ||
const decrypted = crypt.decrypt(encrypted, TEST_KEY); | ||
expect(decrypted).toBeDefined(); | ||
expect(decrypted).toBe(text); | ||
expect(decrypted).toBe(TEST_SECRET); | ||
done(); | ||
}); | ||
it('tests a successful decryption', async (done) => { | ||
const crypt = require('../utils/crypt'); | ||
const decrypted = crypt.decrypt(TEST_SECRET_ENCRYPTED, TEST_KEY); | ||
expect(decrypted).toBeDefined(); | ||
expect(decrypted).toBe(TEST_SECRET); | ||
done(); | ||
}); | ||
it('tests a failed decryption - illegal secret structure', async (done) => { | ||
expect(() => { | ||
const crypt = require('../utils/crypt'); | ||
const encrypted = '2a8660e3e6614b58b1c1b13d5db49ff0|30d052eeab498181b7071e2d5ce0e71a'; | ||
const decrypted = crypt.decrypt(encrypted, TEST_KEY); | ||
}).toThrow(DECRYPT_ERROR); | ||
done(); | ||
}); | ||
it('tests a failed decryption - illegal secret IV', async (done) => { | ||
expect(() => { | ||
const crypt = require('../utils/crypt'); | ||
const encrypted = 'ENCRYPTED|2a8660e3|30d052eeab498181b7071e2d5ce0e71a'; | ||
const decrypted = crypt.decrypt(encrypted, TEST_KEY); | ||
}).toThrow(DECRYPT_ERROR); | ||
done(); | ||
}); | ||
it('tests a failed decryption - illegal secret DATA', async (done) => { | ||
expect(() => { | ||
const crypt = require('../utils/crypt'); | ||
const encrypted = 'ENCRYPTED|2a8660e3e6614b58b1c1b13d5db49ff0|30d052eeab498181b7071e2d5ce0'; | ||
const decrypted = crypt.decrypt(encrypted, TEST_KEY); | ||
}).toThrow(DECRYPT_ERROR); | ||
done(); | ||
}); | ||
it('tests a successful key generation', async (done) => { | ||
const crypt = require('../utils/crypt'); | ||
const hexReg = new RegExp('^[0-9A-F]{64}$', 'i'); | ||
const key = crypt.genkey(); | ||
expect(key).toBeDefined(); | ||
expect(key.length).toBe(64); | ||
expect(hexReg.test(key)).toBeTruthy(); | ||
expect(Buffer.from(key, 'hex').length).toBe(32); | ||
done(); | ||
}); | ||
it('tests a successful key generation with base64', async (done) => { | ||
const crypt = require('../utils/crypt'); | ||
const key = crypt.genkey(true); | ||
expect(key).toBeDefined(); | ||
expect(key.length).toBe(32); | ||
expect(Buffer.from(key).length).toBe(32); | ||
done(); | ||
@@ -47,3 +99,3 @@ }); | ||
it('tests a successful key retrieval', async (done) => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = 'iC771qNLe+OGVcduw8fqpDIIK7lK0T5p'; | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY; | ||
const crypt = require('../utils/crypt'); | ||
@@ -60,3 +112,16 @@ expect(testOutput.length).toBe(0); | ||
it('tests a failes key retrieval - no key found', async (done) => { | ||
it('tests a successful key retrieval for a hexadecimal string', async (done) => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = '9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f'; | ||
const crypt = require('../utils/crypt'); | ||
expect(testOutput.length).toBe(0); | ||
const key = crypt.retrieveKey(true); | ||
expect(key).toBeDefined(); | ||
expect(key.length).toBe(32); | ||
expect(testOutput.length).toBe(1); | ||
expect(testOutput[0].startsWith('CONFIG_ENCRYPTION_KEY found')).toBeTruthy(); | ||
expect(testOutput[0].endsWith('bd0f')).toBeTruthy(); | ||
done(); | ||
}); | ||
it('tests a failed key retrieval - no key found', async (done) => { | ||
expect(() => { | ||
@@ -71,3 +136,3 @@ const crypt = require('../utils/crypt'); | ||
expect(() => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = 'iC771qNLe+OGVcduw8fqpDIIK7lK'; | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY_BROKEN; | ||
const crypt = require('../utils/crypt'); | ||
@@ -79,2 +144,103 @@ crypt.retrieveKey(); | ||
it('tests a successful command line key generation', async (done) => { | ||
const createKey = require('../functions/create-key'); | ||
const hexReg = new RegExp('^[0-9A-F]{64}$', 'i'); | ||
createKey(); | ||
expect(testOutput.length).toBe(1); | ||
expect(testOutput[0].length).toBe(64); | ||
expect(hexReg.test(testOutput[0])).toBeTruthy(); | ||
expect(Buffer.from(testOutput[0], 'hex').length).toBe(32); | ||
done(); | ||
}); | ||
it('tests a successful command line key generation with base64', async (done) => { | ||
const createKey = require('../functions/create-key'); | ||
createKey({ base64: true }); | ||
expect(testOutput.length).toBe(1); | ||
expect(testOutput[0].length).toBe(32); | ||
expect(Buffer.from(testOutput[0]).length).toBe(32); | ||
done(); | ||
}); | ||
it('tests a successful command line secret encryption', async (done) => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY; | ||
const createSecret = require('../functions/create-secret'); | ||
createSecret({ secret: 'MySecret' }); | ||
expect(testOutput.length).toBe(1); | ||
expect(testOutput[0].startsWith('ENCRYPTED|')).toBeTruthy(); | ||
done(); | ||
}); | ||
it('tests a successful command line secret encryption with verbose output', async (done) => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY; | ||
const createSecret = require('../functions/create-secret'); | ||
createSecret({ secret: TEST_SECRET, verbose: true }); | ||
expect(testOutput.length).toBe(5); | ||
expect(testOutput[0].endsWith('lK0T5p')).toBeTruthy(); | ||
expect(testOutput[1].startsWith('ENCRYPTED|')).toBeTruthy(); | ||
expect(testOutput[3]).toBe(TEST_SECRET); | ||
expect(testOutput[4]).toBe('Success.'); | ||
done(); | ||
}); | ||
it('tests a failed command line secret encryption because of a missing key', async (done) => { | ||
const mockExit = jest.spyOn(process, 'exit') | ||
.mockImplementation((number) => { throw new Error('process.exit: ' + number); }); | ||
const createSecret = require('../functions/create-secret'); | ||
expect(() => { | ||
createSecret({ secret: TEST_SECRET }); | ||
}).toThrow(); | ||
expect(mockExit).toHaveBeenCalledWith(-1); | ||
mockExit.mockRestore(); | ||
done(); | ||
}); | ||
it('tests a successful command line secret decryption', async (done) => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY; | ||
const decryptSecret = require('../functions/decrypt-secret'); | ||
decryptSecret(TEST_SECRET_ENCRYPTED, null); | ||
expect(testOutput.length).toBe(1); | ||
expect(testOutput[0]).toBe(TEST_SECRET); | ||
done(); | ||
}); | ||
it('tests a successful command line secret decryption with verbose output', async (done) => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY; | ||
const decryptSecret = require('../functions/decrypt-secret'); | ||
decryptSecret(TEST_SECRET_ENCRYPTED, { verbose: true }); | ||
expect(testOutput.length).toBe(2); | ||
expect(testOutput[0].endsWith('lK0T5p')).toBeTruthy(); | ||
expect(testOutput[1]).toBe(TEST_SECRET); | ||
done(); | ||
}); | ||
it('tests a failed command line secret decryption bevause of a broken secret', async (done) => { | ||
const mockExit = jest.spyOn(process, 'exit') | ||
.mockImplementation((number) => { throw new Error('process.exit: ' + number); }); | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY; | ||
const decryptSecret = require('../functions/decrypt-secret'); | ||
expect(() => { | ||
decryptSecret('ENCRYPTED|f43fda7e3486b77a46b77b1c0b35e3db|9d329de17378813ffe21117360dfe3', null); | ||
}).toThrow(); | ||
expect(mockExit).toHaveBeenCalledWith(-1); | ||
mockExit.mockRestore(); | ||
expect(testOutput.length).toBe(1); | ||
expect(testOutput[0]).toBe(DECRYPT_ERROR); | ||
done(); | ||
}); | ||
it('tests a failed command line secret decryption bevause of a missing key', async (done) => { | ||
const mockExit = jest.spyOn(process, 'exit') | ||
.mockImplementation((number) => { throw new Error('process.exit: ' + number); }); | ||
const decryptSecret = require('../functions/decrypt-secret'); | ||
expect(() => { | ||
decryptSecret(TEST_SECRET_ENCRYPTED, null); | ||
}).toThrow(); | ||
expect(mockExit).toHaveBeenCalledWith(-1); | ||
mockExit.mockRestore(); | ||
expect(testOutput.length).toBe(1); | ||
expect(testOutput[0]).toBe('Environment variable CONFIG_ENCRYPTION_KEY not set.'); | ||
done(); | ||
}); | ||
}); |
@@ -5,6 +5,14 @@ const crypto = require('crypto'); | ||
module.exports.retrieveKey = function (verbose) { | ||
const hexReg = new RegExp('^[0-9A-F]{64}$', 'i'); | ||
let result = null; | ||
if (!process.env.CONFIG_ENCRYPTION_KEY) { | ||
throw new Error('Environment variable CONFIG_ENCRYPTION_KEY not set.'); | ||
} | ||
else if (process.env.CONFIG_ENCRYPTION_KEY.toString().length !== 32) { | ||
else if (process.env.CONFIG_ENCRYPTION_KEY.toString().length == 32) { | ||
result = Buffer.from(process.env.CONFIG_ENCRYPTION_KEY); | ||
} | ||
else if (hexReg.test(process.env.CONFIG_ENCRYPTION_KEY)) { | ||
result = Buffer.from(process.env.CONFIG_ENCRYPTION_KEY, 'hex'); | ||
} | ||
else { | ||
throw new Error('CONFIG_ENCRYPTION_KEY length must be 32 bytes.'); | ||
@@ -15,4 +23,4 @@ } | ||
} | ||
return Buffer.from(process.env.CONFIG_ENCRYPTION_KEY); | ||
} | ||
return result; | ||
}; | ||
@@ -25,18 +33,31 @@ module.exports.encrypt = function (text, key) { | ||
return 'ENCRYPTED|' + iv.toString('hex') + '|' + encrypted.toString('hex'); | ||
} | ||
}; | ||
module.exports.decrypt = function (text, key) { | ||
let input = text.split('|'); | ||
input.shift(); | ||
let iv = Buffer.from(input[0], 'hex'); | ||
let encryptedText = Buffer.from(input[1], 'hex'); | ||
let decipher = crypto.createDecipheriv(algorithm, key, iv); | ||
let decrypted = decipher.update(encryptedText); | ||
decrypted = Buffer.concat([decrypted, decipher.final()]); | ||
let decrypted = null; | ||
try { | ||
let input = text.split('|'); | ||
input.shift(); | ||
let iv = Buffer.from(input[0], 'hex'); | ||
let encryptedText = Buffer.from(input[1], 'hex'); | ||
let decipher = crypto.createDecipheriv(algorithm, key, iv); | ||
decrypted = decipher.update(encryptedText); | ||
decrypted = Buffer.concat([decrypted, decipher.final()]); | ||
} | ||
catch (error) { | ||
throw new Error('Decryption failed. Please check that the encrypted secret is valid and has the form "ENCRYPTED|IV|DATA"\n' + | ||
'Please see the docs under: https://github.com/tsmx/secure-config'); | ||
} | ||
return decrypted.toString(); | ||
} | ||
}; | ||
module.exports.genkey = function () { | ||
return crypto.randomBytes(24) | ||
.toString('base64'); | ||
} | ||
module.exports.genkey = function (base64 = false) { | ||
let result = null; | ||
if (base64) { | ||
result = crypto.randomBytes(24).toString('base64'); | ||
} | ||
else { | ||
result = crypto.randomBytes(32).toString('hex'); | ||
} | ||
return result; | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19384
11
397
49
15
1