You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

402 lines
15 KiB

  1. /*******************************************************************************
  2. YaMD5 - Yet another MD5 hasher.
  3. home: https://github.com/gorhill/yamd5.js
  4. I needed an MD5 hasher, and as usual I want small code base, and fast.
  5. Originally found md5-o-matic [1]. It was fast but did not work with Unicode
  6. strings. Also, eventually realized it was really based on code from
  7. Joseph Myers [2] with no proper credits given (not nice).
  8. Then I found SparkMD5 [3], which works with Unicode strings, but at a steep
  9. cost to performance. Also, glancing at the code I saw avoidable redundancies
  10. causing the code base to be much larger than needed.
  11. So from this point I set out to write my own version, YaMD5 (sorry, I am
  12. not good with naming projects), of course heavily relying on the original
  13. code from Joseph Myers [2], and bits from SparkMD5 -- I started to work from
  14. SparkMD5 implementation, so there might be bits of code original to SparkMD5
  15. code left in a few places (like say, MD5.end()).
  16. Advantages of YaMD5:
  17. - Can handle Unicode strings
  18. - Natively incremental
  19. - Small code base
  20. - Fastest MD5 hasher out there so far for large input [4]
  21. - Even faster than versions supporting only simpler ascii strings
  22. [1] https://github.com/trentmillar/md5-o-matic
  23. [2] http://www.myersdaily.org/joseph/javascript/md5-text.html
  24. [3] https://github.com/satazor/SparkMD5
  25. [4] http://jsperf.com/md5-shootout/75
  26. So with that said, I don't know what license covers Joseph Myers' code (need
  27. to find out). In any case, concerning whatever original code I contributed in
  28. there:
  29. The MIT License (MIT)
  30. Copyright (C) 2014 Raymond Hill
  31. Permission is hereby granted, free of charge, to any person obtaining a copy
  32. of this software and associated documentation files (the "Software"), to deal
  33. in the Software without restriction, including without limitation the rights
  34. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  35. copies of the Software, and to permit persons to whom the Software is
  36. furnished to do so, subject to the following conditions:
  37. The above copyright notice and this permission notice shall be included in
  38. all copies or substantial portions of the Software.
  39. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  40. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  41. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  42. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  43. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  44. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  45. THE SOFTWARE.
  46. **/
  47. /* jshint bitwise: false */
  48. (function(root) {
  49. 'use strict';
  50. /*
  51. * Fastest md5 implementation around (JKM md5)
  52. * Credits: Joseph Myers
  53. *
  54. * @see http://www.myersdaily.org/joseph/javascript/md5-text.html
  55. * @see http://jsperf.com/md5-shootout/7
  56. */
  57. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
  58. var md5cycle = function(x, k) {
  59. var a = x[0],
  60. b = x[1],
  61. c = x[2],
  62. d = x[3];
  63. // ff()
  64. a += (b & c | ~b & d) + k[0] - 680876936 | 0;
  65. a = (a << 7 | a >>> 25) + b | 0;
  66. d += (a & b | ~a & c) + k[1] - 389564586 | 0;
  67. d = (d << 12 | d >>> 20) + a | 0;
  68. c += (d & a | ~d & b) + k[2] + 606105819 | 0;
  69. c = (c << 17 | c >>> 15) + d | 0;
  70. b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
  71. b = (b << 22 | b >>> 10) + c | 0;
  72. a += (b & c | ~b & d) + k[4] - 176418897 | 0;
  73. a = (a << 7 | a >>> 25) + b | 0;
  74. d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
  75. d = (d << 12 | d >>> 20) + a | 0;
  76. c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
  77. c = (c << 17 | c >>> 15) + d | 0;
  78. b += (c & d | ~c & a) + k[7] - 45705983 | 0;
  79. b = (b << 22 | b >>> 10) + c | 0;
  80. a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
  81. a = (a << 7 | a >>> 25) + b | 0;
  82. d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
  83. d = (d << 12 | d >>> 20) + a | 0;
  84. c += (d & a | ~d & b) + k[10] - 42063 | 0;
  85. c = (c << 17 | c >>> 15) + d | 0;
  86. b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
  87. b = (b << 22 | b >>> 10) + c | 0;
  88. a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
  89. a = (a << 7 | a >>> 25) + b | 0;
  90. d += (a & b | ~a & c) + k[13] - 40341101 | 0;
  91. d = (d << 12 | d >>> 20) + a | 0;
  92. c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
  93. c = (c << 17 | c >>> 15) + d | 0;
  94. b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
  95. b = (b << 22 | b >>> 10) + c | 0;
  96. // gg()
  97. a += (b & d | c & ~d) + k[1] - 165796510 | 0;
  98. a = (a << 5 | a >>> 27) + b | 0;
  99. d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
  100. d = (d << 9 | d >>> 23) + a | 0;
  101. c += (d & b | a & ~b) + k[11] + 643717713 | 0;
  102. c = (c << 14 | c >>> 18) + d | 0;
  103. b += (c & a | d & ~a) + k[0] - 373897302 | 0;
  104. b = (b << 20 | b >>> 12) + c | 0;
  105. a += (b & d | c & ~d) + k[5] - 701558691 | 0;
  106. a = (a << 5 | a >>> 27) + b | 0;
  107. d += (a & c | b & ~c) + k[10] + 38016083 | 0;
  108. d = (d << 9 | d >>> 23) + a | 0;
  109. c += (d & b | a & ~b) + k[15] - 660478335 | 0;
  110. c = (c << 14 | c >>> 18) + d | 0;
  111. b += (c & a | d & ~a) + k[4] - 405537848 | 0;
  112. b = (b << 20 | b >>> 12) + c | 0;
  113. a += (b & d | c & ~d) + k[9] + 568446438 | 0;
  114. a = (a << 5 | a >>> 27) + b | 0;
  115. d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
  116. d = (d << 9 | d >>> 23) + a | 0;
  117. c += (d & b | a & ~b) + k[3] - 187363961 | 0;
  118. c = (c << 14 | c >>> 18) + d | 0;
  119. b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
  120. b = (b << 20 | b >>> 12) + c | 0;
  121. a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
  122. a = (a << 5 | a >>> 27) + b | 0;
  123. d += (a & c | b & ~c) + k[2] - 51403784 | 0;
  124. d = (d << 9 | d >>> 23) + a | 0;
  125. c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
  126. c = (c << 14 | c >>> 18) + d | 0;
  127. b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
  128. b = (b << 20 | b >>> 12) + c | 0;
  129. // hh()
  130. a += (b ^ c ^ d) + k[5] - 378558 | 0;
  131. a = (a << 4 | a >>> 28) + b | 0;
  132. d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
  133. d = (d << 11 | d >>> 21) + a | 0;
  134. c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
  135. c = (c << 16 | c >>> 16) + d | 0;
  136. b += (c ^ d ^ a) + k[14] - 35309556 | 0;
  137. b = (b << 23 | b >>> 9) + c | 0;
  138. a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
  139. a = (a << 4 | a >>> 28) + b | 0;
  140. d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
  141. d = (d << 11 | d >>> 21) + a | 0;
  142. c += (d ^ a ^ b) + k[7] - 155497632 | 0;
  143. c = (c << 16 | c >>> 16) + d | 0;
  144. b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
  145. b = (b << 23 | b >>> 9) + c | 0;
  146. a += (b ^ c ^ d) + k[13] + 681279174 | 0;
  147. a = (a << 4 | a >>> 28) + b | 0;
  148. d += (a ^ b ^ c) + k[0] - 358537222 | 0;
  149. d = (d << 11 | d >>> 21) + a | 0;
  150. c += (d ^ a ^ b) + k[3] - 722521979 | 0;
  151. c = (c << 16 | c >>> 16) + d | 0;
  152. b += (c ^ d ^ a) + k[6] + 76029189 | 0;
  153. b = (b << 23 | b >>> 9) + c | 0;
  154. a += (b ^ c ^ d) + k[9] - 640364487 | 0;
  155. a = (a << 4 | a >>> 28) + b | 0;
  156. d += (a ^ b ^ c) + k[12] - 421815835 | 0;
  157. d = (d << 11 | d >>> 21) + a | 0;
  158. c += (d ^ a ^ b) + k[15] + 530742520 | 0;
  159. c = (c << 16 | c >>> 16) + d | 0;
  160. b += (c ^ d ^ a) + k[2] - 995338651 | 0;
  161. b = (b << 23 | b >>> 9) + c | 0;
  162. // ii()
  163. a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
  164. a = (a << 6 | a >>> 26) + b | 0;
  165. d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
  166. d = (d << 10 | d >>> 22) + a | 0;
  167. c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
  168. c = (c << 15 | c >>> 17) + d | 0;
  169. b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
  170. b = (b << 21 |b >>> 11) + c | 0;
  171. a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
  172. a = (a << 6 | a >>> 26) + b | 0;
  173. d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
  174. d = (d << 10 | d >>> 22) + a | 0;
  175. c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
  176. c = (c << 15 | c >>> 17) + d | 0;
  177. b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
  178. b = (b << 21 |b >>> 11) + c | 0;
  179. a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
  180. a = (a << 6 | a >>> 26) + b | 0;
  181. d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
  182. d = (d << 10 | d >>> 22) + a | 0;
  183. c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
  184. c = (c << 15 | c >>> 17) + d | 0;
  185. b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
  186. b = (b << 21 |b >>> 11) + c | 0;
  187. a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
  188. a = (a << 6 | a >>> 26) + b | 0;
  189. d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
  190. d = (d << 10 | d >>> 22) + a | 0;
  191. c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
  192. c = (c << 15 | c >>> 17) + d | 0;
  193. b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
  194. b = (b << 21 | b >>> 11) + c | 0;
  195. x[0] = a + x[0] | 0;
  196. x[1] = b + x[1] | 0;
  197. x[2] = c + x[2] | 0;
  198. x[3] = d + x[3] | 0;
  199. };
  200. var hexChars = '0123456789abcdef';
  201. var hexOut = [];
  202. var hex = function(x) {
  203. var hc = hexChars;
  204. var ho = hexOut;
  205. var n, offset, j;
  206. for (var i = 0; i < 4; i++) {
  207. offset = i * 8;
  208. n = x[i];
  209. for ( j = 0; j < 8; j += 2 ) {
  210. ho[offset+1+j] = hc.charAt(n & 0x0F);
  211. n >>>= 4;
  212. ho[offset+0+j] = hc.charAt(n & 0x0F);
  213. n >>>= 4;
  214. }
  215. }
  216. return ho.join('');
  217. };
  218. var MD5 = function() {
  219. this._dataLength = 0;
  220. this._state = new Int32Array(4);
  221. this._buffer = new ArrayBuffer(68);
  222. this._bufferLength = 0;
  223. this._buffer8 = new Uint8Array(this._buffer, 0, 68);
  224. this._buffer32 = new Uint32Array(this._buffer, 0, 17);
  225. this.start();
  226. };
  227. var stateIdentity = new Int32Array([1732584193, -271733879, -1732584194, 271733878]);
  228. var buffer32Identity = new Int32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
  229. // Char to code point to to array conversion:
  230. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt#Example.3A_Fixing_charCodeAt_to_handle_non-Basic-Multilingual-Plane_characters_if_their_presence_earlier_in_the_string_is_unknown
  231. MD5.prototype.appendStr = function(str) {
  232. var buf8 = this._buffer8;
  233. var buf32 = this._buffer32;
  234. var bufLen = this._bufferLength;
  235. var code;
  236. for ( var i = 0; i < str.length; i++ ) {
  237. code = str.charCodeAt(i);
  238. if ( code < 128 ) {
  239. buf8[bufLen++] = code;
  240. } else if ( code < 0x800 ) {
  241. buf8[bufLen++] = (code >>> 6) + 0xC0;
  242. buf8[bufLen++] = code & 0x3F | 0x80;
  243. } else if ( code < 0xD800 || code > 0xDBFF ) {
  244. buf8[bufLen++] = (code >>> 12) + 0xE0;
  245. buf8[bufLen++] = (code >>> 6 & 0x3F) | 0x80;
  246. buf8[bufLen++] = (code & 0x3F) | 0x80;
  247. } else {
  248. code = ((code - 0xD800) * 0x400) + (str.charCodeAt(++i) - 0xDC00) + 0x10000;
  249. if ( code > 0x10FFFF ) {
  250. throw 'Unicode standard supports code points up to U+10FFFF';
  251. }
  252. buf8[bufLen++] = (code >>> 18) + 0xF0;
  253. buf8[bufLen++] = (code >>> 12 & 0x3F) | 0x80;
  254. buf8[bufLen++] = (code >>> 6 & 0x3F) | 0x80;
  255. buf8[bufLen++] = (code & 0x3F) | 0x80;
  256. }
  257. if ( bufLen >= 64 ) {
  258. this._dataLength += 64;
  259. md5cycle(this._state, buf32);
  260. bufLen -= 64;
  261. buf32[0] = buf32[16];
  262. }
  263. }
  264. this._bufferLength = bufLen;
  265. return this;
  266. };
  267. MD5.prototype.appendAsciiStr = function(str) {
  268. var buf8 = this._buffer8;
  269. var buf32 = this._buffer32;
  270. var bufLen = this._bufferLength;
  271. var i, j = 0;
  272. for (;;) {
  273. i = Math.min(str.length-j, 64-bufLen);
  274. while ( i-- ) {
  275. buf8[bufLen++] = str.charCodeAt(j++);
  276. }
  277. if ( bufLen < 64 ) {
  278. break;
  279. }
  280. this._dataLength += 64;
  281. md5cycle(this._state, buf32);
  282. bufLen = 0;
  283. }
  284. this._bufferLength = bufLen;
  285. return this;
  286. };
  287. MD5.prototype.appendByteArray = function(input) {
  288. var buf8 = this._buffer8;
  289. var buf32 = this._buffer32;
  290. var bufLen = this._bufferLength;
  291. var i, j = 0;
  292. for (;;) {
  293. i = Math.min(input.length-j, 64-bufLen);
  294. while ( i-- ) {
  295. buf8[bufLen++] = input[j++];
  296. }
  297. if ( bufLen < 64 ) {
  298. break;
  299. }
  300. this._dataLength += 64;
  301. md5cycle(this._state, buf32);
  302. bufLen = 0;
  303. }
  304. this._bufferLength = bufLen;
  305. return this;
  306. };
  307. MD5.prototype.start = function() {
  308. this._dataLength = 0;
  309. this._bufferLength = 0;
  310. this._state.set(stateIdentity);
  311. return this;
  312. };
  313. MD5.prototype.end = function(raw) {
  314. var bufLen = this._bufferLength;
  315. this._dataLength += bufLen;
  316. var buf8 = this._buffer8;
  317. buf8[bufLen] = 0x80;
  318. buf8[bufLen+1] = buf8[bufLen+2] = buf8[bufLen+3] = 0;
  319. var buf32 = this._buffer32;
  320. var i = (bufLen >> 2) + 1;
  321. buf32.set(buffer32Identity.subarray(i), i);
  322. if (bufLen > 55) {
  323. md5cycle(this._state, buf32);
  324. buf32.set(buffer32Identity);
  325. }
  326. // Do the final computation based on the tail and length
  327. // Beware that the final length may not fit in 32 bits so we take care of that
  328. var dataBitsLen = this._dataLength * 8;
  329. if ( dataBitsLen <= 0xFFFFFFFF ) {
  330. buf32[14] = dataBitsLen;
  331. } else {
  332. var matches = dataBitsLen.toString(16).match(/(.*?)(.{0,8})$/);
  333. var lo = parseInt(matches[2], 16);
  334. var hi = parseInt(matches[1], 16) || 0;
  335. buf32[14] = lo;
  336. buf32[15] = hi;
  337. }
  338. md5cycle(this._state, buf32);
  339. return !!raw ? this._state : hex(this._state);
  340. };
  341. // This permanent instance is to use for one-call hashing
  342. var onePassHasher = new MD5();
  343. MD5.hashStr = function(str, raw) {
  344. return onePassHasher
  345. .start()
  346. .appendStr(str)
  347. .end(raw);
  348. };
  349. MD5.hashAsciiStr = function(str, raw) {
  350. return onePassHasher
  351. .start()
  352. .appendAsciiStr(str)
  353. .end(raw);
  354. };
  355. // Self-test
  356. // In some cases the fast add32 function cannot be used..
  357. if ( MD5.hashStr('hello') !== '5d41402abc4b2a76b9719d911017c592' ) {
  358. console.error('YaMD5> this javascript engine does not support YaMD5. Sorry.');
  359. }
  360. if ( typeof root === 'object' ) {
  361. root.YaMD5 = MD5;
  362. }
  363. return MD5;
  364. })(this);