1use std::collections::{HashSet, VecDeque};
14use std::time::{SystemTime, UNIX_EPOCH};
15
16use ferogram_crypto::{AuthKey, DequeBuffer, decrypt_data_v2, encrypt_data_v2};
17use ferogram_tl_types::RemoteCall;
18
19const SEEN_MSG_IDS_MAX: usize = 500;
21
22#[derive(Debug)]
24pub enum DecryptError {
25 Crypto(ferogram_crypto::DecryptError),
27 FrameTooShort,
29 SessionMismatch,
31 MsgIdTimeWindow,
33 DuplicateMsgId,
35 InvalidMsgId,
37}
38
39impl std::fmt::Display for DecryptError {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 Self::Crypto(e) => write!(f, "crypto: {e}"),
43 Self::FrameTooShort => write!(f, "inner plaintext too short"),
44 Self::SessionMismatch => write!(f, "session_id mismatch"),
45 Self::MsgIdTimeWindow => write!(f, "server msg_id outside -300s/+30s time window"),
46 Self::DuplicateMsgId => write!(f, "duplicate server msg_id (replay)"),
47 Self::InvalidMsgId => write!(f, "server msg_id has even parity (must be odd)"),
48 }
49 }
50}
51impl std::error::Error for DecryptError {}
52
53pub struct DecryptedMessage {
55 pub salt: i64,
57 pub session_id: i64,
59 pub msg_id: i64,
61 pub seq_no: i32,
63 pub body: Vec<u8>,
65}
66
67pub type SeenMsgIds = std::sync::Arc<std::sync::Mutex<(VecDeque<i64>, HashSet<i64>)>>;
76
77pub fn new_seen_msg_ids() -> SeenMsgIds {
79 std::sync::Arc::new(std::sync::Mutex::new((
80 VecDeque::with_capacity(SEEN_MSG_IDS_MAX),
81 HashSet::with_capacity(SEEN_MSG_IDS_MAX),
82 )))
83}
84
85pub struct EncryptedSession {
87 auth_key: AuthKey,
88 session_id: i64,
89 sequence: i32,
90 last_msg_id: i64,
91 pub salt: i64,
93 pub time_offset: i32,
95 seen_msg_ids: SeenMsgIds,
98}
99
100impl EncryptedSession {
101 pub fn new(auth_key: [u8; 256], first_salt: i64, time_offset: i32) -> Self {
107 Self::with_seen(auth_key, first_salt, time_offset, new_seen_msg_ids())
108 }
109
110 pub fn with_seen(
112 auth_key: [u8; 256],
113 first_salt: i64,
114 time_offset: i32,
115 seen_msg_ids: SeenMsgIds,
116 ) -> Self {
117 let mut rnd = [0u8; 8];
118 getrandom::getrandom(&mut rnd).expect("getrandom");
119 Self {
120 auth_key: AuthKey::from_bytes(auth_key),
121 session_id: i64::from_le_bytes(rnd),
122 sequence: 0,
123 last_msg_id: 0,
124 salt: first_salt,
125 time_offset,
126 seen_msg_ids,
127 }
128 }
129
130 pub fn seen_msg_ids(&self) -> SeenMsgIds {
133 std::sync::Arc::clone(&self.seen_msg_ids)
134 }
135
136 fn next_msg_id(&mut self) -> i64 {
138 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
139 let secs = now.as_secs().wrapping_add(self.time_offset as i64 as u64);
141 let nanos = now.subsec_nanos() as u64;
142 let mut id = ((secs << 32) | (nanos << 2)) as i64;
143 if (id as u64 & 0xFFFF_FFFF) == 0 {
148 id |= 4;
149 }
150 if self.last_msg_id >= id {
151 id = self.last_msg_id + 4;
152 }
153 self.last_msg_id = id;
154 id
155 }
156
157 fn next_seq_no(&mut self) -> i32 {
160 let n = self.sequence * 2 + 1;
161 self.sequence += 1;
162 n
163 }
164
165 pub fn next_seq_no_ncr(&self) -> i32 {
170 self.sequence * 2
171 }
172
173 pub fn correct_seq_no(&mut self, _code: u32) {
184 self.reset_session();
187 log::debug!("[ferogram] seq_no desync (code {_code}): performed full session reset");
188 }
189
190 pub fn undo_seq_no(&mut self) {
197 self.sequence = self.sequence.saturating_sub(1);
198 }
199
200 pub fn correct_time_offset(&mut self, server_msg_id: i64) {
207 let server_time = (server_msg_id >> 32) as i32;
209 let local_now = SystemTime::now()
210 .duration_since(UNIX_EPOCH)
211 .unwrap()
212 .as_secs() as i32;
213 let new_offset = server_time.wrapping_sub(local_now);
214 log::debug!(
215 "[ferogram] time_offset correction: {} → {} (server_time={server_time})",
216 self.time_offset,
217 new_offset
218 );
219 self.time_offset = new_offset;
220 self.last_msg_id = (server_msg_id & !0x3i64).max(self.last_msg_id);
223 }
224
225 pub fn alloc_msg_seqno(&mut self, content_related: bool) -> (i64, i32) {
232 let msg_id = self.next_msg_id();
233 let seqno = if content_related {
234 self.next_seq_no()
235 } else {
236 self.next_seq_no_ncr()
237 };
238 (msg_id, seqno)
239 }
240
241 pub fn pack_body_with_msg_id(&mut self, body: &[u8], content_related: bool) -> (Vec<u8>, i64) {
249 let msg_id = self.next_msg_id();
250 let seq_no = if content_related {
251 self.next_seq_no()
252 } else {
253 self.next_seq_no_ncr()
254 };
255
256 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
257 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
258 buf.extend(self.salt.to_le_bytes());
259 buf.extend(self.session_id.to_le_bytes());
260 buf.extend(msg_id.to_le_bytes());
261 buf.extend(seq_no.to_le_bytes());
262 buf.extend((body.len() as u32).to_le_bytes());
263 buf.extend(body.iter().copied());
264
265 encrypt_data_v2(&mut buf, &self.auth_key);
266 (buf.as_ref().to_vec(), msg_id)
267 }
268
269 pub fn pack_container(&mut self, container_body: &[u8]) -> (Vec<u8>, i64) {
278 self.pack_body_with_msg_id(container_body, false)
279 }
280
281 pub fn pack_body_at_msg_id(&mut self, body: &[u8], msg_id: i64) -> Vec<u8> {
286 let seq_no = self.next_seq_no();
287 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
288 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
289 buf.extend(self.salt.to_le_bytes());
290 buf.extend(self.session_id.to_le_bytes());
291 buf.extend(msg_id.to_le_bytes());
292 buf.extend(seq_no.to_le_bytes());
293 buf.extend((body.len() as u32).to_le_bytes());
294 buf.extend(body.iter().copied());
295 encrypt_data_v2(&mut buf, &self.auth_key);
296 buf.as_ref().to_vec()
297 }
298
299 pub fn pack_serializable<S: ferogram_tl_types::Serializable>(&mut self, call: &S) -> Vec<u8> {
301 let body = call.to_bytes();
302 let msg_id = self.next_msg_id();
303 let seq_no = self.next_seq_no();
304
305 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
306 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
307 buf.extend(self.salt.to_le_bytes());
308 buf.extend(self.session_id.to_le_bytes());
309 buf.extend(msg_id.to_le_bytes());
310 buf.extend(seq_no.to_le_bytes());
311 buf.extend((body.len() as u32).to_le_bytes());
312 buf.extend(body.iter().copied());
313
314 encrypt_data_v2(&mut buf, &self.auth_key);
315 buf.as_ref().to_vec()
316 }
317
318 pub fn pack_serializable_with_msg_id<S: ferogram_tl_types::Serializable>(
320 &mut self,
321 call: &S,
322 ) -> (Vec<u8>, i64) {
323 let body = call.to_bytes();
324 let msg_id = self.next_msg_id();
325 let seq_no = self.next_seq_no();
326 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
327 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
328 buf.extend(self.salt.to_le_bytes());
329 buf.extend(self.session_id.to_le_bytes());
330 buf.extend(msg_id.to_le_bytes());
331 buf.extend(seq_no.to_le_bytes());
332 buf.extend((body.len() as u32).to_le_bytes());
333 buf.extend(body.iter().copied());
334 encrypt_data_v2(&mut buf, &self.auth_key);
335 (buf.as_ref().to_vec(), msg_id)
336 }
337
338 pub fn pack_with_msg_id<R: RemoteCall>(&mut self, call: &R) -> (Vec<u8>, i64) {
340 let body = call.to_bytes();
341 let msg_id = self.next_msg_id();
342 let seq_no = self.next_seq_no();
343 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
344 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
345 buf.extend(self.salt.to_le_bytes());
346 buf.extend(self.session_id.to_le_bytes());
347 buf.extend(msg_id.to_le_bytes());
348 buf.extend(seq_no.to_le_bytes());
349 buf.extend((body.len() as u32).to_le_bytes());
350 buf.extend(body.iter().copied());
351 encrypt_data_v2(&mut buf, &self.auth_key);
352 (buf.as_ref().to_vec(), msg_id)
353 }
354
355 pub fn pack<R: RemoteCall>(&mut self, call: &R) -> Vec<u8> {
357 let body = call.to_bytes();
358 let msg_id = self.next_msg_id();
359 let seq_no = self.next_seq_no();
360
361 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
362 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
363 buf.extend(self.salt.to_le_bytes());
364 buf.extend(self.session_id.to_le_bytes());
365 buf.extend(msg_id.to_le_bytes());
366 buf.extend(seq_no.to_le_bytes());
367 buf.extend((body.len() as u32).to_le_bytes());
368 buf.extend(body.iter().copied());
369
370 encrypt_data_v2(&mut buf, &self.auth_key);
371 buf.as_ref().to_vec()
372 }
373
374 pub fn unpack(&self, frame: &mut [u8]) -> Result<DecryptedMessage, DecryptError> {
376 let plaintext = decrypt_data_v2(frame, &self.auth_key).map_err(DecryptError::Crypto)?;
377
378 if plaintext.len() < 32 {
379 return Err(DecryptError::FrameTooShort);
380 }
381
382 let salt = i64::from_le_bytes(plaintext[..8].try_into().unwrap());
383 let session_id = i64::from_le_bytes(plaintext[8..16].try_into().unwrap());
384 let msg_id = i64::from_le_bytes(plaintext[16..24].try_into().unwrap());
385 let seq_no = i32::from_le_bytes(plaintext[24..28].try_into().unwrap());
386 let body_len = u32::from_le_bytes(plaintext[28..32].try_into().unwrap()) as usize;
387
388 if session_id != self.session_id {
389 return Err(DecryptError::SessionMismatch);
390 }
391
392 if msg_id & 1 == 0 {
394 return Err(DecryptError::InvalidMsgId);
395 }
396
397 let server_secs = (msg_id as u64 >> 32) as i64;
399 let now = SystemTime::now()
400 .duration_since(UNIX_EPOCH)
401 .unwrap()
402 .as_secs() as i64;
403 let corrected = now + self.time_offset as i64;
404 let skew = server_secs - corrected;
405 if !(-300..=30).contains(&skew) {
406 return Err(DecryptError::MsgIdTimeWindow);
407 }
408
409 {
411 let mut seen = self.seen_msg_ids.lock().unwrap();
412 if seen.1.contains(&msg_id) {
413 return Err(DecryptError::DuplicateMsgId);
414 }
415 seen.0.push_back(msg_id);
416 seen.1.insert(msg_id);
417 if seen.0.len() > SEEN_MSG_IDS_MAX
418 && let Some(old_id) = seen.0.pop_front()
419 {
420 seen.1.remove(&old_id);
421 }
422 }
423
424 if body_len > 16 * 1024 * 1024 {
426 return Err(DecryptError::FrameTooShort);
427 }
428 if 32 + body_len > plaintext.len() {
429 return Err(DecryptError::FrameTooShort);
430 }
431 if !body_len.is_multiple_of(4) {
433 return Err(DecryptError::FrameTooShort);
434 }
435 let padding = plaintext.len() - 32 - body_len;
437 if !(12..=1024).contains(&padding) {
438 return Err(DecryptError::FrameTooShort);
439 }
440 let body = plaintext[32..32 + body_len].to_vec();
441
442 Ok(DecryptedMessage {
443 salt,
444 session_id,
445 msg_id,
446 seq_no,
447 body,
448 })
449 }
450
451 pub fn auth_key_bytes(&self) -> [u8; 256] {
453 self.auth_key.to_bytes()
454 }
455
456 pub fn session_id(&self) -> i64 {
458 self.session_id
459 }
460
461 pub fn reset_session(&mut self) {
469 let mut rnd = [0u8; 8];
470 getrandom::getrandom(&mut rnd).expect("getrandom");
471 let old_session = self.session_id;
472 self.session_id = i64::from_le_bytes(rnd);
473 self.sequence = 0;
474 self.last_msg_id = 0;
475 log::debug!(
478 "[ferogram] session reset: {:#018x} → {:#018x}",
479 old_session,
480 self.session_id
481 );
482 }
483
484 pub fn reset_seq_no_only(&mut self) {
495 self.sequence = 0;
496 self.last_msg_id = 0;
497 log::debug!(
498 "[ferogram] seq_no reset (session_id unchanged): {:#018x}",
499 self.session_id
500 );
501 }
502}
503
504impl EncryptedSession {
505 pub fn decrypt_frame_dedup(
513 auth_key: &[u8; 256],
514 session_id: i64,
515 frame: &mut [u8],
516 seen: &SeenMsgIds,
517 ) -> Result<DecryptedMessage, DecryptError> {
518 Self::decrypt_frame_dedup_with_offset(auth_key, session_id, frame, seen, 0)
519 }
520
521 pub fn decrypt_frame_dedup_with_offset(
528 auth_key: &[u8; 256],
529 session_id: i64,
530 frame: &mut [u8],
531 seen: &SeenMsgIds,
532 time_offset: i32,
533 ) -> Result<DecryptedMessage, DecryptError> {
534 let msg = Self::decrypt_frame_with_offset(auth_key, session_id, frame, time_offset)?;
535 {
536 let mut s = seen.lock().unwrap();
537 if s.1.contains(&msg.msg_id) {
538 return Err(DecryptError::DuplicateMsgId);
539 }
540 s.0.push_back(msg.msg_id);
541 s.1.insert(msg.msg_id);
542 if s.0.len() > SEEN_MSG_IDS_MAX
543 && let Some(old_id) = s.0.pop_front()
544 {
545 s.1.remove(&old_id);
546 }
547 }
548 Ok(msg)
549 }
550
551 pub fn decrypt_frame(
555 auth_key: &[u8; 256],
556 session_id: i64,
557 frame: &mut [u8],
558 ) -> Result<DecryptedMessage, DecryptError> {
559 Self::decrypt_frame_with_offset(auth_key, session_id, frame, 0)
560 }
561
562 pub fn decrypt_frame_with_offset(
565 auth_key: &[u8; 256],
566 session_id: i64,
567 frame: &mut [u8],
568 time_offset: i32,
569 ) -> Result<DecryptedMessage, DecryptError> {
570 let key = AuthKey::from_bytes(*auth_key);
571 let plaintext = decrypt_data_v2(frame, &key).map_err(DecryptError::Crypto)?;
572 if plaintext.len() < 32 {
573 return Err(DecryptError::FrameTooShort);
574 }
575 let salt = i64::from_le_bytes(plaintext[..8].try_into().unwrap());
576 let sid = i64::from_le_bytes(plaintext[8..16].try_into().unwrap());
577 let msg_id = i64::from_le_bytes(plaintext[16..24].try_into().unwrap());
578 let seq_no = i32::from_le_bytes(plaintext[24..28].try_into().unwrap());
579 let body_len = u32::from_le_bytes(plaintext[28..32].try_into().unwrap()) as usize;
580 if sid != session_id {
581 return Err(DecryptError::SessionMismatch);
582 }
583 if msg_id & 1 == 0 {
585 return Err(DecryptError::InvalidMsgId);
586 }
587 let server_secs = (msg_id as u64 >> 32) as i64;
589 let now = SystemTime::now()
590 .duration_since(UNIX_EPOCH)
591 .unwrap()
592 .as_secs() as i64;
593 let corrected = now + time_offset as i64;
594 let skew = server_secs - corrected;
595 if !(-300..=30).contains(&skew) {
596 return Err(DecryptError::MsgIdTimeWindow);
597 }
598 if body_len > 16 * 1024 * 1024 {
600 return Err(DecryptError::FrameTooShort);
601 }
602 if 32 + body_len > plaintext.len() {
603 return Err(DecryptError::FrameTooShort);
604 }
605 if !body_len.is_multiple_of(4) {
607 return Err(DecryptError::FrameTooShort);
608 }
609 let padding = plaintext.len() - 32 - body_len;
611 if !(12..=1024).contains(&padding) {
612 return Err(DecryptError::FrameTooShort);
613 }
614 let body = plaintext[32..32 + body_len].to_vec();
615 Ok(DecryptedMessage {
616 salt,
617 session_id: sid,
618 msg_id,
619 seq_no,
620 body,
621 })
622 }
623}