Browse Source

no comments

master
Eduard Kuzmenko 3 years ago
parent
commit
e0999ad4d0
  1. 44
      package.json
  2. 50
      src/components/chat/bubbleGroups.ts
  3. 29
      src/components/chat/bubbles.ts
  4. 2
      src/components/chat/contextMenu.ts
  5. 2
      src/lib/appManagers/appMessagesManager.ts
  6. 2
      src/lib/appManagers/appStateManager.ts
  7. 6
      src/lib/appManagers/appUsersManager.ts
  8. 2
      src/lib/crypto/crypto_utils.ts
  9. 8
      src/lib/mtproto/apiManager.ts
  10. 30
      src/lib/mtproto/mtproto_config.ts
  11. 2
      src/lib/mtproto/mtprotoworker.ts
  12. 53
      src/lib/mtproto/networker.ts
  13. 4
      src/lib/mtproto/tl_utils.ts
  14. 4
      src/lib/mtproto/transports/intermediate.ts
  15. 4
      src/lib/mtproto/transports/obfuscation.ts
  16. 4
      src/lib/mtproto/transports/websocket.ts
  17. 92
      t.py
  18. 83
      t2.py
  19. 6
      webpack.common.js

44
package.json

@ -19,64 +19,64 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"jsbn": "^1.1.0", "jsbn": "^1.1.0",
"webpack-dev-server": "^3.11.0" "webpack-dev-server": "^3.11.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.12.1", "@babel/cli": "^7.12.13",
"@babel/core": "^7.12.3", "@babel/core": "^7.12.13",
"@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-class-properties": "^7.12.13",
"@babel/plugin-transform-typescript": "^7.12.1", "@babel/plugin-transform-typescript": "^7.12.13",
"@babel/polyfill": "^7.12.1", "@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.12.1", "@babel/preset-env": "^7.12.13",
"@babel/preset-es2015": "^7.0.0-beta.53", "@babel/preset-es2015": "^7.0.0-beta.53",
"@babel/preset-typescript": "^7.12.1", "@babel/preset-typescript": "^7.12.13",
"@cryptography/aes": "^0.1.1", "@cryptography/aes": "^0.1.1",
"@cryptography/sha1": "^0.1.0", "@cryptography/sha1": "^0.1.0",
"@cryptography/sha256": "^0.2.0", "@cryptography/sha256": "^0.2.0",
"@peculiar/webcrypto": "^1.1.3", "@peculiar/webcrypto": "^1.1.6",
"@types/aes-js": "^3.1.1", "@types/aes-js": "^3.1.1",
"@types/chrome": "0.0.91", "@types/chrome": "0.0.91",
"@types/jest": "^24.9.1", "@types/jest": "^24.9.1",
"@types/serviceworker-webpack-plugin": "^1.0.1", "@types/serviceworker-webpack-plugin": "^1.0.1",
"aes-js": "^3.1.2", "aes-js": "^3.1.2",
"autoprefixer": "^9.8.0", "autoprefixer": "^9.8.6",
"babel-jest": "^24.9.0", "babel-jest": "^24.9.0",
"babel-loader": "^8.1.0", "babel-loader": "^8.2.2",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"compression": "^1.7.4", "compression": "^1.7.4",
"compression-webpack-plugin": "^3.1.0", "compression-webpack-plugin": "^3.1.0",
"core-js": "^3.6.5", "core-js": "^3.8.3",
"css-loader": "^3.5.3", "css-loader": "^3.6.0",
"express": "^4.17.1", "express": "^4.17.1",
"fast-png": "^5.0.2", "fast-png": "^5.0.3",
"handlebars": "^4.7.6", "handlebars": "^4.7.6",
"handlebars-loader": "^1.7.1", "handlebars-loader": "^1.7.1",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"ifdef-loader": "^2.1.5", "ifdef-loader": "^2.1.5",
"install": "^0.13.0", "install": "^0.13.0",
"jest": "^24.9.0", "jest": "^24.9.0",
"media-query-plugin": "^1.3.1", "media-query-plugin": "^1.4.0",
"mini-css-extract-plugin": "^0.9.0", "mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.14.1", "node-sass": "^4.14.1",
"npm": "^6.14.10", "npm": "^6.14.11",
"on-build-webpack": "^0.1.0", "on-build-webpack": "^0.1.0",
"optimize-css-assets-webpack-plugin": "^5.0.3", "optimize-css-assets-webpack-plugin": "^5.0.4",
"pako": "^1.0.11", "pako": "^1.0.11",
"postcss": "^7.0.32", "postcss": "^7.0.35",
"postcss-loader": "^3.0.0", "postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0", "postcss-preset-env": "^6.7.0",
"qr-code-styling": "^1.0.1", "qr-code-styling": "^1.3.4",
"resolve-url-loader": "^3.1.2", "resolve-url-loader": "^3.1.2",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
"serviceworker-webpack-plugin": "^1.0.1", "serviceworker-webpack-plugin": "^1.0.1",
"style-loader": "^1.2.1", "style-loader": "^1.3.0",
"terser-webpack-plugin": "^3.0.2", "terser-webpack-plugin": "^3.1.0",
"ts-jest": "^24.3.0", "ts-jest": "^24.3.0",
"ts-loader": "^6.2.2", "ts-loader": "^6.2.2",
"typescript": "^3.9.7", "typescript": "^3.9.7",
"url-loader": "^2.3.0", "url-loader": "^2.3.0",
"webpack": "^4.43.0", "webpack": "^4.46.0",
"webpack-cli": "^3.3.11", "webpack-cli": "^3.3.12",
"webpack-merge": "^4.2.2", "webpack-merge": "^4.2.2",
"worker-loader": "^2.0.0" "worker-loader": "^2.0.0"
} }

50
src/components/chat/bubbleGroups.ts

