<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:turbo="http://turbo.yandex.ru" version="2.0">
<channel>
<title>MTProto - Протокол обмена сообщениями | О протоколе</title>
<link>https://www.mtproto.ru/</link>
<language>ru</language>
<description>MTProto - Протокол обмена сообщениями | О протоколе</description>
<generator>DataLife Engine</generator><item turbo="true">
<title>﻿Создание ключа авторизации</title>
<guid isPermaLink="true">https://www.mtproto.ru/mtproto/5-sozdanie-kljucha-avtorizacii.html</guid>
<link>https://www.mtproto.ru/mtproto/5-sozdanie-kljucha-avtorizacii.html</link>
<category><![CDATA[MTProto]]></category>
<dc:creator>mtproto</dc:creator>
<pubDate>Thu, 14 Jan 2021 04:57:18 +0300</pubDate>
<description><![CDATA[<div id="dev_page_content_wrap"><div id="dev_page_content"><p>Формат запроса описывается с использованием <a href="/mtproto/serialize">бинарных данных Serialization</a> и <a href="/mtproto/TL">TL</a> Language. Все большие числа передаются как строки, содержащие требуемую последовательность байтов в большом эндианном порядке. Хэш-функции, такие как SHA1, возвращающие строки (20 байтов), которые также могут быть интерпретированы как большие числа endian. Небольшие номера (<code>int</code>, <code>long</code>, <code>int128</code>, <code>int256</code>) обычно являются небольшими эндианами; однако, если они являются частью SHA1, байты не переставляются. В этом случае, если <code>long</code><code>x</code>это 64 бита нижнего порядка SHA1 из строки <code>s</code>, затем <i>последние</i> 8 байтов 20-байтной струны <code>SHA1(s)</code>Они берутся и интерпретируются как 64-битное целое число.</p><p>Перед отправкой незашифрованных сообщений (требуется в данном случае для генерации ключа авторизации) клиент должен пройти (p,q) авторизацию следующим образом.</p><h4><a class="anchor" href="#dh-exchange-initiation" id="dh-exchange-initiation" name="dh-exchange-initiation"></a>Инициация обмена DH</h4><h5><a class="anchor" href="#1-client-sends-query-to-server" id="1-client-sends-query-to-server" name="1-client-sends-query-to-server"></a>1) Клиент отправляет запрос на сервер</h5><pre class="language-markup"><code>req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;</code></pre><p>Значение <i>nonce</i> выбирается клиентом случайным образом (случайное число) и идентифицирует клиента в этом сообщении. Следуя этому шагу, он всем известен.</p><h5><a class="anchor" href="#2-server-sends-response-of-the-form" id="2-server-sends-response-of-the-form" name="2-server-sends-response-of-the-form"></a>2) Сервер отправляет ответ формы</h5><pre class="language-markup"><code>resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ;</code></pre><p>Здесь струнный pq представляет собой представление натурального числа (в двоичном большом формате endian). Это число является продуктом двух разных нечетных простых чисел. Обычно pq меньше или равен 2^63-1. Значение <i>server_nnce</i> выбирается сервером случайным образом; следуя этому шагу, он известен всем.</p><p><code>server_public_key_fingerprints</code>представляет собой список открытых отпечатков пальцев RSA (64 бита нижнего порядка SHA1 (server_public_key); открытый ключ представлен как голый тип <code>rsa_public_key n:string e:string = RSAPublicKey</code>, где, как обычно, n и e являются числами в большом эндиановом формате, сериализованными как строки байт, после чего SHA1 вычисляется) получен сервером.</p><p>Все последующие сообщения содержат пару (nonce, server_nonnce) как в простом тексте, так и в зашифрованных частях, что позволяет идентифицировать «временный сеанс» — один прогон протокола генерации ключа, описанного на этой странице, который использует одну и ту же (nonce, server_nnce) пару. Злоумышленник не мог создать параллельный сеанс с сервером с теми же параметрами и повторно использовать части зашифрованных сервером или клиентом сообщений для своих собственных целей в таком параллельном сеансе, потому что сервер для любого нового «временного сеанса» будет выбран другой сервером.</p><h4><a class="anchor" href="#proof-of-work" id="proof-of-work" name="proof-of-work"></a>Доказательство работы</h4><h5><a class="anchor" href="#3-client-decomposes-pq-into-prime-factors-such-that-p--q" id="3-client-decomposes-pq-into-prime-factors-such-that-p--q" name="3-client-decomposes-pq-into-prime-factors-such-that-p--q"></a>3) Клиент разлагает pq на простые факторы, такие, что p &lt; q.</h5><p>Это начинает раунд обмена ключами Диффи-Хеллмана.</p><h4><a class="anchor" href="#presenting-proof-of-work-server-authentication" id="presenting-proof-of-work-server-authentication" name="presenting-proof-of-work-server-authentication"></a>Представление доказательств работы; аутентификация сервера</h4><h5><a class="anchor" href="#4-encrypted-data-payload-generation" id="4-encrypted-data-payload-generation" name="4-encrypted-data-payload-generation"></a>4) <code>encrypted_data</code> Полезная нагрузка генерация</h5><p>Прежде всего, порождать <code>encrypted_data</code>Полезная нагрузка следующим образом:</p><ul><li><p>new_nonce := другое (хорошее) случайное число, сгенерированное клиентом; после этого запроса он известен как клиенту, так и серверу;</p></li><li><p>данные := сериализация</p><pre class="language-markup"><code>p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;</code></pre><p>или из</p><pre class="language-markup"><code>p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;</code></pre><p>где <code>dc</code>это идентификатор DC, с которым мы разговариваем; <code>10000</code>Он должен быть добавлен в DC ID при подключении к тестовым серверам; он должен быть отрицательным, если DC, к которому мы подключаемся, является медиа (не CDN) DC.</p></li><li><p>crypted_data := RSA_PAD (data, server_public_key), где RSA_PAD является версией RSA с вариантом набивки OAEP+, описанным ниже в 4.1).</p></li></ul><p>Кто-то может перехватить запрос и заменить его своим собственным, самостоятельно разлагая pq на факторы вместо клиента. Единственное поле, которое имеет смысл изменить, - это new_nonce, который был бы тем, которое нарушить должен был бы регенерировать (потому что злоумышленник не может расшифровать зашифрованные данные, отправленные клиентом). Поскольку все последующие сообщения шифруются с помощью new_nonce или содержат new_nonce_hash, они не будут обрабатываться клиентом (нарушитель не сможет заставить его выглядеть так, как будто они были сгенерированы сервером, потому что они не будут содержать new_nonce). Таким образом, этот перехват приведет только к тому, что злоумышленник завершит протокол генерации ключа авторизации вместо клиента и создаст новый ключ (который не имеет ничего общего с клиентом); однако тот же эффект может быть достигнут просто путем создания нового ключа от имени.</p><p>Альтернативная форма внутренних данных (<code>p_q_inner_data_temp_dc</code>) используется для создания временных ключей, которые хранятся только в RAM сервера и отбрасываются после максимума <code>expires_in</code>Секунды. Сервер может свободно отбрасывать свою копию раньше. Во всех остальных отношениях временный протокол генерации ключа одинаков. После создания временного ключа клиент обычно связывает его с основным ключом авторизации с помощью метода <a href="/method/auth.bindTempAuthKey">auth.bindTempAuthKey</a> и использует его для всех клиент-серверных сообщений до истечения срока его действия; затем генерируется новый временный ключ. Таким образом, достигается совершенная форвардная секретность (PFS) в клиент-серверной коммуникации. <a href="/api/pfs">Читать больше о PFS »</a></p><h5><a class="anchor" href="#41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows" id="41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows" name="41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows"></a>4.1) <code>RSA_PAD(data, server_public_key)</code> Вышеуказанные выше реализованы следующим образом:</h5><ul><li>data_with_padding := данные + random_padding_bytes; -- где выбран random_padding_bytes, поэтому полученная длина data_with_padding составляет ровно 192 байт, а данные - это TL-сериализованные данные, которые должны быть зашифрованы, как и раньше. Нужно проверить, что данные не длиннее 144 байтов.</li><li>data_pad_reversed := BYTE_REVERSE(data_with_padding); -- получается из data_with_padding путем обращения в порядок байт.</li><li>генерируется случайный 32-байт temp_key.</li><li>data_with_hash := data_pad_reversed + SHA256(temp_key + data_with_padding); -- после этого назначения data_with_hash составляет ровно 224 байт.</li><li>aes_encrypted := AES256_IGE(data_with_hash, temp_key, 0); -- AES256-IGE шифрование с нулем IV.</li><li>temp_key_xor := temp_key XOR SHA256(aes_encrypted); -- скорректированный ключ, 32 байт</li><li>key_aes_encrypted := temp_key_xor + aes_encrypted; -- ровно 256 байт (2048 бит) длиной</li><li>Значение key_aes_encrypted сравнивается с RSA-модулем server_pubkey как большой-эндиан 2048-битный (256-байт) неподписанный целое число. Если key_aes_encrypted оказывается больше или равен модулю RSA, предыдущие шаги, начиная с генерации нового случайного temp_key, повторяются. В противном случае выполняется заключительный этап:</li><li>Зашифровано_data := RSA(key_aes_encrypted, server_pubkey); -- 256-байтовый большой-эндианный целочисленный возведен в необходимую мощность из модуля открытого ключа RSA модуля RSA, и результат хранится как большое целое число, состоящее ровно из 256 байтов (с ведущим нулевым байтом, если требуется).</li></ul><h5><a class="anchor" href="#5-send-req-dh-params-query-with-generated-encrypted-data" id="5-send-req-dh-params-query-with-generated-encrypted-data" name="5-send-req-dh-params-query-with-generated-encrypted-data"></a>5) Отправить запрос req_DH_params сгенерированным <code>encrypted_data</code></h5><pre class="language-markup"><code>req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_dаta:string = Server_DH_Params</code></pre><h5><a class="anchor" href="#6-server-responds-with" id="6-server-responds-with" name="6-server-responds-with"></a>6) Сервер отвечает:</h5><pre class="language-markup"><code>server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;</code></pre><blockquote><p>Если запрос неправильный, сервер возвращает <code>-404</code>ошибка и рукопожатие должны быть перезапущены (любой последующий запрос также возвращается) <code>-404</code>Даже если это правильно).<br>А <code>-444</code>Ошибка также может быть возвращена, если тест DC ID передан в <code>p_q_inner_data_(_temp)dc</code>при рукопожатии с производством DC, и наоборот.</p></blockquote><p>Здесь crypted_answer получается следующим образом:</p><ul><li>new_nonce_hash := 128 битов более низкого порядка SHA1 (new_nnce);</li><li>Ответ := сериализация<pre class="language-markup"><code>server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;</code></pre></li><li>reply_with_hash := SHA1(ответ) + ответ + (0-15 случайных байтов); так что длина делится на 16;</li><li>tmp_aes_key := SHA1(new_nnce + server_nonce) + substr (SHA1(server_nonce + new_nonce), 0, 12);</li><li>tmp_aes_iv := substr (SHA1(server_nonce + new_nonce), 12, 8) + SHA1(new_nonce + new_nonce) + substr (new_nnce, 0, 4);</li><li>Зашифрованно_answer := AES256_ige_encrypt (answer_with_hash, tmp_aes_key, tmp_aes_iv); здесь tmp_aes_key - это 256-битный ключ, а tmp_aes_iv - 256-битный вектор инициализации. Как и во всех других случаях, использующих шифрование AES, зашифрованные данные дополняются случайными байтами на длину, делимую на 16 непосредственно перед шифрованием.</li></ul><p>Следуя этому шагу, new_nonce по-прежнему известен только клиенту и серверу. Клиент уверен, что это сервер, который ответил, и что ответ был сгенерирован специально в ответ на клиентский запрос req_DH_params, поскольку данные ответов шифруются с помощью new_nnce.</p><p>Ожидается, что клиент проверит, является ли <b>p = dh_prime</b> безопасным 2048-битным простым (что означает, что как <b>p,</b> так и <b>(p-1)/2</b> являются простыми, и что 2^2047 &lt; p &lt; 2^2048), и что <b>g</b> генерирует циклическую подгруппу прайм-порядка <b>(p-1)</b>/2, т.е. является квадратичным остатком <b>mod p</b>. Поскольку <b>g</b> всегда равен 2, 3, 4, 5, 6 или 7, это легко сделать с использованием квадратарного закона взаимности, давая простое условие на <b>p mod 4g</b>, а именно, <b>p mod 8 = 7</b> для <b>g = 2;</b> <b>p mod 3 =</b> <b>2 для g = 3;</b> нет дополнительного условия для <b>g = 4;</b> <b>p mod 5 =</b> <b>1</b> <b>или 4</b> для <b>g = 5 </b>; <b>p mod 24 = 19 или 23</b> для <b>g = 6</b> <b>p mod 7 = 3, 5 or 6</b><b>g = 7</b>; <b>p</b> После <b>того</b>, как <b>g</b> и <b>p</b> были проверены клиентом, имеет смысл кэшировать результат, чтобы не повторять длительные вычисления в будущем.</p><p>Если проверка занимает слишком много времени (что имеет место для старых мобильных устройств), можно было бы первоначально запустить только 15 итераций Миллера-Рабина для проверки первобытности <b>p</b> и <b>(p - 1)/2</b> с вероятностью ошибки, не превышающей одну миллиардную, и сделать больше итераций позже на заднем плане.</p><p>Другая оптимизация заключается в том, чтобы встроить в клиентский код приложения небольшую таблицу с некоторыми известными «хорошими» парами <b>(g,p)</b> (или просто известными безопасными простыми p<b>p</b>, поскольку условие <b>на g</b> легко проверяется во время выполнения), проверенных во время фазы генерации кода, чтобы вообще избежать такой проверки во время выполнения. Сервер редко меняет эти значения, поэтому обычно приходится помещать текущее значение <b>dh_prime</b> сервера в такую таблицу. Например, текущее значение <b>dh_prime</b> равны (в большом-эндианском байт-порядке)</p><h5><a class="anchor" href="#7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message" id="7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message" name="7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message"></a>7) Клиент вычисляет случайный 2048-битный номер <i>b</i> (используя достаточное количество энтропии) и отправляет серверу сообщение</h5><pre class="language-markup"><code>set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_dаta:string = Set_client_DH_params_answer;</code></pre><p>Здесь шифрованные_данные получают таким образом:</p><ul><li>g_b := pow(g, b) mod dh_prime;</li><li>данные := сериализация<pre class="language-markup"><code>client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data</code></pre></li><li>data_with_hash := SHA1(data) + data + (0-15 случайных байтов); так что длина делится на 16;</li><li>Зашифровано_data := AES256_ige_encrypt (data_with_hash, tmp_aes_key, tmp_aes_iv);</li></ul><p>Поле retry_id равно нулю во время первой попытки; в противном случае оно равно auth_key_aux_hash от предыдущей неудачной попытки (см. <a href="#9-server-responds-in-one-of-three-ways">Пункт</a> 9).</p><h5><a class="anchor" href="#8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime" id="8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime" name="8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime"></a>8) После этого auth_key равны <code>pow(g, {ab}) mod dh_prime</code>; на сервере, он вычисляется как <code>pow(g_b, a) mod dh_prime</code>, и на клиента как <code>(g_a)^b mod dh_prime</code>.</h5><p>Auth_key_hash вычисляется := 64 биты нижнего порядка SHA1 (auth_key). Сервер проверяет, есть ли уже другой ключ с тем же auth_key_hash, и отвечает одним из следующих способов.</p><h4><a class="anchor" href="#dh-key-exchange-complete" id="dh-key-exchange-complete" name="dh-key-exchange-complete"></a>DH обмен ключами завершен</h4><h5><a class="anchor" href="#9-server-responds-in-one-of-three-ways" id="9-server-responds-in-one-of-three-ways" name="9-server-responds-in-one-of-three-ways"></a>9) Сервер отвечает одним из трех способов:</h5><pre class="language-markup"><code>dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;</code></pre><ul><li>new_nonce_hash1, new_nonce_hash2, и new_nonce_hash3 получаются как 128 бит нижнего порядка SHA1 из строки байта, полученной из строки new_nonce, путем добавления одного байт со значением 1, 2, или 3, а затем еще 8 байтов с auth_key_aux_hash. Различные значения необходимы, чтобы нарушитель не изменил реакцию dh_gen_ok сервера на dh_gen_retry.</li><li>auth_key_aux_hash - это 64 биты <i>более высокого порядка</i> SHA1(auth_key). Его не следует путать с auth_key_hash.</li></ul><p>В другом случае клиент переходит к <a href="/mtproto/auth_key#7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message">пункту 7),</a> генерируя новый <i>b.</i> В первом случае клиент и сервер договорились auth_key, после чего забывают все другие временные данные, и клиент создает еще один зашифрованный сеанс с помощью auth_key. При этом сервер_salt изначально установлен на <code>substr(new_nonce, 0, 8) XOR substr(server_nonce, 0, 8)</code>. При необходимости клиент хранит разницу между сервером-временем, полученным в 5) и его местным временем, чтобы всегда иметь хорошее приближение времени сервера, которое требуется для генерации правильных идентификаторов сообщений.</p><p><b>ВАЖНО:</b> Помимо условий на Prime <b>dh_prime</b> и генераторе Diffie-Hellman <b>и</b> генератор g, обе стороны должны проверить, что <b>g,</b> <b>g_a</b> и <b>g_b</b> больше<b>,</b> чем <b>1</b> и меньше, чем <b>dh_prime -</b> 1. Мы рекомендуем проверить, что <b>g_a</b> и <b>g_b</b> находятся между <b>2^{2048-64}</b> и <b>dh_prime - 2^{2048-64}</b>.</p><h4><a class="anchor" href="#error-handling-lost-queries-and-responses" id="error-handling-lost-queries-and-responses" name="error-handling-lost-queries-and-responses"></a>Обработка ошибок (потерянные запросы и ответы)</h4><p>Если клиент не получает никакого ответа на свой запрос от сервера в течение определенного промежутка времени, он может просто повторно отправить запрос. Если сервер уже отправил ответ на этот запрос <i>(</i>точно такой же запрос и не просто похожий: все параметры во время повторного запроса должны принимать одни и те же значения), но он не попал к клиенту, сервер просто повторно отправит тот же ответ. Сервер запоминает ответ в течение 10 минут после получения запроса в 1). Если сервер уже забыл ответ или необходимые временные данные, клиенту придется начинать с самого начала.</p><p>Сервер может считать, что если клиент уже отправил в следующий запрос, используя данные из предыдущего ответа сервера на конкретного клиента, ответ, как известно, был получен клиентом и может быть забыт сервером.</p><h4><a class="anchor" href="#usage-example" id="usage-example" name="usage-example"></a>Пример использования</h4><p>Пример полного списка запросов, необходимых для генерации ключа авторизации, показан на <a href="/mtproto/samples-auth_key">отдельной странице.</a></p></div></div>]]></description>
<turbo:content><![CDATA[ <div id="dev_page_content_wrap"><div id="dev_page_content"><p>Формат запроса описывается с использованием <a href="/mtproto/serialize">бинарных данных Serialization</a> и <a href="/mtproto/TL">TL</a> Language. Все большие числа передаются как строки, содержащие требуемую последовательность байтов в большом эндианном порядке. Хэш-функции, такие как SHA1, возвращающие строки (20 байтов), которые также могут быть интерпретированы как большие числа endian. Небольшие номера (<code>int</code>, <code>long</code>, <code>int128</code>, <code>int256</code>) обычно являются небольшими эндианами; однако, если они являются частью SHA1, байты не переставляются. В этом случае, если <code>long</code><code>x</code>это 64 бита нижнего порядка SHA1 из строки <code>s</code>, затем <i>последние</i> 8 байтов 20-байтной струны <code>SHA1(s)</code>Они берутся и интерпретируются как 64-битное целое число.</p><p>Перед отправкой незашифрованных сообщений (требуется в данном случае для генерации ключа авторизации) клиент должен пройти (p,q) авторизацию следующим образом.</p><h4><a class="anchor" href="#dh-exchange-initiation" id="dh-exchange-initiation" name="dh-exchange-initiation"></a>Инициация обмена DH</h4><h5><a class="anchor" href="#1-client-sends-query-to-server" id="1-client-sends-query-to-server" name="1-client-sends-query-to-server"></a>1) Клиент отправляет запрос на сервер</h5><pre class="language-markup"><code>req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;</code></pre><p>Значение <i>nonce</i> выбирается клиентом случайным образом (случайное число) и идентифицирует клиента в этом сообщении. Следуя этому шагу, он всем известен.</p><h5><a class="anchor" href="#2-server-sends-response-of-the-form" id="2-server-sends-response-of-the-form" name="2-server-sends-response-of-the-form"></a>2) Сервер отправляет ответ формы</h5><pre class="language-markup"><code>resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ;</code></pre><p>Здесь струнный pq представляет собой представление натурального числа (в двоичном большом формате endian). Это число является продуктом двух разных нечетных простых чисел. Обычно pq меньше или равен 2^63-1. Значение <i>server_nnce</i> выбирается сервером случайным образом; следуя этому шагу, он известен всем.</p><p><code>server_public_key_fingerprints</code>представляет собой список открытых отпечатков пальцев RSA (64 бита нижнего порядка SHA1 (server_public_key); открытый ключ представлен как голый тип <code>rsa_public_key n:string e:string = RSAPublicKey</code>, где, как обычно, n и e являются числами в большом эндиановом формате, сериализованными как строки байт, после чего SHA1 вычисляется) получен сервером.</p><p>Все последующие сообщения содержат пару (nonce, server_nonnce) как в простом тексте, так и в зашифрованных частях, что позволяет идентифицировать «временный сеанс» — один прогон протокола генерации ключа, описанного на этой странице, который использует одну и ту же (nonce, server_nnce) пару. Злоумышленник не мог создать параллельный сеанс с сервером с теми же параметрами и повторно использовать части зашифрованных сервером или клиентом сообщений для своих собственных целей в таком параллельном сеансе, потому что сервер для любого нового «временного сеанса» будет выбран другой сервером.</p><h4><a class="anchor" href="#proof-of-work" id="proof-of-work" name="proof-of-work"></a>Доказательство работы</h4><h5><a class="anchor" href="#3-client-decomposes-pq-into-prime-factors-such-that-p--q" id="3-client-decomposes-pq-into-prime-factors-such-that-p--q" name="3-client-decomposes-pq-into-prime-factors-such-that-p--q"></a>3) Клиент разлагает pq на простые факторы, такие, что p &lt; q.</h5><p>Это начинает раунд обмена ключами Диффи-Хеллмана.</p><h4><a class="anchor" href="#presenting-proof-of-work-server-authentication" id="presenting-proof-of-work-server-authentication" name="presenting-proof-of-work-server-authentication"></a>Представление доказательств работы; аутентификация сервера</h4><h5><a class="anchor" href="#4-encrypted-data-payload-generation" id="4-encrypted-data-payload-generation" name="4-encrypted-data-payload-generation"></a>4) <code>encrypted_data</code> Полезная нагрузка генерация</h5><p>Прежде всего, порождать <code>encrypted_data</code>Полезная нагрузка следующим образом:</p><ul><li><p>new_nonce := другое (хорошее) случайное число, сгенерированное клиентом; после этого запроса он известен как клиенту, так и серверу;</p></li><li><p>данные := сериализация</p><pre class="language-markup"><code>p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;</code></pre><p>или из</p><pre class="language-markup"><code>p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;</code></pre><p>где <code>dc</code>это идентификатор DC, с которым мы разговариваем; <code>10000</code>Он должен быть добавлен в DC ID при подключении к тестовым серверам; он должен быть отрицательным, если DC, к которому мы подключаемся, является медиа (не CDN) DC.</p></li><li><p>crypted_data := RSA_PAD (data, server_public_key), где RSA_PAD является версией RSA с вариантом набивки OAEP+, описанным ниже в 4.1).</p></li></ul><p>Кто-то может перехватить запрос и заменить его своим собственным, самостоятельно разлагая pq на факторы вместо клиента. Единственное поле, которое имеет смысл изменить, - это new_nonce, который был бы тем, которое нарушить должен был бы регенерировать (потому что злоумышленник не может расшифровать зашифрованные данные, отправленные клиентом). Поскольку все последующие сообщения шифруются с помощью new_nonce или содержат new_nonce_hash, они не будут обрабатываться клиентом (нарушитель не сможет заставить его выглядеть так, как будто они были сгенерированы сервером, потому что они не будут содержать new_nonce). Таким образом, этот перехват приведет только к тому, что злоумышленник завершит протокол генерации ключа авторизации вместо клиента и создаст новый ключ (который не имеет ничего общего с клиентом); однако тот же эффект может быть достигнут просто путем создания нового ключа от имени.</p><p>Альтернативная форма внутренних данных (<code>p_q_inner_data_temp_dc</code>) используется для создания временных ключей, которые хранятся только в RAM сервера и отбрасываются после максимума <code>expires_in</code>Секунды. Сервер может свободно отбрасывать свою копию раньше. Во всех остальных отношениях временный протокол генерации ключа одинаков. После создания временного ключа клиент обычно связывает его с основным ключом авторизации с помощью метода <a href="/method/auth.bindTempAuthKey">auth.bindTempAuthKey</a> и использует его для всех клиент-серверных сообщений до истечения срока его действия; затем генерируется новый временный ключ. Таким образом, достигается совершенная форвардная секретность (PFS) в клиент-серверной коммуникации. <a href="/api/pfs">Читать больше о PFS »</a></p><h5><a class="anchor" href="#41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows" id="41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows" name="41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows"></a>4.1) <code>RSA_PAD(data, server_public_key)</code> Вышеуказанные выше реализованы следующим образом:</h5><ul><li>data_with_padding := данные + random_padding_bytes; -- где выбран random_padding_bytes, поэтому полученная длина data_with_padding составляет ровно 192 байт, а данные - это TL-сериализованные данные, которые должны быть зашифрованы, как и раньше. Нужно проверить, что данные не длиннее 144 байтов.</li><li>data_pad_reversed := BYTE_REVERSE(data_with_padding); -- получается из data_with_padding путем обращения в порядок байт.</li><li>генерируется случайный 32-байт temp_key.</li><li>data_with_hash := data_pad_reversed + SHA256(temp_key + data_with_padding); -- после этого назначения data_with_hash составляет ровно 224 байт.</li><li>aes_encrypted := AES256_IGE(data_with_hash, temp_key, 0); -- AES256-IGE шифрование с нулем IV.</li><li>temp_key_xor := temp_key XOR SHA256(aes_encrypted); -- скорректированный ключ, 32 байт</li><li>key_aes_encrypted := temp_key_xor + aes_encrypted; -- ровно 256 байт (2048 бит) длиной</li><li>Значение key_aes_encrypted сравнивается с RSA-модулем server_pubkey как большой-эндиан 2048-битный (256-байт) неподписанный целое число. Если key_aes_encrypted оказывается больше или равен модулю RSA, предыдущие шаги, начиная с генерации нового случайного temp_key, повторяются. В противном случае выполняется заключительный этап:</li><li>Зашифровано_data := RSA(key_aes_encrypted, server_pubkey); -- 256-байтовый большой-эндианный целочисленный возведен в необходимую мощность из модуля открытого ключа RSA модуля RSA, и результат хранится как большое целое число, состоящее ровно из 256 байтов (с ведущим нулевым байтом, если требуется).</li></ul><h5><a class="anchor" href="#5-send-req-dh-params-query-with-generated-encrypted-data" id="5-send-req-dh-params-query-with-generated-encrypted-data" name="5-send-req-dh-params-query-with-generated-encrypted-data"></a>5) Отправить запрос req_DH_params сгенерированным <code>encrypted_data</code></h5><pre class="language-markup"><code>req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_dаta:string = Server_DH_Params</code></pre><h5><a class="anchor" href="#6-server-responds-with" id="6-server-responds-with" name="6-server-responds-with"></a>6) Сервер отвечает:</h5><pre class="language-markup"><code>server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;</code></pre><blockquote><p>Если запрос неправильный, сервер возвращает <code>-404</code>ошибка и рукопожатие должны быть перезапущены (любой последующий запрос также возвращается) <code>-404</code>Даже если это правильно).<br>А <code>-444</code>Ошибка также может быть возвращена, если тест DC ID передан в <code>p_q_inner_data_(_temp)dc</code>при рукопожатии с производством DC, и наоборот.</p></blockquote><p>Здесь crypted_answer получается следующим образом:</p><ul><li>new_nonce_hash := 128 битов более низкого порядка SHA1 (new_nnce);</li><li>Ответ := сериализация<pre class="language-markup"><code>server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;</code></pre></li><li>reply_with_hash := SHA1(ответ) + ответ + (0-15 случайных байтов); так что длина делится на 16;</li><li>tmp_aes_key := SHA1(new_nnce + server_nonce) + substr (SHA1(server_nonce + new_nonce), 0, 12);</li><li>tmp_aes_iv := substr (SHA1(server_nonce + new_nonce), 12, 8) + SHA1(new_nonce + new_nonce) + substr (new_nnce, 0, 4);</li><li>Зашифрованно_answer := AES256_ige_encrypt (answer_with_hash, tmp_aes_key, tmp_aes_iv); здесь tmp_aes_key - это 256-битный ключ, а tmp_aes_iv - 256-битный вектор инициализации. Как и во всех других случаях, использующих шифрование AES, зашифрованные данные дополняются случайными байтами на длину, делимую на 16 непосредственно перед шифрованием.</li></ul><p>Следуя этому шагу, new_nonce по-прежнему известен только клиенту и серверу. Клиент уверен, что это сервер, который ответил, и что ответ был сгенерирован специально в ответ на клиентский запрос req_DH_params, поскольку данные ответов шифруются с помощью new_nnce.</p><p>Ожидается, что клиент проверит, является ли <b>p = dh_prime</b> безопасным 2048-битным простым (что означает, что как <b>p,</b> так и <b>(p-1)/2</b> являются простыми, и что 2^2047 &lt; p &lt; 2^2048), и что <b>g</b> генерирует циклическую подгруппу прайм-порядка <b>(p-1)</b>/2, т.е. является квадратичным остатком <b>mod p</b>. Поскольку <b>g</b> всегда равен 2, 3, 4, 5, 6 или 7, это легко сделать с использованием квадратарного закона взаимности, давая простое условие на <b>p mod 4g</b>, а именно, <b>p mod 8 = 7</b> для <b>g = 2;</b> <b>p mod 3 =</b> <b>2 для g = 3;</b> нет дополнительного условия для <b>g = 4;</b> <b>p mod 5 =</b> <b>1</b> <b>или 4</b> для <b>g = 5 </b>; <b>p mod 24 = 19 или 23</b> для <b>g = 6</b> <b>p mod 7 = 3, 5 or 6</b><b>g = 7</b>; <b>p</b> После <b>того</b>, как <b>g</b> и <b>p</b> были проверены клиентом, имеет смысл кэшировать результат, чтобы не повторять длительные вычисления в будущем.</p><p>Если проверка занимает слишком много времени (что имеет место для старых мобильных устройств), можно было бы первоначально запустить только 15 итераций Миллера-Рабина для проверки первобытности <b>p</b> и <b>(p - 1)/2</b> с вероятностью ошибки, не превышающей одну миллиардную, и сделать больше итераций позже на заднем плане.</p><p>Другая оптимизация заключается в том, чтобы встроить в клиентский код приложения небольшую таблицу с некоторыми известными «хорошими» парами <b>(g,p)</b> (или просто известными безопасными простыми p<b>p</b>, поскольку условие <b>на g</b> легко проверяется во время выполнения), проверенных во время фазы генерации кода, чтобы вообще избежать такой проверки во время выполнения. Сервер редко меняет эти значения, поэтому обычно приходится помещать текущее значение <b>dh_prime</b> сервера в такую таблицу. Например, текущее значение <b>dh_prime</b> равны (в большом-эндианском байт-порядке)</p><h5><a class="anchor" href="#7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message" id="7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message" name="7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message"></a>7) Клиент вычисляет случайный 2048-битный номер <i>b</i> (используя достаточное количество энтропии) и отправляет серверу сообщение</h5><pre class="language-markup"><code>set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_dаta:string = Set_client_DH_params_answer;</code></pre><p>Здесь шифрованные_данные получают таким образом:</p><ul><li>g_b := pow(g, b) mod dh_prime;</li><li>данные := сериализация<pre class="language-markup"><code>client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data</code></pre></li><li>data_with_hash := SHA1(data) + data + (0-15 случайных байтов); так что длина делится на 16;</li><li>Зашифровано_data := AES256_ige_encrypt (data_with_hash, tmp_aes_key, tmp_aes_iv);</li></ul><p>Поле retry_id равно нулю во время первой попытки; в противном случае оно равно auth_key_aux_hash от предыдущей неудачной попытки (см. <a href="#9-server-responds-in-one-of-three-ways">Пункт</a> 9).</p><h5><a class="anchor" href="#8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime" id="8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime" name="8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime"></a>8) После этого auth_key равны <code>pow(g, {ab}) mod dh_prime</code>; на сервере, он вычисляется как <code>pow(g_b, a) mod dh_prime</code>, и на клиента как <code>(g_a)^b mod dh_prime</code>.</h5><p>Auth_key_hash вычисляется := 64 биты нижнего порядка SHA1 (auth_key). Сервер проверяет, есть ли уже другой ключ с тем же auth_key_hash, и отвечает одним из следующих способов.</p><h4><a class="anchor" href="#dh-key-exchange-complete" id="dh-key-exchange-complete" name="dh-key-exchange-complete"></a>DH обмен ключами завершен</h4><h5><a class="anchor" href="#9-server-responds-in-one-of-three-ways" id="9-server-responds-in-one-of-three-ways" name="9-server-responds-in-one-of-three-ways"></a>9) Сервер отвечает одним из трех способов:</h5><pre class="language-markup"><code>dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;</code></pre><ul><li>new_nonce_hash1, new_nonce_hash2, и new_nonce_hash3 получаются как 128 бит нижнего порядка SHA1 из строки байта, полученной из строки new_nonce, путем добавления одного байт со значением 1, 2, или 3, а затем еще 8 байтов с auth_key_aux_hash. Различные значения необходимы, чтобы нарушитель не изменил реакцию dh_gen_ok сервера на dh_gen_retry.</li><li>auth_key_aux_hash - это 64 биты <i>более высокого порядка</i> SHA1(auth_key). Его не следует путать с auth_key_hash.</li></ul><p>В другом случае клиент переходит к <a href="/mtproto/auth_key#7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message">пункту 7),</a> генерируя новый <i>b.</i> В первом случае клиент и сервер договорились auth_key, после чего забывают все другие временные данные, и клиент создает еще один зашифрованный сеанс с помощью auth_key. При этом сервер_salt изначально установлен на <code>substr(new_nonce, 0, 8) XOR substr(server_nonce, 0, 8)</code>. При необходимости клиент хранит разницу между сервером-временем, полученным в 5) и его местным временем, чтобы всегда иметь хорошее приближение времени сервера, которое требуется для генерации правильных идентификаторов сообщений.</p><p><b>ВАЖНО:</b> Помимо условий на Prime <b>dh_prime</b> и генераторе Diffie-Hellman <b>и</b> генератор g, обе стороны должны проверить, что <b>g,</b> <b>g_a</b> и <b>g_b</b> больше<b>,</b> чем <b>1</b> и меньше, чем <b>dh_prime -</b> 1. Мы рекомендуем проверить, что <b>g_a</b> и <b>g_b</b> находятся между <b>2^{2048-64}</b> и <b>dh_prime - 2^{2048-64}</b>.</p><h4><a class="anchor" href="#error-handling-lost-queries-and-responses" id="error-handling-lost-queries-and-responses" name="error-handling-lost-queries-and-responses"></a>Обработка ошибок (потерянные запросы и ответы)</h4><p>Если клиент не получает никакого ответа на свой запрос от сервера в течение определенного промежутка времени, он может просто повторно отправить запрос. Если сервер уже отправил ответ на этот запрос <i>(</i>точно такой же запрос и не просто похожий: все параметры во время повторного запроса должны принимать одни и те же значения), но он не попал к клиенту, сервер просто повторно отправит тот же ответ. Сервер запоминает ответ в течение 10 минут после получения запроса в 1). Если сервер уже забыл ответ или необходимые временные данные, клиенту придется начинать с самого начала.</p><p>Сервер может считать, что если клиент уже отправил в следующий запрос, используя данные из предыдущего ответа сервера на конкретного клиента, ответ, как известно, был получен клиентом и может быть забыт сервером.</p><h4><a class="anchor" href="#usage-example" id="usage-example" name="usage-example"></a>Пример использования</h4><p>Пример полного списка запросов, необходимых для генерации ключа авторизации, показан на <a href="/mtproto/samples-auth_key">отдельной странице.</a></p></div></div> ]]></turbo:content>
<content:encoded><![CDATA[ <div id="dev_page_content_wrap"><div id="dev_page_content"><p>Формат запроса описывается с использованием <a href="/mtproto/serialize">бинарных данных Serialization</a> и <a href="/mtproto/TL">TL</a> Language. Все большие числа передаются как строки, содержащие требуемую последовательность байтов в большом эндианном порядке. Хэш-функции, такие как SHA1, возвращающие строки (20 байтов), которые также могут быть интерпретированы как большие числа endian. Небольшие номера (<code>int</code>, <code>long</code>, <code>int128</code>, <code>int256</code>) обычно являются небольшими эндианами; однако, если они являются частью SHA1, байты не переставляются. В этом случае, если <code>long</code><code>x</code>это 64 бита нижнего порядка SHA1 из строки <code>s</code>, затем <i>последние</i> 8 байтов 20-байтной струны <code>SHA1(s)</code>Они берутся и интерпретируются как 64-битное целое число.</p><p>Перед отправкой незашифрованных сообщений (требуется в данном случае для генерации ключа авторизации) клиент должен пройти (p,q) авторизацию следующим образом.</p><h4><a class="anchor" href="#dh-exchange-initiation" id="dh-exchange-initiation" name="dh-exchange-initiation"></a>Инициация обмена DH</h4><h5><a class="anchor" href="#1-client-sends-query-to-server" id="1-client-sends-query-to-server" name="1-client-sends-query-to-server"></a>1) Клиент отправляет запрос на сервер</h5><pre class="language-markup"><code>req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;</code></pre><p>Значение <i>nonce</i> выбирается клиентом случайным образом (случайное число) и идентифицирует клиента в этом сообщении. Следуя этому шагу, он всем известен.</p><h5><a class="anchor" href="#2-server-sends-response-of-the-form" id="2-server-sends-response-of-the-form" name="2-server-sends-response-of-the-form"></a>2) Сервер отправляет ответ формы</h5><pre class="language-markup"><code>resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ;</code></pre><p>Здесь струнный pq представляет собой представление натурального числа (в двоичном большом формате endian). Это число является продуктом двух разных нечетных простых чисел. Обычно pq меньше или равен 2^63-1. Значение <i>server_nnce</i> выбирается сервером случайным образом; следуя этому шагу, он известен всем.</p><p><code>server_public_key_fingerprints</code>представляет собой список открытых отпечатков пальцев RSA (64 бита нижнего порядка SHA1 (server_public_key); открытый ключ представлен как голый тип <code>rsa_public_key n:string e:string = RSAPublicKey</code>, где, как обычно, n и e являются числами в большом эндиановом формате, сериализованными как строки байт, после чего SHA1 вычисляется) получен сервером.</p><p>Все последующие сообщения содержат пару (nonce, server_nonnce) как в простом тексте, так и в зашифрованных частях, что позволяет идентифицировать «временный сеанс» — один прогон протокола генерации ключа, описанного на этой странице, который использует одну и ту же (nonce, server_nnce) пару. Злоумышленник не мог создать параллельный сеанс с сервером с теми же параметрами и повторно использовать части зашифрованных сервером или клиентом сообщений для своих собственных целей в таком параллельном сеансе, потому что сервер для любого нового «временного сеанса» будет выбран другой сервером.</p><h4><a class="anchor" href="#proof-of-work" id="proof-of-work" name="proof-of-work"></a>Доказательство работы</h4><h5><a class="anchor" href="#3-client-decomposes-pq-into-prime-factors-such-that-p--q" id="3-client-decomposes-pq-into-prime-factors-such-that-p--q" name="3-client-decomposes-pq-into-prime-factors-such-that-p--q"></a>3) Клиент разлагает pq на простые факторы, такие, что p &lt; q.</h5><p>Это начинает раунд обмена ключами Диффи-Хеллмана.</p><h4><a class="anchor" href="#presenting-proof-of-work-server-authentication" id="presenting-proof-of-work-server-authentication" name="presenting-proof-of-work-server-authentication"></a>Представление доказательств работы; аутентификация сервера</h4><h5><a class="anchor" href="#4-encrypted-data-payload-generation" id="4-encrypted-data-payload-generation" name="4-encrypted-data-payload-generation"></a>4) <code>encrypted_data</code> Полезная нагрузка генерация</h5><p>Прежде всего, порождать <code>encrypted_data</code>Полезная нагрузка следующим образом:</p><ul><li><p>new_nonce := другое (хорошее) случайное число, сгенерированное клиентом; после этого запроса он известен как клиенту, так и серверу;</p></li><li><p>данные := сериализация</p><pre class="language-markup"><code>p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;</code></pre><p>или из</p><pre class="language-markup"><code>p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;</code></pre><p>где <code>dc</code>это идентификатор DC, с которым мы разговариваем; <code>10000</code>Он должен быть добавлен в DC ID при подключении к тестовым серверам; он должен быть отрицательным, если DC, к которому мы подключаемся, является медиа (не CDN) DC.</p></li><li><p>crypted_data := RSA_PAD (data, server_public_key), где RSA_PAD является версией RSA с вариантом набивки OAEP+, описанным ниже в 4.1).</p></li></ul><p>Кто-то может перехватить запрос и заменить его своим собственным, самостоятельно разлагая pq на факторы вместо клиента. Единственное поле, которое имеет смысл изменить, - это new_nonce, который был бы тем, которое нарушить должен был бы регенерировать (потому что злоумышленник не может расшифровать зашифрованные данные, отправленные клиентом). Поскольку все последующие сообщения шифруются с помощью new_nonce или содержат new_nonce_hash, они не будут обрабатываться клиентом (нарушитель не сможет заставить его выглядеть так, как будто они были сгенерированы сервером, потому что они не будут содержать new_nonce). Таким образом, этот перехват приведет только к тому, что злоумышленник завершит протокол генерации ключа авторизации вместо клиента и создаст новый ключ (который не имеет ничего общего с клиентом); однако тот же эффект может быть достигнут просто путем создания нового ключа от имени.</p><p>Альтернативная форма внутренних данных (<code>p_q_inner_data_temp_dc</code>) используется для создания временных ключей, которые хранятся только в RAM сервера и отбрасываются после максимума <code>expires_in</code>Секунды. Сервер может свободно отбрасывать свою копию раньше. Во всех остальных отношениях временный протокол генерации ключа одинаков. После создания временного ключа клиент обычно связывает его с основным ключом авторизации с помощью метода <a href="/method/auth.bindTempAuthKey">auth.bindTempAuthKey</a> и использует его для всех клиент-серверных сообщений до истечения срока его действия; затем генерируется новый временный ключ. Таким образом, достигается совершенная форвардная секретность (PFS) в клиент-серверной коммуникации. <a href="/api/pfs">Читать больше о PFS »</a></p><h5><a class="anchor" href="#41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows" id="41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows" name="41-rsa-paddata-server-public-key-mentioned-above-is-implemented-as-follows"></a>4.1) <code>RSA_PAD(data, server_public_key)</code> Вышеуказанные выше реализованы следующим образом:</h5><ul><li>data_with_padding := данные + random_padding_bytes; -- где выбран random_padding_bytes, поэтому полученная длина data_with_padding составляет ровно 192 байт, а данные - это TL-сериализованные данные, которые должны быть зашифрованы, как и раньше. Нужно проверить, что данные не длиннее 144 байтов.</li><li>data_pad_reversed := BYTE_REVERSE(data_with_padding); -- получается из data_with_padding путем обращения в порядок байт.</li><li>генерируется случайный 32-байт temp_key.</li><li>data_with_hash := data_pad_reversed + SHA256(temp_key + data_with_padding); -- после этого назначения data_with_hash составляет ровно 224 байт.</li><li>aes_encrypted := AES256_IGE(data_with_hash, temp_key, 0); -- AES256-IGE шифрование с нулем IV.</li><li>temp_key_xor := temp_key XOR SHA256(aes_encrypted); -- скорректированный ключ, 32 байт</li><li>key_aes_encrypted := temp_key_xor + aes_encrypted; -- ровно 256 байт (2048 бит) длиной</li><li>Значение key_aes_encrypted сравнивается с RSA-модулем server_pubkey как большой-эндиан 2048-битный (256-байт) неподписанный целое число. Если key_aes_encrypted оказывается больше или равен модулю RSA, предыдущие шаги, начиная с генерации нового случайного temp_key, повторяются. В противном случае выполняется заключительный этап:</li><li>Зашифровано_data := RSA(key_aes_encrypted, server_pubkey); -- 256-байтовый большой-эндианный целочисленный возведен в необходимую мощность из модуля открытого ключа RSA модуля RSA, и результат хранится как большое целое число, состоящее ровно из 256 байтов (с ведущим нулевым байтом, если требуется).</li></ul><h5><a class="anchor" href="#5-send-req-dh-params-query-with-generated-encrypted-data" id="5-send-req-dh-params-query-with-generated-encrypted-data" name="5-send-req-dh-params-query-with-generated-encrypted-data"></a>5) Отправить запрос req_DH_params сгенерированным <code>encrypted_data</code></h5><pre class="language-markup"><code>req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_dаta:string = Server_DH_Params</code></pre><h5><a class="anchor" href="#6-server-responds-with" id="6-server-responds-with" name="6-server-responds-with"></a>6) Сервер отвечает:</h5><pre class="language-markup"><code>server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;</code></pre><blockquote><p>Если запрос неправильный, сервер возвращает <code>-404</code>ошибка и рукопожатие должны быть перезапущены (любой последующий запрос также возвращается) <code>-404</code>Даже если это правильно).<br>А <code>-444</code>Ошибка также может быть возвращена, если тест DC ID передан в <code>p_q_inner_data_(_temp)dc</code>при рукопожатии с производством DC, и наоборот.</p></blockquote><p>Здесь crypted_answer получается следующим образом:</p><ul><li>new_nonce_hash := 128 битов более низкого порядка SHA1 (new_nnce);</li><li>Ответ := сериализация<pre class="language-markup"><code>server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;</code></pre></li><li>reply_with_hash := SHA1(ответ) + ответ + (0-15 случайных байтов); так что длина делится на 16;</li><li>tmp_aes_key := SHA1(new_nnce + server_nonce) + substr (SHA1(server_nonce + new_nonce), 0, 12);</li><li>tmp_aes_iv := substr (SHA1(server_nonce + new_nonce), 12, 8) + SHA1(new_nonce + new_nonce) + substr (new_nnce, 0, 4);</li><li>Зашифрованно_answer := AES256_ige_encrypt (answer_with_hash, tmp_aes_key, tmp_aes_iv); здесь tmp_aes_key - это 256-битный ключ, а tmp_aes_iv - 256-битный вектор инициализации. Как и во всех других случаях, использующих шифрование AES, зашифрованные данные дополняются случайными байтами на длину, делимую на 16 непосредственно перед шифрованием.</li></ul><p>Следуя этому шагу, new_nonce по-прежнему известен только клиенту и серверу. Клиент уверен, что это сервер, который ответил, и что ответ был сгенерирован специально в ответ на клиентский запрос req_DH_params, поскольку данные ответов шифруются с помощью new_nnce.</p><p>Ожидается, что клиент проверит, является ли <b>p = dh_prime</b> безопасным 2048-битным простым (что означает, что как <b>p,</b> так и <b>(p-1)/2</b> являются простыми, и что 2^2047 &lt; p &lt; 2^2048), и что <b>g</b> генерирует циклическую подгруппу прайм-порядка <b>(p-1)</b>/2, т.е. является квадратичным остатком <b>mod p</b>. Поскольку <b>g</b> всегда равен 2, 3, 4, 5, 6 или 7, это легко сделать с использованием квадратарного закона взаимности, давая простое условие на <b>p mod 4g</b>, а именно, <b>p mod 8 = 7</b> для <b>g = 2;</b> <b>p mod 3 =</b> <b>2 для g = 3;</b> нет дополнительного условия для <b>g = 4;</b> <b>p mod 5 =</b> <b>1</b> <b>или 4</b> для <b>g = 5 </b>; <b>p mod 24 = 19 или 23</b> для <b>g = 6</b> <b>p mod 7 = 3, 5 or 6</b><b>g = 7</b>; <b>p</b> После <b>того</b>, как <b>g</b> и <b>p</b> были проверены клиентом, имеет смысл кэшировать результат, чтобы не повторять длительные вычисления в будущем.</p><p>Если проверка занимает слишком много времени (что имеет место для старых мобильных устройств), можно было бы первоначально запустить только 15 итераций Миллера-Рабина для проверки первобытности <b>p</b> и <b>(p - 1)/2</b> с вероятностью ошибки, не превышающей одну миллиардную, и сделать больше итераций позже на заднем плане.</p><p>Другая оптимизация заключается в том, чтобы встроить в клиентский код приложения небольшую таблицу с некоторыми известными «хорошими» парами <b>(g,p)</b> (или просто известными безопасными простыми p<b>p</b>, поскольку условие <b>на g</b> легко проверяется во время выполнения), проверенных во время фазы генерации кода, чтобы вообще избежать такой проверки во время выполнения. Сервер редко меняет эти значения, поэтому обычно приходится помещать текущее значение <b>dh_prime</b> сервера в такую таблицу. Например, текущее значение <b>dh_prime</b> равны (в большом-эндианском байт-порядке)</p><h5><a class="anchor" href="#7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message" id="7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message" name="7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message"></a>7) Клиент вычисляет случайный 2048-битный номер <i>b</i> (используя достаточное количество энтропии) и отправляет серверу сообщение</h5><pre class="language-markup"><code>set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_dаta:string = Set_client_DH_params_answer;</code></pre><p>Здесь шифрованные_данные получают таким образом:</p><ul><li>g_b := pow(g, b) mod dh_prime;</li><li>данные := сериализация<pre class="language-markup"><code>client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data</code></pre></li><li>data_with_hash := SHA1(data) + data + (0-15 случайных байтов); так что длина делится на 16;</li><li>Зашифровано_data := AES256_ige_encrypt (data_with_hash, tmp_aes_key, tmp_aes_iv);</li></ul><p>Поле retry_id равно нулю во время первой попытки; в противном случае оно равно auth_key_aux_hash от предыдущей неудачной попытки (см. <a href="#9-server-responds-in-one-of-three-ways">Пункт</a> 9).</p><h5><a class="anchor" href="#8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime" id="8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime" name="8-thereafter-auth-key-equals-powg-ab-mod-dh-prime-on-the-server-it-is-computed-as-powg-b-a-mod-dh-prime-and-on-the-client-as-g-ab-mod-dh-prime"></a>8) После этого auth_key равны <code>pow(g, {ab}) mod dh_prime</code>; на сервере, он вычисляется как <code>pow(g_b, a) mod dh_prime</code>, и на клиента как <code>(g_a)^b mod dh_prime</code>.</h5><p>Auth_key_hash вычисляется := 64 биты нижнего порядка SHA1 (auth_key). Сервер проверяет, есть ли уже другой ключ с тем же auth_key_hash, и отвечает одним из следующих способов.</p><h4><a class="anchor" href="#dh-key-exchange-complete" id="dh-key-exchange-complete" name="dh-key-exchange-complete"></a>DH обмен ключами завершен</h4><h5><a class="anchor" href="#9-server-responds-in-one-of-three-ways" id="9-server-responds-in-one-of-three-ways" name="9-server-responds-in-one-of-three-ways"></a>9) Сервер отвечает одним из трех способов:</h5><pre class="language-markup"><code>dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;</code></pre><ul><li>new_nonce_hash1, new_nonce_hash2, и new_nonce_hash3 получаются как 128 бит нижнего порядка SHA1 из строки байта, полученной из строки new_nonce, путем добавления одного байт со значением 1, 2, или 3, а затем еще 8 байтов с auth_key_aux_hash. Различные значения необходимы, чтобы нарушитель не изменил реакцию dh_gen_ok сервера на dh_gen_retry.</li><li>auth_key_aux_hash - это 64 биты <i>более высокого порядка</i> SHA1(auth_key). Его не следует путать с auth_key_hash.</li></ul><p>В другом случае клиент переходит к <a href="/mtproto/auth_key#7-client-computes-random-2048-bit-number-b-using-a-sufficient-amount-of-entropy-and-sends-the-server-a-message">пункту 7),</a> генерируя новый <i>b.</i> В первом случае клиент и сервер договорились auth_key, после чего забывают все другие временные данные, и клиент создает еще один зашифрованный сеанс с помощью auth_key. При этом сервер_salt изначально установлен на <code>substr(new_nonce, 0, 8) XOR substr(server_nonce, 0, 8)</code>. При необходимости клиент хранит разницу между сервером-временем, полученным в 5) и его местным временем, чтобы всегда иметь хорошее приближение времени сервера, которое требуется для генерации правильных идентификаторов сообщений.</p><p><b>ВАЖНО:</b> Помимо условий на Prime <b>dh_prime</b> и генераторе Diffie-Hellman <b>и</b> генератор g, обе стороны должны проверить, что <b>g,</b> <b>g_a</b> и <b>g_b</b> больше<b>,</b> чем <b>1</b> и меньше, чем <b>dh_prime -</b> 1. Мы рекомендуем проверить, что <b>g_a</b> и <b>g_b</b> находятся между <b>2^{2048-64}</b> и <b>dh_prime - 2^{2048-64}</b>.</p><h4><a class="anchor" href="#error-handling-lost-queries-and-responses" id="error-handling-lost-queries-and-responses" name="error-handling-lost-queries-and-responses"></a>Обработка ошибок (потерянные запросы и ответы)</h4><p>Если клиент не получает никакого ответа на свой запрос от сервера в течение определенного промежутка времени, он может просто повторно отправить запрос. Если сервер уже отправил ответ на этот запрос <i>(</i>точно такой же запрос и не просто похожий: все параметры во время повторного запроса должны принимать одни и те же значения), но он не попал к клиенту, сервер просто повторно отправит тот же ответ. Сервер запоминает ответ в течение 10 минут после получения запроса в 1). Если сервер уже забыл ответ или необходимые временные данные, клиенту придется начинать с самого начала.</p><p>Сервер может считать, что если клиент уже отправил в следующий запрос, используя данные из предыдущего ответа сервера на конкретного клиента, ответ, как известно, был получен клиентом и может быть забыт сервером.</p><h4><a class="anchor" href="#usage-example" id="usage-example" name="usage-example"></a>Пример использования</h4><p>Пример полного списка запросов, необходимых для генерации ключа авторизации, показан на <a href="/mtproto/samples-auth_key">отдельной странице.</a></p></div></div> ]]></content:encoded>
</item><item turbo="true">
<title>Мобильный Протокол: Подробное Описание</title>
<guid isPermaLink="true">https://www.mtproto.ru/mtproto/4-mobilnyj-protokol-podrobnoe-opisanie.html</guid>
<link>https://www.mtproto.ru/mtproto/4-mobilnyj-protokol-podrobnoe-opisanie.html</link>
<category><![CDATA[MTProto]]></category>
<dc:creator>mtproto</dc:creator>
<pubDate>Mon, 11 Jan 2021 10:49:25 +0300</pubDate>
<description><![CDATA[<div class="quote">Начиная с версии 4.6, почти все клиенты Telegram используют MTProto 2.0. MTProto v.1.0 устарел и в настоящее время постепенно прекращается.</div>В данной статье описывается базовый уровень протокола MTProto версии 2.0 (облачные чаты, шифрование сервер-клиент). Основные отличия от версии 1.0 (<a href="https://core.telegram.org/mtproto/description_v1" target="_blank" rel="noopener external noreferrer">описанной здесь</a>для справки) заключаются в следующем:<ul><li>Вместо SHA-1 используется SHA-256;</li><li>Байты заполнения участвуют в вычислении <b>msg_key</b>;</li><li><b>msg_key </b>зависит не только от самого сообщения, подлежащего шифрованию, но на часть <b>auth_key</b>, а также;</li><li>12..1024 байта заполнения используются вместо 0..15 байтов заполнения в v.1.0.</li></ul>См. также: <a href="https://core.telegram.org/api/end-to-end" target="_blank" rel="noopener external noreferrer">MTProto 2.0: секретные чаты, сквозное шифрование</a><h2><b>Описание протокола</b></h2>Перед передачей сообщения (или составного сообщения) по сети с использованием транспортного протокола оно определенным образом шифруется, а в верхней части сообщения добавляется внешний заголовок, состоящий из 64-битного идентификатора ключа <b>auth_key_id </b>(который однозначно идентифицирует ключ авторизации как для сервера, так и для пользователя) и 128-битного ключа сообщения <b>msg_key</b>.<br><br>Ключ авторизации <b>auth_key </b>в сочетании с ключом сообщения <b>msg_key </b>определяют фактический 256-битный ключ <b>aes_key </b>и 256-битный вектор инициализации <b>aes_iv</b>, которые используются для шифрования сообщения с использованием шифрования AES-256 в режиме infinite garble extension (IGE). Обратите внимание, что начальная часть зашифрованного сообщения содержит переменные данные (сеанс, идентификатор сообщения, порядковый номер, соль сервера), которые, очевидно, влияют на ключ сообщения (и, следовательно, ключ AES и iv). В <b>MTProto 2.0</b> ключ сообщения определяется как 128 средних битов SHA-256 тела сообщения (включая сеанс, идентификатор сообщения, заполнение и т. д.), к которым добавляются 32 байта, взятые из ключа авторизации. В более старом <b>MTProto 1.0</b> ключ сообщения вычислялся как нижние 128 бит SHA-1 тела сообщения, исключая байты заполнения.<br><br><div style="text-align:center;">Составные сообщения шифруются как одно сообщение.</div><div style="text-align:center;"><a href="https://www.mtproto.ru/uploads/posts/2021-01/1610121214_qggdpxz2t608-01-2021.png" target="_blank"><img src="https://www.mtproto.ru/uploads/posts/2021-01/medium/1610121214_qggdpxz2t608-01-2021.png" alt="" class="fr-fic fr-dii"></a></div><div class="quote">Есть вопросы по поводу этой схемы? — Проверьте <a href="http://core.telegram.org/techfaq" target="_blank" rel="noopener external noreferrer">FAQ</a>!</div><h5>Примечание 1</h5>Каждое открытое текстовое сообщение, шифруемое в MTProto, всегда содержит следующие данные, которые должны быть проверены при расшифровке, чтобы сделать систему устойчивой к известным проблемам с компонентами:<ul><li>серверная соль (64-битная)</li><li>идентификатор сеанса</li><li>порядковый номер сообщения</li><li>длина сообщения</li><li>время</li></ul><h5>Примечание 2</h5>Сквозные зашифрованные секретные чаты Telegram используют дополнительный уровень шифрования поверх описанного выше. <a href="https://core.telegram.org/api/end-to-end" target="_blank" rel="noopener external noreferrer">Смотрите секретные чаты, end-to-end шифрования в деталях</a>.<h2><b>Терминология</b></h2><h3><b>Ключ авторизации (auth_key)</b></h3>2048-битный ключ, совместно используемый клиентским устройством и сервером, созданный при регистрации пользователя непосредственно на клиентском устройстве путем обмена ключами Диффи-Хеллмана и никогда не передаваемый по сети. Каждый ключ авторизации зависит от пользователя. Ничто не мешает пользователю иметь несколько ключей (которые соответствуют “постоянным сеансам” на разных устройствах), и некоторые из них могут быть заблокированы навсегда в случае потери устройства. См. также раздел <a href="https://core.telegram.org/mtproto/auth_key" target="_blank" rel="noopener external noreferrer">создание ключа авторизации</a>.<h3><b>Ключ сервера</b></h3>2048-битный RSA-ключ, используемый сервером в цифровом виде для подписи собственных сообщений во время регистрации и генерации ключа авторизации. Приложение имеет встроенный открытый ключ сервера, который может быть использован для проверки подписи, но не может быть использован для подписи сообщений. Закрытый ключ сервера хранится на сервере и изменяется очень редко.<h3><b>Идентификатор ключа (auth_key_id)</b></h3>64 бит хэша SHA1 ключа авторизации используются для указания того, какой именно ключ был использован для шифрования сообщения. Ключи должны быть однозначно определены 64 битами их SHA1, и в случае столкновения ключ авторизации восстанавливается. Нулевой идентификатор ключа означает, что шифрование не используется, что допустимо для ограниченного набора типов сообщений, используемых при регистрации для генерации ключа авторизации в обмене Диффи-Хеллмана. Для MTProto 2.0 здесь по-прежнему используется SHA1, поскольку auth_key_id должен идентифицировать используемый ключ авторизации независимо от версии протокола.<h3><b>Сессия</b></h3>(Случайное) 64-битное число, генерируемое клиентом для различения отдельных сеансов (например, между различными экземплярами приложения, созданными с одним и тем же ключом авторизации). Сеанс в сочетании с идентификатором ключа соответствует экземпляру приложения. Сервер может поддерживать состояние сеанса. Ни при каких обстоятельствах сообщение, предназначенное для одного сеанса, не может быть отправлено в другой сеанс. Сервер может в одностороннем порядке забыть любые сеансы клиента; клиенты должны быть в состоянии справиться с этим.<h3><b>Соль Сервера</b></h3>(Случайное) 64-битное число периодически (скажем, каждые 24 часа) изменяется (отдельно для каждого сеанса) по запросу сервера. Все последующие сообщения должны содержать новую соль (хотя сообщения со старой солью все еще принимаются в течение следующих 300 секунд). Требуется защита от повторных атак и определенных трюков, связанных с настройкой клиентских часов на момент в отдаленном будущем.<h3><b>Идентификатор сообщения (msg_id)</b></h3>(Зависимое от времени) 64-битное число, используемое для того, чтобы уникально идентифицировать сообщение внутри сессии. Идентификаторы сообщения клиента кратны четырём (делятся на 4), остаток от деления идентификатора сообщения сервера на 4 приравнивается к 1 если сообщение является ответом на сообщение клиента, и к трём в остальных случаях. Идентификаторы сообщения клиента должны увеличиваться монотонно (внутри одной сессии), так же как идентификаторы сообщений сервера, и должны примерно равняться unixtime*2^32. Таким образом, идентификатор сообщения указывает на приблизительный момент времени, в который сообщение было создано. Сообщение отклоняется через 300 секунд после того, как оно было создано, или за 30 секунд до того как оно будет создано (это необходимо для защиты против атак повторного воспроизведения). В этой ситуации, оно должно быть отправлено заново с другим идентификатором (или помещено в контейнер с более высоким идентификатором). Идентификатор контейнера сообщений обязательно должен быть больше, чем идентификатор вложенных в него сообщений.<br><br><b>Важно</b>: для противодействия повторным атакам нижние 32 бита <b>msg_id</b>, передаваемые клиентом, не должны быть пустыми и должны представлять собой дробную часть момента времени создания сообщения.<h3><b>Сообщение, связанное с контентом</b></h3>Сообщение, требующее подтверждения. К ним относятся все пользовательские и многие служебные сообщения, практически все, за исключением контейнеров и подтверждений.<h3><b>Порядковый номер сообщения (msg_seqno)</b></h3>32-битное число, равное двойному числу «связанных с контентом» сообщений (которые требуют подтверждения, и в частности те, которые не являются контейнерами) созданных отправителем до этого сообщения и впоследствии увеличивающееся на один если текущее сообщение является сообщением, связанным с контентом. Контейнер всегда генерируется после того, как генерируется то, что он содержит; таким образом, его порядковый номер больше либо равен порядковым номерам сообщения, содержащимся в нём.<h3><b>Ключ сообщения</b></h3>128 бит нижнего порядка хеша SHA1 части сообщения, которая будет зашифрована (включая внутренний заголовок и исключая байты выравнивания данных).<h3><b>Внутренний (криптографический) заголовок</b></h3>Заголовок (16 байт), добавляемый перед сообщением или контейнером до того, как они все вместе будут зашифрованы. Состоит из соли сервера (64 бита) и сессии (64 бита).<h3><b>Внешний (криптографический) заголовок</b></h3>Заголовок (24 байта), добавляемый перед зашифрованным сообщением или контейнером. Состоит из идентификатора ключа (64 бита) и ключа сообщения (128 бит).<h3><b>Payload (полезная нагрузка)</b></h3>Внешний заголовок + зашифрованное сообщение или контейнер.<h2><b>Определение ключа AES и вектора инициализации</b></h2>2048-битный ключ авторизации (auth_key) и 128-битный ключ сообщения (msg_key) используются для вычисления 256-битного ключа AES (aes_key) и 256-битного вектора инициализации (aes_iv), которые в дальнейшем используются, чтобы зашифровать часть сообщения, которая должна быть зашифрована (то есть всё за исключением внешнего заголовка, который добавляется позднее) с AES-256 в режиме расширения неопределённого искажения (infinite garble extension, IGE).<br><br>Алгоритм для вычисления aes_key и aes_iv из auth_key and msg_key таков:<ul><li>msg_key_large = SHA256 (substr (auth_key, 88+x, 32) + plaintext + random_padding);</li><li>msg_key = substr (msg_key_large, 8, 16);</li><li>sha256_a = SHA256 (msg_key + substr (auth_key, x, 36));</li><li>sha256_b = SHA256 (substr (auth_key, 40+x, 36) + msg_key);</li><li>aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);</li><li>aes_iv  = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);</li></ul>где x = 0 для сообщений от клиента к серверу и x = 8 для сообщений от сервера к клиенту.<br>Для устаревшего MTProto 1.0 msg_key, aes_key и aes_iv были вычислены по-разному (<a href="https://core.telegram.org/mtproto/description_v1#defining-aes-key-and-initialization-vector" rel="noopener noreferrer external" target="_blank">см. Этот документ для справки</a>).<br>1024 бита нижнего порядка auth_key не участвуют в вычислении. Они могут (вместе с оставшимися битами или отдельно) использоваться на клиентском устройстве для шифрования локальной копии данных, полученных с сервера. 512 бит нижнего порядка auth_key не хранятся на сервере, поэтому, если клиентское устройство использует их для шифрования локальных данных, а пользователь теряет ключ или пароль, расшифровка локальных данных невозможна (даже если данные с сервера могут быть получены).<br>В MTProto 1.0, когда AES использовался для шифрования блока данных длиной, не делящейся на 16 байт, данные были дополнены от 0 до 15 случайными байтами заполнения random_padding до длины, делящейся на 16 байт до шифрования.<br>В MTProto 2.0 это дополнение учитывается при вычислении msg_key. Обратите внимание, что MTProto 2.0 требует от 12 до 1024 байт заполнения, но при условии, что результирующая длина сообщения будет делиться на 16 байт.<h2><b>Использование MTProto 2.0 вместо MTProto 1.0</b></h2>Клиент может использовать только MTProto 2.0 или только MTProto 1.0 в одном TCP-соединении. Сервер обнаруживает протокол, используемый первым сообщением, полученным от клиента, а затем использует то же самое шифрование для своих сообщений и ожидает, что клиент будет использовать то же самое шифрование впредь. Мы рекомендуем использовать MTProto 2.0; MTProto 1.0 устарел и поддерживается только для обратной совместимости.<h2><b>Важные тесты</b></h2>Когда зашифрованное сообщение получено, должно быть проверено, что msg_key фактически равен 128 битам нижнего порядка хэша SHA1 от предварительно зашифрованной порции, и что msg_id имеет чётный результат для сообщений от клиента к серверу, и нечётный — для сообщений от сервера к клиенту.<br>Примечание переводчика: имеется в виду бит чётности. Вычисляется сложением по модулю 2, результат 0 считается чётным, результат 1 — нечётным.<br>Дополнительно, идентификаторы (msg_id) последних N сообщений, полученных от другой стороны, должны быть сохранены, и если сообщение приходит с msg_id меньшим или равным любому из сохранённых значений, сообщение будет проигнорировано. В противном случае, новый msg_id сообщения добавляется к комплекту, и, если число сохранённых значений msg_id больше чем N, самое старое (т. е. самое нижнее) забывается.<br>Дополнительно, значения msg_id, относящиеся ко времени более 30 секунд в будущем и более 300 секунд в прошлом, игнорируются. Это особенно важно для сервера. Для клиента это также будет полезным (для защиты от атаки повторного воспроизведения), но только если он его настройки времени точны (например, если его время было синхронизировано с временем сервера).<br>Определённые сервисные сообщения «от клиента к серверу», содержащие данные, отправленные клиентом серверу (например, msg_id последнего запроса клиента) могут, тем не менее, быть обработаны на/в клиенте даже если время «неправильное». Это особенно верно для сообщений, которые меняют сервер-salt и уведомлений о неправильных настройках времени у клиента. См. мобильный протокол: сервисные сообщения.<h2><b>Сохранение ключа авторизации на клиенте</b></h2>Пользователям, озабоченным безопасностью, можно предложить защитить ключ авторизации паролем примерно так же, как в ssh. Это может быть достигнуто путем добавления значения криптографической хэш-функции, такой как SHA-256, ключа к передней части ключа, после чего вся строка шифруется с использованием AES в режиме CBC и ключа, равного паролю пользователя (текстовому). Когда пользователь вводит пароль, сохраненный защищенный пароль расшифровывается и проверяется путем проверки значения SHA-256. С точки зрения пользователя, это практически то же самое, что использовать пароль приложения или веб-сайта.<h2><b>Незашифрованные сообщения</b></h2>Специальные текстовые сообщения могут быть использованы для создания ключа авторизации, а также для выполнения синхронизации времени. Они начинаются с auth_key_id = 0 (64 бита), что означает: auth_key отсутствует. За этим следует непосредственно тело сообщения в сериализованном формате без внутренних или внешних заголовков. Перед телом сообщения добавляется идентификатор сообщения (64 бита) и длина тела в байтах (32 байта).<br>Только очень ограниченное число сообщений специальных типов может быть передано в виде обычного текста.<h2><b>Схематическое представление сообщений</b></h2><b>Зашифрованное сообщение</b><table style="width:100%;"><tbody><tr><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#key-identifier-auth-key-id" rel="external noopener noreferrer"><b>auth_key_id</b></a><br>int64</td><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#message-key-msg-key" rel="external noopener noreferrer"><b>msg_key</b></a><br>int128</td><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#encrypted-message-encrypted-data" rel="external noopener noreferrer"><b>encrypted_data</b></a><br>bytes</td></tr></tbody></table><b>Зашифрованное сообщение: encrypted_data</b><br>Содержит зашифрованный текст для следующих данных:<table style="width:100%;"><tbody><tr><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#server-salt" rel="external noopener noreferrer"><b>salt</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#session" rel="external noopener noreferrer"><b>session_id</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#message-identifier-msg-id" rel="external noopener noreferrer"><b>message_id</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#message-sequence-number-msg-seqno" rel="external noopener noreferrer"><b>seq_no</b></a><br>int32</td><td style="width:16.0515%;"><b>message_data_length</b><br>int32</td><td style="width:14.1785%;"><b>message_data</b><br>bytes</td><td style="width:14.2857%;"><b>padding</b>12..1024<br>bytes</td></tr></tbody></table><b>Незашифрованное Сообщение</b><table style="width:100%;"><tbody><tr><td style="width:25%;"><a href="https://core.telegram.org/mtproto/description#key-identifier-auth-key-id" rel="external noopener noreferrer"><b>auth_key_id</b></a> = 0<br>int64</td><td style="width:25%;"><a href="https://core.telegram.org/mtproto/description#message-identifier-msg-id" rel="external noopener noreferrer"><b>message_id</b></a><br>int64</td><td style="width:25%;"><b>message_data_length</b><br>int32</td><td style="width:25%;"><b>message_data</b><br>bytes</td></tr></tbody></table>MTProto 2.0 использует 12..1024 байта заполнения вместо 0..15, используемых в MTProto 1.0<br><h2><b>Создание ключа авторизации</b></h2>Ключ авторизации обычно создаётся один раз для каждого юзера во время процесса установки приложения непосредственно перед регистрацией. Непосредственно сама регистрация происходит после того, как создан ключ авторизации. Однако, юзеру может быть предложено заполнить форму регистрации одновременно с тем, как в фоновом режиме генерируется ключ авторизации. Интервалы между нажатием клавиш юзером могут быть использованы как источник энтропии для генерации высококачественных случайных чисел, которые требуются для создания ключа авторизации.<br><a href="https://core.telegram.org/mtproto/auth_key" rel="noopener noreferrer external" target="_blank">См. создание ключа авторизации.</a><br>В то время как создаётся ключ авторизации, клиент определяет какова соль сервера (server salt) (то есть для всех сессий, которые будут созданы в ближайшем будущем для ключа). Далее, клиент создаёт первую (зашифрованную) сессию, используя ключ, и каждая последующая коммуникация (включая передачу регистрационной информации юзера и валидацию номера телефона) будут происходить внутри сессии.<b><br></b>]]></description>
<turbo:content><![CDATA[ <div class="quote">Начиная с версии 4.6, почти все клиенты Telegram используют MTProto 2.0. MTProto v.1.0 устарел и в настоящее время постепенно прекращается.</div>В данной статье описывается базовый уровень протокола MTProto версии 2.0 (облачные чаты, шифрование сервер-клиент). Основные отличия от версии 1.0 (<a href="https://core.telegram.org/mtproto/description_v1" target="_blank" rel="noopener external noreferrer">описанной здесь</a>для справки) заключаются в следующем:<ul><li>Вместо SHA-1 используется SHA-256;</li><li>Байты заполнения участвуют в вычислении <b>msg_key</b>;</li><li><b>msg_key </b>зависит не только от самого сообщения, подлежащего шифрованию, но на часть <b>auth_key</b>, а также;</li><li>12..1024 байта заполнения используются вместо 0..15 байтов заполнения в v.1.0.</li></ul>См. также: <a href="https://core.telegram.org/api/end-to-end" target="_blank" rel="noopener external noreferrer">MTProto 2.0: секретные чаты, сквозное шифрование</a><h2><b>Описание протокола</b></h2>Перед передачей сообщения (или составного сообщения) по сети с использованием транспортного протокола оно определенным образом шифруется, а в верхней части сообщения добавляется внешний заголовок, состоящий из 64-битного идентификатора ключа <b>auth_key_id </b>(который однозначно идентифицирует ключ авторизации как для сервера, так и для пользователя) и 128-битного ключа сообщения <b>msg_key</b>.<br><br>Ключ авторизации <b>auth_key </b>в сочетании с ключом сообщения <b>msg_key </b>определяют фактический 256-битный ключ <b>aes_key </b>и 256-битный вектор инициализации <b>aes_iv</b>, которые используются для шифрования сообщения с использованием шифрования AES-256 в режиме infinite garble extension (IGE). Обратите внимание, что начальная часть зашифрованного сообщения содержит переменные данные (сеанс, идентификатор сообщения, порядковый номер, соль сервера), которые, очевидно, влияют на ключ сообщения (и, следовательно, ключ AES и iv). В <b>MTProto 2.0</b> ключ сообщения определяется как 128 средних битов SHA-256 тела сообщения (включая сеанс, идентификатор сообщения, заполнение и т. д.), к которым добавляются 32 байта, взятые из ключа авторизации. В более старом <b>MTProto 1.0</b> ключ сообщения вычислялся как нижние 128 бит SHA-1 тела сообщения, исключая байты заполнения.<br><br><div style="text-align:center;">Составные сообщения шифруются как одно сообщение.</div><div style="text-align:center;"><a href="https://www.mtproto.ru/uploads/posts/2021-01/1610121214_qggdpxz2t608-01-2021.png" target="_blank"><img src="https://www.mtproto.ru/uploads/posts/2021-01/medium/1610121214_qggdpxz2t608-01-2021.png" alt="" class="fr-fic fr-dii"></a></div><div class="quote">Есть вопросы по поводу этой схемы? — Проверьте <a href="http://core.telegram.org/techfaq" target="_blank" rel="noopener external noreferrer">FAQ</a>!</div><h5>Примечание 1</h5>Каждое открытое текстовое сообщение, шифруемое в MTProto, всегда содержит следующие данные, которые должны быть проверены при расшифровке, чтобы сделать систему устойчивой к известным проблемам с компонентами:<ul><li>серверная соль (64-битная)</li><li>идентификатор сеанса</li><li>порядковый номер сообщения</li><li>длина сообщения</li><li>время</li></ul><h5>Примечание 2</h5>Сквозные зашифрованные секретные чаты Telegram используют дополнительный уровень шифрования поверх описанного выше. <a href="https://core.telegram.org/api/end-to-end" target="_blank" rel="noopener external noreferrer">Смотрите секретные чаты, end-to-end шифрования в деталях</a>.<h2><b>Терминология</b></h2><h3><b>Ключ авторизации (auth_key)</b></h3>2048-битный ключ, совместно используемый клиентским устройством и сервером, созданный при регистрации пользователя непосредственно на клиентском устройстве путем обмена ключами Диффи-Хеллмана и никогда не передаваемый по сети. Каждый ключ авторизации зависит от пользователя. Ничто не мешает пользователю иметь несколько ключей (которые соответствуют “постоянным сеансам” на разных устройствах), и некоторые из них могут быть заблокированы навсегда в случае потери устройства. См. также раздел <a href="https://core.telegram.org/mtproto/auth_key" target="_blank" rel="noopener external noreferrer">создание ключа авторизации</a>.<h3><b>Ключ сервера</b></h3>2048-битный RSA-ключ, используемый сервером в цифровом виде для подписи собственных сообщений во время регистрации и генерации ключа авторизации. Приложение имеет встроенный открытый ключ сервера, который может быть использован для проверки подписи, но не может быть использован для подписи сообщений. Закрытый ключ сервера хранится на сервере и изменяется очень редко.<h3><b>Идентификатор ключа (auth_key_id)</b></h3>64 бит хэша SHA1 ключа авторизации используются для указания того, какой именно ключ был использован для шифрования сообщения. Ключи должны быть однозначно определены 64 битами их SHA1, и в случае столкновения ключ авторизации восстанавливается. Нулевой идентификатор ключа означает, что шифрование не используется, что допустимо для ограниченного набора типов сообщений, используемых при регистрации для генерации ключа авторизации в обмене Диффи-Хеллмана. Для MTProto 2.0 здесь по-прежнему используется SHA1, поскольку auth_key_id должен идентифицировать используемый ключ авторизации независимо от версии протокола.<h3><b>Сессия</b></h3>(Случайное) 64-битное число, генерируемое клиентом для различения отдельных сеансов (например, между различными экземплярами приложения, созданными с одним и тем же ключом авторизации). Сеанс в сочетании с идентификатором ключа соответствует экземпляру приложения. Сервер может поддерживать состояние сеанса. Ни при каких обстоятельствах сообщение, предназначенное для одного сеанса, не может быть отправлено в другой сеанс. Сервер может в одностороннем порядке забыть любые сеансы клиента; клиенты должны быть в состоянии справиться с этим.<h3><b>Соль Сервера</b></h3>(Случайное) 64-битное число периодически (скажем, каждые 24 часа) изменяется (отдельно для каждого сеанса) по запросу сервера. Все последующие сообщения должны содержать новую соль (хотя сообщения со старой солью все еще принимаются в течение следующих 300 секунд). Требуется защита от повторных атак и определенных трюков, связанных с настройкой клиентских часов на момент в отдаленном будущем.<h3><b>Идентификатор сообщения (msg_id)</b></h3>(Зависимое от времени) 64-битное число, используемое для того, чтобы уникально идентифицировать сообщение внутри сессии. Идентификаторы сообщения клиента кратны четырём (делятся на 4), остаток от деления идентификатора сообщения сервера на 4 приравнивается к 1 если сообщение является ответом на сообщение клиента, и к трём в остальных случаях. Идентификаторы сообщения клиента должны увеличиваться монотонно (внутри одной сессии), так же как идентификаторы сообщений сервера, и должны примерно равняться unixtime*2^32. Таким образом, идентификатор сообщения указывает на приблизительный момент времени, в который сообщение было создано. Сообщение отклоняется через 300 секунд после того, как оно было создано, или за 30 секунд до того как оно будет создано (это необходимо для защиты против атак повторного воспроизведения). В этой ситуации, оно должно быть отправлено заново с другим идентификатором (или помещено в контейнер с более высоким идентификатором). Идентификатор контейнера сообщений обязательно должен быть больше, чем идентификатор вложенных в него сообщений.<br><br><b>Важно</b>: для противодействия повторным атакам нижние 32 бита <b>msg_id</b>, передаваемые клиентом, не должны быть пустыми и должны представлять собой дробную часть момента времени создания сообщения.<h3><b>Сообщение, связанное с контентом</b></h3>Сообщение, требующее подтверждения. К ним относятся все пользовательские и многие служебные сообщения, практически все, за исключением контейнеров и подтверждений.<h3><b>Порядковый номер сообщения (msg_seqno)</b></h3>32-битное число, равное двойному числу «связанных с контентом» сообщений (которые требуют подтверждения, и в частности те, которые не являются контейнерами) созданных отправителем до этого сообщения и впоследствии увеличивающееся на один если текущее сообщение является сообщением, связанным с контентом. Контейнер всегда генерируется после того, как генерируется то, что он содержит; таким образом, его порядковый номер больше либо равен порядковым номерам сообщения, содержащимся в нём.<h3><b>Ключ сообщения</b></h3>128 бит нижнего порядка хеша SHA1 части сообщения, которая будет зашифрована (включая внутренний заголовок и исключая байты выравнивания данных).<h3><b>Внутренний (криптографический) заголовок</b></h3>Заголовок (16 байт), добавляемый перед сообщением или контейнером до того, как они все вместе будут зашифрованы. Состоит из соли сервера (64 бита) и сессии (64 бита).<h3><b>Внешний (криптографический) заголовок</b></h3>Заголовок (24 байта), добавляемый перед зашифрованным сообщением или контейнером. Состоит из идентификатора ключа (64 бита) и ключа сообщения (128 бит).<h3><b>Payload (полезная нагрузка)</b></h3>Внешний заголовок + зашифрованное сообщение или контейнер.<h2><b>Определение ключа AES и вектора инициализации</b></h2>2048-битный ключ авторизации (auth_key) и 128-битный ключ сообщения (msg_key) используются для вычисления 256-битного ключа AES (aes_key) и 256-битного вектора инициализации (aes_iv), которые в дальнейшем используются, чтобы зашифровать часть сообщения, которая должна быть зашифрована (то есть всё за исключением внешнего заголовка, который добавляется позднее) с AES-256 в режиме расширения неопределённого искажения (infinite garble extension, IGE).<br><br>Алгоритм для вычисления aes_key и aes_iv из auth_key and msg_key таков:<ul><li>msg_key_large = SHA256 (substr (auth_key, 88+x, 32) + plaintext + random_padding);</li><li>msg_key = substr (msg_key_large, 8, 16);</li><li>sha256_a = SHA256 (msg_key + substr (auth_key, x, 36));</li><li>sha256_b = SHA256 (substr (auth_key, 40+x, 36) + msg_key);</li><li>aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);</li><li>aes_iv  = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);</li></ul>где x = 0 для сообщений от клиента к серверу и x = 8 для сообщений от сервера к клиенту.<br>Для устаревшего MTProto 1.0 msg_key, aes_key и aes_iv были вычислены по-разному (<a href="https://core.telegram.org/mtproto/description_v1#defining-aes-key-and-initialization-vector" rel="noopener noreferrer external" target="_blank">см. Этот документ для справки</a>).<br>1024 бита нижнего порядка auth_key не участвуют в вычислении. Они могут (вместе с оставшимися битами или отдельно) использоваться на клиентском устройстве для шифрования локальной копии данных, полученных с сервера. 512 бит нижнего порядка auth_key не хранятся на сервере, поэтому, если клиентское устройство использует их для шифрования локальных данных, а пользователь теряет ключ или пароль, расшифровка локальных данных невозможна (даже если данные с сервера могут быть получены).<br>В MTProto 1.0, когда AES использовался для шифрования блока данных длиной, не делящейся на 16 байт, данные были дополнены от 0 до 15 случайными байтами заполнения random_padding до длины, делящейся на 16 байт до шифрования.<br>В MTProto 2.0 это дополнение учитывается при вычислении msg_key. Обратите внимание, что MTProto 2.0 требует от 12 до 1024 байт заполнения, но при условии, что результирующая длина сообщения будет делиться на 16 байт.<h2><b>Использование MTProto 2.0 вместо MTProto 1.0</b></h2>Клиент может использовать только MTProto 2.0 или только MTProto 1.0 в одном TCP-соединении. Сервер обнаруживает протокол, используемый первым сообщением, полученным от клиента, а затем использует то же самое шифрование для своих сообщений и ожидает, что клиент будет использовать то же самое шифрование впредь. Мы рекомендуем использовать MTProto 2.0; MTProto 1.0 устарел и поддерживается только для обратной совместимости.<h2><b>Важные тесты</b></h2>Когда зашифрованное сообщение получено, должно быть проверено, что msg_key фактически равен 128 битам нижнего порядка хэша SHA1 от предварительно зашифрованной порции, и что msg_id имеет чётный результат для сообщений от клиента к серверу, и нечётный — для сообщений от сервера к клиенту.<br>Примечание переводчика: имеется в виду бит чётности. Вычисляется сложением по модулю 2, результат 0 считается чётным, результат 1 — нечётным.<br>Дополнительно, идентификаторы (msg_id) последних N сообщений, полученных от другой стороны, должны быть сохранены, и если сообщение приходит с msg_id меньшим или равным любому из сохранённых значений, сообщение будет проигнорировано. В противном случае, новый msg_id сообщения добавляется к комплекту, и, если число сохранённых значений msg_id больше чем N, самое старое (т. е. самое нижнее) забывается.<br>Дополнительно, значения msg_id, относящиеся ко времени более 30 секунд в будущем и более 300 секунд в прошлом, игнорируются. Это особенно важно для сервера. Для клиента это также будет полезным (для защиты от атаки повторного воспроизведения), но только если он его настройки времени точны (например, если его время было синхронизировано с временем сервера).<br>Определённые сервисные сообщения «от клиента к серверу», содержащие данные, отправленные клиентом серверу (например, msg_id последнего запроса клиента) могут, тем не менее, быть обработаны на/в клиенте даже если время «неправильное». Это особенно верно для сообщений, которые меняют сервер-salt и уведомлений о неправильных настройках времени у клиента. См. мобильный протокол: сервисные сообщения.<h2><b>Сохранение ключа авторизации на клиенте</b></h2>Пользователям, озабоченным безопасностью, можно предложить защитить ключ авторизации паролем примерно так же, как в ssh. Это может быть достигнуто путем добавления значения криптографической хэш-функции, такой как SHA-256, ключа к передней части ключа, после чего вся строка шифруется с использованием AES в режиме CBC и ключа, равного паролю пользователя (текстовому). Когда пользователь вводит пароль, сохраненный защищенный пароль расшифровывается и проверяется путем проверки значения SHA-256. С точки зрения пользователя, это практически то же самое, что использовать пароль приложения или веб-сайта.<h2><b>Незашифрованные сообщения</b></h2>Специальные текстовые сообщения могут быть использованы для создания ключа авторизации, а также для выполнения синхронизации времени. Они начинаются с auth_key_id = 0 (64 бита), что означает: auth_key отсутствует. За этим следует непосредственно тело сообщения в сериализованном формате без внутренних или внешних заголовков. Перед телом сообщения добавляется идентификатор сообщения (64 бита) и длина тела в байтах (32 байта).<br>Только очень ограниченное число сообщений специальных типов может быть передано в виде обычного текста.<h2><b>Схематическое представление сообщений</b></h2><b>Зашифрованное сообщение</b><table style="width:100%;"><tbody><tr><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#key-identifier-auth-key-id" rel="external noopener noreferrer"><b>auth_key_id</b></a><br>int64</td><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#message-key-msg-key" rel="external noopener noreferrer"><b>msg_key</b></a><br>int128</td><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#encrypted-message-encrypted-data" rel="external noopener noreferrer"><b>encrypted_data</b></a><br>bytes</td></tr></tbody></table><b>Зашифрованное сообщение: encrypted_data</b><br>Содержит зашифрованный текст для следующих данных:<table style="width:100%;"><tbody><tr><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#server-salt" rel="external noopener noreferrer"><b>salt</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#session" rel="external noopener noreferrer"><b>session_id</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#message-identifier-msg-id" rel="external noopener noreferrer"><b>message_id</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#message-sequence-number-msg-seqno" rel="external noopener noreferrer"><b>seq_no</b></a><br>int32</td><td style="width:16.0515%;"><b>message_data_length</b><br>int32</td><td style="width:14.1785%;"><b>message_data</b><br>bytes</td><td style="width:14.2857%;"><b>padding</b>12..1024<br>bytes</td></tr></tbody></table><b>Незашифрованное Сообщение</b><table style="width:100%;"><tbody><tr><td style="width:25%;"><a href="https://core.telegram.org/mtproto/description#key-identifier-auth-key-id" rel="external noopener noreferrer"><b>auth_key_id</b></a> = 0<br>int64</td><td style="width:25%;"><a href="https://core.telegram.org/mtproto/description#message-identifier-msg-id" rel="external noopener noreferrer"><b>message_id</b></a><br>int64</td><td style="width:25%;"><b>message_data_length</b><br>int32</td><td style="width:25%;"><b>message_data</b><br>bytes</td></tr></tbody></table>MTProto 2.0 использует 12..1024 байта заполнения вместо 0..15, используемых в MTProto 1.0<br><h2><b>Создание ключа авторизации</b></h2>Ключ авторизации обычно создаётся один раз для каждого юзера во время процесса установки приложения непосредственно перед регистрацией. Непосредственно сама регистрация происходит после того, как создан ключ авторизации. Однако, юзеру может быть предложено заполнить форму регистрации одновременно с тем, как в фоновом режиме генерируется ключ авторизации. Интервалы между нажатием клавиш юзером могут быть использованы как источник энтропии для генерации высококачественных случайных чисел, которые требуются для создания ключа авторизации.<br><a href="https://core.telegram.org/mtproto/auth_key" rel="noopener noreferrer external" target="_blank">См. создание ключа авторизации.</a><br>В то время как создаётся ключ авторизации, клиент определяет какова соль сервера (server salt) (то есть для всех сессий, которые будут созданы в ближайшем будущем для ключа). Далее, клиент создаёт первую (зашифрованную) сессию, используя ключ, и каждая последующая коммуникация (включая передачу регистрационной информации юзера и валидацию номера телефона) будут происходить внутри сессии.<b><br></b> ]]></turbo:content>
<content:encoded><![CDATA[ <div class="quote">Начиная с версии 4.6, почти все клиенты Telegram используют MTProto 2.0. MTProto v.1.0 устарел и в настоящее время постепенно прекращается.</div>В данной статье описывается базовый уровень протокола MTProto версии 2.0 (облачные чаты, шифрование сервер-клиент). Основные отличия от версии 1.0 (<a href="https://core.telegram.org/mtproto/description_v1" target="_blank" rel="noopener external noreferrer">описанной здесь</a>для справки) заключаются в следующем:<ul><li>Вместо SHA-1 используется SHA-256;</li><li>Байты заполнения участвуют в вычислении <b>msg_key</b>;</li><li><b>msg_key </b>зависит не только от самого сообщения, подлежащего шифрованию, но на часть <b>auth_key</b>, а также;</li><li>12..1024 байта заполнения используются вместо 0..15 байтов заполнения в v.1.0.</li></ul>См. также: <a href="https://core.telegram.org/api/end-to-end" target="_blank" rel="noopener external noreferrer">MTProto 2.0: секретные чаты, сквозное шифрование</a><h2><b>Описание протокола</b></h2>Перед передачей сообщения (или составного сообщения) по сети с использованием транспортного протокола оно определенным образом шифруется, а в верхней части сообщения добавляется внешний заголовок, состоящий из 64-битного идентификатора ключа <b>auth_key_id </b>(который однозначно идентифицирует ключ авторизации как для сервера, так и для пользователя) и 128-битного ключа сообщения <b>msg_key</b>.<br><br>Ключ авторизации <b>auth_key </b>в сочетании с ключом сообщения <b>msg_key </b>определяют фактический 256-битный ключ <b>aes_key </b>и 256-битный вектор инициализации <b>aes_iv</b>, которые используются для шифрования сообщения с использованием шифрования AES-256 в режиме infinite garble extension (IGE). Обратите внимание, что начальная часть зашифрованного сообщения содержит переменные данные (сеанс, идентификатор сообщения, порядковый номер, соль сервера), которые, очевидно, влияют на ключ сообщения (и, следовательно, ключ AES и iv). В <b>MTProto 2.0</b> ключ сообщения определяется как 128 средних битов SHA-256 тела сообщения (включая сеанс, идентификатор сообщения, заполнение и т. д.), к которым добавляются 32 байта, взятые из ключа авторизации. В более старом <b>MTProto 1.0</b> ключ сообщения вычислялся как нижние 128 бит SHA-1 тела сообщения, исключая байты заполнения.<br><br><div style="text-align:center;">Составные сообщения шифруются как одно сообщение.</div><div style="text-align:center;"><a href="https://www.mtproto.ru/uploads/posts/2021-01/1610121214_qggdpxz2t608-01-2021.png" target="_blank"><img src="https://www.mtproto.ru/uploads/posts/2021-01/medium/1610121214_qggdpxz2t608-01-2021.png" alt="" class="fr-fic fr-dii"></a></div><div class="quote">Есть вопросы по поводу этой схемы? — Проверьте <a href="http://core.telegram.org/techfaq" target="_blank" rel="noopener external noreferrer">FAQ</a>!</div><h5>Примечание 1</h5>Каждое открытое текстовое сообщение, шифруемое в MTProto, всегда содержит следующие данные, которые должны быть проверены при расшифровке, чтобы сделать систему устойчивой к известным проблемам с компонентами:<ul><li>серверная соль (64-битная)</li><li>идентификатор сеанса</li><li>порядковый номер сообщения</li><li>длина сообщения</li><li>время</li></ul><h5>Примечание 2</h5>Сквозные зашифрованные секретные чаты Telegram используют дополнительный уровень шифрования поверх описанного выше. <a href="https://core.telegram.org/api/end-to-end" target="_blank" rel="noopener external noreferrer">Смотрите секретные чаты, end-to-end шифрования в деталях</a>.<h2><b>Терминология</b></h2><h3><b>Ключ авторизации (auth_key)</b></h3>2048-битный ключ, совместно используемый клиентским устройством и сервером, созданный при регистрации пользователя непосредственно на клиентском устройстве путем обмена ключами Диффи-Хеллмана и никогда не передаваемый по сети. Каждый ключ авторизации зависит от пользователя. Ничто не мешает пользователю иметь несколько ключей (которые соответствуют “постоянным сеансам” на разных устройствах), и некоторые из них могут быть заблокированы навсегда в случае потери устройства. См. также раздел <a href="https://core.telegram.org/mtproto/auth_key" target="_blank" rel="noopener external noreferrer">создание ключа авторизации</a>.<h3><b>Ключ сервера</b></h3>2048-битный RSA-ключ, используемый сервером в цифровом виде для подписи собственных сообщений во время регистрации и генерации ключа авторизации. Приложение имеет встроенный открытый ключ сервера, который может быть использован для проверки подписи, но не может быть использован для подписи сообщений. Закрытый ключ сервера хранится на сервере и изменяется очень редко.<h3><b>Идентификатор ключа (auth_key_id)</b></h3>64 бит хэша SHA1 ключа авторизации используются для указания того, какой именно ключ был использован для шифрования сообщения. Ключи должны быть однозначно определены 64 битами их SHA1, и в случае столкновения ключ авторизации восстанавливается. Нулевой идентификатор ключа означает, что шифрование не используется, что допустимо для ограниченного набора типов сообщений, используемых при регистрации для генерации ключа авторизации в обмене Диффи-Хеллмана. Для MTProto 2.0 здесь по-прежнему используется SHA1, поскольку auth_key_id должен идентифицировать используемый ключ авторизации независимо от версии протокола.<h3><b>Сессия</b></h3>(Случайное) 64-битное число, генерируемое клиентом для различения отдельных сеансов (например, между различными экземплярами приложения, созданными с одним и тем же ключом авторизации). Сеанс в сочетании с идентификатором ключа соответствует экземпляру приложения. Сервер может поддерживать состояние сеанса. Ни при каких обстоятельствах сообщение, предназначенное для одного сеанса, не может быть отправлено в другой сеанс. Сервер может в одностороннем порядке забыть любые сеансы клиента; клиенты должны быть в состоянии справиться с этим.<h3><b>Соль Сервера</b></h3>(Случайное) 64-битное число периодически (скажем, каждые 24 часа) изменяется (отдельно для каждого сеанса) по запросу сервера. Все последующие сообщения должны содержать новую соль (хотя сообщения со старой солью все еще принимаются в течение следующих 300 секунд). Требуется защита от повторных атак и определенных трюков, связанных с настройкой клиентских часов на момент в отдаленном будущем.<h3><b>Идентификатор сообщения (msg_id)</b></h3>(Зависимое от времени) 64-битное число, используемое для того, чтобы уникально идентифицировать сообщение внутри сессии. Идентификаторы сообщения клиента кратны четырём (делятся на 4), остаток от деления идентификатора сообщения сервера на 4 приравнивается к 1 если сообщение является ответом на сообщение клиента, и к трём в остальных случаях. Идентификаторы сообщения клиента должны увеличиваться монотонно (внутри одной сессии), так же как идентификаторы сообщений сервера, и должны примерно равняться unixtime*2^32. Таким образом, идентификатор сообщения указывает на приблизительный момент времени, в который сообщение было создано. Сообщение отклоняется через 300 секунд после того, как оно было создано, или за 30 секунд до того как оно будет создано (это необходимо для защиты против атак повторного воспроизведения). В этой ситуации, оно должно быть отправлено заново с другим идентификатором (или помещено в контейнер с более высоким идентификатором). Идентификатор контейнера сообщений обязательно должен быть больше, чем идентификатор вложенных в него сообщений.<br><br><b>Важно</b>: для противодействия повторным атакам нижние 32 бита <b>msg_id</b>, передаваемые клиентом, не должны быть пустыми и должны представлять собой дробную часть момента времени создания сообщения.<h3><b>Сообщение, связанное с контентом</b></h3>Сообщение, требующее подтверждения. К ним относятся все пользовательские и многие служебные сообщения, практически все, за исключением контейнеров и подтверждений.<h3><b>Порядковый номер сообщения (msg_seqno)</b></h3>32-битное число, равное двойному числу «связанных с контентом» сообщений (которые требуют подтверждения, и в частности те, которые не являются контейнерами) созданных отправителем до этого сообщения и впоследствии увеличивающееся на один если текущее сообщение является сообщением, связанным с контентом. Контейнер всегда генерируется после того, как генерируется то, что он содержит; таким образом, его порядковый номер больше либо равен порядковым номерам сообщения, содержащимся в нём.<h3><b>Ключ сообщения</b></h3>128 бит нижнего порядка хеша SHA1 части сообщения, которая будет зашифрована (включая внутренний заголовок и исключая байты выравнивания данных).<h3><b>Внутренний (криптографический) заголовок</b></h3>Заголовок (16 байт), добавляемый перед сообщением или контейнером до того, как они все вместе будут зашифрованы. Состоит из соли сервера (64 бита) и сессии (64 бита).<h3><b>Внешний (криптографический) заголовок</b></h3>Заголовок (24 байта), добавляемый перед зашифрованным сообщением или контейнером. Состоит из идентификатора ключа (64 бита) и ключа сообщения (128 бит).<h3><b>Payload (полезная нагрузка)</b></h3>Внешний заголовок + зашифрованное сообщение или контейнер.<h2><b>Определение ключа AES и вектора инициализации</b></h2>2048-битный ключ авторизации (auth_key) и 128-битный ключ сообщения (msg_key) используются для вычисления 256-битного ключа AES (aes_key) и 256-битного вектора инициализации (aes_iv), которые в дальнейшем используются, чтобы зашифровать часть сообщения, которая должна быть зашифрована (то есть всё за исключением внешнего заголовка, который добавляется позднее) с AES-256 в режиме расширения неопределённого искажения (infinite garble extension, IGE).<br><br>Алгоритм для вычисления aes_key и aes_iv из auth_key and msg_key таков:<ul><li>msg_key_large = SHA256 (substr (auth_key, 88+x, 32) + plaintext + random_padding);</li><li>msg_key = substr (msg_key_large, 8, 16);</li><li>sha256_a = SHA256 (msg_key + substr (auth_key, x, 36));</li><li>sha256_b = SHA256 (substr (auth_key, 40+x, 36) + msg_key);</li><li>aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);</li><li>aes_iv  = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);</li></ul>где x = 0 для сообщений от клиента к серверу и x = 8 для сообщений от сервера к клиенту.<br>Для устаревшего MTProto 1.0 msg_key, aes_key и aes_iv были вычислены по-разному (<a href="https://core.telegram.org/mtproto/description_v1#defining-aes-key-and-initialization-vector" rel="noopener noreferrer external" target="_blank">см. Этот документ для справки</a>).<br>1024 бита нижнего порядка auth_key не участвуют в вычислении. Они могут (вместе с оставшимися битами или отдельно) использоваться на клиентском устройстве для шифрования локальной копии данных, полученных с сервера. 512 бит нижнего порядка auth_key не хранятся на сервере, поэтому, если клиентское устройство использует их для шифрования локальных данных, а пользователь теряет ключ или пароль, расшифровка локальных данных невозможна (даже если данные с сервера могут быть получены).<br>В MTProto 1.0, когда AES использовался для шифрования блока данных длиной, не делящейся на 16 байт, данные были дополнены от 0 до 15 случайными байтами заполнения random_padding до длины, делящейся на 16 байт до шифрования.<br>В MTProto 2.0 это дополнение учитывается при вычислении msg_key. Обратите внимание, что MTProto 2.0 требует от 12 до 1024 байт заполнения, но при условии, что результирующая длина сообщения будет делиться на 16 байт.<h2><b>Использование MTProto 2.0 вместо MTProto 1.0</b></h2>Клиент может использовать только MTProto 2.0 или только MTProto 1.0 в одном TCP-соединении. Сервер обнаруживает протокол, используемый первым сообщением, полученным от клиента, а затем использует то же самое шифрование для своих сообщений и ожидает, что клиент будет использовать то же самое шифрование впредь. Мы рекомендуем использовать MTProto 2.0; MTProto 1.0 устарел и поддерживается только для обратной совместимости.<h2><b>Важные тесты</b></h2>Когда зашифрованное сообщение получено, должно быть проверено, что msg_key фактически равен 128 битам нижнего порядка хэша SHA1 от предварительно зашифрованной порции, и что msg_id имеет чётный результат для сообщений от клиента к серверу, и нечётный — для сообщений от сервера к клиенту.<br>Примечание переводчика: имеется в виду бит чётности. Вычисляется сложением по модулю 2, результат 0 считается чётным, результат 1 — нечётным.<br>Дополнительно, идентификаторы (msg_id) последних N сообщений, полученных от другой стороны, должны быть сохранены, и если сообщение приходит с msg_id меньшим или равным любому из сохранённых значений, сообщение будет проигнорировано. В противном случае, новый msg_id сообщения добавляется к комплекту, и, если число сохранённых значений msg_id больше чем N, самое старое (т. е. самое нижнее) забывается.<br>Дополнительно, значения msg_id, относящиеся ко времени более 30 секунд в будущем и более 300 секунд в прошлом, игнорируются. Это особенно важно для сервера. Для клиента это также будет полезным (для защиты от атаки повторного воспроизведения), но только если он его настройки времени точны (например, если его время было синхронизировано с временем сервера).<br>Определённые сервисные сообщения «от клиента к серверу», содержащие данные, отправленные клиентом серверу (например, msg_id последнего запроса клиента) могут, тем не менее, быть обработаны на/в клиенте даже если время «неправильное». Это особенно верно для сообщений, которые меняют сервер-salt и уведомлений о неправильных настройках времени у клиента. См. мобильный протокол: сервисные сообщения.<h2><b>Сохранение ключа авторизации на клиенте</b></h2>Пользователям, озабоченным безопасностью, можно предложить защитить ключ авторизации паролем примерно так же, как в ssh. Это может быть достигнуто путем добавления значения криптографической хэш-функции, такой как SHA-256, ключа к передней части ключа, после чего вся строка шифруется с использованием AES в режиме CBC и ключа, равного паролю пользователя (текстовому). Когда пользователь вводит пароль, сохраненный защищенный пароль расшифровывается и проверяется путем проверки значения SHA-256. С точки зрения пользователя, это практически то же самое, что использовать пароль приложения или веб-сайта.<h2><b>Незашифрованные сообщения</b></h2>Специальные текстовые сообщения могут быть использованы для создания ключа авторизации, а также для выполнения синхронизации времени. Они начинаются с auth_key_id = 0 (64 бита), что означает: auth_key отсутствует. За этим следует непосредственно тело сообщения в сериализованном формате без внутренних или внешних заголовков. Перед телом сообщения добавляется идентификатор сообщения (64 бита) и длина тела в байтах (32 байта).<br>Только очень ограниченное число сообщений специальных типов может быть передано в виде обычного текста.<h2><b>Схематическое представление сообщений</b></h2><b>Зашифрованное сообщение</b><table style="width:100%;"><tbody><tr><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#key-identifier-auth-key-id" rel="external noopener noreferrer"><b>auth_key_id</b></a><br>int64</td><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#message-key-msg-key" rel="external noopener noreferrer"><b>msg_key</b></a><br>int128</td><td style="width:33.3333%;"><a href="https://core.telegram.org/mtproto/description#encrypted-message-encrypted-data" rel="external noopener noreferrer"><b>encrypted_data</b></a><br>bytes</td></tr></tbody></table><b>Зашифрованное сообщение: encrypted_data</b><br>Содержит зашифрованный текст для следующих данных:<table style="width:100%;"><tbody><tr><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#server-salt" rel="external noopener noreferrer"><b>salt</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#session" rel="external noopener noreferrer"><b>session_id</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#message-identifier-msg-id" rel="external noopener noreferrer"><b>message_id</b></a><br>int64</td><td style="width:14.2857%;"><a href="https://core.telegram.org/mtproto/description#message-sequence-number-msg-seqno" rel="external noopener noreferrer"><b>seq_no</b></a><br>int32</td><td style="width:16.0515%;"><b>message_data_length</b><br>int32</td><td style="width:14.1785%;"><b>message_data</b><br>bytes</td><td style="width:14.2857%;"><b>padding</b>12..1024<br>bytes</td></tr></tbody></table><b>Незашифрованное Сообщение</b><table style="width:100%;"><tbody><tr><td style="width:25%;"><a href="https://core.telegram.org/mtproto/description#key-identifier-auth-key-id" rel="external noopener noreferrer"><b>auth_key_id</b></a> = 0<br>int64</td><td style="width:25%;"><a href="https://core.telegram.org/mtproto/description#message-identifier-msg-id" rel="external noopener noreferrer"><b>message_id</b></a><br>int64</td><td style="width:25%;"><b>message_data_length</b><br>int32</td><td style="width:25%;"><b>message_data</b><br>bytes</td></tr></tbody></table>MTProto 2.0 использует 12..1024 байта заполнения вместо 0..15, используемых в MTProto 1.0<br><h2><b>Создание ключа авторизации</b></h2>Ключ авторизации обычно создаётся один раз для каждого юзера во время процесса установки приложения непосредственно перед регистрацией. Непосредственно сама регистрация происходит после того, как создан ключ авторизации. Однако, юзеру может быть предложено заполнить форму регистрации одновременно с тем, как в фоновом режиме генерируется ключ авторизации. Интервалы между нажатием клавиш юзером могут быть использованы как источник энтропии для генерации высококачественных случайных чисел, которые требуются для создания ключа авторизации.<br><a href="https://core.telegram.org/mtproto/auth_key" rel="noopener noreferrer external" target="_blank">См. создание ключа авторизации.</a><br>В то время как создаётся ключ авторизации, клиент определяет какова соль сервера (server salt) (то есть для всех сессий, которые будут созданы в ближайшем будущем для ключа). Далее, клиент создаёт первую (зашифрованную) сессию, используя ключ, и каждая последующая коммуникация (включая передачу регистрационной информации юзера и валидацию номера телефона) будут происходить внутри сессии.<b><br></b> ]]></content:encoded>
</item><item turbo="true">
<title>MTProto Мобильный протокол</title>
<guid isPermaLink="true">https://www.mtproto.ru/mtproto/1-mtproto-mobilnyj-protokol.html</guid>
<link>https://www.mtproto.ru/mtproto/1-mtproto-mobilnyj-protokol.html</link>
<category><![CDATA[MTProto]]></category>
<dc:creator>mtproto</dc:creator>
<pubDate>Fri, 08 Jan 2021 14:52:40 +0300</pubDate>
<description><![CDATA[<div class="quote">Пожалуйста ознакомьтесь с нашим <a href="https://core.telegram.org/techfaq" target="_blank" rel="noopener external noreferrer">FAQ</a>. Разработчики клиентов обязаны соблюдать <a href="https://core.telegram.org/mtproto/security_guidelines" target="_blank" rel="noopener external noreferrer">правила безопасности</a>.</div>Эта страница посвящена базовому уровню шифрования MTProto, используемому для облачных чатов (шифрование сервер-клиент). См. также:<ul><li>Секретные чаты, конец-в-конец шифрования</li><li>Сквозные зашифрованные голосовые вызовы</li></ul><h2><b>Общее Описание</b></h2>Протокол предназначен для доступа к серверному API из приложений, работающих на мобильных устройствах. Следует подчеркнуть, что веб-браузер не является таким приложением.<br>Протокол разделён на три практически независимых компонента:<ul><li>Компонент высокого уровня (язык запросов API): определяет метод, с помощью которого запросы и ответы API преобразуются в двоичные сообщения.</li><li>Криптографический (авторизационный) компонент: определяет метод, с помощью которого сообщения шифруются перед передачей по транспортному протоколу.</li><li>Транспортный компонент: определяет метод передачи сообщений клиентом и сервером по какому-либо другому существующему сетевому протоколу (например, HTTP, HTTPS, WS (plain websockets), WSS (websockets over HTTPS), TCP, UDP).</li></ul><div style="text-align:center;"><a class="highslide" href="https://www.mtproto.ru/uploads/posts/2021-01/1610121214_qggdpxz2t608-01-2021.png" target="_blank"><img src="https://www.mtproto.ru/uploads/posts/2021-01/medium/1610121214_qggdpxz2t608-01-2021.png" alt="" class="fr-dib"></a></div>Начиная с версии 4.6, основные клиенты Telegram используют MTProto 2.0, описанный в этой статье. MTProto v1. 0 (описанный <a href="https://core.telegram.org/mtproto_v1" target="_blank" rel="noopener external noreferrer">здесь</a>  для справки) устарел и в настоящее время постепенно выводится из эксплуатации.<h2><b>Краткий обзор компонентов</b></h2><h3><b>Высокоуровневый компонент (язык запросов RPC/API)</b></h3>С точки зрения высокоуровневого компонента клиент и сервер обмениваются сообщениями внутри сеанса. Сеанс подключается к клиентскому устройству (точнее, к приложению), а не к определенному WebSocket/http/https/tcp-соединению. Кроме того, каждый сеанс привязывается к идентификатору ключа пользователя, с помощью которого фактически осуществляется авторизация.<br><br>Несколько соединений с сервером могут быть открыты; сообщения могут быть отправлены в любом направлении через любое из соединений (ответ на запрос не обязательно возвращается через то же соединение, которое отправляло запрос, хотя чаще всего это так; однако ни в коем случае сообщение не может быть возвращено через соединение, принадлежащее другому сеансу). При использовании протокола UDP ответ может быть возвращен через другой IP-адрес, чем тот, на который был отправлен запрос.<br><br>Существует несколько типов сообщений:<ul><li>Вызовы RPC (клиент-сервер): вызовы методов API</li><li>Ответы RPC (сервер-клиент): результаты вызовов RPC</li><li>Подтверждение приема сообщений (точнее, уведомление о состоянии набора сообщений)</li><li>Запрос статуса сообщения</li><li>Составное сообщение или контейнер (контейнер, содержащий несколько сообщений; необходим, например, для отправки нескольких вызовов RPC одновременно по HTTP-соединению; кроме того, контейнер может поддерживать gzip).</li></ul>С точки зрения протоколов более низкого уровня, сообщение представляет собой двоичный поток данных, выровненный по границе 4 или 16 байт. Первые несколько полей в сообщении фиксированы и используются криптографической/авторизационной системой.<br><br>Каждое сообщение, будь то отдельное или внутри контейнера, состоит из идентификатора сообщения (64 бита, см. ниже), порядкового номера сообщения в сеансе (32 бита), длины (тело сообщения в байтах; 32 бита) и тела (любой размер, кратный 4 байтам). Кроме того, при отправке контейнера или одного сообщения, сверху добавляется внутренний заголовок (см. ниже), затем шифруется все сообщение, а сверху помещается внешний заголовок (64-битный идентификатор ключа и 128-битный ключ сообщения).<br><br>Тело сообщения обычно состоит из 32-битного типа сообщения, за которым следуют параметры, зависящие от типа. В частности, каждая функция RPC имеет соответствующий тип сообщения. Дополнительные сведения см. в разделе сериализация двоичных данных, служебные сообщения.<br><br>Все числа записываются как little-endian. Однако очень большие числа (2048 бит), используемые в RSA и DH, записываются в формате big-endian, потому что именно так это делает библиотека OpenSSL.<h3><b>Авторизация и шифрование</b></h3>Перед передачей сообщения (или составного сообщения) по сети с использованием транспортного протокола оно шифруется определенным образом, и в верхней части сообщения добавляется внешний заголовок, который представляет собой: 64-битный идентификатор ключа (который однозначно идентифицирует ключ авторизации как для сервера, так и для пользователя) и 128-битный ключ сообщения. Пользовательский ключ вместе с ключом сообщения определяет фактический 256-битный ключ, который шифрует сообщение с помощью шифрования AES-256. Обратите внимание, что начальная часть зашифрованного сообщения содержит переменные данные (сеанс, идентификатор сообщения, порядковый номер, соль сервера), которые, очевидно, влияют на ключ сообщения (следовательно, ключ AES и iv). Ключ сообщения определяется как 128 средних битов SHA256 тела сообщения (включая сеанс, идентификатор сообщения и т. д.), включая байты заполнения, перед которыми стоят 32 байта, взятые из ключа авторизации. Составные сообщения шифруются как одно сообщение.<div class="quote">Технические характеристики см. В разделе <a href="https://core.telegram.org/mtproto/description" target="_blank" rel="noopener external noreferrer">Мобильный протокол: подробное описание</a></div>Первое, что должно сделать клиентское приложение - это создать ключ авторизации, который обычно генерируется при первом запуске и почти никогда не меняется.<br><br>Основным недостатком протокола является то, что злоумышленник, пассивно перехватывающий сообщения, а затем каким-то образом присваивающий ключ авторизации (например, путем кражи устройства), сможет расшифровать все перехваченные сообщения <i>post factum</i>. Это, вероятно, не слишком большая проблема (украдя устройство, можно также получить доступ ко всей информации, кэшированной на устройстве, ничего не расшифровывая); однако для преодоления этой проблемы можно предпринять следующие шаги:<ul><li>Сеансовые ключи генерируются с использованием протокола Диффи-Хеллмана и используются в сочетании с ключами авторизации и сообщения для выбора параметров AES. Чтобы создать их, первое, что клиент должен сделать после создания нового сеанса, - это отправить специальный RPC-запрос на сервер (“generate session key”), на который сервер ответит, после чего все последующие сообщения в сеансе также шифруются с помощью сессионного ключа.</li><li>Защита ключа, хранящегося на клиентском устройстве, с помощью (текстового) пароля; этот пароль никогда не хранится в памяти и вводится Пользователем при запуске приложения или чаще (в зависимости от настроек приложения).</li><li>Данные, хранящиеся (кэшированные) на пользовательском устройстве, также могут быть защищены шифрованием с использованием ключа авторизации, который, в свою очередь, должен быть защищен паролем. Затем потребуется пароль, чтобы получить доступ даже к этим данным.</li></ul><h3><b>Синхронизация Времени</b></h3>Если время клиента сильно отличается от времени сервера, сервер может начать игнорировать сообщения клиента или наоборот из-за недопустимого идентификатора сообщения (который тесно связан со временем создания). При таких обстоятельствах сервер отправит клиенту специальное сообщение, содержащее правильное время и определенную 128-битную соль (либо явно предоставленную клиентом в специальном запросе синхронизации RPC, либо равную ключу последнего сообщения, полученного от клиента в течение текущего сеанса). Это сообщение может быть первым в контейнере, включающем другие сообщения (если расхождение во времени значительно, но еще не приводит к игнорированию сообщений клиента).<br><br>Получив такое сообщение или контейнер, содержащий его, клиент сначала выполняет синхронизацию времени (по сути, просто сохраняет разницу между временем сервера и своим собственным, чтобы иметь возможность вычислить “правильное” время в будущем), а затем проверяет правильность идентификаторов сообщения.<br><br>Если коррекцией пренебрегли, клиент должен будет сгенерировать новый сеанс, чтобы обеспечить монотонность идентификаторов сообщений.<h3><b>MTProto транспорт</b></h3>Перед отправкой по выбранному транспортному протоколу полезная нагрузка должна быть завернута во вторичный заголовок протокола, определенный соответствующим транспортным протоколом MTProto.<ul><li>Сокращенный</li><li>Промежуточный</li><li>Мягкий промежуточный материал</li><li>Полный</li></ul>Сервер распознает эти различные протоколы (и также отличает их от HTTP) по заголовку. Кроме того, можно использовать следующие транспортные функции:<ul><li>Быстрая АСК</li><li>Транспортные ошибки</li><li>Транспортная запутанность</li></ul>Примеры реализации этих протоколов можно увидеть в tdlib и MadelineProto.<h3><b>Транспорт</b></h3>Обеспечивает доставку зашифрованных контейнеров вместе с внешним заголовком (далее-полезная нагрузка) от клиента к серверу и обратно. Определены несколько транспортных протоколов:<ul><li>TCP</li><li>С WebSocket</li><li>Websocket через HTTPS</li><li>HTTP</li><li>HTTPS</li><li>UDP</li></ul><a href="https://core.telegram.org/mtproto/transports" rel="external noopener noreferrer">(Мы рассмотрим только первые пять типов.)</a><h3><b>Заключение</b></h3>Напомним, что в качестве сравнения используется стек ISO/OSI:<br><br>Уровень 7 (приложение): высокоуровневый RPC API<br>Уровень 6 (Презентация): Язык Type<br>Уровень 5 (сеанс): сеанс MTProto<br>Уровень 4 (Транспортный):<br> 4.3: транспортный протокол MTProto<br> 4.2: обфускация MTProto (опционально)<br> 4.1: транспортный протокол<br>Уровень 3 (сеть): IP<br>Уровень 2 (канал передачи данных): MAC / LLC<br>Уровень 1 (физический): IEEE 802.3, IEEE 802.11 и т. д...]]></description>
<turbo:content><![CDATA[ <div class="quote">Пожалуйста ознакомьтесь с нашим <a href="https://core.telegram.org/techfaq" target="_blank" rel="noopener external noreferrer">FAQ</a>. Разработчики клиентов обязаны соблюдать <a href="https://core.telegram.org/mtproto/security_guidelines" target="_blank" rel="noopener external noreferrer">правила безопасности</a>.</div>Эта страница посвящена базовому уровню шифрования MTProto, используемому для облачных чатов (шифрование сервер-клиент). См. также:<ul><li>Секретные чаты, конец-в-конец шифрования</li><li>Сквозные зашифрованные голосовые вызовы</li></ul><h2><b>Общее Описание</b></h2>Протокол предназначен для доступа к серверному API из приложений, работающих на мобильных устройствах. Следует подчеркнуть, что веб-браузер не является таким приложением.<br>Протокол разделён на три практически независимых компонента:<ul><li>Компонент высокого уровня (язык запросов API): определяет метод, с помощью которого запросы и ответы API преобразуются в двоичные сообщения.</li><li>Криптографический (авторизационный) компонент: определяет метод, с помощью которого сообщения шифруются перед передачей по транспортному протоколу.</li><li>Транспортный компонент: определяет метод передачи сообщений клиентом и сервером по какому-либо другому существующему сетевому протоколу (например, HTTP, HTTPS, WS (plain websockets), WSS (websockets over HTTPS), TCP, UDP).</li></ul><div style="text-align:center;"><a class="highslide" href="https://www.mtproto.ru/uploads/posts/2021-01/1610121214_qggdpxz2t608-01-2021.png" target="_blank"><img src="https://www.mtproto.ru/uploads/posts/2021-01/medium/1610121214_qggdpxz2t608-01-2021.png" alt="" class="fr-dib"></a></div>Начиная с версии 4.6, основные клиенты Telegram используют MTProto 2.0, описанный в этой статье. MTProto v1. 0 (описанный <a href="https://core.telegram.org/mtproto_v1" target="_blank" rel="noopener external noreferrer">здесь</a>  для справки) устарел и в настоящее время постепенно выводится из эксплуатации.<h2><b>Краткий обзор компонентов</b></h2><h3><b>Высокоуровневый компонент (язык запросов RPC/API)</b></h3>С точки зрения высокоуровневого компонента клиент и сервер обмениваются сообщениями внутри сеанса. Сеанс подключается к клиентскому устройству (точнее, к приложению), а не к определенному WebSocket/http/https/tcp-соединению. Кроме того, каждый сеанс привязывается к идентификатору ключа пользователя, с помощью которого фактически осуществляется авторизация.<br><br>Несколько соединений с сервером могут быть открыты; сообщения могут быть отправлены в любом направлении через любое из соединений (ответ на запрос не обязательно возвращается через то же соединение, которое отправляло запрос, хотя чаще всего это так; однако ни в коем случае сообщение не может быть возвращено через соединение, принадлежащее другому сеансу). При использовании протокола UDP ответ может быть возвращен через другой IP-адрес, чем тот, на который был отправлен запрос.<br><br>Существует несколько типов сообщений:<ul><li>Вызовы RPC (клиент-сервер): вызовы методов API</li><li>Ответы RPC (сервер-клиент): результаты вызовов RPC</li><li>Подтверждение приема сообщений (точнее, уведомление о состоянии набора сообщений)</li><li>Запрос статуса сообщения</li><li>Составное сообщение или контейнер (контейнер, содержащий несколько сообщений; необходим, например, для отправки нескольких вызовов RPC одновременно по HTTP-соединению; кроме того, контейнер может поддерживать gzip).</li></ul>С точки зрения протоколов более низкого уровня, сообщение представляет собой двоичный поток данных, выровненный по границе 4 или 16 байт. Первые несколько полей в сообщении фиксированы и используются криптографической/авторизационной системой.<br><br>Каждое сообщение, будь то отдельное или внутри контейнера, состоит из идентификатора сообщения (64 бита, см. ниже), порядкового номера сообщения в сеансе (32 бита), длины (тело сообщения в байтах; 32 бита) и тела (любой размер, кратный 4 байтам). Кроме того, при отправке контейнера или одного сообщения, сверху добавляется внутренний заголовок (см. ниже), затем шифруется все сообщение, а сверху помещается внешний заголовок (64-битный идентификатор ключа и 128-битный ключ сообщения).<br><br>Тело сообщения обычно состоит из 32-битного типа сообщения, за которым следуют параметры, зависящие от типа. В частности, каждая функция RPC имеет соответствующий тип сообщения. Дополнительные сведения см. в разделе сериализация двоичных данных, служебные сообщения.<br><br>Все числа записываются как little-endian. Однако очень большие числа (2048 бит), используемые в RSA и DH, записываются в формате big-endian, потому что именно так это делает библиотека OpenSSL.<h3><b>Авторизация и шифрование</b></h3>Перед передачей сообщения (или составного сообщения) по сети с использованием транспортного протокола оно шифруется определенным образом, и в верхней части сообщения добавляется внешний заголовок, который представляет собой: 64-битный идентификатор ключа (который однозначно идентифицирует ключ авторизации как для сервера, так и для пользователя) и 128-битный ключ сообщения. Пользовательский ключ вместе с ключом сообщения определяет фактический 256-битный ключ, который шифрует сообщение с помощью шифрования AES-256. Обратите внимание, что начальная часть зашифрованного сообщения содержит переменные данные (сеанс, идентификатор сообщения, порядковый номер, соль сервера), которые, очевидно, влияют на ключ сообщения (следовательно, ключ AES и iv). Ключ сообщения определяется как 128 средних битов SHA256 тела сообщения (включая сеанс, идентификатор сообщения и т. д.), включая байты заполнения, перед которыми стоят 32 байта, взятые из ключа авторизации. Составные сообщения шифруются как одно сообщение.<div class="quote">Технические характеристики см. В разделе <a href="https://core.telegram.org/mtproto/description" target="_blank" rel="noopener external noreferrer">Мобильный протокол: подробное описание</a></div>Первое, что должно сделать клиентское приложение - это создать ключ авторизации, который обычно генерируется при первом запуске и почти никогда не меняется.<br><br>Основным недостатком протокола является то, что злоумышленник, пассивно перехватывающий сообщения, а затем каким-то образом присваивающий ключ авторизации (например, путем кражи устройства), сможет расшифровать все перехваченные сообщения <i>post factum</i>. Это, вероятно, не слишком большая проблема (украдя устройство, можно также получить доступ ко всей информации, кэшированной на устройстве, ничего не расшифровывая); однако для преодоления этой проблемы можно предпринять следующие шаги:<ul><li>Сеансовые ключи генерируются с использованием протокола Диффи-Хеллмана и используются в сочетании с ключами авторизации и сообщения для выбора параметров AES. Чтобы создать их, первое, что клиент должен сделать после создания нового сеанса, - это отправить специальный RPC-запрос на сервер (“generate session key”), на который сервер ответит, после чего все последующие сообщения в сеансе также шифруются с помощью сессионного ключа.</li><li>Защита ключа, хранящегося на клиентском устройстве, с помощью (текстового) пароля; этот пароль никогда не хранится в памяти и вводится Пользователем при запуске приложения или чаще (в зависимости от настроек приложения).</li><li>Данные, хранящиеся (кэшированные) на пользовательском устройстве, также могут быть защищены шифрованием с использованием ключа авторизации, который, в свою очередь, должен быть защищен паролем. Затем потребуется пароль, чтобы получить доступ даже к этим данным.</li></ul><h3><b>Синхронизация Времени</b></h3>Если время клиента сильно отличается от времени сервера, сервер может начать игнорировать сообщения клиента или наоборот из-за недопустимого идентификатора сообщения (который тесно связан со временем создания). При таких обстоятельствах сервер отправит клиенту специальное сообщение, содержащее правильное время и определенную 128-битную соль (либо явно предоставленную клиентом в специальном запросе синхронизации RPC, либо равную ключу последнего сообщения, полученного от клиента в течение текущего сеанса). Это сообщение может быть первым в контейнере, включающем другие сообщения (если расхождение во времени значительно, но еще не приводит к игнорированию сообщений клиента).<br><br>Получив такое сообщение или контейнер, содержащий его, клиент сначала выполняет синхронизацию времени (по сути, просто сохраняет разницу между временем сервера и своим собственным, чтобы иметь возможность вычислить “правильное” время в будущем), а затем проверяет правильность идентификаторов сообщения.<br><br>Если коррекцией пренебрегли, клиент должен будет сгенерировать новый сеанс, чтобы обеспечить монотонность идентификаторов сообщений.<h3><b>MTProto транспорт</b></h3>Перед отправкой по выбранному транспортному протоколу полезная нагрузка должна быть завернута во вторичный заголовок протокола, определенный соответствующим транспортным протоколом MTProto.<ul><li>Сокращенный</li><li>Промежуточный</li><li>Мягкий промежуточный материал</li><li>Полный</li></ul>Сервер распознает эти различные протоколы (и также отличает их от HTTP) по заголовку. Кроме того, можно использовать следующие транспортные функции:<ul><li>Быстрая АСК</li><li>Транспортные ошибки</li><li>Транспортная запутанность</li></ul>Примеры реализации этих протоколов можно увидеть в tdlib и MadelineProto.<h3><b>Транспорт</b></h3>Обеспечивает доставку зашифрованных контейнеров вместе с внешним заголовком (далее-полезная нагрузка) от клиента к серверу и обратно. Определены несколько транспортных протоколов:<ul><li>TCP</li><li>С WebSocket</li><li>Websocket через HTTPS</li><li>HTTP</li><li>HTTPS</li><li>UDP</li></ul><a href="https://core.telegram.org/mtproto/transports" rel="external noopener noreferrer">(Мы рассмотрим только первые пять типов.)</a><h3><b>Заключение</b></h3>Напомним, что в качестве сравнения используется стек ISO/OSI:<br><br>Уровень 7 (приложение): высокоуровневый RPC API<br>Уровень 6 (Презентация): Язык Type<br>Уровень 5 (сеанс): сеанс MTProto<br>Уровень 4 (Транспортный):<br> 4.3: транспортный протокол MTProto<br> 4.2: обфускация MTProto (опционально)<br> 4.1: транспортный протокол<br>Уровень 3 (сеть): IP<br>Уровень 2 (канал передачи данных): MAC / LLC<br>Уровень 1 (физический): IEEE 802.3, IEEE 802.11 и т. д... ]]></turbo:content>
<content:encoded><![CDATA[ <div class="quote">Пожалуйста ознакомьтесь с нашим <a href="https://core.telegram.org/techfaq" target="_blank" rel="noopener external noreferrer">FAQ</a>. Разработчики клиентов обязаны соблюдать <a href="https://core.telegram.org/mtproto/security_guidelines" target="_blank" rel="noopener external noreferrer">правила безопасности</a>.</div>Эта страница посвящена базовому уровню шифрования MTProto, используемому для облачных чатов (шифрование сервер-клиент). См. также:<ul><li>Секретные чаты, конец-в-конец шифрования</li><li>Сквозные зашифрованные голосовые вызовы</li></ul><h2><b>Общее Описание</b></h2>Протокол предназначен для доступа к серверному API из приложений, работающих на мобильных устройствах. Следует подчеркнуть, что веб-браузер не является таким приложением.<br>Протокол разделён на три практически независимых компонента:<ul><li>Компонент высокого уровня (язык запросов API): определяет метод, с помощью которого запросы и ответы API преобразуются в двоичные сообщения.</li><li>Криптографический (авторизационный) компонент: определяет метод, с помощью которого сообщения шифруются перед передачей по транспортному протоколу.</li><li>Транспортный компонент: определяет метод передачи сообщений клиентом и сервером по какому-либо другому существующему сетевому протоколу (например, HTTP, HTTPS, WS (plain websockets), WSS (websockets over HTTPS), TCP, UDP).</li></ul><div style="text-align:center;"><a class="highslide" href="https://www.mtproto.ru/uploads/posts/2021-01/1610121214_qggdpxz2t608-01-2021.png" target="_blank"><img src="https://www.mtproto.ru/uploads/posts/2021-01/medium/1610121214_qggdpxz2t608-01-2021.png" alt="" class="fr-dib"></a></div>Начиная с версии 4.6, основные клиенты Telegram используют MTProto 2.0, описанный в этой статье. MTProto v1. 0 (описанный <a href="https://core.telegram.org/mtproto_v1" target="_blank" rel="noopener external noreferrer">здесь</a>  для справки) устарел и в настоящее время постепенно выводится из эксплуатации.<h2><b>Краткий обзор компонентов</b></h2><h3><b>Высокоуровневый компонент (язык запросов RPC/API)</b></h3>С точки зрения высокоуровневого компонента клиент и сервер обмениваются сообщениями внутри сеанса. Сеанс подключается к клиентскому устройству (точнее, к приложению), а не к определенному WebSocket/http/https/tcp-соединению. Кроме того, каждый сеанс привязывается к идентификатору ключа пользователя, с помощью которого фактически осуществляется авторизация.<br><br>Несколько соединений с сервером могут быть открыты; сообщения могут быть отправлены в любом направлении через любое из соединений (ответ на запрос не обязательно возвращается через то же соединение, которое отправляло запрос, хотя чаще всего это так; однако ни в коем случае сообщение не может быть возвращено через соединение, принадлежащее другому сеансу). При использовании протокола UDP ответ может быть возвращен через другой IP-адрес, чем тот, на который был отправлен запрос.<br><br>Существует несколько типов сообщений:<ul><li>Вызовы RPC (клиент-сервер): вызовы методов API</li><li>Ответы RPC (сервер-клиент): результаты вызовов RPC</li><li>Подтверждение приема сообщений (точнее, уведомление о состоянии набора сообщений)</li><li>Запрос статуса сообщения</li><li>Составное сообщение или контейнер (контейнер, содержащий несколько сообщений; необходим, например, для отправки нескольких вызовов RPC одновременно по HTTP-соединению; кроме того, контейнер может поддерживать gzip).</li></ul>С точки зрения протоколов более низкого уровня, сообщение представляет собой двоичный поток данных, выровненный по границе 4 или 16 байт. Первые несколько полей в сообщении фиксированы и используются криптографической/авторизационной системой.<br><br>Каждое сообщение, будь то отдельное или внутри контейнера, состоит из идентификатора сообщения (64 бита, см. ниже), порядкового номера сообщения в сеансе (32 бита), длины (тело сообщения в байтах; 32 бита) и тела (любой размер, кратный 4 байтам). Кроме того, при отправке контейнера или одного сообщения, сверху добавляется внутренний заголовок (см. ниже), затем шифруется все сообщение, а сверху помещается внешний заголовок (64-битный идентификатор ключа и 128-битный ключ сообщения).<br><br>Тело сообщения обычно состоит из 32-битного типа сообщения, за которым следуют параметры, зависящие от типа. В частности, каждая функция RPC имеет соответствующий тип сообщения. Дополнительные сведения см. в разделе сериализация двоичных данных, служебные сообщения.<br><br>Все числа записываются как little-endian. Однако очень большие числа (2048 бит), используемые в RSA и DH, записываются в формате big-endian, потому что именно так это делает библиотека OpenSSL.<h3><b>Авторизация и шифрование</b></h3>Перед передачей сообщения (или составного сообщения) по сети с использованием транспортного протокола оно шифруется определенным образом, и в верхней части сообщения добавляется внешний заголовок, который представляет собой: 64-битный идентификатор ключа (который однозначно идентифицирует ключ авторизации как для сервера, так и для пользователя) и 128-битный ключ сообщения. Пользовательский ключ вместе с ключом сообщения определяет фактический 256-битный ключ, который шифрует сообщение с помощью шифрования AES-256. Обратите внимание, что начальная часть зашифрованного сообщения содержит переменные данные (сеанс, идентификатор сообщения, порядковый номер, соль сервера), которые, очевидно, влияют на ключ сообщения (следовательно, ключ AES и iv). Ключ сообщения определяется как 128 средних битов SHA256 тела сообщения (включая сеанс, идентификатор сообщения и т. д.), включая байты заполнения, перед которыми стоят 32 байта, взятые из ключа авторизации. Составные сообщения шифруются как одно сообщение.<div class="quote">Технические характеристики см. В разделе <a href="https://core.telegram.org/mtproto/description" target="_blank" rel="noopener external noreferrer">Мобильный протокол: подробное описание</a></div>Первое, что должно сделать клиентское приложение - это создать ключ авторизации, который обычно генерируется при первом запуске и почти никогда не меняется.<br><br>Основным недостатком протокола является то, что злоумышленник, пассивно перехватывающий сообщения, а затем каким-то образом присваивающий ключ авторизации (например, путем кражи устройства), сможет расшифровать все перехваченные сообщения <i>post factum</i>. Это, вероятно, не слишком большая проблема (украдя устройство, можно также получить доступ ко всей информации, кэшированной на устройстве, ничего не расшифровывая); однако для преодоления этой проблемы можно предпринять следующие шаги:<ul><li>Сеансовые ключи генерируются с использованием протокола Диффи-Хеллмана и используются в сочетании с ключами авторизации и сообщения для выбора параметров AES. Чтобы создать их, первое, что клиент должен сделать после создания нового сеанса, - это отправить специальный RPC-запрос на сервер (“generate session key”), на который сервер ответит, после чего все последующие сообщения в сеансе также шифруются с помощью сессионного ключа.</li><li>Защита ключа, хранящегося на клиентском устройстве, с помощью (текстового) пароля; этот пароль никогда не хранится в памяти и вводится Пользователем при запуске приложения или чаще (в зависимости от настроек приложения).</li><li>Данные, хранящиеся (кэшированные) на пользовательском устройстве, также могут быть защищены шифрованием с использованием ключа авторизации, который, в свою очередь, должен быть защищен паролем. Затем потребуется пароль, чтобы получить доступ даже к этим данным.</li></ul><h3><b>Синхронизация Времени</b></h3>Если время клиента сильно отличается от времени сервера, сервер может начать игнорировать сообщения клиента или наоборот из-за недопустимого идентификатора сообщения (который тесно связан со временем создания). При таких обстоятельствах сервер отправит клиенту специальное сообщение, содержащее правильное время и определенную 128-битную соль (либо явно предоставленную клиентом в специальном запросе синхронизации RPC, либо равную ключу последнего сообщения, полученного от клиента в течение текущего сеанса). Это сообщение может быть первым в контейнере, включающем другие сообщения (если расхождение во времени значительно, но еще не приводит к игнорированию сообщений клиента).<br><br>Получив такое сообщение или контейнер, содержащий его, клиент сначала выполняет синхронизацию времени (по сути, просто сохраняет разницу между временем сервера и своим собственным, чтобы иметь возможность вычислить “правильное” время в будущем), а затем проверяет правильность идентификаторов сообщения.<br><br>Если коррекцией пренебрегли, клиент должен будет сгенерировать новый сеанс, чтобы обеспечить монотонность идентификаторов сообщений.<h3><b>MTProto транспорт</b></h3>Перед отправкой по выбранному транспортному протоколу полезная нагрузка должна быть завернута во вторичный заголовок протокола, определенный соответствующим транспортным протоколом MTProto.<ul><li>Сокращенный</li><li>Промежуточный</li><li>Мягкий промежуточный материал</li><li>Полный</li></ul>Сервер распознает эти различные протоколы (и также отличает их от HTTP) по заголовку. Кроме того, можно использовать следующие транспортные функции:<ul><li>Быстрая АСК</li><li>Транспортные ошибки</li><li>Транспортная запутанность</li></ul>Примеры реализации этих протоколов можно увидеть в tdlib и MadelineProto.<h3><b>Транспорт</b></h3>Обеспечивает доставку зашифрованных контейнеров вместе с внешним заголовком (далее-полезная нагрузка) от клиента к серверу и обратно. Определены несколько транспортных протоколов:<ul><li>TCP</li><li>С WebSocket</li><li>Websocket через HTTPS</li><li>HTTP</li><li>HTTPS</li><li>UDP</li></ul><a href="https://core.telegram.org/mtproto/transports" rel="external noopener noreferrer">(Мы рассмотрим только первые пять типов.)</a><h3><b>Заключение</b></h3>Напомним, что в качестве сравнения используется стек ISO/OSI:<br><br>Уровень 7 (приложение): высокоуровневый RPC API<br>Уровень 6 (Презентация): Язык Type<br>Уровень 5 (сеанс): сеанс MTProto<br>Уровень 4 (Транспортный):<br> 4.3: транспортный протокол MTProto<br> 4.2: обфускация MTProto (опционально)<br> 4.1: транспортный протокол<br>Уровень 3 (сеть): IP<br>Уровень 2 (канал передачи данных): MAC / LLC<br>Уровень 1 (физический): IEEE 802.3, IEEE 802.11 и т. д... ]]></content:encoded>
</item></channel></rss>