Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file mtproto_auth.c
6 : * @brief MTProto DH auth key generation.
7 : *
8 : * Implements the 8-step DH key exchange to generate an auth_key.
9 : * Uses: PQ factorization (Pollard's rho), RSA_PAD, AES-IGE, SHA-1/SHA-256.
10 : */
11 :
12 : #include "mtproto_auth.h"
13 : #include "crypto.h"
14 : #include "ige_aes.h"
15 : #include "mtproto_rpc.h"
16 : #include "tl_serial.h"
17 : #include "telegram_server_key.h"
18 : #include "logger.h"
19 : #include "raii.h"
20 :
21 : #include <stdlib.h>
22 : #include <string.h>
23 :
24 : /* ---- TL Constructor IDs ---- */
25 : #define CRC_req_pq_multi 0xbe7e8ef1
26 : #define CRC_resPQ 0x05162463
27 : #define CRC_p_q_inner_data_dc 0xb936a01a
28 : #define CRC_req_DH_params 0xd712e4be
29 : #define CRC_server_DH_params_ok 0xd0e8075c
30 : #define CRC_server_DH_inner_data 0xb5890dba
31 : #define CRC_client_DH_inner_data 0x6643b654
32 : #define CRC_set_client_DH_params 0xf5045f1f
33 : #define CRC_dh_gen_ok 0x3bcbf734
34 : #define CRC_dh_gen_retry 0x46dc1fb9
35 : #define CRC_dh_gen_fail 0xa69dae02
36 :
37 : /* Maximum number of RSA fingerprints accepted in a ResPQ vector.
38 : * Telegram uses ≤8 in practice; cap at 64 to reject DoS from untrusted servers
39 : * during the unauthenticated DH phase. */
40 : #define MAX_FP_COUNT 64
41 :
42 : /* ---- Big-endian byte helpers ---- */
43 :
44 : /** Encode uint64 as big-endian bytes, stripping leading zeros. */
45 4 : static size_t uint64_to_be(uint64_t val, uint8_t *out) {
46 : uint8_t tmp[8];
47 36 : for (int i = 7; i >= 0; i--) {
48 32 : tmp[i] = (uint8_t)(val & 0xFF);
49 32 : val >>= 8;
50 : }
51 : /* Skip leading zeros */
52 4 : size_t start = 0;
53 32 : while (start < 7 && tmp[start] == 0) start++;
54 4 : size_t len = 8 - start;
55 4 : memcpy(out, tmp + start, len);
56 4 : return len;
57 : }
58 :
59 : /** Encode uint32 as big-endian bytes, stripping leading zeros. */
60 9 : static size_t uint32_to_be(uint32_t val, uint8_t *out) {
61 : uint8_t tmp[4];
62 9 : tmp[0] = (uint8_t)((val >> 24) & 0xFF);
63 9 : tmp[1] = (uint8_t)((val >> 16) & 0xFF);
64 9 : tmp[2] = (uint8_t)((val >> 8) & 0xFF);
65 9 : tmp[3] = (uint8_t)( val & 0xFF);
66 9 : size_t start = 0;
67 36 : while (start < 3 && tmp[start] == 0) start++;
68 9 : size_t len = 4 - start;
69 9 : memcpy(out, tmp + start, len);
70 9 : return len;
71 : }
72 :
73 : /** Decode big-endian bytes to uint64. */
74 7 : static uint64_t be_to_uint64(const uint8_t *data, size_t len) {
75 7 : uint64_t val = 0;
76 21 : for (size_t i = 0; i < len; i++) {
77 14 : val = (val << 8) | data[i];
78 : }
79 7 : return val;
80 : }
81 :
82 : /* ---- RSA_PAD (OAEP variant for Telegram) ---- */
83 :
84 4 : static int rsa_pad_encrypt(CryptoRsaKey *rsa_key,
85 : const uint8_t *data, size_t data_len,
86 : uint8_t *out, size_t *out_len) {
87 4 : if (!rsa_key || !data || !out || !out_len) return -1;
88 4 : if (data_len > 144) return -1;
89 :
90 : /* Step 1: Pad data to 192 bytes with random padding */
91 : uint8_t padded[192];
92 4 : memset(padded, 0, sizeof(padded));
93 4 : memcpy(padded, data, data_len);
94 4 : if (data_len < 192) {
95 4 : crypto_rand_bytes(padded + data_len, 192 - data_len);
96 : }
97 :
98 : /* Step 2: Reverse */
99 : uint8_t reversed[192];
100 772 : for (int i = 0; i < 192; i++) {
101 768 : reversed[i] = padded[191 - i];
102 : }
103 :
104 : /* The resulting `rsa_input` must be numerically < RSA modulus to be
105 : * a valid NO_PADDING input. Since temp_key (and hence the MSB of the
106 : * 256-byte buffer) is random, ~half of calls would hit >= modulus
107 : * and EVP_PKEY_encrypt would reject them. TDLib retries with fresh
108 : * temp_key until the result is valid — mirror that here. Bounded to
109 : * 32 attempts to guarantee forward progress in pathological cases
110 : * (e.g. a mock key with a very low-high-bit modulus). */
111 6 : for (int attempt = 0; attempt < 32; ++attempt) {
112 : /* Step 3: Random temp_key (32 bytes) */
113 : uint8_t temp_key[32];
114 6 : crypto_rand_bytes(temp_key, 32);
115 :
116 : /* Step 4: data_with_hash = reversed + SHA256(temp_key + reversed) */
117 : uint8_t temp_key_and_reversed[32 + 192];
118 6 : memcpy(temp_key_and_reversed, temp_key, 32);
119 6 : memcpy(temp_key_and_reversed + 32, reversed, 192);
120 :
121 : uint8_t hash[32];
122 6 : crypto_sha256(temp_key_and_reversed, sizeof(temp_key_and_reversed), hash);
123 :
124 : uint8_t data_with_hash[224]; /* 192 + 32 */
125 6 : memcpy(data_with_hash, reversed, 192);
126 6 : memcpy(data_with_hash + 192, hash, 32);
127 :
128 : /* Step 5: AES-256-IGE encrypt data_with_hash with temp_key, zero IV */
129 : uint8_t zero_iv[32];
130 6 : memset(zero_iv, 0, 32);
131 :
132 : uint8_t aes_encrypted[224];
133 6 : aes_ige_encrypt(data_with_hash, 224, temp_key, zero_iv, aes_encrypted);
134 :
135 : /* Step 6: temp_key_xor = temp_key XOR SHA256(aes_encrypted) */
136 : uint8_t aes_hash[32];
137 6 : crypto_sha256(aes_encrypted, 224, aes_hash);
138 :
139 : uint8_t temp_key_xor[32];
140 198 : for (int i = 0; i < 32; i++) {
141 192 : temp_key_xor[i] = temp_key[i] ^ aes_hash[i];
142 : }
143 :
144 : /* Step 7: RSA(temp_key_xor + aes_encrypted) = 32 + 224 = 256 bytes */
145 : uint8_t rsa_input[256];
146 6 : memcpy(rsa_input, temp_key_xor, 32);
147 6 : memcpy(rsa_input + 32, aes_encrypted, 224);
148 :
149 6 : if (crypto_rsa_public_encrypt(rsa_key, rsa_input, 256,
150 : out, out_len) == 0) {
151 4 : return 0;
152 : }
153 : /* Retry: RSA_NO_PADDING rejects inputs >= modulus, which
154 : * happens for roughly half of random 256-byte buffers. */
155 : }
156 0 : logger_log(LOG_ERROR,
157 : "auth: rsa_pad_encrypt exhausted 32 retries — RSA key anomaly");
158 0 : return -1;
159 : }
160 :
161 : /* ---- DH temp key derivation ---- */
162 :
163 3 : static void dh_derive_temp_aes(const uint8_t new_nonce[32],
164 : const uint8_t server_nonce[16],
165 : uint8_t *tmp_aes_key, /* 32 bytes */
166 : uint8_t *tmp_aes_iv) /* 32 bytes */
167 : {
168 : uint8_t buf[64];
169 :
170 : /* SHA1(new_nonce + server_nonce) */
171 3 : memcpy(buf, new_nonce, 32);
172 3 : memcpy(buf + 32, server_nonce, 16);
173 : uint8_t sha1_a[20];
174 3 : crypto_sha1(buf, 48, sha1_a);
175 :
176 : /* SHA1(server_nonce + new_nonce) */
177 3 : memcpy(buf, server_nonce, 16);
178 3 : memcpy(buf + 16, new_nonce, 32);
179 : uint8_t sha1_b[20];
180 3 : crypto_sha1(buf, 48, sha1_b);
181 :
182 : /* tmp_aes_key = sha1_a(20) + sha1_b[0:12] = 32 bytes */
183 3 : memcpy(tmp_aes_key, sha1_a, 20);
184 3 : memcpy(tmp_aes_key + 20, sha1_b, 12);
185 :
186 : /* tmp_aes_iv = sha1_b[12:8] + SHA1(new_nonce+new_nonce) + new_nonce[0:4] */
187 : uint8_t sha1_c[20];
188 3 : memcpy(buf, new_nonce, 32);
189 3 : memcpy(buf + 32, new_nonce, 32);
190 3 : crypto_sha1(buf, 64, sha1_c);
191 :
192 3 : memcpy(tmp_aes_iv, sha1_b + 12, 8);
193 3 : memcpy(tmp_aes_iv + 8, sha1_c, 20);
194 3 : memcpy(tmp_aes_iv + 28, new_nonce, 4);
195 3 : }
196 :
197 : /* ---- PQ Factorization (Pollard's rho) ---- */
198 :
199 5 : int pq_factorize(uint64_t pq, uint32_t *p_out, uint32_t *q_out) {
200 5 : if (pq < 2 || !p_out || !q_out) return -1;
201 :
202 : /* Try small primes first for quick factorization */
203 5 : if (pq % 2 == 0) {
204 0 : *p_out = 2;
205 0 : *q_out = (uint32_t)(pq / 2);
206 0 : if (*p_out > *q_out) {
207 0 : uint32_t tmp = *p_out; *p_out = *q_out; *q_out = tmp;
208 : }
209 0 : return 0;
210 : }
211 :
212 : /* Pollard's rho algorithm with multiple attempts.
213 : * Uses __uint128_t to avoid overflow in (x*x) for 64-bit pq. */
214 28 : for (uint64_t c = 1; c < 20; c++) {
215 27 : uint64_t x = 2, y = 2, d = 1;
216 27 : int steps = 0;
217 :
218 19000035 : while (d == 1 && steps < 1000000) {
219 19000012 : x = (uint64_t)(((__uint128_t)x * x + c) % pq);
220 19000012 : y = (uint64_t)(((__uint128_t)y * y + c) % pq);
221 19000012 : y = (uint64_t)(((__uint128_t)y * y + c) % pq);
222 19000012 : steps++;
223 :
224 : /* GCD(|x-y|, pq) */
225 19000012 : uint64_t a = x > y ? x - y : y - x;
226 19000012 : if (a == 0) break; /* x == y, try different c */
227 19000008 : uint64_t b = pq;
228 752455376 : while (b != 0) {
229 733455368 : uint64_t t = b;
230 733455368 : b = a % b;
231 733455368 : a = t;
232 : }
233 19000008 : d = a;
234 : }
235 :
236 27 : if (d != 1 && d != pq) {
237 4 : uint64_t p = d;
238 4 : uint64_t q = pq / d;
239 4 : if (p > q) { uint64_t tmp = p; p = q; q = tmp; }
240 4 : if (p > UINT32_MAX || q > UINT32_MAX) {
241 0 : logger_log(LOG_ERROR,
242 : "pq_factorize: factor exceeds 2^32 (p=%llu q=%llu) — "
243 : "server input is invalid",
244 : (unsigned long long)p, (unsigned long long)q);
245 0 : return -1;
246 : }
247 4 : *p_out = (uint32_t)p;
248 4 : *q_out = (uint32_t)q;
249 4 : return 0;
250 : }
251 : }
252 :
253 1 : return -1;
254 : }
255 :
256 : /* ---- Step 1: req_pq_multi → ResPQ ---- */
257 :
258 10 : int auth_step_req_pq(AuthKeyCtx *ctx) {
259 10 : if (!ctx || !ctx->transport || !ctx->session) return -1;
260 :
261 : /* Generate random nonce */
262 10 : crypto_rand_bytes(ctx->nonce, 16);
263 :
264 : /* Build req_pq_multi TL */
265 : TlWriter w;
266 10 : tl_writer_init(&w);
267 10 : tl_write_uint32(&w, CRC_req_pq_multi);
268 10 : tl_write_int128(&w, ctx->nonce);
269 :
270 10 : int rc = rpc_send_unencrypted(ctx->session, ctx->transport, w.data, w.len);
271 10 : tl_writer_free(&w);
272 10 : if (rc != 0) {
273 0 : logger_log(LOG_ERROR, "auth: failed to send req_pq_multi");
274 0 : return -1;
275 : }
276 :
277 : /* Receive ResPQ */
278 : uint8_t buf[4096];
279 10 : size_t buf_len = 0;
280 10 : rc = rpc_recv_unencrypted(ctx->session, ctx->transport, buf, sizeof(buf), &buf_len);
281 10 : if (rc != 0) {
282 1 : logger_log(LOG_ERROR, "auth: failed to receive ResPQ");
283 1 : return -1;
284 : }
285 :
286 : /* Parse ResPQ */
287 9 : TlReader r = tl_reader_init(buf, buf_len);
288 :
289 9 : uint32_t constructor = tl_read_uint32(&r);
290 9 : if (constructor != CRC_resPQ) {
291 1 : logger_log(LOG_ERROR, "auth: unexpected constructor 0x%08x", constructor);
292 1 : return -1;
293 : }
294 :
295 : /* Verify nonce */
296 : uint8_t recv_nonce[16];
297 8 : tl_read_int128(&r, recv_nonce);
298 8 : if (memcmp(recv_nonce, ctx->nonce, 16) != 0) {
299 1 : logger_log(LOG_ERROR, "auth: nonce mismatch in ResPQ");
300 1 : return -1;
301 : }
302 :
303 : /* Server nonce */
304 7 : tl_read_int128(&r, ctx->server_nonce);
305 :
306 : /* PQ as bytes (big-endian) */
307 7 : size_t pq_len = 0;
308 14 : RAII_STRING uint8_t *pq_bytes = tl_read_bytes(&r, &pq_len);
309 7 : if (!pq_bytes) {
310 0 : logger_log(LOG_ERROR, "auth: failed to read pq bytes");
311 0 : return -1;
312 : }
313 7 : ctx->pq = be_to_uint64(pq_bytes, pq_len);
314 : /* pq_bytes freed automatically by RAII_STRING */
315 :
316 : /* Vector of fingerprints */
317 7 : uint32_t vec_crc = tl_read_uint32(&r); /* vector constructor */
318 : (void)vec_crc;
319 7 : uint32_t fp_count = tl_read_uint32(&r);
320 :
321 7 : if (fp_count > MAX_FP_COUNT) {
322 0 : logger_log(LOG_ERROR, "auth: fp_count %u exceeds cap %u — rejecting",
323 : fp_count, MAX_FP_COUNT);
324 0 : return -1;
325 : }
326 :
327 7 : int found_fp = 0;
328 14 : for (uint32_t i = 0; i < fp_count; i++) {
329 7 : uint64_t fp = tl_read_uint64(&r);
330 7 : if (fp == TELEGRAM_RSA_FINGERPRINT) {
331 6 : found_fp = 1;
332 : }
333 : }
334 :
335 7 : if (!found_fp) {
336 1 : logger_log(LOG_ERROR, "auth: no matching RSA fingerprint");
337 1 : return -1;
338 : }
339 :
340 6 : logger_log(LOG_INFO, "auth: ResPQ received, pq=%llu",
341 6 : (unsigned long long)ctx->pq);
342 6 : return 0;
343 : }
344 :
345 : /* ---- Step 2: req_DH_params ---- */
346 :
347 5 : int auth_step_req_dh(AuthKeyCtx *ctx) {
348 5 : if (!ctx || !ctx->transport || !ctx->session) return -1;
349 :
350 : /* Factorize PQ */
351 5 : if (pq_factorize(ctx->pq, &ctx->p, &ctx->q) != 0) {
352 1 : logger_log(LOG_ERROR, "auth: PQ factorization failed");
353 1 : return -1;
354 : }
355 :
356 : /* Generate new_nonce */
357 4 : crypto_rand_bytes(ctx->new_nonce, 32);
358 :
359 : /* Encode p, q, pq as big-endian bytes */
360 : uint8_t pq_be[8];
361 4 : size_t pq_be_len = uint64_to_be(ctx->pq, pq_be);
362 : uint8_t p_be[4];
363 4 : size_t p_be_len = uint32_to_be(ctx->p, p_be);
364 : uint8_t q_be[4];
365 4 : size_t q_be_len = uint32_to_be(ctx->q, q_be);
366 :
367 : /* Build p_q_inner_data_dc */
368 : TlWriter inner;
369 4 : tl_writer_init(&inner);
370 4 : tl_write_uint32(&inner, CRC_p_q_inner_data_dc);
371 4 : tl_write_bytes(&inner, pq_be, pq_be_len);
372 4 : tl_write_bytes(&inner, p_be, p_be_len);
373 4 : tl_write_bytes(&inner, q_be, q_be_len);
374 4 : tl_write_int128(&inner, ctx->nonce);
375 4 : tl_write_int128(&inner, ctx->server_nonce);
376 4 : tl_write_int256(&inner, ctx->new_nonce);
377 4 : tl_write_int32(&inner, ctx->dc_id);
378 :
379 : /* RSA_PAD encrypt */
380 4 : CryptoRsaKey *rsa_key = crypto_rsa_load_public(TELEGRAM_RSA_PEM);
381 4 : if (!rsa_key) {
382 0 : tl_writer_free(&inner);
383 0 : logger_log(LOG_ERROR, "auth: failed to load RSA key");
384 0 : return -1;
385 : }
386 :
387 : uint8_t encrypted[256];
388 4 : size_t enc_len = 0;
389 4 : int rc = rsa_pad_encrypt(rsa_key, inner.data, inner.len, encrypted, &enc_len);
390 4 : crypto_rsa_free(rsa_key);
391 4 : tl_writer_free(&inner);
392 :
393 4 : if (rc != 0) {
394 0 : logger_log(LOG_ERROR, "auth: RSA_PAD encrypt failed");
395 0 : return -1;
396 : }
397 :
398 : /* Build req_DH_params */
399 : TlWriter w;
400 4 : tl_writer_init(&w);
401 4 : tl_write_uint32(&w, CRC_req_DH_params);
402 4 : tl_write_int128(&w, ctx->nonce);
403 4 : tl_write_int128(&w, ctx->server_nonce);
404 4 : tl_write_bytes(&w, p_be, p_be_len);
405 4 : tl_write_bytes(&w, q_be, q_be_len);
406 4 : tl_write_uint64(&w, TELEGRAM_RSA_FINGERPRINT);
407 4 : tl_write_bytes(&w, encrypted, enc_len);
408 :
409 4 : rc = rpc_send_unencrypted(ctx->session, ctx->transport, w.data, w.len);
410 4 : tl_writer_free(&w);
411 :
412 4 : if (rc != 0) {
413 0 : logger_log(LOG_ERROR, "auth: failed to send req_DH_params");
414 0 : return -1;
415 : }
416 :
417 4 : logger_log(LOG_INFO, "auth: req_DH_params sent, p=%u q=%u", ctx->p, ctx->q);
418 4 : return 0;
419 : }
420 :
421 : /* ---- Step 3: Parse server_DH_params_ok ---- */
422 :
423 3 : int auth_step_parse_dh(AuthKeyCtx *ctx) {
424 3 : if (!ctx || !ctx->transport || !ctx->session) return -1;
425 :
426 : /* Receive server_DH_params */
427 : uint8_t buf[4096];
428 3 : size_t buf_len = 0;
429 3 : int rc = rpc_recv_unencrypted(ctx->session, ctx->transport,
430 : buf, sizeof(buf), &buf_len);
431 3 : if (rc != 0) {
432 0 : logger_log(LOG_ERROR, "auth: failed to receive server_DH_params");
433 0 : return -1;
434 : }
435 :
436 3 : TlReader r = tl_reader_init(buf, buf_len);
437 :
438 3 : uint32_t constructor = tl_read_uint32(&r);
439 3 : if (constructor != CRC_server_DH_params_ok) {
440 0 : logger_log(LOG_ERROR, "auth: unexpected constructor 0x%08x", constructor);
441 0 : return -1;
442 : }
443 :
444 : /* Verify nonces */
445 : uint8_t recv_nonce[16];
446 3 : tl_read_int128(&r, recv_nonce);
447 3 : if (memcmp(recv_nonce, ctx->nonce, 16) != 0) {
448 0 : logger_log(LOG_ERROR, "auth: nonce mismatch in server_DH_params");
449 0 : return -1;
450 : }
451 :
452 : uint8_t recv_server_nonce[16];
453 3 : tl_read_int128(&r, recv_server_nonce);
454 3 : if (memcmp(recv_server_nonce, ctx->server_nonce, 16) != 0) {
455 0 : logger_log(LOG_ERROR, "auth: server_nonce mismatch");
456 0 : return -1;
457 : }
458 :
459 : /* Read encrypted_answer */
460 3 : size_t enc_answer_len = 0;
461 6 : RAII_STRING uint8_t *enc_answer = tl_read_bytes(&r, &enc_answer_len);
462 3 : if (!enc_answer || enc_answer_len == 0) {
463 0 : logger_log(LOG_ERROR, "auth: failed to read encrypted_answer");
464 0 : return -1;
465 : }
466 :
467 : /* Derive temp AES key/IV */
468 3 : dh_derive_temp_aes(ctx->new_nonce, ctx->server_nonce,
469 3 : ctx->tmp_aes_key, ctx->tmp_aes_iv);
470 :
471 : /* Decrypt answer */
472 3 : RAII_STRING uint8_t *decrypted = (uint8_t *)malloc(enc_answer_len);
473 3 : if (!decrypted) return -1;
474 3 : aes_ige_decrypt(enc_answer, enc_answer_len,
475 3 : ctx->tmp_aes_key, ctx->tmp_aes_iv, decrypted);
476 : /* enc_answer freed automatically by RAII_STRING */
477 :
478 : /* Parse decrypted: skip 20-byte SHA1 hash, then server_DH_inner_data */
479 3 : if (enc_answer_len < 20 + 4) {
480 0 : return -1;
481 : }
482 :
483 3 : TlReader inner = tl_reader_init(decrypted + 20, enc_answer_len - 20);
484 :
485 3 : uint32_t inner_crc = tl_read_uint32(&inner);
486 3 : if (inner_crc != CRC_server_DH_inner_data) {
487 2 : logger_log(LOG_ERROR, "auth: wrong inner constructor 0x%08x", inner_crc);
488 2 : return -1;
489 : }
490 :
491 : /* Verify inner nonces */
492 : uint8_t inner_nonce[16];
493 1 : tl_read_int128(&inner, inner_nonce);
494 1 : if (memcmp(inner_nonce, ctx->nonce, 16) != 0) {
495 0 : return -1;
496 : }
497 :
498 : uint8_t inner_sn[16];
499 1 : tl_read_int128(&inner, inner_sn);
500 1 : if (memcmp(inner_sn, ctx->server_nonce, 16) != 0) {
501 0 : return -1;
502 : }
503 :
504 1 : ctx->g = tl_read_int32(&inner);
505 :
506 : /* MTProto spec: g must be one of {2, 3, 4, 5, 6, 7}. */
507 1 : if (ctx->g < 2 || ctx->g > 7) {
508 0 : logger_log(LOG_ERROR, "auth: invalid DH g=%d (must be 2–7)", ctx->g);
509 0 : return -1;
510 : }
511 :
512 : /* dh_prime as bytes */
513 1 : size_t prime_len = 0;
514 2 : RAII_STRING uint8_t *prime_bytes = tl_read_bytes(&inner, &prime_len);
515 1 : if (!prime_bytes || prime_len > sizeof(ctx->dh_prime)) return -1;
516 1 : memcpy(ctx->dh_prime, prime_bytes, prime_len);
517 1 : ctx->dh_prime_len = prime_len;
518 : /* prime_bytes freed automatically by RAII_STRING */
519 :
520 : /* g_a as bytes */
521 1 : size_t ga_len = 0;
522 2 : RAII_STRING uint8_t *ga_bytes = tl_read_bytes(&inner, &ga_len);
523 1 : if (!ga_bytes || ga_len > sizeof(ctx->g_a)) return -1;
524 1 : memcpy(ctx->g_a, ga_bytes, ga_len);
525 1 : ctx->g_a_len = ga_len;
526 : /* ga_bytes freed automatically by RAII_STRING */
527 :
528 1 : ctx->server_time = tl_read_int32(&inner);
529 :
530 : /* decrypted is freed automatically by RAII_STRING */
531 1 : logger_log(LOG_INFO, "auth: DH params parsed, g=%d, prime_len=%zu",
532 : ctx->g, ctx->dh_prime_len);
533 1 : return 0;
534 : }
535 :
536 : /* ---- Step 4: set_client_DH_params → dh_gen_ok ---- */
537 :
538 1 : int auth_step_set_client_dh(AuthKeyCtx *ctx) {
539 1 : if (!ctx || !ctx->transport || !ctx->session) return -1;
540 :
541 : /* Generate random b (256 bytes) */
542 1 : crypto_rand_bytes(ctx->b, 256);
543 :
544 : /* Compute g_b = pow(g, b) mod dh_prime */
545 : uint8_t g_be[4];
546 1 : size_t g_be_len = uint32_to_be((uint32_t)ctx->g, g_be);
547 :
548 : uint8_t g_b[256];
549 1 : size_t g_b_len = sizeof(g_b);
550 1 : CryptoBnCtx *bn_ctx = crypto_bn_ctx_new();
551 1 : if (!bn_ctx) return -1;
552 :
553 1 : int rc = crypto_bn_mod_exp(g_b, &g_b_len, g_be, g_be_len,
554 1 : ctx->b, 256,
555 1 : ctx->dh_prime, ctx->dh_prime_len, bn_ctx);
556 1 : if (rc != 0) {
557 0 : crypto_bn_ctx_free(bn_ctx);
558 0 : logger_log(LOG_ERROR, "auth: g_b computation failed");
559 0 : return -1;
560 : }
561 :
562 : /* Build client_DH_inner_data */
563 : TlWriter inner;
564 1 : tl_writer_init(&inner);
565 1 : tl_write_uint32(&inner, CRC_client_DH_inner_data);
566 1 : tl_write_int128(&inner, ctx->nonce);
567 1 : tl_write_int128(&inner, ctx->server_nonce);
568 1 : tl_write_int64(&inner, 0); /* retry_id = 0 */
569 1 : tl_write_bytes(&inner, g_b, g_b_len);
570 :
571 : /* Prepend SHA1 hash + pad to multiple of 16 */
572 : uint8_t sha1_hash[20];
573 1 : crypto_sha1(inner.data, inner.len, sha1_hash);
574 :
575 1 : size_t data_with_hash_len = 20 + inner.len;
576 : /* Pad to 16-byte boundary */
577 1 : size_t padded_len = data_with_hash_len;
578 1 : if (padded_len % 16 != 0) {
579 1 : padded_len += 16 - (padded_len % 16);
580 : }
581 :
582 1 : RAII_STRING uint8_t *padded = (uint8_t *)calloc(1, padded_len);
583 1 : if (!padded) {
584 0 : tl_writer_free(&inner);
585 0 : crypto_bn_ctx_free(bn_ctx);
586 0 : return -1;
587 : }
588 1 : memcpy(padded, sha1_hash, 20);
589 1 : memcpy(padded + 20, inner.data, inner.len);
590 : /* Fill padding with random bytes */
591 1 : if (padded_len > data_with_hash_len) {
592 1 : crypto_rand_bytes(padded + data_with_hash_len,
593 : padded_len - data_with_hash_len);
594 : }
595 1 : tl_writer_free(&inner);
596 :
597 : /* Encrypt with temp AES key/IV */
598 1 : RAII_STRING uint8_t *encrypted = (uint8_t *)malloc(padded_len);
599 1 : if (!encrypted) {
600 0 : crypto_bn_ctx_free(bn_ctx);
601 0 : return -1;
602 : }
603 1 : aes_ige_encrypt(padded, padded_len, ctx->tmp_aes_key, ctx->tmp_aes_iv,
604 : encrypted);
605 :
606 : /* Build set_client_DH_params */
607 : TlWriter w;
608 1 : tl_writer_init(&w);
609 1 : tl_write_uint32(&w, CRC_set_client_DH_params);
610 1 : tl_write_int128(&w, ctx->nonce);
611 1 : tl_write_int128(&w, ctx->server_nonce);
612 1 : tl_write_bytes(&w, encrypted, padded_len);
613 : /* encrypted and padded freed automatically by RAII_STRING */
614 :
615 1 : rc = rpc_send_unencrypted(ctx->session, ctx->transport, w.data, w.len);
616 1 : tl_writer_free(&w);
617 1 : if (rc != 0) {
618 0 : crypto_bn_ctx_free(bn_ctx);
619 0 : logger_log(LOG_ERROR, "auth: failed to send set_client_DH_params");
620 0 : return -1;
621 : }
622 :
623 : /* Receive response */
624 : uint8_t buf[4096];
625 1 : size_t buf_len = 0;
626 1 : rc = rpc_recv_unencrypted(ctx->session, ctx->transport,
627 : buf, sizeof(buf), &buf_len);
628 1 : if (rc != 0) {
629 0 : crypto_bn_ctx_free(bn_ctx);
630 0 : logger_log(LOG_ERROR, "auth: failed to receive DH gen response");
631 0 : return -1;
632 : }
633 :
634 1 : TlReader r = tl_reader_init(buf, buf_len);
635 1 : uint32_t constructor = tl_read_uint32(&r);
636 :
637 1 : if (constructor == CRC_dh_gen_retry) {
638 0 : crypto_bn_ctx_free(bn_ctx);
639 0 : logger_log(LOG_WARN, "auth: dh_gen_retry");
640 0 : return -1;
641 : }
642 1 : if (constructor == CRC_dh_gen_fail) {
643 0 : crypto_bn_ctx_free(bn_ctx);
644 0 : logger_log(LOG_ERROR, "auth: dh_gen_fail");
645 0 : return -1;
646 : }
647 1 : if (constructor != CRC_dh_gen_ok) {
648 0 : crypto_bn_ctx_free(bn_ctx);
649 0 : logger_log(LOG_ERROR, "auth: unexpected constructor 0x%08x", constructor);
650 0 : return -1;
651 : }
652 :
653 : /* Verify nonce in dh_gen_ok */
654 : uint8_t recv_nonce[16];
655 1 : tl_read_int128(&r, recv_nonce);
656 1 : if (memcmp(recv_nonce, ctx->nonce, 16) != 0) {
657 0 : crypto_bn_ctx_free(bn_ctx);
658 0 : return -1;
659 : }
660 :
661 : /* Read server_nonce and new_nonce_hash1 (to be verified below). */
662 : uint8_t recv_sn[16];
663 1 : tl_read_int128(&r, recv_sn);
664 : uint8_t new_nonce_hash[16];
665 1 : tl_read_int128(&r, new_nonce_hash);
666 :
667 : /* Compute auth_key = pow(g_a, b) mod dh_prime */
668 : uint8_t auth_key[256];
669 1 : size_t ak_len = sizeof(auth_key);
670 1 : rc = crypto_bn_mod_exp(auth_key, &ak_len, ctx->g_a, ctx->g_a_len,
671 1 : ctx->b, 256,
672 1 : ctx->dh_prime, ctx->dh_prime_len, bn_ctx);
673 1 : crypto_bn_ctx_free(bn_ctx);
674 :
675 1 : if (rc != 0) {
676 0 : logger_log(LOG_ERROR, "auth: auth_key computation failed");
677 0 : return -1;
678 : }
679 :
680 : /* Set auth_key on session (pad to 256 bytes if needed) */
681 : uint8_t auth_key_padded[256];
682 1 : memset(auth_key_padded, 0, sizeof(auth_key_padded));
683 1 : if (ak_len <= 256) {
684 1 : memcpy(auth_key_padded + (256 - ak_len), auth_key, ak_len);
685 : }
686 :
687 : /* QA-12: verify new_nonce_hash1 =
688 : * last 128 bits of SHA1(new_nonce[32] || 0x01 || auth_key_aux_hash[8])
689 : * where auth_key_aux_hash = SHA1(auth_key)[0:8].
690 : * Without this check a MITM could substitute auth_key during DH
691 : * exchange without detection. */
692 : {
693 : uint8_t ak_full_hash[20];
694 1 : crypto_sha1(auth_key_padded, 256, ak_full_hash);
695 : /* auth_key_aux_hash = ak_full_hash[0:8] */
696 :
697 : uint8_t nonce_hash_input[32 + 1 + 8];
698 1 : memcpy(nonce_hash_input, ctx->new_nonce, 32);
699 1 : nonce_hash_input[32] = 0x01; /* dh_gen_ok marker */
700 1 : memcpy(nonce_hash_input + 33, ak_full_hash, 8);
701 :
702 : uint8_t expected_full[20];
703 1 : crypto_sha1(nonce_hash_input, sizeof(nonce_hash_input), expected_full);
704 : /* last 16 bytes of the SHA1 result */
705 1 : if (memcmp(expected_full + 4, new_nonce_hash, 16) != 0) {
706 0 : logger_log(LOG_ERROR,
707 : "auth: new_nonce_hash1 mismatch — possible MITM");
708 0 : return -1;
709 : }
710 : }
711 :
712 1 : mtproto_session_set_auth_key(ctx->session, auth_key_padded);
713 :
714 : /* Compute server_salt = new_nonce[0:8] XOR server_nonce[0:8] */
715 1 : uint64_t salt = 0;
716 9 : for (int i = 0; i < 8; i++) {
717 8 : ((uint8_t *)&salt)[i] = ctx->new_nonce[i] ^ ctx->server_nonce[i];
718 : }
719 1 : mtproto_session_set_salt(ctx->session, salt);
720 :
721 1 : logger_log(LOG_INFO, "auth: DH key exchange complete, auth_key set");
722 1 : return 0;
723 : }
724 :
725 : /* ---- Auth Key Generation (orchestrator) ---- */
726 :
727 5 : int mtproto_auth_key_gen(Transport *t, MtProtoSession *s) {
728 5 : if (!t || !s) return -1;
729 :
730 3 : logger_log(LOG_INFO, "Starting DH auth key generation...");
731 :
732 : AuthKeyCtx ctx;
733 3 : memset(&ctx, 0, sizeof(ctx));
734 3 : ctx.transport = t;
735 3 : ctx.session = s;
736 3 : ctx.dc_id = t->dc_id;
737 :
738 3 : if (auth_step_req_pq(&ctx) != 0) return -1;
739 2 : if (auth_step_req_dh(&ctx) != 0) return -1;
740 2 : if (auth_step_parse_dh(&ctx) != 0) return -1;
741 1 : if (auth_step_set_client_dh(&ctx) != 0) return -1;
742 :
743 1 : logger_log(LOG_INFO, "Auth key generation complete");
744 1 : return 0;
745 : }
|