I2P-Transportprotokolle wurden vor fast 15 Jahren entwickelt, als die Hauptaufgabe darin bestand, den Inhalt des Verkehrs zu verbergen und nicht die Tatsache, dass das eine oder andere Protokoll verwendet wurde. DPI (Deep Packets Inspection) und Traffic Blocking wurden zu diesem Zeitpunkt nicht berücksichtigt. Die Zeiten ändern sich jedoch, und obwohl die vorhandenen I2P-Protokolle immer noch recht gut geschützt sind, besteht Bedarf an einem neuen Transportprotokoll, das auf bestehende und zukünftige Bedrohungen reagiert, und vor allem an DPI, das die Paketlängen analysiert. Darüber hinaus nutzt das neue Protokoll die neuesten Fortschritte in der Kryptografie. Eine vollständige Beschreibung des Protokolls finden Sie
hier . Die Basis ist
Noise , in dem SHA256 als Hash-Funktion verwendet wird, und x25519 als DH (in der Noise-Terminologie).
Neue Kryptographie
Für NTCP2 müssen zusätzlich zu den bereits in I2P vorhandenen die folgenden kryptografischen Algorithmen implementiert werden:
- x25519
- HMAC-SHA256
- Chacha20
- Poly1305
- Aead
- Siphash
Mit Ausnahme von Siphash sind alle in openssl 1.1.0 implementiert. Siphash wird wiederum in openssl 1.1.1 erscheinen, das in Kürze veröffentlicht wird. Aus Gründen der Kompatibilität mit openssl 1.0.2, das in den meisten derzeit verwendeten Betriebssystemen enthalten ist, hat i2pd eigene Implementierungen hinzugefügt, die von einem der i2pd-Entwickler
Jeff Becker geschrieben wurden , der in ps als I ps bekannt ist.
Im Vergleich zu NTCP ersetzt x25519 DH, AEAD / Chaha20 / Poly1305 ersetzt AES-256-CBC / Adler32 und Siphash wird zum Verschlüsseln der Länge der übertragenen Nachrichten verwendet. Das Verfahren zur Berechnung des gemeinsam genutzten Schlüssels ist komplexer geworden: Mit vielen Aufrufen des HMAC-SHA256.
Änderungen an RouterInfo
Um mit dem NTCP2-Protokoll arbeiten zu können, wird zusätzlich zu den beiden vorhandenen Schlüsseln (Verschlüsselung und Signatur) ein dritter Schlüssel x25519 eingeführt, der als statischer Schlüssel bezeichnet wird und in einigen RouterInfo-Adressen als "s" -Parameter für Clients und Server vorhanden sein muss. Wenn mehr als eine Adresse NTCP2 unterstützt, z. B. ipv4 und ipv6, muss "s" überall gleich sein. Für Clients enthält die Adresse möglicherweise nur "s" und nicht die Parameter "host" und "port". Der erforderliche Parameter von NTCP2 ist auch "v", derzeit immer gleich "2".
Die NTCP2-Adresse kann als Adresse vom Typ „NTCP“ mit zusätzlichen Parametern festgelegt werden. In diesem Fall kann die Verbindung sowohl über NTCP als auch über NTCP2 oder als Adresse vom Typ NTCP2 hergestellt werden, die nur NTCP2-Verbindungen unterstützt. In Java I2P wird die erste Methode verwendet, in i2pd die zweite.
Wenn der Host eingehende NTCP2-Verbindungen akzeptiert, muss er den Parameter "i" mit dem Wert IV veröffentlichen, um den öffentlichen Schlüssel beim Herstellen der Verbindung zu verschlüsseln.
Stellen Sie eine Verbindung her
Beim Herstellen der Verbindung generieren die Parteien Paare von temporären Schlüsseln x25519, und basierend auf diesen und statischen Schlüsseln werden Schlüsselsätze zum Übertragen von Daten berechnet. Es gibt auch die Authentifizierung statischer Schlüssel und die Einhaltung des Inhalts von RouterInfo.
Die Parteien tauschen drei Nachrichten aus:
SessionRequest ------------------->
<- SessionCreated
SessionConfirmed ----------------->
Für jeden von ihnen wird ein gemeinsamer Schlüssel x25519 berechnet, der als "Eingabeschlüsselmaterial" bezeichnet wird, und dann wird ein Nachrichtenverschlüsselungsschlüssel unter Verwendung der MixKey-Operation generiert, während der Wert ck (Verkettungsschlüssel) zwischen Nachrichten gespeichert wird und das Ergebnis ist, auf dessen Grundlage die Schlüssel für die Datenübertragung berechnet werden . Die MixKey-Implementierung sieht ungefähr so aus:
MixKey-Codevoid NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived) {
SessionRequest besteht aus einem 32-Byte-öffentlichen Schlüssel x25519 des Clients und einem verschlüsselten AEAD / Chacha20 / Poly1305-16-Byte-Datenblock + 16-Byte-Hash sowie einem zufälligen Datensatz (Auffüllen), dessen Länge im verschlüsselten Block übertragen wird. Dort wird auch die Länge der zweiten Hälfte der SessionConfirmed-Nachricht übertragen. Der Block wird verschlüsselt und mit einem Schlüssel signiert, der auf dem temporären Schlüssel des Clients und dem statischen Schlüssel des Servers basiert. Das anfängliche ck für MixKey ist auf SHA256 gesetzt ("Noise_XKaesobfse + hs2 + hs3_25519_ChaChaPoly_SHA256").
Da 32 Bytes des öffentlichen Schlüssels x25519 von dpi erkannt werden können, werden sie mit AES-256-CBC verschlüsselt, wobei der Schlüssel der Hash der Serveradresse ist und IV aus dem Parameter "i" der Adresse in RouterInfo entnommen wird.
Die Struktur von SessionCreated ähnelt der von SessionRequest, mit der Ausnahme, dass der Schlüssel auf der Grundlage temporärer Schlüssel beider Parteien berechnet wird und IV als IV für die Verschlüsselung / Entschlüsselung des öffentlichen Schlüssels nach der Entschlüsselung / Verschlüsselung des öffentlichen Schlüssels aus SessionRequest verwendet wird.
SessionConfirmed besteht aus zwei Teilen: dem statischen öffentlichen Schlüssel des Clients und der RouterInfo des Clients. Im Gegensatz zu früheren Nachrichten wird der öffentliche Schlüssel mit AEAD / Chaha20 / Poly1305 mit demselben Schlüssel wie SessionCreated verschlüsselt. Daher beträgt die Länge des ersten Teils nicht 32, sondern 48 Bytes. Der zweite Teil ist ebenfalls mit AEAD / Chaha20 / Poly1305 verschlüsselt, aber mit einem neuen Schlüssel berechnen wir ihn basierend auf dem temporären Schlüssel des Servers und dem statischen Schlüssel des Clients. RouterInfo kann auch ein Block zufälliger Daten hinzugefügt werden. Dies ist jedoch in der Regel nicht erforderlich, da die Länge von RouterInfo unterschiedlich ist.
Schlüsselgenerierung für die Datenübertragung
Wenn alle Überprüfungen von Hashes und Schlüsseln während des Verbindungsaufbaus erfolgreich waren, sollte nach dem letzten MixKey auf beiden Seiten derselbe ck vorhanden sein, aus dem auf jeder Seite 2 Sätze von Dreifachschlüsseln <k, sipk, sipiv> generiert werden, wobei k der AEAD-Schlüssel ist / Chaha20 / Poly1305, sipk ist der Schlüssel für Siphash, sipiv ist der anfängliche IV-Wert für Siphash, der sich nach jeder Anwendung ändert.
Code zum Generieren von Schlüsseln void NTCP2Session::KeyDerivationFunctionDataPhase () { uint8_t tempKey[32]; unsigned int len;
Die ersten 16 Bytes des Sipkeys-Arrays sind der Siphash-Schlüssel, die zweiten 8 Bytes sind IV.
Tatsächlich benötigt Siphash zwei Schlüssel mit jeweils 8 Bytes, aber in i2pd werden sie als 1 Schlüssel mit einer Länge von 16 Bytes betrachtet.
Datenübertragung
Daten werden in Frames übertragen, jeder Frame besteht aus 3 Teilen:
- 2 Bytes Frame-Länge, verschlüsselt von Siphash
- Daten von Chacha20 verschlüsselt
- 16 Bytes Poly1305-Hash
Die maximale Länge der übertragenen Daten in einem Frame beträgt 65519 Bytes.
Die Nachrichtenlänge wird mithilfe der XOR-Operation mit den ersten zwei Bytes des aktuellen IV-Siphash verschlüsselt.
Daten bestehen aus Blöcken. Vor jedem Block befindet sich ein 3-Byte-Header mit Blocktyp und -länge. Grundsätzlich werden I2NP-Blöcke übertragen, die I2NP-Nachrichten mit einem modifizierten Header enthalten. In einem Frame können mehrere I2NP-Blöcke übertragen werden.
Ein weiterer wichtiger Blocktyp ist ein zufälliger Datenblock, der jedem Frame hinzugefügt werden sollte. Es kann nur einer und der letzte sein.
Darüber hinaus gibt es in der aktuellen Implementierung von NTCP2 drei weitere Blocktypen:
- RouterInfo - Enthält normalerweise den RouterInfo-Server unmittelbar nach dem Herstellen der Verbindung. RouterInfo eines beliebigen Knotens kann jedoch jederzeit übertragen werden, um die Arbeit von Floodfills zu beschleunigen, für die das Flag-Feld in der Nachricht angegeben ist.
- Beendigung - wird vom Knoten gesendet, wenn die Verbindung von sich aus unterbrochen wird, und gibt den Grund an.
- DateTime - aktuelle Uhrzeit in Sekunden.
Somit ermöglicht das neue Transportprotokoll nicht nur, DPI effektiv zu widerstehen, sondern reduziert auch die CPU-Last aufgrund einer moderneren und schnelleren Kryptografie erheblich, was besonders wichtig ist, wenn an schwachen Geräten wie Smartphones und Routern gearbeitet wird. Derzeit ist die NTCP2-Unterstützung sowohl in offiziellem I2P als auch in i2pd vollständig implementiert und wird in den nächsten Versionen 0.9.36 bzw. 2.20 offiziell erscheinen. Um ntcp2 in i2pd zu aktivieren, geben Sie den Konfigurationsparameter ntcp2.enabled = true und ntcp2.published = true und ntcp2.port = <port> für eingehende Verbindungen an.