How the MTProto Protocol Is Implemented in TelegramCore for iOS: A Deep Dive into MtProtoKit

The MTProto protocol in Telegram-iOS is implemented through the MtProtoKit sub-module, featuring a central MTProto state machine in MTProto.m that manages sessions, auth keys, and transport layers, while modular message services handle time synchronization and message resend operations.

The MTProto protocol serves as the encrypted transport layer powering all API calls, message exchanges, and file uploads within the Telegram ecosystem. In the TelegramMessenger/Telegram-iOS repository, this implementation lives inside the MtProtoKit sub-module, written in Objective-C and designed to isolate cryptographic operations, network transport, and session management into discrete, testable components.

Core Architecture of MTProto in Telegram-iOS

The implementation follows a layered architecture where a central state machine coordinates between persistence, transport, and cryptographic services. Understanding how these components interact is essential for working with the low-level protocol code.

The MTProto State Machine

At the heart of the implementation lies the MTProto class defined in submodules/MtProtoKit/Sources/MTProto.m. This object maintains an internal bitfield _mtState (enumerated as MTProtoState) that tracks the connection lifecycle through specific flags:

  • AwaitingDatacenterScheme – Indicates no transport scheme is known, triggering a request to MTContext for endpoint details.
  • AwaitingDatacenterAuthorization – Signals that authentication keys must be generated or retrieved.
  • AwaitingTimeFixAndSalts – Activates when client-server clock drift requires correction or salt sets need replenishment.
  • Stopped and Paused – Control user-initiated shutdown or network suspension.

State transitions occur through the setMtState: method, which automatically notifies registered message services and delegates via mtProtoServiceTasksStateChanged:isPerformingServiceTasks:. When you call resume on an MTProto instance, the method clears the paused flag, instantiates a fresh MTTcpTransport if needed, and immediately invokes requestTransportTransaction to begin queued operations.

MTProtoEngine and Persistence Layer

The MTProtoEngine class in MTProtoEngine.m acts as a lightweight wrapper around the MTProtoPersistenceInterface protocol. Rather than handling raw SQLite operations, the engine delegates storage concerns to the host application, which implements methods such as:

  • authInfoForDatacenterWithId:selector: – Retrieves stored authentication keys and salts.
  • updateAuthInfoForDatacenterWithId:authInfo:selector: – Persists new keys after successful Diffie-Hellman exchanges.

This separation allows MTProto instances to remain stateless regarding storage while ensuring that sensitive cryptographic material survives application restarts.

Transport Layer Implementation

Network communication is handled by MTTcpTransport (see MTTcpTransport.m), which manages low-level socket connections, proxy support, and automatic reconnection logic. When resetTransport is called, the system builds a transport instance using MTTransportScheme objects provided by MTContext, determining whether to use SOCKS5 proxies with MTProto-proxy secrets or extended padding configurations.

Cryptographic and Session Management

The MTProto protocol relies on precise cryptographic primitives and monotonic message identification to ensure security and delivery guarantees.

Message Encryption and AES-IGE

Outgoing messages follow a strict preparation pipeline before transmission:

  1. Message ID Generation – MTSessionInfo generates monotonic identifiers via generateClientMessageId: and sequence numbers through takeSeqNo:.
  2. Auth Key Resolution – getAuthKeyForCurrentScheme: retrieves either persistent or ephemeral keys based on the useTempAuthKeys configuration.
  3. Payload Construction – Raw data is wrapped in an MTPreparedMessage object containing the current salt from MTDatacenterSaltInfo.
  4. Encryption – The static helper +[MTProto _manuallyEncryptedMessage:messageId:authKey:] applies AES-IGE encryption with 16-byte padding.

Cryptographic utilities reside in MTEncryption.m, which handles Diffie-Hellman key exchange, proxy secret parsing, and optional extended padding for MTProto-proxy connections.

Session Info and Message IDs

The MTSessionInfo class ensures that every message carries a unique, temporally ordered identifier. This prevents replay attacks and enables the server to detect gaps in message sequences, triggering resend requests when necessary.

Message Services and Protocol Operations

TelegramCore implements auxiliary protocol functions through pluggable message services that conform to the MTMessageService protocol. These services are added via addMessageService: and execute alongside user transactions.

Time Synchronization Service

When the state machine enters AwaitingTimeFixAndSalts, MTProto automatically instantiates MTTimeSyncMessageService (see MTTimeSyncMessageService.m). This service transmits msgs_state_req and msgs_state_info handshakes to calculate clock offset and retrieve fresh salts. The service updates the internal MTTimeFixContext and removes itself upon completion, allowing normal message flow to resume.

Resend and Reliability Services

If the server reports a missing message ID, the core creates MTResendMessageService (see MTResendMessageService.m) through the requestMessageWithId: method. This service constructs a msg_resend_req TL object and automatically destroys itself after the server acknowledges the retransmission, ensuring reliable delivery without blocking the main transport queue.

These services are processed within transportReadyForTransaction:, which aggregates pending operations into a single msg_container respecting MTMaxContainerSize limits and prioritizing high-priority messages.

Practical Implementation Examples

Initializing an MTProto Connection

The following Objective-C snippet demonstrates the standard initialization sequence for establishing a connection to datacenter 2:

#import <MtProtoKit/MTProtoKit.h>

@interface MyController () <MTProtoDelegate>
@property (nonatomic, strong) MTProto *proto;
@end

@implementation MyController

