encryptjs.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*!
  2. * Copyright (c) 2015 Sri Harsha <sri.harsha@zenq.com>
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining
  5. * a copy of this software and associated documentation files (the
  6. * "Software"), to deal in the Software without restriction, including
  7. * without limitation the rights to use, copy, modify, merge, publish,
  8. * distribute, sublicense, and/or sell copies of the Software, and to
  9. * permit persons to whom the Software is furnished to do so, subject to
  10. * the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be
  13. * included in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. */
  23. (function (name, definition) {
  24. if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
  25. module.exports = definition();
  26. } else if (typeof define === 'function' && typeof define.amd === 'object') {
  27. define(definition);
  28. } else if (typeof define === 'function' && typeof define.petal === 'object') {
  29. define(name, [], definition);
  30. } else {
  31. this[name] = definition();
  32. }
  33. })('encryptjs', function (encryptjs) {
  34. var rl;
  35. //Electron doesnt support stdin, so dont setup CLI if its not available.
  36. encryptjs = { version: '1.0.0' };
  37. //Right before exporting the validator object, pass each of the builtins
  38. //through extend() so that their first argument is coerced to a string
  39. encryptjs.init = function () {
  40. console.log("--------------------Applying Encryption Algorithm------------------ ");
  41. };
  42. 'use strict';
  43. // if (typeof module!='undefined' && module.exports)
  44. var Algo = window["Algo"] // CommonJS (Node.js)
  45. encryptjs.encrypt = function(plaintext, password, nBits) {
  46. var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4)
  47. if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys
  48. plaintext = String(plaintext).utf8Encode();
  49. password = String(password).utf8Encode();
  50. // use AES itself to encrypt password to get cipher key (using plain password as source for key
  51. // expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use)
  52. var nBytes = nBits/8; // no bytes in key (16/24/32)
  53. var pwBytes = new Array(nBytes);
  54. for (var i=0; i<nBytes; i++) { // use 1st 16/24/32 chars of password for key
  55. pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
  56. }
  57. var key = Algo.cipher(pwBytes, Algo.keyExpansion(pwBytes)); // gives us 16-byte key
  58. key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long
  59. // initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A �B.2): [0-1] = millisec,
  60. // [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
  61. var counterBlock = new Array(blockSize);
  62. var nonce = (new Date()).getTime(); // timestamp: milliseconds since 1-Jan-1970
  63. var nonceMs = nonce%1000;
  64. var nonceSec = Math.floor(nonce/1000);
  65. var nonceRnd = Math.floor(Math.random()*0xffff);
  66. // for debugging: nonce = nonceMs = nonceSec = nonceRnd = 0;
  67. for (var i=0; i<2; i++) counterBlock[i] = (nonceMs >>> i*8) & 0xff;
  68. for (var i=0; i<2; i++) counterBlock[i+2] = (nonceRnd >>> i*8) & 0xff;
  69. for (var i=0; i<4; i++) counterBlock[i+4] = (nonceSec >>> i*8) & 0xff;
  70. // and convert it to a string to go on the front of the ciphertext
  71. var ctrTxt = '';
  72. for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]);
  73. // generate key schedule - an expansion of the key into distinct Key Rounds for each round
  74. var keySchedule = Algo.keyExpansion(key);
  75. var blockCount = Math.ceil(plaintext.length/blockSize);
  76. var ciphertxt = new Array(blockCount); // ciphertext as array of strings
  77. for (var b=0; b<blockCount; b++) {
  78. // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
  79. // done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
  80. for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff;
  81. for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8);
  82. var cipherCntr = Algo.cipher(counterBlock, keySchedule); // -- encrypt counter block --
  83. // block size is reduced on final block
  84. var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1;
  85. var cipherChar = new Array(blockLength);
  86. for (var i=0; i<blockLength; i++) { // -- xor plaintext with ciphered counter char-by-char --
  87. cipherChar[i] = cipherCntr[i] ^ plaintext.charCodeAt(b*blockSize+i);
  88. cipherChar[i] = String.fromCharCode(cipherChar[i]);
  89. }
  90. ciphertxt[b] = cipherChar.join('');
  91. }
  92. // use Array.join() for better performance than repeated string appends
  93. var ciphertext = ctrTxt + ciphertxt.join('');
  94. // ciphertext = ciphertext.base64Encode();
  95. ciphertext = encryptjs.base64Encode(ciphertext);
  96. return ciphertext;
  97. };
  98. encryptjs.decrypt = function(ciphertext, password, nBits) {
  99. var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
  100. if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys
  101. // ciphertext = String(ciphertext).base64Decode();
  102. ciphertext = encryptjs.base64Decode(String(ciphertext));
  103. password = String(password).utf8Encode();
  104. // use AES to encrypt password (mirroring encrypt routine)
  105. var nBytes = nBits/8; // no bytes in key
  106. var pwBytes = new Array(nBytes);
  107. for (var i=0; i<nBytes; i++) {
  108. pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
  109. }
  110. var key = Algo.cipher(pwBytes, Algo.keyExpansion(pwBytes));
  111. key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long
  112. // recover nonce from 1st 8 bytes of ciphertext
  113. var counterBlock = new Array(8);
  114. var ctrTxt = ciphertext.slice(0, 8);
  115. for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i);
  116. // generate key schedule
  117. var keySchedule = Algo.keyExpansion(key);
  118. // separate ciphertext into blocks (skipping past initial 8 bytes)
  119. var nBlocks = Math.ceil((ciphertext.length-8) / blockSize);
  120. var ct = new Array(nBlocks);
  121. for (var b=0; b<nBlocks; b++) ct[b] = ciphertext.slice(8+b*blockSize, 8+b*blockSize+blockSize);
  122. ciphertext = ct; // ciphertext is now array of block-length strings
  123. // plaintext will get generated block-by-block into array of block-length strings
  124. var plaintxt = new Array(ciphertext.length);
  125. for (var b=0; b<nBlocks; b++) {
  126. // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
  127. for (var c=0; c<4; c++) counterBlock[15-c] = ((b) >>> c*8) & 0xff;
  128. for (var c=0; c<4; c++) counterBlock[15-c-4] = (((b+1)/0x100000000-1) >>> c*8) & 0xff;
  129. var cipherCntr = Algo.cipher(counterBlock, keySchedule); // encrypt counter block
  130. var plaintxtByte = new Array(ciphertext[b].length);
  131. for (var i=0; i<ciphertext[b].length; i++) {
  132. // -- xor plaintxt with ciphered counter byte-by-byte --
  133. plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b].charCodeAt(i);
  134. plaintxtByte[i] = String.fromCharCode(plaintxtByte[i]);
  135. }
  136. plaintxt[b] = plaintxtByte.join('');
  137. }
  138. // join array of blocks into single plaintext string
  139. var plaintext = plaintxt.join('');
  140. plaintext = plaintext.utf8Decode(); // decode from UTF8 back to Unicode multi-byte chars
  141. return plaintext;
  142. };
  143. //----------------base64 start---------------
  144. var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  145. encryptjs.base64Encode = function (input) {
  146. var output = "";
  147. var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
  148. var i = 0;
  149. input = encryptjs._utf8_encode(input);
  150. while (i < input.length) {
  151. chr1 = input.charCodeAt(i++);
  152. chr2 = input.charCodeAt(i++);
  153. chr3 = input.charCodeAt(i++);
  154. enc1 = chr1 >> 2;
  155. enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  156. enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  157. enc4 = chr3 & 63;
  158. if (isNaN(chr2)) {
  159. enc3 = enc4 = 64;
  160. } else if (isNaN(chr3)) {
  161. enc4 = 64;
  162. }
  163. output = output +
  164. _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
  165. _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
  166. }
  167. return output;
  168. }
  169. encryptjs.base64Decode = function (input) {
  170. var output = "";
  171. var chr1, chr2, chr3;
  172. var enc1, enc2, enc3, enc4;
  173. var i = 0;
  174. input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  175. while (i < input.length) {
  176. enc1 = _keyStr.indexOf(input.charAt(i++));
  177. enc2 = _keyStr.indexOf(input.charAt(i++));
  178. enc3 = _keyStr.indexOf(input.charAt(i++));
  179. enc4 = _keyStr.indexOf(input.charAt(i++));
  180. chr1 = (enc1 << 2) | (enc2 >> 4);
  181. chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
  182. chr3 = ((enc3 & 3) << 6) | enc4;
  183. output = output + String.fromCharCode(chr1);
  184. if (enc3 != 64) {
  185. output = output + String.fromCharCode(chr2);
  186. }
  187. if (enc4 != 64) {
  188. output = output + String.fromCharCode(chr3);
  189. }
  190. }
  191. output = encryptjs._utf8_decode(output);
  192. return output;
  193. }
  194. // private method for UTF-8 encoding
  195. encryptjs._utf8_encode = function (string) {
  196. string = string.replace(/\r\n/g,"\n");
  197. var utftext = "";
  198. for (var n = 0; n < string.length; n++) {
  199. var c = string.charCodeAt(n);
  200. if (c < 128) {
  201. utftext += String.fromCharCode(c);
  202. } else if((c > 127) && (c < 2048)) {
  203. utftext += String.fromCharCode((c >> 6) | 192);
  204. utftext += String.fromCharCode((c & 63) | 128);
  205. } else {
  206. utftext += String.fromCharCode((c >> 12) | 224);
  207. utftext += String.fromCharCode(((c >> 6) & 63) | 128);
  208. utftext += String.fromCharCode((c & 63) | 128);
  209. }
  210. }
  211. return utftext;
  212. }
  213. // private method for UTF-8 decoding
  214. encryptjs._utf8_decode = function (utftext) {
  215. var string = "";
  216. var i = 0;
  217. var c = 0;
  218. var c1 = 0;
  219. var c2 = 0;
  220. while ( i < utftext.length ) {
  221. c = utftext.charCodeAt(i);
  222. if (c < 128) {
  223. string += String.fromCharCode(c);
  224. i++;
  225. } else if((c > 191) && (c < 224)) {
  226. c2 = utftext.charCodeAt(i+1);
  227. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  228. i += 2;
  229. } else {
  230. c2 = utftext.charCodeAt(i+1);
  231. c3 = utftext.charCodeAt(i+2);
  232. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  233. i += 3;
  234. }
  235. }
  236. return string;
  237. }
  238. //------------------base64 end----------------
  239. encryptjs.getTextEncryptAndSaveToTextFile = function(filePath,password,nBits) {
  240. if (!rl) throw Error("Command line not supported on this platform");
  241. rl.question("Enter the text to be encrypted: ", function(answer) {
  242. // TODO: Log the answer in a database
  243. console.log("'"+answer+"' This text will be encrypted and stored in a text file 'encrypted.txt'");
  244. var cipherText=encryptjs.encrypt(answer,password,nBits);
  245. rl.close();
  246. });
  247. };
  248. encryptjs.getTextEncryptAndSaveToJSONFile = function(filePath,password,nBits) {
  249. if (!rl) throw Error("Command line not supported on this platform");
  250. rl.question("Enter the text to be encrypted: ", function(answer) {
  251. // TODO: Log the answer in a database
  252. console.log("'"+answer+"' This text will be encrypted and stored in a text file 'encrypted.txt'");
  253. var cipherText=encryptjs.encrypt(answer,password,nBits);
  254. encryptjs.writeCipherTextToJSON(filePath,{EncryptedText:cipherText},function(){
  255. console.log("'encryptedText.JSON' File created in your local directory, if not present refresh your project");
  256. });
  257. rl.close();
  258. });
  259. };
  260. encryptjs.writeCipherTextToJSON=function(file, obj, options, callback) {
  261. if (callback == null) {
  262. callback = options;
  263. options = {}
  264. }
  265. var spaces = typeof options === 'object' && options !== null
  266. ? 'spaces' in options
  267. ? options.spaces : this.spaces
  268. : this.spaces;
  269. var str = '';
  270. try {
  271. str = JSON.stringify(obj, options ? options.replacer : null, spaces) + '\n'
  272. } catch (err) {
  273. if (callback) return callback(err, null)
  274. }
  275. };
  276. if (typeof String.prototype.utf8Encode == 'undefined') {
  277. String.prototype.utf8Encode = function() {
  278. return unescape( encodeURIComponent( this ) );
  279. };
  280. }
  281. if (typeof String.prototype.utf8Decode == 'undefined') {
  282. String.prototype.utf8Decode = function() {
  283. try {
  284. return decodeURIComponent( escape( this ) );
  285. } catch (e) {
  286. return this; // invalid UTF-8? return as-is
  287. }
  288. };
  289. }
  290. if (typeof String.prototype.base64Encode == 'undefined') {
  291. String.prototype.base64Encode = function() {
  292. if (typeof btoa != 'undefined') return btoa(this); // browser
  293. if (typeof Buffer != 'undefined') return new Buffer(this, 'utf8').toString('base64'); // Node.js
  294. throw new Error('No Base64 Encode');
  295. };
  296. }
  297. if (typeof String.prototype.base64Decode == 'undefined') {
  298. String.prototype.base64Decode = function() {
  299. if (typeof atob != 'undefined') return atob(this); // browser
  300. if (typeof Buffer != 'undefined') return new Buffer(this, 'base64').toString('utf8'); // Node.js
  301. throw new Error('No Base64 Decode');
  302. };
  303. }
  304. encryptjs.init();
  305. return encryptjs;
  306. });