1use std::collections::{HashSet, VecDeque};
16use std::time::{SystemTime, UNIX_EPOCH};
17
18use ferogram_crypto::{AuthKey, DequeBuffer, decrypt_data_v2, encrypt_data_v2};
19use ferogram_tl_types::RemoteCall;
20
21const SEEN_MSG_IDS_MAX: usize = 500;
23
24#[derive(Debug)]
26pub enum DecryptError {
27 Crypto(ferogram_crypto::DecryptError),
29 FrameTooShort,
31 SessionMismatch,
33 MsgIdTimeWindow,
35 DuplicateMsgId,
37 InvalidMsgId,
39}
40
41impl std::fmt::Display for DecryptError {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 Self::Crypto(e) => write!(f, "crypto: {e}"),
45 Self::FrameTooShort => write!(f, "inner plaintext too short"),
46 Self::SessionMismatch => write!(f, "session_id mismatch"),
47 Self::MsgIdTimeWindow => write!(f, "server msg_id outside -300s/+30s time window"),
48 Self::DuplicateMsgId => write!(f, "duplicate server msg_id (replay)"),
49 Self::InvalidMsgId => write!(f, "server msg_id has even parity (must be odd)"),
50 }
51 }
52}
53impl std::error::Error for DecryptError {}
54
55pub struct DecryptedMessage {
57 pub salt: i64,
59 pub session_id: i64,
61 pub msg_id: i64,
63 pub seq_no: i32,
65 pub body: Vec<u8>,
67}
68
69pub type SeenMsgIds = std::sync::Arc<std::sync::Mutex<(VecDeque<i64>, HashSet<i64>)>>;
78
79pub fn new_seen_msg_ids() -> SeenMsgIds {
81 std::sync::Arc::new(std::sync::Mutex::new((
82 VecDeque::with_capacity(SEEN_MSG_IDS_MAX),
83 HashSet::with_capacity(SEEN_MSG_IDS_MAX),
84 )))
85}
86
87pub struct EncryptedSession {
89 auth_key: AuthKey,
90 session_id: i64,
91 sequence: i32,
92 last_msg_id: i64,
93 pub salt: i64,
95 pub time_offset: i32,
97 seen_msg_ids: SeenMsgIds,
100}
101
102impl EncryptedSession {
103 pub fn new(auth_key: [u8; 256], first_salt: i64, time_offset: i32) -> Self {
109 Self::with_seen(auth_key, first_salt, time_offset, new_seen_msg_ids())
110 }
111
112 pub fn with_seen(
114 auth_key: [u8; 256],
115 first_salt: i64,
116 time_offset: i32,
117 seen_msg_ids: SeenMsgIds,
118 ) -> Self {
119 let mut rnd = [0u8; 8];
120 ferogram_crypto::fill_random(&mut rnd);
121 Self {
122 auth_key: AuthKey::from_bytes(auth_key),
123 session_id: i64::from_le_bytes(rnd),
124 sequence: 0,
125 last_msg_id: 0,
126 salt: first_salt,
127 time_offset,
128 seen_msg_ids,
129 }
130 }
131
132 pub fn seen_msg_ids(&self) -> SeenMsgIds {
135 std::sync::Arc::clone(&self.seen_msg_ids)
136 }
137
138 fn next_msg_id(&mut self) -> i64 {
140 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
141 let secs = now.as_secs().wrapping_add(self.time_offset as i64 as u64);
143 let nanos = now.subsec_nanos() as u64;
144 let mut id = ((secs << 32) | (nanos << 2)) as i64;
145 if (id as u64 & 0xFFFF_FFFF) == 0 {
150 id |= 4;
151 }
152 if self.last_msg_id >= id {
153 id = self.last_msg_id + 4;
154 }
155 self.last_msg_id = id;
156 id
157 }
158
159 fn next_seq_no(&mut self) -> i32 {
162 let n = self.sequence * 2 + 1;
163 self.sequence += 1;
164 n
165 }
166
167 pub fn next_seq_no_ncr(&self) -> i32 {
172 self.sequence * 2
173 }
174
175 pub fn correct_seq_no(&mut self, _code: u32) {
186 self.reset_session();
189 tracing::debug!(
190 code = _code,
191 "[ferogram::mtproto] seq_no desync: full session reset (new session_id, seq_no=0)"
192 );
193 }
194
195 pub fn undo_seq_no(&mut self) {
202 self.sequence = self.sequence.saturating_sub(1);
203 }
204
205 pub fn correct_time_offset(&mut self, server_msg_id: i64) {
212 let server_time = (server_msg_id >> 32) as i32;
214 let local_now = SystemTime::now()
215 .duration_since(UNIX_EPOCH)
216 .unwrap()
217 .as_secs() as i32;
218 let new_offset = server_time.wrapping_sub(local_now);
219 tracing::debug!(
220 old_offset = self.time_offset,
221 new_offset,
222 server_time,
223 "[ferogram::mtproto] clock skew corrected from bad_msg_notification"
224 );
225 self.time_offset = new_offset;
226 self.last_msg_id = (server_msg_id & !0x3i64).max(self.last_msg_id);
229 }
230
231 pub fn alloc_msg_seqno(&mut self, content_related: bool) -> (i64, i32) {
238 let msg_id = self.next_msg_id();
239 let seqno = if content_related {
240 self.next_seq_no()
241 } else {
242 self.next_seq_no_ncr()
243 };
244 (msg_id, seqno)
245 }
246
247 pub fn pack_body_with_msg_id(&mut self, body: &[u8], content_related: bool) -> (Vec<u8>, i64) {
255 let msg_id = self.next_msg_id();
256 let seq_no = if content_related {
257 self.next_seq_no()
258 } else {
259 self.next_seq_no_ncr()
260 };
261
262 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
263 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
264 buf.extend(self.salt.to_le_bytes());
265 buf.extend(self.session_id.to_le_bytes());
266 buf.extend(msg_id.to_le_bytes());
267 buf.extend(seq_no.to_le_bytes());
268 buf.extend((body.len() as u32).to_le_bytes());
269 buf.extend(body.iter().copied());
270
271 encrypt_data_v2(&mut buf, &self.auth_key);
272 (buf.as_ref().to_vec(), msg_id)
273 }
274
275 pub fn pack_container(&mut self, container_body: &[u8]) -> (Vec<u8>, i64) {
284 self.pack_body_with_msg_id(container_body, false)
285 }
286
287 pub fn pack_body_at_msg_id(&mut self, body: &[u8], msg_id: i64) -> Vec<u8> {
292 let seq_no = self.next_seq_no();
293 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
294 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
295 buf.extend(self.salt.to_le_bytes());
296 buf.extend(self.session_id.to_le_bytes());
297 buf.extend(msg_id.to_le_bytes());
298 buf.extend(seq_no.to_le_bytes());
299 buf.extend((body.len() as u32).to_le_bytes());
300 buf.extend(body.iter().copied());
301 encrypt_data_v2(&mut buf, &self.auth_key);
302 buf.as_ref().to_vec()
303 }
304
305 pub fn pack_serializable<S: ferogram_tl_types::Serializable>(&mut self, call: &S) -> Vec<u8> {
307 let body = call.to_bytes();
308 let msg_id = self.next_msg_id();
309 let seq_no = self.next_seq_no();
310
311 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
312 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
313 buf.extend(self.salt.to_le_bytes());
314 buf.extend(self.session_id.to_le_bytes());
315 buf.extend(msg_id.to_le_bytes());
316 buf.extend(seq_no.to_le_bytes());
317 buf.extend((body.len() as u32).to_le_bytes());
318 buf.extend(body.iter().copied());
319
320 encrypt_data_v2(&mut buf, &self.auth_key);
321 buf.as_ref().to_vec()
322 }
323
324 pub fn pack_serializable_with_msg_id<S: ferogram_tl_types::Serializable>(
326 &mut self,
327 call: &S,
328 ) -> (Vec<u8>, i64) {
329 let body = call.to_bytes();
330 let msg_id = self.next_msg_id();
331 let seq_no = self.next_seq_no();
332 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
333 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
334 buf.extend(self.salt.to_le_bytes());
335 buf.extend(self.session_id.to_le_bytes());
336 buf.extend(msg_id.to_le_bytes());
337 buf.extend(seq_no.to_le_bytes());
338 buf.extend((body.len() as u32).to_le_bytes());
339 buf.extend(body.iter().copied());
340 encrypt_data_v2(&mut buf, &self.auth_key);
341 (buf.as_ref().to_vec(), msg_id)
342 }
343
344 pub fn pack_with_msg_id<R: RemoteCall>(&mut self, call: &R) -> (Vec<u8>, i64) {
346 let body = call.to_bytes();
347 let msg_id = self.next_msg_id();
348 let seq_no = self.next_seq_no();
349 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
350 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
351 buf.extend(self.salt.to_le_bytes());
352 buf.extend(self.session_id.to_le_bytes());
353 buf.extend(msg_id.to_le_bytes());
354 buf.extend(seq_no.to_le_bytes());
355 buf.extend((body.len() as u32).to_le_bytes());
356 buf.extend(body.iter().copied());
357 encrypt_data_v2(&mut buf, &self.auth_key);
358 (buf.as_ref().to_vec(), msg_id)
359 }
360
361 pub fn pack<R: RemoteCall>(&mut self, call: &R) -> Vec<u8> {
363 let body = call.to_bytes();
364 let msg_id = self.next_msg_id();
365 let seq_no = self.next_seq_no();
366
367 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
368 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
369 buf.extend(self.salt.to_le_bytes());
370 buf.extend(self.session_id.to_le_bytes());
371 buf.extend(msg_id.to_le_bytes());
372 buf.extend(seq_no.to_le_bytes());
373 buf.extend((body.len() as u32).to_le_bytes());
374 buf.extend(body.iter().copied());
375
376 encrypt_data_v2(&mut buf, &self.auth_key);
377 buf.as_ref().to_vec()
378 }
379
380 pub fn unpack(&self, frame: &mut [u8]) -> Result<DecryptedMessage, DecryptError> {
382 let plaintext = decrypt_data_v2(frame, &self.auth_key).map_err(DecryptError::Crypto)?;
383
384 if plaintext.len() < 32 {
385 return Err(DecryptError::FrameTooShort);
386 }
387
388 let salt = i64::from_le_bytes(plaintext[..8].try_into().unwrap());
389 let session_id = i64::from_le_bytes(plaintext[8..16].try_into().unwrap());
390 let msg_id = i64::from_le_bytes(plaintext[16..24].try_into().unwrap());
391 let seq_no = i32::from_le_bytes(plaintext[24..28].try_into().unwrap());
392 let body_len = u32::from_le_bytes(plaintext[28..32].try_into().unwrap()) as usize;
393
394 if session_id != self.session_id {
395 return Err(DecryptError::SessionMismatch);
396 }
397
398 if msg_id & 1 == 0 {
400 return Err(DecryptError::InvalidMsgId);
401 }
402
403 let server_secs = (msg_id as u64 >> 32) as i64;
405 let now = SystemTime::now()
406 .duration_since(UNIX_EPOCH)
407 .unwrap()
408 .as_secs() as i64;
409 let corrected = now + self.time_offset as i64;
410 let skew = server_secs - corrected;
411 if !(-300..=30).contains(&skew) {
412 return Err(DecryptError::MsgIdTimeWindow);
413 }
414
415 {
417 let mut seen = self.seen_msg_ids.lock().unwrap();
418 if seen.1.contains(&msg_id) {
419 return Err(DecryptError::DuplicateMsgId);
420 }
421 seen.0.push_back(msg_id);
422 seen.1.insert(msg_id);
423 if seen.0.len() > SEEN_MSG_IDS_MAX
424 && let Some(old_id) = seen.0.pop_front()
425 {
426 seen.1.remove(&old_id);
427 }
428 }
429
430 if body_len > 16 * 1024 * 1024 {
432 return Err(DecryptError::FrameTooShort);
433 }
434 if 32 + body_len > plaintext.len() {
435 return Err(DecryptError::FrameTooShort);
436 }
437 if !body_len.is_multiple_of(4) {
439 return Err(DecryptError::FrameTooShort);
440 }
441 let padding = plaintext.len() - 32 - body_len;
443 if !(12..=1024).contains(&padding) {
444 return Err(DecryptError::FrameTooShort);
445 }
446 let body = plaintext[32..32 + body_len].to_vec();
447
448 Ok(DecryptedMessage {
449 salt,
450 session_id,
451 msg_id,
452 seq_no,
453 body,
454 })
455 }
456
457 pub fn auth_key_bytes(&self) -> [u8; 256] {
459 self.auth_key.to_bytes()
460 }
461
462 pub fn session_id(&self) -> i64 {
464 self.session_id
465 }
466
467 pub fn reset_session(&mut self) {
475 let mut rnd = [0u8; 8];
476 ferogram_crypto::fill_random(&mut rnd);
477 let old_session = self.session_id;
478 self.session_id = i64::from_le_bytes(rnd);
479 self.sequence = 0;
480 self.last_msg_id = 0;
481 tracing::debug!(
484 old_session = format_args!("{old_session:#018x}"),
485 new_session = format_args!("{:#018x}", self.session_id),
486 "[ferogram::mtproto] session reset: new session_id assigned, seq_no zeroed"
487 );
488 }
489
490 pub fn reset_seq_no_only(&mut self) {
501 self.sequence = 0;
502 self.last_msg_id = 0;
503 tracing::debug!(
504 session_id = format_args!("{:#018x}", self.session_id),
505 "[ferogram::mtproto] seq_no reset after new_session_created (session_id unchanged)"
506 );
507 }
508}
509
510impl EncryptedSession {
511 pub fn decrypt_frame_dedup(
519 auth_key: &[u8; 256],
520 session_id: i64,
521 frame: &mut [u8],
522 seen: &SeenMsgIds,
523 ) -> Result<DecryptedMessage, DecryptError> {
524 Self::decrypt_frame_dedup_with_offset(auth_key, session_id, frame, seen, 0)
525 }
526
527 pub fn decrypt_frame_dedup_with_offset(
534 auth_key: &[u8; 256],
535 session_id: i64,
536 frame: &mut [u8],
537 seen: &SeenMsgIds,
538 time_offset: i32,
539 ) -> Result<DecryptedMessage, DecryptError> {
540 let msg = Self::decrypt_frame_with_offset(auth_key, session_id, frame, time_offset)?;
541 {
542 let mut s = seen.lock().unwrap();
543 if s.1.contains(&msg.msg_id) {
544 return Err(DecryptError::DuplicateMsgId);
545 }
546 s.0.push_back(msg.msg_id);
547 s.1.insert(msg.msg_id);
548 if s.0.len() > SEEN_MSG_IDS_MAX
549 && let Some(old_id) = s.0.pop_front()
550 {
551 s.1.remove(&old_id);
552 }
553 }
554 Ok(msg)
555 }
556
557 pub fn decrypt_frame(
561 auth_key: &[u8; 256],
562 session_id: i64,
563 frame: &mut [u8],
564 ) -> Result<DecryptedMessage, DecryptError> {
565 Self::decrypt_frame_with_offset(auth_key, session_id, frame, 0)
566 }
567
568 pub fn decrypt_frame_with_offset(
571 auth_key: &[u8; 256],
572 session_id: i64,
573 frame: &mut [u8],
574 time_offset: i32,
575 ) -> Result<DecryptedMessage, DecryptError> {
576 let key = AuthKey::from_bytes(*auth_key);
577 let plaintext = decrypt_data_v2(frame, &key).map_err(DecryptError::Crypto)?;
578 if plaintext.len() < 32 {
579 return Err(DecryptError::FrameTooShort);
580 }
581 let salt = i64::from_le_bytes(plaintext[..8].try_into().unwrap());
582 let sid = i64::from_le_bytes(plaintext[8..16].try_into().unwrap());
583 let msg_id = i64::from_le_bytes(plaintext[16..24].try_into().unwrap());
584 let seq_no = i32::from_le_bytes(plaintext[24..28].try_into().unwrap());
585 let body_len = u32::from_le_bytes(plaintext[28..32].try_into().unwrap()) as usize;
586 if sid != session_id {
587 return Err(DecryptError::SessionMismatch);
588 }
589 if msg_id & 1 == 0 {
591 return Err(DecryptError::InvalidMsgId);
592 }
593 let server_secs = (msg_id as u64 >> 32) as i64;
595 let now = SystemTime::now()
596 .duration_since(UNIX_EPOCH)
597 .unwrap()
598 .as_secs() as i64;
599 let corrected = now + time_offset as i64;
600 let skew = server_secs - corrected;
601 if !(-300..=30).contains(&skew) {
602 return Err(DecryptError::MsgIdTimeWindow);
603 }
604 if body_len > 16 * 1024 * 1024 {
606 return Err(DecryptError::FrameTooShort);
607 }
608 if 32 + body_len > plaintext.len() {
609 return Err(DecryptError::FrameTooShort);
610 }
611 if !body_len.is_multiple_of(4) {
613 return Err(DecryptError::FrameTooShort);
614 }
615 let padding = plaintext.len() - 32 - body_len;
617 if !(12..=1024).contains(&padding) {
618 return Err(DecryptError::FrameTooShort);
619 }
620 let body = plaintext[32..32 + body_len].to_vec();
621 Ok(DecryptedMessage {
622 salt,
623 session_id: sid,
624 msg_id,
625 seq_no,
626 body,
627 })
628 }
629}