@mtcute/core 0.29.7 → 0.30.1
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/client.cjs +8 -0
- package/client.js +8 -0
- package/highlevel/client.d.cts +67 -7
- package/highlevel/client.d.ts +67 -7
- package/highlevel/methods/chats/join-chat.cjs +38 -16
- package/highlevel/methods/chats/join-chat.d.cts +20 -6
- package/highlevel/methods/chats/join-chat.d.ts +20 -6
- package/highlevel/methods/chats/join-chat.js +38 -16
- package/highlevel/methods/chats/save-draft.d.cts +4 -1
- package/highlevel/methods/chats/save-draft.d.ts +4 -1
- package/highlevel/methods/files/normalize-input-media.cjs +8 -0
- package/highlevel/methods/files/normalize-input-media.js +9 -1
- package/highlevel/methods/messages/create-rich-streaming-draft.cjs +39 -0
- package/highlevel/methods/messages/create-rich-streaming-draft.d.cts +42 -0
- package/highlevel/methods/messages/create-rich-streaming-draft.d.ts +42 -0
- package/highlevel/methods/messages/create-rich-streaming-draft.js +34 -0
- package/highlevel/methods/messages/normalize-rich-message.cjs +157 -0
- package/highlevel/methods/messages/normalize-rich-message.d.cts +10 -0
- package/highlevel/methods/messages/normalize-rich-message.d.ts +10 -0
- package/highlevel/methods/messages/normalize-rich-message.js +152 -0
- package/highlevel/methods/messages/send-rich-message.cjs +49 -0
- package/highlevel/methods/messages/send-rich-message.d.cts +38 -0
- package/highlevel/methods/messages/send-rich-message.d.ts +38 -0
- package/highlevel/methods/messages/send-rich-message.js +44 -0
- package/highlevel/methods.d.cts +5 -0
- package/highlevel/methods.d.ts +5 -0
- package/highlevel/storage/service/peers.cjs +1 -0
- package/highlevel/storage/service/peers.js +1 -0
- package/highlevel/types/bots/inline-message/factories.cjs +14 -0
- package/highlevel/types/bots/inline-message/factories.d.cts +9 -1
- package/highlevel/types/bots/inline-message/factories.d.ts +9 -1
- package/highlevel/types/bots/inline-message/factories.js +14 -0
- package/highlevel/types/bots/inline-message/types.d.cts +17 -1
- package/highlevel/types/bots/inline-message/types.d.ts +17 -1
- package/highlevel/types/files/utils.d.cts +1 -1
- package/highlevel/types/files/utils.d.ts +1 -1
- package/highlevel/types/media/input-media/types.d.cts +1 -1
- package/highlevel/types/media/input-media/types.d.ts +1 -1
- package/highlevel/types/messages/index.d.cts +1 -0
- package/highlevel/types/messages/index.d.ts +1 -0
- package/highlevel/types/messages/message.cjs +5 -0
- package/highlevel/types/messages/message.d.cts +3 -0
- package/highlevel/types/messages/message.d.ts +3 -0
- package/highlevel/types/messages/message.js +5 -0
- package/highlevel/types/messages/rich/index.d.cts +4 -0
- package/highlevel/types/messages/rich/index.d.ts +4 -0
- package/highlevel/types/messages/rich/inner.cjs +219 -0
- package/highlevel/types/messages/rich/inner.d.cts +127 -0
- package/highlevel/types/messages/rich/inner.d.ts +127 -0
- package/highlevel/types/messages/rich/inner.js +214 -0
- package/highlevel/types/messages/rich/rich-message.cjs +50 -0
- package/highlevel/types/messages/rich/rich-message.d.cts +18 -0
- package/highlevel/types/messages/rich/rich-message.d.ts +18 -0
- package/highlevel/types/messages/rich/rich-message.js +45 -0
- package/highlevel/types/messages/rich/types.d.cts +76 -0
- package/highlevel/types/messages/rich/types.d.ts +76 -0
- package/highlevel/types/peers/full-chat.cjs +6 -0
- package/highlevel/types/peers/full-chat.d.cts +2 -0
- package/highlevel/types/peers/full-chat.d.ts +2 -0
- package/highlevel/types/peers/full-chat.js +6 -0
- package/highlevel/types/peers/user.cjs +4 -0
- package/highlevel/types/peers/user.d.cts +2 -0
- package/highlevel/types/peers/user.d.ts +2 -0
- package/highlevel/types/peers/user.js +4 -0
- package/highlevel/types/updates/user-typing-update.cjs +2 -0
- package/highlevel/types/updates/user-typing-update.js +2 -0
- package/highlevel/updates/manager.cjs +195 -0
- package/highlevel/updates/manager.js +195 -0
- package/highlevel/utils/entities.cjs +176 -0
- package/highlevel/utils/entities.d.cts +18 -0
- package/highlevel/utils/entities.d.ts +18 -0
- package/highlevel/utils/entities.js +177 -1
- package/highlevel/utils/inspectable.cjs +1 -0
- package/highlevel/utils/inspectable.d.cts +1 -0
- package/highlevel/utils/inspectable.d.ts +1 -0
- package/highlevel/utils/inspectable.js +1 -0
- package/index.cjs +4 -0
- package/index.js +4 -0
- package/methods.cjs +4 -0
- package/methods.js +4 -0
- package/network/flood-control.cjs +149 -0
- package/network/flood-control.d.cts +79 -0
- package/network/flood-control.d.ts +79 -0
- package/network/flood-control.js +144 -0
- package/network/flood-control.test.d.cts +1 -0
- package/network/flood-control.test.d.ts +1 -0
- package/network/mtproto-session.cjs +1 -21
- package/network/mtproto-session.d.cts +1 -5
- package/network/mtproto-session.d.ts +1 -5
- package/network/mtproto-session.js +2 -22
- package/network/mtproxy/_fake-tls.cjs +169 -107
- package/network/mtproxy/_fake-tls.js +169 -107
- package/network/mtproxy/_fake-tls.test.d.cts +1 -0
- package/network/mtproxy/_fake-tls.test.d.ts +1 -0
- package/network/multi-session-connection.cjs +26 -16
- package/network/multi-session-connection.d.cts +1 -1
- package/network/multi-session-connection.d.ts +1 -1
- package/network/multi-session-connection.js +26 -16
- package/network/multi-session-connection.test.d.cts +1 -0
- package/network/multi-session-connection.test.d.ts +1 -0
- package/network/network-manager.cjs +3 -2
- package/network/network-manager.d.cts +12 -0
- package/network/network-manager.d.ts +12 -0
- package/network/network-manager.js +3 -2
- package/network/persistent-connection.cjs +25 -1
- package/network/persistent-connection.d.cts +5 -0
- package/network/persistent-connection.d.ts +5 -0
- package/network/persistent-connection.js +25 -1
- package/network/session-connection.cjs +5 -10
- package/network/session-connection.d.cts +0 -2
- package/network/session-connection.d.ts +0 -2
- package/network/session-connection.js +5 -10
- package/package.json +1 -1
- package/tl/api-schema.json +1 -1
- package/tl/binary/reader.cjs +182 -26
- package/tl/binary/reader.js +182 -26
- package/tl/binary/writer.cjs +437 -20
- package/tl/binary/writer.js +437 -20
- package/tl/compat/reader.cjs +23 -0
- package/tl/compat/reader.js +23 -0
- package/tl/index.d.cts +425 -14
- package/tl/index.d.ts +425 -14
- package/tl/inner-tl.cjs +22 -8
- package/tl/inner-tl.js +22 -8
- package/types/utils.d.cts +4 -0
- package/types/utils.d.ts +4 -0
- package/utils/binary/compat.cjs +79 -0
- package/utils/binary/compat.js +79 -0
- package/utils/index.d.cts +1 -0
- package/utils/index.d.ts +1 -0
- package/utils/long-utils.cjs +10 -0
- package/utils/long-utils.js +10 -0
- package/utils.cjs +9 -0
- package/utils.js +11 -2
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
8
8
|
const io = require("@fuman/io");
|
|
9
9
|
const utils = require("@fuman/utils");
|
|
10
10
|
const MAX_TLS_PACKET_LENGTH = 2878;
|
|
11
|
+
const MAX_TLS_HELLO_SIZE = 4096;
|
|
11
12
|
const KEY_MOD = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDn;
|
|
12
13
|
const QUAD_RES_MOD = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDn;
|
|
13
14
|
const QUAD_RES_POW = 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6n;
|
|
@@ -34,7 +35,11 @@ function _isQuadraticResidue(a) {
|
|
|
34
35
|
return r === 1n;
|
|
35
36
|
}
|
|
36
37
|
function executeTlsOperations(h) {
|
|
37
|
-
h.string([22, 3, 1
|
|
38
|
+
h.string([22, 3, 1]);
|
|
39
|
+
h.beginScope();
|
|
40
|
+
h.string([1, 0]);
|
|
41
|
+
h.beginScope();
|
|
42
|
+
h.string([3, 3]);
|
|
38
43
|
h.zero(32);
|
|
39
44
|
h.string([32]);
|
|
40
45
|
h.random(32);
|
|
@@ -72,67 +77,34 @@ function executeTlsOperations(h) {
|
|
|
72
77
|
0,
|
|
73
78
|
53,
|
|
74
79
|
1,
|
|
75
|
-
0
|
|
76
|
-
1,
|
|
77
|
-
147
|
|
80
|
+
0
|
|
78
81
|
]);
|
|
79
|
-
h.grease(2);
|
|
80
|
-
h.string([0, 0, 0, 0]);
|
|
81
|
-
h.beginScope();
|
|
82
82
|
h.beginScope();
|
|
83
|
-
h.
|
|
84
|
-
h.
|
|
85
|
-
h.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
0,
|
|
109
|
-
0,
|
|
110
|
-
16,
|
|
111
|
-
0,
|
|
112
|
-
14,
|
|
113
|
-
0,
|
|
114
|
-
12,
|
|
115
|
-
2,
|
|
116
|
-
104,
|
|
117
|
-
50,
|
|
118
|
-
8,
|
|
119
|
-
104,
|
|
120
|
-
116,
|
|
121
|
-
116,
|
|
122
|
-
112,
|
|
123
|
-
47,
|
|
124
|
-
49,
|
|
125
|
-
46,
|
|
126
|
-
49,
|
|
127
|
-
0,
|
|
128
|
-
5,
|
|
129
|
-
0,
|
|
130
|
-
5,
|
|
131
|
-
1,
|
|
132
|
-
0,
|
|
133
|
-
0,
|
|
134
|
-
0,
|
|
135
|
-
0,
|
|
83
|
+
h.grease(2);
|
|
84
|
+
h.string([0, 0]);
|
|
85
|
+
h.permutation([
|
|
86
|
+
(w) => {
|
|
87
|
+
w.string([0, 0]);
|
|
88
|
+
w.beginScope();
|
|
89
|
+
w.beginScope();
|
|
90
|
+
w.string([0]);
|
|
91
|
+
w.beginScope();
|
|
92
|
+
w.domain();
|
|
93
|
+
w.endScope();
|
|
94
|
+
w.endScope();
|
|
95
|
+
w.endScope();
|
|
96
|
+
},
|
|
97
|
+
(w) => w.string([0, 5, 0, 5, 1, 0, 0, 0, 0]),
|
|
98
|
+
// status_request
|
|
99
|
+
(w) => {
|
|
100
|
+
w.string([0, 10, 0, 12, 0, 10]);
|
|
101
|
+
w.grease(4);
|
|
102
|
+
w.string([17, 236, 0, 29, 0, 23, 0, 24]);
|
|
103
|
+
},
|
|
104
|
+
(w) => w.string([0, 11, 0, 2, 1, 0]),
|
|
105
|
+
// ec_point_formats
|
|
106
|
+
(w) => w.string([
|
|
107
|
+
// signature_algorithms
|
|
136
108
|
0,
|
|
137
109
|
13,
|
|
138
110
|
0,
|
|
@@ -154,27 +126,76 @@ function executeTlsOperations(h) {
|
|
|
154
126
|
8,
|
|
155
127
|
6,
|
|
156
128
|
6,
|
|
157
|
-
1
|
|
129
|
+
1
|
|
130
|
+
]),
|
|
131
|
+
(w) => w.string([
|
|
132
|
+
// application_layer_protocol_negotiation (ALPN)
|
|
158
133
|
0,
|
|
159
|
-
|
|
160
|
-
0,
|
|
161
|
-
0,
|
|
162
|
-
0,
|
|
163
|
-
51,
|
|
134
|
+
16,
|
|
164
135
|
0,
|
|
165
|
-
|
|
136
|
+
14,
|
|
166
137
|
0,
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
138
|
+
12,
|
|
139
|
+
2,
|
|
140
|
+
104,
|
|
141
|
+
50,
|
|
142
|
+
8,
|
|
143
|
+
104,
|
|
144
|
+
116,
|
|
145
|
+
116,
|
|
146
|
+
112,
|
|
147
|
+
47,
|
|
148
|
+
49,
|
|
149
|
+
46,
|
|
150
|
+
49
|
|
151
|
+
]),
|
|
152
|
+
(w) => w.string([0, 18, 0, 0]),
|
|
153
|
+
// signed_certificate_timestamp
|
|
154
|
+
(w) => w.string([0, 23, 0, 0]),
|
|
155
|
+
// extended_master_secret
|
|
156
|
+
(w) => w.string([0, 27, 0, 3, 2, 0, 2]),
|
|
157
|
+
// compress_certificate
|
|
158
|
+
(w) => w.string([0, 35, 0, 0]),
|
|
159
|
+
// session_ticket
|
|
160
|
+
(w) => {
|
|
161
|
+
w.string([0, 43, 0, 7, 6]);
|
|
162
|
+
w.grease(6);
|
|
163
|
+
w.string([3, 4, 3, 3]);
|
|
164
|
+
},
|
|
165
|
+
(w) => w.string([0, 45, 0, 2, 1, 1]),
|
|
166
|
+
// psk_key_exchange_modes
|
|
167
|
+
(w) => {
|
|
168
|
+
w.string([0, 51, 4, 239, 4, 237]);
|
|
169
|
+
w.grease(4);
|
|
170
|
+
w.string([0, 1, 0, 17, 236, 4, 192]);
|
|
171
|
+
w.mlKem768Key();
|
|
172
|
+
w.key();
|
|
173
|
+
w.string([0, 29, 0, 32]);
|
|
174
|
+
w.key();
|
|
175
|
+
},
|
|
176
|
+
(w) => w.string([68, 205, 0, 5, 0, 3, 2, 104, 50]),
|
|
177
|
+
// application_settings (ALPS)
|
|
178
|
+
(w) => {
|
|
179
|
+
w.string([254, 13]);
|
|
180
|
+
w.beginScope();
|
|
181
|
+
w.string([0, 0, 1, 0, 1]);
|
|
182
|
+
w.random(1);
|
|
183
|
+
w.string([0, 32]);
|
|
184
|
+
w.random(32);
|
|
185
|
+
w.beginScope();
|
|
186
|
+
w.echPayload();
|
|
187
|
+
w.endScope();
|
|
188
|
+
w.endScope();
|
|
189
|
+
},
|
|
190
|
+
(w) => w.string([255, 1, 0, 1, 0])
|
|
191
|
+
// renegotiation_info
|
|
192
|
+
]);
|
|
176
193
|
h.grease(3);
|
|
177
|
-
h.string([0, 1, 0
|
|
194
|
+
h.string([0, 1, 0]);
|
|
195
|
+
h.padding();
|
|
196
|
+
h.endScope();
|
|
197
|
+
h.endScope();
|
|
198
|
+
h.endScope();
|
|
178
199
|
}
|
|
179
200
|
function initGrease(crypto, size) {
|
|
180
201
|
const buf = crypto.randomBytes(size);
|
|
@@ -189,12 +210,12 @@ function initGrease(crypto, size) {
|
|
|
189
210
|
return buf;
|
|
190
211
|
}
|
|
191
212
|
class TlsHelloWriter {
|
|
192
|
-
constructor(crypto,
|
|
213
|
+
constructor(crypto, domain, grease) {
|
|
193
214
|
this.crypto = crypto;
|
|
194
215
|
this._domain = domain;
|
|
195
|
-
this.buf = utils.u8.alloc(
|
|
216
|
+
this.buf = utils.u8.alloc(MAX_TLS_HELLO_SIZE);
|
|
196
217
|
this.dv = utils.typed.toDataView(this.buf);
|
|
197
|
-
this._grease = initGrease(this.crypto, 7);
|
|
218
|
+
this._grease = grease ?? initGrease(this.crypto, 7);
|
|
198
219
|
}
|
|
199
220
|
buf;
|
|
200
221
|
dv;
|
|
@@ -235,6 +256,24 @@ class TlsHelloWriter {
|
|
|
235
256
|
}
|
|
236
257
|
}
|
|
237
258
|
}
|
|
259
|
+
// fake X25519MLKEM768 hybrid keyshare's ml-kem-768 part: 384 coefficients
|
|
260
|
+
// (mod 3329) packed 3 bytes per 2 coefficients, followed by 32 random bytes
|
|
261
|
+
mlKem768Key() {
|
|
262
|
+
const rnd = this.crypto.randomBytes(384 * 8);
|
|
263
|
+
const dv = utils.typed.toDataView(rnd);
|
|
264
|
+
for (let i = 0; i < 384; i++) {
|
|
265
|
+
const a = dv.getUint32(i * 8, true) % 3329;
|
|
266
|
+
const b = dv.getUint32(i * 8 + 4, true) % 3329;
|
|
267
|
+
this.buf[this.pos++] = a & 255;
|
|
268
|
+
this.buf[this.pos++] = (a >> 8) + ((b & 15) << 4);
|
|
269
|
+
this.buf[this.pos++] = b >> 4;
|
|
270
|
+
}
|
|
271
|
+
this.random(32);
|
|
272
|
+
}
|
|
273
|
+
// GREASE ECH payload: random bytes of length 144/176/208/240
|
|
274
|
+
echPayload() {
|
|
275
|
+
this.random(this._randomInt(4) * 32 + 144);
|
|
276
|
+
}
|
|
238
277
|
beginScope() {
|
|
239
278
|
this._scopes.push(this.pos);
|
|
240
279
|
this.pos += 2;
|
|
@@ -244,29 +283,55 @@ class TlsHelloWriter {
|
|
|
244
283
|
if (begin === void 0) {
|
|
245
284
|
throw new Error("endScope called without beginScope");
|
|
246
285
|
}
|
|
247
|
-
|
|
248
|
-
const size = end - begin - 2;
|
|
249
|
-
this.dv.setUint16(begin, size);
|
|
286
|
+
this.dv.setUint16(begin, this.pos - begin - 2);
|
|
250
287
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
288
|
+
// render each part into its own buffer, then emit them in random order —
|
|
289
|
+
// mimics chrome's per-connection tls extension shuffling
|
|
290
|
+
permutation(parts) {
|
|
291
|
+
const rendered = parts.map((fn) => {
|
|
292
|
+
const sub = new TlsHelloWriter(this.crypto, this._domain, this._grease);
|
|
293
|
+
fn(sub);
|
|
294
|
+
return sub.buf.subarray(0, sub.pos);
|
|
295
|
+
});
|
|
296
|
+
for (let i = rendered.length - 1; i > 0; i--) {
|
|
297
|
+
const j = this._randomInt(i + 1);
|
|
298
|
+
const tmp = rendered[i];
|
|
299
|
+
rendered[i] = rendered[j];
|
|
300
|
+
rendered[j] = tmp;
|
|
301
|
+
}
|
|
302
|
+
for (const part of rendered) {
|
|
303
|
+
this.string(part);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// tls padding extension, only emitted for hellos smaller than 513 bytes
|
|
307
|
+
padding() {
|
|
308
|
+
if (this.pos >= 513) return;
|
|
309
|
+
const size = 513 - this.pos;
|
|
310
|
+
this.string([0, 21]);
|
|
254
311
|
this.beginScope();
|
|
255
|
-
this.zero(
|
|
312
|
+
this.zero(size);
|
|
256
313
|
this.endScope();
|
|
257
|
-
|
|
314
|
+
}
|
|
315
|
+
_randomInt(max) {
|
|
316
|
+
return utils.typed.toDataView(this.crypto.randomBytes(4)).getUint32(0, true) % max;
|
|
317
|
+
}
|
|
318
|
+
async finish(secret) {
|
|
319
|
+
const data = this.buf.subarray(0, this.pos);
|
|
320
|
+
const unixTime = ~~(Date.now() / 1e3);
|
|
321
|
+
const hash = await this.crypto.hmacSha256(data, secret);
|
|
258
322
|
const dv = utils.typed.toDataView(hash);
|
|
259
323
|
const old = dv.getInt32(28, true);
|
|
260
324
|
dv.setInt32(28, old ^ unixTime, true);
|
|
261
|
-
|
|
262
|
-
return
|
|
325
|
+
data.set(hash, 11);
|
|
326
|
+
return data;
|
|
263
327
|
}
|
|
264
328
|
}
|
|
265
329
|
async function generateFakeTlsHeader(domain, secret, crypto) {
|
|
266
|
-
const writer = new TlsHelloWriter(crypto,
|
|
330
|
+
const writer = new TlsHelloWriter(crypto, domain);
|
|
267
331
|
executeTlsOperations(writer);
|
|
268
332
|
return writer.finish(secret);
|
|
269
333
|
}
|
|
334
|
+
const EMPTY_BYTES = io.Bytes.from(utils.u8.empty);
|
|
270
335
|
class FakeTlsPacketCodec {
|
|
271
336
|
// protected _stream: Buffer = Buffer.alloc(0)
|
|
272
337
|
constructor(_inner) {
|
|
@@ -301,23 +366,20 @@ class FakeTlsPacketCodec {
|
|
|
301
366
|
}
|
|
302
367
|
async decode(reader, eof) {
|
|
303
368
|
if (eof) return null;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (!inner) {
|
|
317
|
-
reader.rewind(5 + length);
|
|
318
|
-
return null;
|
|
369
|
+
while (reader.available >= 5) {
|
|
370
|
+
const header = reader.readSync(3);
|
|
371
|
+
if (header[0] !== 23 || header[1] !== 3 || header[2] !== 3) {
|
|
372
|
+
throw new Error("Invalid TLS header");
|
|
373
|
+
}
|
|
374
|
+
const length = io.read.uint16be(reader);
|
|
375
|
+
if (reader.available < length) {
|
|
376
|
+
reader.rewind(5);
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
const inner = await this._inner.decode(io.Bytes.from(reader.readSync(length)), eof);
|
|
380
|
+
if (inner) return inner;
|
|
319
381
|
}
|
|
320
|
-
return
|
|
382
|
+
return this._inner.decode(EMPTY_BYTES, eof);
|
|
321
383
|
}
|
|
322
384
|
reset() {
|
|
323
385
|
this._isFirstTls = true;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Bytes, read } from "@fuman/io";
|
|
2
2
|
import { u8, typed, bigint } from "@fuman/utils";
|
|
3
3
|
const MAX_TLS_PACKET_LENGTH = 2878;
|
|
4
|
+
const MAX_TLS_HELLO_SIZE = 4096;
|
|
4
5
|
const KEY_MOD = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDn;
|
|
5
6
|
const QUAD_RES_MOD = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDn;
|
|
6
7
|
const QUAD_RES_POW = 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6n;
|
|
@@ -27,7 +28,11 @@ function _isQuadraticResidue(a) {
|
|
|
27
28
|
return r === 1n;
|
|
28
29
|
}
|
|
29
30
|
function executeTlsOperations(h) {
|
|
30
|
-
h.string([22, 3, 1
|
|
31
|
+
h.string([22, 3, 1]);
|
|
32
|
+
h.beginScope();
|
|
33
|
+
h.string([1, 0]);
|
|
34
|
+
h.beginScope();
|
|
35
|
+
h.string([3, 3]);
|
|
31
36
|
h.zero(32);
|
|
32
37
|
h.string([32]);
|
|
33
38
|
h.random(32);
|
|
@@ -65,67 +70,34 @@ function executeTlsOperations(h) {
|
|
|
65
70
|
0,
|
|
66
71
|
53,
|
|
67
72
|
1,
|
|
68
|
-
0
|
|
69
|
-
1,
|
|
70
|
-
147
|
|
73
|
+
0
|
|
71
74
|
]);
|
|
72
|
-
h.grease(2);
|
|
73
|
-
h.string([0, 0, 0, 0]);
|
|
74
|
-
h.beginScope();
|
|
75
75
|
h.beginScope();
|
|
76
|
-
h.
|
|
77
|
-
h.
|
|
78
|
-
h.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
0,
|
|
102
|
-
0,
|
|
103
|
-
16,
|
|
104
|
-
0,
|
|
105
|
-
14,
|
|
106
|
-
0,
|
|
107
|
-
12,
|
|
108
|
-
2,
|
|
109
|
-
104,
|
|
110
|
-
50,
|
|
111
|
-
8,
|
|
112
|
-
104,
|
|
113
|
-
116,
|
|
114
|
-
116,
|
|
115
|
-
112,
|
|
116
|
-
47,
|
|
117
|
-
49,
|
|
118
|
-
46,
|
|
119
|
-
49,
|
|
120
|
-
0,
|
|
121
|
-
5,
|
|
122
|
-
0,
|
|
123
|
-
5,
|
|
124
|
-
1,
|
|
125
|
-
0,
|
|
126
|
-
0,
|
|
127
|
-
0,
|
|
128
|
-
0,
|
|
76
|
+
h.grease(2);
|
|
77
|
+
h.string([0, 0]);
|
|
78
|
+
h.permutation([
|
|
79
|
+
(w) => {
|
|
80
|
+
w.string([0, 0]);
|
|
81
|
+
w.beginScope();
|
|
82
|
+
w.beginScope();
|
|
83
|
+
w.string([0]);
|
|
84
|
+
w.beginScope();
|
|
85
|
+
w.domain();
|
|
86
|
+
w.endScope();
|
|
87
|
+
w.endScope();
|
|
88
|
+
w.endScope();
|
|
89
|
+
},
|
|
90
|
+
(w) => w.string([0, 5, 0, 5, 1, 0, 0, 0, 0]),
|
|
91
|
+
// status_request
|
|
92
|
+
(w) => {
|
|
93
|
+
w.string([0, 10, 0, 12, 0, 10]);
|
|
94
|
+
w.grease(4);
|
|
95
|
+
w.string([17, 236, 0, 29, 0, 23, 0, 24]);
|
|
96
|
+
},
|
|
97
|
+
(w) => w.string([0, 11, 0, 2, 1, 0]),
|
|
98
|
+
// ec_point_formats
|
|
99
|
+
(w) => w.string([
|
|
100
|
+
// signature_algorithms
|
|
129
101
|
0,
|
|
130
102
|
13,
|
|
131
103
|
0,
|
|
@@ -147,27 +119,76 @@ function executeTlsOperations(h) {
|
|
|
147
119
|
8,
|
|
148
120
|
6,
|
|
149
121
|
6,
|
|
150
|
-
1
|
|
122
|
+
1
|
|
123
|
+
]),
|
|
124
|
+
(w) => w.string([
|
|
125
|
+
// application_layer_protocol_negotiation (ALPN)
|
|
151
126
|
0,
|
|
152
|
-
|
|
153
|
-
0,
|
|
154
|
-
0,
|
|
155
|
-
0,
|
|
156
|
-
51,
|
|
127
|
+
16,
|
|
157
128
|
0,
|
|
158
|
-
|
|
129
|
+
14,
|
|
159
130
|
0,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
131
|
+
12,
|
|
132
|
+
2,
|
|
133
|
+
104,
|
|
134
|
+
50,
|
|
135
|
+
8,
|
|
136
|
+
104,
|
|
137
|
+
116,
|
|
138
|
+
116,
|
|
139
|
+
112,
|
|
140
|
+
47,
|
|
141
|
+
49,
|
|
142
|
+
46,
|
|
143
|
+
49
|
|
144
|
+
]),
|
|
145
|
+
(w) => w.string([0, 18, 0, 0]),
|
|
146
|
+
// signed_certificate_timestamp
|
|
147
|
+
(w) => w.string([0, 23, 0, 0]),
|
|
148
|
+
// extended_master_secret
|
|
149
|
+
(w) => w.string([0, 27, 0, 3, 2, 0, 2]),
|
|
150
|
+
// compress_certificate
|
|
151
|
+
(w) => w.string([0, 35, 0, 0]),
|
|
152
|
+
// session_ticket
|
|
153
|
+
(w) => {
|
|
154
|
+
w.string([0, 43, 0, 7, 6]);
|
|
155
|
+
w.grease(6);
|
|
156
|
+
w.string([3, 4, 3, 3]);
|
|
157
|
+
},
|
|
158
|
+
(w) => w.string([0, 45, 0, 2, 1, 1]),
|
|
159
|
+
// psk_key_exchange_modes
|
|
160
|
+
(w) => {
|
|
161
|
+
w.string([0, 51, 4, 239, 4, 237]);
|
|
162
|
+
w.grease(4);
|
|
163
|
+
w.string([0, 1, 0, 17, 236, 4, 192]);
|
|
164
|
+
w.mlKem768Key();
|
|
165
|
+
w.key();
|
|
166
|
+
w.string([0, 29, 0, 32]);
|
|
167
|
+
w.key();
|
|
168
|
+
},
|
|
169
|
+
(w) => w.string([68, 205, 0, 5, 0, 3, 2, 104, 50]),
|
|
170
|
+
// application_settings (ALPS)
|
|
171
|
+
(w) => {
|
|
172
|
+
w.string([254, 13]);
|
|
173
|
+
w.beginScope();
|
|
174
|
+
w.string([0, 0, 1, 0, 1]);
|
|
175
|
+
w.random(1);
|
|
176
|
+
w.string([0, 32]);
|
|
177
|
+
w.random(32);
|
|
178
|
+
w.beginScope();
|
|
179
|
+
w.echPayload();
|
|
180
|
+
w.endScope();
|
|
181
|
+
w.endScope();
|
|
182
|
+
},
|
|
183
|
+
(w) => w.string([255, 1, 0, 1, 0])
|
|
184
|
+
// renegotiation_info
|
|
185
|
+
]);
|
|
169
186
|
h.grease(3);
|
|
170
|
-
h.string([0, 1, 0
|
|
187
|
+
h.string([0, 1, 0]);
|
|
188
|
+
h.padding();
|
|
189
|
+
h.endScope();
|
|
190
|
+
h.endScope();
|
|
191
|
+
h.endScope();
|
|
171
192
|
}
|
|
172
193
|
function initGrease(crypto, size) {
|
|
173
194
|
const buf = crypto.randomBytes(size);
|
|
@@ -182,12 +203,12 @@ function initGrease(crypto, size) {
|
|
|
182
203
|
return buf;
|
|
183
204
|
}
|
|
184
205
|
class TlsHelloWriter {
|
|
185
|
-
constructor(crypto,
|
|
206
|
+
constructor(crypto, domain, grease) {
|
|
186
207
|
this.crypto = crypto;
|
|
187
208
|
this._domain = domain;
|
|
188
|
-
this.buf = u8.alloc(
|
|
209
|
+
this.buf = u8.alloc(MAX_TLS_HELLO_SIZE);
|
|
189
210
|
this.dv = typed.toDataView(this.buf);
|
|
190
|
-
this._grease = initGrease(this.crypto, 7);
|
|
211
|
+
this._grease = grease ?? initGrease(this.crypto, 7);
|
|
191
212
|
}
|
|
192
213
|
buf;
|
|
193
214
|
dv;
|
|
@@ -228,6 +249,24 @@ class TlsHelloWriter {
|
|
|
228
249
|
}
|
|
229
250
|
}
|
|
230
251
|
}
|
|
252
|
+
// fake X25519MLKEM768 hybrid keyshare's ml-kem-768 part: 384 coefficients
|
|
253
|
+
// (mod 3329) packed 3 bytes per 2 coefficients, followed by 32 random bytes
|
|
254
|
+
mlKem768Key() {
|
|
255
|
+
const rnd = this.crypto.randomBytes(384 * 8);
|
|
256
|
+
const dv = typed.toDataView(rnd);
|
|
257
|
+
for (let i = 0; i < 384; i++) {
|
|
258
|
+
const a = dv.getUint32(i * 8, true) % 3329;
|
|
259
|
+
const b = dv.getUint32(i * 8 + 4, true) % 3329;
|
|
260
|
+
this.buf[this.pos++] = a & 255;
|
|
261
|
+
this.buf[this.pos++] = (a >> 8) + ((b & 15) << 4);
|
|
262
|
+
this.buf[this.pos++] = b >> 4;
|
|
263
|
+
}
|
|
264
|
+
this.random(32);
|
|
265
|
+
}
|
|
266
|
+
// GREASE ECH payload: random bytes of length 144/176/208/240
|
|
267
|
+
echPayload() {
|
|
268
|
+
this.random(this._randomInt(4) * 32 + 144);
|
|
269
|
+
}
|
|
231
270
|
beginScope() {
|
|
232
271
|
this._scopes.push(this.pos);
|
|
233
272
|
this.pos += 2;
|
|
@@ -237,29 +276,55 @@ class TlsHelloWriter {
|
|
|
237
276
|
if (begin === void 0) {
|
|
238
277
|
throw new Error("endScope called without beginScope");
|
|
239
278
|
}
|
|
240
|
-
|
|
241
|
-
const size = end - begin - 2;
|
|
242
|
-
this.dv.setUint16(begin, size);
|
|
279
|
+
this.dv.setUint16(begin, this.pos - begin - 2);
|
|
243
280
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
281
|
+
// render each part into its own buffer, then emit them in random order —
|
|
282
|
+
// mimics chrome's per-connection tls extension shuffling
|
|
283
|
+
permutation(parts) {
|
|
284
|
+
const rendered = parts.map((fn) => {
|
|
285
|
+
const sub = new TlsHelloWriter(this.crypto, this._domain, this._grease);
|
|
286
|
+
fn(sub);
|
|
287
|
+
return sub.buf.subarray(0, sub.pos);
|
|
288
|
+
});
|
|
289
|
+
for (let i = rendered.length - 1; i > 0; i--) {
|
|
290
|
+
const j = this._randomInt(i + 1);
|
|
291
|
+
const tmp = rendered[i];
|
|
292
|
+
rendered[i] = rendered[j];
|
|
293
|
+
rendered[j] = tmp;
|
|
294
|
+
}
|
|
295
|
+
for (const part of rendered) {
|
|
296
|
+
this.string(part);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// tls padding extension, only emitted for hellos smaller than 513 bytes
|
|
300
|
+
padding() {
|
|
301
|
+
if (this.pos >= 513) return;
|
|
302
|
+
const size = 513 - this.pos;
|
|
303
|
+
this.string([0, 21]);
|
|
247
304
|
this.beginScope();
|
|
248
|
-
this.zero(
|
|
305
|
+
this.zero(size);
|
|
249
306
|
this.endScope();
|
|
250
|
-
|
|
307
|
+
}
|
|
308
|
+
_randomInt(max) {
|
|
309
|
+
return typed.toDataView(this.crypto.randomBytes(4)).getUint32(0, true) % max;
|
|
310
|
+
}
|
|
311
|
+
async finish(secret) {
|
|
312
|
+
const data = this.buf.subarray(0, this.pos);
|
|
313
|
+
const unixTime = ~~(Date.now() / 1e3);
|
|
314
|
+
const hash = await this.crypto.hmacSha256(data, secret);
|
|
251
315
|
const dv = typed.toDataView(hash);
|
|
252
316
|
const old = dv.getInt32(28, true);
|
|
253
317
|
dv.setInt32(28, old ^ unixTime, true);
|
|
254
|
-
|
|
255
|
-
return
|
|
318
|
+
data.set(hash, 11);
|
|
319
|
+
return data;
|
|
256
320
|
}
|
|
257
321
|
}
|
|
258
322
|
async function generateFakeTlsHeader(domain, secret, crypto) {
|
|
259
|
-
const writer = new TlsHelloWriter(crypto,
|
|
323
|
+
const writer = new TlsHelloWriter(crypto, domain);
|
|
260
324
|
executeTlsOperations(writer);
|
|
261
325
|
return writer.finish(secret);
|
|
262
326
|
}
|
|
327
|
+
const EMPTY_BYTES = Bytes.from(u8.empty);
|
|
263
328
|
class FakeTlsPacketCodec {
|
|
264
329
|
// protected _stream: Buffer = Buffer.alloc(0)
|
|
265
330
|
constructor(_inner) {
|
|
@@ -294,23 +359,20 @@ class FakeTlsPacketCodec {
|
|
|
294
359
|
}
|
|
295
360
|
async decode(reader, eof) {
|
|
296
361
|
if (eof) return null;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (!inner) {
|
|
310
|
-
reader.rewind(5 + length);
|
|
311
|
-
return null;
|
|
362
|
+
while (reader.available >= 5) {
|
|
363
|
+
const header = reader.readSync(3);
|
|
364
|
+
if (header[0] !== 23 || header[1] !== 3 || header[2] !== 3) {
|
|
365
|
+
throw new Error("Invalid TLS header");
|
|
366
|
+
}
|
|
367
|
+
const length = read.uint16be(reader);
|
|
368
|
+
if (reader.available < length) {
|
|
369
|
+
reader.rewind(5);
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
const inner = await this._inner.decode(Bytes.from(reader.readSync(length)), eof);
|
|
373
|
+
if (inner) return inner;
|
|
312
374
|
}
|
|
313
|
-
return
|
|
375
|
+
return this._inner.decode(EMPTY_BYTES, eof);
|
|
314
376
|
}
|
|
315
377
|
reset() {
|
|
316
378
|
this._isFirstTls = true;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|