MD5 Shootout (v33)

Revision 33 of this benchmark created by iReal on


Description

This does a simple comparison of a number of JS implementations of the MD5 algorithm. The md5_utf8() function is simply jkm's md5() algorithm with auto detection and conversion of unicode characters to UTF-8.

Preparation HTML

<script>
  /**
   * Namespace for hashing and other cryptographic functions
   * Copyright (c) Andrew Valums
   * Licensed under the MIT license, http://valums.com/mit-license/
   */
  
  var V = V || {};
  V.Security = V.Security || {};
  
  (function() {
   // for faster access
   var S = V.Security;
  
   /**
    * The highest integer value a number can go to without losing precision.
    */
   S.maxExactInt = Math.pow(2, 53);
  
   /**
    * Converts string from internal UTF-16 to UTF-8
    * and saves it using array of numbers (bytes), 0-255 per cell
    * @param {String} str
    * @return {Array}
    */
   S.toUtf8ByteArr = function(str) {
    var arr = [],
        code;
  
    for (var i = 0; i < str.length; i++) {
     code = str.charCodeAt(i);
  
  /*
                        Note that charCodeAt will always return a value that is less than 65,536.
                        This is because the higher code points are represented by a pair of (lower valued)
                        "surrogate" pseudo-characters which are used to comprise the real character.
                        Because of this, in order to examine or reproduce the full character for
                        individual characters of value 65,536 and above, for such characters,
                        it is necessary to retrieve not only charCodeAt(0), but also charCodeAt(1). 
                         */
     if (0xD800 <= code && code <= 0xDBFF) {
      // UTF-16 high surrogate 
      var hi = code,
          low = str.charCodeAt(i + 1);
  
      code = ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
  
      i++;
     }
  
     if (code <= 127) {
      arr[arr.length] = code;
     } else if (code <= 2047) {
      arr[arr.length] = (code >>> 6) + 0xC0;
      arr[arr.length] = code & 0x3F | 0x80;
     } else if (code <= 65535) {
      arr[arr.length] = (code >>> 12) + 0xE0;
      arr[arr.length] = (code >>> 6 & 0x3F) | 0x80;
      arr[arr.length] = (code & 0x3F) | 0x80;
     } else if (code <= 1114111) {
      arr[arr.length] = (code >>> 18) + 0xF0;
      arr[arr.length] = (code >>> 12 & 0x3F) | 0x80;
      arr[arr.length] = (code >>> 6 & 0x3F) | 0x80;
      arr[arr.length] = (code & 0x3F) | 0x80;
     } else {
      throw 'Unicode standart supports code points up-to U+10FFFF';
     }
    }
  
    return arr;
   };
  
   /**
    * Outputs 32 integer bits of a number in hex format.
    * Preserves leading zeros.
    * @param {Number} num
    */
   S.toHex32 = function(num) {
    // if negative
    if (num & 0x80000000) {
     // convert to positive number
     num = num & (~0x80000000);
     num += Math.pow(2, 31);
    }
  
    var str = num.toString(16);
  
    while (str.length < 8) {
     str = '0' + str;
    }
  
    return str;
   };
  
   /**
    * Changes the order of 4 bytes in integer representation of number.
    * From 1234 to 4321. 
    * @param {Number} num Only 32 int bits are used.
    */
   S.reverseBytes = function(num) {
    var res = 0;
    res += ((num >>> 24) & 0xff);
    res += ((num >>> 16) & 0xff) << 8;
    res += ((num >>> 8) & 0xff) << 16;
    res += (num & 0xff) << 24;
    return res;
   };
  
   S.leftRotate = function(x, c) {
    return (x << c) | (x >>> (32 - c));
   };
  
   /**
    * RSA Data Security, Inc. MD5 Message-Digest Algorithm
    * http://tools.ietf.org/html/rfc1321
    * http://en.wikipedia.org/wiki/MD5
    * @param {String} message
    */
   S.md5 = function(message) {
    // r specifies the per-round shift amounts
    var r = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];
  
    // Use binary integer part of the sines of integers (Radians) as constants:
    var k = [];
    for (var i = 0; i <= 63; i++) {
     k[i] = (Math.abs(Math.sin(i + 1)) * Math.pow(2, 32)) << 0;
    }
  
    var h0 = 0x67452301,
        h1 = 0xEFCDAB89,
        h2 = 0x98BADCFE,
        h3 = 0x10325476,
        bytes, unpadded;
  
    //Pre-processing:   
    bytes = S.toUtf8ByteArr(message);
    message = null;
    unpadded = bytes.length;
  
    //append "1" bit to message
    //append "0" bits until message length in bits ≡ 448 (mod 512)    
    bytes.push(0x80);
    var zeroBytes = Math.abs(448 - (bytes.length * 8) % 512) / 8;
  
    while (zeroBytes--) {
     bytes.push(0);
    }
  
    //append bit length of unpadded message as 64-bit little-endian integer to message          
    bytes.push(unpadded * 8 & 0xff, unpadded * 8 >> 8 & 0xff, unpadded * 8 >> 16 & 0xff, unpadded * 8 >> 24 & 0xff);
  
    var i = 4;
    while (i--) {
     bytes.push(0);
    }
  
    var leftRotate = S.leftRotate;
  
    //Process the message in successive 512-bit chunks:
    var i = 0,
        w = [];
    while (i < bytes.length) {
  
     //break chunk into sixteen 32-bit words w[i], 0 ≤ i ≤ 15
     for (var j = 0; j <= 15; j++) {
      w[j] = (bytes[i + 4 * j] << 0) + (bytes[i + 4 * j + 1] << 8) + (bytes[i + 4 * j + 2] << 16) + (bytes[i + 4 * j + 3] << 24);
     }
  
     //Initialize hash value for this chunk:
     var a = h0,
         b = h1,
         c = h2,
         d = h3,
         f, g;
  
     //Main loop:
     for (var j = 0; j <= 63; j++) {
  
      if (j <= 15) {
       f = (b & c) | ((~b) & d);
       g = j;
      } else if (j <= 31) {
       f = (d & b) | ((~d) & c);
       g = (5 * j + 1) % 16;
      } else if (j <= 47) {
       f = b ^ c ^ d;
       g = (3 * j + 5) % 16;
      } else {
       f = c ^ (b | (~d));
       g = (7 * j) % 16;
      }
  
      var temp = d;
  
      d = c;
      c = b;
      b = b + leftRotate((a + f + k[j] + w[g]), r[j]);
      a = temp;
     }
  
     //Add this chunk's hash to result so far:
     h0 = (h0 + a) << 0;
     h1 = (h1 + b) << 0;
     h2 = (h2 + c) << 0;
     h3 = (h3 + d) << 0;
  
     i += 512 / 8;
    }
  
    // fix when starting with 0                 
    var res = out(h0) + out(h1) + out(h2) + out(h3);
  
    function out(h) {
     return S.toHex32(S.reverseBytes(h));
    }
  
    return res;
   };
  })();
  
  
  /*
   * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
   * Digest Algorithm, as defined in RFC 1321.
   * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
   * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
   * Distributed under the BSD License
   * See http://pajhome.org.uk/crypt/md5 for more info.
   */
  
  /*
   * Configurable variables. You may need to tweak these to be compatible with
   * the server-side, but the defaults work in most cases.
   */
  var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase        */
  var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance   */
  
  /*
   * These are the functions you'll usually want to call
   * They take string arguments and return either hex or base-64 encoded strings
   */
  
  function hex_md5(s) {
   return rstr2hex(rstr_md5(str2rstr_utf8(s)));
  }
  
  function b64_md5(s) {
   return rstr2b64(rstr_md5(str2rstr_utf8(s)));
  }
  
  function any_md5(s, e) {
   return rstr2any(rstr_md5(str2rstr_utf8(s)), e);
  }
  
  function hex_hmac_md5(k, d) {
   return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)));
  }
  
  function b64_hmac_md5(k, d) {
   return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)));
  }
  
  function any_hmac_md5(k, d, e) {
   return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e);
  }
  
  /*
   * Perform a simple self-test to see if the VM is working
   */
  
  function md5_vm_test() {
   return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
  }
  
  /*
   * Calculate the MD5 of a raw string
   */
  
  function rstr_md5(s) {
   return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
  }
  
  /*
   * Calculate the HMAC-MD5, of a key and some data (raw strings)
   */
  
  function rstr_hmac_md5(key, data) {
   var bkey = rstr2binl(key);
   if (bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);
  
   var ipad = Array(16),
       opad = Array(16);
   for (var i = 0; i < 16; i++) {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
   }
  
   var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
   return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
  }
  
  /*
   * Convert a raw string to a hex string
   */
  
  function rstr2hex(input) {
   try {
    hexcase
   } catch (e) {
    hexcase = 0;
   }
   var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
   var output = "";
   var x;
   for (var i = 0; i < input.length; i++) {
    x = input.charCodeAt(i);
    output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
   }
   return output;
  }
  
  /*
   * Convert a raw string to a base-64 string
   */
  
  function rstr2b64(input) {
   try {
    b64pad
   } catch (e) {
    b64pad = '';
   }
   var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
   var output = "";
   var len = input.length;
   for (var i = 0; i < len; i += 3) {
    var triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
    for (var j = 0; j < 4; j++) {
     if (i * 8 + j * 6 > input.length * 8) output += b64pad;
     else output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
    }
   }
   return output;
  }
  
  /*
   * Convert a raw string to an arbitrary string encoding
   */
  
  function rstr2any(input, encoding) {
   var divisor = encoding.length;
   var i, j, q, x, quotient;
  
   /* Convert to an array of 16-bit big-endian values, forming the dividend */
   var dividend = Array(Math.ceil(input.length / 2));
   for (i = 0; i < dividend.length; i++) {
    dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
   }
  
  /*
     * Repeatedly perform a long division. The binary array forms the dividend,
     * the length of the encoding is the divisor. Once computed, the quotient
     * forms the dividend for the next step. All remainders are stored for later
     * use.
     */
   var full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
   var remainders = Array(full_length);
   for (j = 0; j < full_length; j++) {
    quotient = Array();
    x = 0;
    for (i = 0; i < dividend.length; i++) {
     x = (x << 16) + dividend[i];
     q = Math.floor(x / divisor);
     x -= q * divisor;
     if (quotient.length > 0 || q > 0) quotient[quotient.length] = q;
    }
    remainders[j] = x;
    dividend = quotient;
   }
  
   /* Convert the remainders to the output string */
   var output = "";
   for (i = remainders.length - 1; i >= 0; i--)
   output += encoding.charAt(remainders[i]);
  
   return output;
  }
  
  /*
   * Encode a string as utf-8.
   * For efficiency, this assumes the input is valid utf-16.
   */
  
  function str2rstr_utf8(input) {
   return unescape(encodeURI(input));
  }
  
  /*
   * Encode a string as utf-16
   */
  
  function str2rstr_utf16le(input) {
   var output = "";
   for (var i = 0; i < input.length; i++)
   output += String.fromCharCode(input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
   return output;
  }
  
  function str2rstr_utf16be(input) {
   var output = "";
   for (var i = 0; i < input.length; i++)
   output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
   return output;
  }
  
  /*
   * Convert a raw string to an array of little-endian words
   * Characters >255 have their high-byte silently ignored.
   */
  
  function rstr2binl(input) {
   var output = Array(input.length >> 2);
   for (var i = 0; i < output.length; i++)
   output[i] = 0;
   for (var i = 0; i < input.length * 8; i += 8)
   output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
   return output;
  }
  
  /*
   * Convert an array of little-endian words to a string
   */
  
  function binl2rstr(input) {
   var output = "";
   for (var i = 0; i < input.length * 32; i += 8)
   output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
   return output;
  }
  
  /*
   * Calculate the MD5 of an array of little-endian words, and a bit length.
   */
  
  function binl_md5(x, len) { /* append padding */
   x[len >> 5] |= 0x80 << ((len) % 32);
   x[(((len + 64) >>> 9) << 4) + 14] = len;
  
   var a = 1732584193;
   var b = -271733879;
   var c = -1732584194;
   var d = 271733878;
  
   for (var i = 0; i < x.length; i += 16) {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
  
    a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
    d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
    b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
    d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
    c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
    d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
    d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
  
    a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
    d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
    c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
    b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
    d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
    c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
    d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
    c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
    a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
    d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
    c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
    b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
  
    a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
    d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
    b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
    d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
    c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
    d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
    a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
    d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
    b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
  
    a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
    d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
    c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
    d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
    d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
    a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
    d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
    b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
  
    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
   }
   return Array(a, b, c, d);
  }
  
  /*
   * These functions implement the four basic operations the algorithm uses.
   */
  
  function md5_cmn(q, a, b, x, s, t) {
   return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
  }
  
  function md5_ff(a, b, c, d, x, s, t) {
   return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
  }
  
  function md5_gg(a, b, c, d, x, s, t) {
   return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
  }
  
  function md5_hh(a, b, c, d, x, s, t) {
   return md5_cmn(b ^ c ^ d, a, b, x, s, t);
  }
  
  function md5_ii(a, b, c, d, x, s, t) {
   return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
  }
  
  /*
   * Add integers, wrapping at 2^32. This uses 16-bit operations internally
   * to work around bugs in some JS interpreters.
   */
  
  function safe_add(x, y) {
   var lsw = (x & 0xFFFF) + (y & 0xFFFF);
   var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
   return (msw << 16) | (lsw & 0xFFFF);
  }
  
  /*
   * Bitwise rotate a 32-bit number to the left.
   */
  
  function bit_rol(num, cnt) {
   return (num << cnt) | (num >>> (32 - cnt));
  }
  
  
  function md5cycle(x, k) {
   var a = x[0],
       b = x[1],
       c = x[2],
       d = x[3];
  
   a = ff(a, b, c, d, k[0], 7, -680876936);
   d = ff(d, a, b, c, k[1], 12, -389564586);
   c = ff(c, d, a, b, k[2], 17, 606105819);
   b = ff(b, c, d, a, k[3], 22, -1044525330);
   a = ff(a, b, c, d, k[4], 7, -176418897);
   d = ff(d, a, b, c, k[5], 12, 1200080426);
   c = ff(c, d, a, b, k[6], 17, -1473231341);
   b = ff(b, c, d, a, k[7], 22, -45705983);
   a = ff(a, b, c, d, k[8], 7, 1770035416);
   d = ff(d, a, b, c, k[9], 12, -1958414417);
   c = ff(c, d, a, b, k[10], 17, -42063);
   b = ff(b, c, d, a, k[11], 22, -1990404162);
   a = ff(a, b, c, d, k[12], 7, 1804603682);
   d = ff(d, a, b, c, k[13], 12, -40341101);
   c = ff(c, d, a, b, k[14], 17, -1502002290);
   b = ff(b, c, d, a, k[15], 22, 1236535329);
  
   a = gg(a, b, c, d, k[1], 5, -165796510);
   d = gg(d, a, b, c, k[6], 9, -1069501632);
   c = gg(c, d, a, b, k[11], 14, 643717713);
   b = gg(b, c, d, a, k[0], 20, -373897302);
   a = gg(a, b, c, d, k[5], 5, -701558691);
   d = gg(d, a, b, c, k[10], 9, 38016083);
   c = gg(c, d, a, b, k[15], 14, -660478335);
   b = gg(b, c, d, a, k[4], 20, -405537848);
   a = gg(a, b, c, d, k[9], 5, 568446438);
   d = gg(d, a, b, c, k[14], 9, -1019803690);
   c = gg(c, d, a, b, k[3], 14, -187363961);
   b = gg(b, c, d, a, k[8], 20, 1163531501);
   a = gg(a, b, c, d, k[13], 5, -1444681467);
   d = gg(d, a, b, c, k[2], 9, -51403784);
   c = gg(c, d, a, b, k[7], 14, 1735328473);
   b = gg(b, c, d, a, k[12], 20, -1926607734);
  
   a = hh(a, b, c, d, k[5], 4, -378558);
   d = hh(d, a, b, c, k[8], 11, -2022574463);
   c = hh(c, d, a, b, k[11], 16, 1839030562);
   b = hh(b, c, d, a, k[14], 23, -35309556);
   a = hh(a, b, c, d, k[1], 4, -1530992060);
   d = hh(d, a, b, c, k[4], 11, 1272893353);
   c = hh(c, d, a, b, k[7], 16, -155497632);
   b = hh(b, c, d, a, k[10], 23, -1094730640);
   a = hh(a, b, c, d, k[13], 4, 681279174);
   d = hh(d, a, b, c, k[0], 11, -358537222);
   c = hh(c, d, a, b, k[3], 16, -722521979);
   b = hh(b, c, d, a, k[6], 23, 76029189);
   a = hh(a, b, c, d, k[9], 4, -640364487);
   d = hh(d, a, b, c, k[12], 11, -421815835);
   c = hh(c, d, a, b, k[15], 16, 530742520);
   b = hh(b, c, d, a, k[2], 23, -995338651);
  
   a = ii(a, b, c, d, k[0], 6, -198630844);
   d = ii(d, a, b, c, k[7], 10, 1126891415);
   c = ii(c, d, a, b, k[14], 15, -1416354905);
   b = ii(b, c, d, a, k[5], 21, -57434055);
   a = ii(a, b, c, d, k[12], 6, 1700485571);
   d = ii(d, a, b, c, k[3], 10, -1894986606);
   c = ii(c, d, a, b, k[10], 15, -1051523);
   b = ii(b, c, d, a, k[1], 21, -2054922799);
   a = ii(a, b, c, d, k[8], 6, 1873313359);
   d = ii(d, a, b, c, k[15], 10, -30611744);
   c = ii(c, d, a, b, k[6], 15, -1560198380);
   b = ii(b, c, d, a, k[13], 21, 1309151649);
   a = ii(a, b, c, d, k[4], 6, -145523070);
   d = ii(d, a, b, c, k[11], 10, -1120210379);
   c = ii(c, d, a, b, k[2], 15, 718787259);
   b = ii(b, c, d, a, k[9], 21, -343485551);
  
   x[0] = add32(a, x[0]);
   x[1] = add32(b, x[1]);
   x[2] = add32(c, x[2]);
   x[3] = add32(d, x[3]);
  
  }
  
  function cmn(q, a, b, x, s, t) {
   a = add32(add32(a, q), add32(x, t));
   return add32((a << s) | (a >>> (32 - s)), b);
  }
  
  function ff(a, b, c, d, x, s, t) {
   return cmn((b & c) | ((~b) & d), a, b, x, s, t);
  }
  
  function gg(a, b, c, d, x, s, t) {
   return cmn((b & d) | (c & (~d)), a, b, x, s, t);
  }
  
  function hh(a, b, c, d, x, s, t) {
   return cmn(b ^ c ^ d, a, b, x, s, t);
  }
  
  function ii(a, b, c, d, x, s, t) {
   return cmn(c ^ (b | (~d)), a, b, x, s, t);
  }
  
  function md51(s) {
   txt = '';
   var n = s.length,
       state = [1732584193, -271733879, -1732584194, 271733878],
       i;
   for (i = 64; i <= s.length; i += 64) {
    md5cycle(state, md5blk(s.substring(i - 64, i)));
   }
   s = s.substring(i - 64);
   var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
   for (i = 0; i < s.length; i++)
   tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
   tail[i >> 2] |= 0x80 << ((i % 4) << 3);
   if (i > 55) {
    md5cycle(state, tail);
    for (i = 0; i < 16; i++) tail[i] = 0;
   }
   tail[14] = n * 8;
   md5cycle(state, tail);
   return state;
  }
  
  /* there needs to be support for Unicode here,
   * unless we pretend that we can redefine the MD-5
   * algorithm for multi-byte characters (perhaps
   * by adding every four 16-bit characters and
   * shortening the sum to 32 bits). Otherwise
   * I suggest performing MD-5 as if every character
   * was two bytes--e.g., 0040 0025 = @%--but then
   * how will an ordinary MD-5 sum be matched?
   * There is no way to standardize text to something
   * like UTF-8 before transformation; speed cost is
   * utterly prohibitive. The JavaScript standard
   * itself needs to look at this: it should start
   * providing access to strings as preformed UTF-8
   * 8-bit unsigned value arrays.
   */
  
  function md5blk(s) { /* I figured global was faster.   */
   var md5blks = [],
       i; /* Andy King said do it this way. */
   for (i = 0; i < 64; i += 4) {
    md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
   }
   return md5blks;
  }
  
  var hex_chr = '0123456789abcdef'.split('');
  
  function rhex(n) {
   var s = '',
       j = 0;
   for (; j < 4; j++)
   s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
   return s;
  }
  
  function hex(x) {
   for (var i = 0; i < x.length; i++)
   x[i] = rhex(x[i]);
   return x.join('');
  }
  
  function md5(s) {
   return hex(md51(s));
  }
  
  /* this function is much faster,
  so if possible we use it. Some IEs
  are the only ones I know of that
  need the idiotic second function,
  generated by an if clause.  */
  
  function add32(a, b) {
   return (a + b) & 0xFFFFFFFF;
  }
  
  if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {
   function add32(x, y) {
    var lsw = (x & 0xFFFF) + (y & 0xFFFF),
        msw = (x >> 16) + (y >> 16) + (lsw >> 16);
    return (msw << 16) | (lsw & 0xFFFF);
   }
  }
  
  
  (function() {
   function md5cycle(x, k) {
    var a = x[0],
        b = x[1],
        c = x[2],
        d = x[3];
  
    a = ff(a, b, c, d, k[0], 7, -680876936);
    d = ff(d, a, b, c, k[1], 12, -389564586);
    c = ff(c, d, a, b, k[2], 17, 606105819);
    b = ff(b, c, d, a, k[3], 22, -1044525330);
    a = ff(a, b, c, d, k[4], 7, -176418897);
    d = ff(d, a, b, c, k[5], 12, 1200080426);
    c = ff(c, d, a, b, k[6], 17, -1473231341);
    b = ff(b, c, d, a, k[7], 22, -45705983);
    a = ff(a, b, c, d, k[8], 7, 1770035416);
    d = ff(d, a, b, c, k[9], 12, -1958414417);
    c = ff(c, d, a, b, k[10], 17, -42063);
    b = ff(b, c, d, a, k[11], 22, -1990404162);
    a = ff(a, b, c, d, k[12], 7, 1804603682);
    d = ff(d, a, b, c, k[13], 12, -40341101);
    c = ff(c, d, a, b, k[14], 17, -1502002290);
    b = ff(b, c, d, a, k[15], 22, 1236535329);
  
    a = gg(a, b, c, d, k[1], 5, -165796510);
    d = gg(d, a, b, c, k[6], 9, -1069501632);
    c = gg(c, d, a, b, k[11], 14, 643717713);
    b = gg(b, c, d, a, k[0], 20, -373897302);
    a = gg(a, b, c, d, k[5], 5, -701558691);
    d = gg(d, a, b, c, k[10], 9, 38016083);
    c = gg(c, d, a, b, k[15], 14, -660478335);
    b = gg(b, c, d, a, k[4], 20, -405537848);
    a = gg(a, b, c, d, k[9], 5, 568446438);
    d = gg(d, a, b, c, k[14], 9, -1019803690);
    c = gg(c, d, a, b, k[3], 14, -187363961);
    b = gg(b, c, d, a, k[8], 20, 1163531501);
    a = gg(a, b, c, d, k[13], 5, -1444681467);
    d = gg(d, a, b, c, k[2], 9, -51403784);
    c = gg(c, d, a, b, k[7], 14, 1735328473);
    b = gg(b, c, d, a, k[12], 20, -1926607734);
  
    a = hh(a, b, c, d, k[5], 4, -378558);
    d = hh(d, a, b, c, k[8], 11, -2022574463);
    c = hh(c, d, a, b, k[11], 16, 1839030562);
    b = hh(b, c, d, a, k[14], 23, -35309556);
    a = hh(a, b, c, d, k[1], 4, -1530992060);
    d = hh(d, a, b, c, k[4], 11, 1272893353);
    c = hh(c, d, a, b, k[7], 16, -155497632);
    b = hh(b, c, d, a, k[10], 23, -1094730640);
    a = hh(a, b, c, d, k[13], 4, 681279174);
    d = hh(d, a, b, c, k[0], 11, -358537222);
    c = hh(c, d, a, b, k[3], 16, -722521979);
    b = hh(b, c, d, a, k[6], 23, 76029189);
    a = hh(a, b, c, d, k[9], 4, -640364487);
    d = hh(d, a, b, c, k[12], 11, -421815835);
    c = hh(c, d, a, b, k[15], 16, 530742520);
    b = hh(b, c, d, a, k[2], 23, -995338651);
  
    a = ii(a, b, c, d, k[0], 6, -198630844);
    d = ii(d, a, b, c, k[7], 10, 1126891415);
    c = ii(c, d, a, b, k[14], 15, -1416354905);
    b = ii(b, c, d, a, k[5], 21, -57434055);
    a = ii(a, b, c, d, k[12], 6, 1700485571);
    d = ii(d, a, b, c, k[3], 10, -1894986606);
    c = ii(c, d, a, b, k[10], 15, -1051523);
    b = ii(b, c, d, a, k[1], 21, -2054922799);
    a = ii(a, b, c, d, k[8], 6, 1873313359);
    d = ii(d, a, b, c, k[15], 10, -30611744);
    c = ii(c, d, a, b, k[6], 15, -1560198380);
    b = ii(b, c, d, a, k[13], 21, 1309151649);
    a = ii(a, b, c, d, k[4], 6, -145523070);
    d = ii(d, a, b, c, k[11], 10, -1120210379);
    c = ii(c, d, a, b, k[2], 15, 718787259);
    b = ii(b, c, d, a, k[9], 21, -343485551);
  
    x[0] = add32(a, x[0]);
    x[1] = add32(b, x[1]);
    x[2] = add32(c, x[2]);
    x[3] = add32(d, x[3]);
  
   }
  
   function cmn(q, a, b, x, s, t) {
    a = add32(add32(a, q), add32(x, t));
    return add32((a << s) | (a >>> (32 - s)), b);
   }
  
   function ff(a, b, c, d, x, s, t) {
    return cmn((b & c) | ((~b) & d), a, b, x, s, t);
   }
  
   function gg(a, b, c, d, x, s, t) {
    return cmn((b & d) | (c & (~d)), a, b, x, s, t);
   }
  
   function hh(a, b, c, d, x, s, t) {
    return cmn(b ^ c ^ d, a, b, x, s, t);
   }
  
   function ii(a, b, c, d, x, s, t) {
    return cmn(c ^ (b | (~d)), a, b, x, s, t);
   }
  
   function md51(s) {
    // Converts the string to UTF-8 "bytes" when necessary
    if (s.match(/[\x80-\xFF]/)) {
     s = unescape(encodeURI(s));
    }
    txt = '';
    var n = s.length,
        state = [1732584193, -271733879, -1732584194, 271733878],
        i;
    for (i = 64; i <= s.length; i += 64) {
     md5cycle(state, md5blk(s.substring(i - 64, i)));
    }
    s = s.substring(i - 64);
    var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for (i = 0; i < s.length; i++)
    tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
    tail[i >> 2] |= 0x80 << ((i % 4) << 3);
    if (i > 55) {
     md5cycle(state, tail);
     for (i = 0; i < 16; i++) tail[i] = 0;
    }
    tail[14] = n * 8;
    md5cycle(state, tail);
    return state;
   }
  
  /* there needs to be support for Unicode here,
         * unless we pretend that we can redefine the MD-5
         * algorithm for multi-byte characters (perhaps
         * by adding every four 16-bit characters and
         * shortening the sum to 32 bits). Otherwise
         * I suggest performing MD-5 as if every character
         * was two bytes--e.g., 0040 0025 = @%--but then
         * how will an ordinary MD-5 sum be matched?
         * There is no way to standardize text to something
         * like UTF-8 before transformation; speed cost is
         * utterly prohibitive. The JavaScript standard
         * itself needs to look at this: it should start
         * providing access to strings as preformed UTF-8
         * 8-bit unsigned value arrays.
         */
  
   function md5blk(s) { /* I figured global was faster.   */
    var md5blks = [],
        i; /* Andy King said do it this way. */
    for (i = 0; i < 64; i += 4) {
     md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
    }
    return md5blks;
   }
  
   var hex_chr = '0123456789abcdef'.split('');
  
   function rhex(n) {
    var s = '',
        j = 0;
    for (; j < 4; j++)
    s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
    return s;
   }
  
   function hex(x) {
    for (var i = 0; i < x.length; i++)
    x[i] = rhex(x[i]);
    return x.join('');
   }
  
   md5_utf8 = function(s) {
    return hex(md51(s));
   }
  
  /* this function is much faster,
        so if possible we use it. Some IEs
        are the only ones I know of that
        need the idiotic second function,
        generated by an if clause.  */
  
   function add32(a, b) {
    return (a + b) & 0xFFFFFFFF;
   }
  
   if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {
    function add32(x, y) {
     var lsw = (x & 0xFFFF) + (y & 0xFFFF),
         msw = (x >> 16) + (y >> 16) + (lsw >> 16);
     return (msw << 16) | (lsw & 0xFFFF);
    }
   }
  })();
  
  /* md5.js - MD5 Message-Digest
   * Copyright (C) 1999,2002 Masanao Izumo <iz@onicos.co.jp>
   * Version: 2.0.0
   * LastModified: May 13 2002
   *
   * This program is free software.  You can redistribute it and/or modify
   * it without any warranty.  This library calculates the MD5 based on RFC1321.
   * See RFC1321 for more information and algorism.
   */
  
  /* Interface:
   * md5_128bits = MD5_hash(data);
   * md5_hexstr = MD5_hexhash(data);
   */
  
  /* ChangeLog
   * 2002/05/13: Version 2.0.0 released
   * NOTICE: API is changed.
   * 2002/04/15: Bug fix about MD5 length.
   */
  
  
  //    md5_T[i] = parseInt(Math.abs(Math.sin(i)) * 4294967296.0);
  var MD5_T = new Array(0x00000000, 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391);
  
  var MD5_round1 = new Array(new Array(0, 7, 1), new Array(1, 12, 2), new Array(2, 17, 3), new Array(3, 22, 4), new Array(4, 7, 5), new Array(5, 12, 6), new Array(6, 17, 7), new Array(7, 22, 8), new Array(8, 7, 9), new Array(9, 12, 10), new Array(10, 17, 11), new Array(11, 22, 12), new Array(12, 7, 13), new Array(13, 12, 14), new Array(14, 17, 15), new Array(15, 22, 16));
  
  var MD5_round2 = new Array(new Array(1, 5, 17), new Array(6, 9, 18), new Array(11, 14, 19), new Array(0, 20, 20), new Array(5, 5, 21), new Array(10, 9, 22), new Array(15, 14, 23), new Array(4, 20, 24), new Array(9, 5, 25), new Array(14, 9, 26), new Array(3, 14, 27), new Array(8, 20, 28), new Array(13, 5, 29), new Array(2, 9, 30), new Array(7, 14, 31), new Array(12, 20, 32));
  
  var MD5_round3 = new Array(new Array(5, 4, 33), new Array(8, 11, 34), new Array(11, 16, 35), new Array(14, 23, 36), new Array(1, 4, 37), new Array(4, 11, 38), new Array(7, 16, 39), new Array(10, 23, 40), new Array(13, 4, 41), new Array(0, 11, 42), new Array(3, 16, 43), new Array(6, 23, 44), new Array(9, 4, 45), new Array(12, 11, 46), new Array(15, 16, 47), new Array(2, 23, 48));
  
  var MD5_round4 = new Array(new Array(0, 6, 49), new Array(7, 10, 50), new Array(14, 15, 51), new Array(5, 21, 52), new Array(12, 6, 53), new Array(3, 10, 54), new Array(10, 15, 55), new Array(1, 21, 56), new Array(8, 6, 57), new Array(15, 10, 58), new Array(6, 15, 59), new Array(13, 21, 60), new Array(4, 6, 61), new Array(11, 10, 62), new Array(2, 15, 63), new Array(9, 21, 64));
  
  function MD5_F(x, y, z) {
   return (x & y) | (~x & z);
  }
  
  function MD5_G(x, y, z) {
   return (x & z) | (y & ~z);
  }
  
  function MD5_H(x, y, z) {
   return x ^ y ^ z;
  }
  
  function MD5_I(x, y, z) {
   return y ^ (x | ~z);
  }
  
  var MD5_round = new Array(new Array(MD5_F, MD5_round1), new Array(MD5_G, MD5_round2), new Array(MD5_H, MD5_round3), new Array(MD5_I, MD5_round4));
  
  function MD5_pack(n32) {
   return String.fromCharCode(n32 & 0xff) + String.fromCharCode((n32 >>> 8) & 0xff) + String.fromCharCode((n32 >>> 16) & 0xff) + String.fromCharCode((n32 >>> 24) & 0xff);
  }
  
  function MD5_unpack(s4) {
   return s4.charCodeAt(0) | (s4.charCodeAt(1) << 8) | (s4.charCodeAt(2) << 16) | (s4.charCodeAt(3) << 24);
  }
  
  function MD5_number(n) {
   while (n < 0)
   n += 4294967296;
   while (n > 4294967295)
   n -= 4294967296;
   return n;
  }
  
  function MD5_apply_round(x, s, f, abcd, r) {
   var a, b, c, d;
   var kk, ss, ii;
   var t, u;
  
   a = abcd[0];
   b = abcd[1];
   c = abcd[2];
   d = abcd[3];
   kk = r[0];
   ss = r[1];
   ii = r[2];
  
   u = f(s[b], s[c], s[d]);
   t = s[a] + u + x[kk] + MD5_T[ii];
   t = MD5_number(t);
   t = ((t << ss) | (t >>> (32 - ss)));
   t += s[b];
   s[a] = MD5_number(t);
  }
  
  function MD5_hash(data) {
   var abcd, x, state, s;
   var len, index, padLen, f, r;
   var i, j, k;
   var tmp;
  
   state = new Array(0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
   len = data.length;
   index = len & 0x3f;
   padLen = (index < 56) ? (56 - index) : (120 - index);
   if (padLen > 0) {
    data += "\x80";
    for (i = 0; i < padLen - 1; i++)
    data += "\x00";
   }
   data += MD5_pack(len * 8);
   data += MD5_pack(0);
   len += padLen + 8;
   abcd = new Array(0, 1, 2, 3);
   x = new Array(16);
   s = new Array(4);
  
   for (k = 0; k < len; k += 64) {
    for (i = 0, j = k; i < 16; i++, j += 4) {
     x[i] = data.charCodeAt(j) | (data.charCodeAt(j + 1) << 8) | (data.charCodeAt(j + 2) << 16) | (data.charCodeAt(j + 3) << 24);
    }
    for (i = 0; i < 4; i++)
    s[i] = state[i];
    for (i = 0; i < 4; i++) {
     f = MD5_round[i][0];
     r = MD5_round[i][1];
     for (j = 0; j < 16; j++) {
      MD5_apply_round(x, s, f, abcd, r[j]);
      tmp = abcd[0];
      abcd[0] = abcd[3];
      abcd[3] = abcd[2];
      abcd[2] = abcd[1];
      abcd[1] = tmp;
     }
    }
  
    for (i = 0; i < 4; i++) {
     state[i] += s[i];
     state[i] = MD5_number(state[i]);
    }
   }
  
   return MD5_pack(state[0]) + MD5_pack(state[1]) + MD5_pack(state[2]) + MD5_pack(state[3]);
  }
  
  function MD5_hexhash(data) {
   var i, out, c;
   var bit128;
  
   bit128 = MD5_hash(data);
   out = "";
   for (i = 0; i < 16; i++) {
    c = bit128.charCodeAt(i);
    out += "0123456789abcdef".charAt((c >> 4) & 0xf);
    out += "0123456789abcdef".charAt(c & 0xf);
   }
   return out;
  }

(function(){md5_opt=function(p){function g(f,e,c,a,b,d){e=k(k(e,f),k(a,d));return k(e<<b|e>>>32-b,c)}function h(f,e,c,a,b,d,h){return g(e&c|~e&a,f,e,b,d,h)}function i(f,e,c,a,b,d,h){return g(e&a|c&~a,f,e,b,d,h)}function j(f,e,c,a,b,d,h){return g(c^(e|~a),f,e,b,d,h)}function l(f,e){var c=f[0],a=f[1],b=f[2],d=f[3],c=h(c,a,b,d,e[0],7,-680876936),d=h(d,c,a,b,e[1],12,-389564586),b=h(b,d,c,a,e[2],17,606105819),a=h(a,b,d,c,e[3],22,-1044525330),c=h(c,a,b,d,e[4],7,-176418897),d=h(d,c,a,b,e[5],12,1200080426),
b=h(b,d,c,a,e[6],17,-1473231341),a=h(a,b,d,c,e[7],22,-45705983),c=h(c,a,b,d,e[8],7,1770035416),d=h(d,c,a,b,e[9],12,-1958414417),b=h(b,d,c,a,e[10],17,-42063),a=h(a,b,d,c,e[11],22,-1990404162),c=h(c,a,b,d,e[12],7,1804603682),d=h(d,c,a,b,e[13],12,-40341101),b=h(b,d,c,a,e[14],17,-1502002290),a=h(a,b,d,c,e[15],22,1236535329),c=i(c,a,b,d,e[1],5,-165796510),d=i(d,c,a,b,e[6],9,-1069501632),b=i(b,d,c,a,e[11],14,643717713),a=i(a,b,d,c,e[0],20,-373897302),c=i(c,a,b,d,e[5],5,-701558691),d=i(d,c,a,b,e[10],9,38016083),
b=i(b,d,c,a,e[15],14,-660478335),a=i(a,b,d,c,e[4],20,-405537848),c=i(c,a,b,d,e[9],5,568446438),d=i(d,c,a,b,e[14],9,-1019803690),b=i(b,d,c,a,e[3],14,-187363961),a=i(a,b,d,c,e[8],20,1163531501),c=i(c,a,b,d,e[13],5,-1444681467),d=i(d,c,a,b,e[2],9,-51403784),b=i(b,d,c,a,e[7],14,1735328473),a=i(a,b,d,c,e[12],20,-1926607734),c=g(a^b^d,c,a,e[5],4,-378558),d=g(c^a^b,d,c,e[8],11,-2022574463),b=g(d^c^a,b,d,e[11],16,1839030562),a=g(b^d^c,a,b,e[14],23,-35309556),c=g(a^b^d,c,a,e[1],4,-1530992060),d=g(c^a^b,d,
c,e[4],11,1272893353),b=g(d^c^a,b,d,e[7],16,-155497632),a=g(b^d^c,a,b,e[10],23,-1094730640),c=g(a^b^d,c,a,e[13],4,681279174),d=g(c^a^b,d,c,e[0],11,-358537222),b=g(d^c^a,b,d,e[3],16,-722521979),a=g(b^d^c,a,b,e[6],23,76029189),c=g(a^b^d,c,a,e[9],4,-640364487),d=g(c^a^b,d,c,e[12],11,-421815835),b=g(d^c^a,b,d,e[15],16,530742520),a=g(b^d^c,a,b,e[2],23,-995338651),c=j(c,a,b,d,e[0],6,-198630844),d=j(d,c,a,b,e[7],10,1126891415),b=j(b,d,c,a,e[14],15,-1416354905),a=j(a,b,d,c,e[5],21,-57434055),c=j(c,a,b,d,
e[12],6,1700485571),d=j(d,c,a,b,e[3],10,-1894986606),b=j(b,d,c,a,e[10],15,-1051523),a=j(a,b,d,c,e[1],21,-2054922799),c=j(c,a,b,d,e[8],6,1873313359),d=j(d,c,a,b,e[15],10,-30611744),b=j(b,d,c,a,e[6],15,-1560198380),a=j(a,b,d,c,e[13],21,1309151649),c=j(c,a,b,d,e[4],6,-145523070),d=j(d,c,a,b,e[11],10,-1120210379),b=j(b,d,c,a,e[2],15,718787259),a=j(a,b,d,c,e[9],21,-343485551);f[0]=k(c,f[0]);f[1]=k(a,f[1]);f[2]=k(b,f[2]);f[3]=k(d,f[3])}function m(f){txt="";var e=f.length,c=[1732584193,-271733879,-1732584194,
271733878],a;for(a=64;a<=e;a+=64){for(var b=c,d=f.substring(a-64,a),h=[],g=void 0,g=0;64>g;g+=4)h[g>>2]=d.charCodeAt(g)+(d.charCodeAt(g+1)<<8)+(d.charCodeAt(g+2)<<16)+(d.charCodeAt(g+3)<<24);l(b,h)}f=f.substring(a-64);b=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];d=f.length;for(a=0;a<d;a++)b[a>>2]|=f.charCodeAt(a)<<(a%4<<3);b[a>>2]|=128<<(a%4<<3);if(55<a){l(c,b);for(a=16;a--;)b[a]=0}b[14]=8*e;l(c,b);return c}function n(f){for(var e=f.length,c=0;c<e;c++){for(var a=f,b=c,d=f[c],g="",h=0;4>h;h++)g+=o[d>>8*h+4&
15]+o[d>>8*h&15];a[b]=g}return f.join("")}function k(f,e){return f+e&4294967295}var o="0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f".split(",");"5d41402abc4b2a76b9719d911017c592"!=n(m("hello"))&&(k=function(f,e){var c=(f&65535)+(e&65535);return(f>>16)+(e>>16)+(c>>16)<<16|c&65535});return n(m(p))}})();

</script>

<script>
/*
FastMD5 by iReal (2013)
https://github.com/iReal/FastMD5
*/
fmd5 = {
 B: {
  res: [],
  tail: [],
  md5blks: []
 },
 c4: [128, 32768, 8388608, -2147483648],
 c16: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"],
 cS: [0, 8, 16, 24],

 cmn: function(q, a, b, x, s1, s2, t) {
  a += q + x + t;

  return ((a << s1 | a >>> s2) + b) << 0;
 },
 hash: function(s, enc, arr) {
  var r;

  for(var i = 0;i < 16;i++) {
   fmd5.B.tail[i] = 0;
  }

  if(enc) {
   r = fmd5.md51(fmd5.encode(s));
  }else{
   r = fmd5.md51(s);
  }

  var tmp = r[0];fmd5.B.res[1] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[0] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[3] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[2] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[5] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[4] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[7] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[6] = fmd5.c16[tmp & 15];

  tmp = r[1];fmd5.B.res[9] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[8] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[11] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[10] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[13] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[12] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[15] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[14] = fmd5.c16[tmp & 15];

  tmp = r[2];fmd5.B.res[17] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[16] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[19] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[18] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[21] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[20] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[23] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[22] = fmd5.c16[tmp & 15];

  tmp = r[3];fmd5.B.res[25] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[24] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[27] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[26] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[29] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[28] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[31] = fmd5.c16[tmp & 15];
  tmp >>= 4;fmd5.B.res[30] = fmd5.c16[tmp & 15];

  if(arr) {
   return fmd5.B.res;
  }else{
   return fmd5.B.res[0] + fmd5.B.res[1] + fmd5.B.res[2] + fmd5.B.res[3] + fmd5.B.res[4] + fmd5.B.res[5] + fmd5.B.res[6] + fmd5.B.res[7] + fmd5.B.res[8] + fmd5.B.res[9] + fmd5.B.res[10] + fmd5.B.res[11] + fmd5.B.res[12] + fmd5.B.res[13] + fmd5.B.res[14] + fmd5.B.res[15] + fmd5.B.res[16] + fmd5.B.res[17] + fmd5.B.res[18] + fmd5.B.res[19] + fmd5.B.res[20] + fmd5.B.res[21] + fmd5.B.res[22] + fmd5.B.res[23] + fmd5.B.res[24] + fmd5.B.res[25] + fmd5.B.res[26] + fmd5.B.res[27] + fmd5.B.res[28] + fmd5.B.res[29] + fmd5.B.res[30] + fmd5.B.res[31];
  }
 },
 encode: function(s) {
  if(s === null || typeof s === "undefined"){
   return "";
  }

  s = s + "";
  var utf = "", start, end, sLen;
  start = end = 0;

  sLen = s.length;
  for(var i = 0;i < sLen;i++) {
   var c1 = s.charCodeAt(i);
   var enc = null;

   if(c1 < 128) {
    end++;
   }else if(c1 > 127 && c1 < 2048) {
    enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128);
   }else{
    enc = String.fromCharCode((c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128);
   }
   if(enc !== null) {
    if(end > start) {
     utf += s.slice(start, end);
    }
    utf += enc;
    start = end = i + 1;
   }
  }

  if(end > start) {
   utf += s.slice(start, sLen);
  }

  return utf;
 },
 md5cycle: function(k) {
  var r = fmd5.md5_rounds(1732584193, -271733879, -1732584194, 271733878, k, true);

  return [
   (r[0] + 1732584193) << 0,
   (r[1] - 271733879) << 0,
   (r[2] - 1732584194) << 0,
   (r[3] + 271733878) << 0
  ];
 },
 md5cycleAdd: function(x, k) {
  var r = fmd5.md5_rounds(x[0], x[1], x[2], x[3], k, false);

  return [
   (r[0] + x[0]) << 0,
   (r[1] + x[1]) << 0,
   (r[2] + x[2]) << 0,
   (r[3] + x[3]) << 0
  ];
 },
 md5_rounds: function(a, b, c, d, k, simple) {
  if(simple) {
   a = k[0] - 680876937;
   a = ((a << 7 | a >>> 25) - 271733879) << 0;
   d = k[1] - 117830708 + ((2004318071 & a) ^ -1732584194);
   d = ((d << 12 | d >>> 20) + a) << 0;
   c = k[2] - 1126478375 + (((a ^ -271733879) & d) ^ -271733879);
   c = ((c << 17 | c >>> 15) + d) << 0;
   b = k[3] - 1316259209 + (((d ^ a) & c) ^ a);
   b = ((b << 22 | b >>> 10) + c) << 0;
  }else{
   a = fmd5.cmn(((c ^ d) & b) ^ d, a, b, k[0], 7, 25, -680876936);
   d = fmd5.cmn(((b ^ c) & a) ^ c, d, a, k[1], 12, 20, -389564586);
   c = fmd5.cmn(((a ^ b) & d) ^ b, c, d, k[2], 17, 15, 606105819);
   b = fmd5.cmn(((d ^ a) & c) ^ a, b, c, k[3], 22, 10, -1044525330);
  }

  a = fmd5.cmn(((c ^ d) & b) ^ d, a, b, k[4], 7, 25, -176418897);
  d = fmd5.cmn(((b ^ c) & a) ^ c, d, a, k[5], 12, 20, 1200080426);
  c = fmd5.cmn(((a ^ b) & d) ^ b, c, d, k[6], 17, 15, -1473231341);
  b = fmd5.cmn(((d ^ a) & c) ^ a, b, c, k[7], 22, 10, -45705983);
  a = fmd5.cmn(((c ^ d) & b) ^ d, a, b, k[8], 7, 25, 1770035416);
  d = fmd5.cmn(((b ^ c) & a) ^ c, d, a, k[9], 12, 20, -1958414417);
  c = fmd5.cmn(((a ^ b) & d) ^ b, c, d, k[10], 17, 15, -42063);
  b = fmd5.cmn(((d ^ a) & c) ^ a, b, c, k[11], 22, 10, -1990404162);
  a = fmd5.cmn(((c ^ d) & b) ^ d, a, b, k[12], 7, 25, 1804603682);
  d = fmd5.cmn(((b ^ c) & a) ^ c, d, a, k[13], 12, 20, -40341101);
  c = fmd5.cmn(((a ^ b) & d) ^ b, c, d, k[14], 17, 15, -1502002290);
  b = fmd5.cmn(((d ^ a) & c) ^ a, b, c, k[15], 22, 10, 1236535329);

  a = fmd5.cmn(((b ^ c) & d) ^ c, a, b, k[1], 5, 27, -165796510);
  d = fmd5.cmn(((a ^ b) & c) ^ b, d, a, k[6], 9, 23, -1069501632);
  c = fmd5.cmn(((d ^ a) & b) ^ a, c, d, k[11], 14, 18, 643717713);
  b = fmd5.cmn(((c ^ d) & a) ^ d, b, c, k[0], 20, 12, -373897302);
  a = fmd5.cmn(((b ^ c) & d) ^ c, a, b, k[5], 5, 27, -701558691);
  d = fmd5.cmn(((a ^ b) & c) ^ b, d, a, k[10], 9, 23, 38016083);
  c = fmd5.cmn(((d ^ a) & b) ^ a, c, d, k[15], 14, 18, -660478335);
  b = fmd5.cmn(((c ^ d) & a) ^ d, b, c, k[4], 20, 12, -405537848);
  a = fmd5.cmn(((b ^ c) & d) ^ c, a, b, k[9], 5, 27, 568446438);
  d = fmd5.cmn(((a ^ b) & c) ^ b, d, a, k[14], 9, 23, -1019803690);
  c = fmd5.cmn(((d ^ a) & b) ^ a, c, d, k[3], 14, 18, -187363961);
  b = fmd5.cmn(((c ^ d) & a) ^ d, b, c, k[8], 20, 12, 1163531501);
  a = fmd5.cmn(((b ^ c) & d) ^ c, a, b, k[13], 5, 27, -1444681467);
  d = fmd5.cmn(((a ^ b) & c) ^ b, d, a, k[2], 9, 23, -51403784);
  c = fmd5.cmn(((d ^ a) & b) ^ a, c, d, k[7], 14, 18, 1735328473);
  b = fmd5.cmn(((c ^ d) & a) ^ d, b, c, k[12], 20, 12, -1926607734);

  a = fmd5.cmn(b ^ c ^ d, a, b, k[5], 4, 28, -378558);
  d = fmd5.cmn(a ^ b ^ c, d, a, k[8], 11, 21, -2022574463);
  c = fmd5.cmn(d ^ a ^ b, c, d, k[11], 16, 16, 1839030562);
  b = fmd5.cmn(c ^ d ^ a, b, c, k[14], 23, 9, -35309556);
  a = fmd5.cmn(b ^ c ^ d, a, b, k[1], 4, 28, -1530992060);
  d = fmd5.cmn(a ^ b ^ c, d, a, k[4], 11, 21, 1272893353);
  c = fmd5.cmn(d ^ a ^ b, c, d, k[7], 16, 16, -155497632);
  b = fmd5.cmn(c ^ d ^ a, b, c, k[10], 23, 9, -1094730640);
  a = fmd5.cmn(b ^ c ^ d, a, b, k[13], 4, 28, 681279174);
  d = fmd5.cmn(a ^ b ^ c, d, a, k[0], 11, 21, -358537222);
  c = fmd5.cmn(d ^ a ^ b, c, d, k[3], 16, 16, -722521979);
  b = fmd5.cmn(c ^ d ^ a, b, c, k[6], 23, 9, 76029189);
  a = fmd5.cmn(b ^ c ^ d, a, b, k[9], 4, 28, -640364487);
  d = fmd5.cmn(a ^ b ^ c, d, a, k[12], 11, 21, -421815835);
  c = fmd5.cmn(d ^ a ^ b, c, d, k[15], 16, 16, 530742520);
  b = fmd5.cmn(c ^ d ^ a, b, c, k[2], 23, 9, -995338651);

  a = fmd5.cmn(c ^ (b | ~d), a, b, k[0], 6, 26, -198630844);
  d = fmd5.cmn(b ^ (a | ~c), d, a, k[7], 10, 22, 1126891415);
  c = fmd5.cmn(a ^ (d | ~b), c, d, k[14], 15, 17, -1416354905);
  b = fmd5.cmn(d ^ (c | ~a), b, c, k[5], 21, 11, -57434055);
  a = fmd5.cmn(c ^ (b | ~d), a, b, k[12], 6, 26, 1700485571);
  d = fmd5.cmn(b ^ (a | ~c), d, a, k[3], 10, 22, -1894986606);
  c = fmd5.cmn(a ^ (d | ~b), c, d, k[10], 15, 17, -1051523);
  b = fmd5.cmn(d ^ (c | ~a), b, c, k[1], 21, 11, -2054922799);
  a = fmd5.cmn(c ^ (b | ~d), a, b, k[8], 6, 26, 1873313359);
  d = fmd5.cmn(b ^ (a | ~c), d, a, k[15], 10, 22, -30611744);
  c = fmd5.cmn(a ^ (d | ~b), c, d, k[6], 15, 17, -1560198380);
  b = fmd5.cmn(d ^ (c | ~a), b, c, k[13], 21, 11, 1309151649);
  a = fmd5.cmn(c ^ (b | ~d), a, b, k[4], 6, 26, -145523070);
  d = fmd5.cmn(b ^ (a | ~c), d, a, k[11], 10, 22, -1120210379);
  c = fmd5.cmn(a ^ (d | ~b), c, d, k[2], 15, 17, 718787259);
  b = fmd5.cmn(d ^ (c | ~a), b, c, k[9], 21, 11, -343485551);

  return [a, b, c, d];
 },
 md51: function(s) {
  var n = s.length,
  state = false;

  for(var i = 64;i <= n;i += 64) {
   if(i == 64) {
    fmd5.md5blk(s.substring(0, 64));
    state = fmd5.md5cycle(fmd5.B.md5blks);
   }else{
    fmd5.md5blk(s.substring(i - 64, i));
    state = fmd5.md5cycleAdd(state, fmd5.B.md5blks);
   }
  }

  if(i > 64) {
   s = s.substring(i - 64);
  }

  var sl = s.length;

  for(i = 0;i < sl;++i) {
   fmd5.B.tail[i >> 2] |= s.charCodeAt(i) << fmd5.cS[i % 4];
  }
  fmd5.B.tail[i >> 2] |= fmd5.c4[i % 4];

  if(i > 55) {
   state = fmd5.md5cycleAdd(state, fmd5.B.tail);
   for(var j = 0;j < 16;j++) {
    fmd5.B.tail[j] = 0;
   }
  }

  fmd5.B.tail[14] = n * 8;

  if(typeof state == "boolean") {
   return fmd5.md5cycle(fmd5.B.tail);
  }else{
   return fmd5.md5cycleAdd(state, fmd5.B.tail);
  }
 },
 md5blk: function(s) {
  fmd5.B.md5blks[0]  =  s.charCodeAt(0) +  (s.charCodeAt(1) << 8) +  (s.charCodeAt(2) << 16) +  (s.charCodeAt(3) << 24);
  fmd5.B.md5blks[1]  =  s.charCodeAt(4) +  (s.charCodeAt(5) << 8) +  (s.charCodeAt(6) << 16) +  (s.charCodeAt(7) << 24);
  fmd5.B.md5blks[2]  =  s.charCodeAt(8) +  (s.charCodeAt(9) << 8) + (s.charCodeAt(10) << 16) + (s.charCodeAt(11) << 24);
  fmd5.B.md5blks[3]  = s.charCodeAt(12) + (s.charCodeAt(13) << 8) + (s.charCodeAt(14) << 16) + (s.charCodeAt(15) << 24);
  fmd5.B.md5blks[4]  = s.charCodeAt(16) + (s.charCodeAt(17) << 8) + (s.charCodeAt(18) << 16) + (s.charCodeAt(19) << 24);
  fmd5.B.md5blks[5]  = s.charCodeAt(20) + (s.charCodeAt(21) << 8) + (s.charCodeAt(22) << 16) + (s.charCodeAt(23) << 24);
  fmd5.B.md5blks[6]  = s.charCodeAt(24) + (s.charCodeAt(25) << 8) + (s.charCodeAt(26) << 16) + (s.charCodeAt(27) << 24);
  fmd5.B.md5blks[7]  = s.charCodeAt(28) + (s.charCodeAt(29) << 8) + (s.charCodeAt(30) << 16) + (s.charCodeAt(31) << 24);
  fmd5.B.md5blks[8]  = s.charCodeAt(32) + (s.charCodeAt(33) << 8) + (s.charCodeAt(34) << 16) + (s.charCodeAt(35) << 24);
  fmd5.B.md5blks[9]  = s.charCodeAt(36) + (s.charCodeAt(37) << 8) + (s.charCodeAt(38) << 16) + (s.charCodeAt(39) << 24);
  fmd5.B.md5blks[10] = s.charCodeAt(40) + (s.charCodeAt(41) << 8) + (s.charCodeAt(42) << 16) + (s.charCodeAt(43) << 24);
  fmd5.B.md5blks[11] = s.charCodeAt(44) + (s.charCodeAt(45) << 8) + (s.charCodeAt(46) << 16) + (s.charCodeAt(47) << 24);
  fmd5.B.md5blks[12] = s.charCodeAt(48) + (s.charCodeAt(49) << 8) + (s.charCodeAt(50) << 16) + (s.charCodeAt(51) << 24);
  fmd5.B.md5blks[13] = s.charCodeAt(52) + (s.charCodeAt(53) << 8) + (s.charCodeAt(54) << 16) + (s.charCodeAt(55) << 24);
  fmd5.B.md5blks[14] = s.charCodeAt(56) + (s.charCodeAt(57) << 8) + (s.charCodeAt(58) << 16) + (s.charCodeAt(59) << 24);
  fmd5.B.md5blks[15] = s.charCodeAt(60) + (s.charCodeAt(61) << 8) + (s.charCodeAt(62) << 16) + (s.charCodeAt(63) << 24);
 }
};
</script>
<script>
/*jshint bitwise:false*/
/*global unescape*/

(function (factory) {
    if (typeof exports === 'object') {
        // Node/CommonJS
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        // AMD
        define(factory);
    } else {
        // Browser globals (with support for web workers)
        var glob;
        try {
            glob = window;
        } catch (e) {
            glob = self;
        }

        glob.SparkMD5 = factory();
    }
}(function (undefined) {

    'use strict';

    ////////////////////////////////////////////////////////////////////////////

    /*
     * Fastest md5 implementation around (JKM md5)
     * Credits: Joseph Myers
     *
     * @see http://www.myersdaily.org/joseph/javascript/md5-text.html
     * @see http://jsperf.com/md5-shootout/7
     */

    /* this function is much faster,
      so if possible we use it. Some IEs
      are the only ones I know of that
      need the idiotic second function,
      generated by an if clause.  */
    var add32 = function (a, b) {
        return (a + b) & 0xFFFFFFFF;
    },

    cmn = function (q, a, b, x, s, t) {
        a = add32(add32(a, q), add32(x, t));
        return add32((a << s) | (a >>> (32 - s)), b);
    },

    ff = function (a, b, c, d, x, s, t) {
        return cmn((b & c) | ((~b) & d), a, b, x, s, t);
    },

    gg = function (a, b, c, d, x, s, t) {
        return cmn((b & d) | (c & (~d)), a, b, x, s, t);
    },

    hh = function (a, b, c, d, x, s, t) {
        return cmn(b ^ c ^ d, a, b, x, s, t);
    },

    ii = function (a, b, c, d, x, s, t) {
        return cmn(c ^ (b | (~d)), a, b, x, s, t);
    },

    md5cycle = function (x, k) {
        var a = x[0],
            b = x[1],
            c = x[2],
            d = x[3];

        a = ff(a, b, c, d, k[0], 7, -680876936);
        d = ff(d, a, b, c, k[1], 12, -389564586);
        c = ff(c, d, a, b, k[2], 17, 606105819);
        b = ff(b, c, d, a, k[3], 22, -1044525330);
        a = ff(a, b, c, d, k[4], 7, -176418897);
        d = ff(d, a, b, c, k[5], 12, 1200080426);
        c = ff(c, d, a, b, k[6], 17, -1473231341);
        b = ff(b, c, d, a, k[7], 22, -45705983);
        a = ff(a, b, c, d, k[8], 7, 1770035416);
        d = ff(d, a, b, c, k[9], 12, -1958414417);
        c = ff(c, d, a, b, k[10], 17, -42063);
        b = ff(b, c, d, a, k[11], 22, -1990404162);
        a = ff(a, b, c, d, k[12], 7, 1804603682);
        d = ff(d, a, b, c, k[13], 12, -40341101);
        c = ff(c, d, a, b, k[14], 17, -1502002290);
        b = ff(b, c, d, a, k[15], 22, 1236535329);

        a = gg(a, b, c, d, k[1], 5, -165796510);
        d = gg(d, a, b, c, k[6], 9, -1069501632);
        c = gg(c, d, a, b, k[11], 14, 643717713);
        b = gg(b, c, d, a, k[0], 20, -373897302);
        a = gg(a, b, c, d, k[5], 5, -701558691);
        d = gg(d, a, b, c, k[10], 9, 38016083);
        c = gg(c, d, a, b, k[15], 14, -660478335);
        b = gg(b, c, d, a, k[4], 20, -405537848);
        a = gg(a, b, c, d, k[9], 5, 568446438);
        d = gg(d, a, b, c, k[14], 9, -1019803690);
        c = gg(c, d, a, b, k[3], 14, -187363961);
        b = gg(b, c, d, a, k[8], 20, 1163531501);
        a = gg(a, b, c, d, k[13], 5, -1444681467);
        d = gg(d, a, b, c, k[2], 9, -51403784);
        c = gg(c, d, a, b, k[7], 14, 1735328473);
        b = gg(b, c, d, a, k[12], 20, -1926607734);

        a = hh(a, b, c, d, k[5], 4, -378558);
        d = hh(d, a, b, c, k[8], 11, -2022574463);
        c = hh(c, d, a, b, k[11], 16, 1839030562);
        b = hh(b, c, d, a, k[14], 23, -35309556);
        a = hh(a, b, c, d, k[1], 4, -1530992060);
        d = hh(d, a, b, c, k[4], 11, 1272893353);
        c = hh(c, d, a, b, k[7], 16, -155497632);
        b = hh(b, c, d, a, k[10], 23, -1094730640);
        a = hh(a, b, c, d, k[13], 4, 681279174);
        d = hh(d, a, b, c, k[0], 11, -358537222);
        c = hh(c, d, a, b, k[3], 16, -722521979);
        b = hh(b, c, d, a, k[6], 23, 76029189);
        a = hh(a, b, c, d, k[9], 4, -640364487);
        d = hh(d, a, b, c, k[12], 11, -421815835);
        c = hh(c, d, a, b, k[15], 16, 530742520);
        b = hh(b, c, d, a, k[2], 23, -995338651);

        a = ii(a, b, c, d, k[0], 6, -198630844);
        d = ii(d, a, b, c, k[7], 10, 1126891415);
        c = ii(c, d, a, b, k[14], 15, -1416354905);
        b = ii(b, c, d, a, k[5], 21, -57434055);
        a = ii(a, b, c, d, k[12], 6, 1700485571);
        d = ii(d, a, b, c, k[3], 10, -1894986606);
        c = ii(c, d, a, b, k[10], 15, -1051523);
        b = ii(b, c, d, a, k[1], 21, -2054922799);
        a = ii(a, b, c, d, k[8], 6, 1873313359);
        d = ii(d, a, b, c, k[15], 10, -30611744);
        c = ii(c, d, a, b, k[6], 15, -1560198380);
        b = ii(b, c, d, a, k[13], 21, 1309151649);
        a = ii(a, b, c, d, k[4], 6, -145523070);
        d = ii(d, a, b, c, k[11], 10, -1120210379);
        c = ii(c, d, a, b, k[2], 15, 718787259);
        b = ii(b, c, d, a, k[9], 21, -343485551);

        x[0] = add32(a, x[0]);
        x[1] = add32(b, x[1]);
        x[2] = add32(c, x[2]);
        x[3] = add32(d, x[3]);
    },

    /* there needs to be support for Unicode here,
       * unless we pretend that we can redefine the MD-5
       * algorithm for multi-byte characters (perhaps
       * by adding every four 16-bit characters and
       * shortening the sum to 32 bits). Otherwise
       * I suggest performing MD-5 as if every character
       * was two bytes--e.g., 0040 0025 = @%--but then
       * how will an ordinary MD-5 sum be matched?
       * There is no way to standardize text to something
       * like UTF-8 before transformation; speed cost is
       * utterly prohibitive. The JavaScript standard
       * itself needs to look at this: it should start
       * providing access to strings as preformed UTF-8
       * 8-bit unsigned value arrays.
       */
    md5blk = function (s) {
        var md5blks = [],
            i; /* Andy King said do it this way. */

        for (i = 0; i < 64; i += 4) {
            md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
        }
        return md5blks;
    },

    md5blk_array = function (a) {
        var md5blks = [],
            i; /* Andy King said do it this way. */

        for (i = 0; i < 64; i += 4) {
            md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
        }
        return md5blks;
    },

    md51 = function (s) {
        var n = s.length,
            state = [1732584193, -271733879, -1732584194, 271733878],
            i,
            length,
            tail,
            tmp,
            lo,
            hi;

        for (i = 64; i <= n; i += 64) {
            md5cycle(state, md5blk(s.substring(i - 64, i)));
        }
        s = s.substring(i - 64);
        length = s.length;
        tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        for (i = 0; i < length; i += 1) {
            tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
        }
        tail[i >> 2] |= 0x80 << ((i % 4) << 3);
        if (i > 55) {
            md5cycle(state, tail);
            for (i = 0; i < 16; i += 1) {
                tail[i] = 0;
            }
        }

        // Beware that the final length might not fit in 32 bits so we take care of that
        tmp = n * 8;
        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
        lo = parseInt(tmp[2], 16);
        hi = parseInt(tmp[1], 16) || 0;

        tail[14] = lo;
        tail[15] = hi;

        md5cycle(state, tail);
        return state;
    },

    md51_array = function (a) {
        var n = a.length,
            state = [1732584193, -271733879, -1732584194, 271733878],
            i,
            length,
            tail,
            tmp,
            lo,
            hi;

        for (i = 64; i <= n; i += 64) {
            md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
        }

        // Not sure if it is a bug, however IE10 will always produce a sub array of length 1
        // containing the last element of the parent array if the sub array specified starts
        // beyond the length of the parent array - weird.
        // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
        a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);

        length = a.length;
        tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        for (i = 0; i < length; i += 1) {
            tail[i >> 2] |= a[i] << ((i % 4) << 3);
        }

        tail[i >> 2] |= 0x80 << ((i % 4) << 3);
        if (i > 55) {
            md5cycle(state, tail);
            for (i = 0; i < 16; i += 1) {
                tail[i] = 0;
            }
        }

        // Beware that the final length might not fit in 32 bits so we take care of that
        tmp = n * 8;
        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
        lo = parseInt(tmp[2], 16);
        hi = parseInt(tmp[1], 16) || 0;

        tail[14] = lo;
        tail[15] = hi;

        md5cycle(state, tail);

        return state;
    },

    hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'],

    rhex = function (n) {
        var s = '',
            j;
        for (j = 0; j < 4; j += 1) {
            s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
        }
        return s;
    },

    hex = function (x) {
        var i;
        for (i = 0; i < x.length; i += 1) {
            x[i] = rhex(x[i]);
        }
        return x.join('');
    },

    md5 = function (s) {
        return hex(md51(s));
    },



    ////////////////////////////////////////////////////////////////////////////

    /**
     * SparkMD5 OOP implementation.
     *
     * Use this class to perform an incremental md5, otherwise use the
     * static methods instead.
     */
    SparkMD5 = function () {
        // call reset to init the instance
        this.reset();
    };


    // In some cases the fast add32 function cannot be used..
    if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') {
        add32 = function (x, y) {
            var lsw = (x & 0xFFFF) + (y & 0xFFFF),
                msw = (x >> 16) + (y >> 16) + (lsw >> 16);
            return (msw << 16) | (lsw & 0xFFFF);
        };
    }


    /**
     * Appends a string.
     * A conversion will be applied if an utf8 string is detected.
     *
     * @param {String} str The string to be appended
     *
     * @return {SparkMD5} The instance itself
     */
    SparkMD5.prototype.append = function (str) {
        // converts the string to utf8 bytes if necessary
        if (/[\u0080-\uFFFF]/.test(str)) {
            str = unescape(encodeURIComponent(str));
        }

        // then append as binary
        this.appendBinary(str);

        return this;
    };

    /**
     * Appends a binary string.
     *
     * @param {String} contents The binary string to be appended
     *
     * @return {SparkMD5} The instance itself
     */
    SparkMD5.prototype.appendBinary = function (contents) {
        this._buff += contents;
        this._length += contents.length;

        var length = this._buff.length,
            i;

        for (i = 64; i <= length; i += 64) {
            md5cycle(this._state, md5blk(this._buff.substring(i - 64, i)));
        }

        this._buff = this._buff.substr(i - 64);

        return this;
    };

    /**
     * Finishes the incremental computation, reseting the internal state and
     * returning the result.
     * Use the raw parameter to obtain the raw result instead of the hex one.
     *
     * @param {Boolean} raw True to get the raw result, false to get the hex result
     *
     * @return {String|Array} The result
     */
    SparkMD5.prototype.end = function (raw) {
        var buff = this._buff,
            length = buff.length,
            i,
            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            ret;

        for (i = 0; i < length; i += 1) {
            tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
        }

        this._finish(tail, length);
        ret = !!raw ? this._state : hex(this._state);

        this.reset();

        return ret;
    };

    /**
     * Finish the final calculation based on the tail.
     *
     * @param {Array}  tail   The tail (will be modified)
     * @param {Number} length The length of the remaining buffer
     */
    SparkMD5.prototype._finish = function (tail, length) {
        var i = length,
            tmp,
            lo,
            hi;

        tail[i >> 2] |= 0x80 << ((i % 4) << 3);
        if (i > 55) {
            md5cycle(this._state, tail);
            for (i = 0; i < 16; i += 1) {
                tail[i] = 0;
            }
        }

        // Do the final computation based on the tail and length
        // Beware that the final length may not fit in 32 bits so we take care of that
        tmp = this._length * 8;
        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
        lo = parseInt(tmp[2], 16);
        hi = parseInt(tmp[1], 16) || 0;

        tail[14] = lo;
        tail[15] = hi;
        md5cycle(this._state, tail);
    };

    /**
     * Resets the internal state of the computation.
     *
     * @return {SparkMD5} The instance itself
     */
    SparkMD5.prototype.reset = function () {
        this._buff = "";
        this._length = 0;
        this._state = [1732584193, -271733879, -1732584194, 271733878];

        return this;
    };

    /**
     * Releases memory used by the incremental buffer and other aditional
     * resources. If you plan to use the instance again, use reset instead.
     */
    SparkMD5.prototype.destroy = function () {
        delete this._state;
        delete this._buff;
        delete this._length;
    };


    /**
     * Performs the md5 hash on a string.
     * A conversion will be applied if utf8 string is detected.
     *
     * @param {String}  str The string
     * @param {Boolean} raw True to get the raw result, false to get the hex result
     *
     * @return {String|Array} The result
     */
    SparkMD5.hash = function (str, raw) {
        // converts the string to utf8 bytes if necessary
        if (/[\u0080-\uFFFF]/.test(str)) {
            str = unescape(encodeURIComponent(str));
        }

        var hash = md51(str);

        return !!raw ? hash : hex(hash);
    };

    /**
     * Performs the md5 hash on a binary string.
     *
     * @param {String}  content The binary string
     * @param {Boolean} raw     True to get the raw result, false to get the hex result
     *
     * @return {String|Array} The result
     */
    SparkMD5.hashBinary = function (content, raw) {
        var hash = md51(content);

        return !!raw ? hash : hex(hash);
    };

    /**
     * SparkMD5 OOP implementation for array buffers.
     *
     * Use this class to perform an incremental md5 ONLY for array buffers.
     */
    SparkMD5.ArrayBuffer = function () {
        // call reset to init the instance
        this.reset();
    };

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Appends an array buffer.
     *
     * @param {ArrayBuffer} arr The array to be appended
     *
     * @return {SparkMD5.ArrayBuffer} The instance itself
     */
    SparkMD5.ArrayBuffer.prototype.append = function (arr) {
        // TODO: we could avoid the concatenation here but the algorithm would be more complex
        //       if you find yourself needing extra performance, please make a PR.
        var buff = this._concatArrayBuffer(this._buff, arr),
            length = buff.length,
            i;

        this._length += arr.byteLength;

        for (i = 64; i <= length; i += 64) {
            md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i)));
        }

        // Avoids IE10 weirdness (documented above)
        this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0);

        return this;
    };

    /**
     * Finishes the incremental computation, reseting the internal state and
     * returning the result.
     * Use the raw parameter to obtain the raw result instead of the hex one.
     *
     * @param {Boolean} raw True to get the raw result, false to get the hex result
     *
     * @return {String|Array} The result
     */
    SparkMD5.ArrayBuffer.prototype.end = function (raw) {
        var buff = this._buff,
            length = buff.length,
            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            i,
            ret;

        for (i = 0; i < length; i += 1) {
            tail[i >> 2] |= buff[i] << ((i % 4) << 3);
        }

        this._finish(tail, length);
        ret = !!raw ? this._state : hex(this._state);

        this.reset();

        return ret;
    };

    SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;

    /**
     * Resets the internal state of the computation.
     *
     * @return {SparkMD5.ArrayBuffer} The instance itself
     */
    SparkMD5.ArrayBuffer.prototype.reset = function () {
        this._buff = new Uint8Array(0);
        this._length = 0;
        this._state = [1732584193, -271733879, -1732584194, 271733878];

        return this;
    };

    /**
     * Releases memory used by the incremental buffer and other aditional
     * resources. If you plan to use the instance again, use reset instead.
     */
    SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;

    /**
     * Concats two array buffers, returning a new one.
     *
     * @param  {ArrayBuffer} first  The first array buffer
     * @param  {ArrayBuffer} second The second array buffer
     *
     * @return {ArrayBuffer} The new array buffer
     */
    SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) {
        var firstLength = first.length,
            result = new Uint8Array(firstLength + second.byteLength);

        result.set(first);
        result.set(new Uint8Array(second), firstLength);

        return result;
    };

    /**
     * Performs the md5 hash on an array buffer.
     *
     * @param {ArrayBuffer} arr The array buffer
     * @param {Boolean}     raw True to get the raw result, false to get the hex result
     *
     * @return {String|Array} The result
     */
    SparkMD5.ArrayBuffer.hash = function (arr, raw) {
        var hash = md51_array(new Uint8Array(arr));

        return !!raw ? hash : hex(hash);
    };

    return SparkMD5;
}));
</script>
<script>
testGet = new XMLHttpRequest(); testGet.open('GET', '/faq', false); testGet.send();
dataStr = testGet.responseText;
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
Paj's MD5
hex_md5(dataStr);
ready
valums MD5
V.Security.md5(dataStr);
ready
jkm MD5
md5(dataStr);
ready
utf8 MD5
md5_utf8(dataStr);
ready
MD5_hexhash()
MD5_hexhash(dataStr);
ready
md5_opt()
md5_opt(dataStr);
ready
FastMD5
/*
*** dataStr contains Unicode characters, so before hashing the data, we have to encode it to utf8 (or computation will be slowed down, like in the previous test case).

*** Anyway, in FastMD5 you have to manually choose if data contains Unicode characters.

*** If second argument set to true, then data encoded to utf8 at first, and then hashed. If false, then we hash without encoding.
*/
fmd5.hash(dataStr, true, true);
ready
SparkMD5
SparkMD5.hash(dataStr);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.