Dieser Artikel über die Ende-zu-Ende-Verschlüsselung von MTProto ist für fortgeschrittene Benutzer gedacht.Wenn Sie mehr über geheime Chats aus einer weniger einschüchternden Quelle erfahren möchten, lesen Sie bitte unsere allgemeinen FAQ.
Beachten Sie, dass ab Version 4.6 die wichtigsten Telegramm-Clients MTProto 2.0 verwenden.MTProto v.1.0 ist veraltet und wird derzeit auslaufen.
Geheime Chats sind Einzelgespräche, bei denen Nachrichten mit einem Schlüssel verschlüsselt werden, der nur von den Chat-Teilnehmern gehalten wird. Beachten Sie, dass sich das Schema für diese Ende-zu-Ende verschlüsselten geheimen Chats von dem unterscheidet, was für Cloud-Chats verwendet wird:
Ein Hinweis zu MTProto 2.0
Dieser Artikel beschreibt die Ende-zu-Ende-Verschlüsselungsschicht im MTProto-Protokoll Version 2.0.Die Hauptunterschiede zu Version 1.0 (hier als Referenz beschrieben) sind wie folgt:
- SHA-256 wird anstelle von SHA-1 verwendet;
- Padding-Bytes sind an der Berechnung von msg_key beteiligt;
- msg_key hängt nicht nur von der zu verschlüsselnden Nachricht ab, sondern auch von einem Teil des geheimen Chat-Schlüssels;
- 12..Anstelle von 0 werden 1024 Padding-Bytes verwendet..15 Auffüllbytes in v.1.0.
Siehe auch: MTProto 2.0: Cloud-Chats, Server-Client-Verschlüsselung
Schlüsselgenerierung
Schlüssel werden mit dem Diffie-Hellman-Protokoll generiert.
Betrachten wir folgendes Szenario: Benutzer A möchte eine Ende-zu-Ende-verschlüsselte Kommunikation mit Benutzer B initiieren.
Senden einer Anfrage
Benutzer A führt Nachrichten aus.getDhConfig , um die Diffie-Hellman-Parameter zu erhalten: eine Primzahl p und ein Element höherer Ordnung g.
Die Ausführung dieser Methode vor jeder neuen Schlüsselgenerierungsprozedur ist von entscheidender Bedeutung. Es ist sinnvoll, die Werte der Parameter zusammen mit der Version zwischenzuspeichern, um zu vermeiden, dass jedes Mal alle Werte empfangen werden müssen. Wenn die auf dem Client gespeicherte Version noch aktuell ist, gibt der Server die Konstruktornachrichten zurück.dhConfigNotModified.
Vom Client wird erwartet, dass er prüft, ob p eine sichere 2048-Bit-Primzahl ist (was bedeutet, dass sowohl p als auch (p-1)/2 Primzahlen sind und dass 2^2047 < p < 2^2048 ), und dass g eine zyklische Untergruppe der Primzahlordnung (p-1)/2 erzeugt, dh eine quadratische rest mod p. Da g immer gleich 2, 3, 4, 5, 6 oder 7 ist, kann dies leicht unter Verwendung des quadratischen Reziprozitätsgesetzes erfolgen, was eine einfache Bedingung für p mod 4g ergibt – nämlich p mod 8 = 7 für g = 2; p mod 3 = 2 für g = 3; keine zusätzliche Bedingung für g = 4; p mod 5 = 1 oder 4 für g = 5; p mod 24 = 19 oder 23 für g = 6; und p mod 7 = 3, 5 oder 6 für g = 7. Nachdem g und p vom Client überprüft wurden, ist es sinnvoll, das Ergebnis zwischenzuspeichern, um in Zukunft keine langwierigen Berechnungen mehr zu wiederholen. Dieser Cache kann mit einem Cache geteilt werden, der für die Autorisierungsschlüsselgenerierung verwendet wird.
Wenn der Client über einen unzureichenden Zufallszahlengenerator verfügt, ist es sinnvoll, den Parameter random_length (random_length> 0) zu übergeben, damit der Server seine eigene Zufallssequenz generiert zufällig der entsprechenden Länge.Wichtig: Die Verwendung der Zufallssequenz des Servers in seiner Rohform kann unsicher sein. Es muss mit einer Client-Sequenz kombiniert werden, indem beispielsweise eine Client-Zufallszahl gleicher Länge (client_random ) generiert und final_random := random XOR client_random
.
Client A berechnet eine 2048-Bit-Zahl a (mit ausreichender Entropie oder dem Zufall des Servers; siehe oben) und führt Nachrichten aus.Anforderungsverschlüsselung nach Übergabe von g_a := pow(g, a) mod dh_prime
.
Benutzer B erhält das Update updateEncryption für alle zugehörigen Autorisierungsschlüssel (alle autorisierten Geräte) mit dem Chat-Konstruktor encryptedChatRequested. Dem Benutzer müssen grundlegende Informationen zu Benutzer A angezeigt werden und er muss aufgefordert werden, die Anforderung anzunehmen oder abzulehnen.
Beide Clients sollen prüfen, ob g, g_a und g_b größer als eins und kleiner als p-1 sind. Wir empfehlen zu überprüfen, ob g_a und g_b auch zwischen 2 ^ {2048-64} und p – 2 ^{2048-64} liegen.
Annehmen einer Anfrage
Nachdem Benutzer B die Erstellung eines geheimen Chats mit A in der Client-Schnittstelle bestätigt hat, erhält Client B auch aktuelle Konfigurationsparameter für die Diffie-Hellman-Methode. Danach erzeugt es eine zufällige 2048-Bit-Zahl, b, mit Regeln ähnlich denen für a.
Nachdem es g_a vom Update mit encryptedChatRequested erhalten hat, kann es sofort den endgültigen gemeinsam genutzten Schlüssel generieren: key = (pow(g_a, b) mod dh_prime)
. Wenn die Schlüssellänge < 256 Byte beträgt, fügen Sie mehrere führende Nullbytes als Auffüllung hinzu — so dass der Schlüssel genau 256 Byte lang ist. Sein Fingerabdruck, key_fingerprint, ist gleich den 64 letzten Bits von SHA1 (Schlüssel).
Anmerkung 1: In diesem speziellen Fall wird SHA1 hier sogar für MTProto 2.0 geheime Chats verwendet.
Anmerkung 2: dieser Fingerabdruck wird als Überprüfung der Vernunft für das Schlüsselaustauschverfahren verwendet, um Fehler bei der Entwicklung von Client-Software zu erkennen – er ist nicht mit der Schlüsselvisualisierung verbunden, die auf den Clients als Mittel zur externen Authentifizierung in geheimen Chats verwendet wird. Schlüsselvisualisierungen auf den Clients werden mit den ersten 128 Bits von SHA1 (Initialschlüssel) generiert, gefolgt von den ersten 160 Bits von SHA256 (Schlüssel, der verwendet wurde, als Secret Chat auf Layer 46 aktualisiert wurde).
Client B führt Nachrichten aus.Akzeptierenverschlüsselung nach Übergabe g_b := pow(g, b) mod dh_prime
und key_fingerprint.
Für alle autorisierten Geräte von Client B, mit Ausnahme des aktuellen, werden updateEncryption-Updates mit dem Konstruktor encryptedChatDiscarded gesendet. Danach ist das einzige Gerät, das auf den geheimen Chat zugreifen kann, Gerät B, das den Anruf an Nachrichten getätigt hat.Akzeptierenverschlüsselung.
Benutzer A erhält ein updateEncryption-Update mit dem Konstruktor encryptedChat für den Autorisierungsschlüssel, der den Chat initiiert hat.
Mit g_b aus dem Update kann Client A auch den gemeinsam genutzten Schlüssel key = (pow(g_b, a) mod dh_prime)
berechnen. Wenn die Schlüssellänge < 256 Byte beträgt, fügen Sie mehrere führende Nullbytes als Auffüllung hinzu — so dass der Schlüssel genau 256 Byte lang ist. Wenn der Fingerabdruck für den empfangenen Schlüssel identisch mit dem ist, der an encryptedChat übergeben wurde, können eingehende Nachrichten gesendet und verarbeitet werden. Andernfalls Nachrichten.discardEncryption muss ausgeführt und der Benutzer benachrichtigt werden.
Perfect Forward Secrecy
Um die Kommunikation in der Vergangenheit zu schützen, initiieren offizielle Telegramm-Clients eine erneute Eingabe, sobald ein Schlüssel zum Entschlüsseln und Verschlüsseln von mehr als 100 Nachrichten verwendet wurde oder seit mehr als einer Woche verwendet wird, vorausgesetzt, der Schlüssel wurde zum Verschlüsseln mindestens einer Nachricht verwendet. Alte Schlüssel werden dann sicher verworfen und können nicht rekonstruiert werden, auch nicht mit Zugriff auf die derzeit verwendeten neuen Schlüssel.
Das Re-Keying-Protokoll wird in diesem Artikel weiter beschrieben: Perfect Forward Secrecy in geheimen Chats.
Bitte beachten Sie, dass Ihr Client Forward Secrecy in geheimen Chats unterstützen muss, um mit offiziellen Telegramm-Clients kompatibel zu sein.
Senden und Empfangen von Nachrichten in einem geheimen Chat
Serialisierung und Verschlüsselung ausgehender Nachrichten
Es wird ein TL-Objekt vom Typ DecryptedMessage erstellt, das die Nachricht im Klartext enthält. Aus Gründen der Abwärtskompatibilität muss das Objekt in den Konstruktor decryptedMessageLayer mit einer Angabe der unterstützten Ebene (beginnend mit 46) eingeschlossen werden.
Das TL-Schema für den Inhalt ende-zu-Ende verschlüsselter Nachrichten finden Sie hier „
Das resultierende Konstrukt wird als Array von Bytes unter Verwendung generischer TL-Regeln serialisiert. Dem resultierenden Array werden 4 Bytes vorangestellt, die die Array-Länge enthalten, ohne diese 4 Bytes zu zählen.
Das Byte-Array wird mit 12 bis 1024 zufälligen Padding-Bytes aufgefüllt, um seine Länge durch 16 Bytes teilbar zu machen. (In der älteren MTProto 1.0-Verschlüsselung wurden nur 0 bis 15 Padding-Bytes verwendet.)
Der Nachrichtenschlüssel msg_key wird als die 128 mittleren Bits des SHA256 der im vorherigen Schritt erhaltenen Daten berechnet, denen 32 Bytes aus dem gemeinsam genutzten Schlüssel key vorangestellt sind. (Für die ältere MTProto 1.0-Verschlüsselung wurde msg_key anders berechnet als die 128 unteren Bits von SHA1 der Daten, die in den vorherigen Schritten erhalten wurden, mit Ausnahme der Padding-Bytes.)
Für MTProto 2.0 werden der AES-Schlüssel aes_key und der Initialisierungsvektor aes_iv (Schlüssel ist der gemeinsam genutzte Schlüssel, der während der Schlüsselgenerierung erhalten wird) wie folgt berechnet:
- msg_key_large = SHA256 (substr (key, 88+x, 32) + plaintext + random_padding);
- msg_key = substr (msg_key_large, 8, 16);
- sha256_a = SHA256 (msg_key + substr (Schlüssel, x, 36));
- sha256_b = SHA256 (substr (Schlüssel, 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);
Für MTProto 2.0, x=0 für nachrichten vom Absender des geheimen Chats, x = 8 für die Nachrichten in die entgegengesetzte Richtung.
Für das veraltete MTProto 1.0, msg_key, aes_key und aes_iv wurden unterschiedlich berechnet (siehe dieses Dokument als Referenz).
Daten werden mit einem 256-Bit-Schlüssel, aes_key, und einem 256-Bit-Initialisierungsvektor, aes-iv, unter Verwendung der AES-256-Verschlüsselung mit Infinite Garble Extension (IGE) verschlüsselt. Verschlüsselungsschlüssel Fingerabdruck key_fingerprint und der Nachrichtenschlüssel msg_key werden am Anfang des resultierenden Byte-Arrays hinzugefügt.
Verschlüsselte Daten werden in eine Nachricht eingebettet.Sendenverschlüsselter API-Aufruf und Weitergabe an den Telegrammserver zur Zustellung an die andere Partei des geheimen Chats.
Upgrade auf MTProto 2.0 von MTProto 1.0
Sobald beide Parteien in einem geheimen Chat mindestens Layer 73 verwenden, sollten sie nur MTProto 2.0 für alle ausgehenden Nachrichten verwenden. Einige der ersten empfangenen Nachrichten können MTProto 1.0 verwenden, wenn bei der Erstellung des geheimen Chats keine ausreichend hohe Startschicht ausgehandelt wurde. Nachdem die erste mit MTProto 2.0 verschlüsselte Nachricht (oder die erste Nachricht mit Layer 73 oder höher) empfangen wurde, müssen alle Nachrichten mit höheren Sequenznummern ebenfalls mit MTProto 2.0 verschlüsselt werden.
Solange die aktuelle Ebene niedriger als 73 ist, sollte jede Partei versuchen, empfangene Nachrichten mit MTProto 1.0 zu entschlüsseln, und wenn dies nicht erfolgreich ist (msg_key stimmt nicht überein), versuchen Sie MTProto 2.0. Sobald die erste MTProto 2.0-verschlüsselte Nachricht eintrifft (oder die Schicht auf 73 aktualisiert wurde), muss die MTProto 1.0-Entschlüsselung für keine der weiteren Nachrichten mehr versucht werden (es sei denn, der Client wartet noch darauf, dass einige Lücken geschlossen werden).
Entschlüsseln einer eingehenden Nachricht
Die obigen Schritte werden in umgekehrter Reihenfolge ausgeführt. Wenn eine verschlüsselte Nachricht empfangen wird, müssen Sie überprüfen, ob msg_key tatsächlich den 128 mittleren Bits des SHA256-Hash der entschlüsselten Nachricht entspricht, denen 32 Bytes aus dem gemeinsam genutzten Schlüssel vorangestellt sind.Wenn die Nachrichtenschicht größer ist als die vom Client unterstützte, muss der Benutzer benachrichtigt werden, dass die Clientversion veraltet ist, und zur Aktualisierung aufgefordert werden.
Sequenznummern
Zum Schutz vor möglichen Manipulationen ist es notwendig, alle Nachrichten in ihrer ursprünglichen Reihenfolge zu interpretieren. Geheime Chats unterstützen einen speziellen Mechanismus, um seq_no-Zähler unabhängig vom Server zu behandeln.
Der richtige Umgang mit diesen Zählern wird weiter in diesem Artikel beschrieben: Sequenznummern in geheimen Chats.
Bitte beachten Sie, dass Ihr Client Sequenznummern in geheimen Chats unterstützen muss, um mit offiziellen Telegramm-Clients kompatibel zu sein.
Verschlüsselte Dateien senden
Alle an geheime Chats gesendeten Dateien werden mit Einmalschlüsseln verschlüsselt, die in keiner Weise mit dem gemeinsam genutzten Schlüssel des Chats zusammenhängen. Bevor eine verschlüsselte Datei gesendet wird, wird davon ausgegangen, dass die Adresse der verschlüsselten Datei mithilfe des Dateiparameters der Nachrichten an die Außenseite einer verschlüsselten Nachricht angehängt wird.sendEncryptedFile-Methode und dass der Schlüssel für die direkte Entschlüsselung im Hauptteil der Nachricht gesendet wird (der Schlüsselparameter in den Konstruktoren decryptedMessageMediaPhoto, decryptedMessageMediaVideo und decryptedMessageMediaFile.
Bevor eine Datei an einen geheimen Chat gesendet wird, werden 2 zufällige 256-Bit-Nummern berechnet, die als AES-Schlüssel und Initialisierungsvektor zum Verschlüsseln der Datei dienen. AES-256-Verschlüsselung mit Infinite Garble Extension (IGE) wird in gleicher Weise verwendet.
Der Schlüsselfingerabdruck wird wie folgt berechnet:
- digest = md5(key + iv)
- fingerprint = substr(digest, 0, 4) XOR substr(digest, 4, 4)
Der verschlüsselte Inhalt einer Datei wird auf dem Server ähnlich wie der einer Datei in Cloud-Chats gespeichert: Stück für Stück mit Aufrufen zum Hochladen.saveFilePart.Ein nachfolgender Aufruf von Nachrichten.sendEncryptedFile weist der gespeicherten Datei eine Kennung zu und sendet die Adresse zusammen mit der Nachricht. Der Empfänger erhält ein Update mit encryptedMessage und der Parameter file enthält Dateiinformationen.
Eingehende und ausgehende verschlüsselte Dateien können mit dem Konstruktor inputEncryptedFile an andere geheime Chats weitergeleitet werden, um zu vermeiden, dass derselbe Inhalt zweimal auf dem Server gespeichert wird.
Arbeiten mit einer Update-Box
Geheime Chats sind bestimmten Geräten (oder vielmehr Autorisierungsschlüsseln) zugeordnet, nicht Benutzern. Ein herkömmliches Meldungsfeld, das pts verwendet, um den Status des Clients zu beschreiben, ist nicht geeignet, da es für die langfristige Nachrichtenspeicherung und den Nachrichtenzugriff von verschiedenen Geräten aus ausgelegt ist.
Eine zusätzliche temporäre Nachrichtenwarteschlange wird als Lösung für dieses Problem eingeführt. Wenn ein Update bezüglich einer Nachricht aus einem geheimen Chat gesendet wird, wird ein neuer Wert von qts gesendet, der hilft, den Unterschied zu rekonstruieren, wenn die Verbindung lange unterbrochen wurde oder wenn ein Update verloren geht.
Wenn die Anzahl der Ereignisse zunimmt, erhöht sich der Wert von qts mit jedem neuen Ereignis um 1. Der Anfangswert darf (und wird) nicht gleich 0 sein.
Die Tatsache, dass Ereignisse aus der temporären Warteschlange vom Client empfangen und gespeichert wurden, wird durch einen Aufruf der Nachrichten explizit bestätigt.receivedQueue-Methode oder implizit durch einen Aufruf von updates.getDifference (der Wert von qts übergeben, nicht der Endzustand). Alle vom Kunden als zugestellt bestätigten Nachrichten sowie alle Nachrichten, die älter als 7 Tage sind, können (und werden) vom Server gelöscht werden.
Bei der Deaktivierung wird die Ereigniswarteschlange des entsprechenden Geräts zwangsweise gelöscht, und der Wert von qts wird irrelevant.
Aktualisieren auf neue Ebenen
Ihr Client sollte immer die maximale Ebene speichern, von der bekannt ist, dass sie vom Client auf der anderen Seite eines geheimen Chats unterstützt wird. Wenn der geheime Chat zum ersten Mal erstellt wird, sollte dieser Wert auf 46 initialisiert werden. Dieser Wert der entfernten Schicht muss immer unmittelbar nach dem Empfang eines Pakets aktualisiert werden, das Informationen einer oberen Schicht enthält, d.h.:
- Jede geheime Chat-Nachricht, die layer_no in ihrer
decryptedMessageLayer
mit layer>=46 enthält, oder - eine decryptedMessageActionNotifyLayer-Servicenachricht, die so verpackt ist, als wäre sie der decryptedMessageService-Konstruktor des veralteten Layers 8 (Konstruktor
decryptedMessageService#aa48327d
).
Benachrichtigung des Remote-Clients über Ihren lokalen Layer
Um den Remote-Client über Ihren lokalen Layer zu benachrichtigen, muss Ihr Client eine Nachricht vom Typ decryptedMessageActionNotifyLayer
senden. Diese Benachrichtigung muss in einen Konstruktor einer entsprechenden Ebene eingeschlossen werden.
Es gibt zwei Fälle, in denen Ihr Client den Remote-Client über seine lokale Schicht informieren muss:
- Sobald ein neuer geheimer Chat erstellt wurde, unmittelbar nachdem der geheime Schlüssel erfolgreich ausgetauscht wurde.
- Unmittelbar nachdem der lokale Client aktualisiert wurde, um eine neue geheime Chat-Ebene zu unterstützen. In diesem Fall müssen Benachrichtigungen an alle derzeit vorhandenen geheimen Chats gesendet werden. Beachten Sie, dass dies nur erforderlich ist, wenn Sie auf neue Layer aktualisieren, die Änderungen in der Implementierung von Secret Chats enthalten (z. sie müssen dies nicht tun, wenn Ihr Client von Layer 46 auf Layer 47 aktualisiert wird).