```js (function(global) { /* 此加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节 我们以prePlain表示前一个明文块,plain表示当前明文块,crypt表示当前明文块加密得到的密文块,preCrypt表示前一个密文块 f表示加密算法,d表示解密算法 那么从plain得到crypt的过程是: crypt = f(plain ^ preCrypt) ^ prePlain 所以,从crypt得到plain的过程自然是 plain = d(crypt ^ prePlain) ^ preCrypt 此外,算法有它的填充机制,其会在明文前和明文后分别填充一定的字节数,以保证明文长度是8字节的倍数 填充的字节数与原始明文长度有关,填充的方法是: ------- 消息填充算法 ----------- a = (明文长度 + 10) mod 8 if(a 不等于 0) a = 8 - a; b = 随机数 & 0xF8 | a; 这个的作用是把a的值保存了下来, a的值小于8 plain[0] = b; 然后把b做为明文的第0个字节,这样第0个字节就保存了a的信息,这个信息在解密时就要用来找到真正明文的起始位置 plain[1..a+2] = 随机数 & 0xFF; 这里用随机数填充明文的第1到第a+2个字节 plain[a+3..a+3+明文长度-1] = 明文; 从a+3字节开始才是真正的明文 plain[a+3+明文长度, 最后] = 0; 在最后,填充0,填充到总长度为8的整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容 ------- 消息填充算法 ------------ @author 马若劼 @author notXX @author 唐浩宸 */ var __key = '', __pos = 0, __plain = [], __prePlain = [], __cryptPos = 0, // 当前密文块位置 __preCryptPos = 0, // 上一个密文块位置 __out = [], // 保存加密/解密的输出 __cipher = [], // 输出的密文 /*用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的, 但是最开始的8个字节没有反馈可用,所有需要标明这种情况*/ __header = true; function __rand() { return Math.round(Math.random()*0xffffffff); } /** * 将数据转化为无符号整形 */ function __getUInt(data, offset, len) { if (!len || len > 4) len = 4; var ret = 0; for (var i=offset; i>> 0; // 无符号化 } /** 把整形数据填充到数组里,要注意端序 */ function __intToBytes(data, offset, value) { data[offset+3] = (value >> 0) & 0xff; data[offset+2] = (value >> 8) & 0xff; data[offset+1] = (value >> 16) & 0xff; data[offset+0] = (value >> 24) & 0xff; } function __bytesInStr(data) { if (!data) return ""; var outInHex = ""; for (var i=0; i 0x0 && code <= 0x7f){ //单字节 //UTF-16 0000 - 007F //UTF-8 0xxxxxxx ret.push(s.charAt(i)); }else if(code >= 0x80 && code <= 0x7ff){ //双字节 //UTF-16 0080 - 07FF //UTF-8 110xxxxx 10xxxxxx ret.push( //110xxxxx String.fromCharCode(0xc0 | ((code >> 6) & 0x1f)), //10xxxxxx String.fromCharCode(0x80 | (code & 0x3f)) ); }else if(code >= 0x800 && code <= 0xffff){ //三字节 //UTF-16 0800 - FFFF //UTF-8 1110xxxx 10xxxxxx 10xxxxxx ret.push( //1110xxxx String.fromCharCode(0xe0 | ((code >> 12) & 0xf)), //10xxxxxx String.fromCharCode(0x80 | ((code >> 6) & 0x3f)), //10xxxxxx String.fromCharCode(0x80 | (code & 0x3f)) ); } } return ret.join(''); } function __encrypt(data) { __plain = new Array(8); __prePlain = new Array(8); __cryptPos = __preCryptPos = 0; __header = true; __pos = 0; var len = data.length; var padding = 0; __pos = (len + 0x0A) % 8; if (__pos != 0) __pos = 8 - __pos; __out = new Array(len + __pos + 10); __plain[0] = ((__rand() & 0xF8) | __pos ) & 0xFF; for (var i=1; i<=__pos; i++) __plain[i] = __rand() & 0xFF; __pos++; for (var i=0; i<8; i++) __prePlain[i] = 0; padding = 1; while (padding <= 2) { if (__pos < 8) { __plain[__pos++] = __rand() & 0xFF; padding++; } if (__pos == 8) __encrypt8bytes(); } var i = 0; while (len > 0) { if (__pos < 8) { __plain[__pos++] = data[i++]; len--; } if (__pos == 8) __encrypt8bytes(); } padding = 1; while (padding <= 7) { if (__pos < 8) { __plain[__pos++] = 0; padding++; } if (__pos == 8) __encrypt8bytes(); } return __out; } function __decrypt(data) { var count = 0; var m = new Array(8); var len = data.length; __cipher = data; if (len % 8 != 0 || len < 16) return null; /* 第一个8字节,加密的时候因为prePlain是全0,所以可以直接解密,得到消息的头部, 关键是可以得到真正明文开始的位置 */ __prePlain = __decipher(data); __pos = __prePlain[0] & 0x7; count = len - __pos - 10; // 真正的明文长度 if (count < 0) return null; // 临时的preCrypt, 与加密时对应,全0的prePlain 对应 全0的preCrypt for (var i=0; i>> 0; while (loop-- > 0) { sum += delta; sum = (sum & 0xFFFFFFFF) >>> 0; y += ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b); y = (y & 0xFFFFFFFF) >>> 0; z += ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d); z = (z & 0xFFFFFFFF) >>> 0; } var bytes = new Array(8); __intToBytes(bytes, 0, y); __intToBytes(bytes, 4, z); return bytes; } function __decipher(data) { var loop = 16; var y = __getUInt(data, 0, 4); var z = __getUInt(data, 4, 4); var a = __getUInt(__key, 0, 4); var b = __getUInt(__key, 4, 4); var c = __getUInt(__key, 8, 4); var d = __getUInt(__key, 12, 4); var sum = 0xE3779B90 >>> 0; var delta = 0x9E3779B9 >>> 0; while (loop-- > 0) { z -= ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d); z = (z & 0xFFFFFFFF) >>> 0; y -= ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b); y = (y & 0xFFFFFFFF) >>> 0; sum -= delta; sum = (sum & 0xFFFFFFFF) >>> 0; } var bytes = new Array(8); __intToBytes(bytes, 0, y); __intToBytes(bytes, 4, z); return bytes; } function __decrypt8Bytes() { var len = __cipher.length; for (var i=0; i<8; i++) { __prePlain[i] ^= __cipher[__cryptPos + i]; } __prePlain = __decipher(__prePlain); __cryptPos += 8; __pos = 0; return true; } /** * 把输入字符串转换为javascript array */ function __dataFromStr(str, isASCII) { var data = []; if (isASCII) { for (var i=0; i> 16, (b10 >> 8) & 0xff, b10 & 0xff)); } switch (pads) { case 1: b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6) x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff)); break; case 2: b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12); x.push(String.fromCharCode(b10 >> 16)); break; } return x.join(''); } */ base64.getbyte = function(s,i) { var x = s.charCodeAt(i); if (x > 255) { throw "INVALID_CHARACTER_ERR: DOM Exception 5"; } return x; } base64.encode = function(s) { if (arguments.length != 1) { throw "SyntaxError: Not enough arguments"; } var padchar = base64.PADCHAR; var alpha = base64.ALPHA; var getbyte = base64.getbyte; var i, b10; var x = []; // convert to string s = "" + s; var imax = s.length - s.length % 3; if (s.length == 0) { return s; } for (i = 0; i < imax; i += 3) { b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2); x.push(alpha.charAt(b10 >> 18)); x.push(alpha.charAt((b10 >> 12) & 0x3F)); x.push(alpha.charAt((b10 >> 6) & 0x3f)); x.push(alpha.charAt(b10 & 0x3f)); } switch (s.length - imax) { case 1: b10 = getbyte(s,i) << 16; x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + padchar + padchar); break; case 2: b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8); x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + alpha.charAt((b10 >> 6) & 0x3f) + padchar); break; } return x.join(''); } if (!window.btoa) window.btoa = base64.encode; //if (!window.atob) window.atob = base64.decode; })(window); ```