@mtkruto/node 0.161.0 → 0.171.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/0_deps.d.ts +1 -1
- package/esm/0_deps.d.ts.map +1 -1
- package/esm/0_deps.js +1 -1
- package/esm/2_tl.d.ts +1 -0
- package/esm/2_tl.d.ts.map +1 -1
- package/esm/2_tl.js +1 -0
- package/esm/3_types.d.ts +3 -0
- package/esm/3_types.d.ts.map +1 -1
- package/esm/3_types.js +3 -0
- package/esm/client/0_markdown.d.ts.map +1 -1
- package/esm/client/0_params.d.ts +39 -5
- package/esm/client/0_params.d.ts.map +1 -1
- package/esm/client/0_secret_chat_state.d.ts +86 -0
- package/esm/client/0_secret_chat_state.d.ts.map +1 -0
- package/esm/client/0_secret_chat_state.js +129 -0
- package/esm/client/0_storage_operations.d.ts +1 -0
- package/esm/client/0_storage_operations.d.ts.map +1 -1
- package/esm/client/0_storage_operations.js +3 -0
- package/esm/client/0_utilities.d.ts +2 -2
- package/esm/client/0_utilities.js +2 -2
- package/esm/client/1_client_generic.d.ts +87 -16
- package/esm/client/1_client_generic.d.ts.map +1 -1
- package/esm/client/2_client_encrypted.js +6 -6
- package/esm/client/2_file_manager.d.ts.map +1 -1
- package/esm/client/2_file_manager.js +26 -3
- package/esm/client/3_account_manager.js +3 -3
- package/esm/client/3_filters.d.ts +22 -7
- package/esm/client/3_filters.d.ts.map +1 -1
- package/esm/client/3_message_manager.d.ts +59 -20
- package/esm/client/3_message_manager.d.ts.map +1 -1
- package/esm/client/3_message_manager.js +6 -0
- package/esm/client/3_secret_chat_manager.d.ts +26 -0
- package/esm/client/3_secret_chat_manager.d.ts.map +1 -0
- package/esm/client/3_secret_chat_manager.js +770 -0
- package/esm/client/4_chat_manager.d.ts +2 -0
- package/esm/client/4_chat_manager.d.ts.map +1 -1
- package/esm/client/4_chat_manager.js +12 -0
- package/esm/client/4_context.d.ts +18 -3
- package/esm/client/4_context.d.ts.map +1 -1
- package/esm/client/4_context.js +65 -10
- package/esm/client/4_poll_manager.js +2 -2
- package/esm/client/6_client.d.ts +88 -17
- package/esm/client/6_client.d.ts.map +1 -1
- package/esm/client/6_client.js +119 -19
- package/esm/client/6_client_dispatcher.d.ts +87 -16
- package/esm/client/6_client_dispatcher.d.ts.map +1 -1
- package/esm/client/6_client_dispatcher.js +108 -16
- package/esm/session/2_session_encrypted.js +5 -5
- package/esm/tl/1_secret_chats_api.d.ts +637 -0
- package/esm/tl/1_secret_chats_api.d.ts.map +1 -0
- package/esm/tl/1_secret_chats_api.js +849 -0
- package/esm/tl/1_tl_reader.js +3 -3
- package/esm/tl/2_secret_chats.d.ts +33 -0
- package/esm/tl/2_secret_chats.d.ts.map +1 -0
- package/esm/tl/2_secret_chats.js +53 -0
- package/esm/types/0_secret_chat.d.ts +66 -0
- package/esm/types/0_secret_chat.d.ts.map +1 -0
- package/esm/types/0_secret_chat.js +30 -0
- package/esm/types/0_secret_message_entity.d.ts +115 -0
- package/esm/types/0_secret_message_entity.d.ts.map +1 -0
- package/esm/types/0_secret_message_entity.js +174 -0
- package/esm/types/1_chat_p.d.ts +3 -3
- package/esm/types/1_chat_p.d.ts.map +1 -1
- package/esm/types/1_input_poll_media.d.ts +2 -2
- package/esm/types/1_photo.d.ts +3 -2
- package/esm/types/1_photo.d.ts.map +1 -1
- package/esm/types/1_sticker.d.ts +5 -4
- package/esm/types/1_sticker.d.ts.map +1 -1
- package/esm/types/1_sticker.js +23 -1
- package/esm/types/1_video.d.ts +2 -2
- package/esm/types/1_video.d.ts.map +1 -1
- package/esm/types/1_video_note.d.ts +1 -1
- package/esm/types/1_video_note.d.ts.map +1 -1
- package/esm/types/2_inactive_chat.d.ts +1 -1
- package/esm/types/2_inactive_chat.d.ts.map +1 -1
- package/esm/types/2_left_channel_list.d.ts +1 -1
- package/esm/types/2_left_channel_list.d.ts.map +1 -1
- package/esm/types/2_message_entity.d.ts +0 -3
- package/esm/types/2_message_entity.d.ts.map +1 -1
- package/esm/types/2_secret_message.d.ts +184 -0
- package/esm/types/2_secret_message.d.ts.map +1 -0
- package/esm/types/2_secret_message.js +341 -0
- package/esm/types/2_user.d.ts +2 -2
- package/esm/types/2_user.d.ts.map +1 -1
- package/esm/types/3_input_media.d.ts +2 -2
- package/esm/types/4_gift.d.ts +2 -2
- package/esm/types/4_gift.d.ts.map +1 -1
- package/esm/types/9_message.d.ts +9 -61
- package/esm/types/9_message.d.ts.map +1 -1
- package/esm/types/9_message.js +1 -0
- package/esm/types/B_update.d.ts +33 -42
- package/esm/types/B_update.d.ts.map +1 -1
- package/package.json +1 -1
- package/script/0_deps.d.ts +1 -1
- package/script/0_deps.d.ts.map +1 -1
- package/script/0_deps.js +2 -1
- package/script/2_tl.d.ts +1 -0
- package/script/2_tl.d.ts.map +1 -1
- package/script/2_tl.js +2 -1
- package/script/3_types.d.ts +3 -0
- package/script/3_types.d.ts.map +1 -1
- package/script/3_types.js +3 -0
- package/script/client/0_markdown.d.ts.map +1 -1
- package/script/client/0_params.d.ts +39 -5
- package/script/client/0_params.d.ts.map +1 -1
- package/script/client/0_secret_chat_state.d.ts +86 -0
- package/script/client/0_secret_chat_state.d.ts.map +1 -0
- package/script/client/0_secret_chat_state.js +133 -0
- package/script/client/0_storage_operations.d.ts +1 -0
- package/script/client/0_storage_operations.d.ts.map +1 -1
- package/script/client/0_storage_operations.js +3 -0
- package/script/client/0_utilities.d.ts +2 -2
- package/script/client/0_utilities.js +2 -2
- package/script/client/1_client_generic.d.ts +87 -16
- package/script/client/1_client_generic.d.ts.map +1 -1
- package/script/client/2_client_encrypted.js +6 -6
- package/script/client/2_file_manager.d.ts.map +1 -1
- package/script/client/2_file_manager.js +25 -2
- package/script/client/3_account_manager.js +3 -3
- package/script/client/3_filters.d.ts +22 -7
- package/script/client/3_filters.d.ts.map +1 -1
- package/script/client/3_message_manager.d.ts +59 -20
- package/script/client/3_message_manager.d.ts.map +1 -1
- package/script/client/3_message_manager.js +6 -0
- package/script/client/3_secret_chat_manager.d.ts +26 -0
- package/script/client/3_secret_chat_manager.d.ts.map +1 -0
- package/script/client/3_secret_chat_manager.js +807 -0
- package/script/client/4_chat_manager.d.ts +2 -0
- package/script/client/4_chat_manager.d.ts.map +1 -1
- package/script/client/4_chat_manager.js +12 -0
- package/script/client/4_context.d.ts +18 -3
- package/script/client/4_context.d.ts.map +1 -1
- package/script/client/4_context.js +65 -10
- package/script/client/4_poll_manager.js +2 -2
- package/script/client/6_client.d.ts +88 -17
- package/script/client/6_client.d.ts.map +1 -1
- package/script/client/6_client.js +119 -19
- package/script/client/6_client_dispatcher.d.ts +87 -16
- package/script/client/6_client_dispatcher.d.ts.map +1 -1
- package/script/client/6_client_dispatcher.js +108 -16
- package/script/session/2_session_encrypted.js +5 -5
- package/script/tl/1_secret_chats_api.d.ts +637 -0
- package/script/tl/1_secret_chats_api.d.ts.map +1 -0
- package/script/tl/1_secret_chats_api.js +852 -0
- package/script/tl/1_tl_reader.js +3 -3
- package/script/tl/2_secret_chats.d.ts +33 -0
- package/script/tl/2_secret_chats.d.ts.map +1 -0
- package/script/tl/2_secret_chats.js +78 -0
- package/script/types/0_secret_chat.d.ts +66 -0
- package/script/types/0_secret_chat.d.ts.map +1 -0
- package/script/types/0_secret_chat.js +33 -0
- package/script/types/0_secret_message_entity.d.ts +115 -0
- package/script/types/0_secret_message_entity.d.ts.map +1 -0
- package/script/types/0_secret_message_entity.js +179 -0
- package/script/types/1_chat_p.d.ts +3 -3
- package/script/types/1_chat_p.d.ts.map +1 -1
- package/script/types/1_input_poll_media.d.ts +2 -2
- package/script/types/1_photo.d.ts +3 -2
- package/script/types/1_photo.d.ts.map +1 -1
- package/script/types/1_sticker.d.ts +5 -4
- package/script/types/1_sticker.d.ts.map +1 -1
- package/script/types/1_sticker.js +23 -0
- package/script/types/1_video.d.ts +2 -2
- package/script/types/1_video.d.ts.map +1 -1
- package/script/types/1_video_note.d.ts +1 -1
- package/script/types/1_video_note.d.ts.map +1 -1
- package/script/types/2_inactive_chat.d.ts +1 -1
- package/script/types/2_inactive_chat.d.ts.map +1 -1
- package/script/types/2_left_channel_list.d.ts +1 -1
- package/script/types/2_left_channel_list.d.ts.map +1 -1
- package/script/types/2_message_entity.d.ts +0 -3
- package/script/types/2_message_entity.d.ts.map +1 -1
- package/script/types/2_secret_message.d.ts +184 -0
- package/script/types/2_secret_message.d.ts.map +1 -0
- package/script/types/2_secret_message.js +344 -0
- package/script/types/2_user.d.ts +2 -2
- package/script/types/2_user.d.ts.map +1 -1
- package/script/types/3_input_media.d.ts +2 -2
- package/script/types/4_gift.d.ts +2 -2
- package/script/types/4_gift.d.ts.map +1 -1
- package/script/types/9_message.d.ts +9 -61
- package/script/types/9_message.d.ts.map +1 -1
- package/script/types/9_message.js +1 -0
- package/script/types/B_update.d.ts +33 -42
- package/script/types/B_update.d.ts.map +1 -1
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var _a;
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.SecretChatManager = void 0;
|
|
38
|
+
/**
|
|
39
|
+
* MTKruto - Cross-runtime JavaScript library for building Telegram clients
|
|
40
|
+
* Copyright (C) 2023-2026 Roj <https://roj.im/>
|
|
41
|
+
*
|
|
42
|
+
* This file is part of MTKruto.
|
|
43
|
+
*
|
|
44
|
+
* This program is free software: you can redistribute it and/or modify
|
|
45
|
+
* it under the terms of the GNU Lesser General Public License as published by
|
|
46
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
47
|
+
* (at your option) any later version.
|
|
48
|
+
*
|
|
49
|
+
* This program is distributed in the hope that it will be useful,
|
|
50
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
51
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
52
|
+
* GNU Lesser General Public License for more details.
|
|
53
|
+
*
|
|
54
|
+
* You should have received a copy of the GNU Lesser General Public License
|
|
55
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
56
|
+
*/
|
|
57
|
+
const dntShim = __importStar(require("../_dnt.shims.js"));
|
|
58
|
+
const _0_deps_js_1 = require("../0_deps.js");
|
|
59
|
+
const _0_errors_js_1 = require("../0_errors.js");
|
|
60
|
+
const _1_utilities_js_1 = require("../1_utilities.js");
|
|
61
|
+
const _2_tl_js_1 = require("../2_tl.js");
|
|
62
|
+
const _3_types_js_1 = require("../3_types.js");
|
|
63
|
+
const _0_secret_chat_js_1 = require("../types/0_secret_chat.js");
|
|
64
|
+
const _2_secret_message_js_1 = require("../types/2_secret_message.js");
|
|
65
|
+
const _0_password_js_1 = require("./0_password.js");
|
|
66
|
+
const _0_secret_chat_state_js_1 = require("./0_secret_chat_state.js");
|
|
67
|
+
const secretChatManagerUpdates = [
|
|
68
|
+
"updateEncryption",
|
|
69
|
+
"updateNewEncryptedMessage",
|
|
70
|
+
];
|
|
71
|
+
class SecretChatManager {
|
|
72
|
+
#c;
|
|
73
|
+
#L;
|
|
74
|
+
constructor(c) {
|
|
75
|
+
this.#c = c;
|
|
76
|
+
this.#L = (0, _1_utilities_js_1.getLogger)("SecretChatManager");
|
|
77
|
+
}
|
|
78
|
+
async loadSecretChats() {
|
|
79
|
+
let loaded = 0;
|
|
80
|
+
for await (const [k, v] of await this.#c.messageStorage.storage.getMany({ prefix: ["secretChats"] })) {
|
|
81
|
+
if (typeof k[1] !== "number") {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
this.#states.set(k[1], _0_secret_chat_state_js_1.SecretChatState.load(v));
|
|
85
|
+
++loaded;
|
|
86
|
+
}
|
|
87
|
+
this.#L.debug("loaded", loaded, "secret chats");
|
|
88
|
+
}
|
|
89
|
+
static #checkDhConfig(dhConfig) {
|
|
90
|
+
const prime = (0, _1_utilities_js_1.intFromBytes)(dhConfig.p, { byteOrder: "big", isSigned: false });
|
|
91
|
+
if (prime.toString(2).length !== 2048) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
let mod_ok = false;
|
|
95
|
+
let mod_r = 0;
|
|
96
|
+
switch (dhConfig.g) {
|
|
97
|
+
case 2:
|
|
98
|
+
mod_ok = prime % 8n === 7n;
|
|
99
|
+
break;
|
|
100
|
+
case 3:
|
|
101
|
+
mod_ok = prime % 3n === 2n;
|
|
102
|
+
break;
|
|
103
|
+
case 4:
|
|
104
|
+
mod_ok = true;
|
|
105
|
+
break;
|
|
106
|
+
case 5:
|
|
107
|
+
mod_ok = (mod_r = Number(prime % 5n)) === 1 || mod_r === 4;
|
|
108
|
+
break;
|
|
109
|
+
case 6:
|
|
110
|
+
mod_ok = (mod_r = Number(prime % 24n)) === 19 || mod_r === 23;
|
|
111
|
+
break;
|
|
112
|
+
case 7:
|
|
113
|
+
mod_ok = (mod_r = Number(prime % 7n)) === 3 || mod_r === 5 || mod_r === 6;
|
|
114
|
+
break;
|
|
115
|
+
default:
|
|
116
|
+
mod_ok = false;
|
|
117
|
+
}
|
|
118
|
+
return mod_ok && ((0, _0_password_js_1.isSafePrime)(dhConfig.p, dhConfig.g));
|
|
119
|
+
}
|
|
120
|
+
async #getDhConfig() {
|
|
121
|
+
const result = _2_tl_js_1.Api.as("messages.dhConfig", await this.#c.invoke({
|
|
122
|
+
_: "messages.getDhConfig",
|
|
123
|
+
version: 0,
|
|
124
|
+
random_length: 256,
|
|
125
|
+
}));
|
|
126
|
+
if (!_a.#checkDhConfig(result)) {
|
|
127
|
+
throw new TypeError("Received invalid dhConfig.");
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
#states = new Map();
|
|
132
|
+
#getSecretChatState(id) {
|
|
133
|
+
let state = this.#states.get(id);
|
|
134
|
+
if (state === undefined) {
|
|
135
|
+
state = new _0_secret_chat_state_js_1.SecretChatState();
|
|
136
|
+
this.#states.set(id, state);
|
|
137
|
+
}
|
|
138
|
+
return state;
|
|
139
|
+
}
|
|
140
|
+
async requestSecretChat(chatId) {
|
|
141
|
+
const user_id = await this.#c.getInputUser(chatId);
|
|
142
|
+
if (_2_tl_js_1.Api.is("inputUserSelf", user_id)) {
|
|
143
|
+
throw new _0_errors_js_1.InputError("Received invalid chat identifier.");
|
|
144
|
+
}
|
|
145
|
+
const dhConfig = await this.#getDhConfig();
|
|
146
|
+
const prime = (0, _1_utilities_js_1.intFromBytes)(dhConfig.p, { byteOrder: "big", isSigned: false });
|
|
147
|
+
const a = (0, _1_utilities_js_1.getRandomInt)(256, false);
|
|
148
|
+
const g_a = (0, _1_utilities_js_1.intToBytes)((0, _1_utilities_js_1.modExp)(BigInt(dhConfig.g), a, prime), 256, { byteOrder: "big", isSigned: false });
|
|
149
|
+
const result = await this.#c.invoke({
|
|
150
|
+
_: "messages.requestEncryption",
|
|
151
|
+
user_id,
|
|
152
|
+
g_a,
|
|
153
|
+
random_id: (0, _1_utilities_js_1.getRandomId)(true),
|
|
154
|
+
});
|
|
155
|
+
const state = this.#getSecretChatState(result.id);
|
|
156
|
+
state.g = dhConfig.g;
|
|
157
|
+
state.prime = prime;
|
|
158
|
+
state.a = a;
|
|
159
|
+
state.pendingExponent = a;
|
|
160
|
+
return (0, _0_secret_chat_js_1.constructSecretChat)(result);
|
|
161
|
+
}
|
|
162
|
+
async acceptSecretChat(id) {
|
|
163
|
+
const state = this.#getSecretChatState(id);
|
|
164
|
+
if (!_2_tl_js_1.Api.is("encryptedChatRequested", state.encryptedChat)) {
|
|
165
|
+
throw new _0_errors_js_1.InputError("Invalid secret chat identifier received.");
|
|
166
|
+
}
|
|
167
|
+
const dhConfig = await this.#getDhConfig();
|
|
168
|
+
const prime = (0, _1_utilities_js_1.intFromBytes)(dhConfig.p, { byteOrder: "big", isSigned: false });
|
|
169
|
+
state.g = dhConfig.g;
|
|
170
|
+
state.prime = prime;
|
|
171
|
+
const b = (0, _1_utilities_js_1.getRandomInt)(256, false);
|
|
172
|
+
// key = (pow(g_a, b) mod dh_prime)
|
|
173
|
+
const gA = (0, _1_utilities_js_1.intFromBytes)(state.encryptedChat.g_a, { byteOrder: "big", isSigned: false });
|
|
174
|
+
if (!(0, _0_password_js_1.isGoodModExpFirst)(gA, prime)) {
|
|
175
|
+
throw new TypeError("Received invalid g_a.");
|
|
176
|
+
}
|
|
177
|
+
let authKey = (0, _1_utilities_js_1.intToBytes)((0, _1_utilities_js_1.modExp)(gA, b, prime), 256, { byteOrder: "big", isSigned: false });
|
|
178
|
+
if (authKey.byteLength < 256) {
|
|
179
|
+
authKey = (0, _0_deps_js_1.concat)([new Uint8Array(256 - authKey.byteLength), authKey]);
|
|
180
|
+
}
|
|
181
|
+
state.authKey = authKey;
|
|
182
|
+
state.authKeyCreatedAt = Date.now();
|
|
183
|
+
state.authKeyUseCount = 0;
|
|
184
|
+
state.isAuthKeyUsed = false;
|
|
185
|
+
const authKeyId = (await (0, _1_utilities_js_1.sha1)(authKey)).subarray(-8);
|
|
186
|
+
state.authKeyId_ = authKeyId;
|
|
187
|
+
const key_fingerprint = (0, _1_utilities_js_1.intFromBytes)(authKeyId);
|
|
188
|
+
state.authKeyId = key_fingerprint;
|
|
189
|
+
// g_b := pow(g, b) mod dh_prime
|
|
190
|
+
const g_b = (0, _1_utilities_js_1.intToBytes)((0, _1_utilities_js_1.modExp)(BigInt(dhConfig.g), b, prime), 256, { byteOrder: "big", isSigned: false });
|
|
191
|
+
const peer = { _: "inputEncryptedChat", chat_id: state.encryptedChat.id, access_hash: state.encryptedChat.access_hash };
|
|
192
|
+
const result = await this.#c.invoke({
|
|
193
|
+
_: "messages.acceptEncryption",
|
|
194
|
+
peer,
|
|
195
|
+
g_b,
|
|
196
|
+
key_fingerprint,
|
|
197
|
+
});
|
|
198
|
+
state.encryptedChat = result;
|
|
199
|
+
return (0, _0_secret_chat_js_1.constructSecretChat)(result);
|
|
200
|
+
}
|
|
201
|
+
#getNextOutSeqNo(id, isCreator) {
|
|
202
|
+
const state = this.#getSecretChatState(id);
|
|
203
|
+
const rawOutSeqNo = state.outSeqNo;
|
|
204
|
+
state.outSeqNo = rawOutSeqNo + 1;
|
|
205
|
+
return 2 * rawOutSeqNo + (isCreator ? 1 : 0);
|
|
206
|
+
}
|
|
207
|
+
#getInSeqNo(id, isCreator) {
|
|
208
|
+
const state = this.#getSecretChatState(id);
|
|
209
|
+
const rawInSeqNo = state.inSeqNo;
|
|
210
|
+
return 2 * rawInSeqNo + (isCreator ? 0 : 1);
|
|
211
|
+
}
|
|
212
|
+
#mustGetEncryptedChat(id) {
|
|
213
|
+
const state = this.#getSecretChatState(id);
|
|
214
|
+
if (!_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
215
|
+
throw new _0_errors_js_1.InputError("Received invalid secret chat identifier.");
|
|
216
|
+
}
|
|
217
|
+
return state;
|
|
218
|
+
}
|
|
219
|
+
async #postSendMessage(state) {
|
|
220
|
+
state.isJustLoaded = false;
|
|
221
|
+
await this.#maybeStartRekey(state);
|
|
222
|
+
}
|
|
223
|
+
async sendSecretMessage(id, text, params) {
|
|
224
|
+
this.#c.storage.assertUser("sendSecretMessage");
|
|
225
|
+
const state = this.#mustGetEncryptedChat(id);
|
|
226
|
+
const random_id = (0, _1_utilities_js_1.getRandomId)();
|
|
227
|
+
const decryptedMessage = {
|
|
228
|
+
_: "decryptedMessage",
|
|
229
|
+
message: text,
|
|
230
|
+
random_id,
|
|
231
|
+
ttl: params?.ttl ?? 0,
|
|
232
|
+
silent: params?.isSilent || undefined,
|
|
233
|
+
reply_to_random_id: params?.replyToMessageId ? BigInt(params.replyToMessageId) : undefined,
|
|
234
|
+
entities: params?.entities?.length ? params.entities.map(_3_types_js_1.secretMessageEntityToTlObject) : undefined,
|
|
235
|
+
via_bot_name: params?.viaBot,
|
|
236
|
+
};
|
|
237
|
+
await this.#sendMessage(decryptedMessage, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
238
|
+
await this.#postSendMessage(state);
|
|
239
|
+
}
|
|
240
|
+
async sendSecretLocation(id, latitude, longitude, params) {
|
|
241
|
+
this.#c.storage.assertUser("sendSecretLocation");
|
|
242
|
+
const state = this.#mustGetEncryptedChat(id);
|
|
243
|
+
const random_id = (0, _1_utilities_js_1.getRandomId)();
|
|
244
|
+
const decryptedMessage = {
|
|
245
|
+
_: "decryptedMessage",
|
|
246
|
+
message: "",
|
|
247
|
+
random_id,
|
|
248
|
+
ttl: params?.ttl ?? 0,
|
|
249
|
+
silent: params?.isSilent || undefined,
|
|
250
|
+
reply_to_random_id: params?.replyToMessageId ? BigInt(params.replyToMessageId) : undefined,
|
|
251
|
+
via_bot_name: params?.viaBot,
|
|
252
|
+
media: { _: "decryptedMessageMediaGeoPoint", lat: latitude, long: longitude },
|
|
253
|
+
};
|
|
254
|
+
await this.#sendMessage(decryptedMessage, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
255
|
+
await this.#postSendMessage(state);
|
|
256
|
+
}
|
|
257
|
+
async sendSecretVenue(id, latitude, longitude, title, address, params) {
|
|
258
|
+
this.#c.storage.assertUser("sendSecretVenue");
|
|
259
|
+
const state = this.#mustGetEncryptedChat(id);
|
|
260
|
+
const random_id = (0, _1_utilities_js_1.getRandomId)();
|
|
261
|
+
const decryptedMessage = {
|
|
262
|
+
_: "decryptedMessage",
|
|
263
|
+
message: "",
|
|
264
|
+
random_id,
|
|
265
|
+
ttl: params?.ttl ?? 0,
|
|
266
|
+
silent: params?.isSilent || undefined,
|
|
267
|
+
reply_to_random_id: params?.replyToMessageId ? BigInt(params.replyToMessageId) : undefined,
|
|
268
|
+
via_bot_name: params?.viaBot,
|
|
269
|
+
media: { _: "decryptedMessageMediaVenue", lat: latitude, long: longitude, title, address, provider: "foursquare", venue_id: params?.foursquareId ?? "" },
|
|
270
|
+
};
|
|
271
|
+
await this.#sendMessage(decryptedMessage, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
272
|
+
await this.#postSendMessage(state);
|
|
273
|
+
}
|
|
274
|
+
async sendSecretContact(id, firstName, phoneNumber, params) {
|
|
275
|
+
this.#c.storage.assertUser("sendSecretContact");
|
|
276
|
+
const state = this.#mustGetEncryptedChat(id);
|
|
277
|
+
const random_id = (0, _1_utilities_js_1.getRandomId)();
|
|
278
|
+
const decryptedMessage = {
|
|
279
|
+
_: "decryptedMessage",
|
|
280
|
+
message: "",
|
|
281
|
+
random_id,
|
|
282
|
+
ttl: params?.ttl ?? 0,
|
|
283
|
+
silent: params?.isSilent || undefined,
|
|
284
|
+
reply_to_random_id: params?.replyToMessageId ? BigInt(params.replyToMessageId) : undefined,
|
|
285
|
+
via_bot_name: params?.viaBot,
|
|
286
|
+
media: { _: "decryptedMessageMediaContact", first_name: firstName, last_name: params?.lastName ?? "", phone_number: phoneNumber, user_id: 0 },
|
|
287
|
+
};
|
|
288
|
+
await this.#sendMessage(decryptedMessage, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
289
|
+
await this.#postSendMessage(state);
|
|
290
|
+
}
|
|
291
|
+
#sendTails = new Map();
|
|
292
|
+
async #sendMessage(message, encryptedChat, authKey, authKeyId) {
|
|
293
|
+
try {
|
|
294
|
+
await this.#sendMessageInner(message, encryptedChat, authKey, authKeyId);
|
|
295
|
+
}
|
|
296
|
+
finally {
|
|
297
|
+
await this.#getSecretChatState(encryptedChat.id).commit(this.#c.messageStorage.storage);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async #sendMessageInner(message, encryptedChat, authKey, authKeyId) {
|
|
301
|
+
const previous = this.#sendTails.get(encryptedChat.id) ?? Promise.resolve();
|
|
302
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
303
|
+
const tail = previous.then(() => promise);
|
|
304
|
+
this.#sendTails.set(encryptedChat.id, tail);
|
|
305
|
+
await previous;
|
|
306
|
+
try {
|
|
307
|
+
await this.#sendMessageUnlocked(message, encryptedChat, authKey, authKeyId);
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
resolve();
|
|
311
|
+
if (this.#sendTails.get(encryptedChat.id) === tail) {
|
|
312
|
+
this.#sendTails.delete(encryptedChat.id);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async #sendMessageUnlocked(message, encryptedChat, authKey, authKeyId) {
|
|
317
|
+
const random_id = (0, _1_utilities_js_1.getRandomId)();
|
|
318
|
+
const isCreator = Number(encryptedChat.admin_id) === await this.#c.getSelfId();
|
|
319
|
+
const out_seq_no = this.#getNextOutSeqNo(encryptedChat.id, isCreator);
|
|
320
|
+
const in_seq_no = this.#getInSeqNo(encryptedChat.id, isCreator);
|
|
321
|
+
const decryptedMessageLayer = { _: "decryptedMessageLayer", in_seq_no, layer: 144, message, out_seq_no, random_bytes: dntShim.crypto.getRandomValues(new Uint8Array(15)) };
|
|
322
|
+
const data = await this.#encryptMessage(isCreator, authKeyId, authKey, decryptedMessageLayer);
|
|
323
|
+
this.#getSecretChatState(encryptedChat.id).outgoingMessages.set((out_seq_no - (isCreator ? 1 : 0)) / 2, data);
|
|
324
|
+
await this.#c.invoke({ _: "messages.sendEncrypted", peer: { _: "inputEncryptedChat", chat_id: encryptedChat.id, access_hash: encryptedChat.access_hash }, random_id, data });
|
|
325
|
+
const state = this.#getSecretChatState(encryptedChat.id);
|
|
326
|
+
if ((0, _0_deps_js_1.equals)(state.authKeyId_, authKeyId)) {
|
|
327
|
+
++state.authKeyUseCount;
|
|
328
|
+
state.isAuthKeyUsed = true;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async #encryptMessage(isCreator, authKeyId, authKey, message) {
|
|
332
|
+
const serializedDecryptedMessageLayer = _2_tl_js_1.SecretChats.serializeObject(message);
|
|
333
|
+
const plainTextWriter = new _2_tl_js_1.TLWriter();
|
|
334
|
+
plainTextWriter.writeInt32(serializedDecryptedMessageLayer.byteLength, false);
|
|
335
|
+
plainTextWriter.write(serializedDecryptedMessageLayer);
|
|
336
|
+
let paddingLength = (0, _1_utilities_js_1.mod)(-(4 + serializedDecryptedMessageLayer.byteLength), 16);
|
|
337
|
+
if (paddingLength < 12) {
|
|
338
|
+
paddingLength += 16;
|
|
339
|
+
}
|
|
340
|
+
plainTextWriter.write(dntShim.crypto.getRandomValues(new Uint8Array(paddingLength)));
|
|
341
|
+
const plainText = plainTextWriter.buffer;
|
|
342
|
+
const x = isCreator ? 0 : 8;
|
|
343
|
+
// msg_key_large = SHA256 (substr (key, 88+x, 32) + plaintext + random_padding);
|
|
344
|
+
const messageKeyLarge = await (0, _1_utilities_js_1.sha256)((0, _0_deps_js_1.concat)([authKey.subarray(88 + x, 88 + x + 32), plainText]));
|
|
345
|
+
// msg_key = substr (msg_key_large, 8, 16);
|
|
346
|
+
const messageKey = messageKeyLarge.subarray(8, 8 + 16);
|
|
347
|
+
// sha256_a = SHA256 (msg_key + substr (key, x, 36));
|
|
348
|
+
const sha256A = await (0, _1_utilities_js_1.sha256)((0, _0_deps_js_1.concat)([messageKey, authKey.subarray(x, x + 36)]));
|
|
349
|
+
// sha256_b = SHA256 (substr (key, 40+x, 36) + msg_key);
|
|
350
|
+
const sha256B = await (0, _1_utilities_js_1.sha256)((0, _0_deps_js_1.concat)([authKey.subarray(40 + x, 40 + x + 36), messageKey]));
|
|
351
|
+
// aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);
|
|
352
|
+
const aesKey = (0, _0_deps_js_1.concat)([sha256A.subarray(0, 8), sha256B.subarray(8, 8 + 16), sha256A.subarray(24, 24 + 8)]);
|
|
353
|
+
// aes_iv = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);
|
|
354
|
+
const aesIv = (0, _0_deps_js_1.concat)([sha256B.subarray(0, 8), sha256A.subarray(8, 8 + 16), sha256B.subarray(24, 24 + 8)]);
|
|
355
|
+
const encryptedText = (0, _0_deps_js_1.ige256Encrypt)(plainText, aesKey, aesIv);
|
|
356
|
+
const dataWriter = new _2_tl_js_1.TLWriter();
|
|
357
|
+
dataWriter.write(authKeyId);
|
|
358
|
+
dataWriter.write(messageKey);
|
|
359
|
+
dataWriter.write(encryptedText);
|
|
360
|
+
const data = dataWriter.buffer;
|
|
361
|
+
return data;
|
|
362
|
+
}
|
|
363
|
+
async #decryptMessage(authKeyId, authKey, isCreator, message) {
|
|
364
|
+
const x = isCreator ? 8 : 0;
|
|
365
|
+
if (message.byteLength < 40 || (message.byteLength - 24) % 16 !== 0) {
|
|
366
|
+
throw new TypeError("Received invalid encrypted message length.");
|
|
367
|
+
}
|
|
368
|
+
const messageReader = new _2_tl_js_1.TLReader(message);
|
|
369
|
+
if (!(0, _0_deps_js_1.equals)(messageReader.read(8), authKeyId)) {
|
|
370
|
+
throw new TypeError("Received invalid auth key identifier.");
|
|
371
|
+
}
|
|
372
|
+
const messageKey = messageReader.read(16);
|
|
373
|
+
// sha256_a = SHA256 (msg_key + substr (key, x, 36));
|
|
374
|
+
const sha256A = await (0, _1_utilities_js_1.sha256)((0, _0_deps_js_1.concat)([messageKey, authKey.subarray(x, x + 36)]));
|
|
375
|
+
// sha256_b = SHA256 (substr (key, 40+x, 36) + msg_key);
|
|
376
|
+
const sha256B = await (0, _1_utilities_js_1.sha256)((0, _0_deps_js_1.concat)([authKey.subarray(40 + x, 40 + x + 36), messageKey]));
|
|
377
|
+
// aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);
|
|
378
|
+
const aesKey = (0, _0_deps_js_1.concat)([sha256A.subarray(0, 8), sha256B.subarray(8, 8 + 16), sha256A.subarray(24, 24 + 8)]);
|
|
379
|
+
// aes_iv = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);
|
|
380
|
+
const aesIv = (0, _0_deps_js_1.concat)([sha256B.subarray(0, 8), sha256A.subarray(8, 8 + 16), sha256B.subarray(24, 24 + 8)]);
|
|
381
|
+
const decryptedText = (0, _0_deps_js_1.ige256Decrypt)(messageReader.buffer, aesKey, aesIv);
|
|
382
|
+
const expectedMessageKey = (await (0, _1_utilities_js_1.sha256)((0, _0_deps_js_1.concat)([authKey.subarray(88 + x, 88 + x + 32), decryptedText]))).subarray(8, 24);
|
|
383
|
+
if (!(0, _0_deps_js_1.equals)(messageKey, expectedMessageKey)) {
|
|
384
|
+
throw new TypeError("Encrypted message key mismatch.");
|
|
385
|
+
}
|
|
386
|
+
const decryptedTextReader = new _2_tl_js_1.TLReader(decryptedText);
|
|
387
|
+
const length = decryptedTextReader.readInt32(false);
|
|
388
|
+
const paddingLength = decryptedText.byteLength - 4 - length;
|
|
389
|
+
if (length < 0 || paddingLength < 12 || paddingLength > 1024) {
|
|
390
|
+
throw new TypeError("Received invalid encrypted message padding.");
|
|
391
|
+
}
|
|
392
|
+
const serializedMessage = decryptedTextReader.read(length);
|
|
393
|
+
return await _2_tl_js_1.SecretChats.deserializeType(_2_tl_js_1.X, serializedMessage);
|
|
394
|
+
}
|
|
395
|
+
async #checkGap(chatId, message, encryptedMessage) {
|
|
396
|
+
const state = this.#getSecretChatState(chatId);
|
|
397
|
+
if (!_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const isCreator = Number(state.encryptedChat.admin_id) === await this.#c.getSelfId();
|
|
401
|
+
const x = isCreator ? 0 : 1;
|
|
402
|
+
const inX = isCreator ? 1 : 0;
|
|
403
|
+
if (message.out_seq_no < 0 || message.out_seq_no % 2 !== x) {
|
|
404
|
+
this.#L.debug("discarding secret chat", chatId, "because an invalid out_seq_no was received");
|
|
405
|
+
await this.#c.invoke({ _: "messages.discardEncryption", chat_id: chatId });
|
|
406
|
+
throw new TypeError("Received invalid secret chat out_seq_no.");
|
|
407
|
+
}
|
|
408
|
+
const outSeqNo = (message.out_seq_no - x) / 2;
|
|
409
|
+
const inSeqNo = (message.in_seq_no - inX) / 2;
|
|
410
|
+
if (inSeqNo % 1 !== 0 || inSeqNo < 0 || inSeqNo > state.outSeqNo) {
|
|
411
|
+
this.#L.debug("discarding secret chat", chatId, "because an invalid in_seq_no was received");
|
|
412
|
+
await this.#c.invoke({ _: "messages.discardEncryption", chat_id: chatId });
|
|
413
|
+
throw new TypeError("Received invalid secret chat in_seq_no.");
|
|
414
|
+
}
|
|
415
|
+
if (outSeqNo < state.inSeqNo) { // old
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const alreadyPending = state.pendingMessages.some((v) => v[0].out_seq_no === message.out_seq_no);
|
|
419
|
+
if (!alreadyPending && _2_tl_js_1.SecretChats.is("decryptedMessageService", message.message) && _2_tl_js_1.SecretChats.is("decryptedMessageActionResend", message.message.action)) {
|
|
420
|
+
await this.#resendMessages(state, message.message.action, isCreator);
|
|
421
|
+
}
|
|
422
|
+
if (outSeqNo > state.inSeqNo) { // gap
|
|
423
|
+
if (!state.pendingMessages.some((v) => v[0].out_seq_no === message.out_seq_no)) {
|
|
424
|
+
state.pendingMessages.push([message, encryptedMessage]);
|
|
425
|
+
}
|
|
426
|
+
if (state.isGapRequested) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
await this.#sendMessage({
|
|
430
|
+
_: "decryptedMessageService",
|
|
431
|
+
random_id: (0, _1_utilities_js_1.getRandomId)(),
|
|
432
|
+
action: {
|
|
433
|
+
_: "decryptedMessageActionResend",
|
|
434
|
+
start_seq_no: state.inSeqNo * 2 + x,
|
|
435
|
+
end_seq_no: message.out_seq_no - 2,
|
|
436
|
+
},
|
|
437
|
+
}, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
438
|
+
state.isGapRequested = true;
|
|
439
|
+
state.gapEndSeqNo = outSeqNo - 1;
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const handle = async (message, encryptedMessage) => {
|
|
443
|
+
const inSeqNo = (message.in_seq_no - inX) / 2;
|
|
444
|
+
if (inSeqNo < state.remoteInSeqNo && !state.isJustLoaded) {
|
|
445
|
+
this.#L.debug("discarding secret chat", chatId, "because of decreasing in_seq_no");
|
|
446
|
+
await this.#c.invoke({ _: "messages.discardEncryption", chat_id: chatId });
|
|
447
|
+
throw new TypeError("Received decreasing secret chat in_seq_no.");
|
|
448
|
+
}
|
|
449
|
+
state.remoteInSeqNo = Math.max(state.remoteInSeqNo, inSeqNo);
|
|
450
|
+
++state.inSeqNo;
|
|
451
|
+
await this.#handleDecryptedMessageLayer(chatId, message, encryptedMessage);
|
|
452
|
+
};
|
|
453
|
+
await handle(message, encryptedMessage);
|
|
454
|
+
while (true) {
|
|
455
|
+
const index = state.pendingMessages.findIndex((v) => (v[0].out_seq_no - x) / 2 === state.inSeqNo);
|
|
456
|
+
if (index === -1) {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
const [pendingMessage] = state.pendingMessages.splice(index, 1);
|
|
460
|
+
await handle(pendingMessage[0], pendingMessage[1]);
|
|
461
|
+
}
|
|
462
|
+
if (state.pendingMessages.length === 0) {
|
|
463
|
+
state.isGapRequested = false;
|
|
464
|
+
state.gapEndSeqNo = -1;
|
|
465
|
+
}
|
|
466
|
+
else if (state.inSeqNo > state.gapEndSeqNo) {
|
|
467
|
+
state.isGapRequested = false;
|
|
468
|
+
const next = state.pendingMessages.reduce((a, b) => a[0].out_seq_no < b[0].out_seq_no ? a : b);
|
|
469
|
+
await this.#checkGap(chatId, next[0], next[1]);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async #resendMessages(state, action, isCreator) {
|
|
473
|
+
if (!_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const x = isCreator ? 1 : 0;
|
|
477
|
+
const start = (action.start_seq_no - x) / 2;
|
|
478
|
+
const end = (action.end_seq_no - x) / 2;
|
|
479
|
+
if (start % 1 !== 0 || end % 1 !== 0 || start < 0 || end < start || end >= state.outSeqNo) {
|
|
480
|
+
this.#L.debug("discarding secret chat", state.encryptedChat.id, "because an invalid resend rage was received");
|
|
481
|
+
await this.#c.invoke({ _: "messages.discardEncryption", chat_id: state.encryptedChat.id });
|
|
482
|
+
throw new TypeError("Received invalid secret chat resend range.");
|
|
483
|
+
}
|
|
484
|
+
const peer = { _: "inputEncryptedChat", chat_id: state.encryptedChat.id, access_hash: state.encryptedChat.access_hash };
|
|
485
|
+
for (let seqNo = start; seqNo <= end; ++seqNo) {
|
|
486
|
+
const message = state.outgoingMessages.get(seqNo);
|
|
487
|
+
if (!message) {
|
|
488
|
+
this.#L.debug("discarding secret chat", state.encryptedChat.id, "because unable to resend message");
|
|
489
|
+
await this.#c.invoke({ _: "messages.discardEncryption", chat_id: state.encryptedChat.id });
|
|
490
|
+
throw new TypeError("Unable to resend secret chat message.");
|
|
491
|
+
}
|
|
492
|
+
await this.#c.invoke({ _: "messages.sendEncrypted", peer, random_id: (0, _1_utilities_js_1.getRandomId)(), data: message });
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
#clearPreviousKey(state) {
|
|
496
|
+
state.previousAuthKey.fill(0);
|
|
497
|
+
state.previousAuthKey = new Uint8Array();
|
|
498
|
+
state.previousAuthKeyId_ = new Uint8Array();
|
|
499
|
+
state.previousAuthKeyDiscardAfterSeqNo = -1;
|
|
500
|
+
state.isAwaitingNewAuthKeyConfirmation = false;
|
|
501
|
+
}
|
|
502
|
+
#installNewKey(state, authKey, authKeyId, authKeyId_, awaitingConfirmation, discardAfterSeqNo = -1) {
|
|
503
|
+
state.previousAuthKey = state.authKey;
|
|
504
|
+
state.previousAuthKeyId_ = state.authKeyId_;
|
|
505
|
+
state.previousAuthKeyDiscardAfterSeqNo = discardAfterSeqNo;
|
|
506
|
+
state.isAwaitingNewAuthKeyConfirmation = awaitingConfirmation;
|
|
507
|
+
state.authKey = authKey;
|
|
508
|
+
state.authKeyId = authKeyId;
|
|
509
|
+
state.authKeyId_ = authKeyId_;
|
|
510
|
+
state.authKeyCreatedAt = Date.now();
|
|
511
|
+
state.authKeyUseCount = 0;
|
|
512
|
+
state.isAuthKeyUsed = false;
|
|
513
|
+
}
|
|
514
|
+
async #abortRekey(state, exchangeId) {
|
|
515
|
+
if (!_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const action = { _: "decryptedMessageActionAbortKey", exchange_id: exchangeId };
|
|
519
|
+
await this.#sendMessage({ _: "decryptedMessageService", random_id: (0, _1_utilities_js_1.getRandomId)(), action }, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
520
|
+
}
|
|
521
|
+
#clearInitiatedRekey(state) {
|
|
522
|
+
state.rekeyId = 0n;
|
|
523
|
+
state.rekeyA = 0n;
|
|
524
|
+
}
|
|
525
|
+
async #processDecryptedMessageActionAcceptKey(chatId, action_) {
|
|
526
|
+
const state = this.#getSecretChatState(chatId);
|
|
527
|
+
if (state.rekeyId !== action_.exchange_id || !_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const gB = (0, _1_utilities_js_1.intFromBytes)(action_.g_b, { byteOrder: "big", isSigned: false });
|
|
531
|
+
if (!(0, _0_password_js_1.isGoodModExpFirst)(gB, state.prime)) {
|
|
532
|
+
await this.#abortRekey(state, action_.exchange_id);
|
|
533
|
+
this.#clearInitiatedRekey(state);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const authKey = (0, _1_utilities_js_1.intToBytes)((0, _1_utilities_js_1.modExp)(gB, state.rekeyA, state.prime), 256, { byteOrder: "big", isSigned: false });
|
|
537
|
+
const authKeyId_ = (await (0, _1_utilities_js_1.sha1)(authKey)).subarray(-8);
|
|
538
|
+
const authKeyId = (0, _1_utilities_js_1.intFromBytes)(authKeyId_);
|
|
539
|
+
if (action_.key_fingerprint !== authKeyId) {
|
|
540
|
+
await this.#abortRekey(state, action_.exchange_id);
|
|
541
|
+
this.#clearInitiatedRekey(state);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const random_id = (0, _1_utilities_js_1.getRandomId)();
|
|
545
|
+
const action = {
|
|
546
|
+
_: "decryptedMessageActionCommitKey",
|
|
547
|
+
exchange_id: state.rekeyId,
|
|
548
|
+
key_fingerprint: authKeyId,
|
|
549
|
+
};
|
|
550
|
+
await this.#sendMessage({ _: "decryptedMessageService", random_id, action }, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
551
|
+
this.#installNewKey(state, authKey, authKeyId, authKeyId_, true);
|
|
552
|
+
this.#clearInitiatedRekey(state);
|
|
553
|
+
}
|
|
554
|
+
async #processDecryptedMessageActionRequestKey(chatId, action_) {
|
|
555
|
+
const state = this.#getSecretChatState(chatId);
|
|
556
|
+
if (!_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (state.previousAuthKey.byteLength !== 0) {
|
|
560
|
+
await this.#abortRekey(state, action_.exchange_id);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (state.toCommitId !== 0n) {
|
|
564
|
+
await this.#abortRekey(state, action_.exchange_id);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (state.rekeyId !== 0n) {
|
|
568
|
+
if (state.rekeyId >= action_.exchange_id) {
|
|
569
|
+
if (state.rekeyId === action_.exchange_id) {
|
|
570
|
+
this.#clearInitiatedRekey(state);
|
|
571
|
+
}
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
this.#clearInitiatedRekey(state);
|
|
575
|
+
}
|
|
576
|
+
const gA = (0, _1_utilities_js_1.intFromBytes)(action_.g_a, { byteOrder: "big", isSigned: false });
|
|
577
|
+
if (!(0, _0_password_js_1.isGoodModExpFirst)(gA, state.prime)) {
|
|
578
|
+
await this.#abortRekey(state, action_.exchange_id);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
let b;
|
|
582
|
+
let gB;
|
|
583
|
+
do {
|
|
584
|
+
b = (0, _1_utilities_js_1.getRandomInt)(256, false);
|
|
585
|
+
gB = (0, _1_utilities_js_1.modExp)(BigInt(state.g), b, state.prime);
|
|
586
|
+
} while (!(0, _0_password_js_1.isGoodModExpFirst)(gB, state.prime));
|
|
587
|
+
// pow(g_a, b) mod p
|
|
588
|
+
const authKey = (0, _1_utilities_js_1.intToBytes)((0, _1_utilities_js_1.modExp)(gA, b, state.prime), 256, { byteOrder: "big", isSigned: false });
|
|
589
|
+
const authKeyId_ = (await (0, _1_utilities_js_1.sha1)(authKey)).subarray(-8);
|
|
590
|
+
const authKeyId = (0, _1_utilities_js_1.intFromBytes)(authKeyId_);
|
|
591
|
+
// pow(g,b) mod p
|
|
592
|
+
const g_b = (0, _1_utilities_js_1.intToBytes)(gB, 256, { byteOrder: "big", isSigned: false });
|
|
593
|
+
const action = {
|
|
594
|
+
_: "decryptedMessageActionAcceptKey",
|
|
595
|
+
exchange_id: action_.exchange_id,
|
|
596
|
+
g_b,
|
|
597
|
+
key_fingerprint: authKeyId,
|
|
598
|
+
};
|
|
599
|
+
const random_id = (0, _1_utilities_js_1.getRandomId)();
|
|
600
|
+
state.toCommitId = action_.exchange_id;
|
|
601
|
+
state.toCommitAuthKey = authKey;
|
|
602
|
+
state.toCommitAuthKeyId = authKeyId;
|
|
603
|
+
state.toCommitAuthKeyId_ = authKeyId_;
|
|
604
|
+
await this.#sendMessage({ _: "decryptedMessageService", random_id, action }, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
605
|
+
}
|
|
606
|
+
async #processDecryptedMessageActionCommitKey(chatId, action) {
|
|
607
|
+
const state = this.#getSecretChatState(chatId);
|
|
608
|
+
if (state.toCommitId !== action.exchange_id) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (state.toCommitAuthKeyId !== action.key_fingerprint || !_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
612
|
+
this.#L.debug(`discarding secret chat ${chatId}: re-key fingerprint mismatch`);
|
|
613
|
+
await this.#c.invoke({ _: "messages.discardEncryption", chat_id: chatId });
|
|
614
|
+
throw new TypeError("Secret chat re-key fingerprint mismatch.");
|
|
615
|
+
}
|
|
616
|
+
this.#installNewKey(state, state.toCommitAuthKey, state.toCommitAuthKeyId, state.toCommitAuthKeyId_, false);
|
|
617
|
+
state.toCommitId = 0n;
|
|
618
|
+
state.toCommitAuthKey = new Uint8Array();
|
|
619
|
+
state.toCommitAuthKeyId = 0n;
|
|
620
|
+
state.toCommitAuthKeyId_ = new Uint8Array();
|
|
621
|
+
const noop = { _: "decryptedMessageActionNoop" };
|
|
622
|
+
await this.#sendMessage({ _: "decryptedMessageService", random_id: (0, _1_utilities_js_1.getRandomId)(), action: noop }, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
623
|
+
this.#clearPreviousKey(state);
|
|
624
|
+
}
|
|
625
|
+
async #handleDecryptedMessageLayer(chatId, decryptedMessageLayer, encryptedMessage) {
|
|
626
|
+
const state = this.#getSecretChatState(chatId);
|
|
627
|
+
if (_2_tl_js_1.SecretChats.is("decryptedMessage", decryptedMessageLayer.message)) {
|
|
628
|
+
const secretMessage = (0, _2_secret_message_js_1.constructSecretMessage)(state.encryptedChat.id, decryptedMessageLayer.message, encryptedMessage);
|
|
629
|
+
this.#c.handleUpdate({ type: "secretMessage", secretMessage });
|
|
630
|
+
}
|
|
631
|
+
else if (_2_tl_js_1.SecretChats.is("decryptedMessageService", decryptedMessageLayer.message)) {
|
|
632
|
+
await this.#processServiceMessage(state.encryptedChat.id, decryptedMessageLayer.message);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
async #processServiceMessage(chatId, message) {
|
|
636
|
+
switch (message.action._) {
|
|
637
|
+
case "decryptedMessageActionAcceptKey":
|
|
638
|
+
await this.#processDecryptedMessageActionAcceptKey(chatId, message.action);
|
|
639
|
+
break;
|
|
640
|
+
case "decryptedMessageActionRequestKey":
|
|
641
|
+
await this.#processDecryptedMessageActionRequestKey(chatId, message.action);
|
|
642
|
+
break;
|
|
643
|
+
case "decryptedMessageActionCommitKey":
|
|
644
|
+
await this.#processDecryptedMessageActionCommitKey(chatId, message.action);
|
|
645
|
+
break;
|
|
646
|
+
case "decryptedMessageActionAbortKey": {
|
|
647
|
+
const state = this.#getSecretChatState(chatId);
|
|
648
|
+
if (state.rekeyId === message.action.exchange_id) {
|
|
649
|
+
this.#clearInitiatedRekey(state);
|
|
650
|
+
}
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
async #processUpdateNewMessageEncrypted(update) {
|
|
656
|
+
try {
|
|
657
|
+
return await this.#processUpdateNewMessageEncryptedInner(update);
|
|
658
|
+
}
|
|
659
|
+
finally {
|
|
660
|
+
await this.#getSecretChatState(update.message.chat_id).commit(this.#c.messageStorage.storage);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
async #processUpdateNewMessageEncryptedInner(update) {
|
|
664
|
+
const state = this.#getSecretChatState(update.message.chat_id);
|
|
665
|
+
if (!_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
666
|
+
this.#L.debug("ignoring encrypted message");
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
const isCreator = Number(state.encryptedChat.admin_id) === await this.#c.getSelfId();
|
|
670
|
+
const receivedKeyId = update.message.bytes.subarray(0, 8);
|
|
671
|
+
let authKey = state.authKey;
|
|
672
|
+
let authKeyId = state.authKeyId_;
|
|
673
|
+
let pendingKey = false;
|
|
674
|
+
if ((0, _0_deps_js_1.equals)(receivedKeyId, state.toCommitAuthKeyId_)) {
|
|
675
|
+
authKey = state.toCommitAuthKey;
|
|
676
|
+
authKeyId = state.toCommitAuthKeyId_;
|
|
677
|
+
pendingKey = true;
|
|
678
|
+
}
|
|
679
|
+
else if ((0, _0_deps_js_1.equals)(receivedKeyId, state.previousAuthKeyId_)) {
|
|
680
|
+
authKey = state.previousAuthKey;
|
|
681
|
+
authKeyId = state.previousAuthKeyId_;
|
|
682
|
+
}
|
|
683
|
+
const decryptedMessage = await this.#decryptMessage(authKeyId, authKey, isCreator, update.message.bytes);
|
|
684
|
+
this.#L.debug("received", decryptedMessage);
|
|
685
|
+
if (_2_tl_js_1.SecretChats.is("decryptedMessageLayer", decryptedMessage)) {
|
|
686
|
+
const x = isCreator ? 0 : 1;
|
|
687
|
+
const rawOutSeqNo = (decryptedMessage.out_seq_no - x) / 2;
|
|
688
|
+
if (pendingKey) {
|
|
689
|
+
this.#installNewKey(state, state.toCommitAuthKey, state.toCommitAuthKeyId, state.toCommitAuthKeyId_, false, rawOutSeqNo);
|
|
690
|
+
++state.authKeyUseCount;
|
|
691
|
+
state.toCommitId = 0n;
|
|
692
|
+
state.toCommitAuthKey = new Uint8Array();
|
|
693
|
+
state.toCommitAuthKeyId = 0n;
|
|
694
|
+
state.toCommitAuthKeyId_ = new Uint8Array();
|
|
695
|
+
}
|
|
696
|
+
else if ((0, _0_deps_js_1.equals)(authKeyId, state.authKeyId_)) {
|
|
697
|
+
++state.authKeyUseCount;
|
|
698
|
+
if (state.previousAuthKey.byteLength !== 0) {
|
|
699
|
+
state.previousAuthKeyDiscardAfterSeqNo = rawOutSeqNo;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
await this.#checkGap(state.encryptedChat.id, decryptedMessage, update.message);
|
|
703
|
+
if (pendingKey && _2_tl_js_1.Api.is("encryptedChat", state.encryptedChat)) {
|
|
704
|
+
const noop = { _: "decryptedMessageActionNoop" };
|
|
705
|
+
await this.#sendMessage({ _: "decryptedMessageService", random_id: (0, _1_utilities_js_1.getRandomId)(), action: noop }, state.encryptedChat, state.authKey, state.authKeyId_);
|
|
706
|
+
}
|
|
707
|
+
if (state.previousAuthKeyDiscardAfterSeqNo >= 0 && state.inSeqNo > state.previousAuthKeyDiscardAfterSeqNo) {
|
|
708
|
+
this.#clearPreviousKey(state);
|
|
709
|
+
}
|
|
710
|
+
await this.#maybeStartRekey(state);
|
|
711
|
+
}
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
canHandleUpdate(update) {
|
|
715
|
+
return _2_tl_js_1.Api.isOneOf(secretChatManagerUpdates, update);
|
|
716
|
+
}
|
|
717
|
+
async handleUpdate(update) {
|
|
718
|
+
if (_2_tl_js_1.Api.is("updateNewEncryptedMessage", update)) {
|
|
719
|
+
return await this.#processUpdateNewMessageEncrypted(update);
|
|
720
|
+
}
|
|
721
|
+
if (_2_tl_js_1.Api.is("encryptedChatDiscarded", update.chat)) {
|
|
722
|
+
const state = this.#states.get(update.chat.id);
|
|
723
|
+
if (state !== undefined) {
|
|
724
|
+
this.#states.delete(update.chat.id);
|
|
725
|
+
state.encryptedChat = update.chat;
|
|
726
|
+
await state.commit(this.#c.messageStorage.storage);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
const state = this.#getSecretChatState(update.chat.id);
|
|
731
|
+
if (_2_tl_js_1.Api.is("encryptedChat", update.chat)) {
|
|
732
|
+
const pending = state.pendingExponent;
|
|
733
|
+
if (pending !== 0n) {
|
|
734
|
+
const gB = (0, _1_utilities_js_1.intFromBytes)(update.chat.g_a_or_b, { byteOrder: "big", isSigned: false });
|
|
735
|
+
if (!(0, _0_password_js_1.isGoodModExpFirst)(gB, state.prime)) {
|
|
736
|
+
this.#L.debug("discarding secret chat", update.chat.id, "because an invalid g_b was received");
|
|
737
|
+
await this.#c.invoke({ _: "messages.discardEncryption", chat_id: update.chat.id });
|
|
738
|
+
state.pendingExponent = 0n;
|
|
739
|
+
throw new TypeError("Received invalid g_b.");
|
|
740
|
+
}
|
|
741
|
+
const authKey = (0, _1_utilities_js_1.intToBytes)((0, _1_utilities_js_1.modExp)(gB, pending, state.prime), 256, { byteOrder: "big", isSigned: false });
|
|
742
|
+
const authKeyId_ = (await (0, _1_utilities_js_1.sha1)(authKey)).subarray(-8);
|
|
743
|
+
const authKeyId = (0, _1_utilities_js_1.intFromBytes)(authKeyId_);
|
|
744
|
+
if (authKeyId !== update.chat.key_fingerprint) {
|
|
745
|
+
this.#L.debug("discarding secret chat", update.chat.id, "because of key fingerprint mismatch");
|
|
746
|
+
await this.#c.invoke({ _: "messages.discardEncryption", chat_id: update.chat.id });
|
|
747
|
+
state.pendingExponent = 0n;
|
|
748
|
+
throw new TypeError("Secret chat key fingerprint mismatch.");
|
|
749
|
+
}
|
|
750
|
+
state.authKey = authKey;
|
|
751
|
+
state.authKeyId = authKeyId;
|
|
752
|
+
state.authKeyId_ = authKeyId_;
|
|
753
|
+
state.authKeyCreatedAt = Date.now();
|
|
754
|
+
state.authKeyUseCount = 0;
|
|
755
|
+
state.isAuthKeyUsed = false;
|
|
756
|
+
state.pendingExponent = 0n;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
state.encryptedChat = update.chat;
|
|
760
|
+
}
|
|
761
|
+
const secretChat = (0, _0_secret_chat_js_1.constructSecretChat)(update.chat);
|
|
762
|
+
return { type: "secretChat", secretChat };
|
|
763
|
+
}
|
|
764
|
+
async #startRekey(encryptedChat, authKeyId, authKey) {
|
|
765
|
+
const state = this.#getSecretChatState(encryptedChat.id);
|
|
766
|
+
if (state.rekeyId !== 0n || state.toCommitId !== 0n || state.previousAuthKey.byteLength !== 0) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
let g_a_;
|
|
770
|
+
let a;
|
|
771
|
+
do {
|
|
772
|
+
a = (0, _1_utilities_js_1.getRandomInt)(256, false);
|
|
773
|
+
g_a_ = (0, _1_utilities_js_1.modExp)(BigInt(state.g), a, state.prime);
|
|
774
|
+
} while (!(0, _0_password_js_1.isGoodModExpFirst)(g_a_, state.prime));
|
|
775
|
+
const exchange_id = (0, _1_utilities_js_1.getRandomId)();
|
|
776
|
+
state.rekeyId = exchange_id;
|
|
777
|
+
state.rekeyA = a;
|
|
778
|
+
const g_a = (0, _1_utilities_js_1.intToBytes)(g_a_, 256, { byteOrder: "big", isSigned: false });
|
|
779
|
+
const random_id = (0, _1_utilities_js_1.getRandomId)();
|
|
780
|
+
const action = {
|
|
781
|
+
_: "decryptedMessageActionRequestKey",
|
|
782
|
+
exchange_id,
|
|
783
|
+
g_a,
|
|
784
|
+
};
|
|
785
|
+
try {
|
|
786
|
+
await this.#sendMessage({
|
|
787
|
+
_: "decryptedMessageService",
|
|
788
|
+
random_id,
|
|
789
|
+
action,
|
|
790
|
+
}, encryptedChat, authKey, authKeyId);
|
|
791
|
+
}
|
|
792
|
+
catch (err) {
|
|
793
|
+
this.#clearInitiatedRekey(state);
|
|
794
|
+
throw err;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
async #maybeStartRekey(state) {
|
|
798
|
+
if (!_2_tl_js_1.Api.is("encryptedChat", state.encryptedChat) || state.rekeyId !== 0n || state.toCommitId !== 0n || state.previousAuthKey.byteLength !== 0) {
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (state.authKeyUseCount > 100 || state.isAuthKeyUsed && Date.now() - state.authKeyCreatedAt >= _0_deps_js_1.WEEK) {
|
|
802
|
+
await this.#startRekey(state.encryptedChat, state.authKeyId_, state.authKey);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
exports.SecretChatManager = SecretChatManager;
|
|
807
|
+
_a = SecretChatManager;
|