Source article oleh orignal di habr.com
I2P transport protocols mulanya dikembangkan sekitar 15 tahun lalu. Waktu itu , tujuan utamanya menyembunyikan data terkirim, bukan menyembunyikan fakta protocol sedang digunakan. Tidak ada yang serius melindungi terhadap DPI (deep packets inspection) dan sensor protocol. Jaman sudah berubah, dan walau transport protocols yang asli masih menyediakan keamanan kuat, ada kebutuhan untuk transport protocol baru. NTCP2 didesain untuk melawan ancaman sensor. Terutama, analisis DPI terhadap panjang packets. Ditambah protokol baru menggunakan perkembangan kriptografi modern terbaru. NTCP2 didasarkan dari Noise Protocol Framework, dengan SHA256 sebagai fungsi hash dan x25519 sebagai elliptic curve Diffie-Hellman (DH) key exchange.
Spek penih protokol NTCP2 dapat dilihat `di sini`_.
Kripto baru
NTCP2 memerlukan menambah cryptographic algorithms berikutnya ke implementasi I2P:
- x25519
- HMAC-SHA256
- Chacha20
- Poly1305
- AEAD
- SipHash
Dibandingkan protokal lama NTCP, NTCP2 menggunakan x25519, bukan ElGamal untuk fungsi DH, menggunakan AEAD/Chaha20/Poly1305 bukan AES-256-CBC/Adler32, dan menggunakan SipHash untuk melakukan obfuscating informasi panjang packet. Key derivation function digunakan di NTCP2 lebih rumit, menggunakan banyak HMAC-SHA256 calls.
Catatan penerapan i2pd (C++): Semua algoritma disebut di atas, kecuali SipHash, diterapkan di OpenSSL 1.1.0. SipHash akan ditambahkan di rilis OpenSSL 1.1.1 mendatang. Untuk kompatibilitas dengan OpenSSL 1.0.2, yang digunakan banyak sistem modern, pengembang inti i2pd Jeff Becker telah memberikan implementaso algoritma kriptografi yang hilang.
Perubahan RouterInfo
NTCP2 memerlukan kunci ketiga (x25519) selain dua kunci lain (enkripsinya dan kunci signature). Ini bernama kunci statis atau static key dan harus ditambah ke salah satu alamat RouterInfo sebagai sebuah "s" parameter. Ini diperlukan oleh initiator (Alice) dan responder (Bob) dari NTCP2. Jika lebih dari satu alamat mendukung NTCP2, contohnya IPv4 and IPv6, "s" harus sama di semua alamat. Alamat Alice dibolehkan hanya memiliki "s" parameter tanpa "host" dan "port" disetel. Juga, sebuah "v" parameter dibutuhkan, yang saat ini selalu ditulis dengan angka "2".
Alamat NTCP2 dapat diumumkan sebagai alamat terpisah NTCP2 atau sebagai alamat NTCP tipe lama dengan parameter tambahan, yang akan menerima koneksi NTCP dan NTCP2. Penerapan Java I2P menggunakan pendekatan kedua, i2pd (penerapan C++) menggunakan yang pertama.
Jika sebuah node menerima koneksi NTCP2, dia harus menyebarkan RouterInfo dengan "i" parameter, yang digunakan sebagai initialization vector (IV) untuk kunci enkripsi publik ketika node itu membuat koneksi baru.
Membuat sambungan
Untuk membuat sambungan atau koneksi, kedua pihak perlu membuat pasangan kunci ephemeral x25519. Berdasarkan kunci-kunci tadi dan kunci "statis", mereka membuat kumpulan kunci untuk transfer data. Kedua pihak harus memeriksa pasangannya benar-benar memiliki private key untuk setiap static key, dan static key tadi sama dengan di RouterInfo.
Tiga pesan dikirim untuk membuat sebuah koneksi:
Alice Bob SessionRequest -------------------> <------------------- SessionCreated SessionConfirmed ----------------->
Semuah common x25519 key, bernama «input key material», dibuat untuk setiap pesan, setelah message encryption key dibuat dengan MixKey function. Sebuah angka atau value ck (chaining key) disimpan sementara pesan-pesan dikirimkan. Angka tadi digunakan sebagai input terakhir ketika membuat kunci untuk transfer data.
MixKey function terlihat seperti ini di penerapan C++ I2P:
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 message is made of a public x25519 Alice key (32 bytes), a block of data encrypted with AEAD/Chacha20/Poly1305 (16 bytes), a hash (16 bytes) and some random data in the end (padding). Padding length didefinisikan di blok data yang terenkripsi. Blok terenkripsi juga berisi panjangnya bagian kedua dari pesan SessionConfirmed. Satu blok data dienkripsi dan ditandatangani dengan sebuah kunci yang diturunkan dari ephemeral key milik Alice dan static key milik Bob. Initial ck value untuk MixKey function dibuat ke SHA256 (Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256).
Karena public x25519 key sepanjang 32 bytes dapat dideteksi oleh DPI, dia dienkripsi dengan AES-256-CBC algorithm menggunakan hash dengan alamat Bob sebagai kunci dan "i" parameter dari RouterInfo sebagai initialization vector (IV).
Pesan SessionCreated memiliki struktur yang sama dengan SessionRequest, kecuali kuncinya dibuat berdasarkan ephemeral keys milik kedua pihak. IV dibuat setelah enkripsi/dekripsi public key dari pesan**SessionRequest** digunakan sebagai IV untuk enkripsi/dekripsi ephemeral public key.
Pesan SessionConfirmed memiliki dua bagian: public static key dan RouterInfo milik Alice. Bedanya dengan pesan-pesan sebelumnya adalahephemeral public key dienkripsi dengan AEAD/Chaha20/Poly1305 menggunakan kunci yang sama dengan SessionCreated. Ini menambah besarnya bagian pertama pesan dari 32 menjadi 48 bytes. Bagian kedua juga dienkripsi dengan AEAD/Chaha20/Poly1305, tapi menggunakan kunci baru, yang dibuat dari ephemeral key milik Bob dan static key milik Alice. RouterInfo part juga dapat di-append dengan random data padding, tapi tidak dibutuhkan, karena RouterInfo biasanya mempunyai panjang beragam.
Pembuatan data transfer keys
Jika semua hash dan verifikasi kunci berhasil, sebuah common ck value seharusnya muncul setelah operasi MixKey terakhir di kedua pihak. Nilai atau value ini digunakan untuk membuat dua kunci <k, sipk, sipiv> untuk setiap sisi koneksi. "k" adalah kunci AEAD/Chaha20/Poly1305, "sipk" adalah kunci SipHash, "sipiv" adalah initial value untuk SipHash IV, yang diubah setiap penggunaan.
Kode yang digunakan untuk membuat kunci terlihat seperti ini di implementasi C++ I2P:
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); }
i2pd (C++) implementation note: First 16 bytes of the "sipkeys" array are a SipHash key, the last 8 bytes are IV. SipHash memerlukan dua kunci 8 byte, tapi i2pd menanganinya sebagai satu kunci 16 bytes.
Transfer Data
Data dikirim dalam frame, setiap frame memiliki 3 bagian:
- fram sepanjang 2 bytes dilakukan obfuscated dengan SipHash
- data dienkripsi dengan Chacha20
- Poly1305 hash value sepanjang 16 bytes
Maksimum panjang data terkirim dalam satu frame is 65519 bytes.
Panjang pesan di-obfuscated dengan menerapkan XOR function dengan dua bytes pertama dari SipHash IV terkini.
Potongan data yang terenkripsi berisi blok data. Di awal setiap blok ditambah 3 bytes header, yang mendefinisikan tipe dan panjang blok. Umumnya, blok tipe I2NP ditransfer, yang berupa pesan-pesan I2NP dengan header yang diubah. Satu NTCP2 frame dapat mentransfer banyak blok I2NP.
Tipe penting blok data lain adalah blok data acak. Disarankan untuk menambah blok data acak ke setiap NTCP2 frame. Hanya ada satu blok data acak yang dapat ditambahkan ant kepadanya dan harus blok terakhir.
Itu adalah blok data lain yang digunakan dalam implementasi NTCP2 saat ini:
- RouterInfo — usually contains Bob's RouterInfo after the connection has been established, but it can also contain RouterInfo of a random node for the purpose of speeding up floodfills (there is a flags field for that case).
- Termination — is used when a host explicitly terminates a connection and specifies a reason for that.
- DateTime — a current time in seconds.
Ringkasan
Protokol transport baru I2P NTCP2 menyediakan kekebalan efektif terhadap sensor DPI. Ini juga menghasilkan kurangnya beban CPU karena kriptografi yang digunakan lebih cepat dan modern. Ini membuat I2P lebih dapat dijalankan di alat berspesifikasi rendah, seperti smartphones dan home routers. Kedua implementasi utama I2P mendukung penuh NTCP2 dan menyediakan NTCP2 mulai dari versi 0.9.36 (Java) dan 2.20 (i2pd, C++).