@robotti.io/escrypt
Version:
Enterprise Secret Encryptor
101 lines (90 loc) • 3.43 kB
JavaScript
const { Validator } = require('jsonschema');
const path = require('path');
const fs = require('fs');
const Cryptor = require('./lib/cryptor');
const ConfigurationInteractor = require('./lib/configurationInteractor');
const { fstat } = require('fs');
const configSchema = {
id: '/configSchema',
type: 'object',
additionalProperties: false,
properties: {
environment: { type: 'string' },
keyDirectory: { type: 'string' },
configurationDirectory: { type: 'string' },
configurationType: { type: 'string', enum: ['json'] }
},
required: [
'environment',
'keyDirectory',
'configurationDirectory',
'configurationType'
]
};
module.exports = class ESCrypt {
// Private Declarations
#cryptor;
#configurationInteractor;
#instance;
#configurationFile;
constructor(config) {
this.#instance = {
encrypt: false,
decrypt: false
};
// Schema Validation
const validator = new Validator();
const validationResults = validator.validate(config, configSchema);
if (!validationResults.valid) throw new Error(validationResults);
// Key Validation
const keyDirectory = path.resolve(config.keyDirectory);
if (!fs.existsSync(keyDirectory) || !fs.lstatSync(keyDirectory).isDirectory()) throw new Error('Invalid key directory provided');
const privateKey = `${config.environment}-private.pem`;
const publicKey = `${config.environment}-public.pem`;
const privateKeyFile = path.join(keyDirectory, privateKey);
const publicKeyFile = path.join(keyDirectory, publicKey);
this.#cryptor = new Cryptor({ publicKey: publicKeyFile, privateKey: privateKeyFile });
if (fs.existsSync(privateKeyFile)) this.#instance.decrypt = true;
if (fs.existsSync(publicKeyFile)) this.#instance.encrypt = true;
// Config Validation
const configDirectory = path.resolve(config.configurationDirectory);
if (!fs.existsSync(configDirectory) || !fs.lstatSync(configDirectory).isDirectory()) throw new Error('Invalid configuration directory provided');
const configFilename = `${config.environment}.config`;
const configurationFile = path.join(configDirectory, configFilename);
if (!fs.existsSync(configurationFile)) {
fs.writeFileSync(configurationFile, JSON.stringify({}));
}
this.#configurationInteractor = new ConfigurationInteractor({
directory: configDirectory,
filename: configFilename,
type: config.configurationType
});
this.#initializeInstance();
}
#initializeInstance() {
const { encrypt, decrypt } = this.#instance;
if (!encrypt && !decrypt) {
const keyGen = this.#cryptor.generateKeys();
if (keyGen.privateKeyFile && keyGen.publicKeyFile) {
// This would only happen if the key generation failed due to permission issues
this.#instance.encrypt = true;
this.#instance.decrypt = true;
}
}
if (this.#instance.encrypt) {
this.encrypt = this.#encrypt;
}
if (this.#instance.decrypt) {
this.decrypt = this.#decrypt;
}
}
#encrypt(key, secret) {
const encryptedSecret = this.#cryptor.encrypt(secret);
this.#configurationInteractor.storeEncryptedSecret(key, encryptedSecret);
}
#decrypt(key) {
const encryptedSecret = this.#configurationInteractor.retrieveEncryptedSecret(key);
const decryptedSecret = this.#cryptor.decrypt(encryptedSecret);
return decryptedSecret;
}
}