Update content of files

This commit is contained in:
GitHub Action 2022-03-02 20:18:45 +00:00
parent 0f787aff3e
commit 68e0ebb042
33 changed files with 189 additions and 137 deletions

View file

@ -60,7 +60,7 @@ MTProto v.1.0 is deprecated and is currently being phased out.</p>
</ul>
<p></div></p>
<hr>
<p>Secret Chats are one-on-one chats wherein messages are encrypted with a key held only by the chats participants. Note that the <a href="/schema/end-to-end">schema</a> for these end-to-end encrypted Secret Chats is different from what is used for <a href="/mtproto">cloud chats</a>:</p>
<p>Secret Chats are one-on-one chats wherein messages are encrypted with a key held only by the chat's participants. Note that the <a href="/schema/end-to-end">schema</a> for these end-to-end encrypted Secret Chats is different from what is used for <a href="/mtproto">cloud chats</a>:</p>
<div>
<a href="/file/811140633/4/hHw6Zy2DPyQ.109500/cabc10049a7190694f" target="_blank"><img src="/file/811140633/4/hHw6Zy2DPyQ.109500/cabc10049a7190694f" title="End-to-end encryption in MTProto 2.0 (Secret Chats)" class="dev_page_image" style="max-width: 600px"></a>
</div>
@ -82,8 +82,8 @@ The principal differences from version 1.0 (<a href="/api/end-to-end_v1">describ
<p>Executing this method before each new key generation procedure is of vital importance. It makes sense to cache the values of the parameters together with the version in order to avoid having to receive all of the values every time. If the version stored on the client is still up-to-date, the server will return the constructor <a href="/constructor/messages.dhConfigNotModified">messages.dhConfigNotModified</a>.</p>
<p>Client is expected to check whether <strong>p</strong> is a safe 2048-bit prime (meaning that both <strong>p</strong> and <strong>(p-1)/2</strong> are prime, and that 2^2047 &lt; p &lt; 2^2048), and that <strong>g</strong> generates a cyclic subgroup of prime order <strong>(p-1)/2</strong>, i.e. is a quadratic residue <strong>mod p</strong>. Since <strong>g</strong> is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on <strong>p mod 4g</strong> -- namely, <strong>p mod 8 = 7</strong> for <strong>g = 2</strong>; <strong>p mod 3 = 2</strong> for <strong>g = 3</strong>; no extra condition for <strong>g = 4</strong>; <strong>p mod 5 = 1 or 4</strong> for <strong>g = 5</strong>; <strong>p mod 24 = 19 or 23</strong> for <strong>g = 6</strong>; and <strong>p mod 7 = 3, 5 or 6</strong> for <strong>g = 7</strong>. After <strong>g</strong> and <strong>p</strong> have been checked by the client, it makes sense to cache the result, so as to avoid repeating lengthy computations in future. This cache might be shared with one used for <a href="/mtproto/auth_key">Authorization Key generation</a>.</p>
<p>If the client needs additional entropy for the random number generator, it can pass the <strong>random_length</strong> parameter (random_length&gt; 0) so the server generates its own random sequence <strong>random</strong> of the appropriate length.
<strong>Important</strong>: using the servers random sequence in its raw form may be unsafe, it must be combined with a client sequence.</p>
<p>Client <strong>A</strong> computes a 2048-bit number <strong>a</strong> (using sufficient entropy or the servers <strong>random</strong>; see above) and executes <a href="/method/messages.requestEncryption">messages.requestEncryption</a> after passing in <code>g_a := pow(g, a) mod dh_prime</code>.</p>
<strong>Important</strong>: using the server's random sequence in its raw form may be unsafe, it must be combined with a client sequence.</p>
<p>Client <strong>A</strong> computes a 2048-bit number <strong>a</strong> (using sufficient entropy or the server's <strong>random</strong>; see above) and executes <a href="/method/messages.requestEncryption">messages.requestEncryption</a> after passing in <code>g_a := pow(g, a) mod dh_prime</code>.</p>
<p>User <strong>B</strong> receives the update <a href="/constructor/updateEncryption">updateEncryption</a> for all associated authorization keys (all authorized devices) with the chat constructor <a href="/constructor/encryptedChatRequested">encryptedChatRequested</a>. The user must be shown basic information about User <strong>A</strong> and must be prompted to accept or reject the request.</p>
<p>Both clients are to check that <strong>g</strong>, <strong>g_a</strong> and <strong>g_b</strong> are greater than one and smaller than <strong>p-1</strong>. We recommend checking that <strong>g_a</strong> and <strong>g_b</strong> are between <strong>2^{2048-64}</strong> and <strong>p - 2^{2048-64}</strong> as well.</p>
<h4><a class="anchor" href="#accepting-a-request" id="accepting-a-request" name="accepting-a-request"><i class="anchor-icon"></i></a>Accepting a Request</h4>
@ -92,7 +92,7 @@ The principal differences from version 1.0 (<a href="/api/end-to-end_v1">describ
<p><strong>Note 1:</strong> in this particular case SHA1 is used here even for MTProto 2.0 secret chats. </p>
<p><strong>Note 2:</strong> this fingerprint is used as a sanity check for the key exchange procedure to detect bugs when developing client software — it is not connected to the key visualization used on the clients as means of external authentication in secret chats. <a href="/api/end-to-end/pfs#key-visualization">Key visualizations</a> on the clients are generated using the first 128 bits of SHA1(initial key) followed by the first 160 bits of SHA256(key used when secret chat was updated to layer 46).</p>
<p>Client <strong>B</strong> executes <a href="/method/messages.acceptEncryption">messages.acceptEncryption</a> after passing it <code>g_b := pow(g, b) mod dh_prime</code> and <strong>key_fingerprint</strong>.</p>
<p>For all of Client <strong>Bs</strong> authorized devices, except the current one, <a href="/constructor/updateEncryption">updateEncryption</a> updates are sent with the constructor <a href="/constructor/encryptedChatDiscarded">encryptedChatDiscarded</a>. Thereafter, the only device that will be able to access the secret chat is Device <strong>B</strong>, which made the call to <a href="/method/messages.acceptEncryption">messages.acceptEncryption</a>.</p>
<p>For all of Client <strong>B's</strong> authorized devices, except the current one, <a href="/constructor/updateEncryption">updateEncryption</a> updates are sent with the constructor <a href="/constructor/encryptedChatDiscarded">encryptedChatDiscarded</a>. Thereafter, the only device that will be able to access the secret chat is Device <strong>B</strong>, which made the call to <a href="/method/messages.acceptEncryption">messages.acceptEncryption</a>.</p>
<p>User <strong>A</strong> will be sent an <a href="/constructor/updateEncryption">updateEncryption</a> update with the constructor <a href="/constructor/encryptedChat">encryptedChat</a>, for the authorization key that initiated the chat.</p>
<p>With <strong>g_b</strong> from the update, Client <strong>A</strong> can also compute the shared key <code>key = (pow(g_b, a) mod dh_prime)</code>. If key length &lt; 256 bytes, add several leading zero bytes as padding — so that the key is exactly 256 bytes long. If the fingerprint for the received key is identical to the one that was passed to <a href="/constructor/encryptedChat">encryptedChat</a>, incoming messages can be sent and processed. Otherwise, <a href="/method/messages.discardEncryption">messages.discardEncryption</a> must be executed and the user notified.</p>
<h4><a class="anchor" href="#perfect-forward-secrecy" id="perfect-forward-secrecy" name="perfect-forward-secrecy"><i class="anchor-icon"></i></a>Perfect Forward Secrecy</h4>
@ -125,7 +125,7 @@ The principal differences from version 1.0 (<a href="/api/end-to-end_v1">describ
<p>Encrypted data is embedded into a <a href="/method/messages.sendEncrypted">messages.sendEncrypted</a> API call and passed to Telegram server for delivery to the other party of the Secret Chat.</p>
<h4><a class="anchor" href="#upgrading-to-mtproto-20-from-mtproto-10" id="upgrading-to-mtproto-20-from-mtproto-10" name="upgrading-to-mtproto-20-from-mtproto-10"><i class="anchor-icon"></i></a>Upgrading to MTProto 2.0 from MTProto 1.0</h4>
<p>As soon as both parties in a secret chat are using at least Layer 73, they should only use MTProto 2.0 for all outgoing messages. Some of the first received messages may use MTProto 1.0, if a sufficiently high starting layer has not been negotiated during the creation of the secret chat. After the first message encrypted with MTProto 2.0 (or the first message with Layer 73 or higher) is received, all messages with higher <a href="#sequence-numbers">sequence numbers</a> must be encrypted with MTProto 2.0 as well.</p>
<p>As long as the current layer is lower than 73, each party should try to decrypt received messages with MTProto 1.0, and if this is not successfull (msg_key does not match), try MTProto 2.0. Once the first MTProto 2.0-encrypted message arrives (or the layer is upgraded to 73), there is no need to try MTProto 1.0 decryption for any of the further messages (unless the client is still waiting for some gaps to be closed).</p>
<p>As long as the current layer is lower than 73, each party should try to decrypt received messages with MTProto 1.0, and if this is not successful (msg_key does not match), try MTProto 2.0. Once the first MTProto 2.0-encrypted message arrives (or the layer is upgraded to 73), there is no need to try MTProto 1.0 decryption for any of the further messages (unless the client is still waiting for some gaps to be closed).</p>
<h4><a class="anchor" href="#decrypting-an-incoming-message" id="decrypting-an-incoming-message" name="decrypting-an-incoming-message"><i class="anchor-icon"></i></a>Decrypting an Incoming Message</h4>
<p>The steps above are performed in reverse order.
When an encrypted message is received, you <strong>must</strong> check that msg_key is <strong>in fact</strong> equal to the 128 middle bits of the SHA256 hash of the decrypted message, prepended by 32 bytes taken from the shared <strong>key</strong>.
@ -137,7 +137,7 @@ If the message layer is greater than the one supported by the client, the user m
</blockquote>
<p>Please note that your client must support sequence numbers in Secret Chats to be compatible with official Telegram clients. </p>
<h4><a class="anchor" href="#sending-encrypted-files" id="sending-encrypted-files" name="sending-encrypted-files"><i class="anchor-icon"></i></a>Sending Encrypted Files</h4>
<p>All files sent to secret chats are encrypted with one-time keys that are in no way related to the chats shared key. Before an encrypted file is sent, it is assumed that the encrypted files address will be attached to the outside of an encrypted message using the <strong>file</strong> parameter of the <a href="/method/messages.sendEncryptedFile">messages.sendEncryptedFile</a> method and that the key for direct decryption will be sent in the body of the message (the <strong>key</strong> parameter in the constructors <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaPhoto</a>, <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaVideo</a> and <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaFile</a>.</p>
<p>All files sent to secret chats are encrypted with one-time keys that are in no way related to the chat's shared key. Before an encrypted file is sent, it is assumed that the encrypted file's address will be attached to the outside of an encrypted message using the <strong>file</strong> parameter of the <a href="/method/messages.sendEncryptedFile">messages.sendEncryptedFile</a> method and that the key for direct decryption will be sent in the body of the message (the <strong>key</strong> parameter in the constructors <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaPhoto</a>, <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaVideo</a> and <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaFile</a>.</p>
<p>Prior to a file being sent to a secret chat, 2 random 256-bit numbers are computed which will serve as the AES key and initialization vector used to encrypt the file. AES-256 encryption with infinite garble extension (IGE) is used in like manner.</p>
<p>The key fingerprint is computed as follows:</p>
<ul>
@ -148,7 +148,7 @@ If the message layer is greater than the one supported by the client, the user m
A subsequent call to <a href="/method/messages.sendEncryptedFile">messages.sendEncryptedFile</a> will assign an identifier to the stored file and send the address together with the message. The recipient will receive an update with <a href="/constructor/encryptedMessage">encryptedMessage</a>, and the <strong>file</strong> parameter will contain file information.</p>
<p>Incoming and outgoing encrypted files can be forwarded to other secret chats using the constructor <a href="/constructor/inputEncryptedFile">inputEncryptedFile</a> to avoid saving the same content on the server twice.</p>
<h4><a class="anchor" href="#working-with-an-update-box" id="working-with-an-update-box" name="working-with-an-update-box"><i class="anchor-icon"></i></a>Working with an Update Box</h4>
<p>Secret chats are associated with specific devices (or rather with <a href="/mtproto/description#creating-an-authorization-key">authorization keys</a>), not users. A conventional message box, which uses <strong>pts</strong> to describe the clients status, is not suitable, because it is designed for long-term message storage and message access from different devices.</p>
<p>Secret chats are associated with specific devices (or rather with <a href="/mtproto/description#creating-an-authorization-key">authorization keys</a>), not users. A conventional message box, which uses <strong>pts</strong> to describe the client's status, is not suitable, because it is designed for long-term message storage and message access from different devices.</p>
<p>An additional temporary message queue is introduced as a solution to this problem. When an update regarding a message from a secret chat is sent, a new value of <strong>qts</strong> is sent, which helps reconstruct the difference if there has been a long break in the connection or in case of loss of an update.</p>
<p>As the number of events increases, the value of <strong>qts</strong> increases by 1 with each new event. The initial value may not (and will not) be equal to 0.</p>
<p>The fact that events from the temporary queue have been received and stored by the client is acknowledged explicitly by a call to the <a href="/method/messages.receivedQueue">messages.receivedQueue</a> method or implicitly by a call to <a href="/method/updates.getDifference">updates.getDifference</a> (the value of <strong>qts</strong> passed, not the final state). All messages acknowledged as delivered by the client, as well as any messages older than 7 days, may (and will) be deleted from the server.</p>
@ -164,7 +164,7 @@ A subsequent call to <a href="/method/messages.sendEncryptedFile">messages.sendE
<p>There are two cases when your client must notify the remote client about its local layer:</p>
<ol>
<li>As soon as a new secret chat has been created, immediately after the secret key has been successfully exchanged.</li>
<li>Immediately after the local client has been updated to support a new secret chat layer. In this case notifications must be sent to <strong>all</strong> currently existing secret chats. Note that this is only necessary when updating to new layers that contain changes in the secret chats implementation (e.g. you dont need to do this when your client is updated from Layer 46 to Layer 47).</li>
<li>Immediately after the local client has been updated to support a new secret chat layer. In this case notifications must be sent to <strong>all</strong> currently existing secret chats. Note that this is only necessary when updating to new layers that contain changes in the secret chats implementation (e.g. you don't need to do this when your client is updated from Layer 46 to Layer 47).</li>
</ol></div>
</div>

View file

@ -57,7 +57,7 @@ For information on end-to-end encryption used in up-to-date Telegram clients, ki
</ul>
<p></div></p>
<hr>
<p>Secret Chats are one-on-one chats wherein messages are encrypted with a key held only by the chats participants. Please note that the <a href="/schema/end-to-end">schema</a> for end-to-end encrypted Secret Chats is different from what is used for <a href="/mtproto">cloud chats</a>:</p>
<p>Secret Chats are one-on-one chats wherein messages are encrypted with a key held only by the chat's participants. Please note that the <a href="/schema/end-to-end">schema</a> for end-to-end encrypted Secret Chats is different from what is used for <a href="/mtproto">cloud chats</a>:</p>
<div>
<a href="/file/811140845/3/3aEkph1_NYU/cf861ae5ea85912769" target="_blank"><img src="/file/811140845/3/3aEkph1_NYU/cf861ae5ea85912769" title="End-to-end encryption in MTProto (Secret Chats)" class="dev_page_image"></a>
</div>
@ -69,8 +69,8 @@ For information on end-to-end encryption used in up-to-date Telegram clients, ki
<p>Executing this method before each new key generation procedure is of vital importance. It makes sense to cache the values of the parameters together with the version in order to avoid having to receive all of the values every time. If the version stored on the client is still up-to-date, the server will return the constructor <a href="/constructor/messages.dhConfigNotModified">messages.dhConfigNotModified</a>.</p>
<p>Client is expected to check whether <strong>p</strong> is a safe 2048-bit prime (meaning that both <strong>p</strong> and <strong>(p-1)/2</strong> are prime, and that 2^2047 &lt; p &lt; 2^2048), and that <strong>g</strong> generates a cyclic subgroup of prime order <strong>(p-1)/2</strong>, i.e. is a quadratic residue <strong>mod p</strong>. Since <strong>g</strong> is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on <strong>p mod 4g</strong> -- namely, <strong>p mod 8 = 7</strong> for <strong>g = 2</strong>; <strong>p mod 3 = 2</strong> for <strong>g = 3</strong>; no extra condition for <strong>g = 4</strong>; <strong>p mod 5 = 1 or 4</strong> for <strong>g = 5</strong>; <strong>p mod 24 = 19 or 23</strong> for <strong>g = 6</strong>; and <strong>p mod 7 = 3, 5 or 6</strong> for <strong>g = 7</strong>. After <strong>g</strong> and <strong>p</strong> have been checked by the client, it makes sense to cache the result, so as to avoid repeating lengthy computations in future. This cache might be shared with one used for <a href="/mtproto/auth_key">Authorization Key generation</a>.</p>
<p>If the client has an inadequate random number generator, it makes sense to pass the <strong>random_length</strong> parameter (random_length&gt; 0) so the server generates its own random sequence <strong>random</strong> of the appropriate length.
<strong>Important</strong>: using the servers random sequence in its raw form may be unsafe. It must be combined with a client sequence, for example, by generating a client random number of the same length (<strong>client_random</strong>) and using <code>final_random := random XOR client_random</code>.</p>
<p>Client <strong>A</strong> computes a 2048-bit number <strong>a</strong> (using sufficient entropy or the servers <strong>random</strong>; see above) and executes <a href="/method/messages.requestEncryption">messages.requestEncryption</a> after passing in <code>g_a := pow(g, a) mod dh_prime</code>.</p>
<strong>Important</strong>: using the server's random sequence in its raw form may be unsafe. It must be combined with a client sequence, for example, by generating a client random number of the same length (<strong>client_random</strong>) and using <code>final_random := random XOR client_random</code>.</p>
<p>Client <strong>A</strong> computes a 2048-bit number <strong>a</strong> (using sufficient entropy or the server's <strong>random</strong>; see above) and executes <a href="/method/messages.requestEncryption">messages.requestEncryption</a> after passing in <code>g_a := pow(g, a) mod dh_prime</code>.</p>
<p>User <strong>B</strong> receives the update <a href="/constructor/updateEncryption">updateEncryption</a> for all associated authorization keys (all authorized devices) with the chat constructor <a href="/constructor/encryptedChatRequested">encryptedChatRequested</a>. The user must be shown basic information about User <strong>A</strong> and must be prompted to accept or reject the request.</p>
<p>Both clients are to check that <strong>g</strong>, <strong>g_a</strong> and <strong>g_b</strong> are greater than one and smaller than <strong>p-1</strong>. We recommend checking that <strong>g_a</strong> and <strong>g_b</strong> are between <strong>2^{2048-64}</strong> and <strong>p - 2^{2048-64}</strong> as well.</p>
<h4><a class="anchor" href="#accepting-a-request" id="accepting-a-request" name="accepting-a-request"><i class="anchor-icon"></i></a>Accepting a Request</h4>
@ -78,7 +78,7 @@ For information on end-to-end encryption used in up-to-date Telegram clients, ki
<p>Having received <strong>g_a</strong> from the update with <a href="/constructor/encryptedChatRequested">encryptedChatRequested</a>, it can immediately generate the final shared key: <code>key = (pow(g_a, b) mod dh_prime)</code>. If key length &lt; 256 bytes, add several leading zero bytes as padding — so that the key is exactly 256 bytes long. Its fingerprint, <strong>key_fingerprint</strong>, is equal to the 64 last bits of SHA1 (key).</p>
<p><strong>Note:</strong> this fingerprint is used as a sanity check for the key exchange procedure to detect bugs while developing client software — it is not connected to the key visualization used on the clients as means of external authentication in secret chats. <a href="/api/end-to-end/pfs#key-visualization">Key visualizations</a> on the clients are generated using the first 128 bits of SHA1(initial key) followed by the first 160 bits of SHA256(key used when secret chat was updated to layer 46).</p>
<p>Client <strong>B</strong> executes <a href="/method/messages.acceptEncryption">messages.acceptEncryption</a> after passing it <code>g_b := pow(g, b) mod dh_prime</code> and <strong>key_fingerprint</strong>.</p>
<p>For all of Client <strong>Bs</strong> authorized devices, except the current one, <a href="/constructor/updateEncryption">updateEncryption</a> updates are sent with the constructor <a href="/constructor/encryptedChatDiscarded">encryptedChatDiscarded</a>. Thereafter, the only device that will be able to access the secret chat is Device <strong>B</strong>, which made the call to <a href="/method/messages.acceptEncryption">messages.acceptEncryption</a>.</p>
<p>For all of Client <strong>B's</strong> authorized devices, except the current one, <a href="/constructor/updateEncryption">updateEncryption</a> updates are sent with the constructor <a href="/constructor/encryptedChatDiscarded">encryptedChatDiscarded</a>. Thereafter, the only device that will be able to access the secret chat is Device <strong>B</strong>, which made the call to <a href="/method/messages.acceptEncryption">messages.acceptEncryption</a>.</p>
<p>User <strong>A</strong> will be sent an <a href="/constructor/updateEncryption">updateEncryption</a> update with the constructor <a href="/constructor/encryptedChat">encryptedChat</a>, for the authorization key that initiated the chat.</p>
<p>With <strong>g_b</strong> from the update, Client <strong>A</strong> can also receive the shared key <code>key = (pow(g_b, a) mod dh_prime)</code>. If key length &lt; 256 bytes, add several leading zero bytes as padding — so that the key is exactly 256 bytes long. If the fingerprint for the received key is identical to the one that was passed to <a href="/constructor/encryptedChat">encryptedChat</a>, incoming messages can be sent and processed. Otherwise, <a href="/method/messages.discardEncryption">messages.discardEncryption</a> must be executed and the user notified.</p>
<h4><a class="anchor" href="#perfect-forward-secrecy" id="perfect-forward-secrecy" name="perfect-forward-secrecy"><i class="anchor-icon"></i></a>Perfect Forward Secrecy</h4>
@ -117,7 +117,7 @@ If the message layer is greater than the one supported by the client, the user m
</blockquote>
<p>Please note that your client must support sequence numbers in Secret Chats to be compatible with official Telegram clients. </p>
<h4><a class="anchor" href="#sending-encrypted-files" id="sending-encrypted-files" name="sending-encrypted-files"><i class="anchor-icon"></i></a>Sending Encrypted Files</h4>
<p>All files sent to secret chats are encrypted with one-time keys that are in no way related to the chats shared key. Before an encrypted file is sent, it is assumed that the encrypted files address will be attached to the outside of an encrypted message using the <strong>file</strong> parameter of the <a href="/method/messages.sendEncryptedFile">messages.sendEncryptedFile</a> method and that the key for direct decryption will be sent in the body of the message (the <strong>key</strong> parameter in the constructors <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaPhoto</a>, <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaVideo</a> and <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaFile</a>.</p>
<p>All files sent to secret chats are encrypted with one-time keys that are in no way related to the chat's shared key. Before an encrypted file is sent, it is assumed that the encrypted file's address will be attached to the outside of an encrypted message using the <strong>file</strong> parameter of the <a href="/method/messages.sendEncryptedFile">messages.sendEncryptedFile</a> method and that the key for direct decryption will be sent in the body of the message (the <strong>key</strong> parameter in the constructors <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaPhoto</a>, <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaVideo</a> and <a href="/constructor/decryptedMessageMediaPhoto">decryptedMessageMediaFile</a>.</p>
<p>Prior to a file being sent to a secret chat, 2 random 256-bit numbers are computed which will serve as the AES key and initialization vector used to encrypt the file. AES-256 encryption with infinite garble extension (IGE) is used in like manner.</p>
<p>The key fingerprint is computed as follows:</p>
<ul>
@ -128,7 +128,7 @@ If the message layer is greater than the one supported by the client, the user m
A subsequent call to <a href="/method/messages.sendEncryptedFile">messages.sendEncryptedFile</a> will assign an identifier to the stored file and send the address together with the message. The recipient will receive an update with <a href="/constructor/encryptedMessage">encryptedMessage</a>, and the <strong>file</strong> parameter will contain file information.</p>
<p>Incoming and outgoing encrypted files can be forwarded to other secret chats using the constructor <a href="/constructor/inputEncryptedFile">inputEncryptedFile</a> to avoid saving the same content on the server twice.</p>
<h4><a class="anchor" href="#working-with-an-update-box" id="working-with-an-update-box" name="working-with-an-update-box"><i class="anchor-icon"></i></a>Working with an Update Box</h4>
<p>Secret chats are associated with specific devices (or rather with <a href="/mtproto/description_v1#authorization-key">authorization keys</a>), not users. A conventional message box, which uses <strong>pts</strong> to describe the clients status, is not suitable, because it is designed for long-term message storage and message access from different devices.</p>
<p>Secret chats are associated with specific devices (or rather with <a href="/mtproto/description_v1#authorization-key">authorization keys</a>), not users. A conventional message box, which uses <strong>pts</strong> to describe the client's status, is not suitable, because it is designed for long-term message storage and message access from different devices.</p>
<p>An additional temporary message queue is introduced as a solution to this problem. When an update regarding a message from a secret chat is sent, a new value of <strong>qts</strong> is sent, which helps reconstruct the difference if there has been a long break in the connection or in case of loss of an update.</p>
<p>As the number of events increases, the value of <strong>qts</strong> increases monotonically (not always by 1). The initial value may not (and will not) be equal to 0.</p>
<p>The fact that events from the temporary queue have been received and stored by the client is acknowledged explicitly by a call to the <a href="/method/messages.receivedQueue">messages.receivedQueue</a> method or implicitly by a call to <a href="/method/updates.getDifference">updates.getDifference</a> (the value of <strong>qts</strong> passed, not the final state). All messages acknowledged as delivered by the client, as well as any messages older than 7 days, may (and will) be deleted from the server.</p>
@ -145,7 +145,7 @@ A subsequent call to <a href="/method/messages.sendEncryptedFile">messages.sendE
<ol>
<li>As soon as a new secret chat has been created, immediately after the secret key has been successfully exchanged.</li>
<li>Immediately after the remote layer value is updated to layer 17 or higher. An exception to this case is when the secret chat in question has been created less than 15 seconds ago. In this case the notification was already sent (see 1).</li>
<li>Immediately after the local client has been updated to support a new secret chat layer. In this case notifications must be sent to <strong>all</strong> currently existing secret chats. Note that this is only necessary when updating to new layers that contain changes in the secret chats implementation (e.g. you dont need to do this when your client is updated from Layer 17 to Layer 18).</li>
<li>Immediately after the local client has been updated to support a new secret chat layer. In this case notifications must be sent to <strong>all</strong> currently existing secret chats. Note that this is only necessary when updating to new layers that contain changes in the secret chats implementation (e.g. you don't need to do this when your client is updated from Layer 17 to Layer 18).</li>
</ol>
<blockquote>
<p>Note that all pending obsolete layer messages must be sent prior to the layer update notification (more on this in <a href="/api/end-to-end/seq_no#preventing-gaps">Handling Sequence numbers</a>).</p>

View file

@ -46,7 +46,7 @@
<h4><a class="anchor" href="#error-type" id="error-type" name="error-type"><i class="anchor-icon"></i></a>Error Type</h4>
<p>A string literal in the form of <code>/[A-Z_0-9]+/</code>, which summarizes the problem. For example, <code>AUTH_KEY_UNREGISTERED</code>. This is an optional parameter.</p>
<h4><a class="anchor" href="#error-database" id="error-database" name="error-database"><i class="anchor-icon"></i></a>Error Database</h4>
<p>A full machine-readable JSON list of RPC errors that can be returned by all methods in the API can be found <a href="/file/464001165/11680/luDh6JDlj7I.76063.json/32eb0548a1749ae301">here »</a>, what follows is a description of its fields: </p>
<p>A full machine-readable JSON list of RPC errors that can be returned by all methods in the API can be found <a href="/file/464001158/122a8/nbJqCk-fuss.77184.json/42ea6a04359d8b7c39">here »</a>, what follows is a description of its fields: </p>
<ul>
<li><code>errors</code> - All error messages and codes for each method (object).<ul>
<li>Keys: Error codes as strings (numeric strings)</li>
@ -103,7 +103,7 @@
<li>NETWORK_MIGRATE_X: the source IP address is associated with a different data center (for registration)</li>
<li>USER_MIGRATE_X: the user whose identity is being used to execute queries is associated with a different data center (for registration)</li>
</ul>
<p>In all these cases, the error descriptions string literal contains the number of the data center (instead of the X) to which the repeated query must be sent.
<p>In all these cases, the error description's string literal contains the number of the data center (instead of the X) to which the repeated query must be sent.
<a href="/api/datacenter">More information about redirects between data centers »</a></p>
<h3><a class="anchor" href="#400-bad-request" id="400-bad-request" name="400-bad-request"><i class="anchor-icon"></i></a>400 BAD_REQUEST</h3>
<p>The query contains errors. In the event that a request was created using a form and contains user generated data, the user should be notified that the data must be corrected before the query is repeated.</p>

View file

@ -41,7 +41,7 @@
<div id="dev_page_content"><!-- scroll_nav -->
<p>When working with the API, it is sometimes necessary to send a relatively large file to the server. For example, when sending a message with a photo/video attachment or when setting the current users profile picture.</p>
<p>When working with the API, it is sometimes necessary to send a relatively large file to the server. For example, when sending a message with a photo/video attachment or when setting the current user's profile picture.</p>
<h3><a class="anchor" href="#uploading-files" id="uploading-files" name="uploading-files"><i class="anchor-icon"></i></a>Uploading files</h3>
<p>There are a number of API methods to save files. The schema of the types and methods used is presented below:</p>
<pre><code><a href='/constructor/inputFile'>inputFile</a>#f52ff27f id:<a href='/type/long'>long</a> parts:<a href='/type/int'>int</a> name:<a href='/type/string'>string</a> md5_checksum:<a href='/type/string'>string</a> = <a href='/type/InputFile'>InputFile</a>;
@ -71,7 +71,7 @@
<a href='/method/upload.saveFilePart'>upload.saveFilePart</a>#b304a621 file_id:<a href='/type/long'>long</a> file_part:<a href='/type/int'>int</a> bytes:<a href='/type/bytes'>bytes</a> = <a href='/type/Bool'>Bool</a>;
<a href='/method/upload.saveBigFilePart'>upload.saveBigFilePart</a>#de7b673d file_id:<a href='/type/long'>long</a> file_part:<a href='/type/int'>int</a> file_total_parts:<a href='/type/int'>int</a> bytes:<a href='/type/bytes'>bytes</a> = <a href='/type/Bool'>Bool</a>;</code></pre>
<p>Before transmitting the contents of the file itself, the file has to be assigned a unique 64-bit client identifier: <strong>file_id</strong>.</p>
<p>The files binary content is then split into parts. All parts must have the same size ( <strong>part_size</strong> ) and the following conditions must be met:</p>
<p>The file's binary content is then split into parts. All parts must have the same size ( <strong>part_size</strong> ) and the following conditions must be met:</p>
<ul>
<li><code>part_size % 1024 = 0</code> (divisible by 1KB)</li>
<li><code>524288 % part_size = 0</code> (512KB must be evenly divisible by <strong>part_size</strong>)</li>
@ -100,7 +100,7 @@ After the entire file is successfully saved, the final method may be called and
<ul>
<li>FILE_PARTS_INVALID: The number of file parts is invalid The value is not between 1 and 3,000.</li>
<li>FILE_PART_Х_MISSING: Part X (where X is a number) of the file is missing from storage. Try repeating the method call to resave the part.</li>
<li>MD5_CHECKSUM_INVALID: The files checksum did not match the <strong>md5_checksum</strong> parameter</li>
<li>MD5_CHECKSUM_INVALID: The file's checksum did not match the <strong>md5_checksum</strong> parameter</li>
</ul>
<h4><a class="anchor" href="#albums-grouped-media" id="albums-grouped-media" name="albums-grouped-media"><i class="anchor-icon"></i></a>Albums, grouped media</h4>
<pre><code><a href='/constructor/inputMediaUploadedPhoto'>inputMediaUploadedPhoto</a>#1e287d04 flags:<a href='/type/%23'>#</a> file:<a href='/type/InputFile'>InputFile</a> stickers:flags.0?<a href='/type/Vector%20t'>Vector</a>&lt;<a href='/type/InputDocument'>InputDocument</a>&gt; ttl_seconds:flags.1?<a href='/type/int'>int</a> = <a href='/type/InputMedia'>InputMedia</a>;

View file

@ -42,10 +42,10 @@
<div id="dev_page_content"><h3><a class="anchor" href="#simplified-acknowledgment-of-message-delivery" id="simplified-acknowledgment-of-message-delivery" name="simplified-acknowledgment-of-message-delivery"><i class="anchor-icon"></i></a>Simplified Acknowledgment of Message Delivery</h3>
<p>An outgoing message may be considered sent once the server has assigned it an identifier. Normally, a client would learn of this from the result of the <a href="/method/messages.sendMessage">messages.sendMessage</a> method.
The MTProto server provides a mechanism for <a href="/mtproto/mtproto-transports#quick-ack">“quick acknowledgments"</a>. Upon receiving such an acknowledgment, the client may be certain that the call to the send message method has at least been fully received by the server and placed in a processing queue, and can inform the user that the delivery was successful.
It is possible that the servers actual response will never be received by the client (an interrupted connection; or the app restarts at exactly the wrong time). To correctly handle these situations, you can use a special type of notification generated by the server when <a href="/method/updates.getDifference">updates.getDifference</a> is called: <a href="/constructor/updateMessageID">updateMessageID</a>. When processing this notification, the client can use the <strong>random_id</strong> identifier to associate the previously transmitted message with the one delivered to the server.
It is possible that the server's actual response will never be received by the client (an interrupted connection; or the app restarts at exactly the wrong time). To correctly handle these situations, you can use a special type of notification generated by the server when <a href="/method/updates.getDifference">updates.getDifference</a> is called: <a href="/constructor/updateMessageID">updateMessageID</a>. When processing this notification, the client can use the <strong>random_id</strong> identifier to associate the previously transmitted message with the one delivered to the server.
If such a notification is not issued when <a href="/method/updates.getDifference">updates.getDifference</a> is called for one of the previously sent messages, the message must be marked as undelivered.</p>
<h3><a class="anchor" href="#server-salt" id="server-salt" name="server-salt"><i class="anchor-icon"></i></a>Server Salt</h3>
<p>Server salt is a 64-bit number added to every outgoing and incoming message. At present, a single salts lifespan is 1 hour, following which it is considered invalid and the server will return an error for all the messages that contain it. The error message will contain the correct salt, which may be immediately used for sending. Given this approach, there will always be a period of waiting before the client receives a new salt if it connects to the server less frequently than once an hour.
<p>Server salt is a 64-bit number added to every outgoing and incoming message. At present, a single salt's lifespan is 1 hour, following which it is considered invalid and the server will return an error for all the messages that contain it. The error message will contain the correct salt, which may be immediately used for sending. Given this approach, there will always be a period of waiting before the client receives a new salt if it connects to the server less frequently than once an hour.
For improved performance, there is a special <a href="/mtproto/service_messages#request-for-several-future-salts">get_future_salts</a> method, which fetches in advance a list of the salts that will be valid during the course of a specified period of time following the call (1 day, for example). A start time and an end time are specified for each salt. The salts overlap one another by half an hour. We recommend always using the record with the longest remaining lifespan.</p>
<h3><a class="anchor" href="#downloading-files-and-uploading-data-to-the-server" id="downloading-files-and-uploading-data-to-the-server" name="downloading-files-and-uploading-data-to-the-server"><i class="anchor-icon"></i></a>Downloading Files and Uploading Data to the Server</h3>
<p>We recommend that separate connections and sessions be created for these tasks. Remember that the extra sessions must be deleted when no longer needed.

View file

@ -41,7 +41,7 @@
<div id="dev_page_content"><!-- scroll_nav -->
<p><strong>Telegram Passport</strong> is a unified authorization method for services that require personal identification. Users can upload their documents once, then instantly share their data with services that require real-world ID (finance, ICOs, etc.). Telegram doesnt have access to the users personal information thanks to end-to-end encryption.</p>
<p><strong>Telegram Passport</strong> is a unified authorization method for services that require personal identification. Users can upload their documents once, then instantly share their data with services that require real-world ID (finance, ICOs, etc.). Telegram doesnt have access to the users' personal information thanks to end-to-end encryption.</p>
<p>This page describes the request flow that client apps must used to send the requested data to the service.</p>
<h3><a class="anchor" href="#overview" id="overview" name="overview"><i class="anchor-icon"></i></a>Overview</h3>
<p>From the perspective of a service that requires real-world ID, the process looks like this:</p>

View file

@ -112,7 +112,7 @@ The client is expected to check whether <strong>p</strong> is a safe 2048-bit pr
<li><code>k_v := (k * v) mod p</code></li>
</ul>
<p>Finally, the <a href="https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol#Protocol">key exchange process</a> starts on both parties.</p>
<p>The client computes a 2048-bit number <strong>a</strong> (using sufficient entropy or the servers <strong>random</strong>; see above) and generates:</p>
<p>The client computes a 2048-bit number <strong>a</strong> (using sufficient entropy or the server's <strong>random</strong>; see above) and generates:</p>
<ul>
<li><code>g_a := pow(g, a) mod p</code>.</li>
</ul>

View file

@ -81,7 +81,7 @@
<tr>
<td><strong>bytes</strong></td>
<td style="text-align: center;"><a href="/type/bytes">bytes</a></td>
<td>TL-serialising of <a href="/type/DecryptedMessage">DecryptedMessage</a> type, encrypted with the key creatied at stage of chat initialization</td>
<td>TL-serialization of <a href="/type/DecryptedMessage">DecryptedMessage</a> type, encrypted with the key created at chat initialization</td>
</tr>
<tr>
<td><strong>file</strong></td>

View file

@ -81,7 +81,7 @@
<tr>
<td><strong>bytes</strong></td>
<td style="text-align: center;"><a href="/type/bytes">bytes</a></td>
<td>TL-serialising of <a href="/type/DecryptedMessage">DecryptedMessage</a> type, encrypted with the key creatied at stage of chat initialization</td>
<td>TL-serialization of the <a href="/type/DecryptedMessage">DecryptedMessage</a> type, encrypted with the key created at chat initialization</td>
</tr>
</tbody>
</table>

View file

@ -89,7 +89,7 @@
<p><a href="/type/InputChatPhoto">InputChatPhoto</a></p>
<h3><a class="anchor" href="#related-pages" id="related-pages" name="related-pages"><i class="anchor-icon"></i></a>Related pages</h3>
<h4><a class="anchor" href="#uploadsavefilepart" id="uploadsavefilepart" name="uploadsavefilepart"><i class="anchor-icon"></i></a><a href="/method/upload.saveFilePart">upload.saveFilePart</a></h4>
<p>Saves a part of file for futher sending to one of the methods.</p></div>
<p>Saves a part of file for further sending to one of the methods.</p></div>
</div>

View file

@ -89,7 +89,7 @@
<p><a href="/type/InputFile">InputFile</a></p>
<h3><a class="anchor" href="#related-pages" id="related-pages" name="related-pages"><i class="anchor-icon"></i></a>Related pages</h3>
<h4><a class="anchor" href="#uploadsavefilepart" id="uploadsavefilepart" name="uploadsavefilepart"><i class="anchor-icon"></i></a><a href="/method/upload.saveFilePart">upload.saveFilePart</a></h4>
<p>Saves a part of file for futher sending to one of the methods.</p></div>
<p>Saves a part of file for further sending to one of the methods.</p></div>
</div>

View file

@ -66,7 +66,7 @@
<tr>
<td><strong>chat_id</strong></td>
<td style="text-align: center;"><a href="/type/long">long</a></td>
<td>Chat idientifier</td>
<td>Chat identifier</td>
</tr>
</tbody>
</table>

View file

@ -66,7 +66,7 @@
<tr>
<td><strong>title</strong></td>
<td style="text-align: center;"><a href="/type/string">string</a></td>
<td>The old chat tite</td>
<td>The old chat title</td>
</tr>
<tr>
<td><strong>chat_id</strong></td>

View file

@ -66,7 +66,7 @@
<tr>
<td><strong>photo</strong></td>
<td style="text-align: center;"><a href="/type/Photo">Photo</a></td>
<td>New group pofile photo</td>
<td>New group profile photo</td>
</tr>
</tbody>
</table>

View file

@ -76,7 +76,7 @@
<tr>
<td><strong>version</strong></td>
<td style="text-align: center;"><a href="/type/int">int</a></td>
<td>Vestion of set of parameters</td>
<td>Version of set of parameters</td>
</tr>
<tr>
<td><strong>random</strong></td>

View file

@ -76,7 +76,7 @@
<tr>
<td><strong>selective</strong></td>
<td style="text-align: center;"><a href="/mtproto/TL-combinators#conditional-fields">flags</a>.2?<a href="/constructor/true">true</a></td>
<td>Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. <br>Example: A user requests to change the bots language, bot replies to the request with a keyboard to select the new language. Other users in the group dont see the keyboard.</td>
<td>Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. <br>Example: A user requests to change the bots language, bot replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard.</td>
</tr>
<tr>
<td><strong>placeholder</strong></td>

View file

@ -81,7 +81,7 @@
<tr>
<td><strong>selective</strong></td>
<td style="text-align: center;"><a href="/mtproto/TL-combinators#conditional-fields">flags</a>.2?<a href="/constructor/true">true</a></td>
<td>Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.<br><br>Example: A user requests to change the bots language, bot replies to the request with a keyboard to select the new language. Other users in the group dont see the keyboard.</td>
<td>Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.<br><br>Example: A user requests to change the bots language, bot replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard.</td>
</tr>
<tr>
<td><strong>rows</strong></td>

View file

@ -4,10 +4,10 @@
<meta charset="utf-8">
<title>invokeAfterMsg</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="description" content="Invokes a query after successfull completion of one of the previous queries.">
<meta property="description" content="Invokes a query after successful completion of one of the previous queries.">
<meta property="og:title" content="invokeAfterMsg">
<meta property="og:image" content="">
<meta property="og:description" content="Invokes a query after successfull completion of one of the previous queries.">
<meta property="og:description" content="Invokes a query after successful completion of one of the previous queries.">
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
@ -39,7 +39,7 @@
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/api" >API</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/methods" >All Methods</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/method/invokeAfterMsg" >invokeAfterMsg</a></li></ul></div>
<h1 id="dev_page_title">invokeAfterMsg</h1>
<div id="dev_page_content"><p>Invokes a query after successfull completion of one of the previous queries.</p>
<div id="dev_page_content"><p>Invokes a query after successful completion of one of the previous queries.</p>
<p><div class="clearfix">
<ul class="dev_layer_select slightly-pull-right nav nav-pills">
<li class="dropdown">

View file

@ -4,10 +4,10 @@
<meta charset="utf-8">
<title>invokeAfterMsgs</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="description" content="Invokes a query after a successfull completion of previous queries">
<meta property="description" content="Invokes a query after a successful completion of previous queries">
<meta property="og:title" content="invokeAfterMsgs">
<meta property="og:image" content="">
<meta property="og:description" content="Invokes a query after a successfull completion of previous queries">
<meta property="og:description" content="Invokes a query after a successful completion of previous queries">
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
@ -39,7 +39,7 @@
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/api" >API</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/methods" >All Methods</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/method/invokeAfterMsgs" >invokeAfterMsgs</a></li></ul></div>
<h1 id="dev_page_title">invokeAfterMsgs</h1>
<div id="dev_page_content"><p>Invokes a query after a successfull completion of previous queries</p>
<div id="dev_page_content"><p>Invokes a query after a successful completion of previous queries</p>
<p><div class="clearfix">
<ul class="dev_layer_select slightly-pull-right nav nav-pills">
<li class="dropdown">

View file

@ -143,7 +143,7 @@
</table>
<h3><a class="anchor" href="#related-pages" id="related-pages" name="related-pages"><i class="anchor-icon"></i></a>Related pages</h3>
<h4><a class="anchor" href="#uploadsavefilepart" id="uploadsavefilepart" name="uploadsavefilepart"><i class="anchor-icon"></i></a><a href="/method/upload.saveFilePart">upload.saveFilePart</a></h4>
<p>Saves a part of file for futher sending to one of the methods.</p>
<p>Saves a part of file for further sending to one of the methods.</p>
<h4><a class="anchor" href="#uploading-and-downloading-files" id="uploading-and-downloading-files" name="uploading-and-downloading-files"><i class="anchor-icon"></i></a><a href="/api/files">Uploading and Downloading Files</a></h4>
<p>How to transfer large data batches correctly.</p></div>

View file

@ -4,10 +4,10 @@
<meta charset="utf-8">
<title>upload.saveFilePart</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="description" content="Saves a part of file for futher sending to one of the methods.">
<meta property="description" content="Saves a part of file for further sending to one of the methods.">
<meta property="og:title" content="upload.saveFilePart">
<meta property="og:image" content="">
<meta property="og:description" content="Saves a part of file for futher sending to one of the methods.">
<meta property="og:description" content="Saves a part of file for further sending to one of the methods.">
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
@ -39,7 +39,7 @@
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/api" >API</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/methods" >All Methods</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/method/upload.saveFilePart" >upload.saveFilePart</a></li></ul></div>
<h1 id="dev_page_title">upload.saveFilePart</h1>
<div id="dev_page_content"><p>Saves a part of file for futher sending to one of the methods.</p>
<div id="dev_page_content"><p>Saves a part of file for further sending to one of the methods.</p>
<p><div class="clearfix">
<ul class="dev_layer_select slightly-pull-right nav nav-pills">
<li class="dropdown">

View file

@ -128,15 +128,15 @@ MTProto v1.0 (<a href="/mtproto_v1">described here</a> for reference) is depreca
<p>For a technical specification, see <a href="/mtproto/description">Mobile Protocol: Detailed Description</a></p>
</blockquote>
<p>The first thing a client application must do is <a href="/mtproto/auth_key">create an authorization key</a> which is normally generated when it is first run and almost never changes.</p>
<p>The protocols principal drawback is that an intruder passively intercepting messages and then somehow appropriating the authorization key (for example, by stealing a device) will be able to decrypt all the intercepted messages <em>post factum</em>. This probably is not too much of a problem (by stealing a device, one could also gain access to all the information cached on the device without decrypting anything); however, the following steps could be taken to overcome this weakness:</p>
<p>The protocol's principal drawback is that an intruder passively intercepting messages and then somehow appropriating the authorization key (for example, by stealing a device) will be able to decrypt all the intercepted messages <em>post factum</em>. This probably is not too much of a problem (by stealing a device, one could also gain access to all the information cached on the device without decrypting anything); however, the following steps could be taken to overcome this weakness:</p>
<ul>
<li><em>Session keys</em> generated using the Diffie-Hellman protocol and used in conjunction with the authorization and the message keys to select AES parameters. To create these, the first thing a client must do after creating a new session is send a special RPC query to the server (“generate session key”) to which the server will respond, whereupon all subsequent messages within the session are encrypted using the session key as well.</li>
<li>Protecting the key stored on the client device with a (text) password; this password is never stored in memory and is entered by a user when starting the application or more frequently (depending on application settings).</li>
<li>Data stored (cached) on the user device can also be protected by encryption using an authorization key which, in turn, is to be password-protected. Then, a password will be required to gain access even to that data.</li>
</ul>
<h4><a class="anchor" href="#time-synchronization" id="time-synchronization" name="time-synchronization"><i class="anchor-icon"></i></a>Time Synchronization</h4>
<p>If client time diverges widely from server time, a server may start ignoring client messages, or vice versa, because of an invalid message identifier (which is closely related to creation time). Under these circumstances, the server will send the client a special message containing the correct time and a certain 128-bit salt (either explicitly provided by the client in a special RPC synchronization request or equal to the key of the latest message received from the client during the current session). This message could be the first one in a container that includes other messages (if the time discrepancy is significant but does not as yet result in the clients messages being ignored).</p>
<p>Having received such a message or a container holding it, the client first performs a time synchronization (in effect, simply storing the difference between the servers time and its own to be able to compute the “correct” time in the future) and then verifies that the message identifiers for correctness.</p>
<p>If client time diverges widely from server time, a server may start ignoring client messages, or vice versa, because of an invalid message identifier (which is closely related to creation time). Under these circumstances, the server will send the client a special message containing the correct time and a certain 128-bit salt (either explicitly provided by the client in a special RPC synchronization request or equal to the key of the latest message received from the client during the current session). This message could be the first one in a container that includes other messages (if the time discrepancy is significant but does not as yet result in the client's messages being ignored).</p>
<p>Having received such a message or a container holding it, the client first performs a time synchronization (in effect, simply storing the difference between the server's time and its own to be able to compute the “correct” time in the future) and then verifies that the message identifiers for correctness.</p>
<p>Where a correction has been neglected, the client will have to generate a new session to assure the monotonicity of message identifiers.</p>
<h3><a class="anchor" href="#mtproto-transport" id="mtproto-transport" name="mtproto-transport"><i class="anchor-icon"></i></a>MTProto transport</h3>
<p>Before being sent using the selected transport protocol, the payload has to be wrapped in a secondary protocol header, defined by the appropriate MTProto transport protocol. </p>

View file

@ -4,10 +4,10 @@
<meta charset="utf-8">
<title>Binary serialization and abstract TL types</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="description" content="TL Language defines abstract data types in the spirit of a general theory of types (more accurately, Martin-Löfs theories…">
<meta property="description" content="TL Language defines abstract data types in the spirit of a general theory of types (more accurately, Martin-Löf&#39;s theories…">
<meta property="og:title" content="Binary serialization and abstract TL types">
<meta property="og:image" content="">
<meta property="og:description" content="TL Language defines abstract data types in the spirit of a general theory of types (more accurately, Martin-Löfs theories…">
<meta property="og:description" content="TL Language defines abstract data types in the spirit of a general theory of types (more accurately, Martin-Löf&#39;s theories…">
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
@ -39,10 +39,10 @@
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/mtproto" >Mobile Protocol</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/mtproto/TL-abstract-types" >Binary serialization and abstract TL types</a></li></ul></div>
<h1 id="dev_page_title">Binary serialization and abstract TL types</h1>
<div id="dev_page_content"><p><a href="TL">TL Language</a> defines <em>abstract</em> data types in the spirit of a general theory of types (more accurately, Martin-Löfs theories of dependent intuitionistic types) without specifying the values of these types should be represented in memory, when saved to disk, or transmitted over a network. In contrast, the article on <a href="serialize">binary serialization</a> discusses the problem of effective serialization of values of abstract types. To this end, the concept of a <em>concrete</em> or <em>serialized</em> type has been defined as the sets of serializations of all possible values of the corresponding abstract type. In this case, the serializations take values in the set A<em> of words in the alphabet </em>A*, which consists of 2^32 characters -- 32-bit integers.</p>
<div id="dev_page_content"><p><a href="TL">TL Language</a> defines <em>abstract</em> data types in the spirit of a general theory of types (more accurately, Martin-Löf's theories of dependent intuitionistic types) without specifying the values of these types should be represented in memory, when saved to disk, or transmitted over a network. In contrast, the article on <a href="serialize">binary serialization</a> discusses the problem of effective serialization of values of abstract types. To this end, the concept of a <em>concrete</em> or <em>serialized</em> type has been defined as the sets of serializations of all possible values of the corresponding abstract type. In this case, the serializations take values in the set A<em> of words in the alphabet </em>A*, which consists of 2^32 characters -- 32-bit integers.</p>
<p>In order to use a TL schema (e.g. “program”) in the TL language to describe the serialization of values of abstract types, we should explain how the concrete type <em>[T]</em> (subset <em>[T]</em> of A^<em>) is associated with the abstract type </em>T<em> (defined in TL), and how the values of the abstract type </em>T<em> correspond to the values of the concrete type </em>[T]<em> (i.e. the elements of </em>[T]*).</p>
<p>Serialization is the process of constructing an element of <em>[T]</em> based on a value of the abstract type <em>T</em>. The reverse process is deserialization. </p>
<p>Values of the abstract type <em>T</em> may be represented in a different way. Typically, some sort of trees or graphs are used in memory or, if desired, a set of nodes may be used, each of which contains a certain tag (“node type”) and several pointers to other nodes and/or values of built-in primitive types such as <code>int</code>. However, for general discussions it is useful to write the values of abstract type <em>T</em> as a string, more specifically, an S-expression. Recall that an S-expression is either an atom (the value of a primitive type, for example, an integer or a string constant in quotation marks; or an identifier that corresponds to a built-in or defined function) or a space-delimited list of S-expressions ending in parentheses. In our case, we use S-expressions, the first element of which is a combinator identifier, while the remaining elements (the number of which depends on the combinator's arity) are S-expressions representing elements of the chosen combinator's fields (or parameters). Moreover, the type of the arguments S-expressions and the type of the S-expressions of the result (e.g. the associated expression) must match.</p>
<p>Values of the abstract type <em>T</em> may be represented in a different way. Typically, some sort of trees or graphs are used in memory or, if desired, a set of nodes may be used, each of which contains a certain tag (“node type”) and several pointers to other nodes and/or values of built-in primitive types such as <code>int</code>. However, for general discussions it is useful to write the values of abstract type <em>T</em> as a string, more specifically, an S-expression. Recall that an S-expression is either an atom (the value of a primitive type, for example, an integer or a string constant in quotation marks; or an identifier that corresponds to a built-in or defined function) or a space-delimited list of S-expressions ending in parentheses. In our case, we use S-expressions, the first element of which is a combinator identifier, while the remaining elements (the number of which depends on the combinator's arity) are S-expressions representing elements of the chosen combinator's fields (or parameters). Moreover, the type of the arguments' S-expressions and the type of the S-expressions of the result (e.g. the associated expression) must match.</p>
<p>For example, for the schema</p>
<pre><code>pair x:int y:int = Pair;
pnil = PairList;
@ -68,7 +68,7 @@ pcons hd:Pair tl:PairList = PairList;</code></pre>
</ul>
<p>If we use <em>[T]</em> to denote the concrete type corresponding to the abstract <em>T</em>, and <em>[E]</em> to denote an element of <em>[T]</em> corresponding to the value <em>E</em> of type <em>T</em>, then the last rule may be written as:</p>
<ul>
<li><em>[T]</em> is the combination, for each constructor of type <em>C</em> <em>T1-&gt;T2-&gt;...-&gt;Tr-&gt;T</em> (i.e. that returns a value of type <em>T</em>), of direct products <em>{C} x [T1] x [T2] x ... x [Tr]</em>, where <em>{C}</em> is a single-element set consisting of the combinator number <em>C</em>. Because <em>{C}&lt;&gt;{C'}</em> when <em>C&lt;&gt;C</em>, this defines a mutually single-valued mapping of the values of the abstract type <em>T</em> (written using S-expressions) to the set <em>[T]</em>.</li>
<li><em>[T]</em> is the combination, for each constructor of type <em>C</em> <em>T1-&gt;T2-&gt;...-&gt;Tr-&gt;T</em> (i.e. that returns a value of type <em>T</em>), of direct products <em>{C} x [T1] x [T2] x ... x [Tr]</em>, where <em>{C}</em> is a single-element set consisting of the combinator number <em>C</em>. Because <em>{C}&lt;&gt;{C'}</em> when <em>C&lt;&gt;C'</em>, this defines a mutually single-valued mapping of the values of the abstract type <em>T</em> (written using S-expressions) to the set <em>[T]</em>.</li>
</ul>
<p>Values of the built-in clothed types <code>Int</code> and <code>String</code> and serialized as if they were defined using <code>int x:int = Int;</code> and <code>string s:string = String;</code>, i.e. the serialization of integer constant or a string is preceded by number of the <code>int</code> or <code>string</code> combinator (constructor). In S-expressions, this may be written as <code>(int 5)</code> or <code>(string "Test")</code>.</p>
<p>However, what has been described above does not account for certain subtleties, such as the existence of naked types, or the difference between functions (active combinators whose application may be reduced, e.g. calculated) and constructors (passive combinators for which there are not and cannot be reduction rules). Furthermore, we have not explained how to handle polymorphic types and <a href="TL-optargs">optional combinator parameters</a>. We will attempt to explain this now.</p>
@ -87,7 +87,7 @@ pcons hd:Pair tl:PairList = PairList;</code></pre>
</ul>
<p>In practice, we most frequently need constant values (for storage and passing any data structures, in particular, responses to RPC queries) and surface expressions (for example, as RPC queries: then the functional combinator of the outer level is the name of the RPC function that we want to call, while its parameters are the arguments, which are constant values, for invoking the function). In some cases, arbitrary functional expressions are helpful (for example, it we want to remotely transmit the result of one RPC query to a different RPC query).</p>
<p>We will use <em>c(T)</em> to denote a subtype of the abstract type <em>T</em>, whose values are constant expressions of type <em>T</em>. Clearly, <em>c(T)</em> possesses approximately the same constructors as <em>T</em> itself (with the types of all arguments <em>Ti</em> replaced by <em>c(Ti)</em>, but it does not have functional combinators.</p>
<p>Analogously, we will use <em>f(T)</em> to denote a subtype of <em>T</em>, whose values are surface expressions of type <em>T</em>. Clearly, the combinators of <em>f(T)</em> are essentially functional combinators of type <em>T</em>, but <em>c()</em> applies to the types of these combinators arguments: The combinator <em>A : T1-&gt;...-&gt;Tr-&gt;T</em> turns into <em>A' : c(T1)-&gt;...-&gt;c(Tr)-&gt;f(T)</em>. (See the clarification of this rule below.)</p>
<p>Analogously, we will use <em>f(T)</em> to denote a subtype of <em>T</em>, whose values are surface expressions of type <em>T</em>. Clearly, the combinators of <em>f(T)</em> are essentially functional combinators of type <em>T</em>, but <em>c()</em> applies to the types of these combinators' arguments: The combinator <em>A : T1-&gt;...-&gt;Tr-&gt;T</em> turns into <em>A' : c(T1)-&gt;...-&gt;c(Tr)-&gt;f(T)</em>. (See the clarification of this rule below.)</p>
<p>Thus, we have defined two “functionals” <em>c : Type -&gt; Type</em> and <em>f : Type -&gt; Type</em>, such that <em>forall T : Type, c(T) :- T</em> and <em>forall T : Type, f(T) :- T</em> (writing <em>T :- T'</em> means that <em>T</em> is contained in <em>T'</em>, or that <em>T</em> is a subtype of <em>T'</em>).</p>
<p>We will assume that <em>c</em> and <em>f</em> are idempotent.</p>
<h3><a class="anchor" href="#naked-types" id="naked-types" name="naked-types"><i class="anchor-icon"></i></a>Naked types</h3>
@ -107,12 +107,12 @@ pcons hd:Pair tl:PairList = PairList;</code></pre>
<pre><code>pair {X Y : Type} x:X y:Y = Pair X Y; // constructor
seq_pair {X Y : Type} x:!X y:!Y = Pair X Y; // functional wrapper for sequential computation
par_pair {X Y : Type} x:!X y:!Y = Pair X Y; // functional wrapper for parallel computation</code></pre>
<p>Now the RPC query <code>(seq_pair (factorial 2) (factorial 3)) : Pair int int</code> first calculates factorial 2, then factorial 3, and returns the pair <code>(pair 2 6)</code>. In this case, the sequence of operations isnt important, because they do not have side effects. It would have been just as well to use <code>(par_pair (factorial 2) (factorial 3))</code>. However, this is not always the case.</p>
<p>Now the RPC query <code>(seq_pair (factorial 2) (factorial 3)) : Pair int int</code> first calculates factorial 2, then factorial 3, and returns the pair <code>(pair 2 6)</code>. In this case, the sequence of operations isn't important, because they do not have side effects. It would have been just as well to use <code>(par_pair (factorial 2) (factorial 3))</code>. However, this is not always the case.</p>
<p>We can also define an analogy to a “comma” operation:</p>
<pre><code>comma {X Y : Type} x:!X y:!Y = Y;</code></pre>
<p>For example, this operation could first calculate <code>x</code>, then forget the result, calculate <code>y</code>, and return <code>y</code>. </p>
<p>Note that the semantics of the <code>seq_pair</code>, <code>par_pair</code> and <code>comma</code> wrappers are indeed defined where they are implemented (like the semantics of all other functional combinators), not by their TL declaration.</p>
<p>In principle, polymorphic wrappers like <code>set_timeout</code> can also be applied, for example, to “annotate” a RPC responses constant values. For example, the server might return a response to a query together with the time it was calculated. However, a value of type <em>!X</em> must be constant, because that is what is expected as the enclosing expressions value. In other words, <code>set_timeout 239 E</code> is a constant/surface value of type <em>X</em> if and only if <em>E</em> is such itself.</p>
<p>In principle, polymorphic wrappers like <code>set_timeout</code> can also be applied, for example, to “annotate” a RPC response's constant values. For example, the server might return a response to a query together with the time it was calculated. However, a value of type <em>!X</em> must be constant, because that is what is expected as the enclosing expression's value. In other words, <code>set_timeout 239 E</code> is a constant/surface value of type <em>X</em> if and only if <em>E</em> is such itself.</p>
<h3><a class="anchor" href="#-modifier" id="-modifier" name="-modifier"><i class="anchor-icon"></i></a><code>$</code> modifier</h3>
<p>The idempotent modifier <code>$</code> permits the use of arbitrary functional values of an appropriate type in contexts where only constants or surface values are usually allowed. It recursively transforms all combinators for all of the types involved, canceling the action of <code>%</code> and affixing <code>$</code> to the parameter types and result of all combinators (<code>$</code> is also added to the front of the transformed combinators). Moreover, built-in types are also transformed (in the final stage): <code>$int = Int</code> and <code>$string = String</code>.</p>
<p>This may be useful to create an RPC query that performs a “deep computation” of the expression passed to it:</p>
@ -122,9 +122,9 @@ par_pair {X Y : Type} x:!X y:!Y = Pair X Y; // functional wrapper for parallel c
<p>(Note that the three has become clothed; the combinator $factorial has type $int -&gt; $int).</p>
<p>This is very powerful tool. It does not have to be implemented in very simple versions of TL. <code>$</code> is not encountered in currently used TL schemas.</p>
<h3><a class="anchor" href="#more-on-modifiers" id="more-on-modifiers" name="more-on-modifiers"><i class="anchor-icon"></i></a>More on modifiers</h3>
<p>In fact, at least in terms of its application to serialization, the TL language by default implies the <em>c()</em> modifier around all combinators parameter types and results, while <code>!</code> and <code>$</code> cancel it (more accurately, <code>!</code> only cancels, and in some sense <code>$</code> reverses the meaning). This is why there is no explicit <code>c()</code> modifier in TL and why it is assumed that all functions only accept constant values and return constant results, unless otherwise specified.</p>
<p>In fact, at least in terms of its application to serialization, the TL language by default implies the <em>c()</em> modifier around all combinators' parameter types and results, while <code>!</code> and <code>$</code> cancel it (more accurately, <code>!</code> only cancels, and in some sense <code>$</code> reverses the meaning). This is why there is no explicit <code>c()</code> modifier in TL and why it is assumed that all functions only accept constant values and return constant results, unless otherwise specified.</p>
<p>You may think that some functional combinators may have a type such as <code>partial_factorial n:int = $int;</code> and that the RPC query <code>(partial_factorial 3)</code> might then unexpectedly return <code>($product (int 3) ($product (int 2) ($product (int 1) (int 1)))) : $int</code> ...</p>
<p>It is probably more correct to think about the <code>!</code> modifier as follows. All types initially include only constant values (and only constructors). The <code>!</code> modifier makes a new type (its twin) out of each type. This new type has no inherent constructors. Functional combinators differ from constructors in that <code>!</code> is implicitly added in front of their results type. After this, the (local or remote) process of calculating the expression can be represented using the polymorphic function <code>eval : !X -&gt; X</code>.</p>
<p>It is probably more correct to think about the <code>!</code> modifier as follows. All types initially include only constant values (and only constructors). The <code>!</code> modifier makes a new type (it's twin) out of each type. This new type has no inherent constructors. Functional combinators differ from constructors in that <code>!</code> is implicitly added in front of their result's type. After this, the (local or remote) process of calculating the expression can be represented using the polymorphic function <code>eval : !X -&gt; X</code>.</p>
<h3><a class="anchor" href="#optional-combinator-parameters-and-their-values" id="optional-combinator-parameters-and-their-values" name="optional-combinator-parameters-and-their-values"><i class="anchor-icon"></i></a>Optional combinator parameters and their values</h3>
<p>See <a href="TL-optargs">Optional combinator parameters and their values</a>.</p></div>

View file

@ -82,13 +82,13 @@ Combinators in TL are…">
<p>Any identifier that begins with an uppercase or lowercase letter and which does not contain references to a namespace can be a field (variable) identifier. Using <em>uc-ident</em> for identifiers of variable types and <em>lc-indent</em> for other variables is good practice. </p>
</li>
<li>
<p>Next a combinator declaration contains the equals sign (<code>=</code>) and the result type (it may be composite or appearing for the first time). The result type may be polymorphic and/or dependent; any fields of the defined constructors fields of type <code>Type</code> or <code>#</code> may be returned (as subexpressions).</p>
<p>Next a combinator declaration contains the equals sign (<code>=</code>) and the result type (it may be composite or appearing for the first time). The result type may be polymorphic and/or dependent; any fields of the defined constructor's fields of type <code>Type</code> or <code>#</code> may be returned (as subexpressions).</p>
</li>
<li>
<p>A combinator declaration is terminated with a semicolon.</p>
</li>
</ul>
<p>In what follows, a constructors <em>fields</em>, <em>variables</em>, and <em>arguments</em> mean the same thing.</p>
<p>In what follows, a constructor's <em>fields</em>, <em>variables</em>, and <em>arguments</em> mean the same thing.</p>
<h3><a class="anchor" href="#optional-field-declarations" id="optional-field-declarations" name="optional-field-declarations"><i class="anchor-icon"></i></a>Optional field declarations</h3>
<ul>
<li>
@ -122,7 +122,7 @@ Combinators in TL are…">
<p>Required field declarations follow one after another, separated by spaces (by any number of whitespace symbols, to be more precise).</p>
</li>
<li>
<p>The declared fields type (<em>type-expr</em>) may use the declared combinator's previously defined variables (fields) as subexpressions (i.e. parameter values). For example:</p>
<p>The declared field's type (<em>type-expr</em>) may use the declared combinator's previously defined variables (fields) as subexpressions (i.e. parameter values). For example:</p>
<p>nil {X:Type} = List X;
cons {X:Type} hd:X tl:(list X) = List X;
typed_list (X:Type) (l : list X) = TypedList;</p>
@ -143,13 +143,13 @@ typed_list (X:Type) (l : list X) = TypedList;</p>
<p>The <em>multiplicity</em> field may be bypassed. In this case, the last preceding parameter of type <code>#</code> from the enclosing combinator is used (it must be).</p>
</li>
<li>
<p>Functionally, the repetition <em>field-id</em> <code>:</code> <em>multiplicity</em> <code>*</code> <code>[</code> <em>args</em> <code>]</code> is equivalent to the declaration of the single field <code>(</code> <em>field-id</em> <code>:</code> <code>%Tuple</code> <code>%AuxType</code> <em>multiplicity</em> <code>)</code>, where <code>aux_type</code> is an auxiliary type with a new name defined as <code>aux_type *args* = AuxType</code>. If any of the enclosing types fields are used within <em>args</em>, they are added to the auxiliary constructor <code>aux_type</code> and to its <code>AuxType</code> result type as the first (optional) parameters. </p>
<p>Functionally, the repetition <em>field-id</em> <code>:</code> <em>multiplicity</em> <code>*</code> <code>[</code> <em>args</em> <code>]</code> is equivalent to the declaration of the single field <code>(</code> <em>field-id</em> <code>:</code> <code>%Tuple</code> <code>%AuxType</code> <em>multiplicity</em> <code>)</code>, where <code>aux_type</code> is an auxiliary type with a new name defined as <code>aux_type *args* = AuxType</code>. If any of the enclosing type's fields are used within <em>args</em>, they are added to the auxiliary constructor <code>aux_type</code> and to its <code>AuxType</code> result type as the first (optional) parameters. </p>
</li>
<li>
<p>If <em>args</em> consists of one anonymous field of type <em>some-type</em>, then <em>some-type</em> can be used directly instead of <code>%AuxType</code>.</p>
</li>
<li>
<p>If during implementation the repetitions are rewritten as indicated above, it is logical to use instead of <code>aux_type</code> and <code>AuxType</code>, some identifiers that contain the name of the outer combinator being defined and the repetitions index number inside its definition.</p>
<p>If during implementation the repetitions are rewritten as indicated above, it is logical to use instead of <code>aux_type</code> and <code>AuxType</code>, some identifiers that contain the name of the outer combinator being defined and the repetition's index number inside its definition.</p>
</li>
</ul>
<p>Example:</p>

View file

@ -81,15 +81,15 @@ nil : forall (X:Type), X -&gt; List X;</code></pre>
<li>serialize the head of the list (hd) as a value of type X;</li>
<li>serialize the tail of the list as a value of the polymorphic type List X. </li>
</ol>
<p>In the first step, the natural question is which string exactly will be used to calculate the CRC32. It is proposed to take "<code>cons X:Type hd:X tl:List X = List X</code>” without the terminating semicolon and without any parentheses (closed type expressions are unambiguously reconstructed based on their constructions prefix).</p>
<p>In the first step, the natural question is which string exactly will be used to calculate the CRC32. It is proposed to take "<code>cons X:Type hd:X tl:List X = List X</code>” without the terminating semicolon and without any parentheses (closed type expressions are unambiguously reconstructed based on their construction's prefix).</p>
<p>In the last step, we recursively resolve the very same problem of serializing a value of type List X; we will consider it resolved based on the assumption of induction in the construction of the value being serialized. We will similarly consider the third step understandable (induction in the construction of the value being serialized). </p>
<p>We still need to describe how to transmit (serialize) types, e.g. values of type <strong>Type</strong>. <em>Types in TL schemas currently appear only as constructors optional parameters and are therefore never serialized explicitly. Rather, their values are inferred from the previously known type of the value being serialized</em>. </p>
<p>We still need to describe how to transmit (serialize) types, e.g. values of type <strong>Type</strong>. <em>Types in TL schemas currently appear only as constructors' optional parameters and are therefore never serialized explicitly. Rather, their values are inferred from the previously known type of the value being serialized</em>. </p>
<p>For completeness we will describe how it would be possible to serialize types (values of type Type). However, keep in mind that for now this information is not useful. See <a href="/mtproto/TL-types">Type serialization</a>.</p>
<h3><a class="anchor" href="#optional-arguments-in-polymorphic-constructors" id="optional-arguments-in-polymorphic-constructors" name="optional-arguments-in-polymorphic-constructors"><i class="anchor-icon"></i></a>Optional arguments in polymorphic constructors</h3>
<p>It was stated above that any subset of (the first few) parameters of any constructor can be identified as optional (by enclosing their declarations in curly brackets), but this is not actually entirely accurate. First, these optional parameters can only be of type <code>Type</code> or <code>#</code> (natural numbers). Second, optional parameters must share the return values type, otherwise their value cannot be determined.</p>
<p>Note that @'''constr-id''' means the constructors “full form” (in which all optional parameters become required), while '''constr-id' denotes its abbreviated form (without the optional arguments). If there are no optional arguments, then these two forms are the same. Constructors full forms are never used at present.</p>
<p>It was stated above that any subset of (the first few) parameters of any constructor can be identified as optional (by enclosing their declarations in curly brackets), but this is not actually entirely accurate. First, these optional parameters can only be of type <code>Type</code> or <code>#</code> (natural numbers). Second, optional parameters must share the return value's type, otherwise their value cannot be determined.</p>
<p>Note that @'''constr-id''' means the constructor's “full form” (in which all optional parameters become required), while '''constr-id'' denotes its abbreviated form (without the optional arguments). If there are no optional arguments, then these two forms are the same. Constructors' full forms are never used at present.</p>
<h3><a class="anchor" href="#bare-polymorphic-types" id="bare-polymorphic-types" name="bare-polymorphic-types"><i class="anchor-icon"></i></a>Bare polymorphic types</h3>
<p>There is a small problem: if we want to serialize the value of the bare type %pair string int or %pair string Y (which in TL is usually denoted simply as “pair”, though the form “%Pair” is preferable), we cannot simultaneously use both the full constructor @pair and the partial pair, because the constructors name will not be serialized. Therefore, we must differentiate the bare types %@pair (type X, type Y, value x:X, and value y:Y are serialized) and %pair (only x:X and y:Y are serialized; types X and Y are known from the context). In practice, we nearly almost always need the bare type %pair, and this is precisely what “pair” means in the types context in TL. Therefore, </p>
<p>There is a small problem: if we want to serialize the value of the bare type %pair string int' or %pair string Y' (which in TL is usually denoted simply as “pair”, though the form “%Pair” is preferable), we cannot simultaneously use both the full constructor @pair and the partial pair, because the constructor's name will not be serialized. Therefore, we must differentiate the bare types %@pair (type X, type Y, value x:X, and value y:Y are serialized) and %pair (only x:X and y:Y are serialized; types X and Y are known from the context). In practice, we nearly almost always need the bare type %pair, and this is precisely what “pair” means in the type's context in TL. Therefore, </p>
<pre><code>record name:string map:(List (pair int string)) = Record;</code></pre>
<p>will be serialized approximately like we want it to be (the serialization of list elements will consist of the serialization of int and the serialization of string, without any additional headers, types, or combinator names).
Incidentally, when calculating the “record” combinator's name <em>'record'</em> in the example given above, the CRC32 of <code>record name:string map:List pair int string = Record</code> will be computed.</p>

View file

@ -97,8 +97,8 @@ tls.typeExpr name:int flags:int children_num:# children:children_num*[tls.Expr]
<p>Schema serialization (version 2) always begins with the index number of the <code>tls.schema_v2</code> constructor for <code>tls.Schema</code>.
Because the CRC32 of the string</p>
<pre><code>tls.schema_v2 version:int date:int types_num:# types:types_num*[ tls.Type ] constructor_num:# constructors:constructor_num*[ tls.Combinator ] functions_num:# functions:functions_num*[ tls.Combinator ] = tls.Schema</code></pre>
<p>is 0x3a2f9be2, this constant is in fact the magic number for tlo files in the current versions format.
If the format is extended in the future (for example, if TLs additional features are supported), then a <code>tls.schema_v3</code> constructor with a different number will appear.</p>
<p>is 0x3a2f9be2, this constant is in fact the magic number for tlo files in the current version's format.
If the format is extended in the future (for example, if TL's additional features are supported), then a <code>tls.schema_v3</code> constructor with a different number will appear.</p>
<h2><a class="anchor" href="#example" id="example" name="example"><i class="anchor-icon"></i></a>Example</h2>
<p>If one adds declarations for the used built-in types (like <code>int ? = Int;</code>) from the file <code>common.tl</code> before <code>tl.tl</code> and serialize the resulting schema, the following binary data is obtained (<code>tl.tlo</code>):</p>
<div class="richcode">

View file

@ -44,8 +44,8 @@ It remains to describe how types, e.g. values of type Type, are transmitted (ser
<div id="dev_page_content"><p>See <a href="/mtproto/TL-polymorph">Polymorphism in TL</a> and <a href="/mtproto/TL">TL Language</a>.</p>
<p>It remains to describe how types, e.g. values of type Type, are transmitted (serialized). In general, there is nothing unexpected going on here: we have type constructors of various arities (for example, List is an arity-1 constructor, but IntList is a 0-arity constructor); and if we know that a 32-bit “name” is assigned to each type constructor, there are no further questions -- values of type Type are serialized exactly like values of any other recursive type with a defined set of constructors of differing arity. </p>
<p>How can a 32-bit “name” be assigned to a type (a type constructor, to be more exact) such as List or IntList?
It is proposed to use the sum of the names of all of its constructors, plus the CRC32 of the string with the designation of the type's name and all of its parameters such as “IntList = Type” or “List X:Type = Type”. This way, the List constructors “name” is the sum of the CRC32s of the three strings "List X:Type = Type", "cons X:Type hd:X tl:List X = List X", and "nil X:Type = List X".
For “bare” types (which, formally speaking, are subtypes of the corresponding “boxed” type), the situation is somewhat more complicated; the logical negation of the corresponding constructors name is used. For built-in bareand boxed types (for example, int and Int), a pseudo-declaration is used (for example, int ? = Int").</p>
It is proposed to use the sum of the names of all of its constructors, plus the CRC32 of the string with the designation of the type's name and all of its parameters such as “IntList = Type” or “List X:Type = Type”. This way, the List constructor's “name” is the sum of the CRC32s of the three strings "List X:Type = Type", "cons X:Type hd:X tl:List X = List X", and "nil X:Type = List X".
For “bare” types (which, formally speaking, are subtypes of the corresponding “boxed” type), the situation is somewhat more complicated; the logical negation of the corresponding constructor's name is used. For built-in bareand boxed types (for example, int and Int), a pseudo-declaration is used (for example, int ? = Int").</p>
<ul>
<li>This description is somewhat outdated and may be updated in the future. Specifically, how to treat the <code>!</code> modifier has not been explained.*</li>
</ul></div>

View file

@ -41,100 +41,152 @@
<div id="dev_page_content"><p>The query format is described using <a href="/mtproto/serialize">Binary Data Serialization</a> and the <a href="/mtproto/TL">TL Language</a>. All large numbers are transmitted as strings containing the required sequence of bytes in big endian order. Hash functions, such as SHA1, return strings (of 20 bytes) which can also be interpreted as big endian numbers. Small numbers (<code>int</code>, <code>long</code>, <code>int128</code>, <code>int256</code>) are normally little endian; however, if they are part of SHA1, the bytes are not rearranged. This way, if <code>long</code> <code>x</code> is the 64 lower-order bits of SHA1 of string <code>s</code>, then the <em>final</em> 8 bytes of 20-byte string <code>SHA1(s)</code> are taken and interpreted as a 64-bit integer.</p>
<p>Prior to sending off unencrypted messages (required in this instance to generate an authorization key), the client must undergo (p,q) authorization as follows.</p>
<h4><a class="anchor" name="dh-exchange-initiation" href="#dh-exchange-initiation"><i class="anchor-icon"></i></a>DH exchange initiation</h4>
<p>1) Client sends query to server</p>
<pre><code>req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;</code></pre>
<h4><a class="anchor" href="#dh-exchange-initiation" id="dh-exchange-initiation" name="dh-exchange-initiation"><i class="anchor-icon"></i></a>DH exchange initiation</h4>
<ol>
<li>
<p>Client sends query to server</p>
<p>req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;</p>
</li>
</ol>
<p>The value of <em>nonce</em> is selected randomly by the client (random number) and identifies the client within this communication. Following this step, it is known to all.</p>
<p>2) Server sends response of the form</p>
<pre><code>resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ;</code></pre>
<ol start="2">
<li>
<p>Server sends response of the form</p>
<p>resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ;</p>
</li>
</ol>
<p>Here, string pq is a representation of a natural number (in binary big endian format). This number is the product of two different odd prime numbers. Normally, pq is less than or equal to 2^63-1. The value of <em>server_nonce</em> is selected randomly by the server; following this step, it is known to all.</p>
<p><code>server_public_key_fingerprints</code> is a list of public RSA key fingerprints (64 lower-order bits of SHA1 (server_public_key); the public key is represented as a bare type <code>rsa_public_key n:string e:string = RSAPublicKey</code>, where, as usual, n and е are numbers in big endian format serialized as strings of bytes, following which SHA1 is computed) received by the server.</p>
<p>All subsequent messages contain the pair (nonce, server_nonce) both in the plain-text, and the encrypted portions which makes it possible to identify a “temporary session” — one run of the key generation protocol described on this page that uses the same (nonce, server_nonce) pair. An intruder could not create a parallel session with the server with the same parameters and reuse parts of server- or client-encrypted messages for its own purposes in such a parallel session, because a different server_nonce would be selected by the server for any new “temporary session”.</p>
<h4><a class="anchor" name="proof-of-work" href="#proof-of-work"><i class="anchor-icon"></i></a>Proof of work</h4>
<p>3) Client decomposes pq into prime factors such that p &lt; q.</p>
<h4><a class="anchor" href="#proof-of-work" id="proof-of-work" name="proof-of-work"><i class="anchor-icon"></i></a>Proof of work</h4>
<ol start="3">
<li>Client decomposes pq into prime factors such that p &lt; q.</li>
</ol>
<p>This starts a round of Diffie-Hellman key exchanges.</p>
<h4><a class="anchor" name="presenting-proof-of-work-server-authentication" href="#presenting-proof-of-work-server-authentication"><i class="anchor-icon"></i></a>Presenting proof of work; Server authentication</h4>
<p>4) Client sends query to server</p>
<pre><code>req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params</code></pre>
<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"><i class="anchor-icon"></i></a>Presenting proof of work; Server authentication</h4>
<ol start="4">
<li>
<p>Client sends query to server</p>
<p>req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params</p>
</li>
</ol>
<p>Here, encrypted_data is obtained as follows:</p>
<ul>
<li>new_nonce := another (good) random number generated by the client; after this query, it is known to both client and server;</li>
<li><p>data := a serialization of </p>
<li>
<p>new_nonce := another (good) random number generated by the client; after this query, it is known to both client and server;</p>
</li>
<li>
<p>data := a serialization of </p>
<pre><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>or of</p>
<pre><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>
</li>
<li><p>encrypted_data := RSA_PAD (data, server_public_key), where RSA_PAD is a version of RSA with a variant of OAEP+ padding explained below in 4.1).</p>
<li>
<p>encrypted_data := RSA_PAD (data, server_public_key), where RSA_PAD is a version of RSA with a variant of OAEP+ padding explained below in 4.1).</p>
</li>
</ul>
<p>Someone might intercept the query and replace it with their own, independently decomposing pq into factors instead of the client. The only field that it makes sense to modify is new_nonce which would be the one an intruder would have to re-generate (because an intruder cannot decrypt the encrypted data sent by the client). Since all subsequent messages are encrypted using new_nonce or contain new_nonce_hash, they will not be processed by the client (an intruder would not be able to make it look as though they had been generated by the server because they would not contain new_nonce). Therefore, this intercept will only result in the intruders completing the authorization key generation protocol in place of the client and creating a new key (that has nothing to do with the client); however, the same effect could be achieved simply by creating a new key in one&#39;s own name.</p>
<p>Someone might intercept the query and replace it with their own, independently decomposing pq into factors instead of the client. The only field that it makes sense to modify is new_nonce which would be the one an intruder would have to re-generate (because an intruder cannot decrypt the encrypted data sent by the client). Since all subsequent messages are encrypted using new_nonce or contain new_nonce_hash, they will not be processed by the client (an intruder would not be able to make it look as though they had been generated by the server because they would not contain new_nonce). Therefore, this intercept will only result in the intruder's completing the authorization key generation protocol in place of the client and creating a new key (that has nothing to do with the client); however, the same effect could be achieved simply by creating a new key in one's own name.</p>
<p>An alternative form of inner data (<code>p_q_inner_data_temp_dc</code>) is used to create temporary keys, that are only stored in the server RAM and are discarded after at most <code>expires_in</code> seconds. The server is free to discard its copy earlier. In all other respects the temporary key generation protocol is the same. After a temporary key is created, the client usually binds it to its principal authorisation key by means of the <a href="/method/auth.bindTempAuthKey">auth.bindTempAuthKey</a> method, and uses it for all client-server communication until it expires; then a new temporary key is generated. Thus Perfect Forward Secrecy (PFS) in client-server communication is achieved. <a href="/api/pfs">Read more about PFS »</a></p>
<p>4.1) RSA_PAD(data, server_public_key) mentioned above is implemented as follows:</p>
<ul>
<li>data_with_padding := data + random_padding_bytes; where random_padding_bytes are chosen so that the resulting length of data_with_padding is precisely 192 bytes, and data is the TL-serialized data to be encrypted as before. One has to check that data is not longer than 144 bytes.</li>
<li>data_pad_reversed := BYTE_REVERSE(data_with_padding); is obtained from data_with_padding by reversing the byte order.</li>
<li>data_with_padding := data + random_padding_bytes; -- where random_padding_bytes are chosen so that the resulting length of data_with_padding is precisely 192 bytes, and data is the TL-serialized data to be encrypted as before. One has to check that data is not longer than 144 bytes.</li>
<li>data_pad_reversed := BYTE_REVERSE(data_with_padding); -- is obtained from data_with_padding by reversing the byte order.</li>
<li>a random 32-byte temp_key is generated.</li>
<li>data_with_hash := data_pad_reversed + SHA256(temp_key + data_with_padding); after this assignment, data_with_hash is exactly 224 bytes long.</li>
<li>aes_encrypted := AES256_IGE(data_with_hash, temp_key, 0); AES256-IGE encryption with zero IV.</li>
<li>temp_key_xor := temp_key XOR SHA256(aes_encrypted); adjusted key, 32 bytes</li>
<li>key_aes_encrypted := temp_key_xor + aes_encrypted; exactly 256 bytes (2048 bits) long</li>
<li>data_with_hash := data_pad_reversed + SHA256(temp_key + data_with_padding); -- after this assignment, data_with_hash is exactly 224 bytes long.</li>
<li>aes_encrypted := AES256_IGE(data_with_hash, temp_key, 0); -- AES256-IGE encryption with zero IV.</li>
<li>temp_key_xor := temp_key XOR SHA256(aes_encrypted); -- adjusted key, 32 bytes</li>
<li>key_aes_encrypted := temp_key_xor + aes_encrypted; -- exactly 256 bytes (2048 bits) long</li>
<li>The value of key_aes_encrypted is compared with the RSA-modulus of server_pubkey as a big-endian 2048-bit (256-byte) unsigned integer. If key_aes_encrypted turns out to be greater than or equal to the RSA modulus, the previous steps starting from the generation of new random temp_key are repeated. Otherwise the final step is performed:</li>
<li>encrypted_data := RSA(key_aes_encrypted, server_pubkey); 256-byte big-endian integer is elevated to the requisite power from the RSA public key modulo the RSA modulus, and the result is stored as a big-endian integer consisting of exactly 256 bytes (with leading zero bytes if required).</li>
<li>encrypted_data := RSA(key_aes_encrypted, server_pubkey); -- 256-byte big-endian integer is elevated to the requisite power from the RSA public key modulo the RSA modulus, and the result is stored as a big-endian integer consisting of exactly 256 bytes (with leading zero bytes if required).</li>
</ul>
<p>5) Server responds with:</p>
<pre><code>server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;</code></pre>
<ol start="5">
<li>
<p>Server responds with:</p>
<p>server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;</p>
</li>
</ol>
<blockquote>
<p>If the query is incorrect, the server returns a <code>-404</code> error and the handshake must be restarted (any subsequent request also returns <code>-404</code>, even if it is correct).</p>
</blockquote>
<p>Here, encrypted_answer is obtained as follows:</p>
<ul>
<li>new_nonce_hash := 128 lower-order bits of SHA1 (new_nonce);</li>
<li><p>answer := serialization </p>
<li>
<p>new_nonce_hash := 128 lower-order bits of SHA1 (new_nonce);</p>
</li>
<li>
<p>answer := serialization </p>
<pre><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><p>answer_with_hash := SHA1(answer) + answer + (0-15 random bytes); such that the length be divisible by 16;</p>
<li>
<p>answer_with_hash := SHA1(answer) + answer + (0-15 random bytes); such that the length be divisible by 16;</p>
</li>
<li>
<p>tmp_aes_key := SHA1(new_nonce + server_nonce) + substr (SHA1(server_nonce + new_nonce), 0, 12);</p>
</li>
<li>
<p>tmp_aes_iv := substr (SHA1(server_nonce + new_nonce), 12, 8) + SHA1(new_nonce + new_nonce) + substr (new_nonce, 0, 4);</p>
</li>
<li>
<p>encrypted_answer := AES256_ige_encrypt (answer_with_hash, tmp_aes_key, tmp_aes_iv); here, tmp_aes_key is a 256-bit key, and tmp_aes_iv is a 256-bit initialization vector. The same as in all the other instances that use AES encryption, the encrypted data is padded with random bytes to a length divisible by 16 immediately prior to encryption.</p>
</li>
<li>tmp_aes_key := SHA1(new_nonce + 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_nonce, 0, 4);</li>
<li>encrypted_answer := AES256_ige_encrypt (answer_with_hash, tmp_aes_key, tmp_aes_iv); here, tmp_aes_key is a 256-bit key, and tmp_aes_iv is a 256-bit initialization vector. The same as in all the other instances that use AES encryption, the encrypted data is padded with random bytes to a length divisible by 16 immediately prior to encryption.</li>
</ul>
<p>Following this step, new_nonce is still known to client and server only. The client is certain that it is the server that responded and that the response was generated specifically in response to client query req_DH_params, since the response data are encrypted using new_nonce.</p>
<p>Client is expected to check whether <strong>p = dh_prime</strong> is a safe 2048-bit prime (meaning that both <strong>p</strong> and <strong>(p-1)/2</strong> are prime, and that 2<sup>2047 &lt; p &lt; 2</sup>2048), and that <strong>g</strong> generates a cyclic subgroup of prime order <strong>(p-1)/2</strong>, i.e. is a quadratic residue <strong>mod p</strong>. Since <strong>g</strong> is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on <strong>p mod 4g</strong> — namely, <strong>p mod 8 = 7</strong> for <strong>g = 2</strong>; <strong>p mod 3 = 2</strong> for <strong>g = 3</strong>; no extra condition for <strong>g = 4</strong>; <strong>p mod 5 = 1 or 4</strong> for <strong>g = 5</strong>; <strong>p mod 24 = 19 or 23</strong> for <strong>g = 6</strong>; and <strong>p mod 7 = 3, 5 or 6</strong> for <strong>g = 7</strong>. After <strong>g</strong> and <strong>p</strong> have been checked by the client, it makes sense to cache the result, so as not to repeat lengthy computations in future.</p>
<p>If the verification takes too long time (which is the case for older mobile devices), one might initially run only 15 Miller—Rabin iterations for verifying primeness of <strong>p</strong> and <strong>(p - 1)/2</strong> with error probability not exceeding one billionth, and do more iterations later in the background.</p>
<p>Another optimization is to embed into the client application code a small table with some known “good” couples <strong>(g,p)</strong> (or just known safe primes <strong>p</strong>, since the condition on <strong>g</strong> is easily verified during execution), checked during code generation phase, so as to avoid doing such verification during runtime altogether. Server changes these values rarely, thus one usually has to put the current value of server&#39;s <strong>dh_prime</strong> into such a table. For example, current value of <strong>dh_prime</strong> equals (in big-endian byte order)</p>
<p>Client is expected to check whether <strong>p = dh_prime</strong> is a safe 2048-bit prime (meaning that both <strong>p</strong> and <strong>(p-1)/2</strong> are prime, and that 2^2047 &lt; p &lt; 2^2048), and that <strong>g</strong> generates a cyclic subgroup of prime order <strong>(p-1)/2</strong>, i.e. is a quadratic residue <strong>mod p</strong>. Since <strong>g</strong> is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law, yielding a simple condition on <strong>p mod 4g</strong> -- namely, <strong>p mod 8 = 7</strong> for <strong>g = 2</strong>; <strong>p mod 3 = 2</strong> for <strong>g = 3</strong>; no extra condition for <strong>g = 4</strong>; <strong>p mod 5 = 1 or 4</strong> for <strong>g = 5</strong>; <strong>p mod 24 = 19 or 23</strong> for <strong>g = 6</strong>; and <strong>p mod 7 = 3, 5 or 6</strong> for <strong>g = 7</strong>. After <strong>g</strong> and <strong>p</strong> have been checked by the client, it makes sense to cache the result, so as not to repeat lengthy computations in future.</p>
<p>If the verification takes too long time (which is the case for older mobile devices), one might initially run only 15 Miller--Rabin iterations for verifying primeness of <strong>p</strong> and <strong>(p - 1)/2</strong> with error probability not exceeding one billionth, and do more iterations later in the background.</p>
<p>Another optimization is to embed into the client application code a small table with some known "good" couples <strong>(g,p)</strong> (or just known safe primes <strong>p</strong>, since the condition on <strong>g</strong> is easily verified during execution), checked during code generation phase, so as to avoid doing such verification during runtime altogether. Server changes these values rarely, thus one usually has to put the current value of server's <strong>dh_prime</strong> into such a table. For example, current value of <strong>dh_prime</strong> equals (in big-endian byte order)</p>
<pre><code>C7 1C AE B9 C6 B1 C9 04 8E 6C 52 2F 70 F1 3F 73 98 0D 40 23 8E 3E 21 C1 49 34 D0 37 56 3D 93 0F 48 19 8A 0A A7 C1 40 58 22 94 93 D2 25 30 F4 DB FA 33 6F 6E 0A C9 25 13 95 43 AE D4 4C CE 7C 37 20 FD 51 F6 94 58 70 5A C6 8C D4 FE 6B 6B 13 AB DC 97 46 51 29 69 32 84 54 F1 8F AF 8C 59 5F 64 24 77 FE 96 BB 2A 94 1D 5B CD 1D 4A C8 CC 49 88 07 08 FA 9B 37 8E 3C 4F 3A 90 60 BE E6 7C F9 A4 A4 A6 95 81 10 51 90 7E 16 27 53 B5 6B 0F 6B 41 0D BA 74 D8 A8 4B 2A 14 B3 14 4E 0E F1 28 47 54 FD 17 ED 95 0D 59 65 B4 B9 DD 46 58 2D B1 17 8D 16 9C 6B C4 65 B0 D6 FF 9C A3 92 8F EF 5B 9A E4 E4 18 FC 15 E8 3E BE A0 F8 7F A9 FF 5E ED 70 05 0D ED 28 49 F4 7B F9 59 D9 56 85 0C E9 29 85 1F 0D 81 15 F6 35 B1 05 EE 2E 4E 15 D0 4B 24 54 BF 6F 4F AD F0 34 B1 04 03 11 9C D8 E3 B9 2F CC 5B</code></pre>
<p>6) Client computes random 2048-bit number <em>b</em> (using a sufficient amount of entropy) and sends the server a message</p>
<pre><code>set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;</code></pre>
<ol start="6">
<li>
<p>Client computes random 2048-bit number <em>b</em> (using a sufficient amount of entropy) and sends the server a message</p>
<p>set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;</p>
</li>
</ol>
<p>Here, encrypted_data is obtained thus:</p>
<ul>
<li>g_b := pow(g, b) mod dh_prime;</li>
<li><p>data := serialization </p>
<li>
<p>g_b := pow(g, b) mod dh_prime;</p>
</li>
<li>
<p>data := serialization </p>
<pre><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><p>data_with_hash := SHA1(data) + data + (0-15 random bytes); such that length be divisible by 16;</p>
<li>
<p>data_with_hash := SHA1(data) + data + (0-15 random bytes); such that length be divisible by 16;</p>
</li>
<li>
<p>encrypted_data := AES256_ige_encrypt (data_with_hash, tmp_aes_key, tmp_aes_iv);</p>
</li>
<li>encrypted_data := AES256_ige_encrypt (data_with_hash, tmp_aes_key, tmp_aes_iv);</li>
</ul>
<p>The retry_id field is equal to zero at the time of the first attempt; otherwise, it is equal to auth_key_aux_hash from the previous failed attempt (see Item 9).</p>
<p>7) Thereafter, auth_key equals <code>pow(g, {ab}) mod dh_prime</code>; on the server, it is computed as <code>pow(g_b, a) mod dh_prime</code>, and on the client as <code>(g_a)^b mod dh_prime</code>.</p>
<p>8) auth_key_hash is computed := 64 lower-order bits of SHA1 (auth_key). The server checks whether there already is another key with the same auth_key_hash and responds in one of the following ways.</p>
<h4><a class="anchor" name="dh-key-exchange-complete" href="#dh-key-exchange-complete"><i class="anchor-icon"></i></a>DH key exchange complete</h4>
<p>9) Server responds in one of three ways:</p>
<pre><code>dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
<ol start="7">
<li>
<p>Thereafter, auth_key equals <code>pow(g, {ab}) mod dh_prime</code>; on the server, it is computed as <code>pow(g_b, a) mod dh_prime</code>, and on the client as <code>(g_a)^b mod dh_prime</code>.</p>
</li>
<li>
<p>auth_key_hash is computed := 64 lower-order bits of SHA1 (auth_key). The server checks whether there already is another key with the same auth_key_hash and responds in one of the following ways.</p>
</li>
</ol>
<h4><a class="anchor" href="#dh-key-exchange-complete" id="dh-key-exchange-complete" name="dh-key-exchange-complete"><i class="anchor-icon"></i></a>DH key exchange complete</h4>
<ol start="9">
<li>
<p>Server responds in one of three ways:</p>
<p>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>
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;</p>
</li>
</ol>
<ul>
<li>new_nonce_hash1, new_nonce_hash2, and new_nonce_hash3 are obtained as the 128 lower-order bits of SHA1 of the byte string derived from the new_nonce string by adding a single byte with the value of 1, 2, or 3, and followed by another 8 bytes with auth_key_aux_hash. Different values are required to prevent an intruder from changing server response dh_gen_ok into dh_gen_retry.</li>
<li>auth_key_aux_hash is the 64 <em>higher-order</em> bits of SHA1(auth_key). It must not be confused with auth_key_hash.</li>
</ul>
<p>In the other case, the client goes to Item 6) generating a new <em>b</em>.<br>In the first case, the client and the server have negotiated auth_key, following which they forget all other temporary data, and the client creates another encrypted session using auth_key. At the same time, server_salt is initially set to <code>substr(new_nonce, 0, 8) XOR substr(server_nonce, 0, 8)</code>. If required, the client stores the difference between server_time received in 5) and its local time, to be able always to have a good approximation of server time which is required to generate correct message identifiers.</p>
<p>In the other case, the client goes to Item 6) generating a new <em>b</em>.
In the first case, the client and the server have negotiated auth_key, following which they forget all other temporary data, and the client creates another encrypted session using auth_key. At the same time, server_salt is initially set to <code>substr(new_nonce, 0, 8) XOR substr(server_nonce, 0, 8)</code>. If required, the client stores the difference between server_time received in 5) and its local time, to be able always to have a good approximation of server time which is required to generate correct message identifiers.</p>
<p><strong>IMPORTANT</strong>: Apart from the conditions on the Diffie-Hellman prime <strong>dh_prime</strong> and generator <strong>g</strong>, both sides are to check that <strong>g</strong>, <strong>g_a</strong> and <strong>g_b</strong> are greater than <strong>1</strong> and less than <strong>dh_prime - 1</strong>. We recommend checking that <strong>g_a</strong> and <strong>g_b</strong> are between <strong>2^{2048-64}</strong> and <strong>dh_prime - 2^{2048-64}</strong> as well.</p>
<h4><a class="anchor" name="error-handling-lost-queries-and-responses" href="#error-handling-lost-queries-and-responses"><i class="anchor-icon"></i></a>Error Handling (Lost Queries and Responses)</h4>
<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"><i class="anchor-icon"></i></a>Error Handling (Lost Queries and Responses)</h4>
<p>If the client fails to receive any response to its query from the server within a certain time interval, it may simply re-send the query. If the server has already sent a response to this query (<em>exactly</em> the same request and not just similar: all the parameters during the repeat request must take on the same values) but it did not get to the client, the server will simply re-send the same response. The server remembers the response for up to 10 minutes after having received the query in 1). If the server has already forgotten the response or the requisite temporary data, the client will have to start from the beginning.</p>
<p>The server may consider that if the client has already sent in the next query using the data from the previous server response to the specific client, the response is known to have been received by the client and may be forgotten by the server.</p>
<h4><a class="anchor" name="usage-example" href="#usage-example"><i class="anchor-icon"></i></a>Usage Example</h4>
<p>An example of a complete list of queries required to generate an authorization key is shown on <a href="/mtproto/samples-auth_key">a separate page</a>.</p>
</div>
<h4><a class="anchor" href="#usage-example" id="usage-example" name="usage-example"><i class="anchor-icon"></i></a>Usage Example</h4>
<p>An example of a complete list of queries required to generate an authorization key is shown on <a href="/mtproto/samples-auth_key">a separate page</a>.</p></div>
</div>

View file

@ -126,7 +126,7 @@ MTProto v.1.0 is deprecated and is currently being phased out.</p>
<p>On top of this, msg_id values that belong over 30 seconds in the future or over 300 seconds in the past are to be ignored. This is especially important for the server. The client would also find this useful (to protect from a replay attack), but only if it is certain of its time (for example, if its time has been synchronized with that of the server).</p>
<p>Certain client-to-server service messages containing data sent by the client to the server (for example, msg_id of a recent client query) may, nonetheless, be processed on the client even if the time appears to be “incorrect”. This is especially true of messages to change server_salt and notifications of invalid client time. See <a href="/mtproto/service_messages">Mobile Protocol: Service Messages</a>.</p>
<h3><a class="anchor" href="#storing-an-authorization-key-on-a-client-device" id="storing-an-authorization-key-on-a-client-device" name="storing-an-authorization-key-on-a-client-device"><i class="anchor-icon"></i></a>Storing an Authorization Key on a Client Device</h3>
<p>It may be suggested to users concerned with security that they password protect the authorization key in approximately the same way as in ssh. This can be accomplished by prepending the value of cryptographic hash function, such as SHA-256, of the key to the front of the key, following which the entire string is encrypted using AES in CBC mode and a key equal to the users (text) password. When the user inputs the password, the stored protected password is decrypted and verified by checking the SHA-256 value. From the users standpoint, this is practically the same as using an application or a website password.</p>
<p>It may be suggested to users concerned with security that they password protect the authorization key in approximately the same way as in ssh. This can be accomplished by prepending the value of cryptographic hash function, such as SHA-256, of the key to the front of the key, following which the entire string is encrypted using AES in CBC mode and a key equal to the user's (text) password. When the user inputs the password, the stored protected password is decrypted and verified by checking the SHA-256 value. From the user's standpoint, this is practically the same as using an application or a website password.</p>
<h3><a class="anchor" href="#unencrypted-messages" id="unencrypted-messages" name="unencrypted-messages"><i class="anchor-icon"></i></a>Unencrypted Messages</h3>
<p>Special plain-text messages may be used to create an authorization key as well as to perform a time synchronization. They begin with auth_key_id = 0 (64 bits) which means that there is no auth_key. This is followed directly by the message body in serialized format without internal or external headers. A message identifier (64 bits) and body length in bytes (32 bytes) are added before the message body.</p>
<p>Only a very limited number of messages of special types can be transmitted as plain text. </p>

View file

@ -97,7 +97,7 @@ For information on encryption used in up-to-date Telegram clients, kindly see <a
<p>In addition, msg_id values that belong over 30 seconds in the future or over 300 seconds in the past are to be ignored. This is especially important for the server. The client would also find this useful (to protect from a replay attack), but only if it is certain of its time (for example, if its time has been synchronized with that of the server).</p>
<p>Certain client-to-server service messages containing data sent by the client to the server (for example, msg_id of a recent client query) may, nonetheless, be processed on the client even if the time appears to be “incorrect”. This is especially true of messages to change server_salt and notifications of invalid client time. See <a href="/mtproto/service_messages">Mobile Protocol: Service Messages</a>.</p>
<h3><a class="anchor" href="#storing-an-authorization-key-on-a-client-device" id="storing-an-authorization-key-on-a-client-device" name="storing-an-authorization-key-on-a-client-device"><i class="anchor-icon"></i></a>Storing an Authorization Key on a Client Device</h3>
<p>It may be suggested to users concerned with security that they password protect the authorization key in approximately the same way as in ssh. This is accomplished by adding the SHA1 of the key to the front of the key, following which the entire string is encrypted using AES in CBC mode and a key equal to the users (text) password. When the user inputs the password, the stored protected password is decrypted and verified by being compared with SHA1. From the users standpoint, this is practically the same as using an application or a website password.</p>
<p>It may be suggested to users concerned with security that they password protect the authorization key in approximately the same way as in ssh. This is accomplished by adding the SHA1 of the key to the front of the key, following which the entire string is encrypted using AES in CBC mode and a key equal to the user's (text) password. When the user inputs the password, the stored protected password is decrypted and verified by being compared with SHA1. From the user's standpoint, this is practically the same as using an application or a website password.</p>
<h3><a class="anchor" href="#unencrypted-messages" id="unencrypted-messages" name="unencrypted-messages"><i class="anchor-icon"></i></a>Unencrypted Messages</h3>
<p>Special plain-text messages may be used to create an authorization key as well as to perform a time synchronization. They begin with auth_key_id = 0 (64 bits) which means that there is no auth_key. This is followed directly by the message body in serialized format without internal or external headers. A message identifier (64 bits) and body length in bytes (32 bytes) are added before the message body.</p>
<p>Only a very limited number of messages of special types can be transmitted as plain text. </p>

View file

@ -49,7 +49,7 @@ The <a href="/mtproto/TL">TL language</a> is used to describe the data types to
<li><em>Value</em>, in this case, is the same as a <em>string in Alphabet A</em>, i. e. a finite (possibly, empty) sequence of 32-bit numbers. The set of all such sequences is designated as <em>A*</em>. </li>
<li><em>Type</em>, for our purposes, is the same as the set of legal values of a type, i. e. some set T which is a subset of A* and is a prefix code (i. e. no element of T may be a prefix for any other element). Therefore, any sequence from A* can contain no more than one prefix that is a member of T. </li>
<li><em>Value of Type T</em> is any sequence (value) which is a member of T as a subset of A*. </li>
<li><em>Compatible Types</em> are the types T and T not intersecting as subsets of A*, such that the union of T and T' is a prefix code. </li>
<li><em>Compatible Types</em> are the types T and T' not intersecting as subsets of A*, such that the union of T and T' is a prefix code. </li>
<li><em>Coordinated System of Types</em> is a finite or infinite set of types T_1, ..., T_n, ..., such that any two types from this set are compatible. </li>
<li><em>Data Type</em> is the same as <em>type</em> in the sense of the definition above. </li>
<li><em>Functional Type</em> is a type describing a function; it is not a type in the sense of the definition above. Initially, we ignore the existence of functional types and describe only the data types; however, in reality, functional types will later be implemented in some extension of this system using the so-called <em>temporary combinators</em>. </li>
@ -66,13 +66,13 @@ The <a href="/mtproto/TL">TL language</a> is used to describe the data types to
<p><em>Combinator identifier</em> is an identifier beginning with a lowercase Roman letter that uniquely identifies a combinator. </p>
</li>
<li>
<p><em>Combinator number</em> or <em>combinator name</em> is a 32-bit number (i.e., an element of A) that uniquely identifies a combinator. Most often, it is CRC32 of the string containing the combinator description without the final semicolon, and with one space between contiguous lexemes. This always falls in the range from 0x01000000 to 0xffffff00. The highest 256 values are reserved for the so-called <em>temporal-logic combinators</em> used to transmit functions. We frequently denote as <em>combinator</em> the combinator name with single quotes: <em>combinator</em>.</p>
<p><em>Combinator number</em> or <em>combinator name</em> is a 32-bit number (i.e., an element of A) that uniquely identifies a combinator. Most often, it is CRC32 of the string containing the combinator description without the final semicolon, and with one space between contiguous lexemes. This always falls in the range from 0x01000000 to 0xffffff00. The highest 256 values are reserved for the so-called <em>temporal-logic combinators</em> used to transmit functions. We frequently denote as <em>combinator</em> the combinator name with single quotes: <em>combinator</em>'.</p>
</li>
<li>
<p><em>Combinator description</em> is a string of format <code>combinator_name type_arg_1 ... type_arg_N = type_res;</code> where <code>N</code> stands for the arity of the combinator, <code>type_arg_i</code> is the type of the i-th argument (or rather, a string with the combinator name), and <code>type_res</code> is the combinator value type.</p>
</li>
<li>
<p><em>Constructor</em> is a combinator that cannot be computed (reduced). This is used to represent composite data types. For example, combinator int_tree with description <code>int_tree IntTree int IntTree = IntTree</code>, alongside combinator <code>empty_tree = IntTree</code>, may be used to define a composite data type called “IntTree” that takes on values in the form of binary trees with integers as nodes. </p>
<p><em>Constructor</em> is a combinator that cannot be computed (reduced). This is used to represent composite data types. For example, combinator int_tree' with description <code>int_tree IntTree int IntTree = IntTree</code>, alongside combinator <code>empty_tree = IntTree</code>, may be used to define a composite data type called “IntTree” that takes on values in the form of binary trees with integers as nodes. </p>
</li>
<li>
<p><em>Function (functional combinator)</em> is a combinator which may be computed (reduced) on condition that the requisite number of arguments of requisite types are provided. The result of the computation is an expression consisting of constructors and base type values only. </p>
@ -87,17 +87,17 @@ The <a href="/mtproto/TL">TL language</a> is used to describe the data types to
<p><em>Type number</em> or <em>type name</em> is a 32-bit number that uniquely identifies a type; it normally is the sum of the CRC32 values of the descriptions of the type constructors.</p>
</li>
<li>
<p><em>Description of (composite) Type T</em> is a collection of the descriptions of all constructors that take on Type <em>T</em> values. This is normally written as text with each string containing the description of a single constructor. Here is a description of Type IntTree, for example:</p>
<p><em>Description of (composite) Type T</em> is a collection of the descriptions of all constructors that take on Type <em>T</em> values. This is normally written as text with each string containing the description of a single constructor. Here is a description of Type IntTree', for example:</p>
<p>int_tree IntTree int IntTree = IntTree;
empty_tree = IntTree;</p>
</li>
<li>
<p><em>Polymorphic type</em> is a type whose description contains parameters (<em>type variables</em>) in lieu of actual types; approximately, what would be a template in C++. Here is a description of Type <code>List alpha</code> where <code>List</code> is a polymorphic type of arity 1 (i. e., dependent on a single argument), and <code>alpha</code> is a type variable which appears as the constructors optional parameter (in curly braces): </p>
<p><em>Polymorphic type</em> is a type whose description contains parameters (<em>type variables</em>) in lieu of actual types; approximately, what would be a template in C++. Here is a description of Type <code>List alpha</code> where <code>List</code> is a polymorphic type of arity 1 (i. e., dependent on a single argument), and <code>alpha</code> is a type variable which appears as the constructor's optional parameter (in curly braces): </p>
<p>cons {alpha:Type} alpha (List alpha) = List alpha;
nil {alpha:Type} = List alpha;</p>
</li>
<li>
<p><em>Value of (composite) Type T</em> is any sequence from A* in the format <code>constr_num arg1 ... argN</code>, where constr_num is the index number of some Constructor <em>C</em> which takes on values of Type <em>T</em>, and arg_i is a value of Type <em>T_i</em> which is the type of the i-th argument to Constructor <em>C</em>. For example, let Combinator int_tree have the index number 17, whereas Combinator empty_tree has the index number 239. Then, the value of Type <code>IntTree</code> is, for example, <code>17 17 239 1 239 2 239</code> which is more conveniently written as <code>'int_tree' 'int_tree' 'empty_tree' 1 'empty_tree' 2 empty_tree</code>. From the standpoint of a high-level language, this is <code>int_tree (int_tree (empty_tree) 1 (empty_tree)) 2 (empty_tree): IntTree</code>.</p>
<p><em>Value of (composite) Type T</em> is any sequence from A* in the format <code>constr_num arg1 ... argN</code>, where constr_num is the index number of some Constructor <em>C</em> which takes on values of Type <em>T</em>, and arg_i is a value of Type <em>T_i</em> which is the type of the i-th argument to Constructor <em>C</em>. For example, let Combinator int_tree have the index number 17, whereas Combinator empty_tree has the index number 239. Then, the value of Type <code>IntTree</code> is, for example, <code>17 17 239 1 239 2 239</code> which is more conveniently written as <code>'int_tree' 'int_tree' 'empty_tree' 1 'empty_tree' 2 empty_tree'</code>. From the standpoint of a high-level language, this is <code>int_tree (int_tree (empty_tree) 1 (empty_tree)) 2 (empty_tree): IntTree</code>.</p>
</li>
<li>
<p><em>Schema</em> is a collection of all the (composite) data type descriptions. This is used to define some agreed-to system of types.</p>

View file

@ -47,20 +47,20 @@ rpc_result#f35c6d01 req_msg_id:long…">
<p>A response to an RPC query is normally wrapped as follows:</p>
<pre><code>rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;</code></pre>
<p>Here req_msg_id is the identifier of the message sent by the other party and containing an RPC query. This way, the recipient knows that the result is a response to the specific RPC query in question.
At the same time, this response serves as acknowledgment of the other partys receipt of the req_msg_id message.</p>
At the same time, this response serves as acknowledgment of the other party's receipt of the req_msg_id message.</p>
<p>Note that the response to an RPC query must also be acknowledged. Most frequently, this coincides with the transmission of the next message (which may have a container attached to it carrying a service message with the acknowledgment).</p>
<h4><a class="anchor" href="#rpc-error" id="rpc-error" name="rpc-error"><i class="anchor-icon"></i></a>RPC Error</h4>
<p>The result field returned in response to any RPC query may also contain an error message in the following format:</p>
<pre><code>rpc_error#2144ca19 error_code:int error_message:string = RpcError;</code></pre>
<h4><a class="anchor" href="#cancellation-of-an-rpc-query" id="cancellation-of-an-rpc-query" name="cancellation-of-an-rpc-query"><i class="anchor-icon"></i></a>Cancellation of an RPC Query</h4>
<p>In certain situations, the client does not want to receive a response to an already transmitted RPC query, for example because the response turns out to be long and the client has decided to do without it because of insufficient link capacity. Simply interrupting the TCP connection will not have any effect because the server would re-send the missing response at the first opportunity. Therefore, the client needs a way to cancel receipt of the RPC response message, actually acknowledging its receipt prior to it being in fact received, which will settle the server down and prevent it from re-sending the response. However, the client does not know the RPC responses msg_id prior to receiving the response; the only thing it knows is the req_msg_id. i. e. the msg_id of the relevant RPC query. Therefore, a special query is used:</p>
<p>In certain situations, the client does not want to receive a response to an already transmitted RPC query, for example because the response turns out to be long and the client has decided to do without it because of insufficient link capacity. Simply interrupting the TCP connection will not have any effect because the server would re-send the missing response at the first opportunity. Therefore, the client needs a way to cancel receipt of the RPC response message, actually acknowledging its receipt prior to it being in fact received, which will settle the server down and prevent it from re-sending the response. However, the client does not know the RPC response's msg_id prior to receiving the response; the only thing it knows is the req_msg_id. i. e. the msg_id of the relevant RPC query. Therefore, a special query is used:</p>
<pre><code>rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;</code></pre>
<p>The response to this query returns as one of the following messages wrapped in rpc_result and requiring an acknowledgment: </p>
<pre><code>rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;</code></pre>
<p>The first version of the response is used if the server remembers nothing of the incoming req_msg_id (if it has already been responded to, for example). The second version is used if the response was canceled while the RPC query was being processed (where the RPC query itself was still fully processed); in this case, the same rpc_answer_dropped_running is also returned in response to the original query, and both of these responses require an acknowledgment from the client. The final version means that the RPC response was removed from the servers outgoing queue, and its msg_id, seq_no, and length in bytes are transmitted to the client.</p>
<p>Note that rpc_answer_dropped_running and rpc_answer_dropped serve as acknowledgments of the servers receipt of the original query (the same one, the response to which we wish to forget). In addition, same as for any RPC queries, any response to rpc_drop_answer is an acknowledgment for rpc_drop_answer itself.</p>
<p>The first version of the response is used if the server remembers nothing of the incoming req_msg_id (if it has already been responded to, for example). The second version is used if the response was canceled while the RPC query was being processed (where the RPC query itself was still fully processed); in this case, the same rpc_answer_dropped_running is also returned in response to the original query, and both of these responses require an acknowledgment from the client. The final version means that the RPC response was removed from the server's outgoing queue, and its msg_id, seq_no, and length in bytes are transmitted to the client.</p>
<p>Note that rpc_answer_dropped_running and rpc_answer_dropped serve as acknowledgments of the server's receipt of the original query (the same one, the response to which we wish to forget). In addition, same as for any RPC queries, any response to rpc_drop_answer is an acknowledgment for rpc_drop_answer itself.</p>
<p>As an alternative to using rpc_drop_answer, a new session may be created after the connection is reset and the old session is removed through destroy_session.</p>
<h4><a class="anchor" href="#messages-associated-with-querying-changing-and-receiving-the-status-of-other-messages" id="messages-associated-with-querying-changing-and-receiving-the-status-of-other-messages" name="messages-associated-with-querying-changing-and-receiving-the-status-of-other-messages"><i class="anchor-icon"></i></a>Messages associated with querying, changing, and receiving the status of other messages</h4>
<p>See <a href="/mtproto/service_messages_about_messages">Mobile Protocol: Service Messages about Messages</a></p>
@ -69,7 +69,7 @@ rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;</c
<pre><code>get_future_salts#b921bd04 num:int = FutureSalts;
future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
future_salts#ae500895 req_msg_id:long now:int salts:vector&lt;future_salt&gt; = FutureSalts;</code></pre>
<p>The client must check to see that the responses req_msg_id in fact coincides with msg_id of the query for get_future_salts. The server returns a maximum of num future server salts (may return fewer). The response serves as the acknowledgment of the query and does not require an acknowledgment itself.</p>
<p>The client must check to see that the response's req_msg_id in fact coincides with msg_id of the query for get_future_salts. The server returns a maximum of num future server salts (may return fewer). The response serves as the acknowledgment of the query and does not require an acknowledgment itself.</p>
<h4><a class="anchor" href="#ping-messages-pingpong" id="ping-messages-pingpong" name="ping-messages-pingpong"><i class="anchor-icon"></i></a>Ping Messages (PING/PONG)</h4>
<pre><code>ping#7abe77ec ping_id:long = Pong;</code></pre>
<p>A response is usually returned to the same connection:</p>
@ -84,7 +84,7 @@ future_salts#ae500895 req_msg_id:long now:int salts:vector&lt;future_salt&gt; =
destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;</code></pre>
<h4><a class="anchor" href="#new-session-creation-notification" id="new-session-creation-notification" name="new-session-creation-notification"><i class="anchor-icon"></i></a>New Session Creation Notification</h4>
<p>The server notifies the client that a new session (from the servers standpoint) had to be created to handle a client message. If, after this, the server receives a message with an even smaller msg_id within the same session, a similar notification will be generated for this msg_id as well. No such notifications are generated for high msg_id values.</p>
<p>The server notifies the client that a new session (from the server's standpoint) had to be created to handle a client message. If, after this, the server receives a message with an even smaller msg_id within the same session, a similar notification will be generated for this msg_id as well. No such notifications are generated for high msg_id values.</p>
<pre><code>new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession</code></pre>
<p>The unique_id parameter is generated by the server every time a session is (re-)created.</p>
<p>This notification must be acknowledged by the client. It is necessary, for instance, for the client to understand that there is, in fact, a “gap” in the stream of long poll notifications received from the server (the user may have failed to receive notifications during some period of time).</p>
@ -105,7 +105,7 @@ Clients should group acknowledgments, state requests and message resend requests
<h4><a class="anchor" href="#message-copies" id="message-copies" name="message-copies"><i class="anchor-icon"></i></a>Message Copies</h4>
<p>In some situations, an old message with a msg_id that is no longer valid needs to be re-sent. Then, it is wrapped in a copy container:</p>
<pre><code>msg_copy#e06046b2 orig_message:Message = MessageCopy;</code></pre>
<p>Once received, the message is processed as if the wrapper were not there. However, if it is known for certain that the message orig_message.msg_id was received, then the new message is not processed (while at the same time, it and orig_message.msg_id are acknowledged). The value of orig_message.msg_id must be lower than the containers msg_id.</p>
<p>Once received, the message is processed as if the wrapper were not there. However, if it is known for certain that the message orig_message.msg_id was received, then the new message is not processed (while at the same time, it and orig_message.msg_id are acknowledged). The value of orig_message.msg_id must be lower than the container's msg_id.</p>
<p>This is not used at this time, because an old message can be wrapped in a simple container with the same result.</p>
<h4><a class="anchor" href="#packed-object" id="packed-object" name="packed-object"><i class="anchor-icon"></i></a>Packed Object</h4>
<p>Used to replace any other object (or rather, a serialization thereof) with its archived (gzipped) representation:</p>
@ -119,7 +119,7 @@ Clients should group acknowledgments, state requests and message resend requests
<p>At the same time, the <code>max_delay</code> parameter has higher priority than <code>wait_after</code>, and <code>max_wait</code> has higher priority than <code>max_delay</code>.</p>
<p>This message does not require a response or an acknowledgement. If the container transmitted over HTTP carries several such messages, the behavior is undefined (in fact, the latest parameter will be used). </p>
<p>If no <code>http_wait</code> is present in container, default values <code>max_delay=0</code> (milliseconds), <code>wait_after=0</code> (milliseconds), and <code>max_wait=25000</code> (milliseconds) are used. </p>
<p>If the clients ping of the server takes a long time, it may make sense to set <code>max_delay</code> to a value that is comparable in magnitude to ping time.</p></div>
<p>If the client's ping of the server takes a long time, it may make sense to set <code>max_delay</code> to a value that is comparable in magnitude to ping time.</p></div>
</div>

View file

@ -68,7 +68,7 @@ bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_se
</ul>
<p>The intention is that error_code values are grouped (error_code &gt;&gt; 4): for example, the codes 0x40 - 0x4f correspond to errors in container decomposition.</p>
<p>Notifications of an ignored message do not require acknowledgment (i.e., are irrelevant).</p>
<p><strong>Important:</strong> if server_salt has changed on the server or if client time is incorrect, any query will result in a notification in the above format. The client must check that it has, in fact, recently sent a message with the specified msg_id, and if that is the case, update its time correction value (the difference between the clients and the servers clocks) and the server salt based on msg_id and the server_salt notification, so as to use these to (re)send future messages. In the meantime, the original message (the one that caused the error message to be returned) must also be re-sent with a better msg_id and/or server_salt.</p>
<p><strong>Important:</strong> if server_salt has changed on the server or if client time is incorrect, any query will result in a notification in the above format. The client must check that it has, in fact, recently sent a message with the specified msg_id, and if that is the case, update its time correction value (the difference between the client's and the server's clocks) and the server salt based on msg_id and the server_salt notification, so as to use these to (re)send future messages. In the meantime, the original message (the one that caused the error message to be returned) must also be re-sent with a better msg_id and/or server_salt.</p>
<p>In addition, the client can update the server_salt value used to send messages to the server, based on the values of RPC responses or containers carrying an RPC response, provided that this RPC response is actually a match for the query sent recently. (If there is doubt, it is best not to update since there is risk of a replay attack).</p>
<h4><a class="anchor" href="#request-for-message-status-information" id="request-for-message-status-information" name="request-for-message-status-information"><i class="anchor-icon"></i></a>Request for Message Status Information</h4>
<p>If either party has not received information on the status of its outgoing messages for a while, it may explicitly request it from the other party:</p>