Начиная с версии 4.6, почти все клиенты Telegram используют MTProto 2.0. MTProto v.1.0 устарел и в настоящее время постепенно прекращается.
В данной статье описывается базовый уровень протокола MTProto версии 2.0 (облачные чаты, шифрование сервер-клиент). Основные отличия от версии 1.0 (
описанной здесьдля справки) заключаются в следующем:
- Вместо SHA-1 используется SHA-256;
- Байты заполнения участвуют в вычислении msg_key;
- msg_key зависит не только от самого сообщения, подлежащего шифрованию, но на часть auth_key, а также;
- 12..1024 байта заполнения используются вместо 0..15 байтов заполнения в v.1.0.
См. также:
MTProto 2.0: секретные чаты, сквозное шифрованиеОписание протокола
Перед передачей сообщения (или составного сообщения) по сети с использованием транспортного протокола оно определенным образом шифруется, а в верхней части сообщения добавляется внешний заголовок, состоящий из 64-битного идентификатора ключа
auth_key_id (который однозначно идентифицирует ключ авторизации как для сервера, так и для пользователя) и 128-битного ключа сообщения
msg_key.
Ключ авторизации
auth_key в сочетании с ключом сообщения
msg_key определяют фактический 256-битный ключ
aes_key и 256-битный вектор инициализации
aes_iv, которые используются для шифрования сообщения с использованием шифрования AES-256 в режиме infinite garble extension (IGE). Обратите внимание, что начальная часть зашифрованного сообщения содержит переменные данные (сеанс, идентификатор сообщения, порядковый номер, соль сервера), которые, очевидно, влияют на ключ сообщения (и, следовательно, ключ AES и iv). В
MTProto 2.0 ключ сообщения определяется как 128 средних битов SHA-256 тела сообщения (включая сеанс, идентификатор сообщения, заполнение и т. д.), к которым добавляются 32 байта, взятые из ключа авторизации. В более старом
MTProto 1.0 ключ сообщения вычислялся как нижние 128 бит SHA-1 тела сообщения, исключая байты заполнения.
Составные сообщения шифруются как одно сообщение.
Есть вопросы по поводу этой схемы? — Проверьте
FAQ!
Примечание 1
Каждое открытое текстовое сообщение, шифруемое в MTProto, всегда содержит следующие данные, которые должны быть проверены при расшифровке, чтобы сделать систему устойчивой к известным проблемам с компонентами:
- серверная соль (64-битная)
- идентификатор сеанса
- порядковый номер сообщения
- длина сообщения
- время
Примечание 2
Сквозные зашифрованные секретные чаты Telegram используют дополнительный уровень шифрования поверх описанного выше.
Смотрите секретные чаты, end-to-end шифрования в деталях.
Терминология
Ключ авторизации (auth_key)
2048-битный ключ, совместно используемый клиентским устройством и сервером, созданный при регистрации пользователя непосредственно на клиентском устройстве путем обмена ключами Диффи-Хеллмана и никогда не передаваемый по сети. Каждый ключ авторизации зависит от пользователя. Ничто не мешает пользователю иметь несколько ключей (которые соответствуют “постоянным сеансам” на разных устройствах), и некоторые из них могут быть заблокированы навсегда в случае потери устройства. См. также раздел
создание ключа авторизации.
Ключ сервера
2048-битный RSA-ключ, используемый сервером в цифровом виде для подписи собственных сообщений во время регистрации и генерации ключа авторизации. Приложение имеет встроенный открытый ключ сервера, который может быть использован для проверки подписи, но не может быть использован для подписи сообщений. Закрытый ключ сервера хранится на сервере и изменяется очень редко.
Идентификатор ключа (auth_key_id)
64 бит хэша SHA1 ключа авторизации используются для указания того, какой именно ключ был использован для шифрования сообщения. Ключи должны быть однозначно определены 64 битами их SHA1, и в случае столкновения ключ авторизации восстанавливается. Нулевой идентификатор ключа означает, что шифрование не используется, что допустимо для ограниченного набора типов сообщений, используемых при регистрации для генерации ключа авторизации в обмене Диффи-Хеллмана. Для MTProto 2.0 здесь по-прежнему используется SHA1, поскольку auth_key_id должен идентифицировать используемый ключ авторизации независимо от версии протокола.
Сессия
(Случайное) 64-битное число, генерируемое клиентом для различения отдельных сеансов (например, между различными экземплярами приложения, созданными с одним и тем же ключом авторизации). Сеанс в сочетании с идентификатором ключа соответствует экземпляру приложения. Сервер может поддерживать состояние сеанса. Ни при каких обстоятельствах сообщение, предназначенное для одного сеанса, не может быть отправлено в другой сеанс. Сервер может в одностороннем порядке забыть любые сеансы клиента; клиенты должны быть в состоянии справиться с этим.
Соль Сервера
(Случайное) 64-битное число периодически (скажем, каждые 24 часа) изменяется (отдельно для каждого сеанса) по запросу сервера. Все последующие сообщения должны содержать новую соль (хотя сообщения со старой солью все еще принимаются в течение следующих 300 секунд). Требуется защита от повторных атак и определенных трюков, связанных с настройкой клиентских часов на момент в отдаленном будущем.
Идентификатор сообщения (msg_id)
(Зависимое от времени) 64-битное число, используемое для того, чтобы уникально идентифицировать сообщение внутри сессии. Идентификаторы сообщения клиента кратны четырём (делятся на 4), остаток от деления идентификатора сообщения сервера на 4 приравнивается к 1 если сообщение является ответом на сообщение клиента, и к трём в остальных случаях. Идентификаторы сообщения клиента должны увеличиваться монотонно (внутри одной сессии), так же как идентификаторы сообщений сервера, и должны примерно равняться unixtime*2^32. Таким образом, идентификатор сообщения указывает на приблизительный момент времени, в который сообщение было создано. Сообщение отклоняется через 300 секунд после того, как оно было создано, или за 30 секунд до того как оно будет создано (это необходимо для защиты против атак повторного воспроизведения). В этой ситуации, оно должно быть отправлено заново с другим идентификатором (или помещено в контейнер с более высоким идентификатором). Идентификатор контейнера сообщений обязательно должен быть больше, чем идентификатор вложенных в него сообщений.
Важно: для противодействия повторным атакам нижние 32 бита
msg_id, передаваемые клиентом, не должны быть пустыми и должны представлять собой дробную часть момента времени создания сообщения.
Сообщение, связанное с контентом
Сообщение, требующее подтверждения. К ним относятся все пользовательские и многие служебные сообщения, практически все, за исключением контейнеров и подтверждений.
Порядковый номер сообщения (msg_seqno)
32-битное число, равное двойному числу «связанных с контентом» сообщений (которые требуют подтверждения, и в частности те, которые не являются контейнерами) созданных отправителем до этого сообщения и впоследствии увеличивающееся на один если текущее сообщение является сообщением, связанным с контентом. Контейнер всегда генерируется после того, как генерируется то, что он содержит; таким образом, его порядковый номер больше либо равен порядковым номерам сообщения, содержащимся в нём.
Ключ сообщения
128 бит нижнего порядка хеша SHA1 части сообщения, которая будет зашифрована (включая внутренний заголовок и исключая байты выравнивания данных).
Внутренний (криптографический) заголовок
Заголовок (16 байт), добавляемый перед сообщением или контейнером до того, как они все вместе будут зашифрованы. Состоит из соли сервера (64 бита) и сессии (64 бита).
Внешний (криптографический) заголовок
Заголовок (24 байта), добавляемый перед зашифрованным сообщением или контейнером. Состоит из идентификатора ключа (64 бита) и ключа сообщения (128 бит).
Payload (полезная нагрузка)
Внешний заголовок + зашифрованное сообщение или контейнер.
Определение ключа AES и вектора инициализации
2048-битный ключ авторизации (auth_key) и 128-битный ключ сообщения (msg_key) используются для вычисления 256-битного ключа AES (aes_key) и 256-битного вектора инициализации (aes_iv), которые в дальнейшем используются, чтобы зашифровать часть сообщения, которая должна быть зашифрована (то есть всё за исключением внешнего заголовка, который добавляется позднее) с AES-256 в режиме расширения неопределённого искажения (infinite garble extension, IGE).
Алгоритм для вычисления aes_key и aes_iv из auth_key and msg_key таков:
- msg_key_large = SHA256 (substr (auth_key, 88+x, 32) + plaintext + random_padding);
- msg_key = substr (msg_key_large, 8, 16);
- sha256_a = SHA256 (msg_key + substr (auth_key, x, 36));
- sha256_b = SHA256 (substr (auth_key, 40+x, 36) + msg_key);
- aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);
- aes_iv = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);
где x = 0 для сообщений от клиента к серверу и x = 8 для сообщений от сервера к клиенту.
Для устаревшего MTProto 1.0 msg_key, aes_key и aes_iv были вычислены по-разному (
см. Этот документ для справки).
1024 бита нижнего порядка auth_key не участвуют в вычислении. Они могут (вместе с оставшимися битами или отдельно) использоваться на клиентском устройстве для шифрования локальной копии данных, полученных с сервера. 512 бит нижнего порядка auth_key не хранятся на сервере, поэтому, если клиентское устройство использует их для шифрования локальных данных, а пользователь теряет ключ или пароль, расшифровка локальных данных невозможна (даже если данные с сервера могут быть получены).
В MTProto 1.0, когда AES использовался для шифрования блока данных длиной, не делящейся на 16 байт, данные были дополнены от 0 до 15 случайными байтами заполнения random_padding до длины, делящейся на 16 байт до шифрования.
В MTProto 2.0 это дополнение учитывается при вычислении msg_key. Обратите внимание, что MTProto 2.0 требует от 12 до 1024 байт заполнения, но при условии, что результирующая длина сообщения будет делиться на 16 байт.
Использование MTProto 2.0 вместо MTProto 1.0
Клиент может использовать только MTProto 2.0 или только MTProto 1.0 в одном TCP-соединении. Сервер обнаруживает протокол, используемый первым сообщением, полученным от клиента, а затем использует то же самое шифрование для своих сообщений и ожидает, что клиент будет использовать то же самое шифрование впредь. Мы рекомендуем использовать MTProto 2.0; MTProto 1.0 устарел и поддерживается только для обратной совместимости.
Важные тесты
Когда зашифрованное сообщение получено, должно быть проверено, что msg_key фактически равен 128 битам нижнего порядка хэша SHA1 от предварительно зашифрованной порции, и что msg_id имеет чётный результат для сообщений от клиента к серверу, и нечётный — для сообщений от сервера к клиенту.
Примечание переводчика: имеется в виду бит чётности. Вычисляется сложением по модулю 2, результат 0 считается чётным, результат 1 — нечётным.
Дополнительно, идентификаторы (msg_id) последних N сообщений, полученных от другой стороны, должны быть сохранены, и если сообщение приходит с msg_id меньшим или равным любому из сохранённых значений, сообщение будет проигнорировано. В противном случае, новый msg_id сообщения добавляется к комплекту, и, если число сохранённых значений msg_id больше чем N, самое старое (т. е. самое нижнее) забывается.
Дополнительно, значения msg_id, относящиеся ко времени более 30 секунд в будущем и более 300 секунд в прошлом, игнорируются. Это особенно важно для сервера. Для клиента это также будет полезным (для защиты от атаки повторного воспроизведения), но только если он его настройки времени точны (например, если его время было синхронизировано с временем сервера).
Определённые сервисные сообщения «от клиента к серверу», содержащие данные, отправленные клиентом серверу (например, msg_id последнего запроса клиента) могут, тем не менее, быть обработаны на/в клиенте даже если время «неправильное». Это особенно верно для сообщений, которые меняют сервер-salt и уведомлений о неправильных настройках времени у клиента. См. мобильный протокол: сервисные сообщения.
Сохранение ключа авторизации на клиенте
Пользователям, озабоченным безопасностью, можно предложить защитить ключ авторизации паролем примерно так же, как в ssh. Это может быть достигнуто путем добавления значения криптографической хэш-функции, такой как SHA-256, ключа к передней части ключа, после чего вся строка шифруется с использованием AES в режиме CBC и ключа, равного паролю пользователя (текстовому). Когда пользователь вводит пароль, сохраненный защищенный пароль расшифровывается и проверяется путем проверки значения SHA-256. С точки зрения пользователя, это практически то же самое, что использовать пароль приложения или веб-сайта.
Незашифрованные сообщения
Специальные текстовые сообщения могут быть использованы для создания ключа авторизации, а также для выполнения синхронизации времени. Они начинаются с auth_key_id = 0 (64 бита), что означает: auth_key отсутствует. За этим следует непосредственно тело сообщения в сериализованном формате без внутренних или внешних заголовков. Перед телом сообщения добавляется идентификатор сообщения (64 бита) и длина тела в байтах (32 байта).
Только очень ограниченное число сообщений специальных типов может быть передано в виде обычного текста.
Схематическое представление сообщений
Зашифрованное сообщениеЗашифрованное сообщение: encrypted_dataСодержит зашифрованный текст для следующих данных:
Незашифрованное СообщениеMTProto 2.0 использует 12..1024 байта заполнения вместо 0..15, используемых в MTProto 1.0
Создание ключа авторизации
Ключ авторизации обычно создаётся один раз для каждого юзера во время процесса установки приложения непосредственно перед регистрацией. Непосредственно сама регистрация происходит после того, как создан ключ авторизации. Однако, юзеру может быть предложено заполнить форму регистрации одновременно с тем, как в фоновом режиме генерируется ключ авторизации. Интервалы между нажатием клавиш юзером могут быть использованы как источник энтропии для генерации высококачественных случайных чисел, которые требуются для создания ключа авторизации.
См. создание ключа авторизации.В то время как создаётся ключ авторизации, клиент определяет какова соль сервера (server salt) (то есть для всех сессий, которые будут созданы в ближайшем будущем для ключа). Далее, клиент создаёт первую (зашифрованную) сессию, используя ключ, и каждая последующая коммуникация (включая передачу регистрационной информации юзера и валидацию номера телефона) будут происходить внутри сессии.