```=javascript
// Node.js core modules
var crypto = require('crypto');
/**
* The encryption algorithm (cipher) type to be used.
* @type {String}
* @const
* @private
*/
var CIPHER_ALGORITHM = 'aes-256-cbc';
var SALT = Buffer.from([0x39, 0x30, 0x00, 0x00, 0x31, 0xd4, 0x00, 0x00])
function EVP_BytesToKey (password, salt, keyBits, ivLen) {
if (!Buffer.isBuffer(password)) password = Buffer.from(password, 'binary')
if (salt) {
if (!Buffer.isBuffer(salt)) salt = Buffer.from(salt, 'binary')
if (salt.length !== 8) throw new RangeError('salt should be Buffer with 8 byte length')
}
var keyLen = keyBits / 8
var key = Buffer.alloc(keyLen)
var iv = Buffer.alloc(ivLen || 0)
var tmp = Buffer.alloc(0)
while (keyLen > 0 || ivLen > 0) {
var hash = new crypto.createHash('sha256')
hash.update(tmp)
hash.update(password)
if (salt) hash.update(salt)
tmp = hash.digest()
var used = 0
if (keyLen > 0) {
var keyStart = key.length - keyLen
used = Math.min(keyLen, tmp.length)
tmp.copy(key, keyStart, 0, used)
keyLen -= used
}
if (used < tmp.length && ivLen > 0) {
var ivStart = iv.length - ivLen
var length = Math.min(ivLen, tmp.length - used)
tmp.copy(iv, ivStart, used, used + length)
ivLen -= length
}
}
tmp.fill(0)
return { key: key, iv: iv }
}
//
// Primary API
//
/**
* An API to allow for greatly simplified AES-256 encryption and decryption using a passphrase of
* any length plus a random Initialization Vector.
* @exports aes256
* @public
*/
var aes256 = {
/**
* Encrypt a clear-text message using AES-256 plus a random Initialization Vector.
* @param {String} key A passphrase of any length to used to generate a symmetric session key.
* @param {String|Buffer} input The clear-text message or buffer to be encrypted.
* @returns {String|Buffer} A custom-encrypted version of the input.
* @public
* @method
*/
encrypt: function(key, input) {
if (typeof key !== 'string' || !key) {
throw new TypeError('Provided "key" must be a non-empty string');
}
var isString = typeof input === 'string';
var isBuffer = Buffer.isBuffer(input);
if (!(isString || isBuffer) || (isString && !input) || (isBuffer && !Buffer.byteLength(input))) {
throw new TypeError('Provided "input" must be a non-empty string or buffer');
}
let evpKey = EVP_BytesToKey(Buffer.from(key, 'utf-8'), SALT, 256, 16);
var cipher = crypto.createCipheriv(CIPHER_ALGORITHM, evpKey.key, evpKey.iv);
var buffer = input;
if (isString) {
let prependZero = '0';
buffer = Buffer.concat([Buffer.from(prependZero.repeat(24 - input.length) + input, 'utf-8'), Buffer.from([ 0x00 ])]);
}
var ciphertext = cipher.update(buffer);
var encrypted = Buffer.concat([ciphertext, cipher.final()]);
if (isString) {
encrypted = encrypted.toString('base64');
}
return encrypted;
},
/**
* Decrypt an encrypted message back to clear-text using AES-256 plus a random Initialization Vector.
* @param {String} key A passphrase of any length to used to generate a symmetric session key.
* @param {String|Buffer} encrypted The encrypted message to be decrypted.
* @returns {String|Buffer} The original plain-text message or buffer.
* @public
* @method
*/
decrypt: function(key, encrypted) {
if (typeof key !== 'string' || !key) {
throw new TypeError('Provided "key" must be a non-empty string');
}
var isString = typeof encrypted === 'string';
var isBuffer = Buffer.isBuffer(encrypted);
if (!(isString || isBuffer) || (isString && !encrypted) || (isBuffer && !Buffer.byteLength(encrypted))) {
throw new TypeError('Provided "encrypted" must be a non-empty string or buffer');
}
let evpKey = EVP_BytesToKey(Buffer.from(key, 'utf-8'), SALT, 256, 16);
var input = encrypted;
if (isString) {
input = Buffer.from(encrypted, 'base64');
if (input.length < 17) {
throw new TypeError('Provided "encrypted" must decrypt to a non-empty string or buffer');
}
} else {
if (Buffer.byteLength(encrypted) < 17) {
throw new TypeError('Provided "encrypted" must decrypt to a non-empty string or buffer');
}
}
// Initialization Vector
var decipher = crypto.createDecipheriv(CIPHER_ALGORITHM, evpKey.key, evpKey.iv);
var ciphertext = input;
var output;
if (isString) {
output = decipher.update(ciphertext) + decipher.final();
} else {
output = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
}
return output.slice(output.length - 9, -1); //remove 0x00 termination character and prepended zeros
}
};
/**
* Create a symmetric cipher with a given passphrase to then encrypt/decrypt data symmetrically.
* @param {String} key A passphrase of any length to used to generate a symmetric session key.
* @public
* @constructor
*/
function AesCipher(key) {
if (typeof key !== 'string' || !key) {
throw new TypeError('Provided "key" must be a non-empty string');
}
/**
* A passphrase of any length to used to generate a symmetric session key.
* @member {String} key
* @readonly
*/
Object.defineProperty(this, 'key', { value: key });
}
/**
* Encrypt a clear-text message using AES-256 plus a random Initialization Vector.
* @param {String} plaintext The clear-text message to be encrypted.
* @returns {String} A custom-encrypted version of the input.
* @public
* @method
*/
AesCipher.prototype.encrypt = function(plaintext) {
return aes256.encrypt(this.key, plaintext);
};
/**
* Decrypt an encrypted message back to clear-text using AES-256 plus a random Initialization Vector.
* @param {String} encrypted The encrypted message to be decrypted.
* @returns {String} The original plain-text message.
* @public
* @method
*/
AesCipher.prototype.decrypt = function(encrypted) {
return aes256.decrypt(this.key, encrypted);
};
//
// API Extension
//
/**
* Create a symmetric cipher with a given passphrase to then encrypt/decrypt data symmetrically.
* @param {String} key A passphrase of any length to used to generate a symmetric session key.
* @returns {AesCipher}
* @public
* @method
*/
aes256.createCipher = function(key) {
return new AesCipher(key);
};
//
// Export the API
//
```