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 16 /* stacked service frames ahead of next reply */
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 96 : void mt_server_reply_error(const MtRpcContext *ctx,
263 : int32_t error_code, const char *error_msg) {
264 96 : if (!ctx) return;
265 : /* rpc_error#2144ca19 = error_code:int error_message:string */
266 : TlWriter w;
267 96 : tl_writer_init(&w);
268 96 : tl_write_uint32(&w, CRC_rpc_error);
269 96 : tl_write_int32(&w, error_code);
270 96 : tl_write_string(&w, error_msg ? error_msg : "");
271 96 : mt_server_reply_result(ctx, w.data, w.len);
272 96 : 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 28 : static int svc_queue_push(const uint8_t *body, size_t body_len) {
477 28 : if (g_srv.pending_service_count >= MT_MAX_PENDING_SVC) return 0;
478 28 : uint8_t *copy = (uint8_t *)malloc(body_len);
479 28 : if (!copy) return 0;
480 28 : memcpy(copy, body, body_len);
481 28 : g_srv.pending_service_frames[g_srv.pending_service_count].bytes = copy;
482 28 : g_srv.pending_service_frames[g_srv.pending_service_count].len = body_len;
483 28 : g_srv.pending_service_count++;
484 28 : 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 20 : void mt_server_reply_msgs_ack(const uint64_t *ids, size_t n) {
515 : /* msgs_ack#62d6b459 msg_ids:Vector<long> */
516 20 : TlWriter w; tl_writer_init(&w);
517 20 : tl_write_uint32(&w, CRC_msgs_ack);
518 20 : tl_write_uint32(&w, CRC_vector);
519 20 : tl_write_uint32(&w, (uint32_t)n);
520 44 : for (size_t i = 0; i < n; ++i) {
521 24 : tl_write_uint64(&w, ids ? ids[i] : (uint64_t)(0xAC000000DEAD0000ULL + i));
522 : }
523 20 : svc_queue_push(w.data, w.len);
524 20 : tl_writer_free(&w);
525 20 : }
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 20 : for (size_t i = 0; i < count; ++i) {
557 18 : uint64_t id = 0xAC000000DEAD0000ULL + i;
558 18 : 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 : uint8_t hash[32];
770 448 : crypto_sha256(auth_key, MT_SERVER_AUTH_KEY_SIZE, hash);
771 448 : uint64_t id = 0;
772 4032 : for (int i = 0; i < 8; ++i) id |= ((uint64_t)hash[24 + i]) << (i * 8);
773 448 : return id;
774 : }
775 :
776 682 : static uint64_t make_server_msg_id(void) {
777 : /* Monotonic even msg_ids (server → client use low-bit 1 in real MT,
778 : * but the client tolerates either — we just need monotonic). */
779 682 : uint64_t now = (uint64_t)time(NULL) << 32;
780 682 : if (now <= g_srv.next_server_msg_id) now = g_srv.next_server_msg_id + 4;
781 682 : now &= ~((uint64_t)3);
782 682 : now |= 1; /* server → client msg_id is odd */
783 682 : g_srv.next_server_msg_id = now;
784 682 : return now;
785 : }
786 :
787 : /* Read an abridged length prefix from buf[cursor..len]. Returns:
788 : * bytes consumed by the prefix on success (1 or 4)
789 : * 0 if not enough bytes yet
790 : * On success, *payload_len receives the payload size in bytes. */
791 1322 : static size_t read_abridged_prefix(const uint8_t *buf, size_t len, size_t cursor,
792 : size_t *payload_len) {
793 1322 : if (cursor >= len) return 0;
794 1322 : uint8_t first = buf[cursor];
795 1322 : if (first < 0x7F) {
796 1194 : *payload_len = (size_t)first * 4;
797 1194 : return 1;
798 : }
799 128 : if (cursor + 4 > len) return 0;
800 128 : size_t wire = (size_t)buf[cursor + 1]
801 128 : | ((size_t)buf[cursor + 2] << 8)
802 128 : | ((size_t)buf[cursor + 3] << 16);
803 128 : *payload_len = wire * 4;
804 128 : return 4;
805 : }
806 :
807 1832 : static void on_client_sent(const uint8_t *buf, size_t len) {
808 1832 : if (!g_srv.seeded) return;
809 :
810 2932 : while (g_srv.parse_cursor < len) {
811 : /* Step 0 — consume the initial 0xEF marker on the very first byte.
812 : * Also handle a one-shot reconnect: if reconnect_pending is set and
813 : * the next byte is 0xEF, treat it as a new-connection marker (reset
814 : * parse state) so that a second transport session opened by production
815 : * code (e.g. cross-DC NETWORK_MIGRATE retry) is parsed cleanly. */
816 1844 : if (!g_srv.saw_marker) {
817 492 : if (buf[g_srv.parse_cursor] != 0xEFu) {
818 : /* Unexpected first byte — not an abridged connection. */
819 666 : return;
820 : }
821 492 : g_srv.parse_cursor++;
822 492 : g_srv.saw_marker = 1;
823 552 : continue;
824 : }
825 1352 : if (g_srv.reconnect_pending && buf[g_srv.parse_cursor] == 0xEFu) {
826 : /* A second transport connection opened by the client. Reset the
827 : * parser to treat this 0xEF as the new connection's abridged
828 : * marker. */
829 30 : g_srv.reconnect_pending = 0;
830 30 : g_srv.saw_marker = 0;
831 30 : continue; /* re-enter the saw_marker=0 branch above */
832 : }
833 :
834 1322 : size_t payload_len = 0;
835 1322 : size_t prefix_bytes = read_abridged_prefix(buf, len, g_srv.parse_cursor,
836 : &payload_len);
837 1322 : if (prefix_bytes == 0) return; /* need more data */
838 1322 : if (payload_len == 0) {
839 : /* Idle keep-alive — skip. */
840 0 : g_srv.parse_cursor += prefix_bytes;
841 0 : continue;
842 : }
843 1322 : if (g_srv.parse_cursor + prefix_bytes + payload_len > len) {
844 666 : return; /* wait for rest of payload */
845 : }
846 :
847 656 : const uint8_t *frame = buf + g_srv.parse_cursor + prefix_bytes;
848 656 : g_srv.parse_cursor += prefix_bytes + payload_len;
849 :
850 : /* Frame = auth_key_id(8) + msg_key(16) + ciphertext */
851 656 : if (payload_len < 24) continue;
852 656 : uint64_t key_id = 0;
853 5904 : for (int i = 0; i < 8; ++i) key_id |= ((uint64_t)frame[i]) << (i * 8);
854 :
855 : /* TEST-71: in cold-boot handshake mode the server's auth_key_id is
856 : * 0 (no session yet) and the client's frame also carries
857 : * auth_key_id = 0, so the equality check below would incorrectly
858 : * route the frame into the encrypted branch. Short-circuit here
859 : * and dispatch to the synthetic handshake responders instead. */
860 656 : if (g_srv.handshake_mode && key_id == 0 && payload_len >= 24) {
861 28 : uint32_t raw_crc = (uint32_t)frame[20]
862 28 : | ((uint32_t)frame[21] << 8)
863 28 : | ((uint32_t)frame[22] << 16)
864 28 : | ((uint32_t)frame[23] << 24);
865 28 : size_t slot = g_srv.crc_ring_count % MT_CRC_RING_SIZE;
866 28 : g_srv.crc_ring[slot] = raw_crc;
867 28 : g_srv.crc_ring_count++;
868 :
869 28 : const uint8_t *tl_body = frame + 20;
870 28 : size_t tl_body_len = payload_len - 20;
871 28 : if (raw_crc == CRC_req_pq_multi) {
872 18 : g_srv.handshake_req_pq_count++;
873 18 : handshake_on_req_pq_multi(tl_body, tl_body_len);
874 10 : } else if (raw_crc == CRC_req_DH_params
875 8 : && g_srv.handshake_mode >= 2) {
876 8 : g_srv.handshake_req_dh_count++;
877 8 : handshake_on_req_dh_params(tl_body, tl_body_len);
878 2 : } else if (raw_crc == CRC_req_DH_params) {
879 : /* Count it but do not reply — tests can observe the
880 : * counter to prove the client reached step 2. */
881 0 : g_srv.handshake_req_dh_count++;
882 2 : } else if (raw_crc == CRC_set_client_DH_params
883 2 : && g_srv.handshake_mode == 3) {
884 : /* TEST-72: full DH — compute auth_key from client's g_b,
885 : * verify key_hash, send dh_gen_ok, persist session. */
886 2 : g_srv.handshake_set_client_dh_count++;
887 2 : handshake_on_set_client_dh(tl_body, tl_body_len);
888 : }
889 28 : continue;
890 : }
891 :
892 628 : if (key_id != g_srv.auth_key_id) {
893 : /* Unencrypted handshake frame: auth_key_id == 0.
894 : * Record the leading CRC for mt_server_request_crc_count().
895 : * Unencrypted layout: key_id(8) + msg_id(8) + msg_len(4) + body */
896 2 : if (key_id == 0 && payload_len >= 24) {
897 2 : uint32_t raw_crc = (uint32_t)frame[20]
898 2 : | ((uint32_t)frame[21] << 8)
899 2 : | ((uint32_t)frame[22] << 16)
900 2 : | ((uint32_t)frame[23] << 24);
901 2 : size_t slot = g_srv.crc_ring_count % MT_CRC_RING_SIZE;
902 2 : g_srv.crc_ring[slot] = raw_crc;
903 2 : g_srv.crc_ring_count++;
904 : } else {
905 0 : fprintf(stderr, "mt_server: auth_key_id mismatch "
906 : "(got %016llx, want %016llx)\n",
907 : (unsigned long long)key_id,
908 0 : (unsigned long long)g_srv.auth_key_id);
909 : }
910 2 : continue;
911 : }
912 :
913 : uint8_t msg_key[16];
914 626 : memcpy(msg_key, frame + 8, 16);
915 626 : const uint8_t *cipher = frame + 24;
916 626 : size_t cipher_len = payload_len - 24;
917 :
918 626 : uint8_t *plain = (uint8_t *)malloc(cipher_len);
919 626 : if (!plain) continue;
920 626 : size_t plain_len = 0;
921 :
922 626 : int rc = mtproto_decrypt(cipher, cipher_len,
923 : g_srv.auth_key, msg_key, 0,
924 : plain, &plain_len);
925 626 : if (rc != 0) {
926 0 : fprintf(stderr, "mt_server: mtproto_decrypt failed\n");
927 0 : free(plain);
928 0 : continue;
929 : }
930 626 : dispatch_frame(plain, plain_len);
931 626 : free(plain);
932 : }
933 : }
934 :
935 626 : static void dispatch_frame(const uint8_t *plain, size_t plain_len) {
936 : /* plaintext header: salt(8) + session_id(8) + msg_id(8) + seq_no(4) + len(4) + body */
937 626 : if (plain_len < 32) return;
938 626 : uint64_t client_session = 0;
939 5634 : for (int i = 0; i < 8; ++i) {
940 5008 : client_session |= ((uint64_t)plain[8 + i]) << (i * 8);
941 : }
942 626 : uint64_t msg_id = 0;
943 5634 : for (int i = 0; i < 8; ++i) {
944 5008 : msg_id |= ((uint64_t)plain[16 + i]) << (i * 8);
945 : }
946 626 : uint32_t body_len = 0;
947 3130 : for (int i = 0; i < 4; ++i) {
948 2504 : body_len |= ((uint32_t)plain[28 + i]) << (i * 8);
949 : }
950 626 : if (32 + body_len > plain_len) return;
951 626 : const uint8_t *body = plain + 32;
952 :
953 : /* First frame carries the client session id — pin it so later msg_container
954 : * updates use the same one. If the client somehow changes it, that's a
955 : * protocol error we mirror by just echoing back whatever we last saw. */
956 626 : if (g_srv.session_id == 0x1122334455667788ULL) {
957 : /* keep the seeded value — the client echoes this from session.bin */
958 : }
959 : (void)client_session;
960 :
961 626 : unwrap_and_dispatch(msg_id, body, body_len);
962 : }
963 :
964 626 : static void unwrap_and_dispatch(uint64_t req_msg_id,
965 : const uint8_t *body, size_t body_len) {
966 1250 : if (body_len < 4) return;
967 626 : const uint8_t *cur = body;
968 626 : size_t remaining = body_len;
969 :
970 : /* Peel invokeWithLayer / initConnection off the front. */
971 1842 : for (int depth = 0; depth < 3; ++depth) {
972 1842 : if (remaining < 4) return;
973 1842 : uint32_t crc = (uint32_t)cur[0] | ((uint32_t)cur[1] << 8)
974 1842 : | ((uint32_t)cur[2] << 16) | ((uint32_t)cur[3] << 24);
975 1842 : if (crc == CRC_invokeWithLayer) {
976 608 : if (remaining < 8) return;
977 608 : cur += 8; remaining -= 8; /* CRC + layer:int */
978 608 : continue;
979 : }
980 1234 : if (crc == CRC_initConnection) {
981 : /* CRC(4) flags(4) api_id(4) string×6 [proxy:flags.0?] [params:flags.1?] query:!X */
982 608 : TlReader r = tl_reader_init(cur, remaining);
983 608 : tl_read_uint32(&r); /* CRC */
984 608 : uint32_t flags = tl_read_uint32(&r);
985 608 : tl_read_int32(&r); /* api_id */
986 4256 : for (int i = 0; i < 6; ++i) {
987 3648 : if (tl_skip_string(&r) != 0) return;
988 : }
989 608 : if (flags & 0x1) {
990 : /* inputClientProxy#75588b3f server:string port:int */
991 0 : tl_read_uint32(&r);
992 0 : if (tl_skip_string(&r) != 0) return;
993 0 : tl_read_int32(&r);
994 : }
995 608 : if (flags & 0x2) {
996 : /* jsonObject#7d748d04 — walking JSONValue is out of scope;
997 : * the client never sets this flag so bail if we ever see it. */
998 0 : return;
999 : }
1000 608 : size_t consumed = r.pos;
1001 608 : cur += consumed; remaining -= consumed;
1002 608 : continue;
1003 : }
1004 626 : break;
1005 : }
1006 :
1007 626 : if (remaining < 4) return;
1008 626 : uint32_t inner_crc = (uint32_t)cur[0] | ((uint32_t)cur[1] << 8)
1009 626 : | ((uint32_t)cur[2] << 16) | ((uint32_t)cur[3] << 24);
1010 :
1011 : /* One-shot bad_server_salt injection — bounces the client back with a
1012 : * fresh salt and forces it to resend. The handler is not called on this
1013 : * round; it fires on the retry. */
1014 626 : if (g_srv.bad_salt_once_pending) {
1015 4 : g_srv.bad_salt_once_pending = 0;
1016 : /* bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int
1017 : * error_code:int new_server_salt:long */
1018 : uint8_t buf[4 + 8 + 4 + 4 + 8];
1019 4 : uint32_t crc = 0xedab447bU;
1020 20 : for (int i = 0; i < 4; ++i) buf[i] = (uint8_t)(crc >> (i * 8));
1021 36 : for (int i = 0; i < 8; ++i) buf[4 + i] = (uint8_t)(req_msg_id >> (i * 8));
1022 : /* bad_msg_seqno + error_code 48 (= incorrect server salt) — values
1023 : * are informational only for the client's logger. */
1024 4 : int32_t seq0 = 0;
1025 20 : for (int i = 0; i < 4; ++i) buf[12 + i] = (uint8_t)(seq0 >> (i * 8));
1026 4 : int32_t ec = 48;
1027 20 : for (int i = 0; i < 4; ++i) buf[16 + i] = (uint8_t)(ec >> (i * 8));
1028 36 : for (int i = 0; i < 8; ++i) {
1029 32 : buf[20 + i] = (uint8_t)(g_srv.bad_salt_new_salt >> (i * 8));
1030 : }
1031 4 : queue_frame(buf, sizeof(buf));
1032 : /* Update server salt so subsequent responses use what the client now
1033 : * expects — the client discards the inbound salt, so this is purely
1034 : * cosmetic (a real server would). */
1035 4 : g_srv.server_salt = g_srv.bad_salt_new_salt;
1036 4 : return;
1037 : }
1038 :
1039 : /* Record the inner CRC in the ring buffer. */
1040 : {
1041 622 : size_t slot = g_srv.crc_ring_count % MT_CRC_RING_SIZE;
1042 622 : g_srv.crc_ring[slot] = inner_crc;
1043 622 : g_srv.crc_ring_count++;
1044 : }
1045 :
1046 : /* Drain any service frames queued by mt_server_reply_* helpers
1047 : * (TEST-88). They land on the wire ahead of the real result so the
1048 : * client's classify_service_frame loop observes them exactly once per
1049 : * iteration. queue_frame wraps each body in its own encrypted envelope,
1050 : * which the client treats as an independent frame. */
1051 650 : for (size_t i = 0; i < g_srv.pending_service_count; ++i) {
1052 28 : queue_frame(g_srv.pending_service_frames[i].bytes,
1053 : g_srv.pending_service_frames[i].len);
1054 28 : free(g_srv.pending_service_frames[i].bytes);
1055 28 : g_srv.pending_service_frames[i].bytes = NULL;
1056 28 : g_srv.pending_service_frames[i].len = 0;
1057 : }
1058 622 : g_srv.pending_service_count = 0;
1059 :
1060 : /* Record + invoke handler. */
1061 622 : g_srv.rpc_call_count++;
1062 622 : g_srv.current_req_msg_id = req_msg_id;
1063 :
1064 802 : for (size_t i = 0; i < MT_MAX_HANDLERS; ++i) {
1065 800 : if (g_srv.handlers[i].used && g_srv.handlers[i].crc == inner_crc) {
1066 620 : MtRpcContext ctx = {
1067 : .req_msg_id = req_msg_id,
1068 : .req_crc = inner_crc,
1069 : .req_body = cur,
1070 : .req_body_len = remaining,
1071 620 : .user_ctx = g_srv.handlers[i].ctx,
1072 : };
1073 620 : g_srv.handlers[i].fn(&ctx);
1074 620 : return;
1075 : }
1076 : }
1077 :
1078 : /* No handler → auto rpc_error so the test fails explicitly rather than
1079 : * hanging on recv. */
1080 2 : MtRpcContext ctx = {
1081 : .req_msg_id = req_msg_id,
1082 : .req_crc = inner_crc,
1083 : .req_body = cur,
1084 : .req_body_len = remaining,
1085 : .user_ctx = NULL,
1086 : };
1087 : char buf[64];
1088 2 : snprintf(buf, sizeof(buf), "NO_HANDLER_CRC_%08x", inner_crc);
1089 2 : mt_server_reply_error(&ctx, 500, buf);
1090 : }
1091 :
1092 654 : static void queue_frame(const uint8_t *body, size_t body_len) {
1093 : /* Build plaintext header: salt + session_id + msg_id + seq_no + len + body + padding. */
1094 : TlWriter plain;
1095 654 : tl_writer_init(&plain);
1096 654 : tl_write_uint64(&plain, g_srv.server_salt);
1097 654 : uint64_t effective_session_id = g_srv.session_id;
1098 654 : if (g_srv.wrong_session_id_once_pending) {
1099 2 : g_srv.wrong_session_id_once_pending = 0;
1100 2 : effective_session_id ^= 0xFFFFFFFFFFFFFFFFULL; /* flip all bits */
1101 : }
1102 654 : tl_write_uint64(&plain, effective_session_id);
1103 654 : tl_write_uint64(&plain, make_server_msg_id());
1104 : /* seq_no: bump by 2 for content-related, start at 1 so first is 1, 3, 5… */
1105 654 : g_srv.seq_no += 2;
1106 654 : tl_write_uint32(&plain, g_srv.seq_no - 1);
1107 654 : tl_write_uint32(&plain, (uint32_t)body_len);
1108 654 : tl_write_raw(&plain, body, body_len);
1109 :
1110 654 : encrypt_and_queue(plain.data, plain.len);
1111 654 : tl_writer_free(&plain);
1112 654 : }
1113 :
1114 : /* ---- TEST-71 / US-20 handshake unencrypted-frame handlers ---- */
1115 :
1116 : /** Queue an unencrypted (auth_key_id = 0) server → client frame with the
1117 : * handshake response TL. Mirrors rpc_send_unencrypted on the client side
1118 : * so the client's rpc_recv_unencrypted parser picks it up. */
1119 28 : static void handshake_queue_unenc(const uint8_t *tl, size_t tl_len) {
1120 : /* Wire: auth_key_id(8) + msg_id(8) + len(4) + body */
1121 28 : TlWriter w; tl_writer_init(&w);
1122 28 : tl_write_uint64(&w, 0); /* auth_key_id */
1123 28 : tl_write_uint64(&w, make_server_msg_id()); /* server msg_id */
1124 28 : tl_write_uint32(&w, (uint32_t)tl_len);
1125 28 : tl_write_raw(&w, tl, tl_len);
1126 :
1127 28 : size_t units = w.len / 4;
1128 28 : if (units < 0x7F) {
1129 28 : uint8_t p = (uint8_t)units;
1130 28 : mock_socket_append_response(&p, 1);
1131 : } else {
1132 : uint8_t p[4];
1133 0 : p[0] = 0x7F;
1134 0 : p[1] = (uint8_t)(units & 0xFFu);
1135 0 : p[2] = (uint8_t)((units >> 8) & 0xFFu);
1136 0 : p[3] = (uint8_t)((units >> 16) & 0xFFu);
1137 0 : mock_socket_append_response(p, 4);
1138 : }
1139 28 : mock_socket_append_response(w.data, w.len);
1140 28 : tl_writer_free(&w);
1141 28 : }
1142 :
1143 : /** Handle an incoming req_pq_multi frame, emit a resPQ per the current
1144 : * cold-boot variant. Assumes @p body points at the CRC-prefixed TL body
1145 : * (CRC already consumed by the caller would break layout — we take the
1146 : * whole 4+16 body here). */
1147 18 : static void handshake_on_req_pq_multi(const uint8_t *body, size_t body_len) {
1148 18 : if (body_len < 4 + 16) return;
1149 : /* body = CRC(4) nonce(16) */
1150 : uint8_t client_nonce[16];
1151 18 : memcpy(client_nonce, body + 4, 16);
1152 :
1153 : /* Echo (possibly tampered) nonce back. */
1154 : uint8_t echo_nonce[16];
1155 18 : memcpy(echo_nonce, client_nonce, 16);
1156 18 : if (g_srv.handshake_cold_boot_variant == MT_COLD_BOOT_NONCE_TAMPER) {
1157 34 : for (int i = 0; i < 16; ++i) echo_nonce[i] ^= 0xFF;
1158 : }
1159 :
1160 : /* Deterministic server_nonce for reproducibility. */
1161 : uint8_t server_nonce[16];
1162 18 : memset(server_nonce, 0xBB, 16);
1163 :
1164 : /* PQ: default 21 (= 3 * 7 — tiny so Pollard's rho finishes instantly).
1165 : * MT_COLD_BOOT_BAD_PQ uses a 64-bit prime so pq_factorize fails. */
1166 18 : uint64_t pq_val = 21ULL;
1167 18 : if (g_srv.handshake_cold_boot_variant == MT_COLD_BOOT_BAD_PQ) {
1168 : /* 2^64 - 59 is a prime. Small enough to survive Pollard's rho
1169 : * 20-c sweep without factoring. */
1170 2 : pq_val = 0xFFFFFFFFFFFFFFC5ULL;
1171 : }
1172 : /* Encode pq as big-endian minimum-length bytes. */
1173 : uint8_t pq_be[8];
1174 18 : size_t pq_be_len = 0;
1175 162 : for (int i = 7; i >= 0; --i) {
1176 144 : uint8_t byte = (uint8_t)((pq_val >> (i * 8)) & 0xFFu);
1177 144 : if (pq_be_len > 0 || byte != 0 || i == 0) {
1178 32 : pq_be[pq_be_len++] = byte;
1179 : }
1180 : }
1181 :
1182 18 : uint64_t fingerprint = COLD_BOOT_FP_OK;
1183 18 : if (g_srv.handshake_cold_boot_variant == MT_COLD_BOOT_BAD_FINGERPRINT) {
1184 2 : fingerprint = COLD_BOOT_FP_BAD;
1185 : }
1186 :
1187 18 : uint32_t constructor = CRC_resPQ;
1188 18 : if (g_srv.handshake_cold_boot_variant == MT_COLD_BOOT_WRONG_CONSTRUCTOR) {
1189 2 : constructor = 0xDEADBEEFU;
1190 : }
1191 :
1192 18 : TlWriter tl; tl_writer_init(&tl);
1193 18 : tl_write_uint32(&tl, constructor);
1194 18 : tl_write_int128(&tl, echo_nonce);
1195 18 : tl_write_int128(&tl, server_nonce);
1196 18 : tl_write_bytes(&tl, pq_be, pq_be_len);
1197 18 : tl_write_uint32(&tl, CRC_vector);
1198 18 : tl_write_uint32(&tl, 1); /* one fingerprint entry */
1199 18 : tl_write_uint64(&tl, fingerprint);
1200 18 : handshake_queue_unenc(tl.data, tl.len);
1201 18 : tl_writer_free(&tl);
1202 : }
1203 :
1204 : /* ---- TEST-72: RSA-PAD decrypt (inverse of rsa_pad_encrypt in mtproto_auth.c) ----
1205 : *
1206 : * Reverses the RSA_PAD scheme used by the client to encrypt p_q_inner_data_dc.
1207 : * Returns 0 on success and writes the decrypted payload into @p plain_out[192].
1208 : *
1209 : * TEST_ONLY — only called from handshake_on_req_dh_params in mode 3. */
1210 2 : static int rsa_pad_decrypt(const uint8_t *ciphertext, size_t cipher_len,
1211 : uint8_t *plain_out /* must be >= 192 bytes */) {
1212 2 : if (cipher_len != 256) return -1;
1213 :
1214 : /* TEST_ONLY private key — matches tests/mocks/telegram_server_key.c pubkey. */
1215 : static const char TEST_PRIVATE_KEY_PEM[] =
1216 : "-----BEGIN PRIVATE KEY-----\n"
1217 : "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbG/j8RdvTAAWv\n"
1218 : "870ayFCbJI73fEEB43/l/NnocYeAh9L/Zcv9HwYwFOXms9o2ccvp+e/4GE55vUzY\n"
1219 : "8XrM1h6dtClGYNvRNpvcthfmVrpGGIjKH2b3snip4ajtUOcZIyTynZo1vMG5uqCx\n"
1220 : "YaXWyhBwMPJQ87Gw5WbcaKNJWg3jZ1GI0g+tJUAqXpfPwF3KjKzIZwa/rbnJ9ick\n"
1221 : "OX12OaG1c2enW1+sv1K1qz6CcxmbCyRuD+xUKVTLHc5ykmcYDJ12CLES8DdfbPOt\n"
1222 : "UGAP082CQOptfpPm5fvnU6c53suIItnddjiT74+hyQBXQpvWXKIZzFEdgxJ2QUzp\n"
1223 : "TlRwqVijAgMBAAECggEAJll6rIjrKlaRkWjOgw4u28Tksize98QTTb4/9DgJnA44\n"
1224 : "7VtyXYFrqryoAOvL0nU9SPq6SZlc4b2bf/HofjecdzphkByHjMkXLTE6ZIFh6c3M\n"
1225 : "GEk+UJSoP7xi41YC5VSqoG+1/n5OWYjajTDK63mnKc34Q2qVLtrxHSKj6JFi6Kuy\n"
1226 : "uO1+lXUiGS5vvlPllAoDTIFn4e4PHChHeVNiwzBe8HNgVWc9JasL7IiS37gsfuZC\n"
1227 : "l4QlW3IkhCMkb5fj7xV/xcwQ4SuYWcNkrfuX7Fu3g4Hpeir3xJlJMxh2iqJnARvM\n"
1228 : "nLy9gY04ejqdxI9+VopQyXafe0tP/veuqO7RzR3x+QKBgQDQvd9drWmvWM9g7Lrj\n"
1229 : "jEr4oo00b5/cOQd12VJaxhWUdfzyDxsygGPeuP3/7QxgwxB+VmnsS2xfNvv4hDdd\n"
1230 : "vL6DhFPicTV5Xv25U52QaxNefc5XSSzY68XMFtUyWjzD+35GIUO27TnDfQhFBkbS\n"
1231 : "RFDg3uJ1KbcI05nM+DSQJZPaqQKBgQC+ObQxrgZBPd6K6vVM/uaoA45Htc8UVIS/\n"
1232 : "+eU69k1lVp6BZm1ebaTOf6CTOO/AcyM4jZu7lVFwfanVMVSfJ+Nbd1BEXtg1BKou\n"
1233 : "2cLlYkJX6mVmCs39sQDscvoO67RMhR4jshCE0nxaAxFatdEfpq9ZTrQIXWKnQhRq\n"
1234 : "nHsyIyHUawKBgQCwTf5vx70AvfkB+1BqSp8z2096X2FdBsn3TqORSccGSpVm+T1W\n"
1235 : "bTxs7ECUPWn7/CVdH619R8LztKQjJcEBqh4bRNP46PdqWMHiGu51AQst/wIdlQ+M\n"
1236 : "865vj0VorvCt8yeXIhdoVHs6Ust+SSveApdxJq+Ml7whd19q0KTMrwBvaQKBgC8C\n"
1237 : "i6mLXDhbVdf24NA6Xj4/QrYuFBLuIDBhTWkY3V+h3GIWMgkYB5aQq9o2Q+nHini7\n"
1238 : "ZjUhXZLzOzlYi5UZgnJkNg3vcncHxBb38dZGRib74jspiGadi6DjeTCex1vxudUQ\n"
1239 : "eEyax+hmwa8tJ5Uu2D612IAItAypo+oE6d0mGYIpAoGAS2P6e2iOW2HG3CA2kiLp\n"
1240 : "xw/guEi8BTTljxMSUEqS5YhDI2uVe2QEYlvgDG7zYMl2UzoadZcK1VVa01WM2LDC\n"
1241 : "wThzA+NhfHS4ukPuuOkJUVwGAJGUG/KcIypTTYz4RPj6BpALLXstF+Qf8F3aqDvL\n"
1242 : "bAd3PHlfEWOhiWzBj/dVjUY=\n"
1243 : "-----END PRIVATE KEY-----\n";
1244 :
1245 2 : CryptoRsaKey *priv = crypto_rsa_load_private(TEST_PRIVATE_KEY_PEM);
1246 2 : if (!priv) {
1247 0 : fprintf(stderr, "mock: rsa_pad_decrypt: failed to load private key\n");
1248 0 : return -1;
1249 : }
1250 :
1251 : /* Step 1: RSA decrypt (NO_PADDING) → rsa_output[256] */
1252 : uint8_t rsa_output[256];
1253 2 : size_t rsa_out_len = sizeof(rsa_output);
1254 2 : if (crypto_rsa_private_decrypt(priv, ciphertext, cipher_len,
1255 : rsa_output, &rsa_out_len) != 0) {
1256 0 : crypto_rsa_free(priv);
1257 0 : fprintf(stderr, "mock: rsa_pad_decrypt: RSA decrypt failed\n");
1258 0 : return -1;
1259 : }
1260 2 : crypto_rsa_free(priv);
1261 :
1262 : /* Step 2: Split into temp_key_xor(32) + aes_encrypted(224) */
1263 2 : const uint8_t *temp_key_xor = rsa_output;
1264 2 : const uint8_t *aes_encrypted = rsa_output + 32;
1265 :
1266 : /* Step 3: SHA256(aes_encrypted) → aes_hash */
1267 : uint8_t aes_hash[32];
1268 2 : crypto_sha256(aes_encrypted, 224, aes_hash);
1269 :
1270 : /* Step 4: temp_key = temp_key_xor XOR aes_hash */
1271 : uint8_t temp_key[32];
1272 66 : for (int i = 0; i < 32; i++) temp_key[i] = temp_key_xor[i] ^ aes_hash[i];
1273 :
1274 : /* Step 5: AES-IGE decrypt(aes_encrypted, key=temp_key, IV=zeros) → data_with_hash */
1275 : uint8_t zero_iv[32];
1276 2 : memset(zero_iv, 0, 32);
1277 : uint8_t data_with_hash[224];
1278 2 : aes_ige_decrypt(aes_encrypted, 224, temp_key, zero_iv, data_with_hash);
1279 :
1280 : /* Step 6: data_with_hash = reversed(192) + hash(32)
1281 : * Verify SHA256(temp_key + reversed) == hash */
1282 : uint8_t verify_buf[32 + 192];
1283 2 : memcpy(verify_buf, temp_key, 32);
1284 2 : memcpy(verify_buf + 32, data_with_hash, 192);
1285 : uint8_t expected_hash[32];
1286 2 : crypto_sha256(verify_buf, sizeof(verify_buf), expected_hash);
1287 2 : if (memcmp(expected_hash, data_with_hash + 192, 32) != 0) {
1288 0 : fprintf(stderr, "mock: rsa_pad_decrypt: hash mismatch\n");
1289 0 : return -1;
1290 : }
1291 :
1292 : /* Step 7: un-reverse to get the original padded data */
1293 2 : const uint8_t *reversed = data_with_hash;
1294 386 : for (int i = 0; i < 192; i++) plain_out[i] = reversed[191 - i];
1295 :
1296 2 : return 0;
1297 : }
1298 :
1299 : /** Handle an incoming req_DH_params frame.
1300 : *
1301 : * Mode 2 (original): emits server_DH_params_ok with random encrypted_answer
1302 : * (garbage). The client's AES-IGE decrypt yields garbage, the inner CRC check
1303 : * fails, and auth_step_parse_dh returns -1 — the negative path.
1304 : *
1305 : * Mode 3 (TEST-72): RSA-PAD-decrypts the client's payload to extract new_nonce,
1306 : * generates a valid server_DH_inner_data (g=2, safe prime, g_a=g^b mod p),
1307 : * AES-IGE-encrypts it with the derived temp key, and sends server_DH_params_ok.
1308 : * Stores DH state for subsequent set_client_DH_params handling. */
1309 8 : static void handshake_on_req_dh_params(const uint8_t *body, size_t body_len) {
1310 14 : if (body_len < 4 + 16 + 16) return;
1311 : /* body = CRC(4) nonce(16) server_nonce(16) p:bytes q:bytes fp:long encrypted_data:bytes */
1312 : uint8_t nonce[16], server_nonce[16];
1313 8 : memcpy(nonce, body + 4, 16);
1314 8 : memcpy(server_nonce, body + 4 + 16, 16);
1315 :
1316 8 : if (g_srv.handshake_mode < 3) {
1317 : /* Original mode 2 path: random garbage encrypted_answer. */
1318 : uint8_t enc_answer[32];
1319 6 : crypto_rand_bytes(enc_answer, sizeof(enc_answer));
1320 6 : TlWriter tl; tl_writer_init(&tl);
1321 6 : tl_write_uint32(&tl, CRC_server_DH_params_ok);
1322 6 : tl_write_int128(&tl, nonce);
1323 6 : tl_write_int128(&tl, server_nonce);
1324 6 : tl_write_bytes(&tl, enc_answer, sizeof(enc_answer));
1325 6 : handshake_queue_unenc(tl.data, tl.len);
1326 6 : tl_writer_free(&tl);
1327 6 : return;
1328 : }
1329 :
1330 : /* ---- Mode 3: full DH handshake ---- */
1331 :
1332 : /* Parse the req_DH_params body to find the encrypted_data.
1333 : * Layout: CRC(4) nonce(16) server_nonce(16) p:bytes q:bytes fp:int64 encrypted_data:bytes */
1334 2 : TlReader r = tl_reader_init(body, body_len);
1335 2 : tl_read_uint32(&r); /* CRC */
1336 2 : uint8_t _nonce[16]; tl_read_int128(&r, _nonce);
1337 2 : uint8_t _snonce[16]; tl_read_int128(&r, _snonce);
1338 2 : size_t p_len = 0, q_len = 0, enc_len = 0;
1339 2 : uint8_t *p_bytes = tl_read_bytes(&r, &p_len);
1340 2 : free(p_bytes);
1341 2 : uint8_t *q_bytes = tl_read_bytes(&r, &q_len);
1342 2 : free(q_bytes);
1343 2 : tl_read_uint64(&r); /* fingerprint */
1344 2 : uint8_t *enc_data = tl_read_bytes(&r, &enc_len);
1345 2 : if (!enc_data || enc_len != 256) {
1346 0 : free(enc_data);
1347 0 : fprintf(stderr, "mock: full DH: unexpected enc_data size %zu\n", enc_len);
1348 0 : return;
1349 : }
1350 :
1351 : /* RSA-PAD decrypt to get padded p_q_inner_data_dc (192 bytes) */
1352 : uint8_t inner_plain[192];
1353 2 : if (rsa_pad_decrypt(enc_data, enc_len, inner_plain) != 0) {
1354 0 : free(enc_data);
1355 0 : fprintf(stderr, "mock: full DH: RSA_PAD decrypt failed\n");
1356 0 : return;
1357 : }
1358 2 : free(enc_data);
1359 :
1360 : /* Parse p_q_inner_data_dc from inner_plain.
1361 : * TL: CRC(4) pq:bytes p:bytes q:bytes nonce(16) server_nonce(16) new_nonce(32) dc:int */
1362 2 : TlReader ir = tl_reader_init(inner_plain, sizeof(inner_plain));
1363 2 : uint32_t inner_crc = tl_read_uint32(&ir);
1364 2 : if (inner_crc != 0xb936a01aU) { /* CRC_p_q_inner_data_dc */
1365 0 : fprintf(stderr, "mock: full DH: unexpected inner CRC 0x%08x\n", inner_crc);
1366 0 : return;
1367 : }
1368 : /* Skip pq, p, q bytes */
1369 2 : size_t tmp_len = 0;
1370 2 : uint8_t *tmp = tl_read_bytes(&ir, &tmp_len); free(tmp); /* pq */
1371 2 : tmp = tl_read_bytes(&ir, &tmp_len); free(tmp); /* p */
1372 2 : tmp = tl_read_bytes(&ir, &tmp_len); free(tmp); /* q */
1373 : /* Read nonce, server_nonce, new_nonce */
1374 : uint8_t extracted_nonce[16], extracted_snonce[16], new_nonce[32];
1375 2 : tl_read_int128(&ir, extracted_nonce);
1376 2 : tl_read_int128(&ir, extracted_snonce);
1377 2 : tl_read_int256(&ir, new_nonce);
1378 :
1379 : /* Save DH state for set_client_DH_params */
1380 2 : memcpy(g_srv.hs_new_nonce, new_nonce, 32);
1381 2 : memcpy(g_srv.hs_nonce, extracted_nonce, 16);
1382 2 : memcpy(g_srv.hs_server_nonce, extracted_snonce, 16);
1383 :
1384 : /* Use a fixed 256-bit safe prime for DH (TEST_ONLY).
1385 : * This prime was generated with openssl: BN_generate_prime_ex(p,256,1,...).
1386 : * g=2 is a generator for this group. */
1387 : static const uint8_t TEST_DH_PRIME[32] = {
1388 : 0xfa, 0xd0, 0x8e, 0x08, 0xa4, 0x4d, 0x25, 0xaa,
1389 : 0x45, 0x2b, 0xda, 0x58, 0x62, 0xac, 0xc4, 0xb2,
1390 : 0x76, 0x23, 0xd3, 0x30, 0x4d, 0xd0, 0x9d, 0x64,
1391 : 0xc1, 0xdd, 0xc0, 0xfb, 0x35, 0x09, 0x40, 0xdb
1392 : };
1393 2 : memcpy(g_srv.hs_dh_prime, TEST_DH_PRIME, 32);
1394 :
1395 : /* Generate server-side DH secret b (256 bytes) and compute g_b = 2^b mod p */
1396 2 : crypto_rand_bytes(g_srv.hs_b, 256);
1397 :
1398 2 : uint8_t g_be[1] = { 0x02 }; /* g = 2 as 1-byte big-endian */
1399 : uint8_t g_b[32];
1400 2 : size_t g_b_len = sizeof(g_b);
1401 2 : CryptoBnCtx *bn_ctx = crypto_bn_ctx_new();
1402 2 : if (!bn_ctx) { fprintf(stderr, "mock: full DH: BN ctx alloc failed\n"); return; }
1403 2 : if (crypto_bn_mod_exp(g_b, &g_b_len, g_be, 1,
1404 : g_srv.hs_b, 256,
1405 : TEST_DH_PRIME, 32, bn_ctx) != 0) {
1406 0 : crypto_bn_ctx_free(bn_ctx);
1407 0 : fprintf(stderr, "mock: full DH: g_b computation failed\n");
1408 0 : return;
1409 : }
1410 2 : crypto_bn_ctx_free(bn_ctx);
1411 :
1412 : /* Derive temp AES key/IV from new_nonce + server_nonce (same as production) */
1413 : uint8_t tmp_aes_key[32], tmp_aes_iv[32];
1414 : {
1415 : uint8_t buf[64];
1416 : uint8_t sha1_a[20], sha1_b[20], sha1_c[20];
1417 :
1418 2 : memcpy(buf, new_nonce, 32); memcpy(buf + 32, extracted_snonce, 16);
1419 2 : crypto_sha1(buf, 48, sha1_a);
1420 2 : memcpy(buf, extracted_snonce, 16); memcpy(buf + 16, new_nonce, 32);
1421 2 : crypto_sha1(buf, 48, sha1_b);
1422 2 : memcpy(buf, new_nonce, 32); memcpy(buf + 32, new_nonce, 32);
1423 2 : crypto_sha1(buf, 64, sha1_c);
1424 :
1425 2 : memcpy(tmp_aes_key, sha1_a, 20);
1426 2 : memcpy(tmp_aes_key + 20, sha1_b, 12);
1427 2 : memcpy(tmp_aes_iv, sha1_b + 12, 8);
1428 2 : memcpy(tmp_aes_iv + 8, sha1_c, 20);
1429 2 : memcpy(tmp_aes_iv + 28, new_nonce, 4);
1430 : }
1431 :
1432 : /* Build server_DH_inner_data TL */
1433 2 : TlWriter inner; tl_writer_init(&inner);
1434 2 : tl_write_uint32(&inner, CRC_server_DH_inner_data);
1435 2 : tl_write_int128(&inner, extracted_nonce);
1436 2 : tl_write_int128(&inner, extracted_snonce);
1437 2 : tl_write_int32(&inner, 2); /* g = 2 */
1438 2 : tl_write_bytes(&inner, TEST_DH_PRIME, 32); /* dh_prime */
1439 2 : tl_write_bytes(&inner, g_b, g_b_len); /* g_a (server's g^b) */
1440 2 : tl_write_int32(&inner, (int32_t)time(NULL)); /* server_time */
1441 :
1442 : /* Prepend SHA1 + pad to 16-byte boundary */
1443 : uint8_t sha1_inner[20];
1444 2 : crypto_sha1(inner.data, inner.len, sha1_inner);
1445 2 : size_t raw_len = 20 + inner.len;
1446 2 : size_t padded_len = (raw_len + 15) & ~(size_t)15;
1447 2 : uint8_t *padded = (uint8_t *)calloc(1, padded_len);
1448 2 : if (!padded) { tl_writer_free(&inner); return; }
1449 2 : memcpy(padded, sha1_inner, 20);
1450 2 : memcpy(padded + 20, inner.data, inner.len);
1451 2 : if (padded_len > raw_len) {
1452 2 : crypto_rand_bytes(padded + raw_len, padded_len - raw_len);
1453 : }
1454 2 : tl_writer_free(&inner);
1455 :
1456 : /* AES-IGE encrypt */
1457 2 : uint8_t *enc_answer = (uint8_t *)malloc(padded_len);
1458 2 : if (!enc_answer) { free(padded); return; }
1459 2 : aes_ige_encrypt(padded, padded_len, tmp_aes_key, tmp_aes_iv, enc_answer);
1460 2 : free(padded);
1461 :
1462 : /* Send server_DH_params_ok */
1463 2 : TlWriter tl; tl_writer_init(&tl);
1464 2 : tl_write_uint32(&tl, CRC_server_DH_params_ok);
1465 2 : tl_write_int128(&tl, extracted_nonce);
1466 2 : tl_write_int128(&tl, extracted_snonce);
1467 2 : tl_write_bytes(&tl, enc_answer, padded_len);
1468 2 : handshake_queue_unenc(tl.data, tl.len);
1469 2 : tl_writer_free(&tl);
1470 2 : free(enc_answer);
1471 : }
1472 :
1473 : /** TEST-72: Handle set_client_DH_params, compute auth_key = g_b^a mod p,
1474 : * verify key_hash, send dh_gen_ok, and persist the session. */
1475 2 : static void handshake_on_set_client_dh(const uint8_t *body, size_t body_len) {
1476 2 : if (body_len < 4 + 16 + 16) return;
1477 :
1478 : /* Derive temp AES key/IV from stored new_nonce + server_nonce */
1479 : uint8_t tmp_aes_key[32], tmp_aes_iv[32];
1480 : {
1481 : uint8_t buf[64];
1482 : uint8_t sha1_a[20], sha1_b[20], sha1_c[20];
1483 :
1484 2 : memcpy(buf, g_srv.hs_new_nonce, 32);
1485 2 : memcpy(buf + 32, g_srv.hs_server_nonce, 16);
1486 2 : crypto_sha1(buf, 48, sha1_a);
1487 :
1488 2 : memcpy(buf, g_srv.hs_server_nonce, 16);
1489 2 : memcpy(buf + 16, g_srv.hs_new_nonce, 32);
1490 2 : crypto_sha1(buf, 48, sha1_b);
1491 :
1492 2 : memcpy(buf, g_srv.hs_new_nonce, 32);
1493 2 : memcpy(buf + 32, g_srv.hs_new_nonce, 32);
1494 2 : crypto_sha1(buf, 64, sha1_c);
1495 :
1496 2 : memcpy(tmp_aes_key, sha1_a, 20);
1497 2 : memcpy(tmp_aes_key + 20, sha1_b, 12);
1498 2 : memcpy(tmp_aes_iv, sha1_b + 12, 8);
1499 2 : memcpy(tmp_aes_iv + 8, sha1_c, 20);
1500 2 : memcpy(tmp_aes_iv + 28, g_srv.hs_new_nonce, 4);
1501 : }
1502 :
1503 : /* Parse set_client_DH_params body:
1504 : * CRC(4) nonce(16) server_nonce(16) encrypted_data:bytes */
1505 2 : TlReader r = tl_reader_init(body, body_len);
1506 2 : tl_read_uint32(&r); /* CRC */
1507 2 : uint8_t _nonce[16]; tl_read_int128(&r, _nonce);
1508 2 : uint8_t _snonce[16]; tl_read_int128(&r, _snonce);
1509 2 : size_t enc_len = 0;
1510 2 : uint8_t *enc_data = tl_read_bytes(&r, &enc_len);
1511 2 : if (!enc_data || enc_len == 0 || enc_len % 16 != 0) {
1512 0 : free(enc_data);
1513 0 : fprintf(stderr, "mock: set_client_DH: bad encrypted_data len %zu\n", enc_len);
1514 0 : return;
1515 : }
1516 :
1517 : /* AES-IGE decrypt encrypted_data */
1518 2 : uint8_t *plain = (uint8_t *)malloc(enc_len);
1519 2 : if (!plain) { free(enc_data); return; }
1520 2 : aes_ige_decrypt(enc_data, enc_len, tmp_aes_key, tmp_aes_iv, plain);
1521 2 : free(enc_data);
1522 :
1523 : /* Parse client_DH_inner_data: SHA1(20) + CRC(4) nonce(16) server_nonce(16)
1524 : * retry_id:int64 g_b:bytes */
1525 2 : if (enc_len < 20 + 4 + 16 + 16 + 8) {
1526 0 : free(plain);
1527 0 : fprintf(stderr, "mock: set_client_DH: plaintext too short\n");
1528 0 : return;
1529 : }
1530 2 : TlReader ir = tl_reader_init(plain + 20, enc_len - 20);
1531 2 : uint32_t crc = tl_read_uint32(&ir);
1532 2 : if (crc != 0x6643b654U) { /* CRC_client_DH_inner_data */
1533 0 : free(plain);
1534 0 : fprintf(stderr, "mock: set_client_DH: wrong CRC 0x%08x\n", crc);
1535 0 : return;
1536 : }
1537 2 : uint8_t cli_nonce[16]; tl_read_int128(&ir, cli_nonce);
1538 2 : uint8_t cli_snonce[16]; tl_read_int128(&ir, cli_snonce);
1539 2 : tl_read_uint64(&ir); /* retry_id */
1540 2 : size_t g_b_len = 0;
1541 2 : uint8_t *g_b = tl_read_bytes(&ir, &g_b_len); /* client's g^client_b mod p */
1542 2 : if (!g_b || g_b_len == 0) {
1543 0 : free(g_b); free(plain);
1544 0 : fprintf(stderr, "mock: set_client_DH: failed to read g_b\n");
1545 0 : return;
1546 : }
1547 2 : free(plain);
1548 :
1549 : /* Save g_a (client's g^client_b mod p) for auth_key computation */
1550 2 : if (g_b_len > sizeof(g_srv.hs_g_a)) {
1551 0 : free(g_b);
1552 0 : fprintf(stderr, "mock: set_client_DH: g_b too large (%zu)\n", g_b_len);
1553 0 : return;
1554 : }
1555 2 : memcpy(g_srv.hs_g_a, g_b, g_b_len);
1556 2 : g_srv.hs_g_a_len = g_b_len;
1557 2 : free(g_b);
1558 :
1559 : /* Compute auth_key = g_b^server_b mod dh_prime */
1560 : uint8_t auth_key[256];
1561 2 : size_t ak_len = sizeof(auth_key);
1562 2 : CryptoBnCtx *bn_ctx = crypto_bn_ctx_new();
1563 2 : if (!bn_ctx) { fprintf(stderr, "mock: set_client_DH: BN ctx alloc\n"); return; }
1564 2 : if (crypto_bn_mod_exp(auth_key, &ak_len,
1565 : g_srv.hs_g_a, g_srv.hs_g_a_len,
1566 : g_srv.hs_b, 256,
1567 : g_srv.hs_dh_prime, 32, bn_ctx) != 0) {
1568 0 : crypto_bn_ctx_free(bn_ctx);
1569 0 : fprintf(stderr, "mock: set_client_DH: auth_key exp failed\n");
1570 0 : return;
1571 : }
1572 2 : crypto_bn_ctx_free(bn_ctx);
1573 :
1574 : /* Pad auth_key to 256 bytes */
1575 : uint8_t auth_key_padded[256];
1576 2 : memset(auth_key_padded, 0, 256);
1577 2 : if (ak_len <= 256) {
1578 2 : memcpy(auth_key_padded + (256 - ak_len), auth_key, ak_len);
1579 : }
1580 :
1581 : /* Compute new_nonce_hash1 = last 16 bytes of SHA1(new_nonce||0x01||ak_aux_hash[8])
1582 : * where ak_aux_hash = SHA1(auth_key)[0:8] */
1583 : uint8_t ak_full_hash[20];
1584 2 : crypto_sha1(auth_key_padded, 256, ak_full_hash);
1585 :
1586 : uint8_t nnh_input[32 + 1 + 8];
1587 2 : memcpy(nnh_input, g_srv.hs_new_nonce, 32);
1588 2 : nnh_input[32] = 0x01;
1589 2 : memcpy(nnh_input + 33, ak_full_hash, 8);
1590 :
1591 : uint8_t nnh_full[20];
1592 2 : crypto_sha1(nnh_input, sizeof(nnh_input), nnh_full);
1593 : /* last 16 bytes = nnh_full[4:20] */
1594 : uint8_t new_nonce_hash1[16];
1595 2 : memcpy(new_nonce_hash1, nnh_full + 4, 16);
1596 :
1597 : /* Save auth_key to mock server state so encrypted frames work after handshake */
1598 2 : memcpy(g_srv.auth_key, auth_key_padded, 256);
1599 2 : g_srv.auth_key_id = derive_auth_key_id(auth_key_padded);
1600 : /* salt = new_nonce[0:8] XOR server_nonce[0:8] */
1601 2 : g_srv.server_salt = 0;
1602 18 : for (int i = 0; i < 8; i++) {
1603 16 : ((uint8_t *)&g_srv.server_salt)[i] =
1604 16 : g_srv.hs_new_nonce[i] ^ g_srv.hs_server_nonce[i];
1605 : }
1606 2 : g_srv.session_id = 0xAABBCCDD11223344ULL;
1607 :
1608 : /* Persist session so the client can verify it was written */
1609 : MtProtoSession sess;
1610 2 : mtproto_session_init(&sess);
1611 2 : mtproto_session_set_auth_key(&sess, auth_key_padded);
1612 2 : mtproto_session_set_salt(&sess, g_srv.server_salt);
1613 2 : sess.session_id = g_srv.session_id;
1614 2 : session_store_save(&sess, 2); /* dc_id=2 matches the test */
1615 :
1616 : /* Send dh_gen_ok */
1617 2 : TlWriter tl; tl_writer_init(&tl);
1618 2 : tl_write_uint32(&tl, CRC_dh_gen_ok);
1619 2 : tl_write_int128(&tl, g_srv.hs_nonce);
1620 2 : tl_write_int128(&tl, g_srv.hs_server_nonce);
1621 2 : tl_write_int128(&tl, new_nonce_hash1);
1622 2 : handshake_queue_unenc(tl.data, tl.len);
1623 2 : tl_writer_free(&tl);
1624 : }
1625 :
1626 654 : static void encrypt_and_queue(const uint8_t *plain, size_t plain_len) {
1627 654 : uint8_t *enc = (uint8_t *)malloc(plain_len + 1024);
1628 654 : if (!enc) return;
1629 654 : size_t enc_len = 0;
1630 : uint8_t msg_key[16];
1631 654 : mtproto_encrypt(plain, plain_len, g_srv.auth_key, 8, enc, &enc_len, msg_key);
1632 :
1633 654 : size_t wire_len = 8 + 16 + enc_len;
1634 654 : uint8_t *wire = (uint8_t *)malloc(wire_len);
1635 654 : if (!wire) { free(enc); return; }
1636 5886 : for (int i = 0; i < 8; ++i) {
1637 5232 : wire[i] = (uint8_t)((g_srv.auth_key_id >> (i * 8)) & 0xFFu);
1638 : }
1639 654 : memcpy(wire + 8, msg_key, 16);
1640 654 : memcpy(wire + 24, enc, enc_len);
1641 654 : free(enc);
1642 :
1643 : /* Abridged length prefix (in 4-byte units). */
1644 654 : size_t units = wire_len / 4;
1645 654 : if (units < 0x7F) {
1646 606 : uint8_t p = (uint8_t)units;
1647 606 : mock_socket_append_response(&p, 1);
1648 : } else {
1649 : uint8_t p[4];
1650 48 : p[0] = 0x7F;
1651 48 : p[1] = (uint8_t)(units & 0xFFu);
1652 48 : p[2] = (uint8_t)((units >> 8) & 0xFFu);
1653 48 : p[3] = (uint8_t)((units >> 16) & 0xFFu);
1654 48 : mock_socket_append_response(p, 4);
1655 : }
1656 654 : mock_socket_append_response(wire, wire_len);
1657 654 : free(wire);
1658 : }
|