Chiffrement de bout en bout, Discussions secrètes

Cet article sur le chiffrement de bout en bout de MTProto est destiné aux utilisateurs avancés.Si vous souhaitez en savoir plus sur les Chats secrets d’une source moins intimidante, veuillez consulter notre FAQ générale.

Notez qu’à partir de la version 4.6, les principaux clients de Telegram utilisent MTProto 2.0.MTProto v.1.0 est obsolète et est actuellement en cours de suppression.

Les discussions secrètes sont des discussions individuelles dans lesquelles les messages sont cryptés avec une clé détenue uniquement par les participants au chat. Notez que le schéma de ces chats secrets cryptés de bout en bout est différent de celui utilisé pour les chats cloud :

Une note sur MTProto 2.0

Cet article décrit la couche de chiffrement de bout en bout dans le protocole MTProto version 2.0.Les principales différences par rapport à la version 1.0 (décrite ici pour référence) sont les suivantes :

  • SHA-256 est utilisé à la place de SHA-1;
  • Les octets de remplissage sont impliqués dans le calcul de msg_key;
  • msg_key dépend non seulement du message à chiffrer, mais également d’une partie de la clé de discussion secrète;
  • 12..1024 octets de remplissage sont utilisés au lieu de 0..15 octets de remplissage dans la version 1.0.

Voir aussi : MTProto 2.0 : Chats Cloud, chiffrement serveur-client

Génération de clés

Les clés sont générées à l’aide du protocole Diffie-Hellman.

Considérons le scénario suivant : L’utilisateur A souhaite initier une communication cryptée de bout en bout avec l’utilisateur B.

Envoi d’une requête

L’utilisateur A exécute les messages.getDhConfig pour obtenir les paramètres de Diffie-Hellman : un p premier et un élément d’ordre élevé g.

L’exécution de cette méthode avant chaque nouvelle procédure de génération de clé est d’une importance vitale. Il est logique de mettre en cache les valeurs des paramètres avec la version afin d’éviter d’avoir à recevoir toutes les valeurs à chaque fois. Si la version stockée sur le client est toujours à jour, le serveur renverra les messages du constructeur.Dhconfignotmodifié.

Le client est censé vérifier si p est un nombre premier sûr de 2048 bits (ce qui signifie que p et (p-1)/2 sont tous deux premiers, et que 2^2047 <p <2^2048), et que g génère un sous-groupe cyclique d’ordre premier (p-1)/2, c’est-à-dire est un résidu quadratique mod p. Puisque g est toujours égal à 2, 3, 4, 5, 6 ou 7, cela se fait facilement en utilisant la loi de réciprocité quadratique, ce qui donne une condition simple sur p mod 4g namely à savoir, p mod 8 = 7 pour g = 2; p mod 3 = 2 pour g = 3; pas de condition supplémentaire pour g = 4; p mod 5 = 1 ou 4 pour g = 5; p mod 24 = 19 ou 23 pour g = 6; et p mod 7 = 3, 5 ou 6 pour g = 7. Une fois que g et p ont été vérifiés par le client, il est logique de mettre en cache le résultat, afin d’éviter de répéter de longs calculs à l’avenir. Ce cache peut être partagé avec celui utilisé pour la génération de clés d’autorisation.

Si le client a un générateur de nombres aléatoires inadéquat, il est logique de passer le paramètre random_length (random_length >0) afin que le serveur génère sa propre séquence aléatoire aléatoire de la longueur appropriée.Important : l’utilisation de la séquence aléatoire du serveur sous sa forme brute peut être dangereuse. Il doit être combiné avec une séquence client, par exemple en générant un nombre aléatoire client de même longueur (client_random) et en utilisant final_random := random XOR client_random.

Le client A calcule un nombre a de 2048 bits (en utilisant une entropie suffisante ou l’aléatoire du serveur ; voir ci-dessus) et exécute les messages.requestEncryption après avoir passé g_a := pow(g, a) mod dh_prime.

L’utilisateur B reçoit la mise à jour updateEncryption pour toutes les clés d’autorisation associées (tous les périphériques autorisés) avec le constructeur de chat encryptedChatRequested. L’utilisateur doit recevoir des informations de base sur l’utilisateur A et doit être invité à accepter ou à rejeter la demande.

Les deux clients doivent vérifier que g, g_a et g_b sont supérieurs à un et inférieurs à p-1. Nous recommandons de vérifier que g_a et g_b sont également compris entre 2^{2048-64} et p-2^{2048-64}.

