⚡ layer
A modular, from-scratch Rust implementation of the Telegram MTProto protocol.
⚠️ Use at your own risk! 😁
This is an experimental, educational project built from scratch to understand Telegram's MTProto protocol at the lowest level. It is not production-ready. For serious projects, use grammers.
✨ About
layer is a hand-written, bottom-up Rust implementation of the Telegram MTProto protocol. Every component — from the TL schema parser, to the AES-IGE cipher, to the Diffie-Hellman key exchange — is written and owned by this project.
Built purely for learning and experimentation: to understand what happens inside a Telegram client at the raw byte level, all the way from TCP to a high-level API call.
💡 Inspiration & Credits
This project is heavily inspired by and based on the architecture of grammers by Lonami.
🙏 A huge Thank You to Lonami for building grammers — an incredibly well-structured, readable library that made MTProto's internals approachable. Without grammers, this project simply would not exist. Thank you for the awesome library! 🎉
The flow, naming conventions, SRP 2FA math, DC migration logic, session persistence, and overall architecture mirror grammers closely — intentionally, as a learning exercise.
Written by: Ankit Chaubey
Inspired by: grammers by Lonami
🏗️ Crate Structure
layer/
├── layer-tl-parser/ ── Parses .tl schema text → AST
├── layer-tl-gen/ ── AST → Rust source code (runs at build time)
├── layer-tl-types/ ── Auto-generated types, functions & enums (Layer 222)
├── layer-crypto/ ── AES-IGE, RSA, SHA, auth key derivation
├── layer-mtproto/ ── MTProto session, DH exchange, message framing, transport
├── layer-client/ ── High-level Client: auth, 2FA, send messages
├── layer-connect/ ── Demo binary: raw DH + getConfig
├── layer-app/ ── Binary: interactive login + send a message
└── layer/ ── Convenience facade re-exporting everything
The code generation pipeline runs automatically at build time:
api.tl / mtproto.tl
│
▼
layer-tl-parser ── .tl text → Definition AST
│
▼
layer-tl-gen ── AST → Rust source (inside build.rs)
│
▼
layer-tl-types ── compiled structs, enums, RemoteCall impls
│
▼
layer-mtproto ── Session + EncryptedSession + Transport
│
▼
layer-client ── Client::connect / sign_in / send_message
🚀 Quick Start
Add to your Cargo.toml:
[]
= { = "https://github.com/ankit-chaubey/layer" }
Basic usage:
use ;
Or just run the included app — fill in your credentials at the top of layer-app/src/main.rs:
✅ What's Implemented
🔐 Cryptography (layer-crypto)
- AES-IGE encryption / decryption (MTProto 2.0)
- RSA encryption with Telegram's public keys
- SHA-1 and SHA-256 hashing utilities
- Auth key derivation from nonce material
- PQ factorization (Pollard's rho algorithm)
- Diffie-Hellman shared secret computation
📡 MTProto Layer (layer-mtproto)
- Full 3-step Diffie-Hellman key exchange handshake
- MTProto 2.0 encrypted sessions (AES-IGE + auth key)
- Proper message framing (salt, session_id, msg_id, seq_no)
- Abridged TCP transport
- Server salt tracking and correction
-
msg_container(multi-message) unpacking - gzip-packed response decompression
- Pong, bad_server_salt, new_session_created handling
📦 TL Type System
- Full
.tlschema parser (layer-tl-parser) - Build-time Rust code generation (
layer-tl-gen) - All Layer 222 constructors — 2,295 definitions (
layer-tl-types) -
Serializable/Deserializabletraits for all types -
RemoteCalltrait for all RPC functions -
From<types::T> for enums::Econversion impls - Optional:
Debug,serde,name_for_id(u32)
👤 High-Level Client (layer-client)
-
Client::connect()— TCP + DH +initConnection(GetConfig) -
Client::load_or_connect()— reuses saved session if available -
Client::save_session()— persists auth key + DC table to disk -
Client::is_authorized()— probes withupdates.getState -
Client::request_login_code()— sends code via SMS or Telegram app -
Client::sign_in()— phone code login -
Client::check_password()— full SRP 2FA (same math as grammers) -
Client::send_message()— sends text to any peer ("me", username, phone) -
Client::invoke()— rawRemoteCallescape hatch for any API method - DC migration (
PHONE_MIGRATE_X,USER_MIGRATE_X) - RPC error propagation (code + message string)
❌ What's NOT Implemented
- Async / Tokio — fully synchronous, blocking I/O
- Update handling — no event loop, no
iter_messages(), no update callbacks - Media — no file upload, no file download, no thumbnails
- Dialogs / contacts — no
get_dialogs(),get_entity() - Automatic flood wait —
FLOOD_WAIT_Xerrors surface asErr, not retried - MTProxy / obfuscation — no proxy support
- Multiple accounts — single session only
- Channels / groups — only basic peer resolution for
send_message - Bots — no bot-specific auth flow
- WebSocket transport — TCP only
🆚 layer vs grammers
| Feature | layer | grammers |
|---|---|---|
| Async / non-blocking | ❌ sync | ✅ Tokio |
| DH key exchange | ✅ | ✅ |
| 2FA / SRP | ✅ | ✅ |
| Session persistence | ✅ | ✅ |
| DC migration | ✅ | ✅ |
| Send message | ✅ | ✅ |
| TL code generation | ✅ custom | ✅ grammers-tl-gen |
| Update / event handling | ❌ | ✅ |
| Media upload/download | ❌ | ✅ |
| Dialogs / contacts | ❌ | ✅ |
| Flood wait handling | ❌ | ✅ |
| MTProxy support | ❌ | ✅ |
| Production ready | ❌ | ✅ |
| Purpose | Learning & experiment | Real projects |
Use grammers for anything real. Use layer to understand how MTProto actually works.
🔧 Feature Flags
layer-tl-types exposes optional compile-time features:
| Feature | Default | Description |
|---|---|---|
tl-api |
✅ | High-level Telegram API schema (api.tl) |
tl-mtproto |
❌ | Low-level MTProto schema (mtproto.tl) |
impl-debug |
✅ | #[derive(Debug)] on all generated types |
impl-from-type |
✅ | From<types::T> for enums::E |
impl-from-enum |
✅ | TryFrom<enums::E> for types::T |
name-for-id |
❌ | name_for_id(u32) -> Option<&'static str> |
impl-serde |
❌ | serde::Serialize / Deserialize on all types |
deserializable-functions |
❌ | Deserializable on RPC function types (server use) |
[]
= { = "...", = ["impl-serde", "name-for-id"] }
📐 Updating to a New TL Layer
- Replace
layer-tl-types/tl/api.tlwith the new schema file. - Update the
// LAYER Ncomment on the first line. - Run
cargo build— all types regenerate automatically. That's it.
🧪 Tests
📄 License
Licensed under either of, at your option:
- MIT License — see LICENSE-MIT
- Apache License, Version 2.0 — see LICENSE-APACHE
👤 Author
Ankit Chaubey
GitHub: github.com/ankit-chaubey
🙏 Acknowledgements
-
Lonami — for grammers. The architecture, design decisions, SRP math, and session handling in this project are all directly inspired by grammers. It's a fantastic library and an even better learning resource. Thank you for making it open source! 🎉
-
Telegram — for the detailed MTProto specification.
📦 Publishing to crates.io
The publishable crates (in dependency order) are:
layer-tl-parser → layer-tl-gen → layer-tl-types → layer-crypto
↓
layer-mtproto
↓
layer-client
↓
layer
One-time setup:
# 1. Init git (crates.io requires a clean git repo)
# 2. Login with your crates.io API token
Publish in this exact order — each crate must finish uploading before the next:
# 1. No layer deps
# 2. Depends on layer-tl-parser
# 3. Depends on layer-tl-parser + layer-tl-gen (build-deps)
# 4. No layer deps
# 5. Depends on layer-tl-types + layer-crypto
# 6. Depends on layer-tl-types + layer-mtproto
# 7. Facade — depends on everything above
layer-appandlayer-connectare markedpublish = false— they are skipped automatically.
Dry run first to catch issues without uploading:
# repeat for each crate
Bump the version for future releases (all crates share the workspace version):
# 1. Edit `version = "0.1.0"` → `"0.2.0"` in the root Cargo.toml
# 2. Commit the change
&&
# 3. Publish in the same order above
layer — because sometimes you have to build it yourself to truly understand it.