Zalogowany jako: gość

Forum

Wątek: Opis techniczny protokołu konferencji

Wróć do listy wątków

1 z 1

1 z 6: pajper

Witajcie!
Jakiś czas temu obiecałem nieco techniczny opis protokołu używanego w konferencjach, oto i on.
Nie jest to pełna dokumentacja, a raczej zarys, jak to działa i o co chodzi.

Konferencje wykorzystują dwa gniazda na porcie 8133: TCP w warstwie kontrolnej i datagram w warstwie transmisji.

1. Wewnętrzna struktura
Serwer współpracuje z bazą danych przy generowaniu kanałów grup, logowaniu użytkowników, zapisywaniu logów i sprawdzaniu uprawnień, ale danych połączeń nie umieszcza w bazie danych, a w pamięci podręcznej. Ma to na celu przyspieszenie działania.
Każdy użytkownik na serwerze ma swój id, który jest generowany w momencie zalogowania. Id użytkownika nie jest stały dla danego konta, po wylogowaniu i zalogowaniu może być inny. Id może mieć wartość od 1 do 65535, wartość 0 jest zarezerwowana dla komunikatów generowanych przez serwer. Może się stać, że w trakcie rozmowy kilka osób otrzyma kolejno ten sam id.
Podobnie sprawa wygląda dla kanałów, każdy kanał może mieć swój id od 1 do 65535. ID kanałów może się zmieniać w trakcie działania serwera, zależnie od zmiany struktury drzew binarnych. Wszystkie informacje o zmianach id kanałów są przekazywane podczas aktualizacji danych. Id kanału jest wymagany tylko do przełączania się między kanałami, nie jest wykorzystywany w transmisji. Do jednoznacznej identyfikacji kanału służy jego uuid, który jest zapisywany i zachowywany przez cały czas życia kanału.
Każdy kanał generuje także swój stamp. Stamp składa się z 24 bitów i jest dostępny tylko dla członków kanału. W żadnym momencie na serwerze dwa kanały nie mają takiego samego stampa.
Stamp zmienia się przy każdej zmianie klucza szyfrującego, tak więc pozwala zidentyfikować kanał oraz klucz wykorzystany w transmisji. Jest to ważne w momencie zmiany klucza, gdy jeszcze odbierane mogą być pakiety szyfrowane kluczem poprzednim. Po otrzymaniu informacji o zmianie klucza, klient powinien przez przynajmniej sekundę generować pakiety z kluczem poprzednim, aby upewnić się, że wszyscy użytkownicy otrzymali nowy klucz. Klient powinien zachowywać przynajmniej 5-sekundową historię kluczy, na wypadek kilku szybkich zmian.
Zmiana klucza kanału występuje przy każdej zmianie danych kanału, a więc w szczególności przy dołączaniu lub rozłączaniu się użytkowników, a także na rządanie serwera, gdy ten uzna, że siła szyfrowania słabnie, na przykład w skutek kończącej się puli nonce. Zapewnia to, że po opuszczeniu kanału przez użytkownika, nie może on podsłuchiwać rozmowy, znając klucz. Podobnie nie może podsłuchać rozmowy przed jego dołączeniem. Nowy klucz wysyłany jest tylko raz w warstwie TCP w momencie wygenerowania i nie ma możliwości zapytania o niego.
Parametry transmisji są sterowane przez serwer. Klient przekazuje do serwera co wybrany czas (5-15 sekund) informację o utracie pakietów. Na podstawie mediany utraty pakietów serwer przekazuje do klientów ustawienia enkodera.
Dopuszcza się zmianę wszystkich ustawień enkodera w otwartych kanałach, wliczając w to także zmianę bitrate. Dozwolona jest także zmiana szerokości ramki, ale by nie powodowała problemów, klient powinien w takim wypadku zresetować enkoder.
Klient powinien przyjmować wszystkie pakiety kierowane przez serwer, także te o różnej od oznaczonej szerokości ramki bądź innym bitrate. Weryfikacja parametrów tych odbywa się na poziomie serwera. W wypadku problemów z wydajnością komunikacji, klient może, za zgodą serwera, wybrać różną od ustawionej w kanale szerokość ramki, ale nie inne bitrate.

