Line data Source code
1 : /**
2 : * @file mock_tel_server.c
3 : * @brief In-process Telegram server emulator — see mock_tel_server.h.
4 : */
5 :
6 : #include "mock_tel_server.h"
7 : #include "mock_socket.h"
8 :
9 : #include "crypto.h"
10 : #include "ige_aes.h"
11 : #include "mtproto_crypto.h"
12 : #include "mtproto_session.h"
13 : #include "tl_serial.h"
14 : #include "tl_skip.h"
15 : #include "session_store.h"
16 : #include "tinf.h"
17 :
18 : #include <stdio.h>
19 : #include <stdlib.h>
20 : #include <string.h>
21 : #include <time.h>
22 :
23 : #define CRC_invokeWithLayer 0xda9b0d0dU
24 : #define CRC_initConnection 0xc1cd5ea9U
25 : #define CRC_rpc_result 0xf35c6d01U
26 : #define CRC_rpc_error 0x2144ca19U
27 : #define CRC_gzip_packed 0x3072cfa1U
28 : #define CRC_msg_container 0x73f1f8dcU
29 :
30 : /* TL CRCs for auth.sendCode / auth.signIn — local copies so the mock
31 : * server does not have to link against src/infrastructure/auth_session.h.
32 : * Kept aligned with CRC_auth_sendCode / CRC_auth_signIn in that header. */
33 : #define CRC_auth_sendCode_local 0xa677244fU
34 : #define CRC_auth_signIn_local 0x8d52a951U
35 :
36 : /* TL CRCs for auth.exportAuthorization / auth.importAuthorization and
37 : * their reply constructors — local copies so the mock stays decoupled
38 : * from src/infrastructure/auth_transfer.c. Kept aligned with the
39 : * CRC_* macros in that file. */
40 : #define CRC_auth_exportAuthorization_local 0xe5bfffcdU
41 : #define CRC_auth_exportedAuthorization_local 0xb434e2b8U
42 : #define CRC_auth_importAuthorization_local 0xa57a7dadU
43 : #define CRC_auth_authorization_local 0x2ea2c0d4U
44 : #define CRC_auth_authorizationSignUpRequired_local 0x44747e9aU
45 : #define CRC_user_local 0x3ff6ecb0U
46 :
47 : #define MT_MAX_HANDLERS 64
48 : #define MT_MAX_UPDATES 32
49 : #define MT_FRAME_MAX (256 * 1024)
50 : #define MT_CRC_RING_SIZE 256
51 : #define MT_MAX_PENDING_SVC 70 /* must exceed SERVICE_FRAME_LIMIT (64) + 1 for storm test */
52 :
53 : /* TL CRCs for the service frames TEST-88 exercises. Keep these local so
54 : * the mock server does not have to link against src/core/tl_registry.h. */
55 : #define CRC_bad_server_salt 0xedab447bU
56 : #define CRC_bad_msg_notification 0xa7eff811U
57 : #define CRC_new_session_created 0x9ec20908U
58 : #define CRC_msgs_ack 0x62d6b459U
59 : #define CRC_pong 0x347773c5U
60 : #define CRC_vector 0x1cb5c415U
61 :
62 : typedef struct {
63 : uint32_t crc;
64 : MtResponder fn;
65 : void *ctx;
66 : int used;
67 : } MtHandler;
68 :
69 : typedef struct {
70 : uint8_t *bytes;
71 : size_t len;
72 : } MtBlob;
73 :
74 : static struct {
75 : int initialized;
76 : int seeded;
77 :
78 : uint8_t auth_key[MT_SERVER_AUTH_KEY_SIZE];
79 : uint64_t auth_key_id;
80 : uint64_t server_salt;
81 : uint64_t session_id;
82 :
83 : uint64_t next_server_msg_id;
84 : uint32_t seq_no;
85 :
86 : size_t parse_cursor; /* next byte in mock_socket sent-buffer */
87 : int saw_marker; /* consumed the initial 0xEF yet? */
88 :
89 : MtHandler handlers[MT_MAX_HANDLERS];
90 :
91 : MtBlob pending_updates[MT_MAX_UPDATES];
92 : size_t pending_update_count;
93 :
94 : int rpc_call_count;
95 :
96 : /* Scratch state that reply builders read to know which client msg_id
97 : * they are answering. Valid only during responder execution. */
98 : uint64_t current_req_msg_id;
99 :
100 : /* One-shot bad_server_salt injection. When non-zero, the next dispatched
101 : * RPC triggers a bad_server_salt reply instead of running the handler. */
102 : int bad_salt_once_pending;
103 : uint64_t bad_salt_new_salt;
104 :
105 : /* One-shot reconnect detection: when set, the next 0xEF byte at the
106 : * parse cursor is treated as a fresh connection marker rather than a
107 : * frame length prefix. Cleared automatically after use. */
108 : int reconnect_pending;
109 :
110 : /* One-shot wrong session_id injection. When set, the next reply frame
111 : * uses a deliberately wrong session_id in the plaintext header so that
112 : * rpc_recv_encrypted() rejects it. Cleared automatically after use. */
113 : int wrong_session_id_once_pending;
114 :
115 : /* Ring buffer of leading CRCs from every frame received (both
116 : * unencrypted handshake frames and encrypted inner-RPC frames).
117 : * Used by mt_server_request_crc_count(). */
118 : uint32_t crc_ring[MT_CRC_RING_SIZE];
119 : size_t crc_ring_count; /* total recorded; wraps at MT_CRC_RING_SIZE */
120 :
121 : /* Stack of service frames to send ahead of the next real reply.
122 : * Each entry holds a raw TL body (with leading CRC). Drained in
123 : * unwrap_and_dispatch before the registered handler runs. */
124 : MtBlob pending_service_frames[MT_MAX_PENDING_SVC];
125 : size_t pending_service_count;
126 :
127 : /* TEST-71 / US-20 cold-boot handshake state.
128 : * When handshake_mode != 0 the mock processes unencrypted frames
129 : * (auth_key_id == 0) alongside encrypted ones, emitting synthetic
130 : * resPQ / server_DH_params_ok envelopes so functional tests can
131 : * exercise src/infrastructure/mtproto_auth.c against real OpenSSL.
132 : * Mode 3 (TEST-72) completes the full DH exchange: RSA-PAD decrypt,
133 : * server_DH_params_ok, dh_gen_ok — enabling full auth_key derivation. */
134 : int handshake_mode; /* 0=off 1=resPQ-only 2=through step3 3=full DH */
135 : int handshake_cold_boot_variant; /* MtColdBootMode value */
136 : int handshake_req_pq_count;
137 : int handshake_req_dh_count;
138 : int handshake_set_client_dh_count; /* TEST-72 */
139 :
140 : /* TEST-72: DH state preserved between req_DH_params and set_client_DH_params. */
141 : uint8_t hs_new_nonce[32]; /* extracted from client's RSA_PAD payload */
142 : uint8_t hs_server_nonce[16]; /* echoed from resPQ */
143 : uint8_t hs_nonce[16]; /* client nonce */
144 : uint8_t hs_b[256]; /* server's DH secret (random) */
145 : uint8_t hs_dh_prime[32]; /* safe prime for DH */
146 : uint8_t hs_g_a[256]; /* g^a mod p (received from client) */
147 : size_t hs_g_a_len;
148 : } g_srv;
149 :
150 : /* ---- forward decls ---- */
151 : static void on_client_sent(const uint8_t *buf, size_t len);
152 : static void dispatch_frame(const uint8_t *plain, size_t plain_len);
153 : static void unwrap_and_dispatch(uint64_t req_msg_id,
154 : const uint8_t *body, size_t body_len);
155 : static void encrypt_and_queue(const uint8_t *plain, size_t plain_len);
156 : static void queue_frame(const uint8_t *body, size_t body_len);
157 : static uint64_t derive_auth_key_id(const uint8_t *auth_key);
158 : static uint64_t make_server_msg_id(void);
159 : static void handshake_on_req_pq_multi(const uint8_t *body, size_t body_len);
160 : static void handshake_on_req_dh_params(const uint8_t *body, size_t body_len);
161 : static void handshake_on_set_client_dh(const uint8_t *body, size_t body_len);
162 : static void handshake_queue_unenc(const uint8_t *tl, size_t tl_len);
163 :
164 : /* ================================================================ */
165 : /* Public API */
166 : /* ================================================================ */
167 :
168 486 : void mt_server_init(void) {
169 486 : if (g_srv.initialized) return;
170 2 : memset(&g_srv, 0, sizeof(g_srv));
171 2 : g_srv.initialized = 1;
172 : }
173 :
174 950 : void mt_server_reset(void) {
175 : /* Preserve initialized flag, wipe everything else. */
176 950 : mock_socket_set_on_sent(NULL);
177 950 : for (size_t i = 0; i < g_srv.pending_update_count; ++i) {
178 0 : free(g_srv.pending_updates[i].bytes);
179 : }
180 950 : for (size_t i = 0; i < g_srv.pending_service_count; ++i) {
181 0 : free(g_srv.pending_service_frames[i].bytes);
182 : }
183 950 : memset(&g_srv, 0, sizeof(g_srv));
184 950 : g_srv.initialized = 1;
185 :
186 950 : mock_socket_reset();
187 950 : mock_socket_set_on_sent(on_client_sent);
188 950 : }
189 :
190 446 : int mt_server_seed_session(int dc_id,
191 : uint8_t auth_key_out[MT_SERVER_AUTH_KEY_SIZE],
192 : uint64_t *salt_out,
193 : uint64_t *session_id_out) {
194 : /* Deterministic-ish auth_key so tests stay reproducible across runs. */
195 114622 : for (int i = 0; i < MT_SERVER_AUTH_KEY_SIZE; ++i) {
196 114176 : g_srv.auth_key[i] = (uint8_t)((i * 31 + 7) & 0xFFu);
197 : }
198 446 : g_srv.auth_key_id = derive_auth_key_id(g_srv.auth_key);
199 446 : g_srv.server_salt = 0xABCDEF0123456789ULL;
200 446 : g_srv.session_id = 0x1122334455667788ULL;
201 446 : g_srv.next_server_msg_id = 0;
202 446 : g_srv.seq_no = 0;
203 :
204 : MtProtoSession s;
205 446 : mtproto_session_init(&s);
206 446 : mtproto_session_set_auth_key(&s, g_srv.auth_key);
207 446 : mtproto_session_set_salt(&s, g_srv.server_salt);
208 446 : s.session_id = g_srv.session_id;
209 :
210 446 : if (session_store_save(&s, dc_id) != 0) return -1;
211 :
212 446 : if (auth_key_out) memcpy(auth_key_out, g_srv.auth_key, MT_SERVER_AUTH_KEY_SIZE);
213 446 : if (salt_out) *salt_out = g_srv.server_salt;
214 446 : if (session_id_out) *session_id_out = g_srv.session_id;
215 :
216 446 : g_srv.seeded = 1;
217 446 : return 0;
218 : }
219 :
220 474 : void mt_server_expect(uint32_t crc, MtResponder fn, void *ctx) {
221 : /* Replace existing handler for the same CRC if present. */
222 29788 : for (size_t i = 0; i < MT_MAX_HANDLERS; ++i) {
223 29330 : if (g_srv.handlers[i].used && g_srv.handlers[i].crc == crc) {
224 16 : g_srv.handlers[i].fn = fn;
225 16 : g_srv.handlers[i].ctx = ctx;
226 16 : return;
227 : }
228 : }
229 510 : for (size_t i = 0; i < MT_MAX_HANDLERS; ++i) {
230 510 : if (!g_srv.handlers[i].used) {
231 458 : g_srv.handlers[i].used = 1;
232 458 : g_srv.handlers[i].crc = crc;
233 458 : g_srv.handlers[i].fn = fn;
234 458 : g_srv.handlers[i].ctx = ctx;
235 458 : return;
236 : }
237 : }
238 : /* Table full — test setup bug. Abort loudly. */
239 0 : fprintf(stderr, "mt_server_expect: handler table full\n");
240 0 : abort();
241 : }
242 :
243 620 : void mt_server_reply_result(const MtRpcContext *ctx,
244 : const uint8_t *body, size_t body_len) {
245 620 : if (!ctx) return;
246 : /* rpc_result#f35c6d01 = req_msg_id:long result:Object */
247 620 : size_t wrapped_len = 4 + 8 + body_len;
248 620 : uint8_t *wrapped = (uint8_t *)malloc(wrapped_len);
249 620 : if (!wrapped) return;
250 620 : wrapped[0] = (uint8_t)(CRC_rpc_result);
251 620 : wrapped[1] = (uint8_t)(CRC_rpc_result >> 8);
252 620 : wrapped[2] = (uint8_t)(CRC_rpc_result >> 16);
253 620 : wrapped[3] = (uint8_t)(CRC_rpc_result >> 24);
254 5580 : for (int i = 0; i < 8; ++i) {
255 4960 : wrapped[4 + i] = (uint8_t)((ctx->req_msg_id >> (i * 8)) & 0xFFu);
256 : }
257 620 : memcpy(wrapped + 12, body, body_len);
258 620 : queue_frame(wrapped, wrapped_len);
259 620 : free(wrapped);
260 : }
261 :
262 94 : void mt_server_reply_error(const MtRpcContext *ctx,
263 : int32_t error_code, const char *error_msg) {
264 94 : if (!ctx) return;
265 : /* rpc_error#2144ca19 = error_code:int error_message:string */
266 : TlWriter w;
267 94 : tl_writer_init(&w);
268 94 : tl_write_uint32(&w, CRC_rpc_error);
269 94 : tl_write_int32(&w, error_code);
270 94 : tl_write_string(&w, error_msg ? error_msg : "");
271 94 : mt_server_reply_result(ctx, w.data, w.len);
272 94 : tl_writer_free(&w);
273 : }
274 :
275 : /* ---- Minimal gzip encoder (deflate stored blocks) ----
276 : *
277 : * Writes @p payload as a sequence of uncompressed deflate blocks ("stored"
278 : * blocks) wrapped in the gzip member format. This avoids pulling in a real
279 : * compressor (zlib) for test fixtures — the stored-block path is a
280 : * well-defined subset of deflate that tinf's inflater handles. Each stored
281 : * block carries at most 65535 bytes; larger payloads span multiple blocks.
282 : *
283 : * Format per RFC 1951 §3.2.4 + RFC 1952:
284 : * gzip header (10) 1f 8b 08 00 00 00 00 00 00 ff
285 : * deflate stream [block_header(1) LEN(2 LE) NLEN(2 LE) data(LEN)]+
286 : * (block_header bit0 = BFINAL, bits1-2 = BTYPE=00)
287 : * CRC32(payload) 4 bytes LE
288 : * ISIZE 4 bytes LE (original length mod 2^32)
289 : *
290 : * Returns a heap-allocated buffer the caller must free; NULL on OOM.
291 : */
292 4 : static uint8_t *gzip_stored(const uint8_t *payload, size_t payload_len,
293 : size_t *out_len) {
294 4 : if (!out_len) return NULL;
295 :
296 : /* Upper bound on output size: 10 header + per 65535-byte block
297 : * overhead (1 + 2 + 2 = 5) + payload + 8 trailer. */
298 4 : size_t blocks = (payload_len + 65534) / 65535;
299 4 : if (payload_len == 0) blocks = 1;
300 4 : size_t cap = 10 + blocks * 5 + payload_len + 8;
301 :
302 4 : uint8_t *buf = (uint8_t *)malloc(cap);
303 4 : if (!buf) return NULL;
304 4 : size_t off = 0;
305 :
306 : /* gzip header */
307 4 : buf[off++] = 0x1F; /* ID1 */
308 4 : buf[off++] = 0x8B; /* ID2 */
309 4 : buf[off++] = 0x08; /* CM = deflate */
310 4 : buf[off++] = 0x00; /* FLG = 0 (no name, comment, extra, crc16) */
311 4 : buf[off++] = 0x00; /* MTIME[0] */
312 4 : buf[off++] = 0x00; /* MTIME[1] */
313 4 : buf[off++] = 0x00; /* MTIME[2] */
314 4 : buf[off++] = 0x00; /* MTIME[3] */
315 4 : buf[off++] = 0x00; /* XFL */
316 4 : buf[off++] = 0xFF; /* OS = unknown */
317 :
318 : /* Deflate stored blocks. */
319 4 : size_t pos = 0;
320 4 : if (payload_len == 0) {
321 : /* Emit one empty final stored block: header=0x01, LEN=0, NLEN=0xFFFF. */
322 0 : buf[off++] = 0x01;
323 0 : buf[off++] = 0x00; buf[off++] = 0x00;
324 0 : buf[off++] = 0xFF; buf[off++] = 0xFF;
325 : } else {
326 8 : while (pos < payload_len) {
327 4 : size_t chunk = payload_len - pos;
328 4 : if (chunk > 65535) chunk = 65535;
329 4 : int is_final = (pos + chunk == payload_len) ? 1 : 0;
330 4 : buf[off++] = (uint8_t)(is_final ? 0x01 : 0x00);
331 4 : buf[off++] = (uint8_t)(chunk & 0xFFu);
332 4 : buf[off++] = (uint8_t)((chunk >> 8) & 0xFFu);
333 4 : uint16_t nlen = (uint16_t)~chunk;
334 4 : buf[off++] = (uint8_t)(nlen & 0xFFu);
335 4 : buf[off++] = (uint8_t)((nlen >> 8) & 0xFFu);
336 4 : memcpy(buf + off, payload + pos, chunk);
337 4 : off += chunk;
338 4 : pos += chunk;
339 : }
340 : }
341 :
342 : /* Trailer: CRC32 of raw payload, then ISIZE mod 2^32. tinf_crc32 matches
343 : * the standard gzip polynomial. Guard the empty-payload case because
344 : * tinf_crc32 returns 0 for empty input (which happens to be correct). */
345 4 : uint32_t crc = tinf_crc32(payload, (unsigned int)payload_len);
346 4 : buf[off++] = (uint8_t)(crc & 0xFFu);
347 4 : buf[off++] = (uint8_t)((crc >> 8) & 0xFFu);
348 4 : buf[off++] = (uint8_t)((crc >> 16) & 0xFFu);
349 4 : buf[off++] = (uint8_t)((crc >> 24) & 0xFFu);
350 4 : uint32_t isize = (uint32_t)(payload_len & 0xFFFFFFFFu);
351 4 : buf[off++] = (uint8_t)(isize & 0xFFu);
352 4 : buf[off++] = (uint8_t)((isize >> 8) & 0xFFu);
353 4 : buf[off++] = (uint8_t)((isize >> 16) & 0xFFu);
354 4 : buf[off++] = (uint8_t)((isize >> 24) & 0xFFu);
355 :
356 4 : *out_len = off;
357 4 : return buf;
358 : }
359 :
360 4 : void mt_server_reply_gzip_wrapped_result(const MtRpcContext *ctx,
361 : const uint8_t *body,
362 : size_t body_len) {
363 4 : if (!ctx || (!body && body_len > 0)) return;
364 4 : size_t gz_len = 0;
365 4 : uint8_t *gz = gzip_stored(body, body_len, &gz_len);
366 4 : if (!gz) return;
367 :
368 : /* gzip_packed#3072cfa1 = packed_data:bytes = Object */
369 : TlWriter w;
370 4 : tl_writer_init(&w);
371 4 : tl_write_uint32(&w, CRC_gzip_packed);
372 4 : tl_write_bytes(&w, gz, gz_len);
373 4 : mt_server_reply_result(ctx, w.data, w.len);
374 4 : tl_writer_free(&w);
375 4 : free(gz);
376 : }
377 :
378 2 : void mt_server_reply_gzip_corrupt(const MtRpcContext *ctx) {
379 2 : if (!ctx) return;
380 : /* Bytes that fail gzip header validation: wrong magic + too short for
381 : * the 18-byte minimum tinf requires. rpc_unwrap_gzip propagates the
382 : * TINF_DATA_ERROR as -1. */
383 : static const uint8_t garbage[] = {
384 : 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22,
385 : 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
386 : 0xDE, 0xAD, 0xBE, 0xEF
387 : };
388 : TlWriter w;
389 2 : tl_writer_init(&w);
390 2 : tl_write_uint32(&w, CRC_gzip_packed);
391 2 : tl_write_bytes(&w, garbage, sizeof(garbage));
392 2 : mt_server_reply_result(ctx, w.data, w.len);
393 2 : tl_writer_free(&w);
394 : }
395 :
396 2 : void mt_server_reply_msg_container(const MtRpcContext *ctx,
397 : const uint8_t *const *children,
398 : const size_t *child_lens,
399 : size_t n_children) {
400 2 : if (!ctx || !children || !child_lens || n_children == 0) return;
401 :
402 : /* msg_container#73f1f8dc messages:vector<message>
403 : * message { msg_id:long seqno:int bytes:int body:bytes_untyped }
404 : * Each body is concatenated raw (no length prefix on the body itself —
405 : * the bytes:int field already specifies its length, and the body is
406 : * 4-byte-aligned TL data). */
407 : TlWriter w;
408 2 : tl_writer_init(&w);
409 2 : tl_write_uint32(&w, CRC_msg_container);
410 2 : tl_write_uint32(&w, (uint32_t)n_children);
411 : /* Use a monotonic msg_id/seqno pair per child. The parser's alignment
412 : * guard only cares that each body_len is a multiple of 4. */
413 2 : uint64_t base_msg_id = (uint64_t)time(NULL) << 32;
414 4 : for (size_t i = 0; i < n_children; ++i) {
415 2 : tl_write_uint64(&w, base_msg_id + ((uint64_t)i << 2));
416 2 : tl_write_uint32(&w, (uint32_t)(i * 2 + 1));
417 2 : tl_write_uint32(&w, (uint32_t)child_lens[i]);
418 2 : tl_write_raw(&w, children[i], child_lens[i]);
419 : }
420 : /* Send the container as the outer body — NOT wrapped in rpc_result. */
421 2 : queue_frame(w.data, w.len);
422 2 : tl_writer_free(&w);
423 : }
424 :
425 0 : void mt_server_push_update(const uint8_t *tl, size_t tl_len) {
426 0 : if (g_srv.pending_update_count >= MT_MAX_UPDATES) return;
427 0 : uint8_t *copy = (uint8_t *)malloc(tl_len);
428 0 : if (!copy) return;
429 0 : memcpy(copy, tl, tl_len);
430 0 : g_srv.pending_updates[g_srv.pending_update_count].bytes = copy;
431 0 : g_srv.pending_updates[g_srv.pending_update_count].len = tl_len;
432 0 : g_srv.pending_update_count++;
433 : }
434 :
435 54 : int mt_server_rpc_call_count(void) { return g_srv.rpc_call_count; }
436 :
437 84 : int mt_server_request_crc_count(uint32_t crc) {
438 84 : int count = 0;
439 84 : size_t n = g_srv.crc_ring_count;
440 84 : if (n > MT_CRC_RING_SIZE) n = MT_CRC_RING_SIZE;
441 354 : for (size_t i = 0; i < n; ++i) {
442 270 : if (g_srv.crc_ring[i] == crc) count++;
443 : }
444 84 : return count;
445 : }
446 :
447 34 : void mt_server_arm_reconnect(void) {
448 34 : g_srv.reconnect_pending = 1;
449 34 : }
450 :
451 32 : int mt_server_seed_extra_dc(int dc_id) {
452 32 : if (!g_srv.seeded) return -1;
453 : MtProtoSession s;
454 32 : mtproto_session_init(&s);
455 32 : mtproto_session_set_auth_key(&s, g_srv.auth_key);
456 32 : mtproto_session_set_salt(&s, g_srv.server_salt);
457 32 : s.session_id = g_srv.session_id;
458 32 : return session_store_save_dc(dc_id, &s);
459 : }
460 :
461 4 : void mt_server_set_bad_salt_once(uint64_t new_salt) {
462 4 : g_srv.bad_salt_once_pending = 1;
463 4 : g_srv.bad_salt_new_salt = new_salt;
464 4 : }
465 :
466 2 : void mt_server_set_wrong_session_id_once(void) {
467 2 : g_srv.wrong_session_id_once_pending = 1;
468 2 : }
469 :
470 : /* ---- TEST-88 service-frame helpers ---------------------------------- */
471 :
472 : /** Append a service-frame body (raw TL, CRC-prefixed) to the queue drained
473 : * ahead of the next handler dispatch. Silently drops if the queue is full
474 : * (tests that need more than MT_MAX_PENDING_SVC would need to raise the
475 : * cap). Returns 1 on success, 0 on drop. */
476 140 : static int svc_queue_push(const uint8_t *body, size_t body_len) {
477 140 : if (g_srv.pending_service_count >= MT_MAX_PENDING_SVC) return 0;
478 140 : uint8_t *copy = (uint8_t *)malloc(body_len);
479 140 : if (!copy) return 0;
480 140 : memcpy(copy, body, body_len);
481 140 : g_srv.pending_service_frames[g_srv.pending_service_count].bytes = copy;
482 140 : g_srv.pending_service_frames[g_srv.pending_service_count].len = body_len;
483 140 : g_srv.pending_service_count++;
484 140 : return 1;
485 : }
486 :
487 2 : void mt_server_reply_bad_server_salt(uint64_t new_salt) {
488 : /* Reuse the existing one-shot path — the SVC_BAD_SALT branch is
489 : * sensitive to ordering (the client retries BEFORE reading any other
490 : * queued frame) so piping through set_bad_salt_once keeps the flow
491 : * identical to what rpc_send_encrypted observes on real hardware. */
492 2 : mt_server_set_bad_salt_once(new_salt);
493 2 : }
494 :
495 2 : void mt_server_reply_new_session_created(void) {
496 : /* new_session_created#9ec20908 first_msg_id:long unique_id:long
497 : * server_salt:long = NewSession */
498 : uint8_t body[4 + 8 + 8 + 8];
499 2 : uint32_t crc = CRC_new_session_created;
500 10 : for (int i = 0; i < 4; ++i) body[i] = (uint8_t)(crc >> (i * 8));
501 : /* first_msg_id — arbitrary recognisable pattern. */
502 2 : uint64_t first = 0xF111F111F111F111ULL;
503 18 : for (int i = 0; i < 8; ++i) body[4 + i] = (uint8_t)(first >> (i * 8));
504 : /* unique_id. */
505 2 : uint64_t uniq = 0xABC0ABC0ABC0ABC0ULL;
506 18 : for (int i = 0; i < 8; ++i) body[12 + i] = (uint8_t)(uniq >> (i * 8));
507 : /* server_salt — the value the client must adopt. Keep this stable so
508 : * tests can assert against a constant. */
509 2 : uint64_t fresh = 0xCAFEF00DBAADC0DEULL;
510 18 : for (int i = 0; i < 8; ++i) body[20 + i] = (uint8_t)(fresh >> (i * 8));
511 2 : svc_queue_push(body, sizeof(body));
512 2 : }
513 :
514 132 : void mt_server_reply_msgs_ack(const uint64_t *ids, size_t n) {
515 : /* msgs_ack#62d6b459 msg_ids:Vector<long> */
516 132 : TlWriter w; tl_writer_init(&w);
517 132 : tl_write_uint32(&w, CRC_msgs_ack);
518 132 : tl_write_uint32(&w, CRC_vector);
519 132 : tl_write_uint32(&w, (uint32_t)n);
520 268 : for (size_t i = 0; i < n; ++i) {
521 136 : tl_write_uint64(&w, ids ? ids[i] : (uint64_t)(0xAC000000DEAD0000ULL + i));
522 : }
523 132 : svc_queue_push(w.data, w.len);
524 132 : tl_writer_free(&w);
525 132 : }
526 :
527 2 : void mt_server_reply_pong(uint64_t msg_id, uint64_t ping_id) {
528 : /* pong#347773c5 msg_id:long ping_id:long = Pong */
529 : uint8_t body[4 + 8 + 8];
530 2 : uint32_t crc = CRC_pong;
531 10 : for (int i = 0; i < 4; ++i) body[i] = (uint8_t)(crc >> (i * 8));
532 18 : for (int i = 0; i < 8; ++i) body[4 + i] = (uint8_t)(msg_id >> (i * 8));
533 18 : for (int i = 0; i < 8; ++i) body[12 + i] = (uint8_t)(ping_id >> (i * 8));
534 2 : svc_queue_push(body, sizeof(body));
535 2 : }
536 :
537 4 : void mt_server_reply_bad_msg_notification(uint64_t bad_id, int code) {
538 : /* bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int
539 : * error_code:int = BadMsgNotification */
540 : uint8_t body[4 + 8 + 4 + 4];
541 4 : uint32_t crc = CRC_bad_msg_notification;
542 20 : for (int i = 0; i < 4; ++i) body[i] = (uint8_t)(crc >> (i * 8));
543 36 : for (int i = 0; i < 8; ++i) body[4 + i] = (uint8_t)(bad_id >> (i * 8));
544 4 : int32_t seqno = 0;
545 20 : for (int i = 0; i < 4; ++i) body[12 + i] = (uint8_t)(seqno >> (i * 8));
546 4 : int32_t ec = code;
547 20 : for (int i = 0; i < 4; ++i) body[16 + i] = (uint8_t)(ec >> (i * 8));
548 4 : svc_queue_push(body, sizeof(body));
549 4 : }
550 :
551 2 : void mt_server_stack_service_frames(size_t count) {
552 : /* Stack N msgs_ack frames. Each carries a distinct synthetic msg_id
553 : * so the client can log them individually (not that classify_service_frame
554 : * inspects the body — but giving them unique ids keeps the wire traffic
555 : * realistic). */
556 132 : for (size_t i = 0; i < count; ++i) {
557 130 : uint64_t id = 0xAC000000DEAD0000ULL + i;
558 130 : mt_server_reply_msgs_ack(&id, 1);
559 : }
560 2 : }
561 :
562 : /* ---- TEST-86 PHONE/USER/NETWORK_MIGRATE helpers ----------------------
563 : *
564 : * Three internal int slots carry the target DC across handler dispatch.
565 : * Tests can reassign a new DC between calls by re-invoking the helper.
566 : * Using static slots (rather than malloc'd ctx) keeps the helpers
567 : * alloc-free and matches the rest of the mock-server setup style. */
568 : static int g_phone_migrate_dc = 0;
569 : static int g_user_migrate_dc = 0;
570 : static int g_network_migrate_dc = 0;
571 :
572 10 : static void on_phone_migrate(MtRpcContext *ctx) {
573 10 : int dc = g_phone_migrate_dc;
574 : char buf[64];
575 10 : snprintf(buf, sizeof(buf), "PHONE_MIGRATE_%d", dc);
576 10 : mt_server_reply_error(ctx, 303, buf);
577 10 : }
578 :
579 2 : static void on_user_migrate(MtRpcContext *ctx) {
580 2 : int dc = g_user_migrate_dc;
581 : char buf[64];
582 2 : snprintf(buf, sizeof(buf), "USER_MIGRATE_%d", dc);
583 2 : mt_server_reply_error(ctx, 303, buf);
584 2 : }
585 :
586 2 : static void on_network_migrate(MtRpcContext *ctx) {
587 2 : int dc = g_network_migrate_dc;
588 : char buf[64];
589 2 : snprintf(buf, sizeof(buf), "NETWORK_MIGRATE_%d", dc);
590 2 : mt_server_reply_error(ctx, 303, buf);
591 2 : }
592 :
593 10 : void mt_server_reply_phone_migrate(int dc_id) {
594 10 : g_phone_migrate_dc = dc_id;
595 10 : mt_server_expect(CRC_auth_sendCode_local, on_phone_migrate, NULL);
596 10 : }
597 :
598 2 : void mt_server_reply_user_migrate(int dc_id) {
599 2 : g_user_migrate_dc = dc_id;
600 2 : mt_server_expect(CRC_auth_signIn_local, on_user_migrate, NULL);
601 2 : }
602 :
603 2 : void mt_server_reply_network_migrate(int dc_id) {
604 2 : g_network_migrate_dc = dc_id;
605 2 : mt_server_expect(CRC_auth_sendCode_local, on_network_migrate, NULL);
606 2 : }
607 :
608 : /* ---- TEST-70 / US-19 auth.exportAuthorization / importAuthorization ----
609 : *
610 : * Static slots hold the export token (id + bytes) and an override flag
611 : * for the one-shot AUTH_KEY_INVALID case. Using statics keeps the helper
612 : * API alloc-free and mirrors how the PHONE/USER/NETWORK_MIGRATE helpers
613 : * store their target DC. */
614 : #define MT_EXPORT_BYTES_MAX 1024
615 : static int64_t g_export_id = 0;
616 : static uint8_t g_export_bytes[MT_EXPORT_BYTES_MAX];
617 : static size_t g_export_bytes_len = 0;
618 : static int g_import_sign_up = 0;
619 : static int g_import_auth_key_invalid_pending = 0;
620 :
621 8 : static void on_export_authorization(MtRpcContext *ctx) {
622 : /* auth.exportedAuthorization#b434e2b8 id:long bytes:bytes = auth.ExportedAuthorization */
623 : TlWriter w;
624 8 : tl_writer_init(&w);
625 8 : tl_write_uint32(&w, CRC_auth_exportedAuthorization_local);
626 8 : tl_write_int64 (&w, g_export_id);
627 8 : tl_write_bytes (&w, g_export_bytes, g_export_bytes_len);
628 8 : mt_server_reply_result(ctx, w.data, w.len);
629 8 : tl_writer_free(&w);
630 8 : }
631 :
632 6 : static void on_import_authorization(MtRpcContext *ctx) {
633 6 : if (g_import_auth_key_invalid_pending) {
634 : /* Simulate a server-side token expiry race: the token we just
635 : * issued has gone stale before the client imported it. */
636 2 : g_import_auth_key_invalid_pending = 0;
637 2 : mt_server_reply_error(ctx, 401, "AUTH_KEY_INVALID");
638 2 : return;
639 : }
640 : TlWriter w;
641 4 : tl_writer_init(&w);
642 4 : if (g_import_sign_up) {
643 : /* auth.authorizationSignUpRequired#44747e9a has flags:#
644 : * terms_of_service:flags.0?help.TermsOfService = auth.Authorization.
645 : * Emit flags=0 (no TOS attached) — the client only cares about the
646 : * constructor CRC. */
647 2 : tl_write_uint32(&w, CRC_auth_authorizationSignUpRequired_local);
648 2 : tl_write_uint32(&w, 0);
649 : } else {
650 : /* auth.authorization#2ea2c0d4 flags:# ... user:User = auth.Authorization.
651 : * Emit the minimal shape accepted by the client's parser:
652 : * flags=0 + a user#3ff6ecb0 stub with flags=0 and id=101. */
653 2 : tl_write_uint32(&w, CRC_auth_authorization_local);
654 2 : tl_write_uint32(&w, 0); /* outer flags */
655 2 : tl_write_uint32(&w, CRC_user_local); /* user constructor */
656 2 : tl_write_uint32(&w, 0); /* user.flags */
657 2 : tl_write_int64 (&w, 101LL); /* user.id */
658 : }
659 4 : mt_server_reply_result(ctx, w.data, w.len);
660 4 : tl_writer_free(&w);
661 : }
662 :
663 8 : void mt_server_reply_export_authorization(int64_t id,
664 : const uint8_t *bytes, size_t len) {
665 8 : if (!bytes || len == 0 || len > MT_EXPORT_BYTES_MAX) return;
666 8 : g_export_id = id;
667 8 : memcpy(g_export_bytes, bytes, len);
668 8 : g_export_bytes_len = len;
669 8 : mt_server_expect(CRC_auth_exportAuthorization_local,
670 : on_export_authorization, NULL);
671 : }
672 :
673 6 : void mt_server_reply_import_authorization(int sign_up) {
674 6 : g_import_sign_up = sign_up ? 1 : 0;
675 6 : mt_server_expect(CRC_auth_importAuthorization_local,
676 : on_import_authorization, NULL);
677 6 : }
678 :
679 2 : void mt_server_reply_import_authorization_auth_key_invalid_once(void) {
680 2 : g_import_auth_key_invalid_pending = 1;
681 : /* Next dispatch after the AUTH_KEY_INVALID one-shot falls through to
682 : * the happy auth.authorization path (sign_up=0) so callers that want
683 : * a full reject→retry cycle do not need two helper calls. */
684 2 : g_import_sign_up = 0;
685 2 : mt_server_expect(CRC_auth_importAuthorization_local,
686 : on_import_authorization, NULL);
687 2 : }
688 :
689 : /* ---- TEST-71 / US-20 cold-boot DH handshake helpers ----
690 : *
691 : * The mock cannot decrypt the client's RSA_PAD(inner_data) because the
692 : * canonical Telegram RSA private key is not available. These helpers
693 : * therefore cover exhaustively only the paths reachable without that
694 : * private key: resPQ generation (step 1), plus a synthetic
695 : * server_DH_params_ok whose AES-IGE-wrapped payload decrypts to random
696 : * bytes and is rejected by the client's inner CRC check (step 3 error
697 : * path). Tests assert handshake start, negative variants, and
698 : * no-persistence-on-failure. */
699 :
700 : /* TEST-72: The functional test runner links tests/mocks/telegram_server_key.c
701 : * which defines TELEGRAM_RSA_FINGERPRINT = 0x8671de275f1cabc5ULL (test-only
702 : * 2048-bit key pair). This constant must match that value so resPQ lists a
703 : * fingerprint the production client recognises during handshake tests.
704 : * NEVER use this fingerprint in production. */
705 : #define COLD_BOOT_FP_OK 0x8671de275f1cabc5ULL
706 : #define COLD_BOOT_FP_BAD 0xDEADBEEFCAFEBABEULL
707 : #define CRC_resPQ 0x05162463U
708 : #define CRC_req_pq_multi 0xbe7e8ef1U
709 : #define CRC_req_DH_params 0xd712e4beU
710 : #define CRC_server_DH_params_ok 0xd0e8075cU
711 : #define CRC_server_DH_inner_data 0xb5890dbaU
712 : #define CRC_set_client_DH_params 0xf5045f1fU
713 : #define CRC_dh_gen_ok 0x3bcbf734U
714 :
715 18 : void mt_server_simulate_cold_boot(MtColdBootMode mode) {
716 : /* A cold-boot session has no persisted auth_key_id. Clear the seeded
717 : * flag so on_client_sent's guard lets unencrypted frames through, but
718 : * keep initialised state + mock_socket hook registered via reset(). */
719 18 : g_srv.handshake_mode = 1;
720 18 : g_srv.handshake_cold_boot_variant = (int)mode;
721 18 : g_srv.handshake_req_pq_count = 0;
722 18 : g_srv.handshake_req_dh_count = 0;
723 : /* Ensure auth_key_id is 0 so the parser takes the unencrypted branch. */
724 18 : g_srv.auth_key_id = 0;
725 18 : memset(g_srv.auth_key, 0, MT_SERVER_AUTH_KEY_SIZE);
726 : /* Allow on_client_sent to parse — the "seeded" label in this mock
727 : * means "ready to parse wire traffic", not "holds a post-handshake
728 : * auth key". Treat handshake_mode as an alternate seed state. */
729 18 : g_srv.seeded = 1;
730 18 : }
731 :
732 6 : void mt_server_simulate_cold_boot_through_step3(void) {
733 : /* Same as cold_boot(OK) but the mock also replies to req_DH_params
734 : * with a synthetic server_DH_params_ok. */
735 6 : if (g_srv.handshake_mode == 0) {
736 6 : mt_server_simulate_cold_boot(MT_COLD_BOOT_OK);
737 : }
738 6 : g_srv.handshake_mode = 2;
739 6 : }
740 :
741 2 : void mt_server_simulate_full_dh_handshake(void) {
742 : /* TEST-72: Full DH handshake — the mock RSA-PAD-decrypts the client's
743 : * req_DH_params payload (using the test RSA private key), generates
744 : * valid server_DH_inner_data, and handles set_client_DH_params to
745 : * return dh_gen_ok. On success the session auth_key is set and the
746 : * session is persisted. NEVER call this outside functional tests. */
747 2 : mt_server_simulate_cold_boot(MT_COLD_BOOT_OK);
748 2 : g_srv.handshake_mode = 3;
749 2 : g_srv.handshake_set_client_dh_count = 0;
750 2 : }
751 :
752 8 : int mt_server_handshake_req_pq_count(void) {
753 8 : return g_srv.handshake_req_pq_count;
754 : }
755 :
756 6 : int mt_server_handshake_req_dh_count(void) {
757 6 : return g_srv.handshake_req_dh_count;
758 : }
759 :
760 2 : int mt_server_handshake_set_client_dh_count(void) {
761 2 : return g_srv.handshake_set_client_dh_count;
762 : }
763 :
764 : /* ================================================================ */
765 : /* Internals */
766 : /* ================================================================ */
767 :
768 448 : static uint64_t derive_auth_key_id(const uint8_t *auth_key) {
769 : /* MTProto spec: auth_key_id = last 8 bytes of SHA1(auth_key) = SHA1[12:20]. */
770 : uint8_t hash[20];
771 448 : crypto_sha1(auth_key, MT_SERVER_AUTH_KEY_SIZE, hash);
772 448 : uint64_t id = 0;
773 4032 : for (int i = 0; i < 8; ++i) id |= ((uint64_t)hash[12 + i]) << (i * 8);
774 448 : return id;
775 : }
776 :
777 794 : static uint64_t make_server_msg_id(void) {
778 : /* Monotonic even msg_ids (server → client use low-bit 1 in real MT,
779 : * but the client tolerates either — we just need monotonic). */
780 794 : uint64_t now = (uint64_t)time(NULL) << 32;
781 794 : if (now <= g_srv.next_server_msg_id) now = g_srv.next_server_msg_id + 4;
782 794 : now &= ~((uint64_t)3);
783 794 : now |= 1; /* server → client msg_id is odd */
784 794 : g_srv.next_server_msg_id = now;
785 794 : return now;
786 : }
787 :
788 : /* Read an abridged length prefix from buf[cursor..len]. Returns:
789 : * bytes consumed by the prefix on success (1 or 4)
790 : * 0 if not enough bytes yet
791 : * On success, *payload_len receives the payload size in bytes. */
792 1322 : static size_t read_abridged_prefix(const uint8_t *buf, size_t len, size_t cursor,
793 : size_t *payload_len) {
794 1322 : if (cursor >= len) return 0;
795 1322 : uint8_t first = buf[cursor];
796 1322 : if (first < 0x7F) {
797 1194 : *payload_len = (size_t)first * 4;
798 1194 : return 1;
799 : }
800 128 : if (cursor + 4 > len) return 0;
801 128 : size_t wire = (size_t)buf[cursor + 1]
802 128 : | ((size_t)buf[cursor + 2] << 8)
803 128 : | ((size_t)buf[cursor + 3] << 16);
804 128 : *payload_len = wire * 4;
805 128 : return 4;
806 : }
807 :
808 1832 : static void on_client_sent(const uint8_t *buf, size_t len) {
809 1832 : if (!g_srv.seeded) return;
810 :
811 2932 : while (g_srv.parse_cursor < len) {
812 : /* Step 0 — consume the initial 0xEF marker on the very first byte.
813 : * Also handle a one-shot reconnect: if reconnect_pending is set and
814 : * the next byte is 0xEF, treat it as a new-connection marker (reset
815 : * parse state) so that a second transport session opened by production
816 : * code (e.g. cross-DC NETWORK_MIGRATE retry) is parsed cleanly. */
817 1844 : if (!g_srv.saw_marker) {
818 492 : if (buf[g_srv.parse_cursor] != 0xEFu) {
819 : /* Unexpected first byte — not an abridged connection. */
820 666 : return;
821 : }
822 492 : g_srv.parse_cursor++;
823 492 : g_srv.saw_marker = 1;
824 552 : continue;
825 : }
826 1352 : if (g_srv.reconnect_pending && buf[g_srv.parse_cursor] == 0xEFu) {
827 : /* A second transport connection opened by the client. Reset the
828 : * parser to treat this 0xEF as the new connection's abridged
829 : * marker. */
830 30 : g_srv.reconnect_pending = 0;
831 30 : g_srv.saw_marker = 0;
832 30 : continue; /* re-enter the saw_marker=0 branch above */
833 : }
834 :
835 1322 : size_t payload_len = 0;
836 1322 : size_t prefix_bytes = read_abridged_prefix(buf, len, g_srv.parse_cursor,
837 : &payload_len);
838 1322 : if (prefix_bytes == 0) return; /* need more data */
839 1322 : if (payload_len == 0) {
840 : /* Idle keep-alive — skip. */
841 0 : g_srv.parse_cursor += prefix_bytes;
842 0 : continue;
843 : }
844 1322 : if (g_srv.parse_cursor + prefix_bytes + payload_len > len) {
845 666 : return; /* wait for rest of payload */
846 : }
847 :
848 656 : const uint8_t *frame = buf + g_srv.parse_cursor + prefix_bytes;
849 656 : g_srv.parse_cursor += prefix_bytes + payload_len;
850 :
851 : /* Frame = auth_key_id(8) + msg_key(16) + ciphertext */
852 656 : if (payload_len < 24) continue;
853 656 : uint64_t key_id = 0;
854 5904 : for (int i = 0; i < 8; ++i) key_id |= ((uint64_t)frame[i]) << (i * 8);
855 :
856 : /* TEST-71: in cold-boot handshake mode the server's auth_key_id is
857 : * 0 (no session yet) and the client's frame also carries
858 : * auth_key_id = 0, so the equality check below would incorrectly
859 : * route the frame into the encrypted branch. Short-circuit here
860 : * and dispatch to the synthetic handshake responders instead. */
861 656 : if (g_srv.handshake_mode && key_id == 0 && payload_len >= 24) {
862 28 : uint32_t raw_crc = (uint32_t)frame[20]
863 28 : | ((uint32_t)frame[21] << 8)
864 28 : | ((uint32_t)frame[22] << 16)
865 28 : | ((uint32_t)frame[23] << 24);
866 28 : size_t slot = g_srv.crc_ring_count % MT_CRC_RING_SIZE;
867 28 : g_srv.crc_ring[slot] = raw_crc;
868 28 : g_srv.crc_ring_count++;
869 :
870 28 : const uint8_t *tl_body = frame + 20;
871 28 : size_t tl_body_len = payload_len - 20;
872 28 : if (raw_crc == CRC_req_pq_multi) {
873 18 : g_srv.handshake_req_pq_count++;
874 18 : handshake_on_req_pq_multi(tl_body, tl_body_len);
875 10 : } else if (raw_crc == CRC_req_DH_params
876 8 : && g_srv.handshake_mode >= 2) {
877 8 : g_srv.handshake_req_dh_count++;
878 8 : handshake_on_req_dh_params(tl_body, tl_body_len);
879 2 : } else if (raw_crc == CRC_req_DH_params) {
880 : /* Count it but do not reply — tests can observe the
881 : * counter to prove the client reached step 2. */
882 0 : g_srv.handshake_req_dh_count++;
883 2 : } else if (raw_crc == CRC_set_client_DH_params
884 2 : && g_srv.handshake_mode == 3) {
885 : /* TEST-72: full DH — compute auth_key from client's g_b,
886 : * verify key_hash, send dh_gen_ok, persist session. */
887 2 : g_srv.handshake_set_client_dh_count++;
888 2 : handshake_on_set_client_dh(tl_body, tl_body_len);
889 : }
890 28 : continue;
891 : }
892 :
893 628 : if (key_id != g_srv.auth_key_id) {
894 : /* Unencrypted handshake frame: auth_key_id == 0.
895 : * Record the leading CRC for mt_server_request_crc_count().
896 : * Unencrypted layout: key_id(8) + msg_id(8) + msg_len(4) + body */
897 2 : if (key_id == 0 && payload_len >= 24) {
898 2 : uint32_t raw_crc = (uint32_t)frame[20]
899 2 : | ((uint32_t)frame[21] << 8)
900 2 : | ((uint32_t)frame[22] << 16)
901 2 : | ((uint32_t)frame[23] << 24);
902 2 : size_t slot = g_srv.crc_ring_count % MT_CRC_RING_SIZE;
903 2 : g_srv.crc_ring[slot] = raw_crc;
904 2 : g_srv.crc_ring_count++;
905 : } else {
906 0 : fprintf(stderr, "mt_server: auth_key_id mismatch "
907 : "(got %016llx, want %016llx)\n",
908 : (unsigned long long)key_id,
909 0 : (unsigned long long)g_srv.auth_key_id);
910 : }
911 2 : continue;
912 : }
913 :
914 : uint8_t msg_key[16];
915 626 : memcpy(msg_key, frame + 8, 16);
916 626 : const uint8_t *cipher = frame + 24;
917 626 : size_t cipher_len = payload_len - 24;
918 :
919 626 : uint8_t *plain = (uint8_t *)malloc(cipher_len);
920 626 : if (!plain) continue;
921 626 : size_t plain_len = 0;
922 :
923 626 : int rc = mtproto_decrypt(cipher, cipher_len,
924 : g_srv.auth_key, msg_key, 0,
925 : plain, &plain_len);
926 626 : if (rc != 0) {
927 0 : fprintf(stderr, "mt_server: mtproto_decrypt failed\n");
928 0 : free(plain);
929 0 : continue;
930 : }
931 626 : dispatch_frame(plain, plain_len);
932 626 : free(plain);
933 : }
934 : }
935 :
936 626 : static void dispatch_frame(const uint8_t *plain, size_t plain_len) {
937 : /* plaintext header: salt(8) + session_id(8) + msg_id(8) + seq_no(4) + len(4) + body */
938 626 : if (plain_len < 32) return;
939 626 : uint64_t client_session = 0;
940 5634 : for (int i = 0; i < 8; ++i) {
941 5008 : client_session |= ((uint64_t)plain[8 + i]) << (i * 8);
942 : }
943 626 : uint64_t msg_id = 0;
944 5634 : for (int i = 0; i < 8; ++i) {
945 5008 : msg_id |= ((uint64_t)plain[16 + i]) << (i * 8);
946 : }
947 626 : uint32_t body_len = 0;
948 3130 : for (int i = 0; i < 4; ++i) {
949 2504 : body_len |= ((uint32_t)plain[28 + i]) << (i * 8);
950 : }
951 626 : if (32 + body_len > plain_len) return;
952 626 : const uint8_t *body = plain + 32;
953 :
954 : /* First frame carries the client session id — pin it so later msg_container
955 : * updates use the same one. If the client somehow changes it, that's a
956 : * protocol error we mirror by just echoing back whatever we last saw. */
957 626 : if (g_srv.session_id == 0x1122334455667788ULL) {
958 : /* keep the seeded value — the client echoes this from session.bin */
959 : }
960 : (void)client_session;
961 :
962 626 : unwrap_and_dispatch(msg_id, body, body_len);
963 : }
964 :
965 626 : static void unwrap_and_dispatch(uint64_t req_msg_id,
966 : const uint8_t *body, size_t body_len) {
967 1250 : if (body_len < 4) return;
968 626 : const uint8_t *cur = body;
969 626 : size_t remaining = body_len;
970 :
971 : /* Peel invokeWithLayer / initConnection off the front. */
972 1842 : for (int depth = 0; depth < 3; ++depth) {
973 1842 : if (remaining < 4) return;
974 1842 : uint32_t crc = (uint32_t)cur[0] | ((uint32_t)cur[1] << 8)
975 1842 : | ((uint32_t)cur[2] << 16) | ((uint32_t)cur[3] << 24);
976 1842 : if (crc == CRC_invokeWithLayer) {
977 608 : if (remaining < 8) return;
978 608 : cur += 8; remaining -= 8; /* CRC + layer:int */
979 608 : continue;
980 : }
981 1234 : if (crc == CRC_initConnection) {
982 : /* CRC(4) flags(4) api_id(4) string×6 [proxy:flags.0?] [params:flags.1?] query:!X */
983 608 : TlReader r = tl_reader_init(cur, remaining);
984 608 : tl_read_uint32(&r); /* CRC */
985 608 : uint32_t flags = tl_read_uint32(&r);
986 608 : tl_read_int32(&r); /* api_id */
987 4256 : for (int i = 0; i < 6; ++i) {
988 3648 : if (tl_skip_string(&r) != 0) return;
989 : }
990 608 : if (flags & 0x1) {
991 : /* inputClientProxy#75588b3f server:string port:int */
992 0 : tl_read_uint32(&r);
993 0 : if (tl_skip_string(&r) != 0) return;
994 0 : tl_read_int32(&r);
995 : }
996 608 : if (flags & 0x2) {
997 : /* jsonObject#7d748d04 — walking JSONValue is out of scope;
998 : * the client never sets this flag so bail if we ever see it. */
999 0 : return;
1000 : }
1001 608 : size_t consumed = r.pos;
1002 608 : cur += consumed; remaining -= consumed;
1003 608 : continue;
1004 : }
1005 626 : break;
1006 : }
1007 :
1008 626 : if (remaining < 4) return;
1009 626 : uint32_t inner_crc = (uint32_t)cur[0] | ((uint32_t)cur[1] << 8)
1010 626 : | ((uint32_t)cur[2] << 16) | ((uint32_t)cur[3] << 24);
1011 :
1012 : /* One-shot bad_server_salt injection — bounces the client back with a
1013 : * fresh salt and forces it to resend. The handler is not called on this
1014 : * round; it fires on the retry. */
1015 626 : if (g_srv.bad_salt_once_pending) {
1016 4 : g_srv.bad_salt_once_pending = 0;
1017 : /* bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int
1018 : * error_code:int new_server_salt:long */
1019 : uint8_t buf[4 + 8 + 4 + 4 + 8];
1020 4 : uint32_t crc = 0xedab447bU;
1021 20 : for (int i = 0; i < 4; ++i) buf[i] = (uint8_t)(crc >> (i * 8));
1022 36 : for (int i = 0; i < 8; ++i) buf[4 + i] = (uint8_t)(req_msg_id >> (i * 8));
1023 : /* bad_msg_seqno + error_code 48 (= incorrect server salt) — values
1024 : * are informational only for the client's logger. */
1025 4 : int32_t seq0 = 0;
1026 20 : for (int i = 0; i < 4; ++i) buf[12 + i] = (uint8_t)(seq0 >> (i * 8));
1027 4 : int32_t ec = 48;
1028 20 : for (int i = 0; i < 4; ++i) buf[16 + i] = (uint8_t)(ec >> (i * 8));
1029 36 : for (int i = 0; i < 8; ++i) {
1030 32 : buf[20 + i] = (uint8_t)(g_srv.bad_salt_new_salt >> (i * 8));
1031 : }
1032 4 : queue_frame(buf, sizeof(buf));
1033 : /* Update server salt so subsequent responses use what the client now
1034 : * expects — the client discards the inbound salt, so this is purely
1035 : * cosmetic (a real server would). */
1036 4 : g_srv.server_salt = g_srv.bad_salt_new_salt;
1037 4 : return;
1038 : }
1039 :
1040 : /* Record the inner CRC in the ring buffer. */
1041 : {
1042 622 : size_t slot = g_srv.crc_ring_count % MT_CRC_RING_SIZE;
1043 622 : g_srv.crc_ring[slot] = inner_crc;
1044 622 : g_srv.crc_ring_count++;
1045 : }
1046 :
1047 : /* Drain any service frames queued by mt_server_reply_* helpers
1048 : * (TEST-88). They land on the wire ahead of the real result so the
1049 : * client's classify_service_frame loop observes them exactly once per
1050 : * iteration. queue_frame wraps each body in its own encrypted envelope,
1051 : * which the client treats as an independent frame. */
1052 762 : for (size_t i = 0; i < g_srv.pending_service_count; ++i) {
1053 140 : queue_frame(g_srv.pending_service_frames[i].bytes,
1054 : g_srv.pending_service_frames[i].len);
1055 140 : free(g_srv.pending_service_frames[i].bytes);
1056 140 : g_srv.pending_service_frames[i].bytes = NULL;
1057 140 : g_srv.pending_service_frames[i].len = 0;
1058 : }
1059 622 : g_srv.pending_service_count = 0;
1060 :
1061 : /* Record + invoke handler. */
1062 622 : g_srv.rpc_call_count++;
1063 622 : g_srv.current_req_msg_id = req_msg_id;
1064 :
1065 802 : for (size_t i = 0; i < MT_MAX_HANDLERS; ++i) {
1066 800 : if (g_srv.handlers[i].used && g_srv.handlers[i].crc == inner_crc) {
1067 620 : MtRpcContext ctx = {
1068 : .req_msg_id = req_msg_id,
1069 : .req_crc = inner_crc,
1070 : .req_body = cur,
1071 : .req_body_len = remaining,
1072 620 : .user_ctx = g_srv.handlers[i].ctx,
1073 : };
1074 620 : g_srv.handlers[i].fn(&ctx);
1075 620 : return;
1076 : }
1077 : }
1078 :
1079 : /* No handler → auto rpc_error so the test fails explicitly rather than
1080 : * hanging on recv. */
1081 2 : MtRpcContext ctx = {
1082 : .req_msg_id = req_msg_id,
1083 : .req_crc = inner_crc,
1084 : .req_body = cur,
1085 : .req_body_len = remaining,
1086 : .user_ctx = NULL,
1087 : };
1088 : char buf[64];
1089 2 : snprintf(buf, sizeof(buf), "NO_HANDLER_CRC_%08x", inner_crc);
1090 2 : mt_server_reply_error(&ctx, 500, buf);
1091 : }
1092 :
1093 766 : static void queue_frame(const uint8_t *body, size_t body_len) {
1094 : /* Build plaintext header: salt + session_id + msg_id + seq_no + len + body + padding. */
1095 : TlWriter plain;
1096 766 : tl_writer_init(&plain);
1097 766 : tl_write_uint64(&plain, g_srv.server_salt);
1098 766 : uint64_t effective_session_id = g_srv.session_id;
1099 766 : if (g_srv.wrong_session_id_once_pending) {
1100 2 : g_srv.wrong_session_id_once_pending = 0;
1101 2 : effective_session_id ^= 0xFFFFFFFFFFFFFFFFULL; /* flip all bits */
1102 : }
1103 766 : tl_write_uint64(&plain, effective_session_id);
1104 766 : tl_write_uint64(&plain, make_server_msg_id());
1105 : /* seq_no: bump by 2 for content-related, start at 1 so first is 1, 3, 5… */
1106 766 : g_srv.seq_no += 2;
1107 766 : tl_write_uint32(&plain, g_srv.seq_no - 1);
1108 766 : tl_write_uint32(&plain, (uint32_t)body_len);
1109 766 : tl_write_raw(&plain, body, body_len);
1110 :
1111 766 : encrypt_and_queue(plain.data, plain.len);
1112 766 : tl_writer_free(&plain);
1113 766 : }
1114 :
1115 : /* ---- TEST-71 / US-20 handshake unencrypted-frame handlers ---- */
1116 :
1117 : /** Queue an unencrypted (auth_key_id = 0) server → client frame with the
1118 : * handshake response TL. Mirrors rpc_send_unencrypted on the client side
1119 : * so the client's rpc_recv_unencrypted parser picks it up. */
1120 28 : static void handshake_queue_unenc(const uint8_t *tl, size_t tl_len) {
1121 : /* Wire: auth_key_id(8) + msg_id(8) + len(4) + body */
1122 28 : TlWriter w; tl_writer_init(&w);
1123 28 : tl_write_uint64(&w, 0); /* auth_key_id */
1124 28 : tl_write_uint64(&w, make_server_msg_id()); /* server msg_id */
1125 28 : tl_write_uint32(&w, (uint32_t)tl_len);
1126 28 : tl_write_raw(&w, tl, tl_len);
1127 :
1128 28 : size_t units = w.len / 4;
1129 28 : if (units < 0x7F) {
1130 28 : uint8_t p = (uint8_t)units;
1131 28 : mock_socket_append_response(&p, 1);
1132 : } else {
1133 : uint8_t p[4];
1134 0 : p[0] = 0x7F;
1135 0 : p[1] = (uint8_t)(units & 0xFFu);
1136 0 : p[2] = (uint8_t)((units >> 8) & 0xFFu);
1137 0 : p[3] = (uint8_t)((units >> 16) & 0xFFu);
1138 0 : mock_socket_append_response(p, 4);
1139 : }
1140 28 : mock_socket_append_response(w.data, w.len);
1141 28 : tl_writer_free(&w);
1142 28 : }
1143 :
1144 : /** Handle an incoming req_pq_multi frame, emit a resPQ per the current
1145 : * cold-boot variant. Assumes @p body points at the CRC-prefixed TL body
1146 : * (CRC already consumed by the caller would break layout — we take the
1147 : * whole 4+16 body here). */
1148 18 : static void handshake_on_req_pq_multi(const uint8_t *body, size_t body_len) {
1149 18 : if (body_len < 4 + 16) return;
1150 : /* body = CRC(4) nonce(16) */
1151 : uint8_t client_nonce[16];
1152 18 : memcpy(client_nonce, body + 4, 16);
1153 :
1154 : /* Echo (possibly tampered) nonce back. */
1155 : uint8_t echo_nonce[16];
1156 18 : memcpy(echo_nonce, client_nonce, 16);
1157 18 : if (g_srv.handshake_cold_boot_variant == MT_COLD_BOOT_NONCE_TAMPER) {
1158 34 : for (int i = 0; i < 16; ++i) echo_nonce[i] ^= 0xFF;
1159 : }
1160 :
1161 : /* Deterministic server_nonce for reproducibility. */
1162 : uint8_t server_nonce[16];
1163 18 : memset(server_nonce, 0xBB, 16);
1164 :
1165 : /* PQ: default 21 (= 3 * 7 — tiny so Pollard's rho finishes instantly).
1166 : * MT_COLD_BOOT_BAD_PQ uses a 64-bit prime so pq_factorize fails. */
1167 18 : uint64_t pq_val = 21ULL;
1168 18 : if (g_srv.handshake_cold_boot_variant == MT_COLD_BOOT_BAD_PQ) {
1169 : /* 2^64 - 59 is a prime. Small enough to survive Pollard's rho
1170 : * 20-c sweep without factoring. */
1171 2 : pq_val = 0xFFFFFFFFFFFFFFC5ULL;
1172 : }
1173 : /* Encode pq as big-endian minimum-length bytes. */
1174 : uint8_t pq_be[8];
1175 18 : size_t pq_be_len = 0;
1176 162 : for (int i = 7; i >= 0; --i) {
1177 144 : uint8_t byte = (uint8_t)((pq_val >> (i * 8)) & 0xFFu);
1178 144 : if (pq_be_len > 0 || byte != 0 || i == 0) {
1179 32 : pq_be[pq_be_len++] = byte;
1180 : }
1181 : }
1182 :
1183 18 : uint64_t fingerprint = COLD_BOOT_FP_OK;
1184 18 : if (g_srv.handshake_cold_boot_variant == MT_COLD_BOOT_BAD_FINGERPRINT) {
1185 2 : fingerprint = COLD_BOOT_FP_BAD;
1186 : }
1187 :
1188 18 : uint32_t constructor = CRC_resPQ;
1189 18 : if (g_srv.handshake_cold_boot_variant == MT_COLD_BOOT_WRONG_CONSTRUCTOR) {
1190 2 : constructor = 0xDEADBEEFU;
1191 : }
1192 :
1193 18 : TlWriter tl; tl_writer_init(&tl);
1194 18 : tl_write_uint32(&tl, constructor);
1195 18 : tl_write_int128(&tl, echo_nonce);
1196 18 : tl_write_int128(&tl, server_nonce);
1197 18 : tl_write_bytes(&tl, pq_be, pq_be_len);
1198 18 : tl_write_uint32(&tl, CRC_vector);
1199 18 : tl_write_uint32(&tl, 1); /* one fingerprint entry */
1200 18 : tl_write_uint64(&tl, fingerprint);
1201 18 : handshake_queue_unenc(tl.data, tl.len);
1202 18 : tl_writer_free(&tl);
1203 : }
1204 :
1205 : /* ---- TEST-72: RSA-PAD decrypt (inverse of rsa_pad_encrypt in mtproto_auth.c) ----
1206 : *
1207 : * Reverses the MTProto 1.0 SHA1 RSA scheme: RSA_NO_PADDING decrypt → 256 bytes,
1208 : * skip leading zero byte, strip 20-byte SHA1 hash prefix → plain data.
1209 : * Returns 0 and writes data into @p plain_out (max @p plain_max bytes) and
1210 : * sets *plain_len to the actual data length.
1211 : *
1212 : * TEST_ONLY — only called from handshake_on_req_dh_params in mode 3. */
1213 2 : static int rsa_sha1_decrypt(const uint8_t *ciphertext, size_t cipher_len,
1214 : uint8_t *plain_out, size_t plain_max,
1215 : size_t *plain_len) {
1216 2 : if (cipher_len != 256) return -1;
1217 :
1218 : /* TEST_ONLY private key — matches tests/mocks/telegram_server_key.c pubkey. */
1219 : static const char TEST_PRIVATE_KEY_PEM[] =
1220 : "-----BEGIN PRIVATE KEY-----\n"
1221 : "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbG/j8RdvTAAWv\n"
1222 : "870ayFCbJI73fEEB43/l/NnocYeAh9L/Zcv9HwYwFOXms9o2ccvp+e/4GE55vUzY\n"
1223 : "8XrM1h6dtClGYNvRNpvcthfmVrpGGIjKH2b3snip4ajtUOcZIyTynZo1vMG5uqCx\n"
1224 : "YaXWyhBwMPJQ87Gw5WbcaKNJWg3jZ1GI0g+tJUAqXpfPwF3KjKzIZwa/rbnJ9ick\n"
1225 : "OX12OaG1c2enW1+sv1K1qz6CcxmbCyRuD+xUKVTLHc5ykmcYDJ12CLES8DdfbPOt\n"
1226 : "UGAP082CQOptfpPm5fvnU6c53suIItnddjiT74+hyQBXQpvWXKIZzFEdgxJ2QUzp\n"
1227 : "TlRwqVijAgMBAAECggEAJll6rIjrKlaRkWjOgw4u28Tksize98QTTb4/9DgJnA44\n"
1228 : "7VtyXYFrqryoAOvL0nU9SPq6SZlc4b2bf/HofjecdzphkByHjMkXLTE6ZIFh6c3M\n"
1229 : "GEk+UJSoP7xi41YC5VSqoG+1/n5OWYjajTDK63mnKc34Q2qVLtrxHSKj6JFi6Kuy\n"
1230 : "uO1+lXUiGS5vvlPllAoDTIFn4e4PHChHeVNiwzBe8HNgVWc9JasL7IiS37gsfuZC\n"
1231 : "l4QlW3IkhCMkb5fj7xV/xcwQ4SuYWcNkrfuX7Fu3g4Hpeir3xJlJMxh2iqJnARvM\n"
1232 : "nLy9gY04ejqdxI9+VopQyXafe0tP/veuqO7RzR3x+QKBgQDQvd9drWmvWM9g7Lrj\n"
1233 : "jEr4oo00b5/cOQd12VJaxhWUdfzyDxsygGPeuP3/7QxgwxB+VmnsS2xfNvv4hDdd\n"
1234 : "vL6DhFPicTV5Xv25U52QaxNefc5XSSzY68XMFtUyWjzD+35GIUO27TnDfQhFBkbS\n"
1235 : "RFDg3uJ1KbcI05nM+DSQJZPaqQKBgQC+ObQxrgZBPd6K6vVM/uaoA45Htc8UVIS/\n"
1236 : "+eU69k1lVp6BZm1ebaTOf6CTOO/AcyM4jZu7lVFwfanVMVSfJ+Nbd1BEXtg1BKou\n"
1237 : "2cLlYkJX6mVmCs39sQDscvoO67RMhR4jshCE0nxaAxFatdEfpq9ZTrQIXWKnQhRq\n"
1238 : "nHsyIyHUawKBgQCwTf5vx70AvfkB+1BqSp8z2096X2FdBsn3TqORSccGSpVm+T1W\n"
1239 : "bTxs7ECUPWn7/CVdH619R8LztKQjJcEBqh4bRNP46PdqWMHiGu51AQst/wIdlQ+M\n"
1240 : "865vj0VorvCt8yeXIhdoVHs6Ust+SSveApdxJq+Ml7whd19q0KTMrwBvaQKBgC8C\n"
1241 : "i6mLXDhbVdf24NA6Xj4/QrYuFBLuIDBhTWkY3V+h3GIWMgkYB5aQq9o2Q+nHini7\n"
1242 : "ZjUhXZLzOzlYi5UZgnJkNg3vcncHxBb38dZGRib74jspiGadi6DjeTCex1vxudUQ\n"
1243 : "eEyax+hmwa8tJ5Uu2D612IAItAypo+oE6d0mGYIpAoGAS2P6e2iOW2HG3CA2kiLp\n"
1244 : "xw/guEi8BTTljxMSUEqS5YhDI2uVe2QEYlvgDG7zYMl2UzoadZcK1VVa01WM2LDC\n"
1245 : "wThzA+NhfHS4ukPuuOkJUVwGAJGUG/KcIypTTYz4RPj6BpALLXstF+Qf8F3aqDvL\n"
1246 : "bAd3PHlfEWOhiWzBj/dVjUY=\n"
1247 : "-----END PRIVATE KEY-----\n";
1248 :
1249 : (void)plain_max;
1250 :
1251 2 : CryptoRsaKey *priv = crypto_rsa_load_private(TEST_PRIVATE_KEY_PEM);
1252 2 : if (!priv) {
1253 0 : fprintf(stderr, "mock: rsa_sha1_decrypt: failed to load private key\n");
1254 0 : return -1;
1255 : }
1256 :
1257 : /* RSA_NO_PADDING decrypt → 256 bytes */
1258 : uint8_t rsa_output[256];
1259 2 : size_t rsa_out_len = sizeof(rsa_output);
1260 2 : if (crypto_rsa_private_decrypt(priv, ciphertext, cipher_len,
1261 : rsa_output, &rsa_out_len) != 0) {
1262 0 : crypto_rsa_free(priv);
1263 0 : fprintf(stderr, "mock: rsa_sha1_decrypt: RSA decrypt failed\n");
1264 0 : return -1;
1265 : }
1266 2 : crypto_rsa_free(priv);
1267 :
1268 : /* rsa_output[0] = 0x00 (zero prefix added by client)
1269 : * rsa_output[1..20] = SHA1(data)
1270 : * rsa_output[21..255] = data + random padding */
1271 2 : const uint8_t *sha1_prefix = rsa_output + 1; /* 20 bytes */
1272 2 : const uint8_t *data_start = rsa_output + 21; /* up to 235 bytes */
1273 2 : size_t max_data = 255 - 20;
1274 :
1275 : /* The data starts with a 4-byte TL CRC. Find the actual data length by
1276 : * verifying SHA1. We try lengths starting at 4 (minimum TL object). */
1277 154 : for (size_t dlen = 4; dlen <= max_data; dlen++) {
1278 : uint8_t sha1_check[20];
1279 154 : crypto_sha1(data_start, dlen, sha1_check);
1280 154 : if (memcmp(sha1_check, sha1_prefix, 20) == 0) {
1281 2 : memcpy(plain_out, data_start, dlen);
1282 2 : *plain_len = dlen;
1283 2 : return 0;
1284 : }
1285 : }
1286 0 : fprintf(stderr, "mock: rsa_sha1_decrypt: SHA1 verification failed\n");
1287 0 : return -1;
1288 : }
1289 :
1290 : /** Handle an incoming req_DH_params frame.
1291 : *
1292 : * Mode 2 (original): emits server_DH_params_ok with random encrypted_answer
1293 : * (garbage). The client's AES-IGE decrypt yields garbage, the inner CRC check
1294 : * fails, and auth_step_parse_dh returns -1 — the negative path.
1295 : *
1296 : * Mode 3 (TEST-72): RSA-PAD-decrypts the client's payload to extract new_nonce,
1297 : * generates a valid server_DH_inner_data (g=2, safe prime, g_a=g^b mod p),
1298 : * AES-IGE-encrypts it with the derived temp key, and sends server_DH_params_ok.
1299 : * Stores DH state for subsequent set_client_DH_params handling. */
1300 8 : static void handshake_on_req_dh_params(const uint8_t *body, size_t body_len) {
1301 14 : if (body_len < 4 + 16 + 16) return;
1302 : /* body = CRC(4) nonce(16) server_nonce(16) p:bytes q:bytes fp:long encrypted_data:bytes */
1303 : uint8_t nonce[16], server_nonce[16];
1304 8 : memcpy(nonce, body + 4, 16);
1305 8 : memcpy(server_nonce, body + 4 + 16, 16);
1306 :
1307 8 : if (g_srv.handshake_mode < 3) {
1308 : /* Original mode 2 path: random garbage encrypted_answer. */
1309 : uint8_t enc_answer[32];
1310 6 : crypto_rand_bytes(enc_answer, sizeof(enc_answer));
1311 6 : TlWriter tl; tl_writer_init(&tl);
1312 6 : tl_write_uint32(&tl, CRC_server_DH_params_ok);
1313 6 : tl_write_int128(&tl, nonce);
1314 6 : tl_write_int128(&tl, server_nonce);
1315 6 : tl_write_bytes(&tl, enc_answer, sizeof(enc_answer));
1316 6 : handshake_queue_unenc(tl.data, tl.len);
1317 6 : tl_writer_free(&tl);
1318 6 : return;
1319 : }
1320 :
1321 : /* ---- Mode 3: full DH handshake ---- */
1322 :
1323 : /* Parse the req_DH_params body to find the encrypted_data.
1324 : * Layout: CRC(4) nonce(16) server_nonce(16) p:bytes q:bytes fp:int64 encrypted_data:bytes */
1325 2 : TlReader r = tl_reader_init(body, body_len);
1326 2 : tl_read_uint32(&r); /* CRC */
1327 2 : uint8_t _nonce[16]; tl_read_int128(&r, _nonce);
1328 2 : uint8_t _snonce[16]; tl_read_int128(&r, _snonce);
1329 2 : size_t p_len = 0, q_len = 0, enc_len = 0;
1330 2 : uint8_t *p_bytes = tl_read_bytes(&r, &p_len);
1331 2 : free(p_bytes);
1332 2 : uint8_t *q_bytes = tl_read_bytes(&r, &q_len);
1333 2 : free(q_bytes);
1334 2 : tl_read_uint64(&r); /* fingerprint */
1335 2 : uint8_t *enc_data = tl_read_bytes(&r, &enc_len);
1336 2 : if (!enc_data || enc_len != 256) {
1337 0 : free(enc_data);
1338 0 : fprintf(stderr, "mock: full DH: unexpected enc_data size %zu\n", enc_len);
1339 0 : return;
1340 : }
1341 :
1342 : /* SHA1-based RSA decrypt to get p_q_inner_data (MTProto 1.0 legacy) */
1343 : uint8_t inner_plain[256];
1344 2 : size_t inner_plain_len = 0;
1345 2 : if (rsa_sha1_decrypt(enc_data, enc_len, inner_plain, sizeof(inner_plain),
1346 : &inner_plain_len) != 0) {
1347 0 : free(enc_data);
1348 0 : fprintf(stderr, "mock: full DH: RSA SHA1 decrypt failed\n");
1349 0 : return;
1350 : }
1351 2 : free(enc_data);
1352 :
1353 : /* Parse p_q_inner_data (legacy, no dc field) or p_q_inner_data_dc.
1354 : * TL: CRC(4) pq:bytes p:bytes q:bytes nonce(16) server_nonce(16) new_nonce(32) [dc:int] */
1355 2 : TlReader ir = tl_reader_init(inner_plain, inner_plain_len);
1356 2 : uint32_t inner_crc = tl_read_uint32(&ir);
1357 2 : int has_dc = 0;
1358 2 : if (inner_crc == 0xa9f55f95U) { /* p_q_inner_data_dc */
1359 0 : has_dc = 1;
1360 2 : } else if (inner_crc == 0x83c95aecU) { /* p_q_inner_data (legacy) */
1361 2 : has_dc = 0;
1362 : } else {
1363 0 : fprintf(stderr, "mock: full DH: unexpected inner CRC 0x%08x\n", inner_crc);
1364 0 : return;
1365 : }
1366 : /* Skip pq, p, q bytes */
1367 2 : size_t tmp_len = 0;
1368 2 : uint8_t *tmp = tl_read_bytes(&ir, &tmp_len); free(tmp); /* pq */
1369 2 : tmp = tl_read_bytes(&ir, &tmp_len); free(tmp); /* p */
1370 2 : tmp = tl_read_bytes(&ir, &tmp_len); free(tmp); /* q */
1371 : /* Read nonce, server_nonce, new_nonce */
1372 : uint8_t extracted_nonce[16], extracted_snonce[16], new_nonce[32];
1373 2 : tl_read_int128(&ir, extracted_nonce);
1374 2 : tl_read_int128(&ir, extracted_snonce);
1375 2 : tl_read_int256(&ir, new_nonce);
1376 : (void)has_dc; /* dc_id field skipped if present — not used by mock */
1377 :
1378 : /* Save DH state for set_client_DH_params */
1379 2 : memcpy(g_srv.hs_new_nonce, new_nonce, 32);
1380 2 : memcpy(g_srv.hs_nonce, extracted_nonce, 16);
1381 2 : memcpy(g_srv.hs_server_nonce, extracted_snonce, 16);
1382 :
1383 : /* Use a fixed 256-bit safe prime for DH (TEST_ONLY).
1384 : * This prime was generated with openssl: BN_generate_prime_ex(p,256,1,...).
1385 : * g=2 is a generator for this group. */
1386 : static const uint8_t TEST_DH_PRIME[32] = {
1387 : 0xfa, 0xd0, 0x8e, 0x08, 0xa4, 0x4d, 0x25, 0xaa,
1388 : 0x45, 0x2b, 0xda, 0x58, 0x62, 0xac, 0xc4, 0xb2,
1389 : 0x76, 0x23, 0xd3, 0x30, 0x4d, 0xd0, 0x9d, 0x64,
1390 : 0xc1, 0xdd, 0xc0, 0xfb, 0x35, 0x09, 0x40, 0xdb
1391 : };
1392 2 : memcpy(g_srv.hs_dh_prime, TEST_DH_PRIME, 32);
1393 :
1394 : /* Generate server-side DH secret b (256 bytes) and compute g_b = 2^b mod p */
1395 2 : crypto_rand_bytes(g_srv.hs_b, 256);
1396 :
1397 2 : uint8_t g_be[1] = { 0x02 }; /* g = 2 as 1-byte big-endian */
1398 : uint8_t g_b[32];
1399 2 : size_t g_b_len = sizeof(g_b);
1400 2 : CryptoBnCtx *bn_ctx = crypto_bn_ctx_new();
1401 2 : if (!bn_ctx) { fprintf(stderr, "mock: full DH: BN ctx alloc failed\n"); return; }
1402 2 : if (crypto_bn_mod_exp(g_b, &g_b_len, g_be, 1,
1403 : g_srv.hs_b, 256,
1404 : TEST_DH_PRIME, 32, bn_ctx) != 0) {
1405 0 : crypto_bn_ctx_free(bn_ctx);
1406 0 : fprintf(stderr, "mock: full DH: g_b computation failed\n");
1407 0 : return;
1408 : }
1409 2 : crypto_bn_ctx_free(bn_ctx);
1410 :
1411 : /* Derive temp AES key/IV from new_nonce + server_nonce (same as production) */
1412 : uint8_t tmp_aes_key[32], tmp_aes_iv[32];
1413 : {
1414 : uint8_t buf[64];
1415 : uint8_t sha1_a[20], sha1_b[20], sha1_c[20];
1416 :
1417 2 : memcpy(buf, new_nonce, 32); memcpy(buf + 32, extracted_snonce, 16);
1418 2 : crypto_sha1(buf, 48, sha1_a);
1419 2 : memcpy(buf, extracted_snonce, 16); memcpy(buf + 16, new_nonce, 32);
1420 2 : crypto_sha1(buf, 48, sha1_b);
1421 2 : memcpy(buf, new_nonce, 32); memcpy(buf + 32, new_nonce, 32);
1422 2 : crypto_sha1(buf, 64, sha1_c);
1423 :
1424 2 : memcpy(tmp_aes_key, sha1_a, 20);
1425 2 : memcpy(tmp_aes_key + 20, sha1_b, 12);
1426 2 : memcpy(tmp_aes_iv, sha1_b + 12, 8);
1427 2 : memcpy(tmp_aes_iv + 8, sha1_c, 20);
1428 2 : memcpy(tmp_aes_iv + 28, new_nonce, 4);
1429 : }
1430 :
1431 : /* Build server_DH_inner_data TL */
1432 2 : TlWriter inner; tl_writer_init(&inner);
1433 2 : tl_write_uint32(&inner, CRC_server_DH_inner_data);
1434 2 : tl_write_int128(&inner, extracted_nonce);
1435 2 : tl_write_int128(&inner, extracted_snonce);
1436 2 : tl_write_int32(&inner, 2); /* g = 2 */
1437 2 : tl_write_bytes(&inner, TEST_DH_PRIME, 32); /* dh_prime */
1438 2 : tl_write_bytes(&inner, g_b, g_b_len); /* g_a (server's g^b) */
1439 2 : tl_write_int32(&inner, (int32_t)time(NULL)); /* server_time */
1440 :
1441 : /* Prepend SHA1 + pad to 16-byte boundary */
1442 : uint8_t sha1_inner[20];
1443 2 : crypto_sha1(inner.data, inner.len, sha1_inner);
1444 2 : size_t raw_len = 20 + inner.len;
1445 2 : size_t padded_len = (raw_len + 15) & ~(size_t)15;
1446 2 : uint8_t *padded = (uint8_t *)calloc(1, padded_len);
1447 2 : if (!padded) { tl_writer_free(&inner); return; }
1448 2 : memcpy(padded, sha1_inner, 20);
1449 2 : memcpy(padded + 20, inner.data, inner.len);
1450 2 : if (padded_len > raw_len) {
1451 2 : crypto_rand_bytes(padded + raw_len, padded_len - raw_len);
1452 : }
1453 2 : tl_writer_free(&inner);
1454 :
1455 : /* AES-IGE encrypt */
1456 2 : uint8_t *enc_answer = (uint8_t *)malloc(padded_len);
1457 2 : if (!enc_answer) { free(padded); return; }
1458 2 : aes_ige_encrypt(padded, padded_len, tmp_aes_key, tmp_aes_iv, enc_answer);
1459 2 : free(padded);
1460 :
1461 : /* Send server_DH_params_ok */
1462 2 : TlWriter tl; tl_writer_init(&tl);
1463 2 : tl_write_uint32(&tl, CRC_server_DH_params_ok);
1464 2 : tl_write_int128(&tl, extracted_nonce);
1465 2 : tl_write_int128(&tl, extracted_snonce);
1466 2 : tl_write_bytes(&tl, enc_answer, padded_len);
1467 2 : handshake_queue_unenc(tl.data, tl.len);
1468 2 : tl_writer_free(&tl);
1469 2 : free(enc_answer);
1470 : }
1471 :
1472 : /** TEST-72: Handle set_client_DH_params, compute auth_key = g_b^a mod p,
1473 : * verify key_hash, send dh_gen_ok, and persist the session. */
1474 2 : static void handshake_on_set_client_dh(const uint8_t *body, size_t body_len) {
1475 2 : if (body_len < 4 + 16 + 16) return;
1476 :
1477 : /* Derive temp AES key/IV from stored new_nonce + server_nonce */
1478 : uint8_t tmp_aes_key[32], tmp_aes_iv[32];
1479 : {
1480 : uint8_t buf[64];
1481 : uint8_t sha1_a[20], sha1_b[20], sha1_c[20];
1482 :
1483 2 : memcpy(buf, g_srv.hs_new_nonce, 32);
1484 2 : memcpy(buf + 32, g_srv.hs_server_nonce, 16);
1485 2 : crypto_sha1(buf, 48, sha1_a);
1486 :
1487 2 : memcpy(buf, g_srv.hs_server_nonce, 16);
1488 2 : memcpy(buf + 16, g_srv.hs_new_nonce, 32);
1489 2 : crypto_sha1(buf, 48, sha1_b);
1490 :
1491 2 : memcpy(buf, g_srv.hs_new_nonce, 32);
1492 2 : memcpy(buf + 32, g_srv.hs_new_nonce, 32);
1493 2 : crypto_sha1(buf, 64, sha1_c);
1494 :
1495 2 : memcpy(tmp_aes_key, sha1_a, 20);
1496 2 : memcpy(tmp_aes_key + 20, sha1_b, 12);
1497 2 : memcpy(tmp_aes_iv, sha1_b + 12, 8);
1498 2 : memcpy(tmp_aes_iv + 8, sha1_c, 20);
1499 2 : memcpy(tmp_aes_iv + 28, g_srv.hs_new_nonce, 4);
1500 : }
1501 :
1502 : /* Parse set_client_DH_params body:
1503 : * CRC(4) nonce(16) server_nonce(16) encrypted_data:bytes */
1504 2 : TlReader r = tl_reader_init(body, body_len);
1505 2 : tl_read_uint32(&r); /* CRC */
1506 2 : uint8_t _nonce[16]; tl_read_int128(&r, _nonce);
1507 2 : uint8_t _snonce[16]; tl_read_int128(&r, _snonce);
1508 2 : size_t enc_len = 0;
1509 2 : uint8_t *enc_data = tl_read_bytes(&r, &enc_len);
1510 2 : if (!enc_data || enc_len == 0 || enc_len % 16 != 0) {
1511 0 : free(enc_data);
1512 0 : fprintf(stderr, "mock: set_client_DH: bad encrypted_data len %zu\n", enc_len);
1513 0 : return;
1514 : }
1515 :
1516 : /* AES-IGE decrypt encrypted_data */
1517 2 : uint8_t *plain = (uint8_t *)malloc(enc_len);
1518 2 : if (!plain) { free(enc_data); return; }
1519 2 : aes_ige_decrypt(enc_data, enc_len, tmp_aes_key, tmp_aes_iv, plain);
1520 2 : free(enc_data);
1521 :
1522 : /* Parse client_DH_inner_data: SHA1(20) + CRC(4) nonce(16) server_nonce(16)
1523 : * retry_id:int64 g_b:bytes */
1524 2 : if (enc_len < 20 + 4 + 16 + 16 + 8) {
1525 0 : free(plain);
1526 0 : fprintf(stderr, "mock: set_client_DH: plaintext too short\n");
1527 0 : return;
1528 : }
1529 2 : TlReader ir = tl_reader_init(plain + 20, enc_len - 20);
1530 2 : uint32_t crc = tl_read_uint32(&ir);
1531 2 : if (crc != 0x6643b654U) { /* CRC_client_DH_inner_data */
1532 0 : free(plain);
1533 0 : fprintf(stderr, "mock: set_client_DH: wrong CRC 0x%08x\n", crc);
1534 0 : return;
1535 : }
1536 2 : uint8_t cli_nonce[16]; tl_read_int128(&ir, cli_nonce);
1537 2 : uint8_t cli_snonce[16]; tl_read_int128(&ir, cli_snonce);
1538 2 : tl_read_uint64(&ir); /* retry_id */
1539 2 : size_t g_b_len = 0;
1540 2 : uint8_t *g_b = tl_read_bytes(&ir, &g_b_len); /* client's g^client_b mod p */
1541 2 : if (!g_b || g_b_len == 0) {
1542 0 : free(g_b); free(plain);
1543 0 : fprintf(stderr, "mock: set_client_DH: failed to read g_b\n");
1544 0 : return;
1545 : }
1546 2 : free(plain);
1547 :
1548 : /* Save g_a (client's g^client_b mod p) for auth_key computation */
1549 2 : if (g_b_len > sizeof(g_srv.hs_g_a)) {
1550 0 : free(g_b);
1551 0 : fprintf(stderr, "mock: set_client_DH: g_b too large (%zu)\n", g_b_len);
1552 0 : return;
1553 : }
1554 2 : memcpy(g_srv.hs_g_a, g_b, g_b_len);
1555 2 : g_srv.hs_g_a_len = g_b_len;
1556 2 : free(g_b);
1557 :
1558 : /* Compute auth_key = g_b^server_b mod dh_prime */
1559 : uint8_t auth_key[256];
1560 2 : size_t ak_len = sizeof(auth_key);
1561 2 : CryptoBnCtx *bn_ctx = crypto_bn_ctx_new();
1562 2 : if (!bn_ctx) { fprintf(stderr, "mock: set_client_DH: BN ctx alloc\n"); return; }
1563 2 : if (crypto_bn_mod_exp(auth_key, &ak_len,
1564 : g_srv.hs_g_a, g_srv.hs_g_a_len,
1565 : g_srv.hs_b, 256,
1566 : g_srv.hs_dh_prime, 32, bn_ctx) != 0) {
1567 0 : crypto_bn_ctx_free(bn_ctx);
1568 0 : fprintf(stderr, "mock: set_client_DH: auth_key exp failed\n");
1569 0 : return;
1570 : }
1571 2 : crypto_bn_ctx_free(bn_ctx);
1572 :
1573 : /* Pad auth_key to 256 bytes */
1574 : uint8_t auth_key_padded[256];
1575 2 : memset(auth_key_padded, 0, 256);
1576 2 : if (ak_len <= 256) {
1577 2 : memcpy(auth_key_padded + (256 - ak_len), auth_key, ak_len);
1578 : }
1579 :
1580 : /* Compute new_nonce_hash1 = last 16 bytes of SHA1(new_nonce||0x01||ak_aux_hash[8])
1581 : * where ak_aux_hash = SHA1(auth_key)[0:8] */
1582 : uint8_t ak_full_hash[20];
1583 2 : crypto_sha1(auth_key_padded, 256, ak_full_hash);
1584 :
1585 : uint8_t nnh_input[32 + 1 + 8];
1586 2 : memcpy(nnh_input, g_srv.hs_new_nonce, 32);
1587 2 : nnh_input[32] = 0x01;
1588 2 : memcpy(nnh_input + 33, ak_full_hash, 8);
1589 :
1590 : uint8_t nnh_full[20];
1591 2 : crypto_sha1(nnh_input, sizeof(nnh_input), nnh_full);
1592 : /* last 16 bytes = nnh_full[4:20] */
1593 : uint8_t new_nonce_hash1[16];
1594 2 : memcpy(new_nonce_hash1, nnh_full + 4, 16);
1595 :
1596 : /* Save auth_key to mock server state so encrypted frames work after handshake */
1597 2 : memcpy(g_srv.auth_key, auth_key_padded, 256);
1598 2 : g_srv.auth_key_id = derive_auth_key_id(auth_key_padded);
1599 : /* salt = new_nonce[0:8] XOR server_nonce[0:8] */
1600 2 : g_srv.server_salt = 0;
1601 18 : for (int i = 0; i < 8; i++) {
1602 16 : ((uint8_t *)&g_srv.server_salt)[i] =
1603 16 : g_srv.hs_new_nonce[i] ^ g_srv.hs_server_nonce[i];
1604 : }
1605 2 : g_srv.session_id = 0xAABBCCDD11223344ULL;
1606 :
1607 : /* Persist session so the client can verify it was written */
1608 : MtProtoSession sess;
1609 2 : mtproto_session_init(&sess);
1610 2 : mtproto_session_set_auth_key(&sess, auth_key_padded);
1611 2 : mtproto_session_set_salt(&sess, g_srv.server_salt);
1612 2 : sess.session_id = g_srv.session_id;
1613 2 : session_store_save(&sess, 2); /* dc_id=2 matches the test */
1614 :
1615 : /* Send dh_gen_ok */
1616 2 : TlWriter tl; tl_writer_init(&tl);
1617 2 : tl_write_uint32(&tl, CRC_dh_gen_ok);
1618 2 : tl_write_int128(&tl, g_srv.hs_nonce);
1619 2 : tl_write_int128(&tl, g_srv.hs_server_nonce);
1620 2 : tl_write_int128(&tl, new_nonce_hash1);
1621 2 : handshake_queue_unenc(tl.data, tl.len);
1622 2 : tl_writer_free(&tl);
1623 : }
1624 :
1625 766 : static void encrypt_and_queue(const uint8_t *plain, size_t plain_len) {
1626 766 : uint8_t *enc = (uint8_t *)malloc(plain_len + 1024);
1627 766 : if (!enc) return;
1628 766 : size_t enc_len = 0;
1629 : uint8_t msg_key[16];
1630 766 : mtproto_encrypt(plain, plain_len, g_srv.auth_key, 8, enc, &enc_len, msg_key);
1631 :
1632 766 : size_t wire_len = 8 + 16 + enc_len;
1633 766 : uint8_t *wire = (uint8_t *)malloc(wire_len);
1634 766 : if (!wire) { free(enc); return; }
1635 6894 : for (int i = 0; i < 8; ++i) {
1636 6128 : wire[i] = (uint8_t)((g_srv.auth_key_id >> (i * 8)) & 0xFFu);
1637 : }
1638 766 : memcpy(wire + 8, msg_key, 16);
1639 766 : memcpy(wire + 24, enc, enc_len);
1640 766 : free(enc);
1641 :
1642 : /* Abridged length prefix (in 4-byte units). */
1643 766 : size_t units = wire_len / 4;
1644 766 : if (units < 0x7F) {
1645 718 : uint8_t p = (uint8_t)units;
1646 718 : mock_socket_append_response(&p, 1);
1647 : } else {
1648 : uint8_t p[4];
1649 48 : p[0] = 0x7F;
1650 48 : p[1] = (uint8_t)(units & 0xFFu);
1651 48 : p[2] = (uint8_t)((units >> 8) & 0xFFu);
1652 48 : p[3] = (uint8_t)((units >> 16) & 0xFFu);
1653 48 : mock_socket_append_response(p, 4);
1654 : }
1655 766 : mock_socket_append_response(wire, wire_len);
1656 766 : free(wire);
1657 : }
|