Ten artykuł na temat szyfrowania End-to-End MTProto jest przeznaczony dla zaawansowanych użytkowników.Jeśli chcesz dowiedzieć się więcej o tajnych czatach z mniej zastraszającego źródła, zapoznaj się z naszym ogólnym FAQ.
zauważ, że od wersji 4.6 główne klienty telegramu używają MTProto 2.0.MTProto V. 1. 0 jest przestarzały i jest obecnie wycofywany.
tajne czaty są czatami jeden-na-jeden, w których wiadomości są szyfrowane kluczem posiadanym tylko przez uczestników czatu. Zauważ, że schemat tych szyfrowanych czatów tajnych różni się od schematu używanego dla czatów w chmurze:
uwaga na temat MTProto 2.0
Ten artykuł opisuje warstwę szyfrowania end-to-end w protokole MTProto w wersji 2.0.Główne różnice w stosunku do wersji 1.0 (opisanej tutaj dla odniesienia) są następujące:
- zamiast SHA-1 zastosowano SHA-256;
- bajty wypełniające są zaangażowane w Obliczanie msg_key;
- msg_key zależy nie tylko od zaszyfrowanej wiadomości, ale także od części tajnego klucza czatu;
- 12..Zamiast 0 użyto 1024 bajtów wypełnienia..15 bajtów wypełnienia w V. 1. 0.
Zobacz także: MTProto 2.0: Cloud Chats, Server-client encryption
generowanie kluczy
klucze są generowane przy użyciu protokołu Diffie-Hellman.
rozważmy następujący scenariusz: Użytkownik a chciałby zainicjować szyfrowaną komunikację end-to-end z użytkownikiem B.
wysłanie żądania
użytkownik A wykonuje wiadomości.getDhConfig do uzyskania parametrów Diffiego-Hellmana: pierwszego p i elementu wysokiego rzędu g.
wykonanie tej metody przed każdą nową procedurą generowania klucza ma kluczowe znaczenie. Warto buforować wartości parametrów razem z wersją, aby uniknąć konieczności odbierania wszystkich wartości za każdym razem. Jeśli Wersja przechowywana na kliencie jest nadal aktualna, serwer zwróci komunikaty konstruktora.dhConfigNotModified.
Klient ma sprawdzić, czy p jest bezpieczną 2048-bitową liczbą pierwszą (co oznacza, że zarówno p, jak i (p-1)/2 są liczbą pierwszą, i że 2^2047 < p < 2^2048), i że g generuje cykliczną podgrupę pierwszego rzędu (p-1) / 2, ponieważ G jest zawsze równe 2, 3, 4, 5, 6 lub 7, można to łatwo zrobić za pomocą kwadratowego prawa wzajemności, dając prosty warunek na p Mod 4G-mianowicie, P Mod 8 = 7 dla G = 2; p Mod 3 = 2 dla G = 3; brak dodatkowego warunku dla G = 4; p Mod 5 = 1 lub 4 dla G = 5; p mod 24 = 19 lub 23 dla G = 6; i p mod 7 = 3, 5 lub 6 dla g = 7. Po sprawdzeniu g i p przez Klienta, sensowne jest buforowanie wyniku, aby uniknąć powtarzania długich obliczeń w przyszłości. Ten bufor może być współdzielony z jednym używanym do generowania kluczy autoryzacyjnych.
Jeśli klient ma nieodpowiedni generator liczb losowych, warto przekazać parametr random_length (random_length> 0), aby serwer generował własną sekwencję losową o odpowiedniej długości.Ważne: używanie sekwencji losowej serwera w postaci surowej może być niebezpieczne. Musi być połączony z sekwencją klienta, na przykład przez wygenerowanie losowej liczby Klienta o tej samej długości (client_random) i użycie final_random := random XOR client_random
.
Klient A Oblicza 2048-bitową liczbę a (używając odpowiedniej entropii lub losowej wartości serwera; patrz wyżej) i wykonuje wiadomości.requestEncryption po przejściu w g_a := pow(g, a) mod dh_prime
.
użytkownik B otrzymuje update updateEncryption dla wszystkich powiązanych kluczy autoryzacji (wszystkich autoryzowanych urządzeń) za pomocą konstruktora czatu encryptedChatRequested. Użytkownik musi otrzymać podstawowe informacje o użytkowniku A i musi zostać poproszony o zaakceptowanie lub odrzucenie żądania.
obaj klienci mają sprawdzić, czy g, g_a i g_b są większe od jednego i mniejsze od p-1. Zalecamy sprawdzenie, czy g_a i g_b znajdują się pomiędzy 2^{2048-64} i p – 2^{2048-64}.
akceptując żądanie
Po potwierdzeniu przez użytkownika B utworzenia tajnego czatu z A w interfejsie Klienta, Klient B otrzymuje również aktualne parametry konfiguracyjne dla metody Diffie-Hellman. Następnie generuje losową 2048-bitową liczbę, b, używając reguł podobnych do tych dla a.
po otrzymaniu g_a z aktualizacji z encryptedChatRequested, może natychmiast wygenerować ostateczny klucz współdzielony: key = (pow(g_a, b) mod dh_prime)
. Jeśli długość klucza < 256 bajtów, dodaj kilka początkowych bajtów zerowych jako wypełnienie-tak, że klucz ma dokładnie 256 bajtów długości. Jego odcisk palca, key_fingerprint, jest równy 64 ostatnim bitom SHA1 (key).
Uwaga 1: w tym konkretnym przypadku SHA1 jest tu używany nawet dla tajnych czatów MTProto 2.0.
Uwaga 2: ten odcisk palca jest używany jako kontrola rozsądku dla procedury wymiany kluczy do wykrywania błędów podczas tworzenia oprogramowania klienckiego — nie jest połączony z wizualizacją klucza używaną na klientach jako środek zewnętrznego uwierzytelniania w tajnych czatach. Wizualizacje kluczy na klientach są generowane przy użyciu pierwszych 128 bitów SHA1 (klucz intial), a następnie pierwszych 160 bitów SHA256 (klucz używany, gdy tajny czat został zaktualizowany do warstwy 46).
Klient B wykonuje wiadomości.acceptEncryption po przekazaniu go g_b := pow(g, b) mod dh_prime
I key_fingerprint.
dla wszystkich autoryzowanych urządzeń klienta B, z wyjątkiem bieżącego, aktualizacje updateEncryption są wysyłane z konstruktorem encryptedChatDiscarded. Następnie jedynym urządzeniem, które będzie mogło uzyskać dostęp do tajnego czatu, jest urządzenie B, które wykonało połączenie z wiadomościami.acceptEncryption.
Użytkownik a otrzyma aktualizację updateEncryption z konstruktorem encryptedChat, dla klucza autoryzacji, który zainicjował czat.
korzystając z g_b z aktualizacji, klient a może również obliczyć klucz współdzielony key = (pow(g_b, a) mod dh_prime)
. Jeśli długość klucza < 256 bajtów, dodaj kilka początkowych bajtów zerowych jako wypełnienie-tak, że klucz ma dokładnie 256 bajtów długości. Jeśli odcisk palca odebranego klucza jest identyczny z tym, który został przekazany do encryptedChat, wiadomości przychodzące mogą być wysyłane i przetwarzane. W przeciwnym razie, wiadomości.discardEncryption musi być wykonane i użytkownik powiadomiony.
Perfect Forward Secrecy
aby zapewnić bezpieczeństwo komunikacji w przeszłości, oficjalni klienci Telegram zainicjują ponowne kluczowanie, gdy klucz zostanie użyty do odszyfrowania i zaszyfrowania ponad 100 wiadomości lub będzie używany przez ponad tydzień, pod warunkiem, że klucz został użyty do zaszyfrowania co najmniej jednej wiadomości. Stare klucze są następnie bezpiecznie wyrzucane i nie można ich zrekonstruować, nawet przy dostępie do nowych kluczy obecnie używanych.
protokół ponownego klucza jest opisany w tym artykule: Perfect Forward Secrecy in Secret Chats.
pamiętaj, że twój Klient musi obsługiwać tajność przekazywania w tajnych Czatach, aby być kompatybilnym z oficjalnymi klientami Telegram.
wysyłanie i odbieranie wiadomości na tajnym czacie
serializacja i szyfrowanie wiadomości wychodzących
tworzony jest obiekt TL typu DecryptedMessage i zawiera wiadomość w postaci zwykłego tekstu. Dla zgodności wstecznej obiekt musi być opakowany w konstruktor decryptedMessageLayer ze wskazaniem obsługiwanej warstwy (począwszy od 46).
schemat TL dla zawartości zaszyfrowanych wiadomości typu end-to-end jest dostępny tutaj „
wynikowa konstrukcja jest serializowana jako tablica bajtów przy użyciu ogólnych reguł TL. Wynikowa tablica jest poprzedzona 4 bajtami zawierającymi długość tablicy nie licząc tych 4 bajtów.
tablica bajtów jest wypełniona od 12 do 1024 losowych bajtów wypełnienia, aby jej długość była podzielna przez 16 bajtów. (W starszym szyfrowaniu MTProto 1.0 użyto tylko 0 do 15 bajtów wypełnienia.)
klucz wiadomości, msg_key, jest obliczany jako 128 środkowych bitów SHA256 danych uzyskanych w poprzednim kroku, poprzedzonych 32 bajtami z klucza klucza współdzielonego. (Dla starszego szyfrowania MTProto 1.0, msg_key był obliczany inaczej, jak 128 dolnych bitów SHA1 danych uzyskanych w poprzednich krokach, z wyłączeniem bajtów wypełnienia.)
Dla MTProto 2.0 klucz AES aes_key i Wektor inicjalizacji aes_iv są obliczane ( klucz jest kluczem współdzielonym uzyskanym podczas generowania klucza) w następujący sposób:
- 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 (key, x, 36));
- sha256_b = SHA256 (substr (key, 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);
dla MTProto 2.0, x=0 dla wiadomości od twórcy tajnego czatu, x=8 dla wiadomości w przeciwnym kierunku.
dla przestarzałego MTProto 1.0, msg_key, aes_key i aes_iv były obliczane w różny sposób (zobacz ten dokument w celach informacyjnych).
dane są szyfrowane za pomocą 256-bitowego klucza, aes_key i 256-bitowego wektora inicjalizacji, AES-iv, przy użyciu szyfrowania AES-256 z nieskończonym rozszerzeniem garble (IGE). Klucz szyfrowania fingerprint key_fingerprint i klucz wiadomości msg_key są dodawane na górze wynikowej tablicy bajtów.
zaszyfrowane dane są osadzane w wiadomości.zaszyfrowane wywołanie API i przekazane do serwera Telegram w celu dostarczenia do drugiej strony tajnego czatu.
Aktualizacja do MTProto 2.0 z MTProto 1.0
jak tylko obie strony w tajnym czacie używają co najmniej warstwy 73, powinny używać MTProto 2.0 tylko dla wszystkich wychodzących wiadomości. Niektóre z pierwszych odebranych wiadomości mogą korzystać z MTProto 1.0, jeśli podczas tworzenia tajnego czatu nie wynegocjowano wystarczająco wysokiej warstwy startowej. Po otrzymaniu pierwszej wiadomości zaszyfrowanej MTProto 2.0 (lub pierwszej wiadomości z warstwą 73 lub wyższą), wszystkie wiadomości z wyższymi numerami porządkowymi muszą być również zaszyfrowane MTProto 2.0.
dopóki bieżąca warstwa jest niższa niż 73, każda ze stron powinna spróbować odszyfrować otrzymane wiadomości za pomocą MTProto 1.0, a jeśli to się nie powiedzie (msg_key nie pasuje), spróbuj MTProto 2.0. Gdy pojawi się pierwsza zaszyfrowana wiadomość MTProto 2.0 (lub warstwa zostanie zaktualizowana do 73), nie ma potrzeby próbowania odszyfrowania MTProto 1.0 Dla dalszych wiadomości (chyba że klient nadal czeka na zamknięcie niektórych luk).
odszyfrowanie wiadomości przychodzącej
powyższe kroki są wykonywane w odwrotnej kolejności. Podczas odbierania zaszyfrowanej wiadomości należy sprawdzić, czy msg_key jest w rzeczywistości równe 128 środkowym bitom skrótu SHA256 odszyfrowanej wiadomości, poprzedzonym 32 bajtami pobranymi z klucza współdzielonego.Jeśli warstwa komunikatów jest większa niż warstwa obsługiwana przez klienta, użytkownik musi zostać powiadomiony, że wersja klienta jest nieaktualna i poproszony o aktualizację.
numery sekwencyjne
konieczne jest interpretowanie wszystkich wiadomości w ich pierwotnej kolejności, aby chronić je przed możliwymi manipulacjami. Tajne czaty obsługują specjalny mechanizm obsługi liczników seq_no niezależnie od serwera.
Prawidłowa obsługa tych liczników jest opisana w tym artykule: numery sekwencyjne w tajnych rozmowach.
pamiętaj, że twój Klient musi obsługiwać numery sekwencyjne w tajnych Czatach, aby być kompatybilnym z oficjalnymi klientami Telegram.
Wysyłanie zaszyfrowanych plików
wszystkie pliki wysyłane do tajnych czatów są szyfrowane za pomocą jednorazowych kluczy, które nie są w żaden sposób powiązane ze wspólnym kluczem czatu. Przed wysłaniem zaszyfrowanego pliku zakłada się, że adres zaszyfrowanego pliku zostanie dołączony na zewnątrz zaszyfrowanej wiadomości za pomocą parametru plik wiadomości.metoda sendEncryptedFile i że klucz do bezpośredniego odszyfrowania zostanie wysłany w treści wiadomości (kluczowy parametr w konstruktorach decryptedMessageMediaPhoto, decryptedMessageMediaVideo i decryptedMessageMediaFile.
przed wysłaniem pliku na tajny czat, obliczane są 2 losowe 256-bitowe liczby, które będą służyć jako klucz AES i Wektor inicjalizacji używany do szyfrowania pliku. Szyfrowanie AES – 256 z nieskończonym rozszerzeniem garble (IGE) jest używane w podobny sposób.
odcisk palca klucza jest obliczany w następujący sposób:
- digest = md5(key + iv)
- fingerprint = substr(digest, 0, 4) XOR substr(digest, 4, 4)
zaszyfrowana zawartość pliku jest przechowywana na serwerze w taki sam sposób, jak zawartość pliku w czatach w chmurze: kawałek po kawałku za pomocą wywołań do przesłania.saveFilePart.Kolejne wywołanie wiadomości.sendEncryptedFile przypisze identyfikator do przechowywanego pliku i wyśle adres wraz z wiadomością. Odbiorca otrzyma aktualizację z encryptedMessage, a parametr plik będzie zawierał informacje o pliku.
przychodzące i wychodzące zaszyfrowane pliki mogą być przekazywane do innych tajnych czatów za pomocą konstruktora inputEncryptedFile, aby uniknąć dwukrotnego zapisywania tej samej zawartości na serwerze.
praca z skrzynką aktualizacji
tajne czaty są powiązane z określonymi urządzeniami (a raczej z kluczami autoryzacji), a nie z użytkownikami. Konwencjonalna Skrzynka komunikacyjna, która wykorzystuje pts do opisania statusu klienta, nie jest odpowiednia, ponieważ jest przeznaczona do długoterminowego przechowywania wiadomości i dostępu do wiadomości z różnych urządzeń.
jako rozwiązanie tego problemu wprowadzono dodatkową tymczasową kolejkę komunikatów. Gdy wysyłana jest aktualizacja dotycząca wiadomości z tajnego czatu, wysyłana jest nowa wartość qts, która pomaga zrekonstruować różnicę w przypadku długiej przerwy w połączeniu lub utraty aktualizacji.
wraz ze wzrostem liczby zdarzeń wartość qts zwiększa się o 1 z każdym nowym zdarzeniem. Wartość początkowa może nie być (i nie będzie) równa 0.
fakt, że zdarzenia z tymczasowej kolejki zostały odebrane i zapisane przez Klienta jest potwierdzany jawnie przez wywołanie wiadomości.metoda receivedQueue lub pośrednio przez wywołanie aktualizacji.getDifference (wartość QTS przekazana, a nie stan końcowy). Wszystkie wiadomości uznane za dostarczone przez Klienta, a także wiadomości starsze niż 7 dni, mogą (i będą) zostać usunięte z serwera.
Po usunięciu autoryzacji Kolejka zdarzeń odpowiedniego urządzenia zostanie siłą wyczyszczona, a wartość qts stanie się nieistotna.
Aktualizacja do nowych warstw
Twój klient powinien zawsze przechowywać maksymalną warstwę, o której wiadomo, że jest obsługiwana przez Klienta po drugiej stronie tajnego czatu. Kiedy tajny czat jest tworzony po raz pierwszy, wartość ta powinna być zainicjowana na 46. Ta zdalna wartość warstwy musi być zawsze aktualizowana natychmiast po otrzymaniu dowolnego pakietu zawierającego informacje o górnej warstwie, tzn.:
- każda tajna wiadomość czatu zawierająca layer_no w swoim
decryptedMessageLayer
z warstwą>=46 lub - wiadomość usługi deszyfrowanej messageactionnotifylayer, owinięta tak, jakby była konstruktorem usługi deszyfrowanej messageservice przestarzałej warstwy 8 (konstruktor
decryptedMessageService#aa48327d
).
Powiadamianie zdalnego klienta o warstwie lokalnej
aby powiadomić zdalnego klienta o warstwie lokalnej, klient musi wysłać wiadomość typudecryptedMessageActionNotifyLayer
. Powiadomienie to musi być zawinięte w konstruktor odpowiedniej warstwy.
istnieją dwa przypadki, w których twój Klient musi powiadomić zdalnego klienta o swojej lokalnej warstwie:
- natychmiast po utworzeniu nowego tajnego czatu, natychmiast po pomyślnej wymianie tajnego klucza.
- natychmiast po aktualizacji lokalnego klienta w celu obsługi nowej tajnej warstwy czatu. W takim przypadku powiadomienia muszą być wysyłane do wszystkich obecnie istniejących tajnych czatów. Należy pamiętać, że jest to konieczne tylko w przypadku aktualizacji do nowych warstw, które zawierają zmiany w implementacji tajnych czatów (np. nie musisz tego robić, gdy twój Klient jest aktualizowany z warstwy 46 do warstwy 47).