2. Połączenie TCP
Połączenie jest szyfrowane z użyciem protokołu TLS w wersji 1.3. Do uwierzytelnienia serwera służy standardowa weryfikacja wystawca/domena (conferencing.elten.link). Protokół używa formatu JSON, obsługuje trzy możliwe kodowania. To klient przekazuje do serwera informacje o kodowaniach, które obsługuje. Serwer może odpowiedzieć w dowolnym obsługiwanym kodowaniu, także innym od użytego w zapytaniu.
Wszystkie zapytania są inicjowane przez klienta, serwer niczego nie wysyła niepytany, podobnie jak ma to miejsce w HTTP. Ma to na celu potwierdzenie, czy połączenie jest nadal aktywne.
Jeśli serwer nie otrzyma od klienta żadnych danych przez 15 sekund, uznaje połączenie za martwe i je przerywa.
2.1. Obsługiwane kodowania
2.1.1. Czysty JSON
W tym trybie klient przekazuje do serwera (i na odwrót) czyste, niekompresowane dane JSON. Składnia to po prostu informacja JSON, zakończona znakiem końca linii.
2.1.2. Kompresja Deflate
Ten tryb kodowania jest oznaczony przez bajt 0x64 (litera d), po którym następuje kodowana base36 długość danych i znak nowej linii 0x13 (\n). Następnie przekazywane są skompresowane dane.
2.1.3. Kompresja XZ/LZMA2
Ten tryb kodowania jest oznaczony przez bajt 0x78 (litera x), po którym następuje kodowana base62 długość danych i znak nowej linii 0x13 (\n). Następnie przekazywane są skompresowane dane.
2.2. Składnia zapytania
Klient wysyła do serwera zapytanie w następującym formacie:
{
":command":{komenda},
":id":{opcjonalny identyfikator polecenia, dowolny},
":timeout":{opcjonalny czas graniczny, w którym jeśli serwer nie przetworzy danych, ma zwrócić błąd, domyślnie 15, nie mniej niż 1},
}
Pozostałe pola są argumentami komendy.
Jeśli treść jest dłuższa, niż 65536 bajtów, natychmiast jest odrzucana.
2.3. Odpowiedź serwera
Odpowiedź serwera ma następujący format.
{
":status":{success, error albo queued, czyli status polecenia},
":id":{wybrany przez klienta identyfikator, pole nie występuje, gdy nie było identyfikatora},
":error_code":{kod błędu, jeśli wystąpił},
":error_description":{opis błędu, jeśli dostępny},
}
Pozostałe pola są dowolne i związane ściśle z komendą.
2.4. Aliasy poleceń
Klient i serwer mogą negocjować używane aliasy dla poleceń, zarówno nazw pól, jak i ich treści, np. konkretnych komend. Służy to zmniejszeniu rozmiaru transmisji.
Serwer używa tylko tych aliasów, które zadeklaruje klient.
2.5. Aktualizacja danych
Wszystkie informacje o zmianie danych transmisji, klucza czy nowych/opuszczających kanał użytkownikach są przesyłane w warstwie TCP. Dlatego klient powinien możliwie często zapytywać o zmianę statusu, polecenie "update". Na chwilę obecną jest to co pół sekundy.