@ -1,22 +1,23 @@
import rootScope from "../../lib/rootScope"; import rootScope from "../../lib/rootScope";
import { generatePathData } from "../../helpers/dom"; //import { generatePathData } from "../../helpers/dom";
import { MyMessage } from "../../lib/appManagers/appMessagesManager"; import { MyMessage } from "../../lib/appManagers/appMessagesManager";
import Chat from "./chat"; import Chat from "./chat";
type Group = {bubble: HTMLDivElement, mid: number, timestamp: number}[]; type Group = {bubble: HTMLElement, mid: number, timestamp: number}[];
type BubbleGroup = {timestamp: number, fromId: number, mid: number, group: Group}; type BubbleGroup = {timestamp: number, fromId: number, mid: number, group: Group};
export default class BubbleGroups { export default class BubbleGroups {
private bubbles: Array<BubbleGroup> = []; // map to group private bubbles: Array<BubbleGroup> = []; // map to group
private detailsMap: Map<HTMLElement, BubbleGroup> = new Map();
private groups: Array<Group> = []; private groups: Array<Group> = [];
//updateRAFs: Map<HTMLDivElement[], number> = new Map(); //updateRAFs: Map<HTMLElement[], number> = new Map();
private newGroupDiff = 121; // * 121 in scheduled messages private newGroupDiff = 121; // * 121 in scheduled messages
constructor(private chat: Chat) { constructor(private chat: Chat) {
} }
removeBubble(bubble: HTMLDivElement, mid: number) { removeBubble(bubble: HTMLElement) {
const details = this.bubbles.findAndSplice(g => g.mid === mid); const details = this.detailsMap.get(bubble);
if(details && details.group.length) { if(details && details.group.length) {
details.group.findAndSplice(d => d.bubble === bubble); details.group.findAndSplice(d => d.bubble === bubble);
if(!details.group.length) { if(!details.group.length) {
@ -27,7 +28,7 @@ export default class BubbleGroups {
} }
} }
addBubble(bubble: HTMLDivElement, message: MyMessage, reverse: boolean) { addBubble(bubble: HTMLElement, message: MyMessage, reverse: boolean) {
//return; //return;
const timestamp = message.date; const timestamp = message.date;
@ -41,7 +42,7 @@ export default class BubbleGroups {
} }
// try to find added // try to find added
//this.removeBubble(message.mid); this.removeBubble(bubble);
const insertObject = {bubble, mid, timestamp}; const insertObject = {bubble, mid, timestamp};
if(this.bubbles.length) { if(this.bubbles.length) {
@ -105,28 +106,24 @@ export default class BubbleGroups {
//console.log('[BUBBLE]: addBubble', bubble, message.mid, fromId, reverse, group); //console.log('[BUBBLE]: addBubble', bubble, message.mid, fromId, reverse, group);
if(mid > 0) { const bubbleGroup = {timestamp, fromId, mid: message.mid, group};
let insertIndex = 0; let insertIndex = 0;
for(; insertIndex < this.bubbles.length; ++insertIndex) { for(; insertIndex < this.bubbles.length; ++insertIndex) {
if(this.bubbles[insertIndex].mid < mid) { if(this.bubbles[insertIndex].mid < mid) {
break; break;
}
} }
this.bubbles.splice(insertIndex, 0, {timestamp, fromId, mid: message.mid, group});
} else {
this.bubbles.unshift({timestamp, fromId, mid: message.mid, group});
} }
//this.bubbles.push({timestamp, fromId, mid: message.mid, group}); this.bubbles.splice(insertIndex, 0, {timestamp, fromId, mid: message.mid, group});
this.updateGroup(group); this.updateGroup(group);
this.detailsMap.set(bubble, bubbleGroup);
} }
setClipIfNeeded(bubble: HTMLDivElement, remove = false) { /* setClipIfNeeded(bubble: HTMLDivElement, remove = false) {
//console.log('setClipIfNeeded', bubble, remove); //console.log('setClipIfNeeded', bubble, remove);
const className = bubble.className; const className = bubble.className;
if(className.includes('is-message-empty')/* && !className.includes('is-reply') */ if(className.includes('is-message-empty') && (className.includes('photo') || className.includes('video'))) {
&& (className.includes('photo') || className.includes('video'))) {
let container = bubble.querySelector('.bubble__media-container') as SVGSVGElement; let container = bubble.querySelector('.bubble__media-container') as SVGSVGElement;
//console.log('setClipIfNeeded', bubble, remove, container); //console.log('setClipIfNeeded', bubble, remove, container);
if(!container) return; if(!container) return;
@ -171,7 +168,7 @@ export default class BubbleGroups {
}); });
} catch(err) {} } catch(err) {}
} }
} } */
updateGroup(group: Group) { updateGroup(group: Group) {
/* if(this.updateRAFs.has(group)) { /* if(this.updateRAFs.has(group)) {
@ -192,25 +189,25 @@ export default class BubbleGroups {
if(group.length === 1) { if(group.length === 1) {
first.classList.add('is-group-first', 'is-group-last'); first.classList.add('is-group-first', 'is-group-last');
this.setClipIfNeeded(first); //this.setClipIfNeeded(first);
return; return;
} else { } else {
first.classList.remove('is-group-last'); first.classList.remove('is-group-last');
first.classList.add('is-group-first'); first.classList.add('is-group-first');
this.setClipIfNeeded(first, true); //this.setClipIfNeeded(first, true);
} }
const length = group.length - 1; const length = group.length - 1;
for(let i = 1; i < length; ++i) { for(let i = 1; i < length; ++i) {
const bubble = group[i].bubble; const bubble = group[i].bubble;
bubble.classList.remove('is-group-last', 'is-group-first'); bubble.classList.remove('is-group-last', 'is-group-first');
this.setClipIfNeeded(bubble, true); //this.setClipIfNeeded(bubble, true);
} }
const last = group[group.length - 1].bubble; const last = group[group.length - 1].bubble;
last.classList.remove('is-group-first'); last.classList.remove('is-group-first');
last.classList.add('is-group-last'); last.classList.add('is-group-last');
this.setClipIfNeeded(last); //this.setClipIfNeeded(last);
//})); //}));
} }
@ -224,6 +221,7 @@ export default class BubbleGroups {
cleanup() { cleanup() {
this.bubbles = []; this.bubbles = [];
this.groups = []; this.groups = [];
this.detailsMap.clear();
/* for(let value of this.updateRAFs.values()) { /* for(let value of this.updateRAFs.values()) {
window.cancelAnimationFrame(value); window.cancelAnimationFrame(value);
} }

29
src/components/chat/bubbles.ts

@ -152,12 +152,9 @@ export default class ChatBubbles {
const message = this.chat.getMessage(mid); const message = this.chat.getMessage(mid);
//bubble.remove(); //bubble.remove();
this.bubbleGroups.removeBubble(bubble, message.mid);
this.setBubblePosition(bubble, message, false); this.setBubblePosition(bubble, message, false);
//this.log('history_update', this.bubbles[mid], mid, message); //this.log('history_update', this.bubbles[mid], mid, message);
this.bubbleGroups.addBubble(bubble, message, false);
if(this.scrollingToNewBubble) { if(this.scrollingToNewBubble) {
this.scrollToNewLastBubble(); this.scrollToNewLastBubble();
} }
@ -268,8 +265,6 @@ export default class ChatBubbles {
//}); //});
bubble.dataset.mid = '' + mid; bubble.dataset.mid = '' + mid;
this.bubbleGroups.removeBubble(bubble, tempId);
} }
if(this.unreadOut.has(tempId)) { if(this.unreadOut.has(tempId)) {
@ -423,7 +418,7 @@ export default class ChatBubbles {
const msgIdsByPeer = e; const msgIdsByPeer = e;
if(!(this.peerId in msgIdsByPeer)) return; if(!(this.peerId in msgIdsByPeer)) return;
const msgIds = msgIdsByPeer[this.peerId]; const msgIds = (msgIdsByPeer[this.peerId] as number[]).slice().sort((a, b) => b - a);
this.renderNewMessagesByIds(msgIds); this.renderNewMessagesByIds(msgIds);
}); });
@ -1147,7 +1142,7 @@ export default class ChatBubbles {
this.firstUnreadBubble = null; this.firstUnreadBubble = null;
} }
this.bubbleGroups.removeBubble(bubble, mid); this.bubbleGroups.removeBubble(bubble);
if(this.unreadedObserver) { if(this.unreadedObserver) {
this.unreadedObserver.unobserve(bubble); this.unreadedObserver.unobserve(bubble);
} }
@ -1691,6 +1686,8 @@ export default class ChatBubbles {
dateMessage.container.append(bubble); dateMessage.container.append(bubble);
} }
} }
this.bubbleGroups.addBubble(bubble, message, reverse);
} }
// * will change .cleaned in cleanup() and new instance will be created // * will change .cleaned in cleanup() and new instance will be created
@ -1779,16 +1776,8 @@ export default class ChatBubbles {
bubble.remove(); // * for positionElementByIndex bubble.remove(); // * for positionElementByIndex
} */ } */
if(!sameMid || updatePosition) {
this.bubbleGroups.removeBubble(bubble, originalMid);
}
if(!sameMid) { if(!sameMid) {
delete this.bubbles[originalMid]; delete this.bubbles[originalMid];
if(!updatePosition) {
this.bubbleGroups.addBubble(bubble, message, reverse);
}
} }
//bubble.innerHTML = ''; //bubble.innerHTML = '';
@ -1815,9 +1804,7 @@ export default class ChatBubbles {
if(updatePosition) { if(updatePosition) {
this.renderMessagesQueue(message, bubble, reverse, loadPromises); this.renderMessagesQueue(message, bubble, reverse, loadPromises);
if(!message.pFlags.is_single) { // * Ignore 'Discussion started' if(message.pFlags.is_single) { // * Ignore 'Discussion started'
this.bubbleGroups.addBubble(bubble, message, reverse);
} else {
bubble.classList.add('is-group-last'); bubble.classList.add('is-group-last');
} }
} }
@ -2452,13 +2439,7 @@ export default class ChatBubbles {
bubble.classList.add(isOut ? 'is-out' : 'is-in'); bubble.classList.add(isOut ? 'is-out' : 'is-in');
if(updatePosition) { if(updatePosition) {
if(!isThreadStarter) {
this.bubbleGroups.addBubble(bubble, message, reverse);
}
this.renderMessagesQueue(message, bubble, reverse, loadPromises); this.renderMessagesQueue(message, bubble, reverse, loadPromises);
} else {
this.bubbleGroups.updateGroupByMessageId(message.mid);
} }
if(withReplies) { if(withReplies) {

2
src/components/chat/contextMenu.ts

@ -215,7 +215,7 @@ export default class ChatContextMenu {
icon: 'link', icon: 'link',
text: 'Copy Link', text: 'Copy Link',
onClick: this.onCopyLinkClick, onClick: this.onCopyLinkClick,
verify: () => this.appPeersManager.isChannel(this.peerId) verify: () => this.appPeersManager.isChannel(this.peerId) && !this.message.pFlags.is_outgoing
}, { }, {
icon: 'pin', icon: 'pin',
text: 'Pin', text: 'Pin',

2
src/lib/appManagers/appMessagesManager.ts

@ -3313,7 +3313,7 @@ export class AppMessagesManager {
} as any, } as any,
id: this.generateMessageId(message.id, true), id: this.generateMessageId(message.id, true),
date: message.date, date: message.date,
from_id: message.from_id, from_id: {_: 'peerUser', user_id: 0}/* message.from_id */,
peer_id: message.peer_id, peer_id: message.peer_id,
action: { action: {
_: 'messageActionCustomAction', _: 'messageActionCustomAction',

2
src/lib/appManagers/appStateManager.ts

@ -156,7 +156,7 @@ export class AppStateManager extends EventListenerBase<{
if(state) { if(state) {
if(state.version !== STATE_VERSION) { if(state.version !== STATE_VERSION) {
state = copy(STATE_INIT); state = copy(STATE_INIT);
} else if((state.stateCreatedTime + REFRESH_EVERY) < time/* && false */) { } else if((state.stateCreatedTime + REFRESH_EVERY) < time/* || true *//* && false */) {
if(DEBUG) { if(DEBUG) {
this.log('will refresh state', state.stateCreatedTime, time); this.log('will refresh state', state.stateCreatedTime, time);
} }

6
src/lib/appManagers/appUsersManager.ts

@ -118,7 +118,9 @@ export class AppUsersManager {
this.pushContact(userId); this.pushContact(userId);
}); });
this.contactsFillPromise = Promise.resolve(this.contactsList); if(this.contactsList.size) {
this.contactsFillPromise = Promise.resolve(this.contactsList);
}
} }
}); });
} }
@ -605,7 +607,7 @@ export class AppUsersManager {
return apiManager.invokeApi('contacts.getTopPeers', { return apiManager.invokeApi('contacts.getTopPeers', {
correspondents: true, correspondents: true,
offset: 0, offset: 0,
limit: 30, limit: 15,
hash: 0, hash: 0,
}).then((result) => { }).then((result) => {
let peerIds: number[] = []; let peerIds: number[] = [];

2
src/lib/crypto/crypto_utils.ts

@ -16,7 +16,7 @@ import { addPadding, bytesFromBigInt } from '../mtproto/bin_utils';
import { bytesToWordss, bytesFromWordss, bytesToHex, bytesFromHex } from '../../helpers/bytes'; import { bytesToWordss, bytesFromWordss, bytesToHex, bytesFromHex } from '../../helpers/bytes';
import { nextRandomInt } from '../../helpers/random'; import { nextRandomInt } from '../../helpers/random';
export function longToBytes(sLong: string) { export function longToBytes(sLong: string): Array<number> {
/* let perf = performance.now(); /* let perf = performance.now();
for(let i = 0; i < 1000000; ++i) { for(let i = 0; i < 1000000; ++i) {
bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse(); bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse();

8
src/lib/mtproto/apiManager.ts

@ -157,12 +157,12 @@ export class ApiManager {
// mtpGetNetworker // mtpGetNetworker
public getNetworker(dcId: number, options: InvokeApiOptions = {}): Promise<MTPNetworker> { public getNetworker(dcId: number, options: InvokeApiOptions = {}): Promise<MTPNetworker> {
const connectionType: ConnectionType = options.fileDownload ? 'download' : (options.fileUpload ? 'upload' : 'client'); //const connectionType: ConnectionType = options.fileDownload ? 'download' : (options.fileUpload ? 'upload' : 'client');
//const connectionType: ConnectionType = 'client'; const connectionType: ConnectionType = 'client';
/// #if MTPROTO_HTTP_UPLOAD /// #if MTPROTO_HTTP_UPLOAD
// @ts-ignore // @ts-ignore
const transportType: TransportType = connectionType === 'upload' && isSafari ? 'https' : 'websocket'; const transportType: TransportType = connectionType === 'upload' && isSafari && false ? 'https' : 'websocket';
//const transportType: TransportType = connectionType !== 'client' ? 'https' : 'websocket'; //const transportType: TransportType = connectionType !== 'client' ? 'https' : 'websocket';
/// #else /// #else
// @ts-ignore // @ts-ignore
@ -183,7 +183,7 @@ export class ApiManager {
} }
const networkers = cache[dcId]; const networkers = cache[dcId];
if(networkers.length >= /* 1 */(connectionType === 'client' || transportType === 'https' ? 1 : (connectionType === 'download' ? 3 : 3))) { if(networkers.length >= /* 1 */(connectionType === 'client' || transportType === 'https' ? 1 : (connectionType === 'download' ? 3 : 1))) {
let i = networkers.length - 1, found = false; let i = networkers.length - 1, found = false;
for(; i >= 0; --i) { for(; i >= 0; --i) {
if(networkers[i].isOnline) { if(networkers[i].isOnline) {

30
src/lib/mtproto/mtproto_config.ts

@ -34,3 +34,33 @@ export const Database = {
export const DEBUG = process.env.NODE_ENV !== 'production' || Modes.debug; export const DEBUG = process.env.NODE_ENV !== 'production' || Modes.debug;
export const MOUNT_CLASS_TO: any = DEBUG ? (typeof(window) !== 'undefined' ? window : self) : null; export const MOUNT_CLASS_TO: any = DEBUG ? (typeof(window) !== 'undefined' ? window : self) : null;
export const superDebug = (object: any, key: string) => {
var d = object[key];
var beforeStr = '', afterStr = '';
for(var r of d) {
beforeStr += r.before.hex + '\n';
afterStr += r.after.hex + '\n';
}
beforeStr = beforeStr.trim();
afterStr = afterStr.trim();
//var beforeStr = d.map(r => r.before.hex).join('\n');
//var afterStr = d.map(r => r.after.hex).join('\n');
var dada = (name: string, str: string) => {
var a = document.createElement('a');
a.target = '_blank';
a.download = name + '.txt';
a.href = URL.createObjectURL(new Blob([str], {
type: 'text/plain'
}));
document.body.append(a);
a.click();
};
dada(key + '_' + 'before', beforeStr);
dada(key + '_' + 'after', afterStr);
}
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.superDebug = superDebug);

2
src/lib/mtproto/mtprotoworker.ts

@ -124,7 +124,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
//const worker = window; //const worker = window;
worker.addEventListener('message', (e) => { worker.addEventListener('message', (e) => {
if(!this.worker) { if(!this.worker) {
this.worker = worker; this.worker = worker as any;
this.log('set webWorker'); this.log('set webWorker');
if(USE_WORKER_AS_WORKER) { if(USE_WORKER_AS_WORKER) {

53
src/lib/mtproto/networker.ts

@ -100,8 +100,9 @@ export default class MTPNetworker {
/// #endif /// #endif
private seqNo: number = 0; private seqNo: number = 0;
private prevSessionId: Array<number> = []; private prevSessionId: Uint8Array;
private sessionId: Array<number> = []; private sessionId: Uint8Array;
private serverSalt: Uint8Array;
private lastResendReq: { private lastResendReq: {
req_msg_id: string, req_msg_id: string,
@ -119,9 +120,12 @@ export default class MTPNetworker {
private tt = 0; private tt = 0;
//public onConnectionStatusChange: (online: boolean) => void; //public onConnectionStatusChange: (online: boolean) => void;
private debugRequests: Array<{before: Uint8Array, after: Uint8Array}> = [];
constructor(public dcId: number, private authKey: number[], private authKeyId: Uint8Array, constructor(public dcId: number, private authKey: number[], private authKeyId: Uint8Array,
private serverSalt: number[], private transport: MTTransport, options: InvokeApiOptions = {}) { serverSalt: number[], private transport: MTTransport, options: InvokeApiOptions = {}) {
this.authKeyUint8 = convertToUint8Array(this.authKey); this.authKeyUint8 = convertToUint8Array(this.authKey);
this.serverSalt = convertToUint8Array(serverSalt);
this.isFileUpload = !!options.fileUpload; this.isFileUpload = !!options.fileUpload;
this.isFileDownload = !!options.fileDownload; this.isFileDownload = !!options.fileDownload;
@ -179,7 +183,7 @@ export default class MTPNetworker {
public updateSession() { public updateSession() {
this.seqNo = 0; this.seqNo = 0;
this.prevSessionId = this.sessionId; this.prevSessionId = this.sessionId;
this.sessionId = [...new Uint8Array(8).randomize()]; this.sessionId = new Uint8Array(8).randomize();
} }
public updateSentMessage(sentMessageId: string) { public updateSentMessage(sentMessageId: string) {
@ -231,7 +235,7 @@ export default class MTPNetworker {
const message = { const message = {
msg_id: messageId, msg_id: messageId,
seq_no: seqNo, seq_no: seqNo,
body: serializer.getBytes() body: serializer.getBytes(true)
}; };
if(Modes.debug) { if(Modes.debug) {
@ -250,7 +254,7 @@ export default class MTPNetworker {
const message = { const message = {
msg_id: messageId, msg_id: messageId,
seq_no: seqNo, seq_no: seqNo,
body: serializer.getBytes() body: serializer.getBytes(true)
}; };
if(Modes.debug) { if(Modes.debug) {
@ -335,7 +339,7 @@ export default class MTPNetworker {
} }
private sendPingDelayDisconnect = () => { private sendPingDelayDisconnect = () => {
if(this.pingPromise) return; if(this.pingPromise || true) return;
if(!this.isOnline) { if(!this.isOnline) {
if((this.transport as Socket).connected) { if((this.transport as Socket).connected) {
@ -667,28 +671,28 @@ export default class MTPNetworker {
this.scheduleRequest(delay); this.scheduleRequest(delay);
} }
// * correct, fully checked
public async getMsgKey(dataWithPadding: ArrayBuffer, isOut: boolean) { public async getMsgKey(dataWithPadding: ArrayBuffer, isOut: boolean) {
const authKey = this.authKeyUint8;
const x = isOut ? 0 : 8 const x = isOut ? 0 : 8
const msgKeyLargePlain = bufferConcat(authKey.subarray(88 + x, 88 + x + 32), dataWithPadding); const msgKeyLargePlain = bufferConcat(this.authKeyUint8.subarray(88 + x, 88 + x + 32), dataWithPadding);
const msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain); const msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain);
const msgKey = new Uint8Array(msgKeyLarge).subarray(8, 24); const msgKey = new Uint8Array(msgKeyLarge).subarray(8, 24);
return msgKey; return msgKey;
}; };
// * correct, fully checked
public getAesKeyIv(msgKey: Uint8Array | number[], isOut: boolean): Promise<[Uint8Array, Uint8Array]> { public getAesKeyIv(msgKey: Uint8Array | number[], isOut: boolean): Promise<[Uint8Array, Uint8Array]> {
const authKey = this.authKeyUint8;
const x = isOut ? 0 : 8; const x = isOut ? 0 : 8;
const sha2aText = new Uint8Array(52); const sha2aText = new Uint8Array(52);
const sha2bText = new Uint8Array(52); const sha2bText = new Uint8Array(52);
const promises: Array<Promise<number[]>> = []; const promises: Array<Promise<number[]>> = [];
sha2aText.set(msgKey, 0); sha2aText.set(msgKey, 0);
sha2aText.set(authKey.subarray(x, x + 36), 16); sha2aText.set(this.authKeyUint8.subarray(x, x + 36), 16);
promises.push(CryptoWorker.sha256Hash(sha2aText)); promises.push(CryptoWorker.sha256Hash(sha2aText));
sha2bText.set(authKey.subarray(40 + x, 40 + x + 36), 0); sha2bText.set(this.authKeyUint8.subarray(40 + x, 40 + x + 36), 0);
sha2bText.set(msgKey, 36); sha2bText.set(msgKey, 36);
promises.push(CryptoWorker.sha256Hash(sha2bText)); promises.push(CryptoWorker.sha256Hash(sha2bText));
@ -900,7 +904,7 @@ export default class MTPNetworker {
return { return {
bytes: encryptedBytes, bytes: encryptedBytes,
msgKey: msgKey msgKey
}; };
} }
@ -934,6 +938,14 @@ export default class MTPNetworker {
data.storeInt(message.body.length, 'message_data_length'); data.storeInt(message.body.length, 'message_data_length');
data.storeRawBytes(message.body, 'message_data'); data.storeRawBytes(message.body, 'message_data');
const des = new TLDeserialization(data.getBuffer().slice(16));
const desSalt = des.fetchLong();
const desSessionId = des.fetchLong();
if(!this.isOnline) {
this.log.error('trying to send message when offline', message, new Uint8Array(des.buffer), desSalt, desSessionId);
}
/* const messageDataLength = message.body.length; /* const messageDataLength = message.body.length;
let canBeLength = 0; // bytes let canBeLength = 0; // bytes
canBeLength += 8; canBeLength += 8;
@ -950,13 +962,15 @@ export default class MTPNetworker {
} */ } */
const paddingLength = (16 - (data.offset % 16)) + 16 * (1 + nextRandomInt(5)); const paddingLength = (16 - (data.offset % 16)) + 16 * (1 + nextRandomInt(5));
const padding = [...new Uint8Array(paddingLength).randomize()]; const padding = (message as any).padding || new Uint8Array(paddingLength).randomize().fill(0);
/* const padding = [167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157, /* const padding = [167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157,
51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150, 51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150,
94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28, 167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157, 94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28, 167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157,
51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150, 51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150,
94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28].slice(0, paddingLength); */ 94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28].slice(0, paddingLength); */
(message as any).padding = padding;
const dataWithPadding = bufferConcat(dataBuffer, padding); const dataWithPadding = bufferConcat(dataBuffer, padding);
// this.log('Adding padding', dataBuffer, padding, dataWithPadding) // this.log('Adding padding', dataBuffer, padding, dataWithPadding)
// this.log('auth_key_id', bytesToHex(self.authKeyID)) // this.log('auth_key_id', bytesToHex(self.authKeyID))
@ -969,6 +983,7 @@ export default class MTPNetworker {
this.log('Send encrypted: body length:', (message.body as ArrayBuffer).byteLength, paddingLength, dataWithPadding); this.log('Send encrypted: body length:', (message.body as ArrayBuffer).byteLength, paddingLength, dataWithPadding);
} */ } */
// * full next block is correct
return this.getEncryptedMessage(dataWithPadding).then((encryptedResult) => { return this.getEncryptedMessage(dataWithPadding).then((encryptedResult) => {
/* if(DEBUG) { /* if(DEBUG) {
this.log('Got encrypted out message', encryptedResult); this.log('Got encrypted out message', encryptedResult);
@ -983,10 +998,12 @@ export default class MTPNetworker {
const requestData = request.getBytes(true); const requestData = request.getBytes(true);
//if(message.fileUpload) { if(this.isFileNetworker) {
//this.log('Send encrypted: requestData length:', requestData.length, requestData.length % 16, paddingLength % 16, paddingLength, data.offset, encryptedResult.msgKey.length % 16, encryptedResult.bytes.length % 16); //this.log('Send encrypted: requestData length:', requestData.length, requestData.length % 16, paddingLength % 16, paddingLength, data.offset, encryptedResult.msgKey.length % 16, encryptedResult.bytes.length % 16);
//this.log('Send encrypted: messageId:', message.msg_id, requestData.length); //this.log('Send encrypted: messageId:', message.msg_id, requestData.length);
//} //this.log('Send encrypted:', message, new Uint8Array(bufferConcat(des.buffer, padding)), requestData, this.serverSalt.hex, this.sessionId.hex/* new Uint8Array(des.buffer) */);
this.debugRequests.push({before: new Uint8Array(bufferConcat(des.buffer, padding)), after: requestData});
}
return requestData; return requestData;
}); });
@ -1137,7 +1154,7 @@ export default class MTPNetworker {
return { return {
response, response,
messageId, messageId,
sessionId: sessionId, sessionId,
seqNo seqNo
}; };
}); });
@ -1151,7 +1168,7 @@ export default class MTPNetworker {
['dc' + this.dcId + '_server_salt']: bytesToHex(serverSalt) ['dc' + this.dcId + '_server_salt']: bytesToHex(serverSalt)
}); });
this.serverSalt = serverSalt; this.serverSalt = new Uint8Array(serverSalt);
} }
// ! таймаут очень сильно тормозит скорость работы сокета (даже нулевой) // ! таймаут очень сильно тормозит скорость работы сокета (даже нулевой)

4
src/lib/mtproto/tl_utils.ts

@ -1,5 +1,6 @@
import { bytesToHex } from '../../helpers/bytes'; import { bytesToHex } from '../../helpers/bytes';
import { bigint, bigStringInt, isObject } from './bin_utils'; import { bigint, bigStringInt, isObject } from './bin_utils';
import { MOUNT_CLASS_TO } from './mtproto_config';
/// #if MTPROTO_WORKER /// #if MTPROTO_WORKER
// @ts-ignore // @ts-ignore
import { gzipUncompress } from '../crypto/crypto_utils'; import { gzipUncompress } from '../crypto/crypto_utils';
@ -789,5 +790,6 @@ class TLDeserialization {
} }
} }
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.TLDeserialization = TLDeserialization);
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.TLSerialization = TLSerialization);
export { TLDeserialization, TLSerialization }; export { TLDeserialization, TLSerialization };

4
src/lib/mtproto/transports/intermediate.ts

@ -4,13 +4,15 @@ export class IntermediatePacketCodec implements Codec {
public tag = 0xee; public tag = 0xee;
public obfuscateTag = new Uint8Array([this.tag, this.tag, this.tag, this.tag]); public obfuscateTag = new Uint8Array([this.tag, this.tag, this.tag, this.tag]);
//private lol = 0;
public encodePacket(data: Uint8Array) { public encodePacket(data: Uint8Array) {
if((data.length % 4) !== 0) { if((data.length % 4) !== 0) {
console.error('Encode error!', data.length, data); console.error('Encode error!', data.length, data);
} }
const len = data.length; const len = data.length;
const header = new Uint8Array(new Uint32Array([len]).buffer); const header = new Uint8Array(new Int32Array([/* ++this.lol >= 25 ? 0x80000001 : */len]).buffer);
//console.log('got nobody cause im braindead', header, len, /* data, */data.buffer.byteLength === data.length); //console.log('got nobody cause im braindead', header, len, /* data, */data.buffer.byteLength === data.length);
return header.concat(data); return header.concat(data);

4
src/lib/mtproto/transports/obfuscation.ts

@ -95,8 +95,8 @@ export default class Obfuscation {
/* this.enc = new aesjs.ModeOfOperation.ctr(encKey, new aesjs.Counter(encIv as any)); /* this.enc = new aesjs.ModeOfOperation.ctr(encKey, new aesjs.Counter(encIv as any));
this.dec = new aesjs.ModeOfOperation.ctr(decKey, new aesjs.Counter(decIv as any)); */ this.dec = new aesjs.ModeOfOperation.ctr(decKey, new aesjs.Counter(decIv as any)); */
/* console.log('encKey', encKey, encIv); console.log('encKey', encKey.hex, encIv.hex);
console.log('decKey', decKey, decIv); */ console.log('decKey', decKey.hex, decIv.hex);
this.encNew = new CTR(encKey, encIv); this.encNew = new CTR(encKey, encIv);
this.decNew = new CTR(decKey, decIv); this.decNew = new CTR(decKey, decIv);

4
src/lib/mtproto/transports/websocket.ts

@ -34,6 +34,8 @@ export default class Socket extends MTTransport {
//private lol: Uint8Array[] = []; //private lol: Uint8Array[] = [];
//private dd: () => void; //private dd: () => void;
//private debugPayloads: MTPNetworker['debugRequests'] = [];
constructor(dcId: number, url: string, logSuffix: string, public retryTimeout: number) { constructor(dcId: number, url: string, logSuffix: string, public retryTimeout: number) {
super(dcId, url); super(dcId, url);
@ -220,6 +222,8 @@ export default class Socket extends MTTransport {
const enc = this.obfuscation.encode(toEncode); const enc = this.obfuscation.encode(toEncode);
//this.log('send after obf:', enc.hex); //this.log('send after obf:', enc.hex);
//this.debugPayloads.push({before: body.slice(), after: enc});
this.debug && this.log.debug('-> body length to send:', enc.length, this.ws.bufferedAmount); this.debug && this.log.debug('-> body length to send:', enc.length, this.ws.bufferedAmount);
/* if(this.ws.bufferedAmount) { /* if(this.ws.bufferedAmount) {
this.log.error('bufferedAmount:', this.ws.bufferedAmount); this.log.error('bufferedAmount:', this.ws.bufferedAmount);

92
t.py

@ -1,8 +1,16 @@
from telethon import TelegramClient, events, sync from telethon import TelegramClient, events, sync
from telethon.network import ConnectionTcpAbridged from telethon.network import ConnectionTcpAbridged
from telethon.network.mtprotostate import MTProtoState
from telethon.crypto.authkey import AuthKey
from telethon.network.connection.tcpobfuscated import ConnectionTcpObfuscated
from telethon.network.connection.tcpintermediate import IntermediatePacketCodec
import sys import struct
print(sys.path) import logging
logging.basicConfig(level=logging.DEBUG)
# import sys
# print(sys.path)
# These example values won't work. You must get your own api_id and # These example values won't work. You must get your own api_id and
# api_hash from https://my.telegram.org, under API Development. # api_hash from https://my.telegram.org, under API Development.
@ -11,4 +19,82 @@ api_hash = '452b0359b988148995f22ff0f4229750'
client = TelegramClient('session_name', api_id, api_hash, connection=ConnectionTcpAbridged) client = TelegramClient('session_name', api_id, api_hash, connection=ConnectionTcpAbridged)
#client.start() #client.start()
client.connect() #client.connect()
authKey = AuthKey(bytes.fromhex("2c45ed62da34d41bc82b6ef660760d3b01cb80028b0c363633f1c40ec80d5f4ad9d4ddee646736b6a9465fd7258b8936dedde6a86c69b4a03277ed6c543ccfd31fa8fa9f9449d686b4822b54542c91255231ecfa6aed4a9896cdcc3d5491e7f1b7529cbff75597f262813ced7eb3b5ac1e7e6794ca7aa00c5ea4d1d714e7c276fb63e5f8a1742cf4707eace8a6ec2ed4dcceceabdc7fc92bd099c811931e0da337f1b0271d8ba33e7a4a22fa1e4a913d8220edb0dd3a810ed408f03fe5d7f2d5dd43db4272d314b20a22376c03b0a05423600f4d012e7196ce9b3c45b28a75dbfa053222f5c86d1c6a706a2fd68ca270fd9a6449df1fa15a21837d34f4cf4e95"))
state = MTProtoState(authKey, loggers=client._log)
state.salt = struct.unpack('q', bytes.fromhex("d1b37eebb4997ca9"))[0]
state.id = struct.unpack('q', bytes.fromhex("502368ee46d39313"))[0]
#print(state.id)
""" with open('file_part.txt', 'r') as file:
data = file.read()
messageData = bytes.fromhex(data)
#messageData = bytes.fromhex("146f8265b5342560290000000c000000ec77be7aaa40f90300000000")
#print(data)
encrypted = state.encrypt_message_data(messageData)
file.close()
with open('file_part_encrypted.txt', 'w') as file:
file.write(encrypted.hex())
file.close()
#print(encrypted) """
ConnectionTcpObfuscated.packet_codec = IntermediatePacketCodec
connection = ConnectionTcpObfuscated("", "", 2, loggers=client._log)
obfuscatedIo = connection.obfuscated_io(connection)
#print(encrypted)
#exit()
#encoded = connection.packet_codec.encode_packet(connection.packet_codec, bytes.fromhex("deadbeef"))
#print(encoded)
#exit()
name = "debugPayloads"
#name = "debugRequests"
with open(name + '_before.txt', 'r') as fileBefore:
data = fileBefore.read()
lines = data.splitlines()
with open(name + '_after.txt', 'r') as fileAfter:
data = fileAfter.read()
linesAfter = data.splitlines()
length = len(lines)
for i in range(length):
print("processing line %i, of %i", i, length)
lineBefore = lines[i]
lineAfter = linesAfter[i]
messageData = bytes.fromhex(lineBefore)
messageData = connection.packet_codec.encode_packet(connection.packet_codec, messageData)
encrypted = obfuscatedIo._encrypt.encrypt(messageData)
#encrypted = state.encrypt_message_data(messageData) # need to comment padding inside
#print(encrypted.hex())
#print(len(encrypted.hex()))
#print(len(lineAfter))
#print(encrypted.hex() == lineAfter)
#break
if encrypted.hex() != lineAfter:
print("lol")
with open('difference_before.txt', 'w') as file:
file.write(lineAfter)
file.close()
with open('difference_after.txt', 'w') as file:
file.write(encrypted.hex())
file.close()
#print(encrypted.hex())
#print(lineAfter)
exit()

83
t2.py

@ -0,0 +1,83 @@
from telethon import TelegramClient, events, sync
from telethon.network import ConnectionTcpAbridged
from telethon.network.mtprotostate import MTProtoState
from telethon.crypto.authkey import AuthKey
import struct
import logging
logging.basicConfig(level=logging.DEBUG)
# import sys
# print(sys.path)
# These example values won't work. You must get your own api_id and
# api_hash from https://my.telegram.org, under API Development.
api_id = 1025907
api_hash = '452b0359b988148995f22ff0f4229750'
client = TelegramClient('session_name', api_id, api_hash, connection=ConnectionTcpAbridged)
#client.start()
#client.connect()
authKey = AuthKey(bytes.fromhex("2c45ed62da34d41bc82b6ef660760d3b01cb80028b0c363633f1c40ec80d5f4ad9d4ddee646736b6a9465fd7258b8936dedde6a86c69b4a03277ed6c543ccfd31fa8fa9f9449d686b4822b54542c91255231ecfa6aed4a9896cdcc3d5491e7f1b7529cbff75597f262813ced7eb3b5ac1e7e6794ca7aa00c5ea4d1d714e7c276fb63e5f8a1742cf4707eace8a6ec2ed4dcceceabdc7fc92bd099c811931e0da337f1b0271d8ba33e7a4a22fa1e4a913d8220edb0dd3a810ed408f03fe5d7f2d5dd43db4272d314b20a22376c03b0a05423600f4d012e7196ce9b3c45b28a75dbfa053222f5c86d1c6a706a2fd68ca270fd9a6449df1fa15a21837d34f4cf4e95"))
state = MTProtoState(authKey, loggers=client._log)
state.salt = struct.unpack('q', bytes.fromhex("d1b37eebb4997ca9"))[0]
state.id = struct.unpack('q', bytes.fromhex("502368ee46d39313"))[0]
print(state.id)
""" with open('file_part.txt', 'r') as file:
data = file.read()
messageData = bytes.fromhex(data)
#messageData = bytes.fromhex("146f8265b5342560290000000c000000ec77be7aaa40f90300000000")
#print(data)
encrypted = state.encrypt_message_data(messageData)
file.close()
with open('file_part_encrypted.txt', 'w') as file:
file.write(encrypted.hex())
file.close()
#print(encrypted) """
with open('debugRequests_before.txt', 'r') as fileBefore:
data = fileBefore.read()
lines = data.splitlines()
with open('debugRequests_after.txt', 'r') as fileAfter:
data = fileAfter.read()
linesAfter = data.splitlines()
length = len(lines)
for i in range(length):
print("processing line %i, of %i", i, length)
lineBefore = lines[i]
messageData = bytes.fromhex(lineBefore)
encrypted = state.encrypt_message_data(messageData) # need to comment padding inside
lineAfter = linesAfter[i]
#print(len(encrypted.hex()))
#print(len(lineAfter))
#print(encrypted.hex() == lineAfter)
#break
if encrypted.hex() != lineAfter:
print("lol")
with open('difference_before.txt', 'w') as file:
file.write(lineAfter)
file.close()
with open('difference_after.txt', 'w') as file:
file.write(encrypted.hex())
file.close()
#print(encrypted.hex())
#print(lineAfter)
exit()

6
webpack.common.js

@ -8,8 +8,8 @@ const fs = require('fs');
const allowedIPs = ['194.58.97.147', '195.66.140.39', '127.0.0.1', '176.100.8.254']; const allowedIPs = ['194.58.97.147', '195.66.140.39', '127.0.0.1', '176.100.8.254'];
const devMode = process.env.NODE_ENV !== 'production'; const devMode = process.env.NODE_ENV !== 'production';
const useLocal = false; const useLocal = true;
const useLocalNotLocal = false; const useLocalNotLocal = true;
if(devMode) { if(devMode) {
console.log('DEVMODE IS ON!'); console.log('DEVMODE IS ON!');
@ -103,7 +103,7 @@ module.exports = {
allowedHosts: useLocal ? undefined : [ allowedHosts: useLocal ? undefined : [
'tweb.enko.club' 'tweb.enko.club'
], ],
host: useLocalNotLocal ? '192.168.93.183' : (useLocal ? undefined : '0.0.0.0'), host: useLocalNotLocal ? '192.168.93.209' : (useLocal ? undefined : '0.0.0.0'),
public: useLocal ? undefined : 'tweb.enko.club', public: useLocal ? undefined : 'tweb.enko.club',
//host: '192.168.0.105', // '0.0.0.0' //host: '192.168.0.105', // '0.0.0.0'
//host: 'tweb.enko.club', // '0.0.0.0' //host: 'tweb.enko.club', // '0.0.0.0'

Loading…
Cancel
Save