Telegram Web, preconfigured for usage in I2P. http://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

668 lines
15 KiB

/*!
* Webogram v0.5.1 - messaging web application for MTProto
* https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
function bigint (num) {
return new BigInteger(num.toString(16), 16);
}
function bigStringInt (strNum) {
return new BigInteger(strNum, 10);
}
function dHexDump (bytes) {
var arr = [];
for (var i = 0; i < bytes.length; i++) {
if (i && !(i % 2)) {
if (!(i % 16)) {
arr.push("\n");
} else if (!(i % 4)) {
arr.push(' ');
} else {
arr.push(' ');
}
}
arr.push((bytes[i] < 16 ? '0' : '') + bytes[i].toString(16));
}
console.log(arr.join(''));
}
function bytesToHex (bytes) {
bytes = bytes || [];
var arr = [];
for (var i = 0; i < bytes.length; i++) {
arr.push((bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16));
}
return arr.join('');
}
function bytesFromHex (hexString) {
var len = hexString.length,
i,
start = 0,
bytes = [];
if (hexString.length % 2) {
bytes.push(parseInt(hexString.charAt(0), 16));
start++;
}
for (i = start; i < len; i += 2) {
bytes.push(parseInt(hexString.substr(i, 2), 16));
}
return bytes;
}
function bytesToBase64 (bytes) {
var mod3, result = '';
for (var nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
mod3 = nIdx % 3;
nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24);
if (mod3 === 2 || nLen - nIdx === 1) {
result += String.fromCharCode(
uint6ToBase64(nUint24 >>> 18 & 63),
uint6ToBase64(nUint24 >>> 12 & 63),
uint6ToBase64(nUint24 >>> 6 & 63),
uint6ToBase64(nUint24 & 63)
);
nUint24 = 0;
}
}
return result.replace(/A(?=A$|$)/g, '=');
}
function uint6ToBase64 (nUint6) {
return nUint6 < 26
? nUint6 + 65
: nUint6 < 52
? nUint6 + 71
: nUint6 < 62
? nUint6 - 4
: nUint6 === 62
? 43
: nUint6 === 63
? 47
: 65;
}
function base64ToBlob(base64str, mimeType) {
var sliceSize = 1024;
var byteCharacters = atob(base64str);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0 ; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return blobConstruct(byteArrays, mimeType);
}
function dataUrlToBlob(url) {
// var name = 'b64blob ' + url.length;
// console.time(name);
var urlParts = url.split(',');
var base64str = urlParts[1];
var mimeType = urlParts[0].split(':')[1].split(';')[0];
var blob = base64ToBlob(base64str, mimeType);
// console.timeEnd(name);
return blob;
}
function blobConstruct (blobParts, mimeType) {
var blob;
try {
blob = new Blob(blobParts, {type: mimeType});
} catch (e) {
var bb = new BlobBuilder;
angular.forEach(blobParts, function(blobPart) {
bb.append(blobPart);
});
blob = bb.getBlob(mimeType);
}
return blob;
}
function bytesCmp (bytes1, bytes2) {
var len = bytes1.length;
if (len != bytes2.length) {
return false;
}
for (var i = 0; i < len; i++) {
if (bytes1[i] != bytes2[i]) {
return false;
}
}
return true;
}
function bytesXor (bytes1, bytes2) {
var len = bytes1.length,
bytes = [];
for (var i = 0; i < len; ++i) {
bytes[i] = bytes1[i] ^ bytes2[i];
}
return bytes;
}
function bytesToWords (bytes) {
if (bytes instanceof ArrayBuffer) {
bytes = new Uint8Array(bytes);
}
var len = bytes.length,
words = [], i;
for (i = 0; i < len; i++) {
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
}
return new CryptoJS.lib.WordArray.init(words, len);
}
function bytesFromWords (wordArray) {
var words = wordArray.words,
sigBytes = wordArray.sigBytes,
bytes = [];
for (var i = 0; i < sigBytes; i++) {
bytes.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
}
return bytes;
}
function bytesFromBigInt (bigInt, len) {
var bytes = bigInt.toByteArray();
if (len && bytes.length < len) {
var padding = [];
for (var i = 0, needPadding = len - bytes.length; i < needPadding; i++) {
padding[i] = 0;
}
if (bytes instanceof ArrayBuffer) {
bytes = bufferConcat(padding, bytes);
} else {
bytes = padding.concat(bytes);
}
}
else {
while (!bytes[0] && (!len || bytes.length > len)) {
bytes = bytes.slice(1);
}
}
return bytes;
}
function bytesFromLeemonBigInt (bigInt, len) {
var str = bigInt2str(bigInt, 16);
return bytesFromHex(str);
}
function bytesToArrayBuffer (b) {
return (new Uint8Array(b)).buffer;
}
function convertToArrayBuffer(bytes) {
// Be careful with converting subarrays!!
if (bytes instanceof ArrayBuffer) {
return bytes;
}
if (bytes.buffer !== undefined &&
bytes.buffer.byteLength == bytes.length * bytes.BYTES_PER_ELEMENT) {
return bytes.buffer;
}
return bytesToArrayBuffer(bytes);
}
function convertToUint8Array(bytes) {
if (bytes.buffer !== undefined) {
return bytes;
}
return new Uint8Array(bytes);
}
function convertToByteArray(bytes) {
if (Array.isArray(bytes)) {
return bytes;
}
bytes = convertToUint8Array(bytes);
var newBytes = [];
for (var i = 0, len = bytes.length; i < len; i++) {
newBytes.push(bytes[i]);
}
return newBytes;
}
function bytesFromArrayBuffer (buffer) {
var len = buffer.byteLength,
byteView = new Uint8Array(buffer),
bytes = [];
for (var i = 0; i < len; ++i) {
bytes[i] = byteView[i];
}
return bytes;
}
function bufferConcat(buffer1, buffer2) {
var l1 = buffer1.byteLength || buffer1.length,
l2 = buffer2.byteLength || buffer2.length;
var tmp = new Uint8Array(l1 + l2);
tmp.set(buffer1 instanceof ArrayBuffer ? new Uint8Array(buffer1) : buffer1, 0);
tmp.set(buffer2 instanceof ArrayBuffer ? new Uint8Array(buffer2) : buffer2, l1);
return tmp.buffer;
}
function longToInts (sLong) {
var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000));
return [divRem[0].intValue(), divRem[1].intValue()];
}
function longToBytes (sLong) {
return bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse();
}
function longFromInts (high, low) {
return bigint(high).shiftLeft(32).add(bigint(low)).toString(10);
}
function intToUint (val) {
val = parseInt(val);
if (val < 0) {
val = val + 4294967296;
}
return val;
}
function uintToInt (val) {
if (val > 2147483647) {
val = val - 4294967296;
}
return val;
}
function sha1HashSync (bytes) {
this.rushaInstance = this.rushaInstance || new Rusha(1024 * 1024);
// console.log(dT(), 'SHA-1 hash start', bytes.byteLength || bytes.length);
var hashBytes = rushaInstance.rawDigest(bytes).buffer;
// console.log(dT(), 'SHA-1 hash finish');
return hashBytes;
}
function sha1BytesSync (bytes) {
return bytesFromArrayBuffer(sha1HashSync(bytes));
}
function sha256HashSync (bytes) {
// console.log(dT(), 'SHA-2 hash start', bytes.byteLength || bytes.length);
var hashWords = CryptoJS.SHA256(bytesToWords(bytes));
// console.log(dT(), 'SHA-2 hash finish');
var hashBytes = bytesFromWords(hashWords);
return hashBytes;
}
function rsaEncrypt (publicKey, bytes) {
bytes = addPadding(bytes, 255);
// console.log('RSA encrypt start');
var N = new BigInteger(publicKey.modulus, 16),
E = new BigInteger(publicKey.exponent, 16),
X = new BigInteger(bytes),
encryptedBigInt = X.modPowInt(E, N),
encryptedBytes = bytesFromBigInt(encryptedBigInt, 256);
// console.log('RSA encrypt finish');
return encryptedBytes;
}
function addPadding(bytes, blockSize, zeroes) {
blockSize = blockSize || 16;
var len = bytes.byteLength || bytes.length;
var needPadding = blockSize - (len % blockSize);
if (needPadding > 0 && needPadding < blockSize) {
var padding = new Array(needPadding);
if (zeroes) {
for (var i = 0; i < needPadding; i++) {
padding[i] = 0
}
} else {
(new SecureRandom()).nextBytes(padding);
}
if (bytes instanceof ArrayBuffer) {
bytes = bufferConcat(bytes, padding);
} else {
bytes = bytes.concat(padding);
}
}
return bytes;
}
function aesEncryptSync (bytes, keyBytes, ivBytes) {
var len = bytes.byteLength || bytes.length;
// console.log(dT(), 'AES encrypt start', len/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/);
bytes = addPadding(bytes);
var encryptedWords = CryptoJS.AES.encrypt(bytesToWords(bytes), bytesToWords(keyBytes), {
iv: bytesToWords(ivBytes),
padding: CryptoJS.pad.NoPadding,
mode: CryptoJS.mode.IGE
}).ciphertext;
var encryptedBytes = bytesFromWords(encryptedWords);
// console.log(dT(), 'AES encrypt finish');
return encryptedBytes;
}
function aesDecryptSync (encryptedBytes, keyBytes, ivBytes) {
// console.log(dT(), 'AES decrypt start', encryptedBytes.length);
var decryptedWords = CryptoJS.AES.decrypt({ciphertext: bytesToWords(encryptedBytes)}, bytesToWords(keyBytes), {
iv: bytesToWords(ivBytes),
padding: CryptoJS.pad.NoPadding,
mode: CryptoJS.mode.IGE
});
var bytes = bytesFromWords(decryptedWords);
// console.log(dT(), 'AES decrypt finish');
return bytes;
}
function gzipUncompress (bytes) {
// console.log('Gzip uncompress start');
var result = (new Zlib.Gunzip(bytes)).decompress();
// console.log('Gzip uncompress finish');
return result;
}
function nextRandomInt (maxValue) {
return Math.floor(Math.random() * maxValue);
};
function pqPrimeFactorization (pqBytes) {
var what = new BigInteger(pqBytes),
result = false;
// console.log(dT(), 'PQ start', pqBytes, what.toString(16), what.bitLength());
try {
result = pqPrimeLeemon(str2bigInt(what.toString(16), 16, Math.ceil(64 / bpe) + 1))
} catch (e) {
console.error('Pq leemon Exception', e);
}
if (result === false && what.bitLength() <= 64) {
// console.time('PQ long');
try {
result = pqPrimeLong(goog.math.Long.fromString(what.toString(16), 16));
} catch (e) {
console.error('Pq long Exception', e);
};
// console.timeEnd('PQ long');
}
// console.log(result);
if (result === false) {
// console.time('pq BigInt');
result = pqPrimeBigInteger(what);
// console.timeEnd('pq BigInt');
}
// console.log(dT(), 'PQ finish');
return result;
}
function pqPrimeBigInteger (what) {
var it = 0,
g;
for (var i = 0; i < 3; i++) {
var q = (nextRandomInt(128) & 15) + 17,
x = bigint(nextRandomInt(1000000000) + 1),
y = x.clone(),
lim = 1 << (i + 18);
for (var j = 1; j < lim; j++) {
++it;
var a = x.clone(),
b = x.clone(),
c = bigint(q);
while (!b.equals(BigInteger.ZERO)) {
if (!b.and(BigInteger.ONE).equals(BigInteger.ZERO)) {
c = c.add(a);
if (c.compareTo(what) > 0) {
c = c.subtract(what);
}
}
a = a.add(a);
if (a.compareTo(what) > 0) {
a = a.subtract(what);
}
b = b.shiftRight(1);
}
x = c.clone();
var z = x.compareTo(y) < 0 ? y.subtract(x) : x.subtract(y);
g = z.gcd(what);
if (!g.equals(BigInteger.ONE)) {
break;
}
if ((j & (j - 1)) == 0) {
y = x.clone();
}
}
if (g.compareTo(BigInteger.ONE) > 0) {
break;
}
}
var f = what.divide(g), P, Q;
if (g.compareTo(f) > 0) {
P = f;
Q = g;
} else {
P = g;
Q = f;
}
return [bytesFromBigInt(P), bytesFromBigInt(Q), it];
}
function gcdLong(a, b) {
while (a.notEquals(goog.math.Long.ZERO) && b.notEquals(goog.math.Long.ZERO)) {
while (b.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) {
b = b.shiftRight(1);
}
while (a.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) {
a = a.shiftRight(1);
}
if (a.compare(b) > 0) {
a = a.subtract(b);
} else {
b = b.subtract(a);
}
}
return b.equals(goog.math.Long.ZERO) ? a : b;
}
function pqPrimeLong(what) {
var it = 0,
g;
for (var i = 0; i < 3; i++) {
var q = goog.math.Long.fromInt((nextRandomInt(128) & 15) + 17),
x = goog.math.Long.fromInt(nextRandomInt(1000000000) + 1),
y = x,
lim = 1 << (i + 18);
for (var j = 1; j < lim; j++) {
++it;
var a = x,
b = x,
c = q;
while (b.notEquals(goog.math.Long.ZERO)) {
if (b.and(goog.math.Long.ONE).notEquals(goog.math.Long.ZERO)) {
c = c.add(a);
if (c.compare(what) > 0) {
c = c.subtract(what);
}
}
a = a.add(a);
if (a.compare(what) > 0) {
a = a.subtract(what);
}
b = b.shiftRight(1);
}
x = c;
var z = x.compare(y) < 0 ? y.subtract(x) : x.subtract(y);
g = gcdLong(z, what);
if (g.notEquals(goog.math.Long.ONE)) {
break;
}
if ((j & (j - 1)) == 0) {
y = x;
}
}
if (g.compare(goog.math.Long.ONE) > 0) {
break;
}
}
var f = what.div(g), P, Q;
if (g.compare(f) > 0) {
P = f;
Q = g;
} else {
P = g;
Q = f;
}
return [bytesFromHex(P.toString(16)), bytesFromHex(Q.toString(16)), it];
}
function pqPrimeLeemon (what) {
var minBits = 64,
minLen = Math.ceil(minBits / bpe) + 1,
it = 0, i, q, j, lim, g, P, Q,
a = new Array(minLen),
b = new Array(minLen),
c = new Array(minLen),
g = new Array(minLen),
z = new Array(minLen),
x = new Array(minLen),
y = new Array(minLen);
for (i = 0; i < 3; i++) {
q = (nextRandomInt(128) & 15) + 17;
copyInt_(x, nextRandomInt(1000000000) + 1);
copy_(y, x);
lim = 1 << (i + 18);
for (j = 1; j < lim; j++) {
++it;
copy_(a, x);
copy_(b, x);
copyInt_(c, q);
while (!isZero(b)) {
if (b[0] & 1) {
add_(c, a);
if (greater(c, what)) {
sub_(c, what);
}
}
add_(a, a);
if (greater(a, what)) {
sub_(a, what);
}
rightShift_(b, 1);
}
copy_(x, c);
if (greater(x,y)) {
copy_(z, x);
sub_(z, y);
} else {
copy_(z, y);
sub_(z, x);
}
eGCD_(z, what, g, a, b);
if (!equalsInt(g, 1)) {
break;
}
if ((j & (j - 1)) == 0) {
copy_(y, x);
}
}
if (greater(g, one)) {
break;
}
}
divide_(what, g, x, y);
if (greater(g, x)) {
P = x;
Q = g;
} else {
P = g;
Q = x;
}
// console.log(dT(), 'done', bigInt2str(what, 10), bigInt2str(P, 10), bigInt2str(Q, 10));
return [bytesFromLeemonBigInt(P), bytesFromLeemonBigInt(Q), it];
}
function bytesModPow (x, y, m) {
try {
var xBigInt = str2bigInt(bytesToHex(x), 16),
yBigInt = str2bigInt(bytesToHex(y), 16),
mBigInt = str2bigInt(bytesToHex(m), 16),
resBigInt = powMod(xBigInt, yBigInt, mBigInt);
return bytesFromHex(bigInt2str(resBigInt, 16));
} catch (e) {
console.error('mod pow error', e);
}
return bytesFromBigInt(new BigInteger(x).modPow(new BigInteger(y), new BigInteger(m)), 256);
}