Acceptation d’une requête

Après que l’utilisateur B a confirmé la création d’un chat secret avec A dans l’interface client, le client B reçoit également des paramètres de configuration à jour pour la méthode Diffie-Hellman. Par la suite, il génère un nombre aléatoire de 2048 bits, b, en utilisant des règles similaires à celles de a.

Après avoir reçu g_a de la mise à jour avec encryptedChatRequested, il peut immédiatement générer la clé partagée finale : key = (pow(g_a, b) mod dh_prime). Si la longueur de la clé < 256 octets, ajoutez plusieurs octets zéro en tête comme remplissage — de sorte que la clé ait exactement 256 octets de long. Son empreinte digitale, key_fingerprint, est égale aux 64 derniers bits de SHA1 (clé).

Note 1: dans ce cas particulier, SHA1 est utilisé ici même pour les discussions secrètes MTProto 2.0.

Note 2: cette empreinte digitale est utilisée comme vérification de la santé mentale de la procédure d’échange de clés pour détecter les bogues lors du développement d’un logiciel client — elle n’est pas connectée à la visualisation des clés utilisée sur les clients comme moyen d’authentification externe dans les chats secrets. Les visualisations de clés sur les clients sont générées à l’aide des 128 premiers bits de SHA1 (clé initiale) suivis des 160 premiers bits de SHA256 (clé utilisée lors de la mise à jour du chat secret vers la couche 46).

Le client B exécute les messages.acceptEncryption après l’avoir passé g_b := pow(g, b) mod dh_prime et key_fingerprint.

Pour tous les périphériques autorisés du client B, à l’exception de celui en cours, les mises à jour updateEncryption sont envoyées avec le constructeur encryptedChatDiscarded. Par la suite, le seul appareil qui pourra accéder au chat secret est le périphérique B, qui a fait l’appel aux messages.Accepter le chiffrement.

L’utilisateur A recevra une mise à jour updateEncryption avec le constructeur encryptedChat, pour la clé d’autorisation qui a initié le chat.

Avec g_b de la mise à jour, le client A peut également calculer la clé partagée key = (pow(g_b, a) mod dh_prime). Si la longueur de la clé < 256 octets, ajoutez plusieurs octets zéro en tête comme remplissage — de sorte que la clé ait exactement 256 octets de long. Si l’empreinte digitale de la clé reçue est identique à celle transmise à encryptedChat, les messages entrants peuvent être envoyés et traités. Sinon, des messages.discardEncryption doit être exécuté et l’utilisateur averti.

Secret direct parfait

Afin de protéger les communications passées, les clients officiels de Telegram lanceront une nouvelle saisie une fois qu’une clé a été utilisée pour déchiffrer et chiffrer plus de 100 messages, ou a été utilisée pendant plus d’une semaine, à condition que la clé ait été utilisée pour chiffrer au moins un message. Les anciennes clés sont alors jetées en toute sécurité et ne peuvent pas être reconstruites, même avec l’accès aux nouvelles clés actuellement utilisées.

Le protocole de re-saisie est décrit plus en détail dans cet article : Perfect Forward Secrecy in Secret Chats.

Veuillez noter que votre client doit prendre en charge le secret direct dans les discussions secrètes pour être compatible avec les clients officiels de Telegram.

Envoi et réception de Messages dans un Chat Secret

Sérialisation et cryptage des Messages Sortants

Un objet TL de type DecryptedMessage est créé et contient le message en texte brut. Pour une compatibilité ascendante, l’objet doit être encapsulé dans le constructeur decryptedMessageLayer avec une indication de la couche prise en charge (en commençant par 46).

Le schéma TL pour le contenu des messages chiffrés de bout en bout est disponible ici « 

La construction résultante est sérialisée sous la forme d’un tableau d’octets à l’aide de règles TL génériques. Le tableau résultant est précédé de 4 octets contenant la longueur du tableau sans compter ces 4 octets.

Le tableau d’octets est rempli de 12 à 1024 octets de remplissage aléatoires pour rendre sa longueur divisible par 16 octets. (Dans l’ancien chiffrement MTProto 1.0, seuls 0 à 15 octets de remplissage étaient utilisés.)