3. Połączenie UDP
W momencie zalogowania się do serwera, serwer generuje 256-bitowy identyfikator, który pozwoli na uwierzytelnienie połączenia UDP. Zaraz po jego otrzymaniu, użytkownik powinien utworzyć gniazdo UDP i przesłać nim do serwera otrzymany klucz. W tym momencie połączenie UDP uważane jest za nawiązane.
W połączeniu UDP każdy pakiet uważany jest za kompletny. Nie zezwala się na dzielenie pakietów, oczywiście z wyłączeniem podziałów na warstwach 1-4 OSI.
Pierwsze 16 bajtów pakietu stanowi nagłówek. Wszystkie pakiety krótsze niż 16 bajtów powinny być ignorowane i przez klienta, i serwer.
Maksymalna dopuszczalna długość pakietu to 1296 bajtów (1280 bajtów danych i 16 nagłówka), ale z wyłączeniem wysokich bitrate, klient powinien starać się nie przekraczać długości 1040 bajtów (1024 bajtów danych i 16 nagłówka). Wynika to ze zróżnicowanych ustawień MTU.
Na chwilę obecną serwer nie weryfikuje poprawności danych, z wyłączeniem identyfikacji użytkownika i typu transmisji. Klient powinien więc po prostu odrzucić pakiety, które zawierają niepoprawne dane, na przykład z błędem szyfrowania lub struktury ramki audio.
3.1. Budowa nagłówka
Wszystkie dane są zakodowane jako Little Endian.
* b0..1 - identyfikator użytkownika nadającego pakiet
* b2..4 - stamp pakietu, 0 oznacza pakiet bez szyfrowania
* b5..6 - index pakietu, numer pakietu wysłanego przez użytkownika od zmiany stampa
* b7 - typ ramki, o tym poniżej
* b8,9,10,11 - parametry zależne od typu ramki
* b12..15 - CRC32, opcjonalnie
3.2. Treść pakietu
Po nagłówku następuje treść. Jeśli stamp nie był zerowy, należy odszyfrować ją z użyciem klucza właściwego dla stampa.
Jeśli stamp jest nieznany, pakiet należy zignorować.
Jeśli podany został CRC32, a odszyfrowane dane mu nie odpowiadają, pakiet należy zignorować.
Treść pakietu jest bezpośrednio zależna od typu ramki.
3.2.1. Pakiety audio
Pakiety typu audio (1) to po prostu ramka Opusa przekazywana na kanał. W tym wypadku b8 nagłówka wskazuje na współrzędną x, zaś b9 na współrzędną y użytkownika wysyłającego transmisję; b10..11 to numer ramki w transmisji (little endian).
Treść poprzedza nagłówek Opusa zgodnie z RFC 6716.
3.2.2. Pakiety chatu
Pakiety chatu (2) to treść tekstowa kierowana na kanał. Treść ta jest niekompresowana, kodowana UTF-8.
Dodatkowe Parametry w tym wypadku nie są używane.
Maksymalna dopuszczalna długość to 1024 znaki, jeśli treść jest dłuższa, należy podzielić ją przed wysłaniem na kilka następujących po sobie treści, w ramach możliwości wg znaku spacji.
3.2.3. Pakiety szeptu
Pakiety szeptu (3) są pakietami skierowanymi tylko do jednego użytkownika.
W tym wypadku parametry b8..9 oznaczają kodowany (little endian) identyfikator użytkownika, do którego szept jest skierowany. Za odpowiednie kierowanie pakietów tego typu odpowiada serwer, klient nie musi weryfikować tego ponownie, a może od razu przekazać ramkę do przetworzenia.
Budowa ramki jest zgodna z RFC 6716.
3.2.4. Pakiety szyfrowanego szeptu
Pakiety szyfrowanego szeptu (4) są pakietami skierowanymi tylko do jednego użytkownika.
W tym wypadku parametry b8..9 oznaczają kodowany (little endian) identyfikator użytkownika, do którego szept jest skierowany. Za odpowiednie kierowanie pakietów tego typu odpowiada serwer, klient nie musi weryfikować tego ponownie, a może od razu przekazać ramkę do przetworzenia.
Ramka jest szyfrowana przez klucz publiczny RSA podany przez użytkownika, a treść pakietu prezentuje się następująco, niech n oznacza rozmiar klucza:
* b0...n/8 - nagłówek, dalej oznaczany jako h
* h0 - rozmiar klucza AES, dalej oznaczany jako a
* h1..a/8 - klucz AES
* ha/8..a/8+16 - wektor szyfrujący IV
* ha/8+16..-1 + bn/8..-1 - zakodowana AES CBC ramka Opusa zgodna z RFC 6716
3.2.5. Pakiety kontroli połączenia
Pakiety kontroli połączenia (200) są używane do kontroli, czy połączenie nie zostało utracone. Ich parametry nie są zdefiniowane, zaś treść jest pusta.
Pakiety te są kierowane
* Przez serwer co 3 sekundy, jeśli użytkownik nie odbiera żadnego strumienia,
* Przez klienta co 3 sekundy, jeśli nie jest strumieniowana żadna treść.
Jeśli przez 15 sekund klient lub serwer nie otrzymają żadnego pakietu, połączenie jest uważane za zerwane.
3.2.6. Pakiety retransmisji
Pakiety prośby retransmisji (201) są kierowane w wypadku, gdy klient lub serwer nie otrzymał jednej z ramek.
W tym wypadku parametr b8..9 oznacza użytkownika, a b10..11 indeks ramki.
Obecnie serwer nie prosi o retransmisję nieodebranych pakietów. Jeśli klient wyśle taką prośbę, odpowiednia ramka jest ponownie przekazywana, jeśli dotarła do serwera i znajduje się w jego pamięci cache.
Funkcja ta jest obsługiwana tylko dla ramek 60 ms.
3.2.7. Pakiety ping/pong
Pakiety ping (251) oraz pong (252) nie zawierają treści. Służą do obliczania opóźnienia.
Kiedy klient lub serwer otrzyma pakiet ping, natychmiast odpowiada pakietem pong.
W parametrze b8..b9 jest umieszczana Little Endian ilość milisekund od pełnej minuty, na przykład jeśli jest godzina 12:34:56 i 789 milisekund, w parametrze znajdzie się wartość 56789.
3.3. Szyfrowanie
Obecnie obsługiwane jest szyfrowanie kluczami AES/CTR o długości 128, 192 oraz 256 bitów (ustawienia kanału), Nonce dla szyfrowania ramki stanowi nagłówek.
Unikalność nagłówka jest zabezpieczona podwójnie, przez indeks i wartość CRC. Jeśli indeks osiągnie 60000 dla któregokolwiek użytkownika, serwer nakaże zmianę klucza.
Protokół dopuszcza także nieszyfrowaną transmisję, w tym wypadku rozmiar klucza wynosi 0.

