Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file mtproto_session.c
6 : * @brief MTProto session state management.
7 : */
8 :
9 : #include "mtproto_session.h"
10 : #include "crypto.h"
11 : #include "raii.h"
12 : #include "fs_util.h"
13 :
14 : #include <stdio.h>
15 : #include <stdlib.h>
16 : #include <string.h>
17 : #include <time.h>
18 :
19 : /**
20 : * @brief Initialize a new MTProto session with a random session_id.
21 : * @param s Session to initialize.
22 : */
23 552 : void mtproto_session_init(MtProtoSession *s) {
24 552 : if (!s) return;
25 552 : memset(s, 0, sizeof(*s));
26 :
27 : /* Random session_id */
28 552 : crypto_rand_bytes((unsigned char *)&s->session_id, sizeof(s->session_id));
29 : /* Ensure non-zero */
30 552 : if (s->session_id == 0) s->session_id = 1;
31 : }
32 :
33 : /**
34 : * @brief Generate the next monotonically increasing message ID.
35 : *
36 : * Message IDs are approximately unix_time * 2^32, must be divisible by 4
37 : * (client→server), and strictly increasing.
38 : *
39 : * @param s Session.
40 : * @return Next message ID, or 0 if s is NULL.
41 : */
42 328 : uint64_t mtproto_session_next_msg_id(MtProtoSession *s) {
43 328 : if (!s) return 0;
44 :
45 : /* msg_id ≈ unix_time * 2^32 with cryptographic randomness in the
46 : * lower 32 bits. Must be monotonically increasing and divisible by
47 : * 4 for client→server. */
48 328 : uint64_t now = (uint64_t)time(NULL);
49 328 : uint32_t low = 0;
50 328 : crypto_rand_bytes((unsigned char *)&low, sizeof(low));
51 328 : uint64_t msg_id = (now << 32) | ((uint64_t)low & 0xFFFFFFFC);
52 :
53 : /* Ensure monotonic increase */
54 328 : if (msg_id <= s->last_msg_id) {
55 72 : msg_id = s->last_msg_id + 4;
56 : }
57 :
58 : /* Client→server: msg_id mod 4 == 0 */
59 328 : msg_id &= ~(uint64_t)3;
60 :
61 328 : s->last_msg_id = msg_id;
62 328 : return msg_id;
63 : }
64 :
65 : /**
66 : * @brief Get the next sequence number.
67 : *
68 : * For content-related messages, increments the internal counter.
69 : *
70 : * @param s Session.
71 : * @param content_related 1 for RPC calls, 0 for acks/pings.
72 : * @return Next sequence number, or 0 if s is NULL.
73 : */
74 313 : uint32_t mtproto_session_next_seq_no(MtProtoSession *s, int content_related) {
75 313 : if (!s) return 0;
76 313 : uint32_t result = s->seq_no * 2 + (content_related ? 1 : 0);
77 313 : if (content_related) s->seq_no++;
78 313 : return result;
79 : }
80 :
81 : /**
82 : * @brief Set the 256-byte authorization key on the session.
83 : * @param s Session.
84 : * @param key 256-byte key to copy.
85 : */
86 242 : void mtproto_session_set_auth_key(MtProtoSession *s, const uint8_t key[256]) {
87 242 : if (!s || !key) return;
88 242 : memcpy(s->auth_key, key, 256);
89 242 : s->has_auth_key = 1;
90 : }
91 :
92 : /**
93 : * @brief Set the server salt.
94 : * @param s Session.
95 : * @param salt Server salt value.
96 : */
97 242 : void mtproto_session_set_salt(MtProtoSession *s, uint64_t salt) {
98 242 : if (!s) return;
99 242 : s->server_salt = salt;
100 : }
101 :
102 : /**
103 : * @brief Save the auth key to a file with mode 0600.
104 : *
105 : * @param s Session (must have auth_key).
106 : * @param path File path to write.
107 : * @return 0 on success, -1 on error.
108 : */
109 0 : int mtproto_session_save_auth_key(const MtProtoSession *s, const char *path) {
110 0 : if (!s || !path || !s->has_auth_key) return -1;
111 0 : RAII_FILE FILE *f = fopen(path, "wb");
112 0 : if (!f) return -1;
113 :
114 : /* Set restrictive permissions (auth key is sensitive) */
115 0 : if (fs_ensure_permissions(path, 0600) != 0) {
116 : /* Non-fatal but log-worthy — file was already created */
117 : }
118 :
119 0 : size_t written = fwrite(s->auth_key, 1, 256, f);
120 0 : return (written == 256) ? 0 : -1;
121 : }
122 :
123 : /**
124 : * @brief Load an auth key from a file.
125 : *
126 : * @param s Session (auth_key and has_auth_key will be set on success).
127 : * @param path File path to read.
128 : * @return 0 on success, -1 on error (file not found, too short, etc.).
129 : */
130 0 : int mtproto_session_load_auth_key(MtProtoSession *s, const char *path) {
131 0 : if (!s || !path) return -1;
132 0 : RAII_FILE FILE *f = fopen(path, "rb");
133 0 : if (!f) return -1;
134 0 : size_t nread = fread(s->auth_key, 1, 256, f);
135 0 : if (nread != 256) return -1;
136 0 : s->has_auth_key = 1;
137 0 : return 0;
138 : }
|