@erfan_bahramali/tping 1.0.0 → 1.1.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.
Files changed (98) hide show
  1. package/README.md +76 -55
  2. package/dist/crypto/x25519.d.ts +10 -0
  3. package/dist/crypto/x25519.d.ts.map +1 -0
  4. package/dist/crypto/x25519.js +51 -0
  5. package/dist/crypto/x25519.js.map +1 -0
  6. package/dist/index.js +82 -39
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/attempt.d.ts +31 -0
  9. package/dist/lib/attempt.d.ts.map +1 -0
  10. package/dist/lib/attempt.js +94 -0
  11. package/dist/lib/attempt.js.map +1 -0
  12. package/dist/lib/checker.d.ts +21 -3
  13. package/dist/lib/checker.d.ts.map +1 -1
  14. package/dist/lib/checker.js +133 -49
  15. package/dist/lib/checker.js.map +1 -1
  16. package/dist/lib/datacenters.d.ts +15 -0
  17. package/dist/lib/datacenters.d.ts.map +1 -0
  18. package/dist/lib/datacenters.js +20 -0
  19. package/dist/lib/datacenters.js.map +1 -0
  20. package/dist/lib/parser.d.ts +18 -3
  21. package/dist/lib/parser.d.ts.map +1 -1
  22. package/dist/lib/parser.js +174 -78
  23. package/dist/lib/parser.js.map +1 -1
  24. package/dist/lib/ping.d.ts +13 -6
  25. package/dist/lib/ping.d.ts.map +1 -1
  26. package/dist/lib/ping.js +44 -44
  27. package/dist/lib/ping.js.map +1 -1
  28. package/dist/mtproto/exchange.d.ts +10 -0
  29. package/dist/mtproto/exchange.d.ts.map +1 -0
  30. package/dist/mtproto/exchange.js +20 -0
  31. package/dist/mtproto/exchange.js.map +1 -0
  32. package/dist/mtproto/fake-tls.d.ts +36 -44
  33. package/dist/mtproto/fake-tls.d.ts.map +1 -1
  34. package/dist/mtproto/fake-tls.js +174 -148
  35. package/dist/mtproto/fake-tls.js.map +1 -1
  36. package/dist/mtproto/messages.d.ts +23 -36
  37. package/dist/mtproto/messages.d.ts.map +1 -1
  38. package/dist/mtproto/messages.js +64 -90
  39. package/dist/mtproto/messages.js.map +1 -1
  40. package/dist/mtproto/obfuscation.d.ts +23 -20
  41. package/dist/mtproto/obfuscation.d.ts.map +1 -1
  42. package/dist/mtproto/obfuscation.js +64 -64
  43. package/dist/mtproto/obfuscation.js.map +1 -1
  44. package/dist/mtproto/secret.d.ts +28 -0
  45. package/dist/mtproto/secret.d.ts.map +1 -0
  46. package/dist/mtproto/secret.js +68 -0
  47. package/dist/mtproto/secret.js.map +1 -0
  48. package/dist/mtproto/transport.d.ts +28 -0
  49. package/dist/mtproto/transport.d.ts.map +1 -0
  50. package/dist/mtproto/transport.js +78 -0
  51. package/dist/mtproto/transport.js.map +1 -0
  52. package/dist/net/http-client.d.ts +37 -0
  53. package/dist/net/http-client.d.ts.map +1 -0
  54. package/dist/net/http-client.js +93 -0
  55. package/dist/net/http-client.js.map +1 -0
  56. package/dist/net/http-proxy.d.ts +26 -0
  57. package/dist/net/http-proxy.d.ts.map +1 -0
  58. package/dist/net/http-proxy.js +71 -0
  59. package/dist/net/http-proxy.js.map +1 -0
  60. package/dist/net/socks5.d.ts +15 -0
  61. package/dist/net/socks5.d.ts.map +1 -0
  62. package/dist/net/socks5.js +112 -0
  63. package/dist/net/socks5.js.map +1 -0
  64. package/dist/net/stream.d.ts +54 -0
  65. package/dist/net/stream.d.ts.map +1 -0
  66. package/dist/net/stream.js +142 -0
  67. package/dist/net/stream.js.map +1 -0
  68. package/dist/net/websocket.d.ts +19 -0
  69. package/dist/net/websocket.d.ts.map +1 -0
  70. package/dist/net/websocket.js +123 -0
  71. package/dist/net/websocket.js.map +1 -0
  72. package/dist/types/index.d.ts +40 -17
  73. package/dist/types/index.d.ts.map +1 -1
  74. package/dist/utils/format.d.ts +7 -14
  75. package/dist/utils/format.d.ts.map +1 -1
  76. package/dist/utils/format.js +101 -87
  77. package/dist/utils/format.js.map +1 -1
  78. package/dist/webproxy/check.d.ts +15 -0
  79. package/dist/webproxy/check.d.ts.map +1 -0
  80. package/dist/webproxy/check.js +423 -0
  81. package/dist/webproxy/check.js.map +1 -0
  82. package/dist/webproxy/frames.d.ts +30 -0
  83. package/dist/webproxy/frames.d.ts.map +1 -0
  84. package/dist/webproxy/frames.js +59 -0
  85. package/dist/webproxy/frames.js.map +1 -0
  86. package/dist/webproxy/link.d.ts +27 -0
  87. package/dist/webproxy/link.d.ts.map +1 -0
  88. package/dist/webproxy/link.js +107 -0
  89. package/dist/webproxy/link.js.map +1 -0
  90. package/package.json +16 -11
  91. package/dist/mtproto/connection.d.ts +0 -59
  92. package/dist/mtproto/connection.d.ts.map +0 -1
  93. package/dist/mtproto/connection.js +0 -198
  94. package/dist/mtproto/connection.js.map +0 -1
  95. package/dist/mtproto/socks5.d.ts +0 -36
  96. package/dist/mtproto/socks5.d.ts.map +0 -1
  97. package/dist/mtproto/socks5.js +0 -148
  98. package/dist/mtproto/socks5.js.map +0 -1
