Skip to main content

layer_crypto/
obfuscated.rs

1//! AES-256-CTR cipher for MTProto transport obfuscation.
2//!
3//! Telegram's obfuscated transport encrypts the entire byte stream with two
4//! separate AES-256-CTR instances (TX and RX) whose keys are derived from a
5//! random 64-byte init header sent at connection start.
6//!
7//! Key derivation from the 64-byte `init` buffer:
8//! ```text
9//! TX key = init[8..40]           TX IV = init[40..56]
10//! RX key = reverse(init)[8..40]  RX IV = reverse(init)[40..56]
11//! ```
12
13#[allow(deprecated)]
14use aes::cipher::{generic_array::GenericArray, KeyIvInit, StreamCipher};
15
16/// AES-256-CTR stream cipher pair for MTProto obfuscated transport.
17pub struct ObfuscatedCipher {
18    #[allow(deprecated)]
19    rx: ctr::Ctr128BE<aes::Aes256>,
20    #[allow(deprecated)]
21    tx: ctr::Ctr128BE<aes::Aes256>,
22}
23
24impl ObfuscatedCipher {
25    /// Build cipher state from the 64-byte random init buffer.
26    #[allow(deprecated)]
27    pub fn new(init: &[u8; 64]) -> Self {
28        let rev: Vec<u8> = init.iter().copied().rev().collect();
29        Self {
30            rx: ctr::Ctr128BE::<aes::Aes256>::new(
31                GenericArray::from_slice(&rev[8..40]),
32                GenericArray::from_slice(&rev[40..56]),
33            ),
34            tx: ctr::Ctr128BE::<aes::Aes256>::new(
35                GenericArray::from_slice(&init[8..40]),
36                GenericArray::from_slice(&init[40..56]),
37            ),
38        }
39    }
40
41    /// Encrypt outgoing bytes in-place (TX direction).
42    pub fn encrypt(&mut self, buf: &mut [u8]) {
43        self.tx.apply_keystream(buf);
44    }
45
46    /// Decrypt incoming bytes in-place (RX direction).
47    pub fn decrypt(&mut self, buf: &mut [u8]) {
48        self.rx.apply_keystream(buf);
49    }
50}