Cipher.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /* globals CryptoJS, blowfish */
  2. /**
  3. * Cipher operations.
  4. *
  5. * @author n1474335 [n1474335@gmail.com]
  6. * @copyright Crown Copyright 2016
  7. * @license Apache-2.0
  8. *
  9. * @namespace
  10. */
  11. var Cipher = {
  12. /**
  13. * @constant
  14. * @default
  15. */
  16. IO_FORMAT1: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
  17. /**
  18. * @constant
  19. * @default
  20. */
  21. IO_FORMAT2: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"],
  22. /**
  23. * @constant
  24. * @default
  25. */
  26. IO_FORMAT3: ["Hex", "Base64", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
  27. /**
  28. * @constant
  29. * @default
  30. */
  31. IO_FORMAT4: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"],
  32. /**
  33. * @constant
  34. * @default
  35. */
  36. MODES: ["CBC", "CFB", "CTR", "OFB", "ECB"],
  37. /**
  38. * @constant
  39. * @default
  40. */
  41. PADDING: ["Pkcs7", "Iso97971", "AnsiX923", "Iso10126", "ZeroPadding", "NoPadding"],
  42. /**
  43. * @constant
  44. * @default
  45. */
  46. RESULT_TYPE: ["Show all", "Ciphertext", "Key", "IV", "Salt"],
  47. /**
  48. * Runs encryption operations using the CryptoJS framework.
  49. *
  50. * @private
  51. * @param {function} algo - The CryptoJS algorithm to use
  52. * @param {byte_array} input
  53. * @param {function} args
  54. * @returns {string}
  55. */
  56. _enc: function (algo, input, args) {
  57. var key = Utils.format[args[0].option].parse(args[0].string || ""),
  58. iv = Utils.format[args[1].option].parse(args[1].string || ""),
  59. salt = Utils.format[args[2].option].parse(args[2].string || ""),
  60. mode = CryptoJS.mode[args[3]],
  61. padding = CryptoJS.pad[args[4]],
  62. result_option = args[5].toLowerCase(),
  63. output_format = args[6];
  64. if (iv.sigBytes === 0) {
  65. // Use passphrase rather than key. Need to convert it to a string.
  66. key = key.toString(CryptoJS.enc.Latin1);
  67. }
  68. var encrypted = algo.encrypt(input, key, {
  69. salt: salt.sigBytes > 0 ? salt : false,
  70. iv: iv.sigBytes > 0 ? iv : null,
  71. mode: mode,
  72. padding: padding
  73. });
  74. var result = "";
  75. if (result_option == "show all") {
  76. result += "Key: " + encrypted.key.toString(Utils.format[output_format]);
  77. result += "\nIV: " + encrypted.iv.toString(Utils.format[output_format]);
  78. if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Utils.format[output_format]);
  79. result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Utils.format[output_format]);
  80. } else {
  81. result = encrypted[result_option].toString(Utils.format[output_format]);
  82. }
  83. return result;
  84. },
  85. /**
  86. * Runs decryption operations using the CryptoJS framework.
  87. *
  88. * @private
  89. * @param {function} algo - The CryptoJS algorithm to use
  90. * @param {byte_array} input
  91. * @param {function} args
  92. * @returns {string}
  93. */
  94. _dec: function (algo, input, args) {
  95. var key = Utils.format[args[0].option].parse(args[0].string || ""),
  96. iv = Utils.format[args[1].option].parse(args[1].string || ""),
  97. salt = Utils.format[args[2].option].parse(args[2].string || ""),
  98. mode = CryptoJS.mode[args[3]],
  99. padding = CryptoJS.pad[args[4]],
  100. input_format = args[5],
  101. output_format = args[6];
  102. // The ZeroPadding option causes a crash when the input length is 0
  103. if (!input.length) {
  104. return "No input";
  105. }
  106. var ciphertext = Utils.format[input_format].parse(input);
  107. if (iv.sigBytes === 0) {
  108. // Use passphrase rather than key. Need to convert it to a string.
  109. key = key.toString(CryptoJS.enc.Latin1);
  110. }
  111. var decrypted = algo.decrypt({
  112. ciphertext: ciphertext,
  113. salt: salt.sigBytes > 0 ? salt : false
  114. }, key, {
  115. iv: iv.sigBytes > 0 ? iv : null,
  116. mode: mode,
  117. padding: padding
  118. });
  119. var result;
  120. try {
  121. result = decrypted.toString(Utils.format[output_format]);
  122. } catch (err) {
  123. result = "Decrypt error: " + err.message;
  124. }
  125. return result;
  126. },
  127. /**
  128. * AES Encrypt operation.
  129. *
  130. * @param {string} input
  131. * @param {Object[]} args
  132. * @returns {string}
  133. */
  134. run_aes_enc: function (input, args) {
  135. return Cipher._enc(CryptoJS.AES, input, args);
  136. },
  137. /**
  138. * AES Decrypt operation.
  139. *
  140. * @param {string} input
  141. * @param {Object[]} args
  142. * @returns {string}
  143. */
  144. run_aes_dec: function (input, args) {
  145. return Cipher._dec(CryptoJS.AES, input, args);
  146. },
  147. /**
  148. * DES Encrypt operation.
  149. *
  150. * @param {string} input
  151. * @param {Object[]} args
  152. * @returns {string}
  153. */
  154. run_des_enc: function (input, args) {
  155. return Cipher._enc(CryptoJS.DES, input, args);
  156. },
  157. /**
  158. * DES Decrypt operation.
  159. *
  160. * @param {string} input
  161. * @param {Object[]} args
  162. * @returns {string}
  163. */
  164. run_des_dec: function (input, args) {
  165. return Cipher._dec(CryptoJS.DES, input, args);
  166. },
  167. /**
  168. * Triple DES Encrypt operation.
  169. *
  170. * @param {string} input
  171. * @param {Object[]} args
  172. * @returns {string}
  173. */
  174. run_triple_des_enc: function (input, args) {
  175. return Cipher._enc(CryptoJS.TripleDES, input, args);
  176. },
  177. /**
  178. * Triple DES Decrypt operation.
  179. *
  180. * @param {string} input
  181. * @param {Object[]} args
  182. * @returns {string}
  183. */
  184. run_triple_des_dec: function (input, args) {
  185. return Cipher._dec(CryptoJS.TripleDES, input, args);
  186. },
  187. /**
  188. * Rabbit Encrypt operation.
  189. *
  190. * @param {string} input
  191. * @param {Object[]} args
  192. * @returns {string}
  193. */
  194. run_rabbit_enc: function (input, args) {
  195. return Cipher._enc(CryptoJS.Rabbit, input, args);
  196. },
  197. /**
  198. * Rabbit Decrypt operation.
  199. *
  200. * @param {string} input
  201. * @param {Object[]} args
  202. * @returns {string}
  203. */
  204. run_rabbit_dec: function (input, args) {
  205. return Cipher._dec(CryptoJS.Rabbit, input, args);
  206. },
  207. /**
  208. * @constant
  209. * @default
  210. */
  211. BLOWFISH_MODES: ["ECB", "CBC", "PCBC", "CFB", "OFB", "CTR"],
  212. /**
  213. * @constant
  214. * @default
  215. */
  216. BLOWFISH_OUTPUT_TYPES: ["Base64", "Hex", "String", "Raw"],
  217. /**
  218. * Blowfish Encrypt operation.
  219. *
  220. * @param {string} input
  221. * @param {Object[]} args
  222. * @returns {string}
  223. */
  224. run_blowfish_enc: function (input, args) {
  225. var key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
  226. mode = args[1],
  227. output_format = args[2];
  228. if (key.length === 0) return "Enter a key";
  229. var enc_hex = blowfish.encrypt(input, key, {
  230. outputType: 1,
  231. cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode)
  232. }),
  233. enc = CryptoJS.enc.Hex.parse(enc_hex);
  234. return enc.toString(Utils.format[output_format]);
  235. },
  236. /**
  237. * Blowfish Decrypt operation.
  238. *
  239. * @param {string} input
  240. * @param {Object[]} args
  241. * @returns {string}
  242. */
  243. run_blowfish_dec: function (input, args) {
  244. var key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
  245. mode = args[1],
  246. input_format = args[2];
  247. if (key.length === 0) return "Enter a key";
  248. input = Utils.format[input_format].parse(input);
  249. return blowfish.decrypt(input.toString(CryptoJS.enc.Base64), key, {
  250. outputType: 0, // This actually means inputType. The library is weird.
  251. cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode)
  252. });
  253. },
  254. /**
  255. * @constant
  256. * @default
  257. */
  258. KDF_KEY_SIZE: 256,
  259. /**
  260. * @constant
  261. * @default
  262. */
  263. KDF_ITERATIONS: 1,
  264. /**
  265. * Derive PBKDF2 key operation.
  266. *
  267. * @param {string} input
  268. * @param {Object[]} args
  269. * @returns {string}
  270. */
  271. run_pbkdf2: function (input, args) {
  272. var key_size = args[0] / 32,
  273. iterations = args[1],
  274. salt = CryptoJS.enc.Hex.parse(args[2] || ""),
  275. input_format = args[3],
  276. output_format = args[4],
  277. passphrase = Utils.format[input_format].parse(input),
  278. key = CryptoJS.PBKDF2(passphrase, salt, { keySize: key_size, iterations: iterations });
  279. return key.toString(Utils.format[output_format]);
  280. },
  281. /**
  282. * Derive EVP key operation.
  283. *
  284. * @param {string} input
  285. * @param {Object[]} args
  286. * @returns {string}
  287. */
  288. run_evpkdf: function (input, args) {
  289. var key_size = args[0] / 32,
  290. iterations = args[1],
  291. salt = CryptoJS.enc.Hex.parse(args[2] || ""),
  292. input_format = args[3],
  293. output_format = args[4],
  294. passphrase = Utils.format[input_format].parse(input),
  295. key = CryptoJS.EvpKDF(passphrase, salt, { keySize: key_size, iterations: iterations });
  296. return key.toString(Utils.format[output_format]);
  297. },
  298. /**
  299. * RC4 operation.
  300. *
  301. * @param {string} input
  302. * @param {Object[]} args
  303. * @returns {string}
  304. */
  305. run_rc4: function (input, args) {
  306. var message = Utils.format[args[1]].parse(input),
  307. passphrase = Utils.format[args[0].option].parse(args[0].string),
  308. encrypted = CryptoJS.RC4.encrypt(message, passphrase);
  309. return encrypted.ciphertext.toString(Utils.format[args[2]]);
  310. },
  311. /**
  312. * @constant
  313. * @default
  314. */
  315. RC4DROP_BYTES: 768,
  316. /**
  317. * RC4 Drop operation.
  318. *
  319. * @param {string} input
  320. * @param {Object[]} args
  321. * @returns {string}
  322. */
  323. run_rc4drop: function (input, args) {
  324. var message = Utils.format[args[1]].parse(input),
  325. passphrase = Utils.format[args[0].option].parse(args[0].string),
  326. drop = args[3],
  327. encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop });
  328. return encrypted.ciphertext.toString(Utils.format[args[2]]);
  329. },
  330. /**
  331. * @constant
  332. * @default
  333. */
  334. VIG_ENC_KEY: "cipher",
  335. /**
  336. * Vigenere cipher encode.
  337. *
  338. * @author Matt C [matt@artemisbot.pw]
  339. * @param {string} input
  340. * @param {Object[]} args
  341. * @returns {string}
  342. */
  343. run_vigenc: function (input, args) {
  344. var alphabet = "abcdefghijklmnopqrstuvwxyz",
  345. keyword = args[0].toLowerCase(),
  346. output = "",
  347. fail = 0,
  348. keyIndex,
  349. msgIndex,
  350. chr;
  351. if (keyword) {
  352. if (/^[a-zA-Z]+$/.test(keyword)) {
  353. for (var i = 0; i < input.length; i++) {
  354. if (alphabet.indexOf(input[i]) >= 0) {
  355. chr = keyword[(i - fail) % keyword.length]; //Gets the corresponding character of keyword for current letter, accounting for chars not in alphabet
  356. keyIndex = alphabet.indexOf(chr); //Gets location in vigenere square of keyword char
  357. msgIndex = alphabet.indexOf(input[i]); //Gets location in vigenere square of message char
  358. output += alphabet[(keyIndex + msgIndex) % 26]; //Gets encoded letter by finding sum of indexes modulo 26 and finding the letter corresponding to that
  359. } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
  360. chr = keyword[(i - fail) % keyword.length].toLowerCase();
  361. keyIndex = alphabet.indexOf(chr);
  362. msgIndex = alphabet.indexOf(input[i].toLowerCase());
  363. output += alphabet[(keyIndex + msgIndex) % 26].toUpperCase();
  364. } else {
  365. output += input[i];
  366. fail++;
  367. }
  368. }
  369. } else {
  370. throw "Keyword can only consist of letters.";
  371. }
  372. } else {
  373. throw "A keyword is required.";
  374. }
  375. return output;
  376. },
  377. /**
  378. * @constant
  379. * @default
  380. */
  381. VIG_DEC_KEY: "cipher",
  382. /**
  383. * Vigenere cipher decode.
  384. *
  385. * @author Matt C [matt@artemisbot.pw]
  386. * @param {string} input
  387. * @param {Object[]} args
  388. * @returns {string}
  389. */
  390. run_vigdec: function (input, args) {
  391. var alphabet = "abcdefghijklmnopqrstuvwxyz",
  392. keyword = args[0].toLowerCase(),
  393. output = "",
  394. fail = 0,
  395. keyIndex,
  396. msgIndex,
  397. chr;
  398. if (keyword) {
  399. if (/^[a-zA-Z]+$/.test(keyword)) {
  400. for (var i = 0; i < input.length; i++) {
  401. if (alphabet.indexOf(input[i]) >= 0) {
  402. chr = keyword[(i - fail) % keyword.length];
  403. keyIndex = alphabet.indexOf(chr);
  404. msgIndex = alphabet.indexOf(input[i]);
  405. 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
  406. } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
  407. chr = keyword[(i - fail) % keyword.length].toLowerCase();
  408. keyIndex = alphabet.indexOf(chr);
  409. msgIndex = alphabet.indexOf(input[i].toLowerCase());
  410. output += alphabet[(msgIndex + alphabet.length - keyIndex) % 26].toUpperCase();
  411. } else {
  412. output += input[i];
  413. fail++;
  414. }
  415. }
  416. } else {
  417. throw "Keyword can only consist of letters.";
  418. }
  419. } else {
  420. throw "A keyword is required.";
  421. }
  422. return output;
  423. }
  424. };
  425. /**
  426. * Overwriting the CryptoJS OpenSSL key derivation function so that it is possible to not pass a
  427. * salt in.
  428. * @param {string} password - The password to derive from.
  429. * @param {number} keySize - The size in words of the key to generate.
  430. * @param {number} ivSize - The size in words of the IV to generate.
  431. * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be
  432. * generated randomly. If set to false, no salt will be added.
  433. *
  434. * @returns {CipherParams} A cipher params object with the key, IV, and salt.
  435. *
  436. * @static
  437. *
  438. * @example
  439. * // Randomly generates a salt
  440. * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
  441. * // Uses the salt 'saltsalt'
  442. * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
  443. * // Does not use a salt
  444. * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, false);
  445. */
  446. CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) {
  447. // Generate random salt if no salt specified and not set to false
  448. // This line changed from `if (!salt) {` to the following
  449. if (salt === undefined || salt === null) {
  450. salt = CryptoJS.lib.WordArray.random(64/8);
  451. }
  452. // Derive key and IV
  453. var key = CryptoJS.algo.EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt);
  454. // Separate key and IV
  455. var iv = CryptoJS.lib.WordArray.create(key.words.slice(keySize), ivSize * 4);
  456. key.sigBytes = keySize * 4;
  457. // Return params
  458. return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt });
  459. };