@@ -1,104 +1,78 @@
1
+ import { randomBytes } from 'crypto';
2
+ const REQ_PQ_MULTI = 0xbe7e8ef1;
3
+ const RES_PQ = 0x05162463;
1
4
  /**
2
- * Generate MTProto message_id.
3
- * Format: time.time() * 2^32, must be divisible by 4
4
- * Client message IDs must be divisible by 4
5
+ * Client message_id: unixtime * 2^32, divisible by 4.
5
6
  */
6
- export function generateMessageId() {
7
- // Get current time in seconds (with milliseconds as decimal)
8
- const now = Date.now() / 1000; // seconds with decimal
9
- // Multiply by 2^32
10
- let msgId = BigInt(Math.floor(now * 2 ** 32));
11
- // Must be divisible by 4 (client messages)
12
- msgId = (msgId / 4n) * 4n;
13
- return msgId;
14
- }
7
+ export const generateMessageId = (nowMs = Date.now()) => {
8
+ const seconds = BigInt(Math.floor(nowMs / 1000));
9
+ const fraction = BigInt(Math.floor(((nowMs % 1000) / 1000) * 2 ** 32));
10
+ return ((seconds << 32n) | fraction) & ~3n;
11
+ };
15
12
  /**
16
- * Build req_pq_multi message.
17
- * TL Schema: req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
18
- *
19
- * @param nonce - 16-byte random nonce
20
- * @returns TL-serialized message
13
+ * Unencrypted message: auth_key_id (0) + message_id + length + body.
21
14
  */
22
- export function buildReqPqMulti(nonce) {
23
- // Constructor ID: 0xbe7e8ef1 (little-endian)
24
- const constructor = Buffer.from([0xf1, 0x8e, 0x7e, 0xbe]);
25
- return Buffer.concat([constructor, nonce]);
26
- }
15
+ export const buildUnencryptedMessage = (body, messageId = generateMessageId()) => {
16
+ const header = Buffer.alloc(20);
17
+ header.writeBigInt64LE(messageId, 8);
18
+ header.writeUInt32LE(body.length, 16);
19
+ return Buffer.concat([header, body]);
20
+ };
27
21
  /**
28
- * Build unencrypted MTProto message wrapper.
29
- *
30
- * Format:
31
- * - auth_key_id: 8 bytes (all zeros for unencrypted)
32
- * - message_id: 8 bytes
33
- * - message_length: 4 bytes
34
- * - message: N bytes
35
- *
36
- * @param tlMessage - TL-serialized message
37
- * @param messageId - Message ID
38
- * @returns Unencrypted message
22
+ * req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
39
23
  */
