mirror of
https://github.com/MarshalX/telegram-crawler.git
synced 2024-12-28 23:38:26 +01:00
233 lines
23 KiB
HTML
233 lines
23 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html class="">
|
|||
|
<head>
|
|||
|
<meta charset="utf-8">
|
|||
|
<title>MTProto Mobile Protocol v.1.0 (DEPRECATED)</title>
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<meta property="description" content="This document describes MTProto v1.0, its status is DEPRECATED.
|
|||
|
For information on encryption used in up-to-date Telegram…">
|
|||
|
<meta property="og:title" content="MTProto Mobile Protocol v.1.0 (DEPRECATED)">
|
|||
|
<meta property="og:image" content="https://blogfork.telegram.org/file/811140187/1/sfBQV3Trp80/3a3c48bad836b853ed">
|
|||
|
<meta property="og:description" content="This document describes MTProto v1.0, its status is DEPRECATED.
|
|||
|
For information on encryption used in up-to-date Telegram…">
|
|||
|
<link rel="icon" type="image/svg+xml" href="/img/website_icon.svg?4">
|
|||
|
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png">
|
|||
|
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png">
|
|||
|
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png">
|
|||
|
<link rel="alternate icon" href="/img/favicon.ico" type="image/x-icon" />
|
|||
|
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
|
|||
|
|
|||
|
<link href="/css/telegram.css?230" rel="stylesheet" media="screen">
|
|||
|
<style>
|
|||
|
</style>
|
|||
|
</head>
|
|||
|
<body class="preload">
|
|||
|
<div class="dev_page_wrap">
|
|||
|
<div class="dev_page_head navbar navbar-static-top navbar-tg">
|
|||
|
<div class="navbar-inner">
|
|||
|
<div class="container clearfix">
|
|||
|
<ul class="nav navbar-nav navbar-right hidden-xs"><li class="navbar-twitter"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i><span> Twitter</span></a></li></ul>
|
|||
|
<ul class="nav navbar-nav">
|
|||
|
<li><a href="//telegram.org/">Home</a></li>
|
|||
|
<li class="hidden-xs"><a href="//telegram.org/faq">FAQ</a></li>
|
|||
|
<li class="hidden-xs"><a href="//telegram.org/apps">Apps</a></li>
|
|||
|
<li class=""><a href="/api">API</a></li>
|
|||
|
<li class=""><a href="/mtproto">Protocol</a></li>
|
|||
|
<li class=""><a href="/schema">Schema</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="container clearfix">
|
|||
|
<div class="dev_page">
|
|||
|
<div id="dev_page_content_wrap" class=" ">
|
|||
|
<div class="dev_page_bread_crumbs"></div>
|
|||
|
<h1 id="dev_page_title">MTProto Mobile Protocol v.1.0 (DEPRECATED)</h1>
|
|||
|
|
|||
|
<div id="dev_page_content"><blockquote>
|
|||
|
<p>This document describes MTProto <strong>v1.0</strong>, its status is <strong>DEPRECATED</strong>.<br>For information on encryption used in up-to-date Telegram clients, kindly see <a href="/mtproto">this document</a>.</p>
|
|||
|
</blockquote>
|
|||
|
<h3><a class="anchor" name="related-articles" href="#related-articles"><i class="anchor-icon"></i></a>Related articles</h3>
|
|||
|
<p><div class="dev_page_nav_wrap"></p>
|
|||
|
<ul>
|
|||
|
<li><a href="/mtproto">MTProto 2.0</a></li>
|
|||
|
<li><a href="/mtproto/description">Mobile Protocol: Detailed Description</a></li>
|
|||
|
<li><a href="/mtproto/auth_key">Creating an Authorization Key</a></li>
|
|||
|
<li><a href="/mtproto/samples-auth_key">Creating an Authorization Key: Example</a></li>
|
|||
|
<li><a href="/mtproto/service_messages">Mobile Protocol: Service Messages</a></li>
|
|||
|
<li><a href="/mtproto/service_messages_about_messages">Mobile Protocol: Service Messages about Messages</a></li>
|
|||
|
<li><a href="/mtproto/serialize">Binary Data Serialization</a></li>
|
|||
|
<li><p><a href="/mtproto/TL">TL Language</a></p>
|
|||
|
</li>
|
|||
|
<li><p><a href="/schema/mtproto">MTProto TL-schema</a></p>
|
|||
|
</li>
|
|||
|
<li><a href="/api/end-to-end">End-to-end encryption, Secret Chats</a></li>
|
|||
|
<li><a href="/schema/end-to-end">End-to-end TL-schema</a></li>
|
|||
|
<li><a href="/mtproto/security_guidelines">Security Guidelines for Client Software Developers</a></li>
|
|||
|
</ul>
|
|||
|
<p></div></p>
|
|||
|
<hr>
|
|||
|
<p>This page deals with the basic layer of MTProto encryption used for Cloud chats (server-client encryption). See also:</p>
|
|||
|
<ul>
|
|||
|
<li><a href="/api/end-to-end">Secret Chats, end-to-end-encryption</a></li>
|
|||
|
<li><a href="/api/end-to-end/voice-calls">End-to-end encrypted Voice Calls</a></li>
|
|||
|
</ul>
|
|||
|
<h3><a class="anchor" name="general-description" href="#general-description"><i class="anchor-icon"></i></a>General Description</h3>
|
|||
|
<p>The protocol is designed for access to a server API from applications running on mobile devices. It must be emphasized that a web browser is not such an application.</p>
|
|||
|
<p>The protocol is subdivided into three virtually independent components:</p>
|
|||
|
<ul>
|
|||
|
<li>High-level component (API query language): defines the method whereby API queries and responses are converted to binary <em>messages</em>.</li>
|
|||
|
<li>Cryptographic (authorization) layer: defines the method by which messages are encrypted prior to being transmitted through the transport protocol.</li>
|
|||
|
<li>Transport component: defines the method for the client and the server to transmit messages over some other existing network protocol (such as, http, https, tcp, udp).</li>
|
|||
|
</ul>
|
|||
|
<div><a href="/file/811140187/1/sfBQV3Trp80/3a3c48bad836b853ed">
|
|||
|
<img src="/file/811140187/1/sfBQV3Trp80/3a3c48bad836b853ed" alt="Server-client encryption in MTProto (Cloud chats)" class="dev_page_image"/>
|
|||
|
</a></div>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Got questions about this setup? — Check out the <a href="http://core.telegram.org/techfaq">Advanced FAQ</a>!</p>
|
|||
|
</blockquote>
|
|||
|
<h6><a class="anchor" name="note-1" href="#note-1"><i class="anchor-icon"></i></a><strong>Note 1</strong></h6>
|
|||
|
<p>Each plaintext message to be encrypted in MTProto always contains the following data to be checked upon decryption in order to make the system robust against known problems with the components:</p>
|
|||
|
<ul>
|
|||
|
<li>server salt (64-Bit)</li>
|
|||
|
<li>session id</li>
|
|||
|
<li>message sequence number</li>
|
|||
|
<li>message length</li>
|
|||
|
<li>time</li>
|
|||
|
</ul>
|
|||
|
<h6><a class="anchor" name="note-2" href="#note-2"><i class="anchor-icon"></i></a><strong>Note 2</strong></h6>
|
|||
|
<p>See additional comments on our use of <a href="https://core.telegram.org/techfaq#q-do-you-use-ige-ige-is-broken">IGE</a>, <a href="https://core.telegram.org/techfaq#q-why-do-you-use-sha-1-not-sha-256-or-x-insert-your-favorite-has">SHA-1</a> and <a href="https://core.telegram.org/techfaq#q-how-are-mtproto-messages-authenticated">message authentication</a>.</p>
|
|||
|
<h6><a class="anchor" name="note-3" href="#note-3"><i class="anchor-icon"></i></a><strong>Note 3</strong></h6>
|
|||
|
<p>Telegram's <strong>End-to-end</strong> encrypted Secret Chats are using an additional layer of encryption on top of the described above. See <a href="https://core.telegram.org/api/end-to-end">Secret Chats, End-to-End encryption</a> for details.</p>
|
|||
|
<h3><a class="anchor" name="brief-component-summary" href="#brief-component-summary"><i class="anchor-icon"></i></a>Brief Component Summary</h3>
|
|||
|
<h4><a class="anchor" name="high-level-component-rpc-query-language-api" href="#high-level-component-rpc-query-language-api"><i class="anchor-icon"></i></a>High-Level Component (RPC Query Language/API)</h4>
|
|||
|
<p>From the standpoint of the high-level component, the client and the server exchange <em>messages</em> inside a <em>session</em>. The session is attached to the client device (the application, to be more exact) rather than a specific http/https/tcp connection. In addition, each session is attached to a <em>user key ID</em> by which authorization is actually accomplished.</p>
|
|||
|
<p>Several connections to a server may be open; messages may be sent in either direction through any of the connections (a response to a query is not necessarily returned through the same connection that carried the original query, although most often, that is the case; however, in no case can a message be returned through a connection belonging to a different session). When the UDP protocol is used, a response might be returned by a different IP address than the one to which the query had been sent.</p>
|
|||
|
<p>There are several types of messages:</p>
|
|||
|
<ul>
|
|||
|
<li>RPC calls (client to server): calls to API methods</li>
|
|||
|
<li>RPC responses (server to client): results of RPC calls</li>
|
|||
|
<li>Message received acknowledgment (or rather, notification of status of a set of messages)</li>
|
|||
|
<li>Message status query</li>
|
|||
|
<li><em>Multipart message</em> or <em>container</em> (a container that holds several messages; needed to send several RPC calls at once over an HTTP connection, for example; also, a container may support gzip).</li>
|
|||
|
</ul>
|
|||
|
<p>From the standpoint of lower level protocols, a message is a binary data stream aligned along a 4 or 16-byte boundary. The first several fields in the message are fixed and are used by the cryptographic/authorization system.</p>
|
|||
|
<p>Each message, either individual or inside a container, consists of a <em>message identifier</em> (64 bits, see below), a <em>message sequence number within a session</em> (32 bits), the <em>length</em> (of the message body in bytes; 32 bits), and a <em>body</em> (any size which is a multiple of 4 bytes). In addition, when a container or a single message is sent, an <em>internal header</em> is added at the top (see below), then the entire message is encrypted, and an <em>external header</em> is placed at the top of the message (a 64-bit <em>key identifier</em> and a 128-bit <em>message key</em>).</p>
|
|||
|
<p>A <em>message body</em> normally consists of a 32-bit <em>message type</em> followed by type-dependent <em>parameters</em>. In particular, each RPC function has a corresponding message type. For more detail, see <a href="/mtproto/serialize">Binary Data Serialization</a>, <a href="/mtproto/service_messages">Mobile Protocol: Service Messages</a>.</p>
|
|||
|
<p>All numbers are written as little endian. However, very large numbers (2048-bit) used in RSA and DH are written in the big endian format because that is what the OpenSSL library does.</p>
|
|||
|
<h4><a class="anchor" name="authorization-and-encryption" href="#authorization-and-encryption"><i class="anchor-icon"></i></a>Authorization and Encryption</h4>
|
|||
|
<p>Prior to a message (or a multipart message) being transmitted over a network using a transport protocol, it is encrypted in a certain way, and an <em>external header</em> is added at the top of the message which is: a 64-bit <em>key identifier</em> (that uniquely identifies an <em>authorization key</em> for the server as well as the <em>user</em>) and a 128-bit <em>message key</em>. A user key together with the message key defines an actual 256-bit key which is what encrypts the message using AES-256 encryption. Note that the initial part of the message to be encrypted contains variable data (session, message ID, sequence number, server salt) that obviously influences the message key (and thus the AES key and iv). The message key is defined as the 128 lower-order bits of the SHA1 of the message body (including session, message ID, etc.). Multipart messages are encrypted as a single message.</p>
|
|||
|
<p>For a technical specification, see <a href="/mtproto/description">Mobile Protocol: Detailed Description</a><br>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 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 those data.</li>
|
|||
|
</ul>
|
|||
|
<h4><a class="anchor" name="time-synchronization" href="#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 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" name="transport" href="#transport"><i class="anchor-icon"></i></a>Transport</h3>
|
|||
|
<p>Enables the delivery of encrypted containers together with the external header (hereinafter, <em>Payload</em>) from client to server and back. There are three types of transport:</p>
|
|||
|
<ul>
|
|||
|
<li>HTTP</li>
|
|||
|
<li>TCP</li>
|
|||
|
<li>UDP</li>
|
|||
|
</ul>
|
|||
|
<p>We shall examine the first two types.</p>
|
|||
|
<h4><a class="anchor" name="http-transport" href="#http-transport"><i class="anchor-icon"></i></a>HTTP Transport</h4>
|
|||
|
<p>Implemented over HTTP/1.1 (with keepalive) running over the traditional TCP Port 80. HTTPS is not used; the above encryption method is used instead.</p>
|
|||
|
<p>An HTTP connection is attached to a session (or rather, to session + key identifier) specified in the most recent user query received; normally, the session is the same in all queries, but crafty HTTP proxies may corrupt that. A server may not return a message into an HTTP connection unless it belongs to the same session, and unless it is the server’s turn (an HTTP request had been received from the client to which a response has not been sent yet).</p>
|
|||
|
<p>The overall arrangement is as follows. The client opens one or more keepalive HTTP connections to the server. If one or more messages need to be sent, they are made into a <em>payload</em> which is followed by a POST request to the URL/api to which the payload is transmitted as data. In addition, <code>Content-Length</code>, <code>Keepalive</code>, and <code>Host</code> are valid HTTP headers.</p>
|
|||
|
<p>Having received the query, the server may either wait a little while (if the query requires a response following a short timeout) or immediately return a dummy response (only acknowledging the receipt of the container). In any case, the response may contain any number of messages. The server may at the same time send out any other messages it might be holding for the session.</p>
|
|||
|
<p>In addition, there exists a special long poll RPC query (valid for HTTP connections only) which transmits maximum timeout T. If the server has messages for the session, they are returned immediately; otherwise, a wait state is entered until such time as the server has a message for the client or T seconds have elapsed. If no events occur in the span of T seconds, a dummy response is returned (special message).</p>
|
|||
|
<p>If a server needs to send a message to a client, it checks for an HTTP connection that belongs to the required session and is in the “answering an HTTP request” state (including long poll) whereupon the message is added to the response container for the connection and sent to the user. In a typical case, there is some additional wait time (50 milliseconds) against the eventuality that the server will soon have more messages for the session.</p>
|
|||
|
<p>If no suitable HTTP connection is available, the messages are placed in the current session’s send queue. However, they find their way there anyway until receipt is explicitly or indirectly confirmed by the client. For the HTTP protocol, sending the next query into the same HTTP connection is regarded as an implicit acknowledgment <em>(not any more, the HTTP protocol also requires that explicit acknowledgments be sent)</em>; in other cases, the client must return an explicit acknowledgment within a reasonable time (it can be added to a container for the following request).</p>
|
|||
|
<p><strong><em>Important</em></strong>: if the acknowledgment fails to arrive on time, the message can be resent (possibly, in a different container). The parties must autonomously be ready for this and must store the identifiers of the most recent messages received (and ignore such duplicates rather than repeat actions). In order not to have the identifiers stored forever, there exist special <em>garbage collection</em> messages that take advantage of message identifier monotonicity.</p>
|
|||
|
<p>If the send queue overflows or if messages stay in the queue for over 10 minutes, the server forgets them (or sends them to <em>swap</em>, no genius required). This may happen even faster, if the server is running out of buffer space (for example, because of serious network issues resulting in a large number of connections becoming severed).</p>
|
|||
|
<h4><a class="anchor" name="tcp-transport" href="#tcp-transport"><i class="anchor-icon"></i></a>TCP Transport</h4>
|
|||
|
<p>This is very similar to the HTTP transport. May also be implemented over Port 80 (to penetrate all firewalls) and even use the same server IP addresses. In this situation, the server understands whether HTTP or TCP protocol must be used for the connection, based on the first four incoming bytes (for HTTP, it is POST).</p>
|
|||
|
<p>When a TCP connection is created, it is assigned to the session (and the authorization key) transmitted in the first user message, and subsequently used exclusively for this session (multiplexing arrangements are not allowed).</p>
|
|||
|
<p>If a payload (packet) needs to be transmitted from server to client or from client to server, it is encapsulated as follows: 4 length bytes are added at the front (to include the length, the sequence number, and CRC32; always divisible by 4) and 4 bytes with the packet sequence number within this TCP connection (the first packet sent is numbered 0, the next one 1, etc.), and 4 CRC32 bytes at the end (length, sequence number, and payload together).</p>
|
|||
|
<p>There is an <em>abridged</em> version of the same protocol: if the client sends <code>0xef</code> as the first byte (<strong>important:</strong> only prior to the very first data packet), then packet length is encoded by a single byte (<code>0x01..0x7e</code> = data length divided by 4; or <code>0x7f</code> followed by 3 length bytes (little endian) divided by 4) followed by the data themselves (sequence number and CRC32 not added). In this case, server responses look the same (the server does not send <code>0xef</code>as the first byte).</p>
|
|||
|
<p>In case 4-byte data alignment is needed, an <em>intermediate</em> version of the original protocol may be used: if the client sends <code>0xeeeeeeee</code> as the first int (four bytes), then packet length is encoded always by four bytes as in the original version, but the sequence number and CRC32 are omitted, thus decreasing total packet size by 8 bytes.</p>
|
|||
|
<p>The full, the intermediate and the abridged versions of the protocol have support for quick acknowledgment. In this case, the client sets the highest-order length bit in the query packet, and the server responds with a special 4 bytes as a separate packet. They are the 32 higher-order SHA1 bits of the encrypted portion of the packet with the most significant bit set to make clear that this is not the length of a regular server response packet; if the abridged version is used, bswap is applied to these four bytes.</p>
|
|||
|
<p>There are no implicit acknowledgments for the TCP transport: all messages must be acknowledged explicitly. Most frequently, acknowledgments are placed in a container with the next query or response if it is transmitted in short order. For example, this is almost always the case for client messages containing RPC queries: the acknowledgment normally arrives with the RPC response.</p>
|
|||
|
<p>In the event of an error, the server may send a packet whose payload consists of 4 bytes as the error code. For example, Error Code 403 corresponds to situations where the corresponding HTTP error would have been returned by the HTTP protocol.</p>
|
|||
|
</div>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="footer_wrap">
|
|||
|
<div class="footer_columns_wrap footer_desktop">
|
|||
|
<div class="footer_column footer_column_telegram">
|
|||
|
<h5>Telegram</h5>
|
|||
|
<div class="footer_telegram_description"></div>
|
|||
|
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="footer_column">
|
|||
|
<h5><a href="//telegram.org/faq">About</a></h5>
|
|||
|
<ul>
|
|||
|
<li><a href="//telegram.org/faq">FAQ</a></li>
|
|||
|
<li><a href="//telegram.org/blog">Blog</a></li>
|
|||
|
<li><a href="//telegram.org/jobs">Jobs</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<div class="footer_column">
|
|||
|
<h5><a href="//telegram.org/apps#mobile-apps">Mobile Apps</a></h5>
|
|||
|
<ul>
|
|||
|
<li><a href="//telegram.org/dl/ios">iPhone/iPad</a></li>
|
|||
|
<li><a href="//telegram.org/dl/android">Android</a></li>
|
|||
|
<li><a href="//telegram.org/dl/wp">Windows Phone</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<div class="footer_column">
|
|||
|
<h5><a href="//telegram.org/apps#desktop-apps">Desktop Apps</a></h5>
|
|||
|
<ul>
|
|||
|
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
|
|||
|
<li><a href="//macos.telegram.org/">macOS</a></li>
|
|||
|
<li><a href="//telegram.org/dl/web">Web-browser</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<div class="footer_column footer_column_platform">
|
|||
|
<h5><a href="/">Platform</a></h5>
|
|||
|
<ul>
|
|||
|
<li><a href="/api">API</a></li>
|
|||
|
<li><a href="//translations.telegram.org/">Translations</a></li>
|
|||
|
<li><a href="//instantview.telegram.org/">Instant View</a></li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="footer_columns_wrap footer_mobile">
|
|||
|
<div class="footer_column">
|
|||
|
<h5><a href="//telegram.org/faq">About</a></h5>
|
|||
|
</div>
|
|||
|
<div class="footer_column">
|
|||
|
<h5><a href="//telegram.org/blog">Blog</a></h5>
|
|||
|
</div>
|
|||
|
<div class="footer_column">
|
|||
|
<h5><a href="//telegram.org/apps">Apps</a></h5>
|
|||
|
</div>
|
|||
|
<div class="footer_column">
|
|||
|
<h5><a href="/">Platform</a></h5>
|
|||
|
</div>
|
|||
|
<div class="footer_column">
|
|||
|
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<script src="/js/main.js?46"></script>
|
|||
|
|
|||
|
<script>backToTopInit("Go up");
|
|||
|
removePreloadInit();
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
|