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