40
- export function buildUnencryptedMessage(tlMessage, messageId) {
41
- const authKeyId = Buffer.alloc(8, 0); // All zeros
42
- const msgIdBuf = Buffer.alloc(8);
43
- msgIdBuf.writeBigInt64LE(messageId, 0);
44
- const msgLen = Buffer.alloc(4);
45
- msgLen.writeUInt32LE(tlMessage.length, 0);
46
- return Buffer.concat([authKeyId, msgIdBuf, msgLen, tlMessage]);
47
- }
24
+ export const buildReqPqMulti = (nonce = randomBytes(16)) => {
25
+ const body = Buffer.alloc(20);
26
+ body.writeUInt32LE(REQ_PQ_MULTI, 0);
27
+ nonce.copy(body, 4);
28
+ return { message: buildUnencryptedMessage(body), nonce };
29
+ };
48
30
  /**
49
- * Add Intermediate protocol length prefix.
31
+ * Parse an unencrypted resPQ message (transport framing already removed;
32
+ * trailing transport padding is allowed).
50
33
  *
51
- * @param payload - Message payload
52
- * @returns Payload with length prefix
34
+ * resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
53
35
  */
54
- export function addLengthPrefix(payload) {
55
- const length = Buffer.alloc(4);
56
- length.writeUInt32LE(payload.length, 0);
57
- return Buffer.concat([length, payload]);
58
- }
36
+ export const parseResPQ = (data) => {
37
+ if (data.length < 20 + 4 + 32) {
38
+ return null;
39
+ }
40
+ if (data.readBigUInt64LE(0) !== 0n) {
41
+ return null;
42
+ }
43
+ const bodyLength = data.readUInt32LE(16);
44
+ if (bodyLength < 36 || 20 + bodyLength > data.length) {
45
+ return null;
46
+ }
47
+ if (data.readUInt32LE(20) !== RES_PQ) {
48
+ return null;
49
+ }
50
+ return {
51
+ nonce: Buffer.from(data.subarray(24, 40)),
52
+ serverNonce: Buffer.from(data.subarray(40, 56)),
53
+ };
54
+ };
59
55
  /**
60
- * Parse ResPQ response.
61
- * TL Schema: resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
62
- *
63
- * @param data - Decrypted response data
64
- * @returns Parsed response or null if invalid
56
+ * Parse a req_pq_multi message (used by tests and mock servers).
65
57
  */
