<metaproperty="description"content="Following the launch of version 4.2 of the official apps, Telegram client apps may be required to download popular files…">
<metaproperty="og:title"content="Encrypted CDNs for Speed and Security">
<metaproperty="og:description"content="Following the launch of version 4.2 of the official apps, Telegram client apps may be required to download popular files…">
<divid="dev_page_content"><p>Following the launch of version 4.2 of the official apps, Telegram client apps may be required to download popular files that were published in public channels with more than <strong>100,000</strong> members from secondary <ahref="https://en.wikipedia.org/wiki/Content_delivery_network">Content Delivery Network</a> data centers. These CDN DCs are located in regions with significant Telegram traffic where we wouldn't want to place Telegram servers for various reasons.</p>
<p>The CDN DCs are not a part of the Telegram cloud and should be considered enemy territory. For this reason, each file that is to be sent to these CDN DCs is encrypted with a unique key using AES-256-CTR encryption. The CDN <strong>can't access the data</strong> it stores because these keys are only accessible to the main MTProto server and to the authorized client.</p>
<p>When a file from a public channel with <strong>~100,000</strong> members becomes popular in a particular region, the Telegram server may encrypt this file with a unique AES-256-CTR key and send it to a relevant CDN DC for storage.</p>
<p>When a file is stored in a CDN DC close to the end user, the download speed will be much higher because the data needs to travel smaller distances and will likely avoid many bottlenecks that exist between regions.</p>
<p>This is secure because CDN DCs are treated the same way as internet providers / random third parties:</p>
<li>Encrypted files fragments are protected from tampering by their SHA-256 hash which is checked on the client upon receipt.</li>
<li>No private data is stored in or passed to the CDN DCs.</li>
<li>The server only allows media from public channels with more than <strong>100,000</strong> subscribers to be cached in CDN DCs (this includes media forwarded from those channels and viral media that originated from other large public channels).</li>
<p>CDNs are very limited when it comes to communication: the master data center only uploads encrypted files for storage and will accept no data from the CDN. The client apps only download encrypted files and accept no other updates. The client apps obtain the keys necessary to decrypt the file from the main Telegram server and verify the integrity of the file by its hash, which means that the CDN may only supply the correct file – anything different will be immediately discarded by the client.</p>
<p>CDN DCs do not store files on hard disks – only in memory. When a CDN server runs out of memory, a simple LRU algorithm is used to replace the least popular files with new ones.</p>
<h3><aclass="anchor"name="how-cdn-dcs-are-different-from-the-master-dcs"href="#how-cdn-dcs-are-different-from-the-master-dcs"><iclass="anchor-icon"></i></a>How CDN DCs are different from the master DCs</h3>
<li><strong>CDNs may not be trusted.</strong></li>
<li>Client developers can use <ahref="/method/help.getCdnConfig">help.getCdnConfig</a> to obtain a list of public RSA keys for CDN DCs, which are different from public RSA keys of the master DCs.</li>
<li>CDNs support only the following methods: <ahref="/method/upload.getCdnFile">upload.getCdnFile</a>, <ahref="/method/initConnection">initConnection</a>, <ahref="/method/invokeWithLayer">invokeWithLayer</a>.</li>
<li>When working with CDNs, client developers must remember that auth_key may be deleted at any given moment (resulting in a -404 error, in which case a new key must be generated).</li>
<p>The API may return the <ahref="/constructor/upload.fileCdnRedirect">upload.fileCdnRedirect</a> constructor after an <ahref="/method/upload.getFile">upload.getFile</a> query. In this case, the client must request the required file from a CDN DC. The <em>dc_id</em> in the response is the id of the new CDN. The IP address for the connection will be available in <ahref="/method/help.getConfig">help.getConfig</a>, same as with the master DCs. The corresponding <ahref="/constructor/dcOption">dcOption</a> will have the flag <em>cdn:flags.3?true</em>.</p>
<p>Once a successful connection to the CDN-dc_id is established, the client must generate an auth_key (after confirming that the public RSA MTProto key of the CDN DC matches one from the list returned in <ahref="/method/help.getCdnConfig">help.getCdnConfig</a>). Then the client must perform an <ahref="/method/upload.getCdnFile">upload.getCdnFile</a> for each <em>offset</em>. For files of an unknown size it is necessary to repeat the query until an empty reply is returned.</p>
<p><ahref="/method/upload.getCdnFile">upload.getCdnFile</a> may return the <ahref="/constructor/upload.cdnFileReuploadNeeded">upload.cdnFileReuploadNeeded</a> constructor. In this case, the client needs to send an <ahref="/method/upload.reuploadCdnFile">upload.reuploadCdnFile</a> request to the DC that got the original <ahref="/method/upload.getFile">upload.getFile</a> request. Once <ahref="/method/upload.reuploadCdnFile">upload.reuploadCdnFile</a> is successfull, the app needs to request the file from the CDN DC again.</p>
<p>The main DC for a file is the DC where its main copy is stored (not to be confused with the main DC of the user) – either <em>userProfilePhoto.dc_id</em>, <em>chatPhoto.dc_id</em>, <em>photo.dc_id</em>, or <em>document.dc_id</em>.</p>
<p>In <ahref="/constructor/upload.fileCdnRedirect">upload.fileCdnRedirect</a>, the server sends a decryption key and IV for the file (the fields <code>encryption_key:bytes</code> and <code>encryption_iv:bytes</code> respectively).</p>
<p>Having received a portion of encrypted data from the CDN DC inside <ahref="/constructor/upload.cdnFile">upload.cdnFile</a>, the client must decrypt this data using <code>AES-256-CTR</code>. For IV, it should use the value of <code>encryption_iv</code>, modified in the following manner: for each <code>offset</code> replace the last 4 bytes of the <code>encryption_iv</code> with <code>offset / 16</code> in big-endian. This allows to effectively decrypt a file and to use random access to a file's content (e.g., for streaming).</p>
<p>In order to confirm that the CDN DC passed an untampered file, clients must verify hashes for each downloaded part. <ahref="/constructor/upload.fileCdnRedirect">upload.fileCdnRedirect</a>, <ahref="/method/upload.reuploadCdnFile">upload.reuploadCdnFile</a> and <ahref="/method/upload.getCdnFileHashes">upload.getCdnFileHashes</a> contain <ahref="/type/FileHash">FileHash</a> constructors. Each of these constructors contains the SHA-256 hash of a part of the file that starts with <code>offset</code> and takes <code>limit</code> bytes.</p>
<p>Before saving each portion of the data received from the CDN DC into the file, the client must confirm that its hash matches the hash that was received from the master DC. If missing a hash for any file part, client developers must use the <ahref="/method/upload.getCdnFileHashes">upload.getCdnFileHashes</a> method to obtain the missing hash.</p>
<h3><aclass="anchor"name="restrictions-on-upload-getfile-and-upload-getcdnfile-parameters"href="#restrictions-on-upload-getfile-and-upload-getcdnfile-parameters"><iclass="anchor-icon"></i></a>Restrictions on upload.getFile and upload.getCdnFile parameters</h3>
<li><code>offset / (1024 * 1024) == (offset + limit - 1) / (1024 * 1024)</code><br>(file parts that are being downloaded must always be inside the same megabyte-sized fragment)</li>
<h3><aclass="anchor"name="possible-errors-and-their-meanings"href="#possible-errors-and-their-meanings"><iclass="anchor-icon"></i></a>Possible errors and their meanings</h3>
<td>The CDN DC did not accept the <code>file_token</code> (e.g., the token has expired). Continue downloading the file from the master DC using upload.getFile.</td>
</tr>
<tr>
<td>upload.reuploadCdnFile</td>
<td>FILE_TOKEN_INVALID</td>
<td>The master DC did not accept the <code>file_token</code> (e.g., the token has expired). Continue downloading the file from the master DC using upload.getFile.</td>
</tr>
<tr>
<td>upload.reuploadCdnFile</td>
<td>REQUEST_TOKEN_INVALID</td>
<td>The master DC did not accept the <code>request_token</code> from the CDN DC. Continue downloading the file from the master DC using upload.getFile.</td>