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 self.last_msg_id >= id {
142 id = self.last_msg_id + 4;
143 }
144 self.last_msg_id = id;
145 id
146 }
147
148 fn next_seq_no(&mut self) -> i32 {
151 let n = self.sequence * 2 + 1;
152 self.sequence += 1;
153 n
154 }
155
156 pub fn next_seq_no_ncr(&self) -> i32 {
161 self.sequence * 2
162 }
163
164 pub fn correct_seq_no(&mut self, code: u32) {
169 match code {
170 32 => {
171 self.sequence += 64;
173 log::debug!(
174 "[ferogram] seq_no correction: code 32, bumped seq to {}",
175 self.sequence
176 );
177 }
178 33 => {
179 self.sequence = self.sequence.saturating_sub(16).max(1);
184 log::debug!(
185 "[ferogram] seq_no correction: code 33, lowered seq to {}",
186 self.sequence
187 );
188 }
189 _ => {}
190 }
191 }
192
193 pub fn undo_seq_no(&mut self) {
200 self.sequence = self.sequence.saturating_sub(1);
201 }
202
203 pub fn correct_time_offset(&mut self, server_msg_id: i64) {
210 let server_time = (server_msg_id >> 32) as i32;
212 let local_now = SystemTime::now()
213 .duration_since(UNIX_EPOCH)
214 .unwrap()
215 .as_secs() as i32;
216 let new_offset = server_time.wrapping_sub(local_now);
217 log::debug!(
218 "[ferogram] time_offset correction: {} → {} (server_time={server_time})",
219 self.time_offset,
220 new_offset
221 );
222 self.time_offset = new_offset;
223 self.last_msg_id = (server_msg_id & !0x3i64).max(self.last_msg_id);
226 }
227
228 pub fn alloc_msg_seqno(&mut self, content_related: bool) -> (i64, i32) {
235 let msg_id = self.next_msg_id();
236 let seqno = if content_related {
237 self.next_seq_no()
238 } else {
239 self.next_seq_no_ncr()
240 };
241 (msg_id, seqno)
242 }
243
244 pub fn pack_body_with_msg_id(&mut self, body: &[u8], content_related: bool) -> (Vec<u8>, i64) {
252 let msg_id = self.next_msg_id();
253 let seq_no = if content_related {
254 self.next_seq_no()
255 } else {
256 self.next_seq_no_ncr()
257 };
258
259 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
260 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
261 buf.extend(self.salt.to_le_bytes());
262 buf.extend(self.session_id.to_le_bytes());
263 buf.extend(msg_id.to_le_bytes());
264 buf.extend(seq_no.to_le_bytes());
265 buf.extend((body.len() as u32).to_le_bytes());
266 buf.extend(body.iter().copied());
267
268 encrypt_data_v2(&mut buf, &self.auth_key);
269 (buf.as_ref().to_vec(), msg_id)
270 }
271
272 pub fn pack_container(&mut self, container_body: &[u8]) -> (Vec<u8>, i64) {
281 self.pack_body_with_msg_id(container_body, false)
282 }
283
284 pub fn pack_body_at_msg_id(&mut self, body: &[u8], msg_id: i64) -> Vec<u8> {
289 let seq_no = self.next_seq_no();
290 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
291 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
292 buf.extend(self.salt.to_le_bytes());
293 buf.extend(self.session_id.to_le_bytes());
294 buf.extend(msg_id.to_le_bytes());
295 buf.extend(seq_no.to_le_bytes());
296 buf.extend((body.len() as u32).to_le_bytes());
297 buf.extend(body.iter().copied());
298 encrypt_data_v2(&mut buf, &self.auth_key);
299 buf.as_ref().to_vec()
300 }
301
302 pub fn pack_serializable<S: ferogram_tl_types::Serializable>(&mut self, call: &S) -> Vec<u8> {
304 let body = call.to_bytes();
305 let msg_id = self.next_msg_id();
306 let seq_no = self.next_seq_no();
307
308 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
309 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
310 buf.extend(self.salt.to_le_bytes());
311 buf.extend(self.session_id.to_le_bytes());
312 buf.extend(msg_id.to_le_bytes());
313 buf.extend(seq_no.to_le_bytes());
314 buf.extend((body.len() as u32).to_le_bytes());
315 buf.extend(body.iter().copied());
316
317 encrypt_data_v2(&mut buf, &self.auth_key);
318 buf.as_ref().to_vec()
319 }
320
321 pub fn pack_serializable_with_msg_id<S: ferogram_tl_types::Serializable>(
323 &mut self,
324 call: &S,
325 ) -> (Vec<u8>, i64) {
326 let body = call.to_bytes();
327 let msg_id = self.next_msg_id();
328 let seq_no = self.next_seq_no();
329 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
330 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
331 buf.extend(self.salt.to_le_bytes());
332 buf.extend(self.session_id.to_le_bytes());
333 buf.extend(msg_id.to_le_bytes());
334 buf.extend(seq_no.to_le_bytes());
335 buf.extend((body.len() as u32).to_le_bytes());
336 buf.extend(body.iter().copied());
337 encrypt_data_v2(&mut buf, &self.auth_key);
338 (buf.as_ref().to_vec(), msg_id)
339 }
340
341 pub fn pack_with_msg_id<R: RemoteCall>(&mut self, call: &R) -> (Vec<u8>, i64) {
343 let body = call.to_bytes();
344 let msg_id = self.next_msg_id();
345 let seq_no = self.next_seq_no();
346 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
347 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
348 buf.extend(self.salt.to_le_bytes());
349 buf.extend(self.session_id.to_le_bytes());
350 buf.extend(msg_id.to_le_bytes());
351 buf.extend(seq_no.to_le_bytes());
352 buf.extend((body.len() as u32).to_le_bytes());
353 buf.extend(body.iter().copied());
354 encrypt_data_v2(&mut buf, &self.auth_key);
355 (buf.as_ref().to_vec(), msg_id)
356 }
357
358 pub fn pack<R: RemoteCall>(&mut self, call: &R) -> Vec<u8> {
360 let body = call.to_bytes();
361 let msg_id = self.next_msg_id();
362 let seq_no = self.next_seq_no();
363
364 let inner_len = 8 + 8 + 8 + 4 + 4 + body.len();
365 let mut buf = DequeBuffer::with_capacity(inner_len, 32);
366 buf.extend(self.salt.to_le_bytes());
367 buf.extend(self.session_id.to_le_bytes());
368 buf.extend(msg_id.to_le_bytes());
369 buf.extend(seq_no.to_le_bytes());
370 buf.extend((body.len() as u32).to_le_bytes());
371 buf.extend(body.iter().copied());
372
373 encrypt_data_v2(&mut buf, &self.auth_key);
374 buf.as_ref().to_vec()
375 }
376
377 pub fn unpack(&self, frame: &mut [u8]) -> Result<DecryptedMessage, DecryptError> {
379 let plaintext = decrypt_data_v2(frame, &self.auth_key).map_err(DecryptError::Crypto)?;
380
381 if plaintext.len() < 32 {
382 return Err(DecryptError::FrameTooShort);
383 }
384
385 let salt = i64::from_le_bytes(plaintext[..8].try_into().unwrap());
386 let session_id = i64::from_le_bytes(plaintext[8..16].try_into().unwrap());
387 let msg_id = i64::from_le_bytes(plaintext[16..24].try_into().unwrap());
388 let seq_no = i32::from_le_bytes(plaintext[24..28].try_into().unwrap());
389 let body_len = u32::from_le_bytes(plaintext[28..32].try_into().unwrap()) as usize;
390
391 if session_id != self.session_id {
392 return Err(DecryptError::SessionMismatch);
393 }
394
395 if msg_id & 1 == 0 {
397 return Err(DecryptError::InvalidMsgId);
398 }
399
400 let server_secs = (msg_id as u64 >> 32) as i64;
402 let now = SystemTime::now()
403 .duration_since(UNIX_EPOCH)
404 .unwrap()
405 .as_secs() as i64;
406 let corrected = now + self.time_offset as i64;
407 let skew = server_secs - corrected;
408 if !(-300..=30).contains(&skew) {
409 return Err(DecryptError::MsgIdTimeWindow);
410 }
411
412 {
414 let mut seen = self.seen_msg_ids.lock().unwrap();
415 if seen.1.contains(&msg_id) {
416 return Err(DecryptError::DuplicateMsgId);
417 }
418 seen.0.push_back(msg_id);
419 seen.1.insert(msg_id);
420 if seen.0.len() > SEEN_MSG_IDS_MAX
421 && let Some(old_id) = seen.0.pop_front()
422 {
423 seen.1.remove(&old_id);
424 }
425 }
426
427 if body_len > 16 * 1024 * 1024 {
429 return Err(DecryptError::FrameTooShort);
430 }
431 if 32 + body_len > plaintext.len() {
432 return Err(DecryptError::FrameTooShort);
433 }
434 if !body_len.is_multiple_of(4) {
436 return Err(DecryptError::FrameTooShort);
437 }
438 let padding = plaintext.len() - 32 - body_len;
440 if padding < 12 {
441 return Err(DecryptError::FrameTooShort);
442 }
443 let body = plaintext[32..32 + body_len].to_vec();
444
445 Ok(DecryptedMessage {
446 salt,
447 session_id,
448 msg_id,
449 seq_no,
450 body,
451 })
452 }
453
454 pub fn auth_key_bytes(&self) -> [u8; 256] {
456 self.auth_key.to_bytes()
457 }
458
459 pub fn session_id(&self) -> i64 {
461 self.session_id
462 }
463
464 pub fn reset_session(&mut self) {
470 let mut rnd = [0u8; 8];
471 getrandom::getrandom(&mut rnd).expect("getrandom");
472 let old_session = self.session_id;
473 self.session_id = i64::from_le_bytes(rnd);
474 self.sequence = 0;
475 self.last_msg_id = 0;
476 log::debug!(
479 "[ferogram] session reset: {:#018x} → {:#018x}",
480 old_session,
481 self.session_id
482 );
483 }
484}
485
486impl EncryptedSession {
487 pub fn decrypt_frame_dedup(
490 auth_key: &[u8; 256],
491 session_id: i64,
492 frame: &mut [u8],
493 seen: &SeenMsgIds,
494 ) -> Result<DecryptedMessage, DecryptError> {
495 let msg = Self::decrypt_frame_with_offset(auth_key, session_id, frame, 0)?;
496 {
497 let mut s = seen.lock().unwrap();
498 if s.1.contains(&msg.msg_id) {
499 return Err(DecryptError::DuplicateMsgId);
500 }
501 s.0.push_back(msg.msg_id);
502 s.1.insert(msg.msg_id);
503 if s.0.len() > SEEN_MSG_IDS_MAX
504 && let Some(old_id) = s.0.pop_front()
505 {
506 s.1.remove(&old_id);
507 }
508 }
509 Ok(msg)
510 }
511
512 pub fn decrypt_frame(
516 auth_key: &[u8; 256],
517 session_id: i64,
518 frame: &mut [u8],
519 ) -> Result<DecryptedMessage, DecryptError> {
520 Self::decrypt_frame_with_offset(auth_key, session_id, frame, 0)
521 }
522
523 pub fn decrypt_frame_with_offset(
526 auth_key: &[u8; 256],
527 session_id: i64,
528 frame: &mut [u8],
529 time_offset: i32,
530 ) -> Result<DecryptedMessage, DecryptError> {
531 let key = AuthKey::from_bytes(*auth_key);
532 let plaintext = decrypt_data_v2(frame, &key).map_err(DecryptError::Crypto)?;
533 if plaintext.len() < 32 {
534 return Err(DecryptError::FrameTooShort);
535 }
536 let salt = i64::from_le_bytes(plaintext[..8].try_into().unwrap());
537 let sid = i64::from_le_bytes(plaintext[8..16].try_into().unwrap());
538 let msg_id = i64::from_le_bytes(plaintext[16..24].try_into().unwrap());
539 let seq_no = i32::from_le_bytes(plaintext[24..28].try_into().unwrap());
540 let body_len = u32::from_le_bytes(plaintext[28..32].try_into().unwrap()) as usize;
541 if sid != session_id {
542 return Err(DecryptError::SessionMismatch);
543 }
544 if msg_id & 1 == 0 {
546 return Err(DecryptError::InvalidMsgId);
547 }
548 let server_secs = (msg_id as u64 >> 32) as i64;
550 let now = SystemTime::now()
551 .duration_since(UNIX_EPOCH)
552 .unwrap()
553 .as_secs() as i64;
554 let corrected = now + time_offset as i64;
555 let skew = server_secs - corrected;
556 if !(-300..=30).contains(&skew) {
557 return Err(DecryptError::MsgIdTimeWindow);
558 }
559 if body_len > 16 * 1024 * 1024 {
561 return Err(DecryptError::FrameTooShort);
562 }
563 if 32 + body_len > plaintext.len() {
564 return Err(DecryptError::FrameTooShort);
565 }
566 if !body_len.is_multiple_of(4) {
568 return Err(DecryptError::FrameTooShort);
569 }
570 let padding = plaintext.len() - 32 - body_len;
572 if padding < 12 {
573 return Err(DecryptError::FrameTooShort);
574 }
575 let body = plaintext[32..32 + body_len].to_vec();
576 Ok(DecryptedMessage {
577 salt,
578 session_id: sid,
579 msg_id,
580 seq_no,
581 body,
582 })
583 }
584}