Les protocoles de transport I2P ont été développés il y a près de 15 ans, lorsque la tâche principale était de dissimuler le contenu du trafic, et non le fait d'utiliser l'un ou l'autre protocole. DPI (inspection approfondie des paquets) et le blocage du trafic n'étaient pas pris en compte à l'époque. Cependant, les temps changent et bien que les protocoles I2P existants soient encore assez bien protégés, il existe un besoin pour un nouveau protocole de transport qui réponde aux menaces existantes et futures, et, tout d'abord, DPI, qui analyse les longueurs de paquets. De plus, le nouveau protocole utilise les dernières avancées de la cryptographie. Une description complète du protocole est
ici . La base est
Noise , dans laquelle SHA256 est utilisé comme fonction de hachage, et x25519 comme DH (dans la terminologie Noise).
Nouvelle cryptographie
Pour NTCP2, en plus de ceux déjà existants dans I2P, il est nécessaire d'implémenter les algorithmes cryptographiques suivants:
- x25519
- HMAC-SHA256
- Chacha20
- Poly1305
- Aead
- Siphash
Tous, à l'exception de Siphash, sont implémentés dans openssl 1.1.0. Siphash, à son tour, apparaîtra dans openssl 1.1.1, qui sera publié sous peu. Pour la compatibilité avec openssl 1.0.2, qui est inclus dans la plupart des systèmes d'exploitation actuellement utilisés, i2pd a ajouté ses propres implémentations écrites par l'un des développeurs d'i2pd
Jeff Becker , connu sous le psi dans I2P.
Comparé au NTCP, x25519 remplace DH, AEAD / Chaha20 / Poly1305 remplace AES-256-CBC / Adler32, et Siphash est utilisé pour crypter la longueur des messages transmis. La procédure de calcul de la clé partagée est devenue plus complexe: avec de nombreux appels au HMAC-SHA256.
Modifications apportées à RouterInfo
Pour travailler sur le protocole NTCP2, en plus des deux clés existantes (cryptage et signature), une troisième clé x25519 est introduite, appelée clé statique, qui doit être présente dans une adresse RouterInfo en tant que paramètre «s» pour les clients et les serveurs. Si plusieurs adresses prennent en charge NTCP2, par exemple ipv4 et ipv6, alors "s" doit être le même partout. Pour les clients, l'adresse peut contenir uniquement «s» et ne pas contenir les paramètres «host» et «port». Le paramètre NTCP2 est également «v», actuellement toujours égal à «2».
L'adresse NTCP2 peut être définie comme une adresse de type «NTCP» avec des paramètres supplémentaires - dans ce cas, la connexion peut être établie en utilisant à la fois NTCP et NTCP2, ou comme une adresse de type NTCP2 qui prend en charge uniquement les connexions NTCP2. Dans Java I2P, la première méthode est utilisée, dans i2pd - la seconde.
Si l'hôte accepte les connexions NTCP2 entrantes, il doit publier le paramètre «i» avec la valeur IV pour crypter la clé publique lors de l'établissement de la connexion.
Établir une connexion
Au cours de l'établissement de la connexion, les parties génèrent des paires de clés temporaires x25519, et en fonction de celles-ci et des clés statiques, des ensembles de clés pour la transmission des données sont calculés. Les clés statiques sont également authentifiées et mises en correspondance avec le contenu de RouterInfo.
Les parties échangent trois messages:
SessionRequest ------------------->
<- SessionCreated
SessionConfirmed ----------------->
pour chacune desquelles une clé commune x25519 est calculée, appelée «matériau de clé d'entrée», puis une clé de chiffrement de message est générée à l'aide de l'opération MixKey, tandis que la valeur ck (clé de chaînage) est enregistrée entre les messages et est le résultat sur la base duquel les clés de transmission des données sont calculées . L'implémentation de MixKey ressemble à ceci:
Code MixKeyvoid NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived) {
SessionRequest se compose d'une clé publique de 32 octets x25519 du client et d'un bloc de données crypté AEAD / Chacha20 / Poly1305 de 16 octets + 16 octets de hachage, ainsi que d'un ensemble de données aléatoires (remplissage), dont la longueur est transmise dans le bloc crypté. La longueur de la seconde moitié du message SessionConfirmed y est également transmise. Le bloc est chiffré et signé avec une clé basée sur la clé temporaire du client et la clé statique du serveur. Le ck initial pour MixKey est défini sur SHA256 ("Noise_XKaesobfse + hs2 + hs3_25519_ChaChaPoly_SHA256").
Étant donné que 32 octets de la clé publique x25519 peuvent être reconnus par dpi, ils sont cryptés à l'aide d'AES-256-CBC, où la clé est le hachage de l'adresse du serveur, et IV est tiré du paramètre «i» de l'adresse dans RouterInfo.
La structure de SessionCreated est similaire à SessionRequest, sauf que la clé est calculée sur la base des clés temporaires des deux parties, et IV est utilisé comme IV pour le cryptage / décryptage de la clé publique après décryptage / cryptage de la clé publique à partir de SessionRequest.
SessionConfirmed se compose de deux parties: la clé publique statique du client et le RouterInfo du client. Contrairement aux messages précédents, la clé publique est chiffrée avec AEAD / Chaha20 / Poly1305 avec la même clé que SessionCreated. Par conséquent, la longueur de la première partie n'est pas de 32, mais de 48 octets. La deuxième partie est également chiffrée avec AEAD / Chaha20 / Poly1305, mais avec une nouvelle clé, nous la calculons en fonction de la clé temporaire du serveur et de la clé statique du client. En outre, un bloc de données aléatoires peut être ajouté à RouterInfo, mais, en règle générale, cela n'est pas nécessaire, car la longueur de RouterInfo est différente.
Génération de clés pour la transmission de données
Si toutes les vérifications des hachages et des clés pendant la configuration de la connexion ont réussi, alors après la dernière MixKey des deux côtés, il devrait y avoir le même ck, à partir duquel 2 jeux de triplets de clés <k, sipk, sipiv> seront générés de chaque côté, où k est la clé AEAD / Chaha20 / Poly1305, sipk est la clé pour Siphash, sipiv est la valeur IV initiale pour Siphash, qui change après chaque application.
Code générateur de clé void NTCP2Session::KeyDerivationFunctionDataPhase () { uint8_t tempKey[32]; unsigned int len;
Les 16 premiers octets du tableau sipkeys sont la clé Siphash, les 8 derniers octets sont IV.
En fait, Siphash nécessite deux clés de 8 octets chacune, mais dans i2pd, elles sont considérées comme une clé d'une longueur de 16 octets.
Transfert de données
Les données sont transmises en trames, chaque trame se compose de 3 parties:
- 2 octets de longueur de trame cryptés par Siphash
- données chiffrées par Chacha20
- 16 octets de hachage Poly1305
La longueur maximale des données transmises dans une trame est de 65519 octets.
La longueur du message est chiffrée à l'aide de l'opération XOR avec les deux premiers octets du Siphash IV actuel.
Les données sont constituées de blocs, chaque bloc est précédé d'un en-tête de 3 octets avec type et longueur de bloc. Fondamentalement, les blocs I2NP contenant des messages I2NP avec un en-tête modifié sont transmis. Dans une trame, plusieurs blocs I2NP peuvent être transmis.
Un autre type important de bloc est un bloc de données aléatoire, qu'il est recommandé d'ajouter à chaque trame. Ce ne peut être qu'un et le dernier.
En plus d'eux, dans l'implémentation actuelle de NTCP2, il existe 3 types de blocs supplémentaires:
- RouterInfo - contient généralement le serveur RouterInfo immédiatement après l'établissement de la connexion, mais RouterInfo d'un nœud arbitraire peut être transmis à tout moment afin d'accélérer le travail des inondations, pour lesquelles le champ indicateur est fourni dans le message.
- Résiliation - est envoyée par le nœud lorsque la connexion est interrompue à son initiative, indiquant la raison.
- DateTime - heure actuelle en secondes.
Ainsi, le nouveau protocole de transport permet non seulement de résister efficacement au DPI, mais réduit également considérablement la charge sur le processeur en raison d'une cryptographie plus moderne et plus rapide, ce qui est particulièrement important lorsque vous travaillez sur des appareils faibles tels que les smartphones et les routeurs. Actuellement, le support NTCP2 est entièrement implémenté dans les deux I2P et i2pd officiels et apparaîtra officiellement dans les prochaines versions 0.9.36 et 2.20, respectivement. Pour activer ntcp2 dans i2pd, spécifiez le paramètre de configuration ntcp2.enabled = true et ntcp2.published = true et ntcp2.port = <port> pour les connexions entrantes.