<div align="center">
<img src="https://raw.githubusercontent.com/ankit-chaubey/layer/main/docs/images/crate-mtproto-banner.svg" alt="layer-mtproto" width="100%" />
# π‘ layer-mtproto
**MTProto 2.0 session management, DH key exchange, and message framing for Rust.**
[](https://crates.io/crates/layer-mtproto)
[](https://docs.rs/layer-mtproto)
[](#license)
[](https://www.rust-lang.org/)
[](https://core.telegram.org/mtproto)
*A complete, from-scratch implementation of Telegram's MTProto 2.0 session layer.*
</div>
---
## π¦ Installation
```toml
[dependencies]
layer-mtproto = "0.4.6"
layer-tl-types = { version = "0.4.6", features = ["tl-mtproto"] }
```
---
## β¨ What It Does
`layer-mtproto` implements the full MTProto 2.0 session layer β the encrypted tunnel through which all Telegram API calls travel. This is the core protocol machinery that sits between your application code and the TCP socket.
It handles:
- π€ **3-step DH key exchange** β deriving a shared auth key from scratch
- π **Encrypted sessions** β packing/unpacking MTProto 2.0 messages with AES-IGE
- π¦ **Message framing** β salt, session_id, message_id, sequence numbers
- ποΈ **Containers & compression** β `msg_container`, `gzip_packed` responses
- π§ **Salt management** β server salt tracking and auto-correction
- π **Session state** β time offset, sequence number, salt rotation
- π **Error recovery** β `bad_msg_notification`, `bad_server_salt`, `msg_resend_req`
- β
**Acknowledgements** β `MsgsAck` with a pending-ack queue
---
## ποΈ Architecture
```
Application (layer-client)
β
βΌ
EncryptedSession β encrypt/decrypt, pack/unpack
β
βΌ
Authentication β 3-step DH handshake (step1 β step2 β step3 β finish)
β
βΌ
layer-crypto β AES-IGE, RSA, SHA, Diffie-Hellman
β
βΌ
TCP Socket
```
---
## π Core Types
### `EncryptedSession`
Manages the live MTProto session after a key has been established.
```rust
use layer_mtproto::EncryptedSession;
// Create from a completed DH handshake
let session = EncryptedSession::new(auth_key, first_salt, time_offset);
// Pack a RemoteCall into encrypted wire bytes
let wire_bytes = session.pack(&my_request)?;
// Pack any Serializable directly (bypasses RemoteCall bound)
let wire_bytes = session.pack_serializable(&raw_obj)?;
// Unpack an encrypted response from the server
let msg = session.unpack(&mut raw_bytes)?;
println!("msg_id={}, body_len={}", msg.msg_id, msg.body.len());
```
---
### `authentication` β 3-Step DH Key Exchange
The full MTProto DH handshake as specified by Telegram:
```rust
use layer_mtproto::authentication as auth;
// Step 1 β req_pq_multi: get the server's PQ
let (req1, state1) = auth::step1()?;
// ... send req1 over the wire, receive res_pq ...
// Step 2 β req_DH_params: send our DH parameters
let (req2, state2) = auth::step2(state1, res_pq)?;
// ... send req2, receive server_DH_params ...
// Step 3 β set_client_DH_params: send our client DH
let (req3, state3) = auth::step3(state2, dh_params)?;
// ... send req3, receive dh_answer ...
// Finish β derive the auth key from the completed handshake
let done = auth::finish(state3, dh_answer)?;
// done.auth_key β [u8; 256] β the shared secret
// done.first_salt β i64 β first server salt to use
// done.time_offset β i32 β clock skew relative to server
```
---
### `Message`
A decoded MTProto message as returned by `session.unpack()`:
```rust
pub struct Message {
pub msg_id: i64,
pub seq_no: i32,
pub salt: i64,
pub body: Vec<u8>, // raw TL bytes of the inner object
}
```
The `body` bytes are deserialized by `layer-client` using `layer-tl-types`'s `Deserializable` trait.
---
### `Session` (plain / unencrypted)
Used only for sending the initial DH handshake messages before an auth key exists:
```rust
use layer_mtproto::Session;
let mut plain = Session::new();
// Pack a plaintext MTProto message
let framed = plain.pack_plain(&my_handshake_request)?;
```
---
## π What's Inside
### Encryption Details
- AES-IGE encryption/decryption via `layer-crypto`
- `msg_key` = SHA-256 of `(auth_key[88..120] || plaintext)` for clientβserver
- `msg_key` = SHA-256 of `(auth_key[96..128] || plaintext)` for serverβclient
- The 256-byte auth key is split into sub-keys for encryption, MAC, and padding
### Message Framing
Every outgoing MTProto message has this structure:
```
server_salt (8 bytes) β current server salt
session_id (8 bytes) β random, stable for this session's lifetime
message_id (8 bytes) β Unix time * 2^32, monotonically increasing
seq_no (4 bytes) β content-related message counter
message_length (4 bytes) β length of payload in bytes
payload (N bytes) β serialized TL object
padding (M bytes) β 12β1024 random bytes so total % 16 == 0
```
Wrapped in a 32-byte `msg_key` prefix after encryption.
### Containers & Compression
- `msg_container` (TL ID `0x73f1f8dc`) β wraps multiple logical messages in one TCP frame; both packing and unpacking are supported
- `gzip_packed` (TL ID `0x3072cfa1`) β response bodies are decompressed automatically; outgoing large requests are optionally compressed
### Salt & Time Management
- `bad_server_salt` β session records the corrected salt and automatically resets
- `future_salts` prefetch β salts are requested in advance; session rotates before expiry
- `time_offset` correction β all outgoing `msg_id` values are adjusted for clock skew
### Error Recovery
- `bad_msg_notification` β messages with wrong `msg_id`, `seq_no`, or `session_id` are resent with corrected framing
- `seq_no` auto-correction for error codes 32 (seq_no too low) and 33 (seq_no too high)
- `msg_resend_req` β fulfils resend requests by replaying from a sent-body cache
### Acknowledgements
- Received content-related messages accumulate in a `pending_ack` list
- The `flush_acks()` call bundles them into a single `MsgsAck` message
- `layer-client` flushes pending ACKs on a timer and before each outgoing call
---
## π Part of the layer stack
```
layer-client
βββ layer-mtproto β you are here
βββ layer-tl-types (tl-mtproto feature)
βββ layer-crypto
```
---
## π License
Licensed under either of, at your option:
- **MIT License** β see [LICENSE-MIT](../LICENSE-MIT)
- **Apache License, Version 2.0** β see [LICENSE-APACHE](../LICENSE-APACHE)
---
## π€ Author
**Ankit Chaubey**
[github.com/ankit-chaubey](https://github.com/ankit-chaubey) Β· [ankitchaubey.in](https://ankitchaubey.in) Β· [ankitchaubey.dev@gmail.com](mailto:ankitchaubey.dev@gmail.com)
π¦ [github.com/ankit-chaubey/layer](https://github.com/ankit-chaubey/layer)