CINXE.COM
Perfect Forward Secrecy
<!DOCTYPE html> <html class=""> <head> <meta charset="utf-8"> <title>Perfect Forward Secrecy</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta property="description" content="Telegram supports Perfect Forward Secrecy (PFS) in Secret Chats as of Layer 20. See updating to new layers. In order to…"> <meta property="og:title" content="Perfect Forward Secrecy"> <meta property="og:image" content=""> <meta property="og:description" content="Telegram supports Perfect Forward Secrecy (PFS) in Secret Chats as of Layer 20. See updating to new layers. In order to…"> <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?241" 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="active"><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"><ul class="breadcrumb clearfix"><li><a href="/api" >API</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/api/end-to-end" >Secret Chats</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/api/end-to-end%2Fpfs" >Perfect Forward Secrecy</a></li></ul></div> <h1 id="dev_page_title">Perfect Forward Secrecy</h1> <div id="dev_page_content"><blockquote> <p>Telegram supports Perfect Forward Secrecy (PFS) in Secret Chats as of Layer 20. See <a href="/api/end-to-end#updating-to-new-layers">updating to new layers</a>.</p> </blockquote> <p>In order to keep past communications safe, official Telegram clients will initiate re-keying once a key has been used to decrypt and encrypt more than 100 messages, or has been in use for more than one week, provided the key has been used to encrypt at least one message. Old keys are then securely discarded and cannot be reconstructed, even with access to the new keys currently in use.</p> <p>Any client participating in a Secret Chat can initiate re-keying as soon as it perceives that the current key has been used for too long or for encrypting too many messages. Please note that you should never initiate a new instance of the re-keying protocol if an uncompleted instance exists, initiated by either party.</p> <p><strong>Note:</strong> third-party developers are required to maintain the same level of security. All clients with secret chat support must be able to initiate re-keying and accept relevant service messages. See <a href="/mtproto/security_guidelines">Security Guidelines</a>.</p> <h3><a class="anchor" href="#re-keying-protocol" id="re-keying-protocol" name="re-keying-protocol"><i class="anchor-icon"></i></a>Re-keying protocol</h3> <p>New keys are generated by exchanging special messages, using previously established keys for encryption. The re-keying protocol between parties A and B normally consists of four steps:</p> <h5><a class="anchor" href="#1-decryptedmessageactionrequestkey" id="1-decryptedmessageactionrequestkey" name="1-decryptedmessageactionrequestkey"><i class="anchor-icon"></i></a>1. decryptedMessageActionRequestKey</h5> <p>A (re-keying initiator) generates a new value of <em>a</em>, subject to the same limitations as for the <a href="https://core.telegram.org/api/end-to-end#key-generation">initial Diffie-Hellman key exchange</a>, and sends the value of <em>pow(g,a)</em> to B, embedded in a <a href="/constructor/decryptedMessageService">decryptedMessageService</a>:</p> <pre><code><a href='/constructor/decryptedMessageActionRequestKey'>decryptedMessageActionRequestKey</a> exchange_id:<a href='/type/long'>long</a> g_a:<a href='/type/string'>string</a> = <a href='/type/DecryptedMessageAction'>DecryptedMessageAction</a>;</code></pre> <ul> <li><em>exchange_id</em> is a random number identifying this instance of the Re-Keying Protocol for both parties</li> <li><em>g_a</em> is the value of <em>pow(g,a) mod p</em></li> </ul> <p>Note that the same Diffie--Hellman parameters <em>(p,g)</em> as for the initial Diffie--Hellman key exchange in this secret chat are used. They do not need to be re-transmitted explicitly.</p> <h5><a class="anchor" href="#2-decryptedmessageactionacceptkey" id="2-decryptedmessageactionacceptkey" name="2-decryptedmessageactionacceptkey"><i class="anchor-icon"></i></a>2. decryptedMessageActionAcceptKey</h5> <p>Upon receipt of the above service message, B checks its content, and generates a response with same <em>exchange_id</em>, for a newly generated value of <em>b</em>:</p> <pre><code><a href='/constructor/decryptedMessageActionAcceptKey'>decryptedMessageActionAcceptKey</a> exchange_id:<a href='/type/long'>long</a> g_b:<a href='/type/string'>string</a> key_fingerprint:<a href='/type/long'>long</a> = <a href='/type/DecryptedMessageAction'>DecryptedMessageAction</a>;</code></pre> <ul> <li><em>exchange_id</em> is the same as in the received <a href="/constructor/decryptedMessageActionRequestKey">decryptedMessageActionRequestKey</a></li> <li><em>g_b</em> is the value of <em>pow(g,b) mod p</em></li> <li><em>key_fingerprint</em> is the 64-bit fingerprint of the newly generated <em>key = pow(g_a, b) mod p</em>, used as a sanity check of the implementation</li> </ul> <p>At this stage, B can already compute the new key <em>key</em> = <em>pow(g_a, b) mod p</em> and its <em>key_fingerprint</em> (last 64 bits of its SHA-1). However, it continues using the previous key until the completion of the exchange.</p> <p>Once side B sends <a href="/constructor/decryptedMessageActionAcceptKey">decryptedMessageActionAcceptKey</a>, it cannot abort the key exchange; it must be ready to switch to the new key immediately after a <code>decryptedMessageActionCommitKey</code> is received. Therefore, if side B wishes to delay the usage of new key, for example in order to fill some seq_no gaps first, it must delay the <code>decryptedMessageActionAcceptKey</code> answer accordingly.</p> <h5><a class="anchor" href="#3-decryptedmessageactioncommitkey" id="3-decryptedmessageactioncommitkey" name="3-decryptedmessageactioncommitkey"><i class="anchor-icon"></i></a>3. decryptedMessageActionCommitKey</h5> <p>Once A receives a valid <code>decryptedMessageActionAcceptKey</code>, it performs all necessary checks, and "commits" the new key by means of the following service message:</p> <pre><code><a href='/constructor/decryptedMessageActionCommitKey'>decryptedMessageActionCommitKey</a> exchange_id:<a href='/type/long'>long</a> key_fingerprint:<a href='/type/long'>long</a> = <a href='/type/DecryptedMessageAction'>DecryptedMessageAction</a>;</code></pre> <ul> <li><em>exchange_id</em> is the same as in the two previous messages</li> <li><em>key_fingerprint</em> is the value of the hash (last 64 bits of SHA-1) of the new key computed by A, for implementation sanity check</li> </ul> <p>After that, A can (and must) encrypt all following messages with the new key.</p> <p>If side A wishes to delay installation of the new key, for example because there are some seq_no gaps that it wants to fill first, it must delay <a href="/constructor/decryptedMessageActionCommitKey">decryptedMessageActionCommitKey</a> answer accordingly.</p> <h5><a class="anchor" href="#4-final-step" id="4-final-step" name="4-final-step"><i class="anchor-icon"></i></a>4. Final step</h5> <p>When B receives either a <code>decryptedMessageActionCommitKey</code> or a message encrypted by the new key, recognized by the value of <em>key_fingerprint</em> prepended to the encrypted message (it may happen that the <code>decryptedMessageActionCommitKey</code> has been lost and will be re-requested later), it assumes that A has started using the new key for encryption, and does the same.</p> <p>However, the previous key may be kept until there are no gaps in received messages up to the switch to the new key. Once all the gaps have been filled, the old key must be securely discarded.</p> <p>There is one exception to this rule — the SHA-1 of the original key (generated during the establishment of Secret Chat in question) is always stored, in order to show <a href="#key-visualization">key visualizations</a> on the clients.</p> <h4><a class="anchor" href="#aborting-protocol" id="aborting-protocol" name="aborting-protocol"><i class="anchor-icon"></i></a>Aborting protocol</h4> <p>Any of the parties may abort any instance of an uncompleted re-keying protocol, unless <code>decryptedMessageActionCommitKey</code> or <code>decryptedMessageActionAcceptKey</code> has been already sent by the party in question. In order to abort re-keying, send</p> <pre><code><a href='/constructor/decryptedMessageActionAbortKey'>decryptedMessageActionAbortKey</a> exchange_id:<a href='/type/long'>long</a> = <a href='/type/DecryptedMessageAction'>DecryptedMessageAction</a>;</code></pre> <p>This could be done, for example, if the party is already participating in a different instance of the re-keying protocol, or if the received values of <em>g_a</em>, <em>g_b</em> and other parameters do not pass security checks. In the latter case, it might be advisable to abort the Secret Chat altogether.</p> <h4><a class="anchor" href="#discarding-previous-keys" id="discarding-previous-keys" name="discarding-previous-keys"><i class="anchor-icon"></i></a>Discarding Previous Keys</h4> <p>Once B receives <code>decryptedMessageActionCommitKey</code>, it can safely discard the previous key provided there are no gaps. However, A may only discard the previous key after a message encrypted with the new key has been received. If no ordinary messages are scheduled to be sent, a special <a href="/constructor/decryptedMessageActionNoop">no-op message</a> should sent by B for this purpose:</p> <pre><code><a href='/constructor/decryptedMessageActionNoop'>decryptedMessageActionNoop</a> = <a href='/type/DecryptedMessageAction'>DecryptedMessageAction</a>;</code></pre> <h4><a class="anchor" href="#concurrent-re-keying" id="concurrent-re-keying" name="concurrent-re-keying"><i class="anchor-icon"></i></a>Concurrent Re-Keying</h4> <p>It may happen that both parties concurrently initiate re-keying by sending <code>decryptedMessageActionRequestKey</code> without knowing that the other party has already done so. If each side aborts re-keying because it is already participating in another instance of the protocol initiated by itself, the re-keying will never happen.</p> <p>Because of this possibility, we suggest that only the instance with the smaller <em>exchange_id</em> is aborted, with the option to re-use its <em>(a,g_a)</em> for the re-keying protocol instance with the larger <em>exchange_id</em> (when compared as a <code>long</code>, i.e. signed little-endian 64-bit integer).</p> <p>In other words, if a <code>decryptedMessageActionRequestKey</code> is received after A has sent its <code>decryptedMessageActionRequestKey</code>, but has not yet received <code>decryptedMessageActionAcceptKey</code>, the following is to be done:</p> <ul> <li>if <em>exchange_id</em> in the sent <code>decryptedMessageActionRequestKey</code> was larger than that in the <code>decryptionActionRequestKey</code> just received, abort the newly-suggested re-keying protocol instance without sending explicit <a href="/constructor/decryptedMessageActionAbortKey">decryptedMessageActionAbortKey</a> (the other side will do the same according to the next rule).</li> <li>if <em>exchange_id</em> in our <code>decryptedMessageActionRequestKey</code> was smaller, respond to the newly-received <code>decryptedMessageActionRequestKey</code> with a <code>decryptedMessageActionAcceptKey</code>, and participate only in the re-keying protocol instance initiated by the other side. It is possible to re-use at this stage the value of <em>g_a</em> (now called <em>g_b</em>) that was generated for the original <code>decryptedMessageActionRequestKey</code>, now abandoned, or totally new <em>(b,g_b)</em> can be generated. </li> <li>in the unlikely (2^{-64}) case both <em>exchange_id</em> are equal, abort both instances without sending an explicit <code>decryptedMessageActionAbortKey</code>. The other side will do the same.</li> </ul> <h5><a class="anchor" href="#key-visualization" id="key-visualization" name="key-visualization"><i class="anchor-icon"></i></a>Key Visualization</h5> <p>Since all re-keying instances are carried over the secure channel established when the secret chat is created, it is necessary for the user to confirm that no MITM attack had taken place during the initial exchange. The key visualization on the clients uses the first 128-bits of the SHA-1 of the original key created when the Secret Chat was first established, followed by the first 160 bits of the SHA-256 of the key in use when the secret chat was updated to layer 46 (coincides with the original key if chat was created using layer 46).</p> <blockquote> <p>Please note that the <em>key_fingerprint</em> parameter was introduced as a maintenance tool (with a misleading name) and is <strong>not</strong> related to key visualization on the clients.</p> </blockquote></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/privacy">Privacy</a></li> <li><a href="//telegram.org/press">Press</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/android">Android</a></li> <li><a href="//telegram.org/dl/web">Mobile Web</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="//telegram.org/press">Press</a></h5> </div> </div> </div> </div> <script src="/js/main.js?47"></script> <script>backToTopInit("Go up"); removePreloadInit(); </script> </body> </html> <!-- page generated in 9.62ms -->