- (void)setupProto {
    // 1️⃣ Context – holds datacenter list, proxy, etc.
    MTContext *context = [[MTContext alloc] initWithApiEnvironment:[MTApiEnvironment sharedInstance]];

    // 2️⃣ Persistence – simple SQLite wrapper (provided by the app)
    id<MTProtoPersistenceInterface> persistence = [[MTProtoPersistence alloc] initWithPath:@"/tmp/mtproto.db"];

    // 3️⃣ Engine – ties persistence to the core (optional, used by higher‑level layers)
    MTProtoEngine *engine = [[MTProtoEngine alloc] initWithPersistenceInterface:persistence];

    // 4️⃣ Create the core MTProto instance for DC 2 (main Telegram DC)
    self.proto = [[MTProto alloc] initWithContext:context
                                      datacenterId:2
                            usageCalculationInfo:nil
                                requiredAuthToken:nil
                       authTokenMasterDatacenterId:0];
    self.proto.delegate = self;

    // 5️⃣ Start the connection
    [self.proto resume];
}

#pragma mark - MTProtoDelegate

- (void)mtProtoConnectionStateChanged:(MTProto *)mtProto state:(MTProtoConnectionState *)state {
    NSLog(@"Connection state – connected: %d, proxy issues: %d", state.isConnected, state.proxyHasConnectionIssues);
}

- (void)mtProtoNetworkAvailabilityChanged:(MTProto *)mtProto isNetworkAvailable:(bool)isNetworkAvailable {
    NSLog(@"Network available: %d", isNetworkAvailable);
}
@end

Sending an RPC Request

To transmit a ping request (or any TL-method), wrap the payload in an MTOutgoingMessage and attach it via a transaction:

#import <MtProtoKit/MTMessageTransaction.h>
#import <MtProtoKit/MTOutgoingMessage.h>
#import <MtProtoKit/MTBuffer.h>

- (void)sendPing {
    // Build the raw TL‑object for “ping#7abe77ec ping_id:long = Pong”
    MTBuffer *buf = [[MTBuffer alloc] init];
    [buf appendInt32:0x7abe77ec];                     // constructor id
    int64_t pingId = (int64_t)arc4random();          // random 64‑bit ping_id
    [buf appendInt64:pingId];

    MTOutgoingMessage *msg = [[MTOutgoingMessage alloc] initWithData:buf.data
                                                            metadata:@"ping"
                                          additionalDebugDescription:nil
                                                shortMetadata:@"ping"];
    msg.requiresConfirmation = true; // we want a server response

    // Wrap the outgoing message in a transaction
    MTMessageTransaction *tx = [[MTMessageTransaction alloc] initWithMessagePayload:@[msg]
                                                                          prepared:nil
                                                                            failed:nil
                                                                       completion:^(NSDictionary *msgIdToTxId,
                                                                                    NSDictionary *msgIdToPrepared,
                                                                                    NSDictionary *msgIdToQuickAck) {
        NSLog(@"Ping transaction completed");
    }];

    // Attach a lightweight service that will forward the response to us
    [self.proto addMessageService:tx];
    // Request the transport to flush the pending messages
    [self.proto requestTransportTransaction];
}

Handling Lost Messages

When the server indicates a missing message ID, trigger the automatic resend mechanism:

- (void)handleMissingMessageId:(int64_t)msgId {
    // The core offers a tiny helper – it will create the resend service for us
    [self.proto requestMessageWithId:msgId];
}

This call spawns an MTResendMessageService that constructs and transmits a msg_resend_req without manual intervention.

Summary

  • MtProtoKit serves as the dedicated sub-module implementing the MTProto protocol in Telegram-iOS, isolating network, cryptographic, and state management concerns.
  • The MTProto class in MTProto.m operates as a central state machine tracking connection status through bitfield flags like AwaitingDatacenterAuthorization and AwaitingTimeFixAndSalts.
  • MTProtoEngine delegates persistence to the host application via the MTProtoPersistenceInterface, ensuring auth keys and salts survive app restarts.
  • MTTcpTransport manages raw TCP sockets, proxy configurations, and automatic reconnection logic.
  • Message services such as MTTimeSyncMessageService and MTResendMessageService provide modular, self-contained implementations of protocol-level reliability features.
  • All outgoing traffic undergoes AES-IGE encryption via MTEncryption.m after being prepared with unique message IDs generated by MTSessionInfo.

Frequently Asked Questions

What is the role of MTProtoKit in Telegram-iOS?

MTProtoKit is the dedicated sub-module that encapsulates the entire MTProto protocol implementation, providing the cryptographic primitives, transport layers, and session management required for secure communication with Telegram servers. According to the TelegramMessenger/Telegram-iOS source code, this module lives in submodules/MtProtoKit and exposes Objective-C interfaces for initializing connections, managing auth keys, and encrypting payloads.

How does Telegram-iOS handle message encryption in MTProto?

Messages are encrypted using AES-IGE mode with auth keys retrieved via getAuthKeyForCurrentScheme:, as implemented in MTProto.m. The encryption process involves generating unique message IDs through MTSessionInfo, wrapping payloads in MTPreparedMessage objects with current salts, and calling the static helper +[MTProto _manuallyEncryptedMessage:messageId:authKey:] which applies the cipher with 16-byte padding.

What happens when the client and server clocks are out of sync?

The MTTimeSyncMessageService automatically initiates a time-fix handshake when the MTProto state machine enters AwaitingTimeFixAndSalts. This service, defined in MTTimeSyncMessageService.m, transmits msgs_state_req and msgs_state_info messages to calculate clock offset and retrieve fresh salts, updating the internal MTTimeFixContext before removing itself from the active service queue.

How does the protocol ensure reliable message delivery?

Reliability is ensured through the MTResendMessageService and sequence number tracking, as defined in MTResendMessageService.m. When the server reports a missing message ID, the requestMessageWithId: method spawns a temporary service that constructs a msg_resend_req and automatically destroys itself upon server acknowledgment, ensuring gaps in message sequences are filled without blocking other operations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →