123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- /* 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]]);
- },
-
- /**
- * @constant
- * @default
- */
- VIG_ENC_KEY: "cipher",
- /**
- * Vigenere cipher encode.
- *
- * @author Matt C [matt@artemisbot.pw]
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run_vigenc: function (input, args) {
- var alphabet = "abcdefghijklmnopqrstuvwxyz",
- keyword = args[0].toLowerCase(),
- output = "",
- fail = 0,
- keyIndex,
- msgIndex,
- chr;
- if (keyword) {
- if (/^[a-zA-Z]+$/.test(keyword)) {
- for (var i = 0; i < input.length; i++) {
- if (alphabet.indexOf(input[i]) >= 0) {
- chr = keyword[(i - fail) % keyword.length]; //Gets the corresponding character of keyword for current letter, accounting for chars not in alphabet
- keyIndex = alphabet.indexOf(chr); //Gets location in vigenere square of keyword char
- msgIndex = alphabet.indexOf(input[i]); //Gets location in vigenere square of message char
- output += alphabet[(keyIndex + msgIndex) % 26]; //Gets encoded letter by finding sum of indexes modulo 26 and finding the letter corresponding to that
- } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
- chr = keyword[(i - fail) % keyword.length].toLowerCase();
- keyIndex = alphabet.indexOf(chr);
- msgIndex = alphabet.indexOf(input[i].toLowerCase());
- output += alphabet[(keyIndex + msgIndex) % 26].toUpperCase();
- } else {
- output += input[i];
- fail++;
- }
- }
- } else {
- throw "Keyword can only consist of letters.";
- }
- } else {
- throw "A keyword is required.";
- }
- return output;
- },
- /**
- * @constant
- * @default
- */
- VIG_DEC_KEY: "cipher",
- /**
- * Vigenere cipher decode.
- *
- * @author Matt C [matt@artemisbot.pw]
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run_vigdec: function (input, args) {
- var alphabet = "abcdefghijklmnopqrstuvwxyz",
- keyword = args[0].toLowerCase(),
- output = "",
- fail = 0,
- keyIndex,
- msgIndex,
- chr;
- if (keyword) {
- if (/^[a-zA-Z]+$/.test(keyword)) {
- for (var i = 0; i < input.length; i++) {
- if (alphabet.indexOf(input[i]) >= 0) {
- chr = keyword[(i - fail) % keyword.length];
- keyIndex = alphabet.indexOf(chr);
- msgIndex = alphabet.indexOf(input[i]);
- output += alphabet[(msgIndex - keyIndex + alphabet.length ) % 26]; //subtract indexes from each other, add 26 just in case the value is negative, modulo to remove if neccessary
- } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
- chr = keyword[(i - fail) % keyword.length].toLowerCase();
- keyIndex = alphabet.indexOf(chr);
- msgIndex = alphabet.indexOf(input[i].toLowerCase());
- output += alphabet[(msgIndex + alphabet.length - keyIndex) % 26].toUpperCase();
- } else {
- output += input[i];
- fail++;
- }
- }
- } else {
- throw "Keyword can only consist of letters.";
- }
- } else {
- throw "A keyword is required.";
- }
- return output;
- }
- };
- /**
- * 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 });
- };
|