/*! * Webogram v0.2.1 - messaging web application for MTProto * https://github.com/zhukov/webogram * Copyright (C) 2014 Igor Zhukov * 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, bytes = []; for (i = 0; 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 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) { var len = bytes.length, words = []; for (var 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(); 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 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 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 sha1Hash (bytes) { // console.log('SHA-1 hash start'); var hashBytes = sha1.hash(bytes, true); // console.log('SHA-1 hash finish'); return hashBytes; } function rsaEncrypt (publicKey, bytes) { var needPadding = 255 - bytes.length; if (needPadding > 0) { var padding = new Array(needPadding); (new SecureRandom()).nextBytes(padding); bytes = bytes.concat(padding); } // 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 aesEncrypt (bytes, keyBytes, ivBytes) { // console.log('AES encrypt start', bytes.length/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/); var needPadding = 16 - (bytes.length % 16); if (needPadding > 0 && needPadding < 16) { var padding = new Array(needPadding); (new SecureRandom()).nextBytes(padding); bytes = bytes.concat(padding); } 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('AES encrypt finish'); return encryptedBytes; } function aesDecrypt (encryptedBytes, keyBytes, ivBytes) { // console.log('AES decrypt start', encryptedBytes.length/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/); 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('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('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('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(x, 64), yBigInt = str2bigInt(y, 64), mBigInt = str2bigInt(bytesToHex(m), 16, 2), resBigInt = powMod(xBigInt, yBigInt, mBigInt); return bytesFromHex(bigInt2str(resBigInt, 16)); } catch (e) {} return bytesFromBigInt(new BigInteger(x).modPow(new BigInteger(y), new BigInteger(m))); }