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