/* globals CryptoJS, blowfish */ /** * Cipher operations. * * @author n1474335 [n1474335@gmail.com] * @copyright Crown Copyright 2016 * @license Apache-2.0 * * @namespace */ var Cipher = { /** * @constant * @default */ IO_FORMAT1: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"], /** * @constant * @default */ IO_FORMAT2: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"], /** * @constant * @default */ IO_FORMAT3: ["Hex", "Base64", "UTF16", "UTF16LE", "UTF16BE", "Latin1"], /** * @constant * @default */ IO_FORMAT4: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"], /** * @constant * @default */ MODES: ["CBC", "CFB", "CTR", "OFB", "ECB"], /** * @constant * @default */ PADDING: ["Pkcs7", "Iso97971", "AnsiX923", "Iso10126", "ZeroPadding", "NoPadding"], /** * @constant * @default */ RESULT_TYPE: ["Show all", "Ciphertext", "Key", "IV", "Salt"], /** * Runs encryption operations using the CryptoJS framework. * * @private * @param {function} algo - The CryptoJS algorithm to use * @param {byte_array} input * @param {function} args * @returns {string} */ _enc: function (algo, input, args) { var key = Utils.format[args[0].option].parse(args[0].string || ""), iv = Utils.format[args[1].option].parse(args[1].string || ""), salt = Utils.format[args[2].option].parse(args[2].string || ""), mode = CryptoJS.mode[args[3]], padding = CryptoJS.pad[args[4]], result_option = args[5].toLowerCase(), output_format = args[6]; if (iv.sigBytes === 0) { // Use passphrase rather than key. Need to convert it to a string. key = key.toString(CryptoJS.enc.Latin1); } var encrypted = algo.encrypt(input, key, { salt: salt.sigBytes > 0 ? salt : false, iv: iv.sigBytes > 0 ? iv : null, mode: mode, padding: padding }); var result = ""; if (result_option == "show all") { result += "Key: " + encrypted.key.toString(Utils.format[output_format]); result += "\nIV: " + encrypted.iv.toString(Utils.format[output_format]); if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Utils.format[output_format]); result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Utils.format[output_format]); } else { result = encrypted[result_option].toString(Utils.format[output_format]); } return result; }, /** * Runs decryption operations using the CryptoJS framework. * * @private * @param {function} algo - The CryptoJS algorithm to use * @param {byte_array} input * @param {function} args * @returns {string} */ _dec: function (algo, input, args) { var key = Utils.format[args[0].option].parse(args[0].string || ""), iv = Utils.format[args[1].option].parse(args[1].string || ""), salt = Utils.format[args[2].option].parse(args[2].string || ""), mode = CryptoJS.mode[args[3]], padding = CryptoJS.pad[args[4]], input_format = args[5], output_format = args[6]; // The ZeroPadding option causes a crash when the input length is 0 if (!input.length) { return "No input"; } var ciphertext = Utils.format[input_format].parse(input); if (iv.sigBytes === 0) { // Use passphrase rather than key. Need to convert it to a string. key = key.toString(CryptoJS.enc.Latin1); } var decrypted = algo.decrypt({ ciphertext: ciphertext, salt: salt.sigBytes > 0 ? salt : false }, key, { iv: iv.sigBytes > 0 ? iv : null, mode: mode, padding: padding }); var result; try { result = decrypted.toString(Utils.format[output_format]); } catch (err) { result = "Decrypt error: " + err.message; } return result; }, /** * AES Encrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_aes_enc: function (input, args) { return Cipher._enc(CryptoJS.AES, input, args); }, /** * AES Decrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_aes_dec: function (input, args) { return Cipher._dec(CryptoJS.AES, input, args); }, /** * DES Encrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_des_enc: function (input, args) { return Cipher._enc(CryptoJS.DES, input, args); }, /** * DES Decrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_des_dec: function (input, args) { return Cipher._dec(CryptoJS.DES, input, args); }, /** * Triple DES Encrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_triple_des_enc: function (input, args) { return Cipher._enc(CryptoJS.TripleDES, input, args); }, /** * Triple DES Decrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_triple_des_dec: function (input, args) { return Cipher._dec(CryptoJS.TripleDES, input, args); }, /** * Rabbit Encrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_rabbit_enc: function (input, args) { return Cipher._enc(CryptoJS.Rabbit, input, args); }, /** * Rabbit Decrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_rabbit_dec: function (input, args) { return Cipher._dec(CryptoJS.Rabbit, input, args); }, /** * @constant * @default */ BLOWFISH_MODES: ["ECB", "CBC", "PCBC", "CFB", "OFB", "CTR"], /** * @constant * @default */ BLOWFISH_OUTPUT_TYPES: ["Base64", "Hex", "String", "Raw"], /** * Blowfish Encrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_blowfish_enc: function (input, args) { var key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1), mode = args[1], output_format = args[2]; if (key.length === 0) return "Enter a key"; var enc_hex = blowfish.encrypt(input, key, { outputType: 1, cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode) }), enc = CryptoJS.enc.Hex.parse(enc_hex); return enc.toString(Utils.format[output_format]); }, /** * Blowfish Decrypt operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_blowfish_dec: function (input, args) { var key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1), mode = args[1], input_format = args[2]; if (key.length === 0) return "Enter a key"; input = Utils.format[input_format].parse(input); return blowfish.decrypt(input.toString(CryptoJS.enc.Base64), key, { outputType: 0, // This actually means inputType. The library is weird. cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode) }); }, /** * @constant * @default */ KDF_KEY_SIZE: 256, /** * @constant * @default */ KDF_ITERATIONS: 1, /** * Derive PBKDF2 key operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_pbkdf2: function (input, args) { var key_size = args[0] / 32, iterations = args[1], salt = CryptoJS.enc.Hex.parse(args[2] || ""), input_format = args[3], output_format = args[4], passphrase = Utils.format[input_format].parse(input), key = CryptoJS.PBKDF2(passphrase, salt, { keySize: key_size, iterations: iterations }); return key.toString(Utils.format[output_format]); }, /** * Derive EVP key operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_evpkdf: function (input, args) { var key_size = args[0] / 32, iterations = args[1], salt = CryptoJS.enc.Hex.parse(args[2] || ""), input_format = args[3], output_format = args[4], passphrase = Utils.format[input_format].parse(input), key = CryptoJS.EvpKDF(passphrase, salt, { keySize: key_size, iterations: iterations }); return key.toString(Utils.format[output_format]); }, /** * RC4 operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_rc4: function (input, args) { var message = Utils.format[args[1]].parse(input), passphrase = Utils.format[args[0].option].parse(args[0].string), encrypted = CryptoJS.RC4.encrypt(message, passphrase); return encrypted.ciphertext.toString(Utils.format[args[2]]); }, /** * @constant * @default */ RC4DROP_BYTES: 768, /** * RC4 Drop operation. * * @param {string} input * @param {Object[]} args * @returns {string} */ run_rc4drop: function (input, args) { var message = Utils.format[args[1]].parse(input), passphrase = Utils.format[args[0].option].parse(args[0].string), drop = args[3], encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop }); return encrypted.ciphertext.toString(Utils.format[args[2]]); }, }; /** * Overwriting the CryptoJS OpenSSL key derivation function so that it is possible to not pass a * salt in. * @param {string} password - The password to derive from. * @param {number} keySize - The size in words of the key to generate. * @param {number} ivSize - The size in words of the IV to generate. * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be * generated randomly. If set to false, no salt will be added. * * @returns {CipherParams} A cipher params object with the key, IV, and salt. * * @static * * @example * // Randomly generates a salt * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32); * // Uses the salt 'saltsalt' * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt'); * // Does not use a salt * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, false); */ CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) { // Generate random salt if no salt specified and not set to false // This line changed from `if (!salt) {` to the following if (salt === undefined || salt === null) { salt = CryptoJS.lib.WordArray.random(64/8); } // Derive key and IV var key = CryptoJS.algo.EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt); // Separate key and IV var iv = CryptoJS.lib.WordArray.create(key.words.slice(keySize), ivSize * 4); key.sigBytes = keySize * 4; // Return params return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt }); };