La clé de message, msg_key, est calculée comme les 128 bits moyens du SHA256 des données obtenues à l’étape précédente, précédés de 32 octets à partir de la clé de clé partagée. (Pour l’ancien chiffrement MTProto 1.0, msg_key a été calculé différemment, comme les 128 bits inférieurs de SHA1 des données obtenues dans les étapes précédentes, à l’exclusion des octets de remplissage.)

Pour MTProto 2.0, la clé AES aes_key et le vecteur d’initialisation aes_iv sont calculés (la clé est la clé partagée obtenue lors de la génération de la clé) comme suit :

  • msg_key_large= SHA256 (substr(clé, 88 + x, 32) + texte en clair + ajout aléatoire);
  • msg_key = substr(msg_key_large, 8, 16);
  • sha256_a= SHA256(msg_key +substr(clé, x, 36));
  • sha256_b = SHA256(substr(clé, 40+x, 36)+msg_key);
  • aes_key =substr(sha256_a, 0, 8) + substr(sha256_b, 8,16) + substr(sha256_a, 24,8);
  • aes_iv = substr(sha256_b, 0, 8) + substr(sha256_a, 8,16) +substr(sha256_b, 24, 8);

Pour MTProto 2.0, x = 0 pour les messages de l’auteur du secret chat, x = 8 pour les messages dans la direction opposée.

Pour le MTProto obsolète 1.0, msg_key, aes_key et aes_iv ont été calculés différemment (voir ce document pour référence).

Les données sont chiffrées avec une clé de 256 bits, aes_key, et un vecteur d’initialisation de 256 bits, aes-iv, à l’aide du chiffrement AES-256 avec extension de garble infinie (GE). Clé de chiffrement empreinte digitale key_fingerprint et la clé de message msg_key sont ajoutées en haut du tableau d’octets résultant.

Les données chiffrées sont intégrées dans un message.Envoyez un appel API chiffré et transmis au serveur Telegram pour la livraison à l’autre partie du chat secret.

Mise à niveau vers MTProto 2.0 à partir de MTProto 1.0

Dès que les deux parties d’une conversation secrète utilisent au moins la couche 73, elles ne doivent utiliser MTProto 2.0 que pour tous les messages sortants. Certains des premiers messages reçus peuvent utiliser MTProto 1.0, si une couche de départ suffisamment élevée n’a pas été négociée lors de la création du chat secret. Une fois que le premier message chiffré avec MTProto 2.0 (ou le premier message avec la couche 73 ou supérieure) est reçu, tous les messages avec des numéros de séquence plus élevés doivent également être chiffrés avec MTProto 2.0.

Tant que la couche actuelle est inférieure à 73, chaque partie doit essayer de déchiffrer les messages reçus avec MTProto 1.0, et si cela ne réussit pas (msg_key ne correspond pas), essayez MTProto 2.0. Une fois que le premier message chiffré MTProto 2.0 arrive (ou que la couche est mise à niveau vers 73), il n’est pas nécessaire d’essayer le déchiffrement MTProto 1.0 pour les autres messages (sauf si le client attend toujours que certaines lacunes soient comblées).

Décryptage d’un Message entrant

Les étapes ci-dessus sont effectuées dans l’ordre inverse. Lorsqu’un message chiffré est reçu, vous devez vérifier que msg_key est en fait égal aux 128 bits intermédiaires du hachage SHA256 du message déchiffré, précédés de 32 octets pris dans la clé partagée.Si la couche de messages est supérieure à celle prise en charge par le client, l’utilisateur doit être informé que la version du client est obsolète et invité à la mettre à jour.

Numéros de séquence

Il est nécessaire d’interpréter tous les messages dans leur ordre d’origine pour se protéger d’éventuelles manipulations. Les chats secrets prennent en charge un mécanisme spécial pour gérer les compteurs seq_no indépendamment du serveur.

La bonne manipulation de ces compteurs est décrite plus en détail dans cet article: Numéros de séquence dans les discussions secrètes.

Veuillez noter que votre client doit prendre en charge les numéros de séquence dans les conversations secrètes pour être compatible avec les clients officiels de Telegram.

Envoi de fichiers cryptés

