Cipher.js 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. import Utils from "../Utils.js";
  2. import {toBase64} from "../lib/Base64";
  3. import CryptoJS from "crypto-js";
  4. import forge from "imports-loader?jQuery=>null!node-forge/dist/forge.min.js";
  5. import {blowfish as Blowfish} from "sladex-blowfish";
  6. import BigNumber from "bignumber.js";
  7. /**
  8. * Cipher operations.
  9. *
  10. * @author n1474335 [n1474335@gmail.com]
  11. * @copyright Crown Copyright 2016
  12. * @license Apache-2.0
  13. *
  14. * @namespace
  15. */
  16. const Cipher = {
  17. /**
  18. * @constant
  19. * @default
  20. */
  21. IO_FORMAT1: ["Hex", "UTF8", "Latin1", "Base64"],
  22. /**
  23. * @constant
  24. * @default
  25. */
  26. IO_FORMAT2: ["UTF8", "Latin1", "Hex", "Base64"],
  27. /**
  28. * @constant
  29. * @default
  30. */
  31. IO_FORMAT3: ["Raw", "Hex"],
  32. /**
  33. * @constant
  34. * @default
  35. */
  36. IO_FORMAT4: ["Hex", "Raw"],
  37. /**
  38. * @constant
  39. * @default
  40. */
  41. AES_MODES: ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"],
  42. /**
  43. * AES Encrypt operation.
  44. *
  45. * @param {string} input
  46. * @param {Object[]} args
  47. * @returns {string}
  48. */
  49. runAesEnc: function (input, args) {
  50. const key = Utils.convertToByteArray(args[0].string, args[0].option),
  51. iv = Utils.convertToByteArray(args[1].string, args[1].option),
  52. mode = args[2],
  53. inputType = args[3],
  54. outputType = args[4];
  55. if ([16, 24, 32].indexOf(key.length) < 0) {
  56. return `Invalid key length: ${key.length} bytes
  57. The following algorithms will be used based on the size of the key:
  58. 16 bytes = AES-128
  59. 24 bytes = AES-192
  60. 32 bytes = AES-256`;
  61. }
  62. input = Utils.convertToByteString(input, inputType);
  63. const cipher = forge.cipher.createCipher("AES-" + mode, key);
  64. cipher.start({iv: iv});
  65. cipher.update(forge.util.createBuffer(input));
  66. cipher.finish();
  67. if (outputType === "Hex") {
  68. if (mode === "GCM") {
  69. return cipher.output.toHex() + "\n\n" +
  70. "Tag: " + cipher.mode.tag.toHex();
  71. }
  72. return cipher.output.toHex();
  73. } else {
  74. if (mode === "GCM") {
  75. return cipher.output.getBytes() + "\n\n" +
  76. "Tag: " + cipher.mode.tag.getBytes();
  77. }
  78. return cipher.output.getBytes();
  79. }
  80. },
  81. /**
  82. * AES Decrypt operation.
  83. *
  84. * @param {string} input
  85. * @param {Object[]} args
  86. * @returns {string}
  87. */
  88. runAesDec: function (input, args) {
  89. const key = Utils.convertToByteArray(args[0].string, args[0].option),
  90. iv = Utils.convertToByteArray(args[1].string, args[1].option),
  91. mode = args[2],
  92. inputType = args[3],
  93. outputType = args[4],
  94. gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
  95. if ([16, 24, 32].indexOf(key.length) < 0) {
  96. return `Invalid key length: ${key.length} bytes
  97. The following algorithms will be used based on the size of the key:
  98. 16 bytes = AES-128
  99. 24 bytes = AES-192
  100. 32 bytes = AES-256`;
  101. }
  102. input = Utils.convertToByteString(input, inputType);
  103. const decipher = forge.cipher.createDecipher("AES-" + mode, key);
  104. decipher.start({
  105. iv: iv,
  106. tag: gcmTag
  107. });
  108. decipher.update(forge.util.createBuffer(input));
  109. const result = decipher.finish();
  110. if (result) {
  111. return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
  112. } else {
  113. return "Unable to decrypt input with these parameters.";
  114. }
  115. },
  116. /**
  117. * @constant
  118. * @default
  119. */
  120. DES_MODES: ["CBC", "CFB", "OFB", "CTR", "ECB"],
  121. /**
  122. * DES Encrypt operation.
  123. *
  124. * @param {string} input
  125. * @param {Object[]} args
  126. * @returns {string}
  127. */
  128. runDesEnc: function (input, args) {
  129. const key = Utils.convertToByteString(args[0].string, args[0].option),
  130. iv = Utils.convertToByteArray(args[1].string, args[1].option),
  131. mode = args[2],
  132. inputType = args[3],
  133. outputType = args[4];
  134. if (key.length !== 8) {
  135. return `Invalid key length: ${key.length} bytes
  136. DES uses a key length of 8 bytes (64 bits).
  137. Triple DES uses a key length of 24 bytes (192 bits).`;
  138. }
  139. input = Utils.convertToByteString(input, inputType);
  140. const cipher = forge.cipher.createCipher("DES-" + mode, key);
  141. cipher.start({iv: iv});
  142. cipher.update(forge.util.createBuffer(input));
  143. cipher.finish();
  144. return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
  145. },
  146. /**
  147. * DES Decrypt operation.
  148. *
  149. * @param {string} input
  150. * @param {Object[]} args
  151. * @returns {string}
  152. */
  153. runDesDec: function (input, args) {
  154. const key = Utils.convertToByteString(args[0].string, args[0].option),
  155. iv = Utils.convertToByteArray(args[1].string, args[1].option),
  156. mode = args[2],
  157. inputType = args[3],
  158. outputType = args[4];
  159. if (key.length !== 8) {
  160. return `Invalid key length: ${key.length} bytes
  161. DES uses a key length of 8 bytes (64 bits).
  162. Triple DES uses a key length of 24 bytes (192 bits).`;
  163. }
  164. input = Utils.convertToByteString(input, inputType);
  165. const decipher = forge.cipher.createDecipher("DES-" + mode, key);
  166. decipher.start({iv: iv});
  167. decipher.update(forge.util.createBuffer(input));
  168. const result = decipher.finish();
  169. if (result) {
  170. return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
  171. } else {
  172. return "Unable to decrypt input with these parameters.";
  173. }
  174. },
  175. /**
  176. * Triple DES Encrypt operation.
  177. *
  178. * @param {string} input
  179. * @param {Object[]} args
  180. * @returns {string}
  181. */
  182. runTripleDesEnc: function (input, args) {
  183. const key = Utils.convertToByteString(args[0].string, args[0].option),
  184. iv = Utils.convertToByteArray(args[1].string, args[1].option),
  185. mode = args[2],
  186. inputType = args[3],
  187. outputType = args[4];
  188. if (key.length !== 24) {
  189. return `Invalid key length: ${key.length} bytes
  190. Triple DES uses a key length of 24 bytes (192 bits).
  191. DES uses a key length of 8 bytes (64 bits).`;
  192. }
  193. input = Utils.convertToByteString(input, inputType);
  194. const cipher = forge.cipher.createCipher("3DES-" + mode, key);
  195. cipher.start({iv: iv});
  196. cipher.update(forge.util.createBuffer(input));
  197. cipher.finish();
  198. return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
  199. },
  200. /**
  201. * Triple DES Decrypt operation.
  202. *
  203. * @param {string} input
  204. * @param {Object[]} args
  205. * @returns {string}
  206. */
  207. runTripleDesDec: function (input, args) {
  208. const key = Utils.convertToByteString(args[0].string, args[0].option),
  209. iv = Utils.convertToByteArray(args[1].string, args[1].option),
  210. mode = args[2],
  211. inputType = args[3],
  212. outputType = args[4];
  213. if (key.length !== 24) {
  214. return `Invalid key length: ${key.length} bytes
  215. Triple DES uses a key length of 24 bytes (192 bits).
  216. DES uses a key length of 8 bytes (64 bits).`;
  217. }
  218. input = Utils.convertToByteString(input, inputType);
  219. const decipher = forge.cipher.createDecipher("3DES-" + mode, key);
  220. decipher.start({iv: iv});
  221. decipher.update(forge.util.createBuffer(input));
  222. const result = decipher.finish();
  223. if (result) {
  224. return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
  225. } else {
  226. return "Unable to decrypt input with these parameters.";
  227. }
  228. },
  229. /**
  230. * RC2 Encrypt operation.
  231. *
  232. * @param {string} input
  233. * @param {Object[]} args
  234. * @returns {string}
  235. */
  236. runRc2Enc: function (input, args) {
  237. const key = Utils.convertToByteString(args[0].string, args[0].option),
  238. iv = Utils.convertToByteString(args[1].string, args[1].option),
  239. inputType = args[2],
  240. outputType = args[3],
  241. cipher = forge.rc2.createEncryptionCipher(key);
  242. input = Utils.convertToByteString(input, inputType);
  243. cipher.start(iv || null);
  244. cipher.update(forge.util.createBuffer(input));
  245. cipher.finish();
  246. return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
  247. },
  248. /**
  249. * RC2 Decrypt operation.
  250. *
  251. * @param {string} input
  252. * @param {Object[]} args
  253. * @returns {string}
  254. */
  255. runRc2Dec: function (input, args) {
  256. const key = Utils.convertToByteString(args[0].string, args[0].option),
  257. iv = Utils.convertToByteString(args[1].string, args[1].option),
  258. inputType = args[2],
  259. outputType = args[3],
  260. decipher = forge.rc2.createDecryptionCipher(key);
  261. input = Utils.convertToByteString(input, inputType);
  262. decipher.start(iv || null);
  263. decipher.update(forge.util.createBuffer(input));
  264. decipher.finish();
  265. return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
  266. },
  267. /**
  268. * @constant
  269. * @default
  270. */
  271. BLOWFISH_MODES: ["CBC", "PCBC", "CFB", "OFB", "CTR", "ECB"],
  272. /**
  273. * @constant
  274. * @default
  275. */
  276. BLOWFISH_OUTPUT_TYPES: ["Hex", "Base64", "Raw"],
  277. /**
  278. * Lookup table for Blowfish output types.
  279. *
  280. * @private
  281. */
  282. _BLOWFISH_OUTPUT_TYPE_LOOKUP: {
  283. Base64: 0, Hex: 1, String: 2, Raw: 3
  284. },
  285. /**
  286. * Lookup table for Blowfish modes.
  287. *
  288. * @private
  289. */
  290. _BLOWFISH_MODE_LOOKUP: {
  291. ECB: 0, CBC: 1, PCBC: 2, CFB: 3, OFB: 4, CTR: 5
  292. },
  293. /**
  294. * Blowfish Encrypt operation.
  295. *
  296. * @param {string} input
  297. * @param {Object[]} args
  298. * @returns {string}
  299. */
  300. runBlowfishEnc: function (input, args) {
  301. const key = Utils.convertToByteString(args[0].string, args[0].option),
  302. iv = Utils.convertToByteArray(args[1].string, args[1].option),
  303. mode = args[2],
  304. inputType = args[3],
  305. outputType = args[4];
  306. if (key.length === 0) return "Enter a key";
  307. input = Utils.convertToByteString(input, inputType);
  308. Blowfish.setIV(toBase64(iv), 0);
  309. const enc = Blowfish.encrypt(input, key, {
  310. outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[outputType],
  311. cipherMode: Cipher._BLOWFISH_MODE_LOOKUP[mode]
  312. });
  313. return outputType === "Raw" ? Utils.byteArrayToChars(enc) : enc ;
  314. },
  315. /**
  316. * Blowfish Decrypt operation.
  317. *
  318. * @param {string} input
  319. * @param {Object[]} args
  320. * @returns {string}
  321. */
  322. runBlowfishDec: function (input, args) {
  323. const key = Utils.convertToByteString(args[0].string, args[0].option),
  324. iv = Utils.convertToByteArray(args[1].string, args[1].option),
  325. mode = args[2],
  326. inputType = args[3],
  327. outputType = args[4];
  328. if (key.length === 0) return "Enter a key";
  329. input = inputType === "Raw" ? Utils.strToByteArray(input) : input;
  330. Blowfish.setIV(toBase64(iv), 0);
  331. const result = Blowfish.decrypt(input, key, {
  332. outputType: Cipher._BLOWFISH_OUTPUT_TYPE_LOOKUP[inputType], // This actually means inputType. The library is weird.
  333. cipherMode: Cipher._BLOWFISH_MODE_LOOKUP[mode]
  334. });
  335. return outputType === "Hex" ? Utils.toHexFast(Utils.strToByteArray(result)) : result;
  336. },
  337. /**
  338. * @constant
  339. * @default
  340. */
  341. KDF_KEY_SIZE: 128,
  342. /**
  343. * @constant
  344. * @default
  345. */
  346. KDF_ITERATIONS: 1,
  347. /**
  348. * @constant
  349. * @default
  350. */
  351. HASHERS: ["SHA1", "SHA256", "SHA384", "SHA512", "MD5"],
  352. /**
  353. * Derive PBKDF2 key operation.
  354. *
  355. * @param {string} input
  356. * @param {Object[]} args
  357. * @returns {string}
  358. */
  359. runPbkdf2: function (input, args) {
  360. const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
  361. keySize = args[1],
  362. iterations = args[2],
  363. hasher = args[3],
  364. salt = Utils.convertToByteString(args[4].string, args[4].option) ||
  365. forge.random.getBytesSync(keySize),
  366. derivedKey = forge.pkcs5.pbkdf2(passphrase, salt, iterations, keySize / 8, hasher.toLowerCase());
  367. return forge.util.bytesToHex(derivedKey);
  368. },
  369. /**
  370. * Derive EVP key operation.
  371. *
  372. * @param {string} input
  373. * @param {Object[]} args
  374. * @returns {string}
  375. */
  376. runEvpkdf: function (input, args) {
  377. const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
  378. keySize = args[1] / 32,
  379. iterations = args[2],
  380. hasher = args[3],
  381. salt = Utils.convertToByteString(args[4].string, args[4].option),
  382. key = CryptoJS.EvpKDF(passphrase, salt, {
  383. keySize: keySize,
  384. hasher: CryptoJS.algo[hasher],
  385. iterations: iterations,
  386. });
  387. return key.toString(CryptoJS.enc.Hex);
  388. },
  389. /**
  390. * @constant
  391. * @default
  392. */
  393. RC4_KEY_FORMAT: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"],
  394. /**
  395. * @constant
  396. * @default
  397. */
  398. CJS_IO_FORMAT: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"],
  399. /**
  400. * RC4 operation.
  401. *
  402. * @param {string} input
  403. * @param {Object[]} args
  404. * @returns {string}
  405. */
  406. runRc4: function (input, args) {
  407. let message = Cipher._format[args[1]].parse(input),
  408. passphrase = Cipher._format[args[0].option].parse(args[0].string),
  409. encrypted = CryptoJS.RC4.encrypt(message, passphrase);
  410. return encrypted.ciphertext.toString(Cipher._format[args[2]]);
  411. },
  412. /**
  413. * @constant
  414. * @default
  415. */
  416. RC4DROP_BYTES: 768,
  417. /**
  418. * RC4 Drop operation.
  419. *
  420. * @param {string} input
  421. * @param {Object[]} args
  422. * @returns {string}
  423. */
  424. runRc4drop: function (input, args) {
  425. let message = Cipher._format[args[1]].parse(input),
  426. passphrase = Cipher._format[args[0].option].parse(args[0].string),
  427. drop = args[3],
  428. encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop });
  429. return encrypted.ciphertext.toString(Cipher._format[args[2]]);
  430. },
  431. /**
  432. * @constant
  433. * @default
  434. */
  435. PRNG_BYTES: 32,
  436. /**
  437. * @constant
  438. * @default
  439. */
  440. PRNG_OUTPUT: ["Hex", "Integer", "Byte array", "Raw"],
  441. /**
  442. * Pseudo-Random Number Generator operation.
  443. *
  444. * @param {string} input
  445. * @param {Object[]} args
  446. * @returns {string}
  447. */
  448. runPRNG: function(input, args) {
  449. const numBytes = args[0],
  450. outputAs = args[1];
  451. let bytes;
  452. if (ENVIRONMENT_IS_WORKER() && self.crypto) {
  453. bytes = self.crypto.getRandomValues(new Uint8Array(numBytes));
  454. bytes = Utils.arrayBufferToStr(bytes.buffer);
  455. } else {
  456. bytes = forge.random.getBytesSync(numBytes);
  457. }
  458. let value = new BigNumber(0),
  459. i;
  460. switch (outputAs) {
  461. case "Hex":
  462. return forge.util.bytesToHex(bytes);
  463. case "Integer":
  464. for (i = bytes.length - 1; i >= 0; i--) {
  465. value = value.times(256).plus(bytes.charCodeAt(i));
  466. }
  467. return value.toFixed();
  468. case "Byte array":
  469. return JSON.stringify(Utils.strToCharcode(bytes));
  470. case "Raw":
  471. default:
  472. return bytes;
  473. }
  474. },
  475. /**
  476. * Vigenère Encode operation.
  477. *
  478. * @author Matt C [matt@artemisbot.uk]
  479. * @param {string} input
  480. * @param {Object[]} args
  481. * @returns {string}
  482. */
  483. runVigenereEnc: function (input, args) {
  484. let alphabet = "abcdefghijklmnopqrstuvwxyz",
  485. key = args[0].toLowerCase(),
  486. output = "",
  487. fail = 0,
  488. keyIndex,
  489. msgIndex,
  490. chr;
  491. if (!key) return "No key entered";
  492. if (!/^[a-zA-Z]+$/.test(key)) return "The key must consist only of letters";
  493. for (let i = 0; i < input.length; i++) {
  494. if (alphabet.indexOf(input[i]) >= 0) {
  495. // Get the corresponding character of key for the current letter, accounting
  496. // for chars not in alphabet
  497. chr = key[(i - fail) % key.length];
  498. // Get the location in the vigenere square of the key char
  499. keyIndex = alphabet.indexOf(chr);
  500. // Get the location in the vigenere square of the message char
  501. msgIndex = alphabet.indexOf(input[i]);
  502. // Get the encoded letter by finding the sum of indexes modulo 26 and finding
  503. // the letter corresponding to that
  504. output += alphabet[(keyIndex + msgIndex) % 26];
  505. } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
  506. chr = key[(i - fail) % key.length].toLowerCase();
  507. keyIndex = alphabet.indexOf(chr);
  508. msgIndex = alphabet.indexOf(input[i].toLowerCase());
  509. output += alphabet[(keyIndex + msgIndex) % 26].toUpperCase();
  510. } else {
  511. output += input[i];
  512. fail++;
  513. }
  514. }
  515. return output;
  516. },
  517. /**
  518. * Vigenère Decode operation.
  519. *
  520. * @author Matt C [matt@artemisbot.uk]
  521. * @param {string} input
  522. * @param {Object[]} args
  523. * @returns {string}
  524. */
  525. runVigenereDec: function (input, args) {
  526. let alphabet = "abcdefghijklmnopqrstuvwxyz",
  527. key = args[0].toLowerCase(),
  528. output = "",
  529. fail = 0,
  530. keyIndex,
  531. msgIndex,
  532. chr;
  533. if (!key) return "No key entered";
  534. if (!/^[a-zA-Z]+$/.test(key)) return "The key must consist only of letters";
  535. for (let i = 0; i < input.length; i++) {
  536. if (alphabet.indexOf(input[i]) >= 0) {
  537. chr = key[(i - fail) % key.length];
  538. keyIndex = alphabet.indexOf(chr);
  539. msgIndex = alphabet.indexOf(input[i]);
  540. // Subtract indexes from each other, add 26 just in case the value is negative,
  541. // modulo to remove if neccessary
  542. output += alphabet[(msgIndex - keyIndex + alphabet.length) % 26];
  543. } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
  544. chr = key[(i - fail) % key.length].toLowerCase();
  545. keyIndex = alphabet.indexOf(chr);
  546. msgIndex = alphabet.indexOf(input[i].toLowerCase());
  547. output += alphabet[(msgIndex + alphabet.length - keyIndex) % 26].toUpperCase();
  548. } else {
  549. output += input[i];
  550. fail++;
  551. }
  552. }
  553. return output;
  554. },
  555. /**
  556. * @constant
  557. * @default
  558. */
  559. AFFINE_A: 1,
  560. /**
  561. * @constant
  562. * @default
  563. */
  564. AFFINE_B: 0,
  565. /**
  566. * Affine Cipher Encode operation.
  567. *
  568. * @author Matt C [matt@artemisbot.uk]
  569. * @param {string} input
  570. * @param {Object[]} args
  571. * @returns {string}
  572. */
  573. runAffineEnc: function (input, args) {
  574. let alphabet = "abcdefghijklmnopqrstuvwxyz",
  575. a = args[0],
  576. b = args[1],
  577. output = "";
  578. if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
  579. return "The values of a and b can only be integers.";
  580. }
  581. for (let i = 0; i < input.length; i++) {
  582. if (alphabet.indexOf(input[i]) >= 0) {
  583. // Uses the affine function ax+b % m = y (where m is length of the alphabet)
  584. output += alphabet[((a * alphabet.indexOf(input[i])) + b) % 26];
  585. } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
  586. // Same as above, accounting for uppercase
  587. output += alphabet[((a * alphabet.indexOf(input[i].toLowerCase())) + b) % 26].toUpperCase();
  588. } else {
  589. // Non-alphabetic characters
  590. output += input[i];
  591. }
  592. }
  593. return output;
  594. },
  595. /**
  596. * Affine Cipher Decode operation.
  597. *
  598. * @author Matt C [matt@artemisbot.uk]
  599. * @param {string} input
  600. * @param {Object[]} args
  601. * @returns {string}
  602. */
  603. runAffineDec: function (input, args) {
  604. let alphabet = "abcdefghijklmnopqrstuvwxyz",
  605. a = args[0],
  606. b = args[1],
  607. output = "",
  608. aModInv;
  609. if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
  610. return "The values of a and b can only be integers.";
  611. }
  612. if (Utils.gcd(a, 26) !== 1) {
  613. return "The value of a must be coprime to 26.";
  614. }
  615. // Calculates modular inverse of a
  616. aModInv = Utils.modInv(a, 26);
  617. for (let i = 0; i < input.length; i++) {
  618. if (alphabet.indexOf(input[i]) >= 0) {
  619. // Uses the affine decode function (y-b * A') % m = x (where m is length of the alphabet and A' is modular inverse)
  620. output += alphabet[Utils.mod((alphabet.indexOf(input[i]) - b) * aModInv, 26)];
  621. } else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
  622. // Same as above, accounting for uppercase
  623. output += alphabet[Utils.mod((alphabet.indexOf(input[i].toLowerCase()) - b) * aModInv, 26)].toUpperCase();
  624. } else {
  625. // Non-alphabetic characters
  626. output += input[i];
  627. }
  628. }
  629. return output;
  630. },
  631. /**
  632. * Atbash Cipher Encode operation.
  633. *
  634. * @author Matt C [matt@artemisbot.uk]
  635. * @param {string} input
  636. * @param {Object[]} args
  637. * @returns {string}
  638. */
  639. runAtbash: function (input, args) {
  640. return Cipher.runAffineEnc(input, [25, 25]);
  641. },
  642. /**
  643. * Generates a polybius square for the given keyword
  644. *
  645. * @private
  646. * @author Matt C [matt@artemisbot.uk]
  647. * @param {string} keyword - Must be upper case
  648. * @returns {string}
  649. */
  650. _genPolybiusSquare: function (keyword) {
  651. const alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
  652. const polArray = `${keyword}${alpha}`.split("").unique();
  653. let polybius = [];
  654. for (let i = 0; i < 5; i++) {
  655. polybius[i] = polArray.slice(i*5, i*5 + 5);
  656. }
  657. return polybius;
  658. },
  659. /**
  660. * Bifid Cipher Encode operation
  661. *
  662. * @author Matt C [matt@artemisbot.uk]
  663. * @param {string} input
  664. * @param {Object[]} args
  665. * @returns {string}
  666. */
  667. runBifidEnc: function (input, args) {
  668. const keywordStr = args[0].toUpperCase().replace("J", "I"),
  669. keyword = keywordStr.split("").unique(),
  670. alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
  671. let output = "",
  672. xCo = [],
  673. yCo = [],
  674. structure = [],
  675. count = 0;
  676. if (keyword.length > 25)
  677. return "The alphabet keyword must be less than 25 characters.";
  678. if (!/^[a-zA-Z]+$/.test(keywordStr) && keyword.length > 0)
  679. return "The key must consist only of letters";
  680. const polybius = Cipher._genPolybiusSquare(keywordStr);
  681. input.replace("J", "I").split("").forEach(letter => {
  682. let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0,
  683. polInd;
  684. if (alpInd) {
  685. for (let i = 0; i < 5; i++) {
  686. polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
  687. if (polInd >= 0) {
  688. xCo.push(polInd);
  689. yCo.push(i);
  690. }
  691. }
  692. if (alpha.split("").indexOf(letter) >= 0) {
  693. structure.push(true);
  694. } else if (alpInd) {
  695. structure.push(false);
  696. }
  697. } else {
  698. structure.push(letter);
  699. }
  700. });
  701. const trans = `${yCo.join("")}${xCo.join("")}`;
  702. structure.forEach(pos => {
  703. if (typeof pos === "boolean") {
  704. let coords = trans.substr(2*count, 2).split("");
  705. output += pos ?
  706. polybius[coords[0]][coords[1]] :
  707. polybius[coords[0]][coords[1]].toLocaleLowerCase();
  708. count++;
  709. } else {
  710. output += pos;
  711. }
  712. });
  713. return output;
  714. },
  715. /**
  716. * Bifid Cipher Decode operation
  717. *
  718. * @author Matt C [matt@artemisbot.uk]
  719. * @param {string} input
  720. * @param {Object[]} args
  721. * @returns {string}
  722. */
  723. runBifidDec: function (input, args) {
  724. const keywordStr = args[0].toUpperCase().replace("J", "I"),
  725. keyword = keywordStr.split("").unique(),
  726. alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
  727. let output = "",
  728. structure = [],
  729. count = 0,
  730. trans = "";
  731. if (keyword.length > 25)
  732. return "The alphabet keyword must be less than 25 characters.";
  733. if (!/^[a-zA-Z]+$/.test(keywordStr) && keyword.length > 0)
  734. return "The key must consist only of letters";
  735. const polybius = Cipher._genPolybiusSquare(keywordStr);
  736. input.replace("J", "I").split("").forEach((letter) => {
  737. let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0,
  738. polInd;
  739. if (alpInd) {
  740. for (let i = 0; i < 5; i++) {
  741. polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
  742. if (polInd >= 0) {
  743. trans += `${i}${polInd}`;
  744. }
  745. }
  746. if (alpha.split("").indexOf(letter) >= 0) {
  747. structure.push(true);
  748. } else if (alpInd) {
  749. structure.push(false);
  750. }
  751. } else {
  752. structure.push(letter);
  753. }
  754. });
  755. structure.forEach(pos => {
  756. if (typeof pos === "boolean") {
  757. let coords = [trans[count], trans[count+trans.length/2]];
  758. output += pos ?
  759. polybius[coords[0]][coords[1]] :
  760. polybius[coords[0]][coords[1]].toLocaleLowerCase();
  761. count++;
  762. } else {
  763. output += pos;
  764. }
  765. });
  766. return output;
  767. },
  768. /**
  769. * @constant
  770. * @default
  771. */
  772. SUBS_PLAINTEXT: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
  773. /**
  774. * @constant
  775. * @default
  776. */
  777. SUBS_CIPHERTEXT: "XYZABCDEFGHIJKLMNOPQRSTUVW",
  778. /**
  779. * Substitute operation.
  780. *
  781. * @param {string} input
  782. * @param {Object[]} args
  783. * @returns {string}
  784. */
  785. runSubstitute: function (input, args) {
  786. let plaintext = Utils.expandAlphRange(args[0]).join(""),
  787. ciphertext = Utils.expandAlphRange(args[1]).join(""),
  788. output = "",
  789. index = -1;
  790. if (plaintext.length !== ciphertext.length) {
  791. output = "Warning: Plaintext and Ciphertext lengths differ\n\n";
  792. }
  793. for (let i = 0; i < input.length; i++) {
  794. index = plaintext.indexOf(input[i]);
  795. output += index > -1 && index < ciphertext.length ? ciphertext[index] : input[i];
  796. }
  797. return output;
  798. },
  799. /**
  800. * A mapping of string formats to their classes in the CryptoJS library.
  801. *
  802. * @private
  803. * @constant
  804. */
  805. _format: {
  806. "Hex": CryptoJS.enc.Hex,
  807. "Base64": CryptoJS.enc.Base64,
  808. "UTF8": CryptoJS.enc.Utf8,
  809. "UTF16": CryptoJS.enc.Utf16,
  810. "UTF16LE": CryptoJS.enc.Utf16LE,
  811. "UTF16BE": CryptoJS.enc.Utf16BE,
  812. "Latin1": CryptoJS.enc.Latin1,
  813. },
  814. };
  815. export default Cipher;
  816. /**
  817. * Overwriting the CryptoJS OpenSSL key derivation function so that it is possible to not pass a
  818. * salt in.
  819. * @param {string} password - The password to derive from.
  820. * @param {number} keySize - The size in words of the key to generate.
  821. * @param {number} ivSize - The size in words of the IV to generate.
  822. * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be
  823. * generated randomly. If set to false, no salt will be added.
  824. *
  825. * @returns {CipherParams} A cipher params object with the key, IV, and salt.
  826. *
  827. * @static
  828. *
  829. * @example
  830. * // Randomly generates a salt
  831. * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
  832. * // Uses the salt 'saltsalt'
  833. * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
  834. * // Does not use a salt
  835. * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, false);
  836. */
  837. CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) {
  838. // Generate random salt if no salt specified and not set to false
  839. // This line changed from `if (!salt) {` to the following
  840. if (salt === undefined || salt === null) {
  841. salt = CryptoJS.lib.WordArray.random(64/8);
  842. }
  843. // Derive key and IV
  844. const key = CryptoJS.algo.EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt);
  845. // Separate key and IV
  846. const iv = CryptoJS.lib.WordArray.create(key.words.slice(keySize), ivSize * 4);
  847. key.sigBytes = keySize * 4;
  848. // Return params
  849. return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt });
  850. };
  851. /**
  852. * Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
  853. * the hex string.
  854. *
  855. * @param {string} hexStr
  856. * @returns {CryptoJS.lib.WordArray}
  857. */
  858. CryptoJS.enc.Hex.parse = function (hexStr) {
  859. // Remove whitespace
  860. hexStr = hexStr.replace(/\s/g, "");
  861. // Shortcut
  862. const hexStrLength = hexStr.length;
  863. // Convert
  864. const words = [];
  865. for (let i = 0; i < hexStrLength; i += 2) {
  866. words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
  867. }
  868. return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
  869. };