4. Założenia dotyczące bezpieczeństwa
Gwarantem bezpieczeństwa protokołu jest warstwa TCP, zabezpieczona 4096-bitowym kluczem RSA.
Wszystkie pakiety UDP zawierające dane powinny być szyfrowane, z możliwym wyjątkiem pakietu retransmisji, który i tak zwróci dane zaszyfrowane.
Należy pamiętać, że protokół UDP nie zawiera żadnego mechanizmu weryfikowania nadawcy. Teoretycznie możliwe jest więc podszywanie się pod innego użytkownika obecnego w kanale przy nadawaniu, ale nie przy odbiorze.
W wypadku standardowego szeptania bezpieczeństwo jest osiągane przez kierowanie pakietu do jednego odbiorcy, nie zaś przez podwójne szyfrowanie. Należy więc traktować szept tak, jakby był transmisją nieszyfrowaną, przejęcie takiego pakietu na przykład na poziomie routera sieci osobie należącej do kanału umożliwiłoby bezproblemowe odsłuchanie treści.
Zmienia się to od wersji 2.4.1 z użyciem szyfrowanych szeptów, które są kodowane kluczem publicznym użytkownika, przejęcie szeptu nie ujawni jego treści.
Protokół jest dobrze chroniony przed podsłuchem z zewnątrz, poprzez weryfikację serwera, szyfrowanie asynchroniczne warstwy kontroli transmisji, silne szyfrowanie przesyłanych danych i częstą zmianę klucza.
#StandWithUkraine Shoot for the Moon. Even if you miss, you'll land among the stars.
26.01.2021 12:20

2 z 6: nuno69

Spodziewałem się, że ten protokol bedzie trudniejszy w zrozumieniu.
- "Intelligence and wisdom is like jam. The less you have, the harder you're trying to spread it arround." - French proverb
26.01.2021 14:49

3 z 6: zywek

Też tak sądziłem.

28.01.2021 16:36

4 z 6: pajper

Aktualizacja, protokół od teraz pozwala na:
* Ustawianie ciągłego bitrate od 6 do 510 kbps
* Ustawianie wszystkich obsługiwanych przez Opusa ramek, a więc 2.5, 5, 10, 20, 40, 60, 80, 100, 120
Przy czym ramki powyżej 60ms nie są zalecane.
#StandWithUkraine Shoot for the Moon. Even if you miss, you'll land among the stars.
29.01.2021 11:30

5 z 6: pajper

Od wersji 2.4.1 szepty są podwójnie szyfrowane z użyciem indywidualnego klucza RSA generowanego przez użytkownika. Pełen opis znajdzie się po premierze 2.4.1, konspekt wygląda jednak następująco:
* W chwili połączenia użytkownik wysyła do serwera publiczny klucz RSA 2048,
* Gdy inna osoba chce coś wyszeptać, klient pobiera ten klucz,
* Generowany jest 128-bitowy klucz AES w trybie CBC, posłuży on na czas szeptu,
* Kodowana jest ramka o id 4, która w treści zawiera:
pierwsze 256 bajty: nagłówek,
Reszta: stopka.
Nagłówek jest szyfrowany RSA i zawiera:
b0: długość klucza (k),
b1..k: klucz,
bk...k+16: wektor inicjujący
bk+16..255: początek ramki Opusa.
Stopka zawiera szyfrowane AES128 pozostałe dane ramki Opusa, należy je połączyć.

* Umieszczenie części danych w samym nagłówku RSA wynika z ograniczeń protokołu UDP i używanych wartości fragmentacji pakietów, które nakładają limit 1492 bajtów długości transmisji (256 bajtów szyfru RSA i 1280 ramki Opusa przekraczałoby ten limit).
#StandWithUkraine Shoot for the Moon. Even if you miss, you'll land among the stars.
06.03.2021 23:44

6 z 6: pajper

Zaktualizowałem opis protokołu o nowe ustawienia kompresji danych i szyfrowane szepty (nowości z 2.4.1.).
#StandWithUkraine Shoot for the Moon. Even if you miss, you'll land among the stars.
16.03.2021 23:51

Wróć do listy wątków

1 z 1


Nawigacja


Copyright (©) 2014-2024, Dawid Pieper