· 6 years ago · Feb 17, 2020, 11:10 PM
1// import {pqPrimeLeemon} from './crypto';
2import { Authorization } from './mtproto';
3import { Subject } from 'rxjs';
4
5let json;
6let api;
7let mtproto;
8
9fetch('/mtproto.json').then((it) => {
10 it.json().then((answa) => {
11 json = {
12 constructors: toMap(answa.constructors, 'predicate'),
13 constructorIds: toMap(answa.constructors, 'id'),
14 constructorTypes: toMap(answa.constructors, 'type'),
15 methods: toMap(answa.methods, 'method'),
16 };
17 window.mtproto = json;
18 mtproto = window.mtproto;
19 if (!currentDc.authorization.authData.authKeyID) {
20 window.json = window.mtproto;
21 }
22 // sendPlainMessage('req_pq', { nonce: randomBytes(nonce) });
23 });
24});
25fetch('/api.json').then((it) => {
26 it.json().then((answa) => {
27 window.api = {
28 constructors: toMap(answa.constructors, 'predicate'),
29 constructorIds: toMap(answa.constructors, 'id'),
30 constructorTypes: toMap(answa.constructors, 'type'),
31 methods: toMap(answa.methods, 'method'),
32 };
33 api = window.api;
34 if (currentDc.authorization.authData.authKeyID) {
35 window.json = window.api;
36 }
37 // sendPlainMessage('req_pq', { nonce: randomBytes(nonce) });
38 });
39});
40
41export class DataCenter {
42 seqNo = 0;
43
44 authorization;
45 socketConnection: WebSocket;
46
47 isConnecting = true;
48 initConnection = false;
49 sendCode = false;
50
51 sessionID;
52 prevSessionID;
53
54 cipher;
55 decipher;
56
57 lastMessageID = [0, 0];
58 timerOffset = 0;
59
60 pendingRequests = {};
61
62 constructor(public dcId) {
63 this.authorization = new Authorization(this);
64 this.updateSession();
65 }
66
67 connect() {
68 var { init, cipher, decipher } = generateObfuscatedInitPayload();
69 this.cipher = cipher;
70 this.decipher = decipher;
71 this.socketConnection = new WebSocket(`wss://${sslSubdomains[this.dcId - 1]}-1.web.telegram.org:443/apiws`, ['binary']);
72 this.socketConnection.binaryType = 'arraybuffer';
73
74 this.socketConnection.onopen = () => {
75 this.socketConnection.send(init);
76 if (!this.authorization.authData.authKeyID) {
77 this.authorization.start();
78 return;
79 } else {
80 this.isConnecting = false;
81 console.log('dawn we authorized from start');
82 this.respondPending();
83 }
84 };
85 // this.socketConnection.
86 this.socketConnection.onmessage = (data) => {
87 this.handleAnswer(data)
88 };
89
90 }
91
92 respondPending() {
93 let keys = Object.keys(this.pendingRequests)
94 if (keys.length != 0) {
95 console.log('start responding for');
96 keys.forEach((it) => {
97 let request = this.pendingRequests[it];
98 console.log(request);
99 this.sendApiMessage(request.method, request.payload, request.subject, request.generatedId)
100 })
101 }
102 }
103
104 sendPlainMessage(methodName, payload) {
105 var generatedId = this.generateMessageID();
106 let tlSerializator = new TLSerialization({ mtproto: true });
107 tlSerializator.storeLong('0');
108 tlSerializator.storeLong(generatedId);
109 tlSerializator.storeInt(20);
110 tlSerializator.storeMethod(methodName, payload);
111 let tlLength = tlSerializator.offset;
112 tlSerializator.offset = 16;
113 tlSerializator.storeInt(tlLength - 20);
114 tlSerializator.offset = tlLength
115 let encrypted = this.cipher.encrypt(toIntermidiateEnvelope(tlSerializator.getBytes())).buffer;
116 this.socketConnection.send(encrypted);
117 }
118
119
120 sendApiMessage(method, payload, answerSubject?, generatedId?) {
121
122 if (!answerSubject) {
123 answerSubject = new Subject();
124 }
125
126 if (!generatedId) {
127 generatedId = this.generateMessageID();
128 this.pendingRequests[generatedId] = {
129 subject: answerSubject,
130 method: method,
131 payload: payload,
132 generatedId,
133 };
134 }
135 console.log('begin');
136 console.log(method);
137 if (!this.socketConnection) {
138 console.log('begin connect to dc');
139 this.connect();
140 return answerSubject;
141 }
142 if (this.isConnecting) {
143 console.log('still connecting');
144 return answerSubject;
145 }
146 const tlMessage = new TLSerialization({ mtproto: false });
147 console.log('plus seqno');
148 if (!this.initConnection) {
149 tlMessage.storeInt(0xda9b0d0d, 'invokeWithLayer')
150 tlMessage.storeInt(105, 'layer')
151 tlMessage.storeInt(0xc7481da6, 'initConnection')
152 tlMessage.storeInt(12312, 'api_id')
153 tlMessage.storeString(navigator.userAgent || 'Unknown UserAgent', 'device_model')
154 tlMessage.storeString(navigator.platform || 'Unknown Platform', 'system_version')
155 tlMessage.storeString('1.0.1', 'app_version')
156 tlMessage.storeString(navigator.language || 'en', 'system_lang_code')
157 tlMessage.storeString('', 'lang_pack')
158 tlMessage.storeString(navigator.language || 'en', 'lang_code')
159 this.initConnection = true;
160 }
161 tlMessage.storeMethod(method, payload);
162 this.sendEncryptedMessage(tlMessage, generatedId);
163 return answerSubject;
164 }
165
166 sendEncryptedMessage(tlMessage: TLSerialization, generatedId, notContentRelated = false) {
167 let authorization = this.authorization;
168 let sessionID = this.sessionID;
169 const tlSerializator = new TLSerialization({ mtproto: false });
170 tlSerializator.storeLong(authorization.authData.serverSalt);
171 tlSerializator.storeRawBytes(sessionID);
172 tlSerializator.storeLong(generatedId);
173 tlSerializator.storeInt(this.generateSeqNo(notContentRelated));
174
175 tlSerializator.storeInt(tlMessage.offset);
176 tlSerializator.storeRawBytes(tlMessage.getBytes());
177
178 var paddingLength = (16 - (tlSerializator.offset % 16)) + 16 * (1 + nextRandomInt(5))
179 var padding = randomBytes(paddingLength);
180 var dataWithPadding = concat(tlSerializator.getBytes(), padding)
181
182 const bytes = dataWithPadding;
183 const msgKey = getMsgKey(dataWithPadding, true, authorization.authData.authKey);
184 const keyIv = getMsgKeyIv(msgKey, true, this.authorization.authData.authKey)
185 const encryptedBytes = aesEncryptSync(bytes, keyIv[0], keyIv[1])
186 const request = new TLSerialization({ mtproto: false });
187 request.storeRawBytes(this.authorization.authData.authKeyID);
188 request.storeRawBytes(msgKey);
189 request.storeRawBytes(encryptedBytes);
190
191 let encrypted = this.cipher.encrypt(toIntermidiateEnvelope(request.getBytes())).buffer;
192 this.socketConnection.send(encrypted);
193 }
194
195 updateSession() {
196 this.seqNo = 0;
197 this.prevSessionID = this.sessionID;
198 this.sessionID = randomBytes(8);
199 }
200
201 handleAnswer(data) {
202 const decryptedData = fromIntermidiateEnvelope(this.decipher.decrypt((new Uint8Array(data.data))));
203 const authorization = this.authorization
204 if (!authorization.isAuthorized) {
205 authorization.onMessage(decryptedData)
206 }
207 if (authorization.isAuthorized && this.isConnecting) {
208 this.isConnecting = false;
209 this.respondPending();
210 return;
211 }
212 if (authorization.isAuthorized) {
213 let deser = new Desearelizator(decryptedData, false);
214 let deserializerRaw = deser.desearelization;
215 let length = deserializerRaw.fetchInt();
216 const authKeyID = deserializerRaw.fetchIntBytes(64, 'auth_key_id');
217 const msgKey = deserializerRaw.fetchIntBytes(128, 'msg_key');
218 const encryptedData = deserializerRaw.fetchRawBytes(decryptedData.byteLength - deserializerRaw.getOffset(), 'encrypted_data')
219
220
221 const keyIv = getMsgKeyIv(msgKey, false, authorization.authData.authKey)
222 const dataWithPadding = new Uint8Array(aesDecryptSync(encryptedData, keyIv[0], keyIv[1]))
223 const deserializer = new Desearelizator(dataWithPadding, false);
224 deserializer.desearelization.fetchIntBytes(64, 'salt')
225 const locSessionId = new Uint8Array(deserializer.desearelization.fetchIntBytes(64, 'session_id'))
226 const messageId = deserializer.desearelization.fetchLong('messageid');
227 const isInvalidSession =
228 !bytesCmp(locSessionId, this.sessionID) && (
229 !this.prevSessionID || !bytesCmp(locSessionId, this.prevSessionID));
230 if (isInvalidSession) {
231 console.warn(this.dcId + 'Sessions', this.sessionID, locSessionId, this.prevSessionID)
232 // throw new Error(`[MT] Invalid server session_id: ${ bytesToHex(sessionID) }`)
233 }
234 const seqNoAnsw = deserializer.desearelization.fetchInt('seq_no');
235 const messageBodyLength = deserializer.desearelization.fetchInt('message_data[length]')
236 const messageBody = deserializer.desearelization.fetchRawBytes(messageBodyLength, 'message_data');
237 // console.log('body');
238 // console.log(messageBody);
239 const bodyDeser = new TLDeserialization(messageBody.buffer, { mtproto: true });
240 console.log('obob ' + this.dcId);
241 let obobo = bodyDeser.fetchObject('Objdect');
242
243 // if(obobo._ )
244 // this.seqNo += 1;
245 // if(obobo[''])
246 this.handleResult(obobo, messageId);
247 }
248 }
249
250 handleResult(resultObject, messageId) {
251 console.log(resultObject);
252 if (resultObject._ === 'bad_server_salt') {
253 this.authorization.updateSalt(resultObject.new_server_salt);
254 this.respondPending();
255 return;
256 }
257
258 if (resultObject['_'] === 'rpc_result') {
259 console.log('plus seqno answer');
260 console.log('add on rpc result');
261 this.acknowledge(messageId);
262 if (resultObject.result && resultObject.result.error_code && resultObject.result.error_message == 'AUTH_KEY_UNREGISTERED') {
263 currentDc.sendApiMessage('auth.exportAuthorization', {
264 dc_id: this.dcId,
265 }).subscribe((it) => {
266 console.log('got dat key');
267 console.log(it);
268 this.sendApiMessage('auth.importAuthorization', it);
269 });
270 return;
271 }
272
273 if (resultObject.result && resultObject.result.error_code && resultObject.result.error_message.indexOf('MIGRATE') !== -1) {
274 currentDc = dataCenters[(+resultObject.result.error_message.slice(-1)) - 1];
275 currentDc.pendingRequests = this.pendingRequests;
276 this.pendingRequests = {};
277 currentDc.respondPending();
278 return;
279 }
280
281 if (resultObject.result._ == 'auth.authorization') {
282 console.log('authorized dat bitch');
283 console.log(this.pendingRequests);
284 if (this.pendingRequests[resultObject['req_msg_id']])
285 this.pendingRequests[resultObject['req_msg_id']].subject.next(resultObject['result']);
286 delete this.pendingRequests[resultObject['req_msg_id']];
287 this.updateSession();
288 this.respondPending();
289 }
290 if (this.pendingRequests[resultObject['req_msg_id']])
291 this.pendingRequests[resultObject['req_msg_id']].subject.next(resultObject['result']);
292 delete this.pendingRequests[resultObject['req_msg_id']];
293 }
294
295 if (resultObject['_'] === 'msg_container') {
296 resultObject.messages.forEach((it) => {
297 this.handleResult(it.body, it.msg_id);
298 })
299 }
300 }
301
302 generateMessageID() {
303 const timeTicks = tsNow(),
304 timeSec = Math.floor(timeTicks / 1000) + this.timerOffset,
305 timeMSec = timeTicks % 1000,
306 random = nextRandomInt(0xFFFF)
307
308 let messageID = [timeSec, timeMSec << 21 | random << 3 | 4]
309 if (this.lastMessageID[0] > messageID[0] ||
310 this.lastMessageID[0] == messageID[0] && this.lastMessageID[1] >= messageID[1]) {
311 messageID = [this.lastMessageID[0], this.lastMessageID[1] + 4]
312 }
313
314 this.lastMessageID = messageID
315
316 // console.log('generated msg id', messageID, timerOffset)
317
318 return lshift32(messageID[0], messageID[1])
319 };
320
321 applyServerTime(serverTime, localTime) {
322 console.log('apply server time');
323 console.log(serverTime);
324 const newTimeOffset = serverTime - Math.floor((localTime || tsNow()) / 1000)
325 const changed = Math.abs(this.timerOffset - newTimeOffset) > 10
326 this.timerOffset = newTimeOffset
327
328 this.lastMessageID = [0, 0]
329 if (changed) {
330 console.log('changed offset');
331 }
332 return changed
333 };
334
335 generateSeqNo(notContentRelated) {
336 var seqNo = this.seqNo * 2
337 //
338 if (!notContentRelated) {
339 seqNo++;
340 this.seqNo++
341 }
342 return seqNo
343 }
344
345 acknowledge(msgid) {
346 let ser = new TLSerialization({});
347 ser.storeObject({
348 _: 'msgs_ack',
349 msg_ids: [msgid],
350 }, 'MsgsAck');
351 this.sendEncryptedMessage(ser, this.generateMessageID(), true)
352 }
353}
354
355var sslSubdomains = ['pluto', 'venus', 'aurora', 'vesta', 'flora'];
356var dataCenters = [
357 new DataCenter(1),
358 new DataCenter(2),
359 new DataCenter(3),
360 new DataCenter(4),
361 new DataCenter(5),
362];
363
364let dcNumber = (JSON.parse(localStorage.getItem('auth') || `{}`).dcId-1) || 1;
365
366let currentDc = dataCenters[dcNumber];
367
368// currentDc.connect();
369function getMsgKeyIv(msgKey: number[], isOut: boolean, authKey: Uint8Array) {
370 authKey = new Uint8Array(authKey);
371 const x = isOut
372 ? 0
373 : 8;
374 var sha2aText = new Uint8Array(52);
375 var sha2bText = new Uint8Array(52);
376
377 sha2aText.set(msgKey, 0)
378 sha2aText.set(authKey.subarray(x, x + 36), 16)
379 sha2aText = sha256HashSync(sha2aText);
380
381 sha2bText.set(authKey.subarray(40 + x, 40 + x + 36), 0)
382 sha2bText.set(msgKey, 36)
383 sha2bText = sha256HashSync(sha2bText)
384
385 var aesKey = new Uint8Array(32)
386 var aesIv = new Uint8Array(32)
387 var sha2a = new Uint8Array(sha2aText)
388 var sha2b = new Uint8Array(sha2bText)
389
390 aesKey.set(sha2a.subarray(0, 8))
391 aesKey.set(sha2b.subarray(8, 24), 8)
392 aesKey.set(sha2a.subarray(24, 32), 24)
393
394 aesIv.set(sha2b.subarray(0, 8))
395 aesIv.set(sha2a.subarray(8, 24), 8)
396 aesIv.set(sha2b.subarray(24, 32), 24)
397
398 return [aesKey, aesIv]
399}
400
401function getMsgKey(dataWithPadding, isOut, authKey: Uint8Array) {
402 authKey = new Uint8Array(authKey)
403 var x = isOut ? 0 : 8
404 var msgKeyLargePlain = concat(authKey.subarray(88 + x, 88 + x + 32), dataWithPadding);
405 let withSha = sha256HashSync(msgKeyLargePlain);
406 var msgKey = new Uint8Array(withSha).subarray(8, 24)
407 return msgKey;
408}
409
410
411function toIntermidiateEnvelope(payload: Uint8Array) {
412 let length = new Uint8Array(intToByteArray(payload.length));
413 return concat(length, payload);
414}
415
416function fromIntermidiateEnvelope(payload: Uint8Array) {
417 return payload.slice(payload.slice(4, numberFromByteArray(payload.slice(0, 4))))
418}
419
420
421// generateObfuscatedInitPayload();
422
423function generateObfuscatedInitPayload() {
424 let result = generateRandomKey();
425 let reversed = new Uint8Array(result).reverse();
426 let encryptKey = substr(result, 8, 32);
427 let encryptIv = substr(result, 40, 16);
428 let decryptKey = substr(reversed, 8, 32);
429 let decryptIv = substr(reversed, 40, 16);
430 let cipher = new aesjs.ModeOfOperation.ctr(encryptKey, encryptIv);
431 let decipher = new aesjs.ModeOfOperation.ctr(decryptKey, decryptIv);
432 let encrypted = new Uint8Array(cipher.encrypt(result));
433 return { init: concat(substr(result, 0, 56), substr(encrypted, 56, 8)), cipher, decipher };
434}
435
436function generateRandomKey() {
437 let funcResult;
438 while (!funcResult) {
439 let start = randomBytes(56);
440 let protocol = new Uint8Array([238, 238, 238, 238]);
441
442 let dcId = new Uint8Array([2, 0]);
443 let random = randomBytes(2);
444 let result = concat(start, protocol, dcId, random);
445 if (result[0] == 0xef) {
446 continue;
447 }
448 let deprecated = [
449 0x44414548,
450 0x54534f50,
451 0x20544547,
452 0x4954504f,
453 0xdddddddd,
454 0xeeeeeeee,
455 ];
456
457 if (deprecated.indexOf(numberFromByteArray(substr(result, 0, 4))) != -1) {
458 continue;
459 }
460 funcResult = result
461 }
462 return funcResult;
463}
464
465
466function tsNow(seconds) {
467 var t = +new Date() + (0)
468 return seconds ? Math.floor(t / 1000) : t
469}
470
471window.tsNow = tsNow;
472
473function nextRandomInt(maxValue) {
474 return Math.floor(Math.random() * maxValue)
475}
476
477
478window.tgApi = {
479 sendCode: (phoneNumber) => {
480 return currentDc.sendApiMessage('auth.sendCode', {
481 phone_number: phoneNumber,
482 api_id: -123,
483 api_hash: '-',
484 settings: {
485 _: 'codeSettings',
486 allow_flashcall: false,
487 current_number: false,
488 allow_app_hash: true,
489 },
490 });
491 },
492 signIn: (phoneNumber, phoneCodeHash, phoneCode) => {
493 console.log('nop nop nop2')
494 return currentDc.sendApiMessage('auth.signIn', {
495 phone_number: phoneNumber,
496 phone_code_hash: phoneCodeHash,
497 phone_code: phoneCode,
498 });
499 },
500
501 signUp: (phoneNumber, phoneCodeHash, name, lastname) => {
502 return currentDc.sendApiMessage('auth.signUp', {
503 phone_number: phoneNumber,
504 phone_code_hash: phoneCodeHash,
505 first_name: name,
506 last_name: lastname
507 });
508 },
509 getDialogs: () => {
510 console.log('get that sweet dialogs');
511 return currentDc.sendApiMessage('messages.getDialogs', {
512 exclude_pinned: false,
513 folder_id: 0,
514 offset_date: 0,
515 offset_id: 0,
516 offset_peer: { _: 'inputPeerEmpty' },
517 limit: 20,
518 hash: 0,
519 });
520 },
521
522 getMoreDialogs: (offset_date, offset_id, offset_peer) => {
523 console.log('get that sweet dialogs');
524 return currentDc.sendApiMessage('messages.getDialogs', {
525 exclude_pinned: true,
526 folder_id: 0,
527 offset_date,
528 offset_id,
529 offset_peer,
530 limit: 20,
531 hash: 0,
532 });
533 },
534 getPeerPhoto(peerPhoto) {
535 console.log(peerPhoto);
536 return dataCenters[peerPhoto.dc_id - 1].sendApiMessage('upload.getFile', {
537 location: {
538 _: 'inputPeerPhotoFileLocation',
539 peer: peerPhoto.inputPeer,
540 volume_id: peerPhoto.volume_id,
541 local_id: peerPhoto.local_id,
542 },
543 offset: 0,
544 limit: 1024 * 1024,
545 });
546 },
547
548 getPhoto(dcId, photoId, accessHash, fileReference, type) {
549 return dataCenters[dcId - 1].sendApiMessage('upload.getFile', {
550 location: {
551 _: 'inputPhotoFileLocation',
552 id: photoId,
553 access_hash: accessHash,
554 file_reference: fileReference,
555 thumb_size: type,
556 },
557 offset: 0,
558 limit: 1024 * 1024,
559 });
560 },
561
562 getMessages: (offset_date, offset_id, peer) => {
563 console.log('get that sweet dialogs');
564 return currentDc.sendApiMessage('messages.getHistory', {
565 offset_date,
566 offset_id,
567 peer,
568 add_offset: 0,
569 limit: 20,
570 hash: 0,
571 max_id: 0,
572 min_id: 0,
573 });
574 },
575
576 getPassword: () => {
577 console.log('get that sweet dialogs');
578 return currentDc.sendApiMessage('account.getPassword', {});
579 },
580
581 getDataCenter: ()=>{
582 return currentDc;
583 },
584 checkPassword: (srpId, A, m1) => {
585 console.log('get that sweet dialogs');
586 return currentDc.sendApiMessage('auth.checkPassword', {
587 password: {
588 _: 'inputCheckPasswordSRP',
589 srp_id: srpId,
590 A: A,
591 M1: m1,
592 },
593 });
594 },
595};