@mtcute/core 0.30.0 → 0.30.2
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 +63 -1
- package/highlevel/client.d.ts +63 -1
- package/highlevel/methods/files/download-iterable.cjs +80 -45
- package/highlevel/methods/files/download-iterable.js +80 -45
- package/highlevel/methods/files/normalize-input-media.cjs +9 -1
- package/highlevel/methods/files/normalize-input-media.js +10 -2
- package/highlevel/methods/files/upload-file.cjs +6 -1
- package/highlevel/methods/files/upload-file.js +6 -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 +168 -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 +163 -0
- package/highlevel/methods/messages/send-common.cjs +3 -2
- package/highlevel/methods/messages/send-common.d.cts +4 -1
- package/highlevel/methods/messages/send-common.d.ts +4 -1
- package/highlevel/methods/messages/send-common.js +3 -2
- 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 +3 -0
- package/highlevel/methods.d.ts +3 -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 +9 -0
- package/highlevel/types/files/utils.d.ts +9 -0
- 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 -1
- package/highlevel/types/messages/index.d.ts +1 -1
- package/highlevel/types/messages/message.cjs +1 -1
- package/highlevel/types/messages/message.d.cts +1 -1
- package/highlevel/types/messages/message.d.ts +1 -1
- package/highlevel/types/messages/message.js +1 -1
- 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-message.cjs → rich/rich-message.cjs} +6 -6
- package/highlevel/types/messages/{rich-message.d.cts → rich/rich-message.d.cts} +5 -5
- package/highlevel/types/messages/{rich-message.d.ts → rich/rich-message.d.ts} +5 -5
- package/highlevel/types/messages/{rich-message.js → rich/rich-message.js} +6 -6
- package/highlevel/types/messages/rich/types.d.cts +76 -0
- package/highlevel/types/messages/rich/types.d.ts +76 -0
- package/highlevel/updates/manager.cjs +10 -169
- package/highlevel/updates/manager.js +10 -169
- 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/index.d.cts +1 -0
- package/highlevel/utils/index.d.ts +1 -0
- package/highlevel/utils/walk-page-blocks.cjs +164 -0
- package/highlevel/utils/walk-page-blocks.d.cts +9 -0
- package/highlevel/utils/walk-page-blocks.d.ts +9 -0
- package/highlevel/utils/walk-page-blocks.js +159 -0
- package/index.cjs +3 -1
- package/index.js +3 -1
- package/methods.cjs +4 -0
- package/methods.js +4 -0
- package/network/authorization.cjs +1 -1
- package/network/authorization.js +1 -1
- package/network/delay-gate.cjs +29 -0
- package/network/delay-gate.d.cts +5 -0
- package/network/delay-gate.d.ts +5 -0
- package/network/delay-gate.js +24 -0
- package/network/delay-gate.test.d.cts +1 -0
- package/network/delay-gate.test.d.ts +1 -0
- package/network/message-id-store.cjs +30 -0
- package/network/message-id-store.d.cts +7 -0
- package/network/message-id-store.d.ts +7 -0
- package/network/message-id-store.js +25 -0
- package/network/message-id-store.test.d.cts +1 -0
- package/network/message-id-store.test.d.ts +1 -0
- package/network/middlewares/bundle.cjs +2 -0
- package/network/middlewares/bundle.d.cts +1 -0
- package/network/middlewares/bundle.d.ts +1 -0
- package/network/middlewares/bundle.js +2 -0
- package/network/middlewares/default.cjs +6 -1
- package/network/middlewares/default.js +6 -1
- package/network/middlewares/flood-waiter.test.d.cts +1 -0
- package/network/middlewares/flood-waiter.test.d.ts +1 -0
- package/network/middlewares/internal-errors.test.d.cts +1 -0
- package/network/middlewares/internal-errors.test.d.ts +1 -0
- package/network/middlewares/media-throttle.cjs +65 -0
- package/network/middlewares/media-throttle.d.cts +5 -0
- package/network/middlewares/media-throttle.d.ts +5 -0
- package/network/middlewares/media-throttle.js +60 -0
- package/network/middlewares/media-throttle.test.d.cts +1 -0
- package/network/middlewares/media-throttle.test.d.ts +1 -0
- package/network/middlewares/on-error.test.d.cts +1 -0
- package/network/middlewares/on-error.test.d.ts +1 -0
- package/network/middlewares/on-method.test.d.cts +1 -0
- package/network/middlewares/on-method.test.d.ts +1 -0
- package/network/mtproto-session.cjs +9 -2
- package/network/mtproto-session.d.cts +5 -2
- package/network/mtproto-session.d.ts +5 -2
- package/network/mtproto-session.js +9 -2
- 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 +7 -0
- package/network/multi-session-connection.d.cts +2 -0
- package/network/multi-session-connection.d.ts +2 -0
- package/network/multi-session-connection.js +7 -0
- package/network/network-manager.cjs +7 -1
- package/network/network-manager.js +7 -1
- package/network/resource-limiter.cjs +60 -0
- package/network/resource-limiter.d.cts +11 -0
- package/network/resource-limiter.d.ts +11 -0
- package/network/resource-limiter.js +55 -0
- package/network/resource-limiter.test.d.cts +1 -0
- package/network/resource-limiter.test.d.ts +1 -0
- package/network/server-salt.cjs +7 -3
- package/network/server-salt.d.cts +2 -0
- package/network/server-salt.d.ts +2 -0
- package/network/server-salt.js +7 -3
- package/network/session-connection.cjs +106 -20
- package/network/session-connection.d.cts +11 -0
- package/network/session-connection.d.ts +11 -0
- package/network/session-connection.js +106 -20
- package/package.json +1 -1
- package/tl/api-schema.json +1 -1
- package/tl/binary/reader.cjs +115 -109
- package/tl/binary/reader.js +115 -109
- package/tl/binary/writer.cjs +243 -245
- package/tl/binary/writer.js +243 -245
- package/tl/index.d.cts +227 -216
- package/tl/index.d.ts +227 -216
- package/tl/inner-tl.cjs +3 -1
- package/tl/inner-tl.js +3 -1
- package/types/utils.d.cts +4 -0
- package/types/utils.d.ts +4 -0
- package/utils.cjs +4 -0
- package/utils.js +5 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { combineAbortSignals } from "../../utils/abort-signal.js";
|
|
2
|
+
import { ResourceLimiter } from "../resource-limiter.js";
|
|
3
|
+
const DOWNLOAD_RESOURCE_LIMIT = 2 * 1024 * 1024;
|
|
4
|
+
const DOWNLOAD_RESOURCE_LIMIT_PREMIUM = 16 * 1024 * 1024;
|
|
5
|
+
const UPLOAD_RESOURCE_LIMIT = 4 * 1024 * 1024;
|
|
6
|
+
function defaultResourceLimit(kind, isPremium) {
|
|
7
|
+
if (kind === "upload") return UPLOAD_RESOURCE_LIMIT;
|
|
8
|
+
return isPremium ? DOWNLOAD_RESOURCE_LIMIT_PREMIUM : DOWNLOAD_RESOURCE_LIMIT;
|
|
9
|
+
}
|
|
10
|
+
function mediaThrottle(params) {
|
|
11
|
+
const { getResourceLimit = defaultResourceLimit } = params ?? {};
|
|
12
|
+
const limiters = /* @__PURE__ */ new Map();
|
|
13
|
+
const getLimiter = (kind, dcId, isPremium) => {
|
|
14
|
+
let byDc = limiters.get(kind);
|
|
15
|
+
if (!byDc) {
|
|
16
|
+
byDc = /* @__PURE__ */ new Map();
|
|
17
|
+
limiters.set(kind, byDc);
|
|
18
|
+
}
|
|
19
|
+
const max = getResourceLimit(kind, isPremium);
|
|
20
|
+
let limiter = byDc.get(dcId);
|
|
21
|
+
if (!limiter) {
|
|
22
|
+
limiter = new ResourceLimiter(max);
|
|
23
|
+
byDc.set(dcId, limiter);
|
|
24
|
+
} else if (limiter.max !== max) {
|
|
25
|
+
limiter.setMax(max);
|
|
26
|
+
}
|
|
27
|
+
return limiter;
|
|
28
|
+
};
|
|
29
|
+
return async (ctx, next) => {
|
|
30
|
+
let kind;
|
|
31
|
+
let bytes = 0;
|
|
32
|
+
switch (ctx.request._) {
|
|
33
|
+
case "upload.getFile":
|
|
34
|
+
case "upload.getWebFile":
|
|
35
|
+
kind = ctx.params?.kind ?? "download";
|
|
36
|
+
bytes = ctx.request.limit;
|
|
37
|
+
break;
|
|
38
|
+
case "upload.saveFilePart":
|
|
39
|
+
case "upload.saveBigFilePart":
|
|
40
|
+
kind = "upload";
|
|
41
|
+
bytes = ctx.request.bytes.length;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
if (kind === void 0) return next(ctx);
|
|
45
|
+
const dcId = ctx.params?.dcId ?? ctx.manager.getPrimaryDcId();
|
|
46
|
+
const limiter = getLimiter(kind, dcId, ctx.manager.params.isPremium);
|
|
47
|
+
if (!limiter.tryAcquire(bytes)) {
|
|
48
|
+
const abortSignal = combineAbortSignals(ctx.manager.params.stopSignal, ctx.params?.abortSignal);
|
|
49
|
+
await limiter.acquire(bytes, abortSignal);
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return await next(ctx);
|
|
53
|
+
} finally {
|
|
54
|
+
limiter.release(bytes);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
mediaThrottle
|
|
60
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -12,6 +12,7 @@ const longUtils = require("../utils/long-utils.cjs");
|
|
|
12
12
|
const miscUtils = require("../utils/misc-utils.cjs");
|
|
13
13
|
const sortedArray = require("../utils/sorted-array.cjs");
|
|
14
14
|
const authKey = require("./auth-key.cjs");
|
|
15
|
+
const messageIdStore = require("./message-id-store.cjs");
|
|
15
16
|
const errors = require("../types/errors.cjs");
|
|
16
17
|
class MtprotoSession {
|
|
17
18
|
constructor(_crypto, log, _readerMap, _writerMap, _salts) {
|
|
@@ -21,6 +22,7 @@ class MtprotoSession {
|
|
|
21
22
|
this._writerMap = _writerMap;
|
|
22
23
|
this._salts = _salts;
|
|
23
24
|
this.log.prefix = `[SESSION ${this._sessionId.toString(16)}] `;
|
|
25
|
+
this._salts.setTimeSource(this.getServerTime.bind(this));
|
|
24
26
|
this._authKey = new authKey.AuthKey(_crypto, log, _readerMap);
|
|
25
27
|
this._authKeyTemp = new authKey.AuthKey(_crypto, log, _readerMap);
|
|
26
28
|
this._authKeyTempSecondary = new authKey.AuthKey(_crypto, log, _readerMap);
|
|
@@ -35,7 +37,7 @@ class MtprotoSession {
|
|
|
35
37
|
/// state ///
|
|
36
38
|
// recent msg ids
|
|
37
39
|
recentOutgoingMsgIds = new utils.LruSet(1e3, longUtils.LongSet);
|
|
38
|
-
recentIncomingMsgIds = new
|
|
40
|
+
recentIncomingMsgIds = new messageIdStore.MessageIdStore();
|
|
39
41
|
// <deno-tsignore>
|
|
40
42
|
recentStateRequests = new utils.LruMap(1e3, longUtils.LongMap);
|
|
41
43
|
// queues
|
|
@@ -58,6 +60,7 @@ class MtprotoSession {
|
|
|
58
60
|
lastPingRtt = Number.NaN;
|
|
59
61
|
lastPingTime = 0;
|
|
60
62
|
lastPingMsgId = Long.ZERO;
|
|
63
|
+
lastActivityTime = 0;
|
|
61
64
|
lastSessionCreatedUid = Long.ZERO;
|
|
62
65
|
initConnectionCalled = false;
|
|
63
66
|
authorizationPending = false;
|
|
@@ -80,11 +83,15 @@ class MtprotoSession {
|
|
|
80
83
|
this._authKeyTemp.reset();
|
|
81
84
|
this._authKeyTempSecondary.reset();
|
|
82
85
|
}
|
|
83
|
-
updateTimeOffset(offset) {
|
|
86
|
+
updateTimeOffset(offset, force = false) {
|
|
87
|
+
if (!force && offset <= this._timeOffset) return;
|
|
84
88
|
this.log.debug("time offset updated: %d", offset);
|
|
85
89
|
this._timeOffset = offset;
|
|
86
90
|
this._lastMessageId = Long.ZERO;
|
|
87
91
|
}
|
|
92
|
+
getServerTime() {
|
|
93
|
+
return Math.floor(performance.now() / 1e3) + this._timeOffset;
|
|
94
|
+
}
|
|
88
95
|
/**
|
|
89
96
|
* Reset session state and generate a new session ID.
|
|
90
97
|
*
|
|
@@ -5,6 +5,7 @@ import { ICryptoProvider, Logger, LongMap, SortedArray } from '../utils/index.js
|
|
|
5
5
|
import { ServerSaltManager } from './server-salt.js';
|
|
6
6
|
import { default as Long } from 'long';
|
|
7
7
|
import { AuthKey } from './auth-key.js';
|
|
8
|
+
import { MessageIdStore } from './message-id-store.js';
|
|
8
9
|
export interface PendingRpc {
|
|
9
10
|
method: string;
|
|
10
11
|
data: Uint8Array;
|
|
@@ -76,7 +77,7 @@ export declare class MtprotoSession {
|
|
|
76
77
|
_lastMessageId: Long;
|
|
77
78
|
_seqNo: number;
|
|
78
79
|
recentOutgoingMsgIds: LruSet<Long>;
|
|
79
|
-
recentIncomingMsgIds:
|
|
80
|
+
recentIncomingMsgIds: MessageIdStore;
|
|
80
81
|
recentStateRequests: LruMap<Long, Long[]>;
|
|
81
82
|
queuedRpc: Deque<PendingRpc>;
|
|
82
83
|
queuedAcks: Long[];
|
|
@@ -91,6 +92,7 @@ export declare class MtprotoSession {
|
|
|
91
92
|
lastPingRtt: number;
|
|
92
93
|
lastPingTime: number;
|
|
93
94
|
lastPingMsgId: Long;
|
|
95
|
+
lastActivityTime: number;
|
|
94
96
|
lastSessionCreatedUid: Long;
|
|
95
97
|
initConnectionCalled: boolean;
|
|
96
98
|
authorizationPending: boolean;
|
|
@@ -101,7 +103,8 @@ export declare class MtprotoSession {
|
|
|
101
103
|
*/
|
|
102
104
|
reset(withAuthKey?: boolean): void;
|
|
103
105
|
resetAuthKey(): void;
|
|
104
|
-
updateTimeOffset(offset: number): void;
|
|
106
|
+
updateTimeOffset(offset: number, force?: boolean): void;
|
|
107
|
+
getServerTime(): number;
|
|
105
108
|
/**
|
|
106
109
|
* Reset session state and generate a new session ID.
|
|
107
110
|
*
|
|
@@ -5,6 +5,7 @@ import { ICryptoProvider, Logger, LongMap, SortedArray } from '../utils/index.js
|
|
|
5
5
|
import { ServerSaltManager } from './server-salt.js';
|
|
6
6
|
import { default as Long } from 'long';
|
|
7
7
|
import { AuthKey } from './auth-key.js';
|
|
8
|
+
import { MessageIdStore } from './message-id-store.js';
|
|
8
9
|
export interface PendingRpc {
|
|
9
10
|
method: string;
|
|
10
11
|
data: Uint8Array;
|
|
@@ -76,7 +77,7 @@ export declare class MtprotoSession {
|
|
|
76
77
|
_lastMessageId: Long;
|
|
77
78
|
_seqNo: number;
|
|
78
79
|
recentOutgoingMsgIds: LruSet<Long>;
|
|
79
|
-
recentIncomingMsgIds:
|
|
80
|
+
recentIncomingMsgIds: MessageIdStore;
|
|
80
81
|
recentStateRequests: LruMap<Long, Long[]>;
|
|
81
82
|
queuedRpc: Deque<PendingRpc>;
|
|
82
83
|
queuedAcks: Long[];
|
|
@@ -91,6 +92,7 @@ export declare class MtprotoSession {
|
|
|
91
92
|
lastPingRtt: number;
|
|
92
93
|
lastPingTime: number;
|
|
93
94
|
lastPingMsgId: Long;
|
|
95
|
+
lastActivityTime: number;
|
|
94
96
|
lastSessionCreatedUid: Long;
|
|
95
97
|
initConnectionCalled: boolean;
|
|
96
98
|
authorizationPending: boolean;
|
|
@@ -101,7 +103,8 @@ export declare class MtprotoSession {
|
|
|
101
103
|
*/
|
|
102
104
|
reset(withAuthKey?: boolean): void;
|
|
103
105
|
resetAuthKey(): void;
|
|
104
|
-
updateTimeOffset(offset: number): void;
|
|
106
|
+
updateTimeOffset(offset: number, force?: boolean): void;
|
|
107
|
+
getServerTime(): number;
|
|
105
108
|
/**
|
|
106
109
|
* Reset session state and generate a new session ID.
|
|
107
110
|
*
|
|
@@ -5,6 +5,7 @@ import { randomLong, LongSet, LongMap, compareLongs } from "../utils/long-utils.
|
|
|
5
5
|
import { getRandomInt } from "../utils/misc-utils.js";
|
|
6
6
|
import { SortedArray } from "../utils/sorted-array.js";
|
|
7
7
|
import { AuthKey } from "./auth-key.js";
|
|
8
|
+
import { MessageIdStore } from "./message-id-store.js";
|
|
8
9
|
import { MtcuteError } from "../types/errors.js";
|
|
9
10
|
class MtprotoSession {
|
|
10
11
|
constructor(_crypto, log, _readerMap, _writerMap, _salts) {
|
|
@@ -14,6 +15,7 @@ class MtprotoSession {
|
|
|
14
15
|
this._writerMap = _writerMap;
|
|
15
16
|
this._salts = _salts;
|
|
16
17
|
this.log.prefix = `[SESSION ${this._sessionId.toString(16)}] `;
|
|
18
|
+
this._salts.setTimeSource(this.getServerTime.bind(this));
|
|
17
19
|
this._authKey = new AuthKey(_crypto, log, _readerMap);
|
|
18
20
|
this._authKeyTemp = new AuthKey(_crypto, log, _readerMap);
|
|
19
21
|
this._authKeyTempSecondary = new AuthKey(_crypto, log, _readerMap);
|
|
@@ -28,7 +30,7 @@ class MtprotoSession {
|
|
|
28
30
|
/// state ///
|
|
29
31
|
// recent msg ids
|
|
30
32
|
recentOutgoingMsgIds = new LruSet(1e3, LongSet);
|
|
31
|
-
recentIncomingMsgIds = new
|
|
33
|
+
recentIncomingMsgIds = new MessageIdStore();
|
|
32
34
|
// <deno-tsignore>
|
|
33
35
|
recentStateRequests = new LruMap(1e3, LongMap);
|
|
34
36
|
// queues
|
|
@@ -51,6 +53,7 @@ class MtprotoSession {
|
|
|
51
53
|
lastPingRtt = Number.NaN;
|
|
52
54
|
lastPingTime = 0;
|
|
53
55
|
lastPingMsgId = Long.ZERO;
|
|
56
|
+
lastActivityTime = 0;
|
|
54
57
|
lastSessionCreatedUid = Long.ZERO;
|
|
55
58
|
initConnectionCalled = false;
|
|
56
59
|
authorizationPending = false;
|
|
@@ -73,11 +76,15 @@ class MtprotoSession {
|
|
|
73
76
|
this._authKeyTemp.reset();
|
|
74
77
|
this._authKeyTempSecondary.reset();
|
|
75
78
|
}
|
|
76
|
-
updateTimeOffset(offset) {
|
|
79
|
+
updateTimeOffset(offset, force = false) {
|
|
80
|
+
if (!force && offset <= this._timeOffset) return;
|
|
77
81
|
this.log.debug("time offset updated: %d", offset);
|
|
78
82
|
this._timeOffset = offset;
|
|
79
83
|
this._lastMessageId = Long.ZERO;
|
|
80
84
|
}
|
|
85
|
+
getServerTime() {
|
|
86
|
+
return Math.floor(performance.now() / 1e3) + this._timeOffset;
|
|
87
|
+
}
|
|
81
88
|
/**
|
|
82
89
|
* Reset session state and generate a new session ID.
|
|
83
90
|
*
|
|
@@ -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;
|