|
|
|
/*!
|
|
|
|
* Webogram v0.2 - 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,
|
|
|
|
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)));
|
|
|
|
}
|