Tous les fichiers envoyés aux chats secrets sont cryptés avec des clés uniques qui ne sont en aucun cas liées à la clé partagée du chat. Avant l’envoi d’un fichier chiffré, il est supposé que l’adresse du fichier chiffré sera attachée à l’extérieur d’un message chiffré en utilisant le paramètre fichier des messages.Méthode sendEncryptedFile et que la clé pour le déchiffrement direct sera envoyée dans le corps du message (le paramètre key dans les constructeurs decryptedMessageMediaPhoto, decryptedMessageMediaVideo et decryptedMessageMediaFile.

Avant qu’un fichier ne soit envoyé à un chat secret, 2 nombres aléatoires de 256 bits sont calculés qui serviront de clé AES et de vecteur d’initialisation utilisés pour chiffrer le fichier. Le cryptage AES-256 avec extension de garble infinie (GE) est utilisé de la même manière.

L’empreinte digitale de la clé est calculée comme suit:

  • digest=md5(key + iv)
  • fingerprint=substr(digest, 0, 4) XOR substr(digest, 4, 4)

Le contenu crypté d’un fichier est stocké sur le serveur de la même manière que ceux d’un fichier dans les chats cloud: pièce par pièce en utilisant des appels à télécharger.Sauvez la partie.Un appel ultérieur aux messages.sendEncryptedFile attribuera un identifiant au fichier stocké et enverra l’adresse avec le message. Le destinataire recevra une mise à jour avec encryptedMessage et le paramètre file contiendra des informations sur le fichier.

Les fichiers cryptés entrants et sortants peuvent être transférés vers d’autres chats secrets à l’aide du constructeur inputEncryptedFile pour éviter d’enregistrer deux fois le même contenu sur le serveur.

Travailler avec une boîte de mise à jour

Les chats secrets sont associés à des périphériques spécifiques (ou plutôt à des clés d’autorisation), pas à des utilisateurs. Une boîte de message conventionnelle, qui utilise pts pour décrire l’état du client, ne convient pas, car elle est conçue pour le stockage de messages à long terme et l’accès aux messages à partir de différents appareils.

Une file d’attente de messages temporaire supplémentaire est introduite comme solution à ce problème. Lorsqu’une mise à jour concernant un message d’un chat secret est envoyée, une nouvelle valeur de qts est envoyée, ce qui permet de reconstituer la différence s’il y a eu une longue interruption de la connexion ou en cas de perte d’une mise à jour.

À mesure que le nombre d’événements augmente, la valeur des qts augmente de 1 à chaque nouvel événement. La valeur initiale peut ne pas (et ne sera pas) égale à 0.

Le fait que des événements de la file d’attente temporaire aient été reçus et stockés par le client est explicitement reconnu par un appel aux messages.Méthode receivedQueue ou implicitement par un appel aux mises à jour.getDifference (la valeur de qts passée, pas l’état final). Tous les messages reconnus comme délivrés par le client, ainsi que tous les messages de plus de 7 jours, peuvent (et seront) supprimés du serveur.

Lors de la désautorisation, la file d’attente d’événements du périphérique correspondant sera effacée de force et la valeur de qts deviendra non pertinente.

Mise à jour vers de nouvelles couches

Votre client doit toujours stocker la couche maximale qui est connue pour être prise en charge par le client de l’autre côté d’une conversation secrète. Lorsque le chat secret est créé pour la première fois, cette valeur doit être initialisée à 46. Cette valeur de couche distante doit toujours être mise à jour immédiatement après la réception de tout paquet contenant des informations d’une couche supérieure, i.e.:

  • tout message de chat secret contenant layer_no dans son decryptedMessageLayer avec layer >=46, ou
  • un message de service decryptedMessageActionNotifyLayer, enveloppé comme s’il s’agissait du constructeur decryptedMessageService de la couche 8 obsolète (constructeur decryptedMessageService#aa48327d).

Notifier le client distant de votre couche locale

Afin de notifier le client distant de votre couche locale, votre client doit envoyer un message du type decryptedMessageActionNotifyLayer. Cette notification doit être encapsulée dans un constructeur d’une couche appropriée.

Il y a deux cas où votre client doit informer le client distant de sa couche locale :

  1. Dès qu’un nouveau chat secret a été créé, immédiatement après que la clé secrète a été échangée avec succès.
  2. Immédiatement après la mise à jour du client local pour prendre en charge une nouvelle couche de discussion secrète. Dans ce cas, les notifications doivent être envoyées à tous les chats secrets actuellement existants. Notez que cela n’est nécessaire que lors de la mise à jour vers de nouvelles couches contenant des modifications de l’implémentation des chats secrets (par ex. vous n’avez pas besoin de le faire lorsque votre client est mis à jour de la couche 46 à la couche 47).

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.