@am92/kms
Version:
Key Management Service
191 lines (190 loc) • 7.79 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeKms = void 0;
const crypto_1 = __importDefault(require("crypto"));
const NodeKmsError_1 = require("./NodeKmsError");
const CONSTANTS_1 = require("./CONSTANTS");
const ERRORS_1 = require("./ERRORS");
/**
* Class to execute KMS methods using Node Crypto
*
* @class
*/
class NodeKms {
/**
* Configurations used for Node Crypto
*/
CONFIG;
/**
* Creates an instance of NodeKms.
*
* @constructor
* @param config
*/
constructor(config) {
this.CONFIG = validateConfigAndExtend(config);
this.generateDataKey = this.generateDataKey.bind(this);
this.generateDataKeyPair = this.generateDataKeyPair.bind(this);
this.encrypt = this.encrypt.bind(this);
this.decrypt = this.decrypt.bind(this);
}
/**
* Generates encryption keys for symmetric encryption algorithm
*
* @async
* @returns
*/
async generateDataKey() {
try {
const { KEY_ALGO, KEY_LENGTH, KEY_FORMAT } = this.CONFIG;
const options = { length: KEY_LENGTH };
const keyObject = crypto_1.default.generateKeySync(KEY_ALGO, options);
const dataKey = keyObject.export().toString(KEY_FORMAT);
const encryptOptions = {
cipherTextFormat: KEY_FORMAT,
plainTextFormat: KEY_FORMAT
};
const encryptedDataKey = await this.encrypt(dataKey, encryptOptions);
const dataKeyObject = { dataKey, encryptedDataKey };
return dataKeyObject;
}
catch (error) {
const errorCode = `NodeKms::${error.code}`;
throw new NodeKmsError_1.NodeKmsError(error, { errorCode });
}
}
/**
* Generates encryption keys for asymmetric encryption algorithm
*
* @async
* @returns
*/
async generateDataKeyPair() {
try {
const { KEY_PAIR_ALGO, KEY_PAIR_LENGTH, KEY_FORMAT } = this.CONFIG;
const options = { modulusLength: KEY_PAIR_LENGTH };
const keyObjects = crypto_1.default.generateKeyPairSync(KEY_PAIR_ALGO, options);
const privateKey = keyObjects.privateKey
.export(CONSTANTS_1.PRIVATE_KEY_EXPORT_OPTIONS)
.toString(KEY_FORMAT);
const publicKey = keyObjects.publicKey
.export(CONSTANTS_1.PUBLIC_KEY_EXPORT_OPTIONS)
.toString(KEY_FORMAT);
const encryptOptions = {
cipherTextFormat: KEY_FORMAT,
plainTextFormat: KEY_FORMAT
};
const encryptedPrivateKey = await this.encrypt(privateKey, encryptOptions);
const dataKeyPairObject = {
privateKey,
publicKey,
encryptedPrivateKey
};
return dataKeyPairObject;
}
catch (error) {
const errorCode = `NodeKms::${error.code}`;
throw new NodeKmsError_1.NodeKmsError(error, { errorCode });
}
}
/**
* Encrypts a given string using AES-256-CBC. The key and IV are as defined in `CONFIG.MASTER_KEY_HEX` and `CONFIG.MASTER_IV_HEX` respectively.
*
* @async
* @param [plainText='']
* @param [options]
* @returns
*/
async encrypt(plainText = '', options) {
const { MASTER_KEY_UINT8, MASTER_IV_UINT8, CIPHER_TEXT_FORMAT, PLAIN_TEXT_FORMAT } = this.CONFIG;
const { cipherTextFormat = CIPHER_TEXT_FORMAT, plainTextFormat = PLAIN_TEXT_FORMAT } = options || {};
try {
const plainTextUint8 = new Uint8Array(Buffer.from(plainText, plainTextFormat));
const encryptor = crypto_1.default.createCipheriv(CONSTANTS_1.ENCRYPTION_ALGO, MASTER_KEY_UINT8, MASTER_IV_UINT8);
const encryptedUint8 = new Uint8Array(encryptor.update(plainTextUint8));
const finalEncryptedUint8 = new Uint8Array(encryptor.final());
const cipherTextBuffer = Buffer.concat([
encryptedUint8,
finalEncryptedUint8
]);
const cipherText = cipherTextBuffer.toString(cipherTextFormat);
return cipherText;
}
catch (error) {
const errorCode = `NodeKms::${error.code}`;
throw new NodeKmsError_1.NodeKmsError(error, { errorCode });
}
}
/**
* Decrypts a given string using AES-256-CBC. The key and IV are as defined in `CONFIG.MASTER_KEY_HEX` and `CONFIG.MASTER_IV_HEX` respectively.
*
* @async
* @param [ciphertext='']
* @param [options]
* @returns
*/
async decrypt(ciphertext = '', options) {
const { MASTER_KEY_UINT8, MASTER_IV_UINT8, CIPHER_TEXT_FORMAT, PLAIN_TEXT_FORMAT } = this.CONFIG;
const { cipherTextFormat = CIPHER_TEXT_FORMAT, plainTextFormat = PLAIN_TEXT_FORMAT } = options || {};
const cipherTextUint8 = new Uint8Array(Buffer.from(ciphertext, cipherTextFormat));
try {
const decryptor = crypto_1.default.createDecipheriv(CONSTANTS_1.ENCRYPTION_ALGO, MASTER_KEY_UINT8, MASTER_IV_UINT8);
const decryptedUint8 = new Uint8Array(decryptor.update(cipherTextUint8));
const finalDecryptedUint8 = new Uint8Array(decryptor.final());
const plainTextBuffer = Buffer.concat([
decryptedUint8,
finalDecryptedUint8
]);
const plainText = plainTextBuffer.toString(plainTextFormat);
return plainText;
}
catch (error) {
const errorCode = `NodeKms::${error.code}`;
throw new NodeKmsError_1.NodeKmsError(error, { errorCode });
}
}
}
exports.NodeKms = NodeKms;
/** @ignore */
function validateConfigAndExtend(config) {
const { KEY_SPEC = '', KEY_PAIR_SPEC = '', MASTER_KEY_HEX, MASTER_IV_HEX } = config;
if (!MASTER_KEY_HEX) {
throw new NodeKmsError_1.NodeKmsError(config, ERRORS_1.INVALID_CONFIG_ERROR);
}
if (!MASTER_IV_HEX) {
throw new NodeKmsError_1.NodeKmsError(config, ERRORS_1.INVALID_CONFIG_ERROR);
}
if (!CONSTANTS_1.VALID_KEY_SPECS.includes(KEY_SPEC)) {
throw new NodeKmsError_1.NodeKmsError({ KEY_SPEC, VALID_KEY_SPECS: CONSTANTS_1.VALID_KEY_SPECS }, ERRORS_1.INVALID_KEY_SPEC_ERROR);
}
if (!CONSTANTS_1.VALID_KEY_PAIR_SPECS.includes(KEY_PAIR_SPEC)) {
throw new NodeKmsError_1.NodeKmsError({ KEY_PAIR_SPEC, VALID_KEY_PAIR_SPECS: CONSTANTS_1.VALID_KEY_PAIR_SPECS }, ERRORS_1.INVALID_KEY_PAIR_SPEC_ERROR);
}
const keySpecArray = KEY_SPEC.split('_');
const KEY_ALGO = 'aes';
const KEY_LENGTH = parseInt(keySpecArray[1], 10);
const keyPairSpecArray = KEY_PAIR_SPEC.split('_');
const KEY_PAIR_ALGO = 'rsa';
const KEY_PAIR_LENGTH = parseInt(keyPairSpecArray[1], 10);
const MASTER_KEY_UINT8 = new Uint8Array(Buffer.from(MASTER_KEY_HEX, 'hex'));
const MASTER_IV_UINT8 = new Uint8Array(Buffer.from(MASTER_IV_HEX, 'hex'));
if (MASTER_KEY_UINT8.length !== CONSTANTS_1.MASTER_KEY_LENGTH) {
throw new NodeKmsError_1.NodeKmsError({ MASTER_KEY_HEX }, ERRORS_1.INVALD_MASTER_KEY_HEX_ERROR);
}
if (MASTER_IV_UINT8.length !== CONSTANTS_1.MASTER_IV_LENGTH) {
throw new NodeKmsError_1.NodeKmsError({ MASTER_IV_HEX }, ERRORS_1.INVALID_MASTER_IV_HEX_ERROR);
}
const extendedConfig = {
...config,
KEY_ALGO,
KEY_LENGTH,
KEY_PAIR_ALGO,
KEY_PAIR_LENGTH,
MASTER_KEY_UINT8,
MASTER_IV_UINT8
};
return extendedConfig;
}