Apply new server time offset on update

This commit is contained in:
Eduard Kuzmenko 2021-06-28 00:58:53 +03:00
parent 3c764cc0a2
commit bb632f4208
5 changed files with 118 additions and 77 deletions

View File

@ -20,6 +20,7 @@ import appUsersManager from "./appUsersManager";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import appPeersManager from "./appPeersManager"; import appPeersManager from "./appPeersManager";
import appStateManager from './appStateManager'; import appStateManager from './appStateManager';
import serverTimeManager from '../mtproto/serverTimeManager';
type UpdatesState = { type UpdatesState = {
pendingPtsUpdates: {pts: number, pts_count: number}[], pendingPtsUpdates: {pts: number, pts_count: number}[],
@ -302,8 +303,8 @@ export class ApiUpdatesManager {
updatesState.date = nextState.date; updatesState.date = nextState.date;
} else { } else {
updatesState.pts = differenceResult.pts; updatesState.pts = differenceResult.pts;
updatesState.date = (Date.now() / 1000 | 0) + serverTimeManager.serverTimeOffset;
delete updatesState.seq; delete updatesState.seq;
delete updatesState.date;
this.channelStates = {}; this.channelStates = {};

View File

@ -20,6 +20,7 @@ import { logger, LogTypes } from "../logger";
import { bytesCmp, bytesToHex, bytesFromHex, bytesXor } from "../../helpers/bytes"; import { bytesCmp, bytesToHex, bytesFromHex, bytesXor } from "../../helpers/bytes";
import DEBUG from "../../config/debug"; import DEBUG from "../../config/debug";
import { cmp, int2bigInt, one, pow, str2bigInt, sub } from "../../vendor/leemon"; import { cmp, int2bigInt, one, pow, str2bigInt, sub } from "../../vendor/leemon";
import { Awaited } from "../../types";
/* let fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse(); /* let fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse();
let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse(); let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse();
@ -91,11 +92,11 @@ export class Authorizer {
resultArray.set(headerArray); resultArray.set(headerArray);
resultArray.set(requestArray, headerArray.length); resultArray.set(requestArray, headerArray.length);
/* var headerBuffer = header.getBuffer(), /* const headerBuffer = header.getBuffer(),
headerArray = new Int32Array(headerBuffer); headerArray = new Int32Array(headerBuffer);
var headerLength = headerBuffer.byteLength; const headerLength = headerBuffer.byteLength;
var resultBuffer = new ArrayBuffer(headerLength + requestLength), const resultBuffer = new ArrayBuffer(headerLength + requestLength),
resultArray = new Int32Array(resultBuffer); resultArray = new Int32Array(resultBuffer);
resultArray.set(headerArray); resultArray.set(headerArray);
@ -155,7 +156,7 @@ export class Authorizer {
} }
public async mtpSendReqPQ(auth: AuthOptions) { public async mtpSendReqPQ(auth: AuthOptions) {
var request = new TLSerialization({mtproto: true}); const request = new TLSerialization({mtproto: true});
request.storeMethod('req_pq_multi', {nonce: auth.nonce}); request.storeMethod('req_pq_multi', {nonce: auth.nonce});
@ -165,14 +166,16 @@ export class Authorizer {
if(DEBUG) { if(DEBUG) {
this.log('Send req_pq', auth.nonce.hex); this.log('Send req_pq', auth.nonce.hex);
} }
let deserializer: TLDeserialization;
try { try {
var deserializer = await this.mtpSendPlainRequest(auth.dcId, request.getBytes(true)); deserializer = await this.mtpSendPlainRequest(auth.dcId, request.getBytes(true));
} catch(error) { } catch(error) {
this.log.error('req_pq error', error.message); this.log.error('req_pq error', error.message);
throw error; throw error;
} }
var response = deserializer.fetchObject('ResPQ'); const response = deserializer.fetchObject('ResPQ');
if(response._ !== 'resPQ') { if(response._ !== 'resPQ') {
throw new Error('[MT] resPQ response invalid: ' + response._); throw new Error('[MT] resPQ response invalid: ' + response._);
@ -192,7 +195,7 @@ export class Authorizer {
this.log('Got ResPQ', bytesToHex(auth.serverNonce), bytesToHex(auth.pq), auth.fingerprints); this.log('Got ResPQ', bytesToHex(auth.serverNonce), bytesToHex(auth.pq), auth.fingerprints);
} }
let publicKey = await rsaKeysManager.select(auth.fingerprints); const publicKey = await rsaKeysManager.select(auth.fingerprints);
if(!publicKey) { if(!publicKey) {
throw new Error('[MT] No public key found'); throw new Error('[MT] No public key found');
} }
@ -203,8 +206,9 @@ export class Authorizer {
this.log('PQ factorization start', auth.pq); this.log('PQ factorization start', auth.pq);
} }
let pAndQ: Awaited<ReturnType<typeof CryptoWorker['factorize']>>;
try { try {
var pAndQ = await CryptoWorker.factorize(auth.pq); pAndQ = await CryptoWorker.factorize(auth.pq);
} catch(error) { } catch(error) {
this.log.error('worker error factorize', error); this.log.error('worker error factorize', error);
throw error; throw error;
@ -216,8 +220,8 @@ export class Authorizer {
if(DEBUG) { if(DEBUG) {
this.log('PQ factorization done', pAndQ); this.log('PQ factorization done', pAndQ);
} }
/* let p = new Uint32Array(new Uint8Array(auth.p).buffer)[0]; /* const p = new Uint32Array(new Uint8Array(auth.p).buffer)[0];
let q = new Uint32Array(new Uint8Array(auth.q).buffer)[0]; const q = new Uint32Array(new Uint8Array(auth.q).buffer)[0];
console.log(dT(), 'PQ factorization done', pAndQ, p.toString(16), q.toString(16)); */ console.log(dT(), 'PQ factorization done', pAndQ, p.toString(16), q.toString(16)); */
return this.mtpSendReqDhParams(auth); return this.mtpSendReqDhParams(auth);
@ -234,7 +238,7 @@ export class Authorizer {
// auth.newNonce = fNewNonce ? fNewNonce : auth.newNonce; // auth.newNonce = fNewNonce ? fNewNonce : auth.newNonce;
// console.log("TCL: Authorizer -> mtpSendReqDhParams -> auth.newNonce", auth.newNonce); // console.log("TCL: Authorizer -> mtpSendReqDhParams -> auth.newNonce", auth.newNonce);
let p_q_inner_data = { const p_q_inner_data = {
_: 'p_q_inner_data', _: 'p_q_inner_data',
pq: auth.pq, pq: auth.pq,
p: auth.p, p: auth.p,
@ -244,29 +248,28 @@ export class Authorizer {
new_nonce: auth.newNonce new_nonce: auth.newNonce
}; };
let data = new TLSerialization({mtproto: true}); const data = new TLSerialization({mtproto: true});
data.storeObject(p_q_inner_data, 'P_Q_inner_data', 'DECRYPTED_DATA'); data.storeObject(p_q_inner_data, 'P_Q_inner_data', 'DECRYPTED_DATA');
/* console.log('p_q_inner_data', p_q_inner_data, /* console.log('p_q_inner_data', p_q_inner_data,
bytesToHex(bytesFromArrayBuffer(data.getBuffer())), bytesToHex(bytesFromArrayBuffer(data.getBuffer())),
sha1BytesSync(data.getBuffer()), sha1BytesSync(data.getBuffer()),
bytesFromArrayBuffer(await CryptoWorker.sha1Hash(data.getBuffer()))); */ bytesFromArrayBuffer(await CryptoWorker.sha1Hash(data.getBuffer()))); */
let uint8Data = data.getBytes(true); const uint8Data = data.getBytes(true);
let sha1Hashed = await CryptoWorker.sha1Hash(uint8Data); const sha1Hashed = await CryptoWorker.sha1Hash(uint8Data);
//var dataWithHash = sha1BytesSync(data.getBuffer()).concat(data.getBytes() as number[]); //const dataWithHash = sha1BytesSync(data.getBuffer()).concat(data.getBytes() as number[]);
let dataWithHash = sha1Hashed.concat(uint8Data); const dataWithHash = sha1Hashed.concat(uint8Data);
//dataWithHash = addPadding(dataWithHash, 255); //dataWithHash = addPadding(dataWithHash, 255);
//dataWithHash = dataWithHash.concat(bytesFromHex('96228ea7790e71caaabc2ab67f4412e9aa224c664d232cc08617a32ce1796aa052da4a737083211689858f461e4473fd6394afd3aa0c8014840dc13f47beaf4fc3b9229aea9cfa83f9f6e676e50ee7676542fb75606879ee7e65cf3a2295b4ba0934ceec1011560c62395a6e9593bfb117cd0da75ba56723672d100ac17ec4d805aa59f7852e3a25a79ee4')); //dataWithHash = dataWithHash.concat(bytesFromHex('96228ea7790e71caaabc2ab67f4412e9aa224c664d232cc08617a32ce1796aa052da4a737083211689858f461e4473fd6394afd3aa0c8014840dc13f47beaf4fc3b9229aea9cfa83f9f6e676e50ee7676542fb75606879ee7e65cf3a2295b4ba0934ceec1011560c62395a6e9593bfb117cd0da75ba56723672d100ac17ec4d805aa59f7852e3a25a79ee4'));
//console.log('sha1Hashed', bytesToHex(sha1Hashed), 'dataWithHash', bytesToHex(dataWithHash), dataWithHash.length); //console.log('sha1Hashed', bytesToHex(sha1Hashed), 'dataWithHash', bytesToHex(dataWithHash), dataWithHash.length);
let rsaEncrypted = await CryptoWorker.rsaEncrypt(auth.publicKey, dataWithHash); const rsaEncrypted = await CryptoWorker.rsaEncrypt(auth.publicKey, dataWithHash);
//let rsaEncrypted = await CryptoWorker.rsaEncrypt(auth.publicKey, dataWithHash);
//console.log('rsaEncrypted', rsaEncrypted, new Uint8Array(rsaEncrypted).hex); //console.log('rsaEncrypted', rsaEncrypted, new Uint8Array(rsaEncrypted).hex);
let req_DH_params = { const req_DH_params = {
nonce: auth.nonce, nonce: auth.nonce,
server_nonce: auth.serverNonce, server_nonce: auth.serverNonce,
p: auth.p, p: auth.p,
@ -275,23 +278,24 @@ export class Authorizer {
encrypted_data: rsaEncrypted encrypted_data: rsaEncrypted
}; };
var request = new TLSerialization({mtproto: true}); const request = new TLSerialization({mtproto: true});
request.storeMethod('req_DH_params', req_DH_params); request.storeMethod('req_DH_params', req_DH_params);
let requestBytes = request.getBytes(true); const requestBytes = request.getBytes(true);
if(DEBUG) { if(DEBUG) {
this.log('Send req_DH_params', req_DH_params/* , requestBytes.hex */); this.log('Send req_DH_params', req_DH_params/* , requestBytes.hex */);
} }
let deserializer: TLDeserialization;
try { try {
var deserializer = await this.mtpSendPlainRequest(auth.dcId, requestBytes); deserializer = await this.mtpSendPlainRequest(auth.dcId, requestBytes);
} catch(error) { } catch(error) {
this.log.error('Send req_DH_params FAIL!', error); this.log.error('Send req_DH_params FAIL!', error);
throw error; throw error;
} }
var response = deserializer.fetchObject('Server_DH_Params', 'RESPONSE'); const response = deserializer.fetchObject('Server_DH_Params', 'RESPONSE');
if(DEBUG) { if(DEBUG) {
this.log('Sent req_DH_params, response:', response); this.log('Sent req_DH_params, response:', response);
@ -310,8 +314,8 @@ export class Authorizer {
} }
if(response._ === 'server_DH_params_fail') { if(response._ === 'server_DH_params_fail') {
//var newNonceHash = sha1BytesSync(auth.newNonce).slice(-16); //const newNonceHash = sha1BytesSync(auth.newNonce).slice(-16);
var newNonceHash = (await CryptoWorker.sha1Hash(auth.newNonce)).slice(-16); const newNonceHash = (await CryptoWorker.sha1Hash(auth.newNonce)).slice(-16);
if(!bytesCmp(newNonceHash, response.new_nonce_hash)) { if(!bytesCmp(newNonceHash, response.new_nonce_hash)) {
throw new Error('[MT] server_DH_params_fail new_nonce_hash mismatch'); throw new Error('[MT] server_DH_params_fail new_nonce_hash mismatch');
} }
@ -350,16 +354,16 @@ export class Authorizer {
console.log(auth.newNonce.concat(auth.newNonce)); */ console.log(auth.newNonce.concat(auth.newNonce)); */
//var answerWithHash = aesDecryptSync(encryptedAnswer, auth.tmpAesKey, auth.tmpAesIv); //const answerWithHash = aesDecryptSync(encryptedAnswer, auth.tmpAesKey, auth.tmpAesIv);
var answerWithHash = new Uint8Array(await CryptoWorker.aesDecrypt(encryptedAnswer, auth.tmpAesKey, auth.tmpAesIv)); const answerWithHash = new Uint8Array(await CryptoWorker.aesDecrypt(encryptedAnswer, auth.tmpAesKey, auth.tmpAesIv));
var hash = answerWithHash.slice(0, 20); const hash = answerWithHash.slice(0, 20);
var answerWithPadding = answerWithHash.slice(20); const answerWithPadding = answerWithHash.slice(20);
// console.log('hash', hash); // console.log('hash', hash);
var deserializer = new TLDeserialization(answerWithPadding, {mtproto: true}); const deserializer = new TLDeserialization(answerWithPadding, {mtproto: true});
var response = deserializer.fetchObject('Server_DH_inner_data'); const response = deserializer.fetchObject('Server_DH_inner_data');
if(response._ !== 'server_DH_inner_data') { if(response._ !== 'server_DH_inner_data') {
throw new Error('[MT] server_DH_inner_data response invalid: ' + response); throw new Error('[MT] server_DH_inner_data response invalid: ' + response);
@ -384,7 +388,7 @@ export class Authorizer {
this.mtpVerifyDhParams(auth.g, auth.dhPrime, auth.gA); this.mtpVerifyDhParams(auth.g, auth.dhPrime, auth.gA);
var offset = deserializer.getOffset(); const offset = deserializer.getOffset();
//if(!bytesCmp(hash, sha1BytesSync(answerWithPadding.slice(0, offset)))) { //if(!bytesCmp(hash, sha1BytesSync(answerWithPadding.slice(0, offset)))) {
if(!bytesCmp(hash, await CryptoWorker.sha1Hash(answerWithPadding.slice(0, offset)))) { if(!bytesCmp(hash, await CryptoWorker.sha1Hash(answerWithPadding.slice(0, offset)))) {
@ -399,7 +403,7 @@ export class Authorizer {
this.log('Verifying DH params', g, dhPrime, gA); this.log('Verifying DH params', g, dhPrime, gA);
} }
var dhPrimeHex = bytesToHex(dhPrime); const dhPrimeHex = bytesToHex(dhPrime);
if(g !== 3 || dhPrimeHex !== 'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b') { if(g !== 3 || dhPrimeHex !== 'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b') {
// The verified value is from https://core.telegram.org/mtproto/security_guidelines // The verified value is from https://core.telegram.org/mtproto/security_guidelines
throw new Error('[MT] DH params are not verified: unknown dhPrime'); throw new Error('[MT] DH params are not verified: unknown dhPrime');
@ -409,9 +413,9 @@ export class Authorizer {
this.log('dhPrime cmp OK'); this.log('dhPrime cmp OK');
} }
//var gABigInt = new BigInteger(bytesToHex(gA), 16); //const gABigInt = new BigInteger(bytesToHex(gA), 16);
const _gABigInt = str2bigInt(bytesToHex(gA), 16); const _gABigInt = str2bigInt(bytesToHex(gA), 16);
//var dhPrimeBigInt = new BigInteger(dhPrimeHex, 16); //const dhPrimeBigInt = new BigInteger(dhPrimeHex, 16);
const _dhPrimeBigInt = str2bigInt(dhPrimeHex, 16); const _dhPrimeBigInt = str2bigInt(dhPrimeHex, 16);
//this.log('gABigInt.compareTo(BigInteger.ONE) <= 0', gABigInt.compareTo(BigInteger.ONE), BigInteger.ONE.compareTo(BigInteger.ONE), greater(_gABigInt, one)); //this.log('gABigInt.compareTo(BigInteger.ONE) <= 0', gABigInt.compareTo(BigInteger.ONE), BigInteger.ONE.compareTo(BigInteger.ONE), greater(_gABigInt, one));
@ -432,14 +436,14 @@ export class Authorizer {
} }
//var two = new BigInteger(/* null */''); //const two = new BigInteger(/* null */'');
//two.fromInt(2); //two.fromInt(2);
const _two = int2bigInt(2, 32, 0); const _two = int2bigInt(2, 32, 0);
//this.log('_two:', bigInt2str(_two, 16), two.toString(16)); //this.log('_two:', bigInt2str(_two, 16), two.toString(16));
let perf = performance.now(); // let perf = performance.now();
//var twoPow = two.pow(2048 - 64); //const twoPow = two.pow(2048 - 64);
//console.log('jsbn pow', performance.now() - perf); //console.log('jsbn pow', performance.now() - perf);
perf = performance.now(); // perf = performance.now();
const _twoPow = pow(_two, 2048 - 64); const _twoPow = pow(_two, 2048 - 64);
//console.log('leemon pow', performance.now() - perf); //console.log('leemon pow', performance.now() - perf);
//this.log('twoPow:', twoPow.toString(16), bigInt2str(_twoPow, 16)); //this.log('twoPow:', twoPow.toString(16), bigInt2str(_twoPow, 16));
@ -462,19 +466,20 @@ export class Authorizer {
} }
public async mtpSendSetClientDhParams(auth: AuthOptions): Promise<AuthOptions> { public async mtpSendSetClientDhParams(auth: AuthOptions): Promise<AuthOptions> {
var gBytes = bytesFromHex(auth.g.toString(16)); const gBytes = bytesFromHex(auth.g.toString(16));
auth.b = new Array(256); auth.b = new Array(256);
auth.b = [...new Uint8Array(auth.b.length).randomize()]; auth.b = [...new Uint8Array(auth.b.length).randomize()];
//MTProto.secureRandom.nextBytes(auth.b); //MTProto.secureRandom.nextBytes(auth.b);
let gB: number[];
try { try {
var gB = await CryptoWorker.modPow(gBytes, auth.b, auth.dhPrime); gB = await CryptoWorker.modPow(gBytes, auth.b, auth.dhPrime);
} catch(error) { } catch(error) {
throw error; throw error;
} }
var data = new TLSerialization({mtproto: true}); const data = new TLSerialization({mtproto: true});
data.storeObject({ data.storeObject({
_: 'client_DH_inner_data', _: 'client_DH_inner_data',
nonce: auth.nonce, nonce: auth.nonce,
@ -483,13 +488,13 @@ export class Authorizer {
g_b: gB g_b: gB
}, 'Client_DH_Inner_Data'); }, 'Client_DH_Inner_Data');
//var dataWithHash = sha1BytesSync(data.getBuffer()).concat(data.getBytes()); //const dataWithHash = sha1BytesSync(data.getBuffer()).concat(data.getBytes());
var dataWithHash = (await CryptoWorker.sha1Hash(data.getBuffer())).concat(data.getBytes()); const dataWithHash = (await CryptoWorker.sha1Hash(data.getBuffer())).concat(data.getBytes());
//var encryptedData = aesEncryptSync(dataWithHash, auth.tmpAesKey, auth.tmpAesIv); //const encryptedData = aesEncryptSync(dataWithHash, auth.tmpAesKey, auth.tmpAesIv);
var encryptedData = await CryptoWorker.aesEncrypt(dataWithHash, auth.tmpAesKey, auth.tmpAesIv); const encryptedData = await CryptoWorker.aesEncrypt(dataWithHash, auth.tmpAesKey, auth.tmpAesIv);
var request = new TLSerialization({mtproto: true}); const request = new TLSerialization({mtproto: true});
request.storeMethod('set_client_DH_params', { request.storeMethod('set_client_DH_params', {
nonce: auth.nonce, nonce: auth.nonce,
server_nonce: auth.serverNonce, server_nonce: auth.serverNonce,
@ -500,13 +505,14 @@ export class Authorizer {
this.log('Send set_client_DH_params'); this.log('Send set_client_DH_params');
} }
let deserializer: TLDeserialization;
try { try {
var deserializer = await this.mtpSendPlainRequest(auth.dcId, request.getBytes(true)); deserializer = await this.mtpSendPlainRequest(auth.dcId, request.getBytes(true));
} catch(err) { } catch(err) {
throw err; throw err;
} }
let response = deserializer.fetchObject('Set_client_DH_params_answer'); const response = deserializer.fetchObject('Set_client_DH_params_answer');
if(response._ !== 'dh_gen_ok' && response._ !== 'dh_gen_retry' && response._ !== 'dh_gen_fail') { if(response._ !== 'dh_gen_ok' && response._ !== 'dh_gen_retry' && response._ !== 'dh_gen_fail') {
throw new Error('[MT] Set_client_DH_params_answer response invalid: ' + response._); throw new Error('[MT] Set_client_DH_params_answer response invalid: ' + response._);
@ -520,14 +526,15 @@ export class Authorizer {
throw new Error('[MT] Set_client_DH_params_answer server_nonce mismatch'); throw new Error('[MT] Set_client_DH_params_answer server_nonce mismatch');
} }
let authKey: number[];
try { try {
var authKey = await CryptoWorker.modPow(auth.gA, auth.b, auth.dhPrime); authKey = await CryptoWorker.modPow(auth.gA, auth.b, auth.dhPrime);
} catch(err) { } catch(err) {
throw authKey; throw authKey;
} }
//var authKeyHash = sha1BytesSync(authKey), //const authKeyHash = sha1BytesSync(authKey),
let authKeyHash = await CryptoWorker.sha1Hash(new Uint8Array(authKey)), const authKeyHash = await CryptoWorker.sha1Hash(new Uint8Array(authKey)),
authKeyAux = authKeyHash.slice(0, 8), authKeyAux = authKeyHash.slice(0, 8),
authKeyId = authKeyHash.slice(-8); authKeyId = authKeyHash.slice(-8);
@ -536,14 +543,14 @@ export class Authorizer {
} }
switch(response._) { switch(response._) {
case 'dh_gen_ok': case 'dh_gen_ok':
var newNonceHash1 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([1], authKeyAux))).slice(-16); const newNonceHash1 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([1], authKeyAux))).slice(-16);
//var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16); //const newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16);
if(!bytesCmp(newNonceHash1, response.new_nonce_hash1)) { if(!bytesCmp(newNonceHash1, response.new_nonce_hash1)) {
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch'); throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch');
} }
var serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8)); const serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8));
if(DEBUG) { if(DEBUG) {
this.log('Auth successfull!', authKeyId, authKey, serverSalt); this.log('Auth successfull!', authKeyId, authKey, serverSalt);
} }
@ -556,8 +563,8 @@ export class Authorizer {
break; break;
case 'dh_gen_retry': case 'dh_gen_retry':
//var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16); //const newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16);
var newNonceHash2 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([2], authKeyAux))).slice(-16); const newNonceHash2 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([2], authKeyAux))).slice(-16);
if(!bytesCmp(newNonceHash2, response.new_nonce_hash2)) { if(!bytesCmp(newNonceHash2, response.new_nonce_hash2)) {
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch'); throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch');
} }
@ -565,8 +572,8 @@ export class Authorizer {
return this.mtpSendSetClientDhParams(auth); return this.mtpSendSetClientDhParams(auth);
case 'dh_gen_fail': case 'dh_gen_fail':
//var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16); //const newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16);
var newNonceHash3 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([3], authKeyAux))).slice(-16); const newNonceHash3 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([3], authKeyAux))).slice(-16);
if(!bytesCmp(newNonceHash3, response.new_nonce_hash3)) { if(!bytesCmp(newNonceHash3, response.new_nonce_hash3)) {
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch'); throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch');
} }
@ -581,8 +588,8 @@ export class Authorizer {
return this.cached[dcId]; return this.cached[dcId];
} }
let nonce = /* fNonce ? fNonce : */new Uint8Array(16).randomize(); const nonce = /* fNonce ? fNonce : */new Uint8Array(16).randomize();
/* var nonce = new Array(16); /* const nonce = new Array(16);
MTProto.secureRandom.nextBytes(nonce); */ MTProto.secureRandom.nextBytes(nonce); */
if(!dcConfigurator.chooseServer(dcId)) { if(!dcConfigurator.chooseServer(dcId)) {
@ -590,7 +597,7 @@ export class Authorizer {
} }
try { try {
let promise = this.mtpSendReqPQ({dcId, nonce}); const promise = this.mtpSendReqPQ({dcId, nonce});
this.cached[dcId] = promise; this.cached[dcId] = promise;
return await promise; return await promise;
} catch(err) { } catch(err) {

View File

@ -9,32 +9,48 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
*/ */
import type { ApplyServerTimeOffsetTask } from './timeManager';
import { MOUNT_CLASS_TO } from '../../config/debug'; import { MOUNT_CLASS_TO } from '../../config/debug';
import { tsNow } from '../../helpers/date'; // import { tsNow } from '../../helpers/date';
import sessionStorage from '../sessionStorage'; import sessionStorage from '../sessionStorage';
import apiManager from './mtprotoworker';
export class ServerTimeManager { export class ServerTimeManager {
public timestampNow = tsNow(true); /* private midnightNoOffset: number;
public midnightNoOffset = this.timestampNow - (this.timestampNow % 86400); private midnightOffseted: Date;
public midnightOffseted = new Date();
public midnightOffset = this.midnightNoOffset - (Math.floor(+this.midnightOffseted / 1000)); private midnightOffset: number; */
public serverTimeOffset = 0; // in seconds public serverTimeOffset: number; // in seconds
public timeParams = { /* private timeParams: {
midnightOffset: this.midnightOffset, midnightOffset: number,
serverTimeOffset: this.serverTimeOffset serverTimeOffset: number
}; }; */
constructor() { constructor() {
/* const timestampNow = tsNow(true);
this.midnightNoOffset = timestampNow - (timestampNow % 86400);
this.midnightOffseted = new Date();
this.midnightOffseted.setHours(0, 0, 0, 0); this.midnightOffseted.setHours(0, 0, 0, 0);
this.midnightOffset = this.midnightNoOffset - (Math.floor(+this.midnightOffseted / 1000)); */
this.serverTimeOffset = 0;
/* this.timeParams = {
midnightOffset: this.midnightOffset,
serverTimeOffset: this.serverTimeOffset
}; */
sessionStorage.get('server_time_offset').then((to) => { sessionStorage.get('server_time_offset').then((to) => {
if(to) { if(to) {
this.serverTimeOffset = to; this.serverTimeOffset = to;
this.timeParams.serverTimeOffset = to; // this.timeParams.serverTimeOffset = to;
} }
}); });
apiManager.addTaskListener('applyServerTimeOffset', (task: ApplyServerTimeOffsetTask) => {
this.serverTimeOffset = task.payload;
});
} }
} }

View File

@ -13,6 +13,8 @@ import sessionStorage from '../sessionStorage';
import { longFromInts } from './bin_utils'; import { longFromInts } from './bin_utils';
import { nextRandomInt } from '../../helpers/random'; import { nextRandomInt } from '../../helpers/random';
import { MOUNT_CLASS_TO } from '../../config/debug'; import { MOUNT_CLASS_TO } from '../../config/debug';
import { WorkerTaskVoidTemplate } from '../../types';
import { notifySomeone } from '../../helpers/context';
/* /*
let lol: any = {}; let lol: any = {};
@ -21,9 +23,14 @@ for(var i = 0; i < 100; i++) {
} }
*/ */
export interface ApplyServerTimeOffsetTask extends WorkerTaskVoidTemplate {
type: 'applyServerTimeOffset',
payload: TimeManager['timeOffset']
};
export class TimeManager { export class TimeManager {
private lastMessageId = [0, 0]; private lastMessageId: [number, number] = [0, 0];
private timeOffset = 0; private timeOffset: number = 0;
constructor() { constructor() {
sessionStorage.get('server_time_offset').then((to) => { sessionStorage.get('server_time_offset').then((to) => {
@ -39,7 +46,7 @@ export class TimeManager {
timeMSec = timeTicks % 1000, timeMSec = timeTicks % 1000,
random = nextRandomInt(0xFFFF); random = nextRandomInt(0xFFFF);
let messageId = [timeSec, (timeMSec << 21) | (random << 3) | 4]; let messageId: TimeManager['lastMessageId'] = [timeSec, (timeMSec << 21) | (random << 3) | 4];
if(this.lastMessageId[0] > messageId[0] || if(this.lastMessageId[0] > messageId[0] ||
this.lastMessageId[0] === messageId[0] && this.lastMessageId[1] >= messageId[1]) { this.lastMessageId[0] === messageId[0] && this.lastMessageId[1] >= messageId[1]) {
messageId = [this.lastMessageId[0], this.lastMessageId[1] + 4]; messageId = [this.lastMessageId[0], this.lastMessageId[1] + 4];
@ -72,6 +79,14 @@ export class TimeManager {
//console.log('[TimeManager]: Apply server time', serverTime, localTime, newTimeOffset, changed); //console.log('[TimeManager]: Apply server time', serverTime, localTime, newTimeOffset, changed);
/// #if MTPROTO_WORKER
const task: ApplyServerTimeOffsetTask = {
type: 'applyServerTimeOffset',
payload: newTimeOffset
};
notifySomeone(task);
/// #endif
return changed; return changed;
} }
} }

2
src/types.d.ts vendored
View File

@ -48,6 +48,8 @@ export type AnyFunction = (...args: any) => any;
export type AnyToVoidFunction = (...args: any) => void; export type AnyToVoidFunction = (...args: any) => void;
export type NoneToVoidFunction = () => void; export type NoneToVoidFunction = () => void;
export type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
export type AuthState = AuthState.signIn | AuthState.signQr | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn; export type AuthState = AuthState.signIn | AuthState.signQr | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn;
export namespace AuthState { export namespace AuthState {
export type signIn = { export type signIn = {