Telegram Passport data is stored encrypted End-to-End which means that the Telegram server does not have access to the data and only functions as a storage for encrypted data it can't decipher. Encryption and decryption are handled exclusively by the Telegram clients, which are open source.
To encrypt each particular element of Telegram Passport, the client generates a random secret. The secret is a 32-byte number with the modulo 255 sum of bytes equal to 239. This secret is in turn encrypted with the passport_secret that is generated when the user creates their Telegram Passport. passport_secret is encrypted with the user's password and is stored encrypted in the Telegram Cloud.
When Telegram Passport is first used, the client generates a passport_secret (a 32-byte number with the modulo 255 sum of bytes equal to 239), using a part of server-generated random passport_secret_random as an additional source of entropy for OpenSSL. Then passport_secret is encrypted using the following scheme:
A fingerprint of the secret is calculated ( passport_secret_fingerprint ):
passport_secret_fingerprint = long( slice( SHA256( passport_secret ), 0, 8 ) )
The encryption key secret_key is calculated using 8 bytes of the server salt server_passport_salt and 32 bytes of the client salt client_passport_salt. To make the password hashes stored on the server more resilient to brute-force attacks while maintaining practical speeds on the range of devices popular among Telegram users, PBKDF2-HMAC-SHA512 with 100000 iterations is used:
client_passport_salt = random_bytes(32) passport_secret_salt = server_passport_salt + client_passport_salt password_hash = PBKDF2( password, passport_secret_salt, HMACSHA512, 100000) secret_key = slice( password_hash, 0, 32 ) iv = slice( password_hash, 32, 16 )
The passport_secret generated previously is encrypted using AES256-CBC with the key secret_key and iv
Subsequently, the client receives the encrypted passport_secret from the server and decrypts it after the user enters their password.
In case the password is changed, the client re-encrypted the passport_secret using the new password. If the password is disabled, all Telegram Passport data is lost.
To encrypt Telegram Passport data, the client generates a data_secret (a 32-byte number with the modulo 255 sum of bytes equal to 239). The the data is encrypted according to the following scheme:
We calculate the hash from this data data_hash:
data_hash = SHA256( data_bytes )
The encryption key data_key is calculated:
data_secret_hash = SHA512( data_secret + data_hash ) data_key = slice( data_secret_hash, 0, 32 ) iv = slice( data_secret_hash, 32, 16 )
Data is encrypted using AES256-CBC with the key data_key and iv.
secret_key, the key for encrypting the secret, is calculated:
secret_hash = SHA512( passport_secret + data_hash ) secret_key = slice( secret_hash, 0, 32 ) iv = slice( secret_hash, 32, 16 )
data_secret is encrypted using AES256-CBC with the key secret_key and iv
When a service requests data, it passes a nonce to the client. The nonce is a cryptographically secure unique identifier which allows the service to identify a request when receiving data as well as confirm the integrity of the data. The Telegram server doesn't have access to this nonce.
Once the user authorizes the Telegram Passport data transfer, the client forms the credentials. Credentials contain the data_hash and data_secret from each element of Telegram Passport to which the user has allowed access. In addition to this, the credentials will always contain the nonce that the client received from the service at the initiation of the request.
Credentials are then passed to the service through the Bot API in encrypted form. To encrypt the credentials, the client generates a credentials_secret (a 32-byte number with the modulo 255 sum of bytes equal to 239). Then the credentials are encrypted according to the following scheme:
A hash of the padded credentials credentials_hash is calculated:
credentials_hash = SHA256( credentials )
The encryption key credentials_key is calculated:
credentials_secret_hash = SHA512( credentials_secret + credentials_hash ) credentials_key = slice( credentials_secret_hash, 0, 32 ) iv = slice( credentials_secret_hash, 32, 16 )
Credentials are encrypted using AES256-CBC with the key credentials_key and iv.
Then the service decrypts the data as described here.