(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<offset+len; i++) {
ret <<= 8;
ret |= data[i];
}
return (ret & 0xffffffff) >>> 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<data.length; i++) {
var hex = Number(data[i]).toString(16);
if (hex.length == 1)
hex = "0" + hex;
outInHex += hex;
}
return outInHex;
}
function __bytesToStr(data) {
var str = "";
for (var i=0; i<data.length; i+=2) // 输入的16进制字符串
str += String.fromCharCode(parseInt(data.substr(i, 2), 16));
return str;
}
function __strToBytes(str, unicode) {
if (!str) return "";
if (unicode) str = utf16ToUtf8(str);
var data = [];
for (var i=0; i<str.length; i++)
data[i] = str.charCodeAt(i);
return __bytesInStr(data);
}
//UTF-16转UTF-8
function utf16ToUtf8(s){
var i, code, ret = [], len = s.length;
for(i = 0; i < len; i++){
code = s.charCodeAt(i);
if(code > 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<m.length; i++)
m[i] = 0;
__out = new Array(count);
__preCryptPos = 0;
__cryptPos = 8; // 头部已经解密过,所以是8
__pos++; // 与解密过程对应,+1
/* 开始跳过头部,如果在这个过程中满了8字节,则解密下一块
因为是解密下一块,所以我们有一个语句 m = data,下一块当然有preCrypt了,我们不再用m了
但是如果不满8,这说明了什么?说明了头8个字节的密文是包含了明文信息的,当然还是要用m把明文弄出来
所以,很显然,满了8的话,说明了头8个字节的密文除了一个长度信息有用之外,其他都是无用的填充*/
var padding = 1;
while (padding <= 2) {
if (__pos < 8) {
__pos++;
padding++;
}
if (__pos == 8) {
m = data;
if (!__decrypt8Bytes())
return null;
}
}
/* 这里是解密的重要阶段,这个时候头部的填充都已经跳过了,开始解密
注意如果上面一个while没有满8,这里第一个if里面用的就是原始的m,否则这个m就是data了*/
var i=0;
while (count != 0) {
if (__pos < 8) {
__out[i] = (m[__preCryptPos + __pos] ^ __prePlain[__pos]) & 0xff;
i++;
count--;
__pos++;
}
if (__pos == 8) {
m = data;
__preCryptPos = __cryptPos - 8;
if (!__decrypt8Bytes())
return null;
}
}
/*
明文已经解密完毕了,到这里剩下的只有尾部的填充,应该全是0,如果解密后非0,即出错了,返回null
*/
for (padding=1; padding<8; padding++) {
if (__pos < 8) {
if ((m[__preCryptPos + __pos] ^ __prePlain[__pos]) != 0)
return null;
__pos++;
}
if (__pos == 8) {
m = data;
__preCryptPos = __cryptPos;
if (!__decrypt8Bytes())
return null;
}
}
return __out;
}
function __encrypt8bytes() {
for (var i=0; i<8; i++) {
if (__header)
__plain[i] ^= __prePlain[i];
else
__plain[i] ^= __out[__preCryptPos + i];
}
var crypted = __encipher(__plain);
for (var i=0; i<8; i++) {
__out[__cryptPos+i] = crypted[i] ^ __prePlain[i];
__prePlain[i] = __plain[i];
}
__preCryptPos = __cryptPos;
__cryptPos += 8;
__pos = 0;
__header = false;
}
function __encipher(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 = 0;
var delta = 0x9E3779B9 >>> 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<str.length; i++)
data[i] = str.charCodeAt(i) & 0xff;
} else {
var k = 0;
for (var i=0; i<str.length; i+=2) // 输入的16进制字符串
data[k++] = parseInt(str.substr(i, 2), 16);
}
return data;
}
global.TEA = {
encrypt: function(str, isASCII) {
var data = __dataFromStr(str, isASCII);
var encrypted = __encrypt(data);
return __bytesInStr(encrypted);
},
enAsBase64: function(str, isASCII) { // output base64 encoded
var data = __dataFromStr(str, isASCII);
var encrypted = __encrypt(data);
var bytes = "";
for (var i=0; i<encrypted.length; i++)
bytes += String.fromCharCode(encrypted[i]);
return btoa(bytes);
},
decrypt: function(str) {
var data = __dataFromStr(str, false);
var decrypted = __decrypt(data);
return __bytesInStr(decrypted);
},
initkey: function(key, isASCII) {
__key = __dataFromStr(key, isASCII);
},
bytesToStr: __bytesToStr,
strToBytes: __strToBytes,
bytesInStr: __bytesInStr,
dataFromStr: __dataFromStr
};
/**
* base64 兼容window.btoa window.atob
* if (!window.btoa) window.btoa = base64.encode
* if (!window.atob) window.atob = base64.decode
*/
var base64 = {};
base64.PADCHAR = '=';
base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
/*
base64.getbyte64 = function(s,i) {
// This is oddly fast, except on Chrome/V8.
// Minimal or no improvement in performance by using a
// object with properties mapping chars to value (eg. 'A': 0)
var idx = base64.ALPHA.indexOf(s.charAt(i));
if (idx == -1) {
throw "Cannot decode base64";
}
return idx;
}
base64.decode = function(s) {
// convert to string
s = "" + s;
var getbyte64 = base64.getbyte64;
var pads, i, b10;
var imax = s.length
if (imax == 0) {
return s;
}
if (imax % 4 != 0) {
throw "Cannot decode base64";
}
pads = 0
if (s.charAt(imax -1) == base64.PADCHAR) {
pads = 1;
if (s.charAt(imax -2) == base64.PADCHAR) {
pads = 2;
}
// either way, we want to ignore this last block
imax -= 4;
}
var x = [];
for (i = 0; i < imax; i += 4) {
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
(getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
x.push(String.fromCharCode(b10 >> 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);