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}