66
- export function parseResPQ(data) {
67
- try {
68
- let offset = 0;
69
- // Intermediate frame: 4-byte LE length prefix, then the unencrypted
70
- // MTProto envelope: auth_key_id(8) + message_id(8) + body_length(4) + body.
71
- if (data.length < 4)
72
- return null;
73
- const length = data.readUInt32LE(offset);
74
- offset += 4;
75
- if (data.length < 4 + length)
76
- return null;
77
- // Parse auth_key_id (8 bytes) - should be all zeros
78
- const authKeyId = data.slice(offset, offset + 8);
79
- offset += 8;
80
- if (!authKeyId.equals(Buffer.alloc(8, 0)))
81
- return null;
82
- // Parse message_id (8 bytes) - skip
83
- offset += 8;
84
- // Parse message_length (4 bytes) - skip
85
- offset += 4;
86
- // Parse constructor (4 bytes)
87
- const constructor = data.readUInt32LE(offset);
88
- offset += 4;
89
- // Valid constructors: 0x05162463 or 0x83c95aec
90
- if (constructor !== 0x05162463 && constructor !== 0x83c95aec) {
91
- return null;
92
- }
93
- // Parse nonce (16 bytes)
94
- const nonce = data.slice(offset, offset + 16);
95
- offset += 16;
96
- // Parse server_nonce (16 bytes)
97
- const serverNonce = data.slice(offset, offset + 16);
98
- return { nonce, serverNonce, constructor };
99
- }
100
- catch (err) {
58
+ export const parseReqPqMulti = (data) => {
59
+ if (data.length < 40 || data.readBigUInt64LE(0) !== 0n || data.readUInt32LE(20) !== REQ_PQ_MULTI) {
101
60
  return null;
102
61
  }
103
- }
62
+ return Buffer.from(data.subarray(24, 40));
63
+ };
64
+ /**
65
+ * Build a resPQ reply for the given nonce (used by tests and mock servers).
66
+ */
67
+ export const buildResPQ = (nonce) => {
68
+ const pq = Buffer.from([0x08, 0x17, 0xed, 0x48, 0x94, 0x1a, 0x08, 0xf9, 0x81, 0x00, 0x00, 0x00]);
69
+ const fingerprints = Buffer.alloc(16);
70
+ fingerprints.writeUInt32LE(0x1cb5c415, 0); // vector constructor
71
+ fingerprints.writeUInt32LE(1, 4);
72
+ fingerprints.writeBigUInt64LE(0xc3b42b026ce86b21n, 8);
73
+ const body = Buffer.concat([Buffer.alloc(4), nonce, randomBytes(16), pq, fingerprints]);
74
+ body.writeUInt32LE(RES_PQ, 0);
75
+ const message = buildUnencryptedMessage(body, (generateMessageId() | 1n));
76
+ return message;
77
+ };
104
78
  //# sourceMappingURL=messages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/mtproto/messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/mtproto/messages.ts"],"names":[],"mappings":"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"}
@@ -1,26 +1,29 @@
1
1
  import { AESCTR } from '../crypto/aes-ctr.js';
2
- /**
3
- * Protocol tags for MTProto transports.
4
- */
5
- export declare const PROTO_TAG: {
6
- ABRIDGED: Buffer<ArrayBuffer>;
7
- INTERMEDIATE: Buffer<ArrayBuffer>;
8
- SECURE: Buffer<ArrayBuffer>;
9
- };
10
- interface ObfuscationKeys {
11
- header: Buffer;
12
- encryptor: AESCTR;
13
- decryptor: AESCTR;
2
+ import type { Pipe } from '../net/stream.js';
3
+ import { type TransportMode } from './transport.js';
4
+ export interface Obfuscation {
5
+ readonly header: Buffer;
6
+ readonly encryptor: AESCTR;
7
+ readonly decryptor: AESCTR;
14
8
  }
15
9
  /**
16
- * Initialize MTProto obfuscation layer.
17
- * Generates random 64-byte header and derives encryption keys.
10
+ * Build the obfuscated2 init header (MTProto transport obfuscation).
18
11
  *
19
- * @param secret - 16-byte secret from proxy
20
- * @param dcId - Telegram datacenter ID
21
- * @param protocolTag - Transport protocol tag (default: INTERMEDIATE)
22
- * @returns Header buffer and encryptor/decryptor ciphers
12
+ * @param dcId - DC id written at offset 60 (signed 16-bit LE); negative for media DCs
13
+ * @param mode - transport framing, selects the protocol tag at offset 56
14
+ * @param secret - 16-byte proxy key; omitted for direct connections to a DC
23
15
  */
24
- export declare function initObfuscation(secret: Buffer, dcId: number, protocolTag?: Buffer): ObfuscationKeys;
25
- export {};
16
+ export declare const createObfuscation: (dcId: number, mode: TransportMode, secret?: Buffer) => Obfuscation;
17
+ /**
18
+ * Pipe that prepends the init header to the first write and AES-CTR
19
+ * encrypts/decrypts everything else.
20
+ */
21
+ export declare class ObfuscatedPipe implements Pipe {
22
+ private readonly inner;
23
+ private readonly obfuscation;
24
+ private headerSent;
25
+ constructor(inner: Pipe, obfuscation: Obfuscation);
26
+ write(data: Buffer): void;
27
+ read(length: number): Promise<Buffer>;
28
+ }
26
29
  //# sourceMappingURL=obfuscation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"obfuscation.d.ts","sourceRoot":"","sources":["../../src/mtproto/obfuscation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,SAAS;;;;CAIrB,CAAC;AAYF,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,GAAE,MAA+B,GAC3C,eAAe,CA+DjB"}
1
+ {"version":3,"file":"obfuscation.d.ts","sourceRoot":"","sources":["../../src/mtproto/obfuscation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAgBnE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAeD;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,MAAM,aAAa,EAAE,SAAS,MAAM,KAAG,WAoBtF,CAAC;AAEF;;;GAGG;AACH,qBAAa,cAAe,YAAW,IAAI;IAG7B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAAQ,OAAO,CAAC,QAAQ,CAAC,WAAW;IAFtE,OAAO,CAAC,UAAU,CAAS;gBAEE,KAAK,EAAE,IAAI,EAAmB,WAAW,EAAE,WAAW;IAEnF,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAUnB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG5C"}
@@ -1,78 +1,78 @@
1
1
  import { randomBytes, createHash } from 'crypto';
2
2
  import { AESCTR } from '../crypto/aes-ctr.js';
3
+ import { PROTOCOL_TAGS } from './transport.js';
3
4
  /**
4
- * Protocol tags for MTProto transports.
5
+ * First-int values the 64-byte init header must not start with: they would make
6
+ * the connection look like HTTP (HEAD/POST/GET /OPTIONS), TLS, or a plain transport tag.
5
7
  */
6
- export const PROTO_TAG = {
7
- ABRIDGED: Buffer.from([0xef, 0xef, 0xef, 0xef]),
8
- INTERMEDIATE: Buffer.from([0xee, 0xee, 0xee, 0xee]),
9
- SECURE: Buffer.from([0xdd, 0xdd, 0xdd, 0xdd]),
10
- };
11
- /**
12
- * Reserved byte sequences that must be avoided in random nonce.
13
- */
14
- const RESERVED_KEYWORDS = [
15
- Buffer.from([0x50, 0x56, 0x72, 0x47]), // PVrG
16
- Buffer.from([0x47, 0x45, 0x54, 0x20]), // GET
17
- Buffer.from([0x50, 0x4f, 0x53, 0x54]), // POST
18
- Buffer.from([0xee, 0xee, 0xee, 0xee]), // eeee
8
+ const FORBIDDEN_FIRST_INTS = [
9
+ 0x44414548, // HEAD
10
+ 0x54534f50, // POST
11
+ 0x20544547, // GET
12
+ 0x4954504f, // OPTI
13
+ 0x02010316, // TLS handshake
14
+ 0xdddddddd,
15
+ 0xeeeeeeee,
19
16
  ];
20
- /**
21
- * Initialize MTProto obfuscation layer.
22
- * Generates random 64-byte header and derives encryption keys.
23
- *
24
- * @param secret - 16-byte secret from proxy
25
- * @param dcId - Telegram datacenter ID
26
- * @param protocolTag - Transport protocol tag (default: INTERMEDIATE)
27
- * @returns Header buffer and encryptor/decryptor ciphers
28
- */
29
- export function initObfuscation(secret, dcId, protocolTag = PROTO_TAG.INTERMEDIATE) {
30
- if (secret.length !== 16) {
31
- throw new Error('Secret must be 16 bytes');
32
- }
33
- // Generate 64 random bytes, avoiding reserved patterns
34
- let random;
35
- while (true) {
36
- random = randomBytes(64);
37
- // Check if first byte is not 0xef
17
+ const generateRandomHeader = () => {
18
+ for (;;) {
19
+ const random = randomBytes(64);
38
20
  if (random[0] === 0xef)
39
21
  continue;
40
- // Check if bytes 4-8 are not all zeros
41
- if (random.slice(4, 8).equals(Buffer.alloc(4)))
22
+ if (FORBIDDEN_FIRST_INTS.includes(random.readUInt32LE(0)))
42
23
  continue;
43
- // Check if first 4 bytes don't match reserved keywords
44
- const isReserved = RESERVED_KEYWORDS.some((keyword) => keyword.equals(random.slice(0, 4)));
45
- if (isReserved)
24
+ if (random.readUInt32LE(4) === 0)
46
25
  continue;
47
- break;
26
+ return random;
27
+ }
28
+ };
29
+ const deriveKey = (material, secret) => secret ? createHash('sha256').update(material).update(secret).digest() : Buffer.from(material);
30
+ /**
31
+ * Build the obfuscated2 init header (MTProto transport obfuscation).
32
+ *
33
+ * @param dcId - DC id written at offset 60 (signed 16-bit LE); negative for media DCs
34
+ * @param mode - transport framing, selects the protocol tag at offset 56
35
+ * @param secret - 16-byte proxy key; omitted for direct connections to a DC
36
+ */
37
+ export const createObfuscation = (dcId, mode, secret) => {
38
+ if (secret && secret.length !== 16) {
39
+ throw new Error('Proxy secret key must be 16 bytes');
48
40
  }
49
- // Convert to mutable buffer
50
- const header = Buffer.from(random);
51
- // Reverse bytes 8-56 for decryption key derivation
52
- const reversed = Buffer.from(header.slice(8, 56)).reverse();
53
- // Derive encryption key and IV
54
- const encryptKey = createHash('sha256')
55
- .update(Buffer.concat([header.slice(8, 40), secret]))
56
- .digest(); // 32 bytes
57
- const encryptIv = header.slice(40, 56); // 16 bytes
58
- // Derive decryption key and IV
59
- const decryptKey = createHash('sha256')
60
- .update(Buffer.concat([reversed.slice(0, 32), secret]))
61
- .digest();
62
- const decryptIv = reversed.slice(32, 48);
63
- // Create AES-256-CTR ciphers
64
- const encryptor = new AESCTR(encryptKey, encryptIv);
65
- const decryptor = new AESCTR(decryptKey, decryptIv);
66
- // Encode protocol tag and DC ID at positions 56-62
67
- protocolTag.copy(header, 56);
68
- // DC ID as signed 16-bit little-endian
41
+ const header = generateRandomHeader();
42
+ const reversed = Buffer.from(header.subarray(8, 56)).reverse();
43
+ const encryptor = new AESCTR(deriveKey(header.subarray(8, 40), secret), Buffer.from(header.subarray(40, 56)));
44
+ const decryptor = new AESCTR(deriveKey(reversed.subarray(0, 32), secret), Buffer.from(reversed.subarray(32, 48)));
45
+ header.writeUInt32LE(PROTOCOL_TAGS[mode], 56);
69
46
  header.writeInt16LE(dcId, 60);
70
- // Encrypt the tail (tag + dc ID) in place. The cipher stream position must
71
- // be 56 here: the proxy decrypts the whole 64-byte frame from position 0 and
72
- // expects the tag to appear at bytes 56-60. Encrypting the whole header and
73
- // taking the tail (as official clients do) yields exactly that.
74
- const encryptedTail = encryptor.encrypt(header).subarray(56);
75
- encryptedTail.copy(header, 56);
47
+ // Encrypt the full header to advance the keystream by 64 bytes, but only
48
+ // the tail (protocol tag, DC id, 2 random bytes) goes out encrypted.
49
+ const encrypted = encryptor.encrypt(header);
50
+ encrypted.copy(header, 56, 56);
76
51
  return { header, encryptor, decryptor };
52
+ };
53
+ /**
54
+ * Pipe that prepends the init header to the first write and AES-CTR
55
+ * encrypts/decrypts everything else.
56
+ */
57
+ export class ObfuscatedPipe {
58
+ inner;
59
+ obfuscation;
60
+ headerSent = false;
61
+ constructor(inner, obfuscation) {
62
+ this.inner = inner;
63
+ this.obfuscation = obfuscation;
64
+ }
65
+ write(data) {
66
+ const encrypted = this.obfuscation.encryptor.encrypt(data);
67
+ if (this.headerSent) {
68
+ this.inner.write(encrypted);
69
+ return;
70
+ }
71
+ this.headerSent = true;
72
+ this.inner.write(Buffer.concat([this.obfuscation.header, encrypted]));
73
+ }
74
+ async read(length) {
75
+ return this.obfuscation.decryptor.encrypt(await this.inner.read(length));
76
+ }
77
77
  }
78
78
  //# sourceMappingURL=obfuscation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"obfuscation.js","sourceRoot":"","sources":["../../src/mtproto/obfuscation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"obfuscation.js","sourceRoot":"","sources":["../../src/mtproto/obfuscation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * MTProto proxy secret handling, matching Telegram Android (tgnet) and Desktop.
3
+ *
4
+ * Accepted encodings: hex, or base64 / base64url (padding optional).
5
+ * Accepted layouts (raw bytes):
6
+ * 16 bytes -> simple obfuscation, intermediate transport (0xeeeeeeee)
7
+ * 0xdd + 16 bytes -> secure, padded intermediate transport (0xdddddddd)
8
+ * 0xee + 16 bytes + domain -> fake TLS, padded intermediate inside TLS records
9
+ */
10
+ export type SecretKind = 'simple' | 'secure' | 'faketls';
11
+ export interface ParsedSecret {
12
+ readonly kind: SecretKind;
13
+ /** 16-byte key used for obfuscation key derivation and fake TLS HMAC. */
14
+ readonly key: Buffer;
15
+ /** SNI domain (fake TLS only). */
16
+ readonly domain?: string;
17
+ }
18
+ export declare class SecretError extends Error {
19
+ constructor(message: string);
20
+ }
21
+ /**
22
+ * Decode a secret string the same way the official clients do:
23
+ * all-hex with even length is hex, anything else is base64url.
24
+ */
25
+ export declare const decodeSecret: (value: string) => Buffer | null;
26
+ export declare const parseSecret: (value: string) => ParsedSecret;
27
+ export declare const describeSecretKind: (kind: SecretKind) => string;
28
+ //# sourceMappingURL=secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../src/mtproto/secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,IAcrD,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,YA0B3C,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM,UAAU,KAAG,MASrD,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * MTProto proxy secret handling, matching Telegram Android (tgnet) and Desktop.
3
+ *
4
+ * Accepted encodings: hex, or base64 / base64url (padding optional).
5
+ * Accepted layouts (raw bytes):
6
+ * 16 bytes -> simple obfuscation, intermediate transport (0xeeeeeeee)
7
+ * 0xdd + 16 bytes -> secure, padded intermediate transport (0xdddddddd)
8
+ * 0xee + 16 bytes + domain -> fake TLS, padded intermediate inside TLS records
9
+ */
10
+ export class SecretError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = 'SecretError';
14
+ }
15
+ }
16
+ /**
17
+ * Decode a secret string the same way the official clients do:
18
+ * all-hex with even length is hex, anything else is base64url.
19
+ */
20
+ export const decodeSecret = (value) => {
21
+ const text = value.trim();
22
+ if (text.length === 0) {
23
+ return null;
24
+ }
25
+ if (text.length % 2 === 0 && /^[0-9a-fA-F]+$/.test(text)) {
26
+ return Buffer.from(text, 'hex');
27
+ }
28
+ if (/^[A-Za-z0-9+/_-]+={0,2}$/.test(text)) {
29
+ const standard = text.replace(/-/g, '+').replace(/_/g, '/').replace(/=+$/, '');
30
+ const decoded = Buffer.from(standard, 'base64');
31
+ return decoded.length > 0 ? decoded : null;
32
+ }
33
+ return null;
34
+ };
35
+ export const parseSecret = (value) => {
36
+ const bytes = decodeSecret(value);
37
+ if (!bytes) {
38
+ throw new SecretError('Secret is neither hex nor base64');
39
+ }
40
+ if (bytes.length === 16) {
41
+ return { kind: 'simple', key: bytes };
42
+ }
43
+ if (bytes[0] === 0xdd) {
44
+ if (bytes.length !== 17) {
45
+ throw new SecretError(`dd secret must be 17 bytes, got ${bytes.length}`);
46
+ }
47
+ return { kind: 'secure', key: bytes.subarray(1, 17) };
48
+ }
49
+ if (bytes[0] === 0xee) {
50
+ if (bytes.length <= 17) {
51
+ throw new SecretError(`Fake-TLS (ee) secret has no domain; Telegram rejects it (as dd it would be dd${bytes.subarray(1).toString('hex')})`);
52
+ }
53
+ const domain = bytes.subarray(17).toString('utf8');
54
+ return { kind: 'faketls', key: bytes.subarray(1, 17), domain };
55
+ }
56
+ throw new SecretError(`Unsupported secret format (${bytes.length} bytes, prefix 0x${bytes[0].toString(16).padStart(2, '0')})`);
57
+ };
58
+ export const describeSecretKind = (kind) => {
59
+ switch (kind) {
60
+ case 'faketls':
61
+ return 'Fake TLS';
62
+ case 'secure':
63
+ return 'dd';
64
+ default:
65
+ return 'simple';
66
+ }
67
+ };
68
+ //# sourceMappingURL=secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret.js","sourceRoot":"","sources":["../../src/mtproto/secret.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Pipe } from '../net/stream.js';
2
+ /**
3
+ * MTProto TCP transports (https://core.telegram.org/mtproto/mtproto-transports).
4
+ *
5
+ * The official clients pick the transport from the secret:
6
+ * no secret (direct DC, SOCKS5, HTTP) -> abridged
7
+ * 16-byte secret -> intermediate
8
+ * dd / ee secret -> padded intermediate
9
+ */
10
+ export type TransportMode = 'abridged' | 'intermediate' | 'padded';
11
+ export declare const PROTOCOL_TAGS: Readonly<Record<TransportMode, number>>;
12
+ /**
13
+ * A 4-byte frame carrying a negative error code instead of a message.
14
+ */
15
+ export declare class TransportError extends Error {
16
+ readonly code: number;
17
+ constructor(code: number);
18
+ }
19
+ export declare class FramingError extends Error {
20
+ constructor(message: string);
21
+ }
22
+ export declare const encodeFrame: (mode: TransportMode, payload: Buffer) => Buffer;
23
+ /**
24
+ * Read one server frame. Quick-ack frames are skipped; 4-byte error frames throw TransportError.
25
+ * For the padded transport the returned payload may carry trailing padding.
26
+ */
27
+ export declare const readFrame: (mode: TransportMode, pipe: Pipe) => Promise<Buffer>;
28
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/mtproto/transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,cAAc,GAAG,QAAQ,CAAC;AAEnE,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAIjE,CAAC;AAUF;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAIlC;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,eAAO,MAAM,WAAW,GAAI,MAAM,aAAa,EAAE,SAAS,MAAM,KAAG,MAiBlE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAU,MAAM,aAAa,EAAE,MAAM,IAAI,KAAG,OAAO,CAAC,MAAM,CA6B/E,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { randomBytes, randomInt } from 'crypto';
2
+ export const PROTOCOL_TAGS = {
3
+ abridged: 0xefefefef,
4
+ intermediate: 0xeeeeeeee,
5
+ padded: 0xdddddddd,
6
+ };
7
+ const MAX_FRAME = 1 << 24;
8
+ const TRANSPORT_ERRORS = {
9
+ [-404]: 'not found',
10
+ [-429]: 'too many connections from this IP (flood)',
11
+ [-444]: 'invalid DC',
12
+ };
13
+ /**
14
+ * A 4-byte frame carrying a negative error code instead of a message.
15
+ */
16
+ export class TransportError extends Error {
17
+ code;
18
+ constructor(code) {
19
+ super(`Telegram returned transport error ${code}${TRANSPORT_ERRORS[code] ? ` (${TRANSPORT_ERRORS[code]})` : ''}`);
20
+ this.code = code;
21
+ this.name = 'TransportError';
22
+ }
23
+ }
24
+ export class FramingError extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = 'FramingError';
28
+ }
29
+ }
30
+ export const encodeFrame = (mode, payload) => {
31
+ if (payload.length % 4 !== 0) {
32
+ throw new Error('MTProto payload length must be a multiple of 4');
33
+ }
34
+ if (mode === 'abridged') {
35
+ const words = payload.length / 4;
36
+ const header = words < 0x7f
37
+ ? Buffer.from([words])
38
+ : Buffer.from([0x7f, words & 0xff, (words >> 8) & 0xff, (words >> 16) & 0xff]);
39
+ return Buffer.concat([header, payload]);
40
+ }
41
+ const padding = mode === 'padded' ? randomBytes(randomInt(16)) : Buffer.alloc(0);
42
+ const header = Buffer.alloc(4);
43
+ header.writeUInt32LE(payload.length + padding.length, 0);
44
+ return Buffer.concat([header, payload, padding]);
45
+ };
46
+ /**
47
+ * Read one server frame. Quick-ack frames are skipped; 4-byte error frames throw TransportError.
48
+ * For the padded transport the returned payload may carry trailing padding.
49
+ */
50
+ export const readFrame = async (mode, pipe) => {
51
+ for (;;) {
52
+ let length;
53
+ if (mode === 'abridged') {
54
+ const first = (await pipe.read(1))[0];
55
+ if (first & 0x80) {
56
+ await pipe.read(3); // quick ack
57
+ continue;
58
+ }
59
+ length = first === 0x7f ? (await pipe.read(3)).readUIntLE(0, 3) * 4 : first * 4;
60
+ }
61
+ else {
62
+ const value = (await pipe.read(4)).readUInt32LE(0);
63
+ if (value & 0x80000000) {
64
+ continue; // quick ack
65
+ }
66
+ length = value;
67
+ }
68
+ if (length < 4 || length > MAX_FRAME) {
69
+ throw new FramingError(`Invalid frame length ${length} from server (wrong secret or not an MTProto proxy)`);
70
+ }
71
+ const payload = await pipe.read(length);
72
+ if (length === 4) {
73
+ throw new TransportError(payload.readInt32LE(0));
74
+ }
75
+ return payload;
76
+ }
77
+ };
78
+ //# sourceMappingURL=transport.js.map