
- •Глава 1. Понятие «сетевой протокол»………………………………….......3
- •Глава 2. Мобильный протокол mTproto в Telegram………………………7
- •Глава 1. Понятие «сетевой протокол»
- •1.1 Сетевой протокол
- •1.2 Примеры сетевых протоколов.
- •Глава 2. Мобильный протокол mTproto в Telegram.
- •2.1 Общее описание
- •2.2 Краткий обзор компонент
- •2.3 Запрос состояния сообщений
- •2.4 Авторизация и криптография
- •2.4 Мобильный протокол: подробное описание
2.4 Авторизация и криптография
Перед передачей сообщений (или составных сообщений) по сети посредством транспортного протокола они шифруются определенным образом; при этом перед сообщением приписывается внешний заголовок: 64-битный идентификатор ключа (однозначно определяющий авторизационный ключ для сервера, а также пользователя) и 128-битный ключ сообщения. Пользовательский ключ вместе с ключом сообщения определяют реальный 256-битный ключ, которым и зашифровано сообщение посредством шифра AES-256. Начало тела незашифрованного сообщения содержит некоторые данные (сессию, идентификатор сообщения, порядковый номер сообщения в сессии, серверную соль); ключ сообщения должен совпадать с младшими 128 битами SHA1 от тела сообщения (включая сессию, идентификатор сообщения и т.п.). Составные сообщения шифруются как единое целое.
Первым делом клиентское приложение должно произвести создание авторизационного ключа, который обычно создается при первом запуске и практически никогда не изменяется.
Основной недостаток протокола — в том, что злоумышленник, пассивно перехватывающий сообщения, а затем каким-либо образом заполучивший авторизационный ключ (например, украв устройство) получит возможность расшифровать все перехваченные сообщения post factum. Вероятно, это не слишком серьезно (украв устройство, можно получить и всю закешированную на нем информацию, ничего не расшифровывая), однако для преодоления этих проблем можно сделать следующее:
Сессионные ключи, генерируемые по протоколу Диффи-Хелмана, и используемые совместно с авторизационным ключом и ключом сообщения для выбора параметров AES. Для их создания клиент должен первым действием после создания новой сессии отправить серверу специальный RPC-запрос (“сгенерировать сессионный ключ”), сервер ответит на него, после чего все последующие сообщения сессии шифруются с учетом и сессионного ключа.
Защищать ключ, хранимый на клиентском устройством, (текстовым) паролем; этот пароль никогда не хранится в памяти и вводится пользователем при запуске приложения или чаще (в зависимости от настроек приложения).
Данные, хранимые (кэшируемые) на пользовательском устройстве, можно также защищать, шифруя с помощью авторизационного ключа, который, в свою очередь, надо защитить паролем. Тогда без ввода пароля невозможно будет получить доступ даже к этим данным.
2.4 Мобильный протокол: подробное описание
Перед передачей сообщений (или составных сообщений) по сети посредством транспортного протокола они шифруются определенным образом; при этом перед сообщением приписывается внешний заголовок: 64-битный идентификатор ключа (однозначно определяющий авторизационный ключ для сервера, а также пользователя) и 128-битный ключ сообщения.
Пользовательский ключ вместе с ключом сообщения определяют реальный 256-битный ключ и 256-битный инициализационный вектор, которым и зашифровано сообщение посредством шифра AES-256 в сцепленном режиме (IGE). Начало тела незашифрованного сообщения содержит некоторые данные (сессию, идентификатор сообщения, серверную соль); ключ сообщения должен совпадать с младшими 128 битами SHA1 от тела сообщения (включая сессию, идентификатор сообщения и т.п.). Составные сообщения шифруются как единое целое.
Терминология
Авторизационный ключ
2048-битный ключ, общий для клиентского устройства и для сервера, создается при регистрации пользователя непосредственно на клиентском устройстве с помощью обмена ключами Диффи—Хелмана и никогда не передается по сети. Каждый авторизационный ключ соответствует конкретному пользователю. У пользователя в принципе может быть несколько ключей (соответствующие “вечным сессиям” на различных устройствах); он может заблокировать некоторые из них навсегда в случае утраты устройства. См. также Создание авторизационного ключа.
Серверный ключ
2048-битный RSA-ключ, используемый сервером для цифровой подписи своих сообщений во время регистрации и генерации авторизационного ключа. В приложение встроен публичный серверный ключ, с помощью которого можно проверить подпись, но невозможно подписывать сообщения. Приватный серверный ключ хранится на сервере и меняется крайне редко.
Идентификатор ключа
Младшие 64 бита SHA1 авторизационного ключа, используется для указания того, каким именно ключом зашифровано сообщение. Ключи должны однозначно задаваться младшими 64 битами своего SHA1; в случае коллизии авторизационный ключ перегенерируется. Нулевой идентификатор ключа означает, что шифрование не используется; это допускается для ограниченного набора типов сообщений, используемых при регистрации для создания авторизационного ключа по Диффи-Хелману.
Сессия
64-битное число, генерируемое клиентом для различения отдельных сессий (например, различных экземпляров приложения, запущенных с одним и тем же авторизационным ключом). Сессия вместе с идентификатором ключа соответствуют экземпляру приложения. Сервер может хранить состояние, соответствующее сессии. Ни при каких обстоятельствах сообщение, предназначенное для одной сессии, не может быть отправлено в другую сессию. Сервер имеет право в одностороннем порядке забыть любую клиентскую сессию; клиенты должны правильно обрабатывать такие ситуации.
Серверная соль
64-битное число, время от времени (скажем, раз в сутки) изменяемое (отдельно для каждой сессии) по инициативе сервера. После этого все сообщения должны содержать новую соль (хотя в течение 300 секунд еще принимаются сообщения и со старой солью). Нужна для защиты от replay-атак, и некоторых фокусов, связанных с переводом часов на клиенте в далекое будущее.
Идентификатор сообщения (msg_id)
64-битное число, уникально идентифицирующее сообщение в рамках сессии. Идентификаторы клиентских сообщений делятся на 4, серверных - дают остаток 1 от деления на 4, если они являются ответом на клиентское сообщение, и остаток 3 иначе. Идентификаторы клиентских сообщений должны монотонно расти (в пределах одной сессии), равно как и серверных сообщений; они должны примерно равняться unixtime * 2^32. Таким образом, идентификатор сообщения примерно определяет время его создания. Сообщение не принимается, если прошло более 300 секунд после его создания или за 30 секунд до его создания (это нужно для защиты от replay-атак); в этом случае оно должно быть перепослано с другим идентификатором (или помещено в контейнер с большим идентификатором). Идентификатор сообщения-контейнера должен быть строго больше идентификаторов содержащихся в нем сообщений.
Содержательное сообщение
Сообщение, нуждающееся в явном подтверждении. Это все пользовательские сообщения, и многие служебные — практически все, кроме контейнеров и сообщений с подтверждениями.
Порядковый номер сообщения (msg_seqno)
32-битное число, равное удвоенному количеству “содержательных” сообщений (т.е. нуждающихся в подтверждении, и, в частности, не являющихся контейнерами), созданных отправителем до данного сообщения, и увеличенному затем на единицу, если текущее сообщение является содержательным. Контейнер всегда генерируется после всего, что в нем содержится, поэтому его порядковый номер не меньше порядковых номеров сообщений, что есть внутри него.
Важные проверки
При получении зашифрованного сообщения надо обязательно проверить, что msg_key действительно равен младшим 128 битам SHA1 от ранее зашифрованной части, и что msg_id четно для сообщений от клиента к серверу и нечетно для сообщений от сервера к клиенту.
Кроме того, следует хранить идентификаторы (msg_id) последних N сообщений, полученных от другой стороны, и если приходит сообщение с msg_id, меньшим, чем все запомненные, либо совпадающим с одним из запомненных - игнорировать это сообщение. В противном случае msg_id нового сообщения добавляется в набор и, если запомненных msg_id становится больше N, то самый старый (т.е. наименьший) из них забывается.
Помимо этого, следует игнорировать msg_id, относящиеся более чем на 30 секунд в будущее или более 300 секунд в прошлое. Это особенно важно для сервера; клиенту это тоже полезно делать (чтобы избежать replay-атаки), но только в том случае, если он уверен в своем времени (например, если время было синхронизировано с сервером).
Некоторые служебные сообщения от сервера к клиенту, содержащие в себе данные, отправленные от клиента к серверу (например, msg_id недавнего клиентского запроса) все-таки можно обрабатывать на клиенте, даже если время якобы “неправильное”. Это особенно относится к сообщениям об изменении server_salt и о неправильном времени на клиенте. См. Мобильный протокол: служебные сообщения.
Хранение авторизационного ключа на клиентском устройств.
Для озабоченных безопасностью пользователей можно предлагать защитить авторизационный ключ паролем, примерно так же, как это делает ssh. Для этого к ключу спереди добавляется SHA1 от него, после чего все это шифруется AES в режиме CBC с помощью ключа, равного (текстовому) паролю пользователя. При вводе пользователем пароля сохраненный защищенный ключ расшифровывается и проверяется совпадение SHA1. С точки зрения пользователя это почти равносильно паролю на приложение или на вход на сайт.
Незашифрованные сообщения
Для создания авторизационного ключа, а также для синхронизации времени могут использоваться специальные незашифрованные сообщения. Они начинаются с auth_key_id = 0 (64 бита), что означает, что auth_key нет. Далее идет непосредственно тело сообщения в сериализованном формате, без внешних или внутренних заголовков. Перед телом сообщения добавляется его идентификатор (64 бита) и длина тела в байтах (32 бита). Только очень ограниченное количество типов специальных сообщений могут передаваться незашифрованными.
Таким образом, важность протокола подчеркивается своим массовым использованиям в информационных отраслях, при создании как мобильных, так и компьютерных приложениях.
Список источников: