Transports
Enables the delivery of encrypted containers together with the external header (hereinafter, Payload) from client to server and back.
Multiple transport protocols are defined:
The URI format for connecting to the websocket and HTTP endpoints is the following:
The TCP transport is implemented simply by sending the payloads generated by the chosen MTProto transport over a plain TCP socket on ports 80, 443, 5222 or other (a different port number may be returned by help.getConfig).
Framing is managed by the chosen MTProto transport protocol.
There are no implicit acknowledgments for the TCP transport: all messages must be acknowledged explicitly. Most frequently, acknowledgments are placed in a container with the next query or response if it is transmitted in short order. For example, this is almost always the case for client messages containing RPC queries: the acknowledgment normally arrives with the RPC response.
Implementation of the websocket transport is pretty much the same as with TCP: a websocket connection is established to the chosen MTProto server over port 80 using the specified URI format.
Framing of payloads is still managed by the chosen MTProto transport protocol, not by websocket messages: the length of MTProto payloads is defined by the MTProto transport protocol, not by the length of the single websocket messages.
This simply means that all data received and sent through websocket messages is to be treated as a single duplex stream of bytes, just like with TCP.
When using the websocket transport, transport obfuscation is required.
Transport errors are transmitted the usual way, as with TCP.
The close code of websockets will always be 1000 (normal closure), regardless of the actual exit status.
In all cases, the description string will be a decimal encoded real error code (which may be forward/back-padded with whitespaces for constant length) and can be safely ignored.
To establish a websocket connection over HTTPS, simply use the TLS URI format.
The rest is the same as with plain websockets.
Note: when implementing browser clients, websocket transport is recommended instead of HTTP, thanks to its full-duplex stream logic similar to TCP’s; this removes the need for HTTP long polling and eventual delays while relaying RPC replies.
Implemented over HTTP/1.1 (with keepalive) running over the traditional TCP Port 80.
HTTPS can also be used.
Message framing is not managed by MTProto transport protocols; it is instead handled by the HTTP protocol itself.
Transport errors are also not transmitted the usual way, instead they are simply returned as normal HTTP status codes.
An HTTP connection is attached to a session (or rather, to session + key identifier) specified in the most recent user query received; normally, the session is the same in all queries, but crafty HTTP proxies may corrupt that. A server may not return a message into an HTTP connection unless it belongs to the same session, and unless it is the server’s turn (an HTTP request had been received from the client to which a response has not been sent yet).
Having received the query, the server may either wait a little while (if the query requires a response following a short timeout) or immediately return a dummy response (only acknowledging the receipt of the container). In any case, the response may contain any number of messages. The server may at the same time send out any other messages it might be holding for the session.
In addition, there exists a special long poll RPC query (valid for HTTP connections only) which transmits maximum timeout T. If the server has messages for the session, they are returned immediately; otherwise, a wait state is entered until such time as the server has a message for the client or T seconds have elapsed. If no events occur in the span of T seconds, a dummy response is returned (special message).
If a server needs to send a message to a client, it checks for an HTTP connection that belongs to the required session and is in the “answering an HTTP request” state (including long poll) whereupon the message is added to the response container for the connection and sent to the user. In a typical case, there is some additional wait time (50 milliseconds) against the eventuality that the server will soon have more messages for the session.
If no suitable HTTP connection is available, the messages are placed in the current session’s send queue. However, they find their way there anyway until receipt is explicitly confirmed by the client. For all protocols, the client must return an explicit acknowledgment within a reasonable time (it can be added to a container for the following request).
Important: if the acknowledgment fails to arrive on time, the message can be resent (possibly, in a different container). The parties must autonomously be ready for this and must store the identifiers of the most recent messages received (and ignore such duplicates rather than repeat actions). In order not to have the identifiers stored forever, there exist special garbage collection messages that take advantage of message identifier monotonicity.
If the send queue overflows or if messages stay in the queue for over 10 minutes, the server forgets them. This may happen even faster, if the server is running out of buffer space (for example, because of serious network issues resulting in a large number of connections becoming severed).
To establish a connection over HTTPS, simply use the TLS URI format.
The rest is the same as with plain HTTP.
The URI format that must be used when connecting to the plain websocket and HTTP endpoints is the following:
The w flag is added when CORS headers are required in order to connect from a browser.
The s flag enables the websocket API.
The name placeholder in the domain version specifies the DC ID to connect to:
-1 can be appended to the DC name to raise the maximum limit of simultaneous requests per hostname.
The _test flag, when connecting to the domain version of the URL, specifies that connection to the test DCs must be made, instead.
When connecting to the HTTPS and WSS endpoints, only the domain name URI can be used over port 443:
See the URI format for an explanation of the placeholders.
Example implementations: tdlib, MadelineProto (client side), MTProxy (server side).
Мобильный протокол MTProto
Разработчикам клиентов нужно соблюдать наши требования безопасности.
Связанные статьи
Общее описание
Протокол предназначен для доступа к серверному API с приложений, запущенных на мобильных устройствах. Подчеркнем, что интернет-браузер не считается таким приложением.
Протокол разбит на три почти независимых части:
Примечание 1: Каждое текстовое сообщение, которое нужно зашифровать через MTProto, всегда содержит следующие данные, которые проверяются расшифровкой, чтобы сделать систему устойчивой против известных проблем с компонентами:
Краткий обзор компонентов
Высокоуровневая часть (язык RPC-запросов/API)
С точки зрения высокоуровневой части, клиент и сервер обмениваются сообщениями в рамках некоторой сессии. Сессия привязана к клиентскому устройству (вернее, приложению), но не к конкретному http/https/tcp-соединению. Кроме того, каждая сессия привязана к идентификатору пользовательского ключа, по которому фактически производится авторизация.
Может быть открыто несколько соединений к серверу; сообщения в ту или иную сторону могут идти по любому из них (ответ на запрос не обязан прийти по тому же соединению, по которому был отправлен сам запрос, хотя чаще всего это так; однако ни в коем случае сообщение не может быть возвращено в соединении, принадлежащем другой сессии). При использовании UDP-протокола может случиться, что ответ на запрос приходит не с того IP, на который был отправлен запрос.
Сообщения бывают нескольких типов:
С точки зрения протоколов более низкого уровня, сообщение — это поток двоичных данных, выровненный по границе 4 или 16 байтов. Первые несколько полей сообщения фиксированы и используются системой криптографии/авторизации.
Каждое сообщение, отдельное или внутри контейнера, состоит из идентификатора сообщения (64 бита; см. ниже), порядкового номера сообщения в сессии (32 бита), длины (тела в байтах; 32 бита) и тела (любой размер, кратный 4 байтам). Кроме того, при отправке контейнера или одиночного сообщения, в его начало дописывается внутренний заголовок (см. ниже), после чего все это шифруется, и в начало зашифрованного сообщения добавляется внешний заголовок (64-битный идентификатор ключа и 128-битный ключ сообщения).
Тело сообщения обычно состоит из 32-битного типа сообщения, за которым следуют параметры, зависящие от типа. В частности, каждой RPC-функции соответствует свой тип сообщения. Более подробно читайте в статье про двоичную сериализацию данных и служебные сообщения.
Все числа записываются как little-endian. Однако очень большие числа (2048-битные), используемые в RSA и DH, записываются как big-endian, потому что так делает библиотека OpenSSL.
Авторизация и криптография
Перед передачей сообщений (или составных сообщений) по сети посредством транспортного протокола они шифруются определенным образом; при этом перед сообщением приписывается внешний заголовок: 64-битный идентификатор ключа (однозначно определяющий авторизационный ключ для сервера, а также пользователя) и 128-битный ключ сообщения. Пользовательский ключ вместе с ключом сообщения определяют реальный 256-битный ключ, которым и зашифровано сообщение посредством шифра AES-256. Начало тела незашифрованного сообщения содержит некоторые данные (сессию, идентификатор сообщения, порядковый номер сообщения в сессии, серверную соль); ключ сообщения должен совпадать с младшими 128 битами SHA1 от тела сообщения (включая сессию, идентификатор сообщения и т.п.). Составные сообщения шифруются как единое целое.
Первым делом клиентское приложение должно произвести создание авторизационного ключа, который обычно создается при первом запуске и практически никогда не изменяется.
Примечание от переводчика: по некоторым сведениям, в последних обновлениях этот ключ меняется через каждые 100 отправленных сообщений — https://twitter.com/durov/status/539489480676085760
Основной недостаток протокола — в том, что злоумышленник, пассивно перехватывающий сообщения, а затем каким-либо образом заполучивший авторизационный ключ (например, украв устройство) получит возможность расшифровать все перехваченные сообщения post factum. Вероятно, это не слишком серьезно (украв устройство, можно получить и всю закешированную на нем информацию, ничего не расшифровывая), однако для преодоления этих проблем можно сделать следующее:
Синхронизация времени
Если время на клиенте сильно отличается от времени на сервере, может так получиться, что сервер начнет игнорировать сообщения клиента, или наоборот, из-за некорректного значения идентификатора сообщения (которое тесно связано с временем создания). В таких ситуациях сервер шлет клиенту специальное сообщение с правильным временем, содержащие, помимо него, некую 128-битную соль (либо явно присланную клиентом в специальном RPC-запросе синхронизации, либо равную ключу последнего сообщения, полученного от клиента в рамках данной сессии). Такое сообщение может быть первым в контейнере, содержащим и другие сообщения (если рассинхронизация существенна, но еще не приводит к игнорированию клиентских сообщений).
При получении такого сообщения (или содержащего его контейнера) клиент сначала выполняет синхронизацию времени (фактически всего лишь запоминает разницу своего и серверного времени, чтобы уметь впредь вычислять «правильное» время), а затем проверяет идентификаторы сообщений на корректность.
В запущенных случаях клиенту придется сгенерировать новую сессию, чтобы обеспечить монотонность идентификаторов сообщений.
Транспорт
Позволяет доставлять уже зашифрованные контейнеры вместе с внешним заголовком (в дальнейшем — полезную нагрузку) от клиента к серверу и наоборот. Есть три типа транспорта:
Рассмотрим первые два типа.
Передача по HTTP
Реализуется поверх HTTP/1.1 (с keepalive), запущенного поверх классического TCP-порта 80. HTTPS не используется; используется криптографическая схема, объясненная выше.
HTTP-соединение привязывается к сессии (вернее, сессии + идентификатору ключа), указанной в последнем пришедшем пользовательском запросе; обычно во всех запросах сессия одинакова, однако хитрые HTTP-прокси могут это испортить. Сервер может вернуть сообщение в HTTP-соединение только в том случае, если оно принадлежит той же сессии, и если сейчас очередь сервера (был получен HTTP-запрос от клиента, на который еще не было отправлено ответа).
После получения запроса сервер может либо подождать немного (если запрос подразумевает ответ после небольшого ожидания), либо сразу вернуть фиктивный ответ (сообщающий всего лишь о том, что контейнер был получен). В любом случае в ответе может оказаться сколько угодно сообщений — сервер вправе заодно отправить любые накопившиеся у него сообщения для этой сессии.
Кроме того, есть специальный longpoll RPC-запрос (действительный только для http-соединений), в котором передается максимальное время ожидания T. Если у сервера есть сообщения для этой сессии, они возвращаются сразу же; в противном случае происходит ожидание до тех пор, пока у сервера не появится сообщение для клиента, либо не пройдет T секунд. Если за T секунд не произошло никаких событий, возвращается фиктивный ответ (специальное сообщение).
Если серверу надо отправить сообщение клиенту, он проверяет, нет ли HTTP-соединения, принадлежащего нужной сессии, и находящегося в состоянии «выполнения HTTP-запроса» (включая long poll), после чего сообщение добавляется в контейнер ответа этого соединения и отправляется пользователю. В типичном случае происходит небольшое дополнительное ожидание (50 миллисекунд), на тот случай, если у сервера вскоре появятся еще сообщения для этой сессии.
Если ни одного подходящего HTTP-соединения нет, сообщения ставятся в очередь отправки для данной сессии. Впрочем, они туда попадают в любом случае, пока явно или косвенно не подтверждено получение клиентом. Для http-протокола неявным подтверждением считается отправка следующего запроса по тому же HTTP-соединению (уже нет — и для HTTP-протокола необходимо слать явные подтверждения); в остальных случаях клиент должен прислать явное подтверждение за разумное время (его можно добавить в контейнер для следующего запроса).
Важно: если подтверждение вовремя не пришло, сообщение может быть перепослано (возможно, в составе другого контейнера). Стороны должны быть морально готовы к этому и хранить идентификаторы последних полученных сообщений (и игнорировать такие дубли, а не повторять действие). Для того, чтобы не хранить идентификаторы вечно, есть специальные сообщения сборки мусора, эксплуатирующие монотонность идентификаторов сообщений.
TCP-транспорт
Очень похож на HTTP-транспорт, может быть реализован тоже на порт 80 (чтобы проходить все фаерволы) и даже на те же ip-адреса серверов. В этом случае сервер понимает, нужно ли использовать HTTP или TCP-протокол для данного соединения по первым четырем пришедшим байтам (для HTTP это будет POST).
При создании TCP-соединения оно приписывается сессии (и авторизационному ключу), переданному в первом сообщении пользователя, и потом используется исключительно для данной сессии (схемы мультиплексирования не допускаются).
При необходимости отправки полезной нагрузки (пакета) от сервера к клиенту или от клиента к серверу она инкапсулируется следующим образом: спереди дописывается 4 байта длины (включая длину, порядковый номер и CRC32; всегда делится на четыре) и 4 байта с порядковым номером пакета внутри данного tcp-соединения (первый отправленный пакет помечается 0, следующий — 1 и т.д.), а в конце — 4 байта CRC32 (длины, порядкового номера и полезной нагрузки вместе).
В случае, если требуется выравнивание 4-байтовых данных, может быть использована промежуточная версия оригинального протокола: если клиент отправляет 0xEEEEEEEE как первый инт (int) (четыре байта), то длина пакета зашифрована всегда четырьмя байтами как в оригинальной версии, но порядковый номер и CRC32 опускаются, таким образом уменьшая итоговый /общий размер пакета на 8 байт.
В полной и в сокращённой версии протокола есть поддержка быстрых подтверждений. В этом случае клиент устанавливает старший бит длины в пакете с запросом, а сервер отсылает в ответ специальные 4 байта, представляющие собой самостоятельный пакет. Они представляют собой старшие 32 бита SHA1 от зашифрованной части пакета, с установленным старшим битом, чтобы было понятно, что это не длина обычного пакета с ответом сервера; если используется сокращённая версия, то к этим четырём байтам применяется bswap.
Неявных подтверждений для TCP-транспорта не бывает: все сообщения должны быть явно подтверждены. Чаще всего подтверждения помещаются в контейнер вместе со следующим запросом или ответом, если он отправляется вскоре. Например, это почти всегда так для сообщений от клиента, содержащих RPC-запросы: подтверждение обычно приходит вместе с RPC-ответом.
В случае возникновения ошибки сервер может прислать пакет, полезная нагрузка которого состоит из 4 байтов — кода ошибки. Например, код ошибки −403 соответствует ситуациям, в которых через HTTP-протокол вернулась бы соответствующая HTTP-ошибка.
Сайт про Telegram на русском (неофициальный).
Здесь собраны приложения на базе MTProto, переведена некоторая документация с официального сайта, а также работает Webogram.
Шо там по MTProto в Telegram-то?
Рассматриваем тележку, так сказать.
Оглавление
Предисловие
Здравствуйте, дорогие хабровчане! Я здесь новенький, поэтому не кидайтесь там палками, помидорами и всем прочим (хотя, кидайтесь, мб, полезнее будет). Как один из всадников ноябрь-декабрьского криптографического апокалипсиса, я пришел вам поведать о протоколе, что используется как в секретной переписке с мамой о покупке продуктов, так и в не очень доброжелательных целях.
21-й год на дворе. В чем там сейчас чатятся зумеры, бумеры, думеры, миллениалы и вот эти все касты? Тележка, конечно, тележка. Каждый из нас хочет, чтобы его самые сокровенные секреты, будь то причудливые мемы, переписки со странными субъектами, да и что уж таить, подписки на аниме-каналы, оставались секретами до конца. Но VK же, вроде, удовлетворяет всем нуждам и потребностям, удобно и доступно. Соглашусь, мы в СНГ выросли уже на этой социальной сети, и она нам как родная (а ее, как и тележку, основал Пашенька). Но на данный момент она переживает не лучшие времена, пытаясь подражать всему и вся, добавляя никому не нужный функционал, а также заполняя ленту невероятным количеством рекламы (на каждые 6 постов, 1 рекламный – ужас). Также, бог весть, что там за защита у VK.
В то же время тележка намного проще и чище в плане функционала и рекламы (пока что). Более того, Пашенька позиционирует свой чатек как несокрушимую и непоколебимую крепость в плане взлома, а сам алгоритм шифрования MTProto выставлен перед всеми на обозрение. Так давайте поговорим о внутреннем устройстве MTProto, разработанного собственноручно командой тележки, с точки зрения криптографии.
High-level описание
А дела в алгоритме обстоят довольно запутанно с первого взгляда. Разработчики тележки – самые что ни на есть храбрецы: взять уже готовые протоколы, по типу Signal – не, скучно; создать свой протокол, используя всевозможные известные алгоритмы с добавлением кучи своих фишек и фокусов, чтобы все было еще секретнее и сложнее – прекрасное решение. Так и появился MTProto.
Начать, пожалуй, нужно с описания работы в целом. MTProto – клиент-серверный набор протоколов, служащий для доступа к серверу из клиентского приложения через незащищенное соединение. Этот набор можно разделить на 3 основные части:
Общая схема работы MTProto 2.0.
High-level API and Type language: отвечает как за API запросы и ответы, так и за сериализацию данных.
Cryptographic and authorization components: определяет, как приложение (клиент) авторизируется на сервере, а также шифрование сообщений перед отправкой на транспортный уровень.
Transport component: определяет, как клиент и сервер обмениваются сообщениями посредством таких транспортных протоколов, как UDP, TCP, HTTP(S), WebSocket и другие.
Итак, как я и сказал, мы остановимся на втором уровне, связанном с криптографией и авторизацией (C&A), и поглядим, как вся кухня устроена. В свою очередь данный уровень можно поделить на 3 модуля:
Authorization: этот модуль отвечает за первоначальную авторизацию клиента. Он работает во время первого запуска приложения, чтобы получить ключ авторизации.
Secret chat key exchange and rekeying: данный модуль отвечает за установку сессионного общего секретного ключа между клиентами, используемого в сквозном шифровании.
Message encryption: модуль-работяга, не покладая рук, шифрует наши сообщения симметричным шифром AES.
Терминология
Начать наше путешествие, думаю, стоит с описания всех фигурирующих величин и терминов. Для удобства в статье большинство из них будут написаны на забугорском языке (лучше выделяются, а также легче яндексятся).
Итак, дамы и господа, наша сегодняшняя терминология:
Authorization key (AK, auth_key) – 2048-битный ключ, который создается на этапе пользовательской регистрации посредством алгоритма Diffie-Hellman, а доступ к нему имеет только клиент и сервер. Более того, утверждается, что ключ шифруется на сервере, а ключ к ключу отправляется на другой сервер. Таким образом, получение доступа лишь к одному серверу – недостаточно для взлома.
RSA:– 2048-битный закрытый ключ RSA,
– 2048-битный открытый ключ, используемые на этапе регистрации и генерации ключа авторизации.
Key identifier (auth_key _id) – 64 младших бита хеша (SHA-1) auth_key, которые используются для идентификации конкретного ключа, используемого для шифрования сообщения.
Session_id – случайное 64-битное число, генерируемое клиентом с целью различить отдельные сеансы одного пользователя (на телефоне, на ПК несколько окон).
Server salt – случайное 64-битное число, меняющееся каждые 30 минут (отдельно для каждой сессии) по запросу сервера. Сообщения должны приниматься лишь с новой солью ( 😀 ), но старые валидны в течение 1800 секунд. Требуется для защиты от атак повторного воспроизведения.
Message identifier (msg_id) – 64-битное число, используемое для однозначной идентификации сообщения в сеансе. Идентификаторы сообщений сервера и клиента должны монотонно увеличиваться (в пределах одного сеанса), и указывать приблизительный момент создания сообщения.
Internal header – 16-байтовый заголовок, добавляемый перед сообщением и содержащий server salt и session_id.
Padding (12-1024 бит) – добавление ничего не значащих данных к информации, нацеленное на повышение криптостойкости.
Message key (msg_key) – средние 128 бит хеша (SHA-256) сообщения, которое надо зашифровать (учитывает при расчёте internal header и padding).
External header – 24-байтовый заголовок, добавляемый перед зашифрованным сообщением и содержащий auth_key_id и msg_key.
Key derivative function (KDF) – функция, формирующая один или несколько секретных ключей на основе секретного значения с помощью псевдослучайной функции (используется SHA-256).
Фух, тяжело, понимаю, прямо как на матане, когда на тебя вываливают гору определений, но крепитесь. Не забудьте открыть в дополнительном окне этот список, чтобы подсмотреть, что за что отвечает (людям с телефона RIP – мотайте).
Клиент-серверное шифрование
Давайте-ка взглянем на схему этого криптографического чуда:
Алгоритм шифрования для обычных сообщений.
Выглядит не так уж и сложно. Ну что тут: обычный Diffie-Hellman, SHA-256, AES – да и все, делов-то. Но давайте все же немного приоткроем завесу тайны и попробуем разобраться, что да как.
В общих словах работа алгоритма такова:
Собирается пакет для шифрования, состоящий из server salt, session_id, самого сообщения (в него включены время, длина и порядковый номер, которые проверяются на стороне получателя) и padding.
Далее, находится msg_key, 128 средних бита хеша (SHA-256) от сообщения с добавлением 32-байтового фрагмента auth_key.
Auth_key в комбинации с новонайденным msg_key определяет при помощи KDF 256-битный aes_key и 256-битный начальный вектор aes_iv.
Далее найденные значения aes_key и aes_iv используются в алгоритме AES IGE для шифрования сообщения.
В самом конце собирается пакет, состоящий из external header, а также зашифрованного сообщения.
Создание auth_key
Начнем с самой важной состовляющей, а в то же время с самой замороченной и интересной, с создания auth_key. В основном здесь используется RSA, SHA-1, AES и Diffie-Hellman, а также добавлены некоторые тонкости для еще большей надежности и секретности. Привожу схему (не пугайтесь):
Схема получения AK.
Поехали по порядку:
C: генерирует случайное 128-битное число (идентифицирует C в рамках этого процесса) и отправляет его.
S: генерирует свое случайное 128-битное число(далее
включены во все сообщения как в зашифрованном виде, так и в обычном), создает
натуральное число, являющееся произведением простых чисел
и
. После, отправляет
, где
– отпечаток RSA ключа равный 64 нижним битам хеша (SHA-1) известных
(публичный ключ RSA). В свою очередь S хранит
(закрытые ключи RSA) для дешифровки.
C: раскладывает на простые множители (реализовано для защиты от DoS атак), выбирает публичный ключ
, отвечающий одному из отпечатков
, генерирует еще одно случайное 256-битное число
, которое вместе с
используется для получения эфемерного ключа
и начального вектора
для последующей шифровки сообщения с помощью AES IGE. Посылает
, где
, а также введено обозначение
– шифрование с применением
.
S: выбирает параметры для алгоритма Diffie-Hellman, рассчитывает
для случайного 2048-битового числа
, вычисляет
. Посылает
, где
(
— серверное время).
C: вычисляет , рассчитывает
для случайного 2048-битового числа b, проводит советующие проверки для
(об этом чуть дальше), получает auth_key:
. Посылает
, где
, если это первая попытка посылки данного сообщения,
, если S позже запросит для повторного согласования auth_key в том же сеансе путем генерации нового
(такая ситуация может возникнуть при проверке уникальности, выполненной в конце).
S: получает auth_key: , затем удостоверяется что, полуенный ключ уникальный, сравнивая его хеш с хешами других auth_keys. Если хеш уникален, то S отправляет подтверждение
, в противном случае S отправляет сообщение об ошибке и процесс возвращается к стадии 4.
В течение работы этого алгоритма необходимо делать следующие проверки:
C должен проверить: и
являются простыми числами,
,
порождает циклическую подгруппу по простому основанию
.
C и S должны верифицировать, что . Также телеграмм рекомендует, чтобы обе стороны проверяли следующее:
.
C и S должны проверять, что msg_key равен 128 средним битам хеша (SHA-256) расшифрованных данных с добавлением 32-байтового фрагмента auth_key и что msg_id четное для сообщений от C, и нечетное в противном случае.
У C и S должны быть сохранены идентификаторы msg_id последних N сообщений. Если сообщение приходит с идентификатором меньшим или равным тем, что хранятся – сообщение игнорируется. В противном случае новый msg_id сохраняется, и если требуется, самый старый затирается.
Ну и каша, не правда ли (хотя здесь опущены многие технические фокусы).
Так-так, самое мудреное и сложное позади, здесь можно выдохнуть. На самом деле это все не так сложно, если позалипать в схему и вчитаться в ход алгоритма.
Немного про AES IGE
Вы могли заметить, как при создании auth_key, так и в основном алгоритме MTProto участвует некое шифрование AES IGE. Это всеми любимое AES шифрование, работающее в Infinite Garble Extension режиме. Пару слов про данный режим:
Схема AES IGE.
IGE режим задается следующим цепным равенством:
где – блок шифротекста,
– блок сообщения, которое необходимо закодировать,
– обозначение манипуляций, производимых над блоком сообщения сксоренным с блоком шифротекста предыдущей итерации во время выполнения блочного шифрования (в нашем случае AES) с ключом
,
– индекс, пробегающий от 1 до
(количество текстовых блоков, на которое разбито сообщение). Для запуска сего механизма требуются начальные значения
и
, которые могут быть получены из начального вектора
.
Получается, здесь разработчики не изобретали никаких велосипедов, а лишь повесили на руль звонок. Как они утверждают, их реализация IGE не сломанная и работает вполне прилично, а также тот факт, что ключ зависит от содержимого сообщения, сводит на нет блочно-адаптивные атаки.
Наберитесь сил, осталось совсем немного.
Секретные чаты и сквозное шифрование
Окей, вроде все секретно, все отлично. Но это все под защитой покуда у злоумышленника нет доступа до серверов тележки, так как на них хранится вся зашифрованная переписка вместе с ключами для того, чтобы иметь доступ в нескольких сессиях. Так-так, а вдруг я захочу обсудить с другом кто лучший из BTS, но не хотелось бы, чтобы это знали другие. В такой ситуации есть решение – секретные чаты. Давайте перейдем к рассмотрению сквозного шифрования в тележке.
Наше желание: только пользователи, которые инициируют общение, имеют доступ к переписке, и никто больше, при том только в одной сессии. Для этого нам требуется, чтобы у каждого пользователя были ключи, дающие возможность шифровки и дешифровки сообщений.
Схема шифрования почти идентична с предыдущей:
Алгоритм шифрования для секретных сообщений.
Опять же, в общих словах, по очереди происходит 2 идентичных шифрования с разными ключами SK и AK. Нам требуется добыть SK да так, чтобы никто и никогда не знал его. В этом нам опять же поможет всеми любимый алгоритм DH.
Схема получения SK перед вами (довольно знакомая, не правда ли):
Схема получения SK.
A: получает параметры от S, генерирует
(для определения текущей сессии), рассчитывает
Отправляет
, где
– запрос на инициализацию секретного чата от клиента A клиенту B.
B: при согласии на инициализацию секретного чата, получает параметры от S, рассчитывает
а также
и его 64-битный хеш (SHA1). Отправляет
.
A: рассчитывает и сверяет хеш (для удостоверения правильной работы клиентского приложения).
Собственно, все. Теперь сообщение сначала шифруется с использованием ключа SK и алгоритма, расписанного выше, а потом результат проходит второй круг шифрования с использованием AK и отправляется через сервер другому клиенту.
Rekeying (смена ключа шифрования)
Посидели разработчики тележки, посмотрели на все, что они наворотили, почесали репу, да решили, что как-то это все не слишком секретно, ненадежно. А вдруг кто-то получит секретный ключ и сможет расшифровать сообщения. Недолго думая, они взяли да реализовали алгоритм смены ключей. По их задумке секретные ключи, используемые в сквозном шифровании, сменяются каждые 100 сообщений или же каждую неделю. Старые ключи должны быть уничтожены и более не использоваться. Думаю, в третий раз разъяснение алгоритма DH – это уже перебор, поэтому просто приведу схему:
Схема смены ключа шифрования
На данном этапе разработчики почувствовали себя в безопасности и решили остановиться.
Заключение
Фух, смотрите, сколько Пашенька и его команда вложила души в секретность тележки, грех не пользоваться. Хотя, криптоаналитики и разработчики были в полном ужасе, увидав кашу из всех этих алгоритмов и своих тонкостей (в данной статье опущено множество технических подробностей, например, расчеты хэшей, а там все очень и очень запутанно).
Теперь запросы правительств о выдаче ключей шифрования звучат уж слишком смешно. Ну а нам, простым смертным, со спокойной душой можно пользоваться всеми нами любимой тележкой, покуда квантовые компьютеры не пришли и не раздали нам подзатыльники за наши математические фокусы.
Надеюсь, мне удалось донести основы того, как функционирует MTProto и стало немного яснее, как устроено шифрование в мессенджерах (устройство общее). А вам спасибо за ваше внимание и приглашаю в комментарии для дальнейшего обсуждения. Буду рад, если кто-то укажет неточности, ежели таковые заприметит.
P.S. И помните, когда вы отправляете одну-единственную скобку, реагируя на какую-то новость, вы заставляете ваш бедный телефон и сервера тележки вкалывать, чтобы это все было секретно.
Довольный, наверное понял, как работает MTProto.
Литература и ссылки на почитать
К сожалению или к счастью, все на английском.



