تنفيذ بروتوكول النقل NTCP2 الجديد لشبكة I2P

تم تطوير بروتوكولات النقل I2P قبل 15 عامًا تقريبًا ، عندما كانت المهمة الرئيسية هي إخفاء محتويات حركة المرور ، وليس حقيقة استخدام بروتوكول أو آخر. لم يتم أخذ DPI (فحص الحزم العميق) وحظر حركة المرور في الاعتبار في ذلك الوقت. ومع ذلك ، تتغير الأوقات ، وعلى الرغم من أن بروتوكولات I2P الحالية لا تزال محمية بشكل جيد ، هناك حاجة إلى بروتوكول نقل جديد للاستجابة للتهديدات الحالية والمستقبلية ، وقبل كل شيء ، DPI ، التي تحلل أطوال الحزم. بالإضافة إلى ذلك ، يستخدم البروتوكول الجديد أحدث التطورات في التشفير. الوصف الكامل للبروتوكول هنا . الأساس هو الضوضاء ، حيث يتم استخدام SHA256 كوظيفة تجزئة ، و x25519 كـ DH (في مصطلحات الضوضاء).

الصورة

تشفير جديد


بالنسبة لـ NTCP2 ، بالإضافة إلى تلك الموجودة بالفعل في I2P ، من الضروري تنفيذ خوارزميات التشفير التالية:

  • × 25519
  • HMAC-SHA256
  • Chacha20
  • بولي 1305
  • Aead
  • سيفاش

جميعها ، باستثناء سيفاش ، تم تنفيذها في opensl 1.1.0. سيظهر Siphash بدوره في opensl 1.1.1 ، والذي سيتم إصداره قريبًا. للتوافق مع opensl 1.0.2 ، والذي تم تضمينه في معظم أنظمة التشغيل المستخدمة حاليًا ، أضافت i2pd تطبيقاتها الخاصة التي كتبها أحد مطوري i2pd Jeff Becker ، والمعروف باسم psi في I2P.

مقارنة بـ NTCP ، يستبدل x25519 DH ، ويحل AEAD / Chaha20 / Poly1305 محل AES-256-CBC / Adler32 ، ويستخدم Siphash لتشفير طول الرسائل المرسلة. أصبح إجراء حساب المفتاح المشترك أكثر تعقيدًا: مع العديد من المكالمات إلى HMAC-SHA256.

التغييرات على RouterInfo


للعمل على بروتوكول NTCP2 ، بالإضافة إلى المفتاحين الحاليين (التشفير والتوقيع) ، يتم تقديم مفتاح ثالث x25519 ، يسمى مفتاح ثابت ، والذي يجب أن يكون موجودًا في بعض عناوين RouterInfo كمعلمة "s" لكل من العملاء والخوادم. إذا كان هناك أكثر من عنوان يدعم NTCP2 ، على سبيل المثال ipv4 و ipv6 ، فيجب أن يكون "s" هو نفسه في كل مكان. بالنسبة للعملاء ، قد يحتوي العنوان على "s" فقط ولا يحتوي على المعلمتين "host" و "port". المعلمة المطلوبة أيضًا لـ NTCP2 هي "v" ، وهي حاليًا تساوي دائمًا "2".

يمكن تعيين عنوان NTCP2 كعنوان نوع "NTCP" مع معلمات إضافية - في هذه الحالة ، يمكن تأسيس الاتصال باستخدام كل من NTCP و NTCP2 ، أو كعنوان من نوع NTCP2 يدعم اتصالات NTCP2 فقط. في Java I2P ، يتم استخدام الطريقة الأولى ، في i2pd - الثانية.

إذا قبل المضيف اتصالات NTCP2 الواردة ، فيجب عليه نشر المعلمة "i" بالقيمة IV لتشفير المفتاح العام عند إنشاء الاتصال.

إنشاء اتصال


في عملية إنشاء الاتصال ، تقوم الأطراف بتوليد أزواج من المفاتيح المؤقتة x25519 ، وعلى أساسها والمفاتيح الثابتة ، يتم حساب مجموعات المفاتيح لإرسال البيانات. يتم أيضًا مصادقة المفاتيح الثابتة ومطابقتها مع محتويات RouterInfo.

يتبادل الطرفان ثلاث رسائل:

