import { type KeyObject } from 'node:crypto'; import type { TlObject } from '@mt-tl/tl'; import { type TlCodec } from '@mt-tl/server/testkit'; import type { ClientTransport } from './transport.js'; /** Overrides for a single encrypted send. `salt` exercises `bad_server_salt`; * `msgId`/`seqNo` exercise `bad_msg_notification`. */ export interface SendOpts { salt?: bigint; msgId?: bigint; seqNo?: number; } /** * A protocol-correct MTProto 2.0 client: full RSA/DH handshake, AES-IGE + * msg_key crypto (v2), and `invoke`/`receive` over the intermediate framing. * This is the low-level engine — most tests use the ergonomic {@link TestSession} * (`src/session.ts`) which wraps it. The crypto is shared with the server and * pinned by `packages/server/test/crypto.kat.test.ts`. */ export declare class TestClient { private readonly transport; private readonly serverPubKey; private readonly codec; private frames; private waiters; private inbound; private sentInit; private authKey; private authKeyId; private sessionId; private serverSalt; private seq; constructor(transport: ClientTransport, serverPubKey: KeyObject, codec: TlCodec); connect(): Promise; private onBytes; private nextPacket; private sendPacket; private sendPlain; private recvPlainBody; handshake(): Promise; /** The salt this client encrypts with (server-advertised). */ get salt(): bigint; set salt(v: bigint); /** This client's session id (for `destroy_session` tests). */ get session(): bigint; /** This client's auth key id, set after `handshake` (for storage assertions). */ get authKey_id(): bigint; /** Next seqno for a content-related (odd, counted) or service (even) message. */ private nextSeq; /** Send an encrypted body, collect `expectN` decrypted replies. `opts.salt` * overrides the salt (to exercise `bad_server_salt`); `opts.msgId` overrides the * msg_id and `opts.seqNo` the seqno (to exercise `bad_msg_notification`). The * seqno is otherwise chosen per content-relatedness (odd for queries, even for * pure service messages), matching the protocol. */ invoke(body: TlObject, expectN: number, opts?: SendOpts): Promise; /** Send a raw inner payload (e.g. a hand-built container) and collect replies. */ invokeRaw(payload: Buffer, seqNo: number, expectN: number, opts?: { salt?: bigint; msgId?: bigint; }): Promise; /** Encrypt and send a body, returning its msg_id — does NOT wait for replies. * The ergonomic {@link TestSession} uses this to drive its own receive loop * (match `rpc_result.req_msg_id`, route interleaved updates). Seqno follows * content-relatedness like {@link invoke} unless `opts.seqNo` is given. */ sendBody(body: TlObject, opts?: SendOpts): bigint; private send; /** Frame + encrypt one payload and write it; returns the msg_id used. */ private encryptAndSend; /** Wait for and decrypt one server-initiated message (e.g. a pushed update). */ receive(timeoutMs?: number): Promise; close(): void; } /** A protocol-valid client msg_id: unix-seconds<<32, ÷4, strictly increasing. */ export declare function genMsgId(): bigint; //# sourceMappingURL=test-client.d.ts.map