UNPKG

node-tea

Version:

a string encrypt & decrypt package based on TEA

504 lines (451 loc) 18.3 kB
/* - - - - - - - - - - - - - - - - Tea by fiefdx- - - - - - - - - - - - - - - - - */ "use strict"; var SparkMD5 = require('./spark-md5'); var Long = require('./long'); var Tea = {}; Tea.random = function (s, e) { return Math.floor((Math.random() * e) + s); //[s, e] } Tea.teaEncrypt = function (v, k, tea_sum) { var v0 = v[0], v1 = v[1], sum = 0; var delta = 0x9e3779b9; var k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; for (var i = 0; i < tea_sum; i++) { sum += delta; v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>>5) + k1); v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>>5) + k3); } v[0] = v0 & 0x00000000ffffffff; v[1] = v1 & 0x00000000ffffffff; } Tea.teaDecrypt = function (v, k, tea_sum) { var v0 = v[0], v1 = v[1], sum = 0xc6ef3720; var delta = 0x9e3779b9; var k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; if (tea_sum == 64) { sum = 0x8DDE6E40; } for (var i = 0; i < tea_sum; i++) { v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>>5) + k3); v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>>5) + k1); sum -= delta; } v[0] = v0 & 0x00000000ffffffff; v[1] = v1 & 0x00000000ffffffff; } Tea.teaStrEncrypt = function (v, k, tea_sum) { var i_0, i_1; var v_tmp = [0, 0]; var cipertext = new Long(0x00000000, 0x00000000, true); var pre_plaintext = new Long(0x00000000, 0x00000000, true); var plaintext = new Long(0x00000000, 0x00000000, true); var encrypt_text = new Long(0x00000000, 0x00000000, true); var and_flag = new Long(0xffffffff, 0x00000000, true); var length = v.length / 2; pre_plaintext = pre_plaintext.or(and_flag.and(v[0])); pre_plaintext = pre_plaintext.shiftLeft(32); pre_plaintext = pre_plaintext.or(v[1]); v_tmp[0] = v[0]; v_tmp[1] = v[1]; Tea.teaEncrypt(v_tmp, k, tea_sum); v[0] = v_tmp[0]; v[1] = v_tmp[1]; cipertext = cipertext.or(and_flag.and(v[0])); cipertext = cipertext.shiftLeft(32); cipertext = cipertext.or(and_flag.and(v[1])); for (var i = 1; i<length; i++){ i_0 = i*2; i_1 = i_0 + 1; plaintext = plaintext.or(and_flag.and(v[i_0])); plaintext = plaintext.shiftLeft(32); plaintext = plaintext.or(and_flag.and(v[i_1])); plaintext = plaintext.xor(cipertext); v_tmp[0] = plaintext.shiftRightUnsigned(32).toUnsigned().toInt(); v_tmp[1] = plaintext.toUnsigned().toInt(); Tea.teaEncrypt(v_tmp, k, tea_sum); v[i_0] = v_tmp[0]; v[i_1] = v_tmp[1]; encrypt_text = encrypt_text.or(and_flag.and(v[i_0])); encrypt_text = encrypt_text.shiftLeft(32); encrypt_text = encrypt_text.or(and_flag.and(v[i_1])); encrypt_text = encrypt_text.xor(pre_plaintext); v[i_0] = encrypt_text.shiftRightUnsigned(32).toUnsigned().toInt(); v[i_1] = encrypt_text.toUnsigned().toInt(); cipertext = encrypt_text.toUnsigned(); pre_plaintext = plaintext.toUnsigned(); plaintext = new Long(0x00000000, 0x00000000, true); encrypt_text = new Long(0x00000000, 0x00000000, true); } } Tea.teaStrDecrypt = function (v, k, tea_sum) { var i_0, i_1; var pos = 0; var v_tmp = [0, 0]; var cipertext = new Long(0x00000000, 0x00000000, true); var cipertext_tmp = new Long(0x00000000, 0x00000000, true); var pre_plaintext = new Long(0x00000000, 0x00000000, true); var plaintext = new Long(0x00000000, 0x00000000, true); var encrypt_text = new Long(0x00000000, 0x00000000, true); var and_flag = new Long(0xffffffff, 0x00000000, true); var length = v.length / 2; cipertext = cipertext.or(and_flag.and(v[0])); cipertext = cipertext.shiftLeft(32); cipertext = cipertext.or(and_flag.and(v[1])); v_tmp[0] = v[0]; v_tmp[1] = v[1]; Tea.teaDecrypt(v_tmp, k, tea_sum); v[0] = v_tmp[0]; v[1] = v_tmp[1]; pos = v[0]; pre_plaintext = pre_plaintext.or(and_flag.and(v[0])); pre_plaintext = pre_plaintext.shiftLeft(32); pre_plaintext = pre_plaintext.or(and_flag.and(v[1])); for (var i = 1; i<length; i++){ i_0 = i*2; i_1 = i_0 + 1; cipertext_tmp = cipertext_tmp.or(and_flag.and(v[i_0])); cipertext_tmp = cipertext_tmp.shiftLeft(32); cipertext_tmp = cipertext_tmp.or(and_flag.and(v[i_1])); encrypt_text = encrypt_text.or(and_flag.and(v[i_0])); encrypt_text = encrypt_text.shiftLeft(32); encrypt_text = encrypt_text.or(and_flag.and(v[i_1])); encrypt_text = encrypt_text.xor(pre_plaintext); v[i_0] = encrypt_text.shiftRightUnsigned(32).toUnsigned().toInt(); v[i_1] = encrypt_text.toUnsigned().toInt(); v_tmp[0] = v[i_0]; v_tmp[1] = v[i_1]; Tea.teaDecrypt(v_tmp, k, tea_sum); v[i_0] = v_tmp[0]; v[i_1] = v_tmp[1]; plaintext = plaintext.or(and_flag.and(v[i_0])); plaintext = plaintext.shiftLeft(32); plaintext = plaintext.or(and_flag.and(v[i_1])); plaintext = plaintext.xor(cipertext); v[i_0] = plaintext.shiftRightUnsigned(32).toUnsigned().toInt(); v[i_1] = plaintext.toUnsigned().toInt(); pre_plaintext = plaintext.xor(cipertext); cipertext = cipertext_tmp.toUnsigned(); cipertext_tmp = new Long(0x00000000, 0x00000000, true); plaintext = new Long(0x00000000, 0x00000000, true); encrypt_text = new Long(0x00000000, 0x00000000, true); } return pos; } Tea.strBase64Encrypt = function (v, k) { // v: string, k: string var hex_k = SparkMD5.hash(SparkMD5.hash(k)); var int_k = Tea.hexStrToInts(hex_k); var s_b64 = Base64.encode(v); var int_v = Tea.composeInts(s_b64); Tea.teaStrEncrypt(int_v, int_k, 32); var int_s = Tea.intsToStr(int_v); return Base64.encode(int_s); } Tea.strBase64Decrypt = function (v, k) { // v: string base64, k: string var hex_k = SparkMD5.hash(SparkMD5.hash(k)); var int_k = Tea.hexStrToInts(hex_k); var s_str = Base64.decode(v); var int_v = Tea.strToInts(s_str); var pos = Tea.teaStrDecrypt(int_v, int_k, 32); var b64 = Tea.intsParse(int_v, pos, false); return Base64.decode(b64); } Tea.strEncrypt = function (v, k) { // v: string, k: string var hex_k = SparkMD5.hash(SparkMD5.hash(k)); var int_k = Tea.hexStrToInts(hex_k); var int_v = Tea.composeInts(v); Tea.teaStrEncrypt(int_v, int_k, 32); return Tea.intsToStr(int_v); } Tea.strDecrypt = function (v, k) { // v: string, k: string var hex_k = SparkMD5.hash(SparkMD5.hash(k)); var int_k = Tea.hexStrToInts(hex_k); var int_v = Tea.strToInts(v); var pos = Tea.teaStrDecrypt(int_v, int_k, 32); return Tea.intsParse(int_v, pos, false); } Tea.bytesEncrypt = function (v, k) { // v: bytes array, k: string var hex_k = SparkMD5.hash(SparkMD5.hash(k)); var int_k = Tea.hexStrToInts(hex_k); var int_v = Tea.composeInts(v); Tea.teaStrEncrypt(int_v, int_k, 32); return Tea.intsToBytes(int_v); } Tea.bytesDecrypt = function (v, k) { // v: bytes array, k: string var hex_k = SparkMD5.hash(SparkMD5.hash(k)); var int_k = Tea.hexStrToInts(hex_k); var int_v = Tea.bytesToInts(v); var pos = Tea.teaStrDecrypt(int_v, int_k, 32); return Tea.intsParse(int_v, pos, true); } Tea.strToBytes = function (s) { var bytes = []; for (var i = 0; i < s.length; i++) { bytes.push(s.charCodeAt(i)); } return bytes; } Tea.bytesToStr = function (b) { var result = ""; for(var i = 0; i < b.length; i++) { result += (String.fromCharCode(b[i])); } return result; } Tea.bytesToInts = function (b) { var r = new Array(b.length / 4); for (var i = 0; i < (b.length / 4); i++) { r[i] = (((b[i*4] & 0x000000ff) << 24)| ((b[i*4+1] & 0x000000ff) << 16)| ((b[i*4+2] & 0x000000ff) << 8)| (b[i*4+3] & 0x000000ff)); } return r; } Tea.intsToBytes = function (i) { var b = new Array(i.length * 4); for (var j = 0; j < i.length; j++) { b[j*4+3] = i[j] & 0x000000ff; b[j*4+2] = (i[j] >>> 8) & 0x000000ff; b[j*4+1] = (i[j] >>> 16) & 0x000000ff; b[j*4] = (i[j] >>> 24) & 0x000000ff; } return b; } Tea.strToInts = function (s) { var b = Tea.strToBytes(s); var r = new Array(b.length / 4); for (var i = 0; i < (b.length / 4); i++) { r[i] = (((b[i*4] & 0x000000ff) << 24)| ((b[i*4+1] & 0x000000ff) << 16)| ((b[i*4+2] & 0x000000ff) << 8)| (b[i*4+3] & 0x000000ff)); } return r; } Tea.intsToStr = function (i) { var b = new Array(i.length * 4); var r = ""; for (var j = 0; j < i.length; j++) { b[j*4+3] = i[j] & 0x000000ff; b[j*4+2] = (i[j] >>> 8) & 0x000000ff; b[j*4+1] = (i[j] >>> 16) & 0x000000ff; b[j*4] = (i[j] >>> 24) & 0x000000ff; } r = Tea.bytesToStr(b); return r; } Tea.composeInts = function (s) { var b; if (s instanceof Array) { // bytes array b = s; } else { // string b = Tea.strToBytes(s); } var fill_n = (8 - (b.length + 2)) % 8; if (fill_n < 0) { fill_n = 8 + fill_n + 2; } else { fill_n += 2; } var fill_b = new Array(fill_n); for(var i = 0; i < fill_n; i++) { fill_b[i] = Tea.random(0, 0x7f); } var b_after_fill = new Array(); b_after_fill.push(((fill_n - 2)|0xf8)); b_after_fill = b_after_fill.concat(fill_b); b_after_fill = b_after_fill.concat(b); b_after_fill = b_after_fill.concat([0, 0, 0, 0, 0, 0, 0]); var r = new Array(b_after_fill.length / 4); for (var i = 0; i < (b_after_fill.length / 4); i++) { r[i] = (b_after_fill[i*4+3]| (b_after_fill[i*4+2] << 8)| (b_after_fill[i*4+1] << 16)| (b_after_fill[i*4] << 24)); } return r; } Tea.intsParse = function (i, pos, array) { var b_after_fill = new Array(i.length * 4); var r; for (var j = 0; j < i.length; j++) { b_after_fill[j*4+3] = i[j] & 0x000000ff; b_after_fill[j*4+2] = (i[j] >>> 8) & 0x000000ff; b_after_fill[j*4+1] = (i[j] >>> 16) & 0x000000ff; b_after_fill[j*4] = (i[j] >>> 24) & 0x000000ff; } pos = (((pos >>> 24) & 0x000000ff) & 0x07) + 2; if (b_after_fill.slice(b_after_fill.length - 7, b_after_fill.length).join("") != "0000000") { if (array) { r = []; } else { r = ""; } } else { if (array) { r = b_after_fill.slice(pos + 1, b_after_fill.length - 7) } else { r = Tea.bytesToStr(b_after_fill.slice(pos + 1, b_after_fill.length - 7)); } } return r; } Tea.hexStrToInts = function (h) { var r = []; while (h.length >= 8) { r.push(parseInt(h.substring(0, 8), 16)); h = h.substring(8, h.length); } return r; } Tea.strToLongs = function(s) { // convert string to array of longs, each containing 4 chars // note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long var l = new Array(Math.ceil(s.length/4)); for (var i=0; i<l.length; i++) { // note little-endian encoding - endianness is irrelevant as long as // it is the same in longsToStr() l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)<<8) + (s.charCodeAt(i*4+2)<<16) + (s.charCodeAt(i*4+3)<<24); } return l; // note running off the end of the string generates nulls since } // bitwise operators treat NaN as 0 Tea.longsToStr = function(l) { // convert array of longs back to string var a = new Array(l.length); for (var i=0; i<l.length; i++) { a[i] = String.fromCharCode(l[i] & 0xFF, l[i]>>>8 & 0xFF, l[i]>>>16 & 0xFF, l[i]>>>24 & 0xFF); } return a.join(''); // use Array.join() rather than repeated string appends for efficiency in IE } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Base64 class: Base 64 encoding / decoding (c) Chris Veness 2002-2012 */ /* note: depends on Utf8 class */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var Base64 = {}; // Base64 namespace Base64.code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; /** * Encode string into Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648] * (instance method extending String object). As per RFC 4648, no newlines are added. * * @param {String} str The string to be encoded as base-64 * @param {Boolean} [utf8encode=false] Flag to indicate whether str is Unicode string to be encoded * to UTF8 before conversion to base64; otherwise string is assumed to be 8-bit characters * @returns {String} Base64-encoded string */ Base64.encode = function(str, utf8encode) { // http://tools.ietf.org/html/rfc4648 utf8encode = (typeof utf8encode == 'undefined') ? false : utf8encode; var o1, o2, o3, bits, h1, h2, h3, h4, e=[], pad = '', c, plain, coded; var b64 = Base64.code; plain = utf8encode ? Utf8.encode(str) : str; c = plain.length % 3; // pad string to length of multiple of 3 if (c > 0) { while (c++ < 3) { pad += '='; plain += '\0'; } } // note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars for (c=0; c<plain.length; c+=3) { // pack three octets into four hexets o1 = plain.charCodeAt(c); o2 = plain.charCodeAt(c+1); o3 = plain.charCodeAt(c+2); bits = o1<<16 | o2<<8 | o3; h1 = bits>>18 & 0x3f; h2 = bits>>12 & 0x3f; h3 = bits>>6 & 0x3f; h4 = bits & 0x3f; // use hextets to index into code string e[c/3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } coded = e.join(''); // join() is far faster than repeated string concatenation in IE // replace 'A's from padded nulls with '='s coded = coded.slice(0, coded.length-pad.length) + pad; return coded; } /** * Decode string from Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648] * (instance method extending String object). As per RFC 4648, newlines are not catered for. * * @param {String} str The string to be decoded from base-64 * @param {Boolean} [utf8decode=false] Flag to indicate whether str is Unicode string to be decoded * from UTF8 after conversion from base64 * @returns {String} decoded string */ Base64.decode = function(str, utf8decode) { utf8decode = (typeof utf8decode == 'undefined') ? false : utf8decode; var o1, o2, o3, h1, h2, h3, h4, bits, d=[], plain, coded; var b64 = Base64.code; coded = utf8decode ? Utf8.decode(str) : str; for (var c=0; c<coded.length; c+=4) { // unpack four hexets into three octets h1 = b64.indexOf(coded.charAt(c)); h2 = b64.indexOf(coded.charAt(c+1)); h3 = b64.indexOf(coded.charAt(c+2)); h4 = b64.indexOf(coded.charAt(c+3)); bits = h1<<18 | h2<<12 | h3<<6 | h4; o1 = bits>>>16 & 0xff; o2 = bits>>>8 & 0xff; o3 = bits & 0xff; d[c/4] = String.fromCharCode(o1, o2, o3); // check for padding if (h4 == 0x40) d[c/4] = String.fromCharCode(o1, o2); if (h3 == 0x40) d[c/4] = String.fromCharCode(o1); } plain = d.join(''); // join() is far faster than repeated string concatenation in IE return utf8decode ? Utf8.decode(plain) : plain; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Utf8 class: encode / decode between multi-byte Unicode characters and UTF-8 multiple */ /* single-byte character encoding (c) Chris Veness 2002-2012 */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var Utf8 = {}; // Utf8 namespace /** * Encode multi-byte Unicode string into utf-8 multiple single-byte characters * (BMP / basic multilingual plane only) * * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars * * @param {String} strUni Unicode string to be encoded as UTF-8 * @returns {String} encoded string */ Utf8.encode = function(strUni) { // use regular expressions & String.replace callback function for better efficiency // than procedural approaches var strUtf = strUni.replace( /[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } ); strUtf = strUtf.replace( /[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } ); return strUtf; } /** * Decode utf-8 encoded string back into multi-byte Unicode characters * * @param {String} strUtf UTF-8 string to be decoded back to Unicode * @returns {String} decoded string */ Utf8.decode = function(strUtf) { // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char! var strUni = strUtf.replace( /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars function(c) { // (note parentheses for precence) var cc = ((c.charCodeAt(0)&0x0f)<<12) | ((c.charCodeAt(1)&0x3f)<<6) | ( c.charCodeAt(2)&0x3f); return String.fromCharCode(cc); } ); strUni = strUni.replace( /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars function(c) { // (note parentheses for precence) var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; return String.fromCharCode(cc); } ); return strUni; } exports.encryptBase64 = Tea.strBase64Encrypt exports.decryptBase64 = Tea.strBase64Decrypt exports.encrypt = Tea.strEncrypt exports.decrypt = Tea.strDecrypt exports.encryptBytes = Tea.bytesEncrypt exports.decryptBytes = Tea.bytesDecrypt exports.strToBytes = Tea.strToBytes exports.bytesToStr = Tea.bytesToStr exports.encodeUtf8 = Utf8.encode exports.decodeUtf8 = Utf8.decode