SessionRequest ------------------->
<- SessionCreated
تم تأكيد الجلسة ----------------->

لكل منها يتم حساب مفتاح مشترك x25519 ، يسمى "مادة مفتاح الإدخال" ، ثم يتم إنشاء مفتاح تشفير الرسائل باستخدام عملية MixKey ، في حين يتم حفظ القيمة ck (مفتاح تسلسل) بين الرسائل وهي النتيجة التي يتم على أساسها حساب مفاتيح نقل البيانات . يبدو تطبيق MixKey شيئًا مثل هذا:

رمز MixKey
void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived) { // temp_key = HMAC-SHA256(ck, input_key_material) uint8_t tempKey[32]; unsigned int len; HMAC(EVP_sha256(), m_CK, 32, inputKeyMaterial, 32, tempKey, &len); // ck = HMAC-SHA256(temp_key, byte(0x01)) static uint8_t one[1] = { 1 }; HMAC(EVP_sha256(), tempKey, 32, one, 1, m_CK, &len); // derived = HMAC-SHA256(temp_key, ck || byte(0x02)) m_CK[32] = 2; HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, derived, &len); } 


يتكون SessionRequest من مفتاح عمومي 32 بايت x25519 للعميل ، وكتلة بيانات مشفرة AEAD / Chacha20 / Poly1305 16 بايت + تجزئة 16 بايت ، بالإضافة إلى مجموعة بيانات عشوائية (حشو) ، يتم إرسال طولها في الكتلة المشفرة. يتم أيضًا إرسال طول النصف الثاني من رسالة SessionConfirm هناك. يتم تشفير الكتلة وتوقيعها باستخدام مفتاح يستند إلى المفتاح المؤقت للعميل والمفتاح الثابت للخادم. يتم تعيين ck الأولي لـ MixKey على SHA256 ("Noise_XKaesobfse + hs2 + hs3_25519_ChaChaPoly_SHA256").

نظرًا لأنه يمكن التعرف على 32 بايت من المفتاح العام x25519 بواسطة dpi ، يتم تشفيرها باستخدام AES-256-CBC ، حيث يكون المفتاح هو تجزئة عنوان الخادم ، ويتم أخذ IV من المعلمة "i" للعنوان في RouterInfo.

يشبه SessionCreated في البنية SessionRequest ، باستثناء أنه يتم احتساب المفتاح على أساس المفاتيح المؤقتة لكلا الطرفين ، ويتم استخدام IV بمثابة IV لتشفير / فك تشفير المفتاح العام بعد فك تشفير / تشفير المفتاح العام من SessionRequest.

يتكون SessionConfertain من جزأين: المفتاح العام الثابت للعميل و RouterInfo للعميل. على عكس الرسائل السابقة ، يتم تشفير المفتاح العام باستخدام AEAD / Chaha20 / Poly1305 بنفس المفتاح مثل SessionCreated. لذلك ، طول الجزء الأول ليس 32 ، ولكن 48 بايت. يتم تشفير الجزء الثاني أيضًا باستخدام AEAD / Chaha20 / Poly1305 ، ولكن باستخدام مفتاح جديد ، نقوم بحسابه بناءً على المفتاح المؤقت للخادم والمفتاح الثابت للعميل. أيضا ، يمكن إضافة كتلة بيانات عشوائية إلى RouterInfo ، ولكن ، كقاعدة عامة ، هذا ليس ضروريًا لأن طول RouterInfo مختلف.

توليد المفاتيح لنقل البيانات


إذا نجحت جميع عمليات التحقق من التجزئات والمفاتيح أثناء إعداد الاتصال ، فيجب أن يكون هناك نفس ck بعد آخر MixKey على كلا الجانبين ، حيث سيتم إنشاء مجموعتين من ثلاثة أضعاف المفاتيح <k و sipk و sipiv> على كل جانب ، حيث k هو مفتاح AEAD / Chaha20 / Poly1305 ، sipk هو مفتاح Siphash ، sipiv هو قيمة IV الأولية لـ Siphash ، والتي تتغير بعد كل تطبيق.

كود توليد المفتاح
 void NTCP2Session::KeyDerivationFunctionDataPhase () { uint8_t tempKey[32]; unsigned int len; // temp_key = HMAC-SHA256(ck, zerolen) HMAC(EVP_sha256(), m_Establisher->GetCK (), 32, nullptr, 0, tempKey, &len); static uint8_t one[1] = { 1 }; // k_ab = HMAC-SHA256(temp_key, byte(0x01)). HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Kab, &len); m_Kab[32] = 2; // k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02)) HMAC(EVP_sha256(), tempKey, 32, m_Kab, 33, m_Kba, &len); static uint8_t ask[4] = { 'a', 's', 'k', 1 }, master[32]; // ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01)) HMAC(EVP_sha256(), tempKey, 32, ask, 4, master, &len); uint8_t h[39]; memcpy (h, m_Establisher->GetH (), 32); memcpy (h + 32, "siphash", 7); // temp_key = HMAC-SHA256(ask_master, h || "siphash") HMAC(EVP_sha256(), master, 32, h, 39, tempKey, &len); // sip_master = HMAC-SHA256(temp_key, byte(0x01)) HMAC(EVP_sha256(), tempKey, 32, one, 1, master, &len); // temp_key = HMAC-SHA256(sip_master, zerolen) HMAC(EVP_sha256(), master, 32, nullptr, 0, tempKey, &len); // sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)). HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Sipkeysab, &len); m_Sipkeysab[32] = 2; // sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02)) HMAC(EVP_sha256(), tempKey, 32, m_Sipkeysab, 33, m_Sipkeysba, &len); } 


أول 16 بايت من مجموعة sipkeys هي مفتاح Siphash ، والثانية 8 بايت هي IV.
في الواقع ، يتطلب Siphash مفتاحين لكل منهما 8 بايت ، ولكن في i2pd يتم اعتبارهما مفتاحًا واحدًا بطول 16 بايت.

نقل البيانات


يتم إرسال البيانات في إطارات ، يتكون كل إطار من 3 أجزاء:

  1. 2 بايت من طول الإطار المشفر بواسطة Siphash
  2. البيانات المشفرة بواسطة Chacha20
  3. 16 بايت من تجزئة Poly1305

يبلغ الحد الأقصى لطول البيانات المرسلة في إطار واحد 65519 بايت.

يتم تشفير طول الرسالة باستخدام عملية XOR مع أول وحدتي بايت من IV Siphash الحالي.

تتكون البيانات من كتل ، ويسبق كل كتلة رأس 3 بايت مع نوع الكتلة وطولها. بشكل أساسي ، يتم إرسال كتل I2NP التي تحتوي على رسائل I2NP برأس معدل. في إطار واحد ، يمكن إرسال عدة كتل I2NP.

نوع آخر مهم من الكتل هو كتلة بيانات عشوائية ، يوصى بإضافتها إلى كل إطار. يمكن أن تكون واحدة فقط والأخيرة.

بالإضافة إلى ذلك ، في التنفيذ الحالي لـ NTCP2 هناك 3 أنواع أخرى من الكتل:

  • RouterInfo - عادة ما يحتوي على خادم RouterInfo مباشرة بعد إنشاء الاتصال ، ولكن يمكن إرسال RouterInfo لأي عقدة في أي وقت من أجل تسريع عمل الفيضانات ، والتي يتم توفير حقل العلم لها في الرسالة.
  • الإنهاء - يتم إرساله بواسطة العقدة عند قطع الاتصال بمبادرة منه ، مشيرًا إلى السبب.
  • DateTime - الوقت الحالي بالثواني.

وبالتالي ، لا يسمح بروتوكول النقل الجديد بمقاومة DPI بشكل فعال فحسب ، بل يقلل أيضًا بشكل كبير من حمل وحدة المعالجة المركزية بسبب التشفير الأكثر حداثة وأسرع ، وهو أمر مهم بشكل خاص عند العمل على الأجهزة الضعيفة مثل الهواتف الذكية وأجهزة التوجيه. حاليًا ، يتم تنفيذ دعم NTCP2 بالكامل في كل من I2P و i2pd الرسمي وسيظهر رسميًا في الإصدارين التاليين 0.9.36 و 2.20 على التوالي. لتمكين ntcp2 في i2pd ، حدد معلمة التكوين ntcp2.enabled = true ، و ntcp2.published = true و ntcp2.port = <port> للاتصالات الواردة.

Source: https://habr.com/ru/post/ar416785/


All Articles