Skip to main content

layer_crypto/
obfuscated.rs

1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4// NOTE:
5// The "Layer" project is no longer maintained or supported.
6// Its original purpose for personal SDK/APK experimentation and learning
7// has been fulfilled.
8//
9// Please use Ferogram instead:
10// https://github.com/ankit-chaubey/ferogram
11// Ferogram will receive future updates and development, although progress
12// may be slower.
13//
14// Ferogram is an async Telegram MTProto client library written in Rust.
15// Its implementation follows the behaviour of the official Telegram clients,
16// particularly Telegram Desktop and TDLib, and aims to provide a clean and
17// modern async interface for building Telegram clients and tools.
18
19//! AES-256-CTR cipher for MTProto transport obfuscation.
20//!
21//! Telegram's obfuscated transport encrypts the entire byte stream with two
22//! separate AES-256-CTR instances (TX and RX) whose keys are derived from a
23//! random 64-byte init header sent at connection start.
24//!
25//! Key derivation from the 64-byte `init` buffer:
26//! ```text
27//! TX key = init[8..40]           TX IV = init[40..56]
28//! RX key = reverse(init)[8..40]  RX IV = reverse(init)[40..56]
29//! ```
30
31#[allow(deprecated)]
32use aes::cipher::{KeyIvInit, StreamCipher, generic_array::GenericArray};
33
34/// AES-256-CTR stream cipher pair for MTProto obfuscated transport.
35pub struct ObfuscatedCipher {
36    #[allow(deprecated)]
37    rx: ctr::Ctr128BE<aes::Aes256>,
38    #[allow(deprecated)]
39    tx: ctr::Ctr128BE<aes::Aes256>,
40}
41
42impl ObfuscatedCipher {
43    /// Build cipher state from the 64-byte random init buffer.
44    #[allow(deprecated)]
45    pub fn new(init: &[u8; 64]) -> Self {
46        let rev: Vec<u8> = init.iter().copied().rev().collect();
47        Self {
48            rx: ctr::Ctr128BE::<aes::Aes256>::new(
49                GenericArray::from_slice(&rev[8..40]),
50                GenericArray::from_slice(&rev[40..56]),
51            ),
52            tx: ctr::Ctr128BE::<aes::Aes256>::new(
53                GenericArray::from_slice(&init[8..40]),
54                GenericArray::from_slice(&init[40..56]),
55            ),
56        }
57    }
58
59    /// Build cipher from explicit key/IV pairs (used when MTProxy secret
60    /// mixing has already been applied externally via SHA-256).
61    #[allow(deprecated)]
62    pub fn from_keys(
63        tx_key: &[u8; 32],
64        tx_iv: &[u8; 16],
65        rx_key: &[u8; 32],
66        rx_iv: &[u8; 16],
67    ) -> Self {
68        Self {
69            tx: ctr::Ctr128BE::<aes::Aes256>::new(
70                GenericArray::from_slice(tx_key),
71                GenericArray::from_slice(tx_iv),
72            ),
73            rx: ctr::Ctr128BE::<aes::Aes256>::new(
74                GenericArray::from_slice(rx_key),
75                GenericArray::from_slice(rx_iv),
76            ),
77        }
78    }
79
80    /// Encrypt outgoing bytes in-place (TX direction).
81    pub fn encrypt(&mut self, buf: &mut [u8]) {
82        self.tx.apply_keystream(buf);
83    }
84
85    /// Decrypt incoming bytes in-place (RX direction).
86    pub fn decrypt(&mut self, buf: &mut [u8]) {
87        self.rx.apply_keystream(buf);
88    }
89}