Skip to main content

ferogram_crypto/
obfuscated.rs

1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3//
4// ferogram: async Telegram MTProto client in Rust
5// https://github.com/ankit-chaubey/ferogram
6//
7//
8// If you use or modify this code, keep this notice at the top of your file
9// and include the LICENSE-MIT or LICENSE-APACHE file from this repository:
10// https://github.com/ankit-chaubey/ferogram
11
12//! AES-256-CTR cipher for MTProto transport obfuscation.
13//!
14//! Telegram's obfuscated transport encrypts the entire byte stream with two
15//! separate AES-256-CTR instances (TX and RX) whose keys are derived from a
16//! random 64-byte init header sent at connection start.
17//!
18//! Key derivation from the 64-byte `init` buffer:
19//! ```text
20//! TX key = init[8..40]           TX IV = init[40..56]
21//! RX key = reverse(init)[8..40]  RX IV = reverse(init)[40..56]
22//! ```
23
24#[allow(deprecated)]
25use aes::cipher::{KeyIvInit, StreamCipher, generic_array::GenericArray};
26
27/// AES-256-CTR stream cipher pair for MTProto obfuscated transport.
28pub struct ObfuscatedCipher {
29    #[allow(deprecated)]
30    rx: ctr::Ctr128BE<aes::Aes256>,
31    #[allow(deprecated)]
32    tx: ctr::Ctr128BE<aes::Aes256>,
33}
34
35impl ObfuscatedCipher {
36    /// Build cipher state from the 64-byte random init buffer.
37    #[allow(deprecated)]
38    pub fn new(init: &[u8; 64]) -> Self {
39        let rev: Vec<u8> = init.iter().copied().rev().collect();
40        Self {
41            rx: ctr::Ctr128BE::<aes::Aes256>::new(
42                GenericArray::from_slice(&rev[8..40]),
43                GenericArray::from_slice(&rev[40..56]),
44            ),
45            tx: ctr::Ctr128BE::<aes::Aes256>::new(
46                GenericArray::from_slice(&init[8..40]),
47                GenericArray::from_slice(&init[40..56]),
48            ),
49        }
50    }
51
52    /// Build cipher from explicit key/IV pairs (used when MTProxy secret
53    /// mixing has already been applied externally via SHA-256).
54    #[allow(deprecated)]
55    pub fn from_keys(
56        tx_key: &[u8; 32],
57        tx_iv: &[u8; 16],
58        rx_key: &[u8; 32],
59        rx_iv: &[u8; 16],
60    ) -> Self {
61        Self {
62            tx: ctr::Ctr128BE::<aes::Aes256>::new(
63                GenericArray::from_slice(tx_key),
64                GenericArray::from_slice(tx_iv),
65            ),
66            rx: ctr::Ctr128BE::<aes::Aes256>::new(
67                GenericArray::from_slice(rx_key),
68                GenericArray::from_slice(rx_iv),
69            ),
70        }
71    }
72
73    /// Encrypt outgoing bytes in-place (TX direction).
74    pub fn encrypt(&mut self, buf: &mut [u8]) {
75        self.tx.apply_keystream(buf);
76    }
77
78    /// Decrypt incoming bytes in-place (RX direction).
79    pub fn decrypt(&mut self, buf: &mut [u8]) {
80        self.rx.apply_keystream(buf);
81    }
82}