Эта страница посвящена базовому уровню шифрования MTProto, используемому для облачных чатов (шифрование сервер-клиент). См. также:
- Секретные чаты, конец-в-конец шифрования
- Сквозные зашифрованные голосовые вызовы
Общее Описание
Протокол предназначен для доступа к серверному API из приложений, работающих на мобильных устройствах. Следует подчеркнуть, что веб-браузер не является таким приложением.
Протокол разделён на три практически независимых компонента:
- Компонент высокого уровня (язык запросов API): определяет метод, с помощью которого запросы и ответы API преобразуются в двоичные сообщения.
- Криптографический (авторизационный) компонент: определяет метод, с помощью которого сообщения шифруются перед передачей по транспортному протоколу.
- Транспортный компонент: определяет метод передачи сообщений клиентом и сервером по какому-либо другому существующему сетевому протоколу (например, HTTP, HTTPS, WS (plain websockets), WSS (websockets over HTTPS), TCP, UDP).
Начиная с версии 4.6, основные клиенты Telegram используют MTProto 2.0, описанный в этой статье. MTProto v1. 0 (описанный
здесь для справки) устарел и в настоящее время постепенно выводится из эксплуатации.
Краткий обзор компонентов
Высокоуровневый компонент (язык запросов RPC/API)
С точки зрения высокоуровневого компонента клиент и сервер обмениваются сообщениями внутри сеанса. Сеанс подключается к клиентскому устройству (точнее, к приложению), а не к определенному WebSocket/http/https/tcp-соединению. Кроме того, каждый сеанс привязывается к идентификатору ключа пользователя, с помощью которого фактически осуществляется авторизация.
Несколько соединений с сервером могут быть открыты; сообщения могут быть отправлены в любом направлении через любое из соединений (ответ на запрос не обязательно возвращается через то же соединение, которое отправляло запрос, хотя чаще всего это так; однако ни в коем случае сообщение не может быть возвращено через соединение, принадлежащее другому сеансу). При использовании протокола UDP ответ может быть возвращен через другой IP-адрес, чем тот, на который был отправлен запрос.
Существует несколько типов сообщений:
- Вызовы RPC (клиент-сервер): вызовы методов API
- Ответы RPC (сервер-клиент): результаты вызовов RPC
- Подтверждение приема сообщений (точнее, уведомление о состоянии набора сообщений)
- Запрос статуса сообщения
- Составное сообщение или контейнер (контейнер, содержащий несколько сообщений; необходим, например, для отправки нескольких вызовов RPC одновременно по HTTP-соединению; кроме того, контейнер может поддерживать gzip).
С точки зрения протоколов более низкого уровня, сообщение представляет собой двоичный поток данных, выровненный по границе 4 или 16 байт. Первые несколько полей в сообщении фиксированы и используются криптографической/авторизационной системой.
Каждое сообщение, будь то отдельное или внутри контейнера, состоит из идентификатора сообщения (64 бита, см. ниже), порядкового номера сообщения в сеансе (32 бита), длины (тело сообщения в байтах; 32 бита) и тела (любой размер, кратный 4 байтам). Кроме того, при отправке контейнера или одного сообщения, сверху добавляется внутренний заголовок (см. ниже), затем шифруется все сообщение, а сверху помещается внешний заголовок (64-битный идентификатор ключа и 128-битный ключ сообщения).
Тело сообщения обычно состоит из 32-битного типа сообщения, за которым следуют параметры, зависящие от типа. В частности, каждая функция RPC имеет соответствующий тип сообщения. Дополнительные сведения см. в разделе сериализация двоичных данных, служебные сообщения.
Все числа записываются как little-endian. Однако очень большие числа (2048 бит), используемые в RSA и DH, записываются в формате big-endian, потому что именно так это делает библиотека OpenSSL.
Авторизация и шифрование
Перед передачей сообщения (или составного сообщения) по сети с использованием транспортного протокола оно шифруется определенным образом, и в верхней части сообщения добавляется внешний заголовок, который представляет собой: 64-битный идентификатор ключа (который однозначно идентифицирует ключ авторизации как для сервера, так и для пользователя) и 128-битный ключ сообщения. Пользовательский ключ вместе с ключом сообщения определяет фактический 256-битный ключ, который шифрует сообщение с помощью шифрования AES-256. Обратите внимание, что начальная часть зашифрованного сообщения содержит переменные данные (сеанс, идентификатор сообщения, порядковый номер, соль сервера), которые, очевидно, влияют на ключ сообщения (следовательно, ключ AES и iv). Ключ сообщения определяется как 128 средних битов SHA256 тела сообщения (включая сеанс, идентификатор сообщения и т. д.), включая байты заполнения, перед которыми стоят 32 байта, взятые из ключа авторизации. Составные сообщения шифруются как одно сообщение.
Первое, что должно сделать клиентское приложение - это создать ключ авторизации, который обычно генерируется при первом запуске и почти никогда не меняется.
Основным недостатком протокола является то, что злоумышленник, пассивно перехватывающий сообщения, а затем каким-то образом присваивающий ключ авторизации (например, путем кражи устройства), сможет расшифровать все перехваченные сообщения
post factum. Это, вероятно, не слишком большая проблема (украдя устройство, можно также получить доступ ко всей информации, кэшированной на устройстве, ничего не расшифровывая); однако для преодоления этой проблемы можно предпринять следующие шаги:
- Сеансовые ключи генерируются с использованием протокола Диффи-Хеллмана и используются в сочетании с ключами авторизации и сообщения для выбора параметров AES. Чтобы создать их, первое, что клиент должен сделать после создания нового сеанса, - это отправить специальный RPC-запрос на сервер (“generate session key”), на который сервер ответит, после чего все последующие сообщения в сеансе также шифруются с помощью сессионного ключа.
- Защита ключа, хранящегося на клиентском устройстве, с помощью (текстового) пароля; этот пароль никогда не хранится в памяти и вводится Пользователем при запуске приложения или чаще (в зависимости от настроек приложения).
- Данные, хранящиеся (кэшированные) на пользовательском устройстве, также могут быть защищены шифрованием с использованием ключа авторизации, который, в свою очередь, должен быть защищен паролем. Затем потребуется пароль, чтобы получить доступ даже к этим данным.
Синхронизация Времени
Если время клиента сильно отличается от времени сервера, сервер может начать игнорировать сообщения клиента или наоборот из-за недопустимого идентификатора сообщения (который тесно связан со временем создания). При таких обстоятельствах сервер отправит клиенту специальное сообщение, содержащее правильное время и определенную 128-битную соль (либо явно предоставленную клиентом в специальном запросе синхронизации RPC, либо равную ключу последнего сообщения, полученного от клиента в течение текущего сеанса). Это сообщение может быть первым в контейнере, включающем другие сообщения (если расхождение во времени значительно, но еще не приводит к игнорированию сообщений клиента).
Получив такое сообщение или контейнер, содержащий его, клиент сначала выполняет синхронизацию времени (по сути, просто сохраняет разницу между временем сервера и своим собственным, чтобы иметь возможность вычислить “правильное” время в будущем), а затем проверяет правильность идентификаторов сообщения.
Если коррекцией пренебрегли, клиент должен будет сгенерировать новый сеанс, чтобы обеспечить монотонность идентификаторов сообщений.
MTProto транспорт
Перед отправкой по выбранному транспортному протоколу полезная нагрузка должна быть завернута во вторичный заголовок протокола, определенный соответствующим транспортным протоколом MTProto.
- Сокращенный
- Промежуточный
- Мягкий промежуточный материал
- Полный
Сервер распознает эти различные протоколы (и также отличает их от HTTP) по заголовку. Кроме того, можно использовать следующие транспортные функции:
- Быстрая АСК
- Транспортные ошибки
- Транспортная запутанность
Примеры реализации этих протоколов можно увидеть в tdlib и MadelineProto.
Транспорт
Обеспечивает доставку зашифрованных контейнеров вместе с внешним заголовком (далее-полезная нагрузка) от клиента к серверу и обратно. Определены несколько транспортных протоколов:
- TCP
- С WebSocket
- Websocket через HTTPS
- HTTP
- HTTPS
- UDP
(Мы рассмотрим только первые пять типов.)Заключение
Напомним, что в качестве сравнения используется стек ISO/OSI:
Уровень 7 (приложение): высокоуровневый RPC API
Уровень 6 (Презентация): Язык Type
Уровень 5 (сеанс): сеанс MTProto
Уровень 4 (Транспортный):
4.3: транспортный протокол MTProto
4.2: обфускация MTProto (опционально)
4.1: транспортный протокол
Уровень 3 (сеть): IP
Уровень 2 (канал передачи данных): MAC / LLC
Уровень 1 (физический): IEEE 802.3, IEEE 802.11 и т. д...