Telegram supports Perfect Forward Secrecy (PFS) in Secret Chats as of Layer 20. See updating to new layers.
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.
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.
Note: 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 Security Guidelines.
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:
A (re-keying initiator) generates a new value of a, subject to the same limitations as for the initial Diffie-Hellman key exchange, and sends the value of pow(g,a) to B, embedded in a decryptedMessageService:
decryptedMessageActionRequestKey exchange_id:long g_a:string = DecryptedMessageAction;
Note that the same Diffie--Hellman parameters (p,g) as for the initial Diffie--Hellman key exchange in this secret chat are used. They do not need to be re-transmitted explicitly.
Upon receipt of the above service message, B checks its content, and generates a response with same exchange_id, for a newly generated value of b:
decryptedMessageActionAcceptKey exchange_id:long g_b:string key_fingerprint:long = DecryptedMessageAction;
At this stage, B can already compute the new key key = pow(g_a, b) mod p and its key_fingerprint (last 64 bits of its SHA-1). However, it continues using the previous key until the completion of the exchange.
Once side B sends decryptedMessageActionAcceptKey, it cannot abort the key exchange; it must be ready to switch to the new key immediately after a decryptedMessageActionCommitKey
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 decryptedMessageActionAcceptKey
answer accordingly.
Once A receives a valid decryptedMessageActionAcceptKey
, it performs all necessary checks, and "commits" the new key by means of the following service message:
decryptedMessageActionCommitKey exchange_id:long key_fingerprint:long = DecryptedMessageAction;
After that, A can (and must) encrypt all following messages with the new key.
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 decryptedMessageActionCommitKey answer accordingly.
When B receives either a decryptedMessageActionCommitKey
or a message encrypted by the new key, recognized by the value of key_fingerprint prepended to the encrypted message (it may happen that the decryptedMessageActionCommitKey
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.
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.
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 key visualizations on the clients.
Any of the parties may abort any instance of an uncompleted re-keying protocol, unless decryptedMessageActionCommitKey
or decryptedMessageActionAcceptKey
has been already sent by the party in question. In order to abort re-keying, send
decryptedMessageActionAbortKey exchange_id:long = DecryptedMessageAction;
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 g_a, g_b and other parameters do not pass security checks. In the latter case, it might be advisable to abort the Secret Chat altogether.
Once B receives decryptedMessageActionCommitKey
, 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 no-op message should sent by B for this purpose:
decryptedMessageActionNoop = DecryptedMessageAction;
It may happen that both parties concurrently initiate re-keying by sending decryptedMessageActionRequestKey
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.
Because of this possibility, we suggest that only the instance with the smaller exchange_id is aborted, with the option to re-use its (a,g_a) for the re-keying protocol instance with the larger exchange_id (when compared as a long
, i.e. signed little-endian 64-bit integer).
In other words, if a decryptedMessageActionRequestKey
is received after A has sent its decryptedMessageActionRequestKey
, but has not yet received decryptedMessageActionAcceptKey
, the following is to be done:
decryptedMessageActionRequestKey
was larger than that in the decryptionActionRequestKey
just received, abort the newly-suggested re-keying protocol instance without sending explicit decryptedMessageActionAbortKey (the other side will do the same according to the next rule).decryptedMessageActionRequestKey
was smaller, respond to the newly-received decryptedMessageActionRequestKey
with a decryptedMessageActionAcceptKey
, 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 g_a (now called g_b) that was generated for the original decryptedMessageActionRequestKey
, now abandoned, or totally new (b,g_b) can be generated. decryptedMessageActionAbortKey
. The other side will do the same.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).
Please note that the key_fingerprint parameter was introduced as a maintenance tool (with a misleading name) and is not related to key visualization on the clients.