
Hai semuanya!
Saya suka Tox dan menghormati para peserta proyek ini dan pekerjaan mereka. Dalam upaya membantu pengembang dan pengguna Tox, saya melihat ke dalam kode dan melihat potensi masalah yang dapat mengarah pada rasa aman yang salah. Sejak saya awalnya menerbitkan artikel ini pada tahun 2016 ( dalam bahasa Rusia ), banyak perbaikan telah dilakukan untuk Tox, dan saya memimpin sebuah tim yang menulis ulang perangkat lunak Tox aman dari awal menggunakan bahasa pemrograman Rust (lihat Tox-rs ). Saya sarankan menggunakan racun pada tahun 2019. Mari kita lihat apa yang sebenarnya membuat kita menulis ulang Tox di Rust.
Artikel asli 2016
Ada kecenderungan tidak sehat untuk melebih-lebihkan keamanan sistem E2E hanya atas dasar bahwa mereka E2E. Saya akan menyajikan fakta-fakta obyektif yang dilengkapi dengan komentar saya sendiri agar Anda menarik kesimpulan sendiri.
Spoiler: Pengembang Tox setuju dengan poin saya dan permintaan tarik kode sumber saya diterima.
Fakta No. 1. cabang utama gagal tes
Semuanya dimulai dengan artikel tentang Habr tentang menginstal node ( dalam bahasa Rusia ).
Dalam komentar, orang-orang mengeluh tentang kerumitan membangun dan menginstal node pada CentOS, jadi saya memutuskan untuk menulis sistem build di CMake. Setelah beberapa hari, saya siap untuk mempresentasikan PR saya ke komunitas Tox di Freenode, tetapi saya menemui kekurangan pemahaman:
seseorang telah berkontribusi pada cmake awalnya, tetapi pengembang lain tidak tahu bagaimana menggunakannya dan tidak dapat membuatnya membangun kode mereka, jadi mereka beralih ke autotools (sic!), yang mereka menjadi tahu lebih baik sekarang.
Saya mencatat bahwa kode yang gagal dalam tes di Travis CI masih diterima di cabang utama, tetapi mereka menjawab: "kami mengerti kami perlu melakukan sesuatu dengan tes, tetapi biarkan untuk sekarang."
Selanjutnya, saya menyelam lebih dalam untuk melihat kode dari utusan yang menarik ini.
Fakta No. 2. memset (ptr, 0, size) sebelum menelepon gratis
Mataku tertangkap
memset(c, 0, sizeof(Net_Crypto)); free(c);
Jika Anda masih belum terbiasa dengan PVS-Studio dan artikelnya tentang fungsi memset PVS-Studio: kompiler dapat menghapus panggilan fungsi 'memset' jika wilayah memori tersebut tidak digunakan setelahnya. Logika kompiler sangat mudah: "Anda tidak akan menggunakan variabel ini setelah memanggil 'gratis', memset tidak akan mempengaruhi perilaku yang diamati, izinkan saya menghapus panggilan tidak berguna ini ke 'memset'".
Sebagai siswa yang rajin, saya mengganti setiap kemunculan memset($DST, 0, $SIZE)
dengan sodium_memzero dan TEST CRASHED.
Fakta No. 3. Perbandingan kunci publik rentan terhadap serangan waktu
Ada fungsi khusus yang sangat bagus untuk membandingkan kunci publik di toxcore
:
int public_key_cmp(const uint8_t *pk1, const uint8_t *pk2) { return crypto_verify_32(pk1, pk2); }
crypto_verify_32 - adalah fungsi dari pustaka NaCL / Sodium crypto, yang dapat membantu Anda menghindari serangan timing, karena ia bekerja dalam waktu yang konstan, sementara memcmp dapat merusak byte pertama yang tidak sama. Anda harus menggunakan crypto_verify_32 untuk membandingkan data sensitif seperti kunci.
Perbandingan string yang dilakukan byte-per-byte rentan terhadap eksploitasi oleh serangan timing, misalnya untuk memalsukan MAC (lihat kerentanan ini di perpustakaan Google Keyczar crypto).
Basis kode dari proyek toxcore cukup luas, itulah sebabnya Tox dilahirkan dengan bug kerentanan waktu:
bool id_equal(const uint8_t *dest, const uint8_t *src) { return memcmp(dest, src, crypto_box_PUBLICKEYBYTES) == 0; }
Tapi itu belum semuanya. Pengembang masih lebih suka membandingkan kunci dengan caranya sendiri menggunakan tiga fungsi berbeda: id_equal atau public_key_cmp dan crypto_verify_32 .
Berikut ini adalah keluaran grep pendek dari DHT, routing bawang merah dan subsistem penting lainnya:
if (memcmp(ping->to_ping[i].public_key, public_key, crypto_box_PUBLICKEYBYTES) == 0) { if (memcmp(public_key, onion_c->friends_list[i].real_public_key, crypto_box_PUBLICKEYBYTES) == 0) if (memcmp(public_key, onion_c->path_nodes_bs[i].public_key, crypto_box_PUBLICKEYBYTES) == 0) if (memcmp(dht_public_key, dht_public_key_temp, crypto_box_PUBLICKEYBYTES) != 0) if (Local_ip(ip_port.ip) && memcmp(friend_con->dht_temp_pk, public_key, crypto_box_PUBLICKEYBYTES) == 0)
Fakta No. 4. increment_nonce dalam waktu yang tidak konstan
void increment_nonce(uint8_t *nonce) { uint32_t i; for (i = crypto_box_NONCEBYTES; i != 0; --i) { ++nonce[i - 1]; if (nonce[i - 1] != 0) break;
Jika operasi tersebut melibatkan parameter rahasia, variasi waktu ini dapat membocorkan beberapa informasi. Dengan pengetahuan implementasi yang cukup, analisis statistik yang cermat bahkan dapat mengarah pada pemulihan total parameter rahasia.
Ada fungsi khusus dalam Sodium untuk meningkatkan nonces:
Telur paskah yang tidak disengaja ironis adalah bahwa fungsi increment_nonce terletak di file yang dimulai dengan kata-kata:
Kode ini harus sempurna. Kami tidak main-main dengan enkripsi.
Mari kita lihat lebih dekat file yang sempurna ini.
Fakta No. 5. Anda dapat menemukan kunci dan data pribadi di tumpukan
Kode merepotkan:
void encrypt_precompute(const uint8_t *public_key, const uint8_t *secret_key, uint8_t *enc_key) { crypto_box_beforenm(enc_key, public_key, secret_key);
encrypt_data_symmetric calls crypto_box_detached_afternm dari Nacl / Sodium, saya tidak akan memasukkan seluruh kode, ini adalah tautan untuk memeriksa sendiri.
Tampaknya sulit membuat kesalahan dalam empat baris kode, bukan?
Mari kita menggali Sodium:
int crypto_box_detached(unsigned char *c, unsigned char *mac, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *pk, const unsigned char *sk) { unsigned char k[crypto_box_BEFORENMBYTES]; int ret; (void) sizeof(int[crypto_box_BEFORENMBYTES >= crypto_secretbox_KEYBYTES ? 1 : -1]); if (crypto_box_beforenm(k, pk, sk) != 0) { return -1; } ret = crypto_box_detached_afternm(c, mac, m, mlen, n, k); sodium_memzero(k, sizeof k); return ret; }
Menghapus semua cek yang kami dapatkan:
unsigned char k[crypto_box_BEFORENMBYTES]; int ret; crypto_box_beforenm(k, pk, sk); ret = crypto_box_detached_afternm(c, mac, m, mlen, n, k); sodium_memzero(k, sizeof k); return ret;
Apakah itu terlihat familier? Ya! Ini adalah kode fungsi yang sedikit dimodifikasi, mengenkripsi data dari toxcore, satu-satunya perbedaan adalah mereka lupa membersihkan kunci pada stack dengan sodium_memzero ... Dan ada juga kesalahan di: handle_TCP_handshake , handle_handshake , dan mungkin di tempat lain juga.
Fakta No. 6. Peringatan kompiler adalah untuk dummiez!
Para dev dari proyek toxcore dengan tegas menyangkal perlunya menghidupkan semua peringatan kompiler, atau mereka tidak tahu tentang mereka.
Fungsi yang tidak digunakan (Saya sangat senang dengan peringatan dalam pengujian):
../auto_tests/dht_test.c:351:12: warning: unused function 'test_addto_lists_ipv4' [-Wunused-function] START_TEST(test_addto_lists_ipv4) ^ ../auto_tests/dht_test.c:360:12: warning: unused function 'test_addto_lists_ipv6' [-Wunused-function] START_TEST(test_addto_lists_ipv6) ^ ../toxcore/TCP_server.c:1026:13: warning: unused function 'do_TCP_accept_new' [-Wunused-function] static void do_TCP_accept_new(TCP_Server *TCP_server) ^ ../toxcore/TCP_server.c:1110:13: warning: unused function 'do_TCP_incomming' [-Wunused-function] static void do_TCP_incomming(TCP_Server *TCP_server) ^ ../toxcore/TCP_server.c:1119:13: warning: unused function 'do_TCP_unconfirmed' [-Wunused-function] static void do_TCP_unconfirmed(TCP_Server *TCP_server) ^
../toxcore/Messenger.c:2040:28: warning: comparison of constant 256 with expression of type 'uint8_t' (aka 'unsigned char') is always false [-Wtautological-constant-out-of-range-compare] if (filenumber >= MAX_CONCURRENT_FILE_PIPES) ~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~ ../toxcore/Messenger.c:2095:28: warning: comparison of constant 256 with expression of type 'uint8_t' (aka 'unsigned char') is always false [-Wtautological-constant-out-of-range-compare] if (filenumber >= MAX_CONCURRENT_FILE_PIPES) ~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~ ../toxcore/Messenger.c:2110:28: warning: comparison of constant 256 with expression of type 'uint8_t' (aka 'unsigned char') is always false [-Wtautological-constant-out-of-range-compare] if (filenumber >= MAX_CONCURRENT_FILE_PIPES) ~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~
../auto_tests/TCP_test.c:205:24: warning: unsequenced modification and access to 'len' [-Wunsequenced] ck_assert_msg((len = recv(con->sock, data, length, 0)) == length, "wrong len %i\n", len); ^ ~~~ /usr/include/check.h:273:18: note: expanded from macro 'ck_assert_msg' _ck_assert_msg(expr, __FILE__, __LINE__,\ ^
Dan beberapa lusin peringatan tentang variabel yang tidak digunakan, perbandingan ditandatangani dan tidak ditandatangani, dan banyak lagi.
Kesimpulan saya
Kutipan dari repositori:
Kami ingin Tox sesederhana mungkin sambil tetap seaman mungkin.
Jika saya, seorang non-kriptografi, dapat menemukan bug mengerikan dalam sehari, bayangkan berapa banyak hal yang dapat ditemukan oleh spesialis kriptografi setelah sengaja menggali selama sebulan?
Versi awal Tox menimbulkan bahaya besar bagi pengguna yang mengandalkan keamanan Tox. Solusi eksklusif tidak layak dipercaya dan bahkan solusi open source tidak seaman yang Anda inginkan. Lihatlah pelanggaran keamanan terbaru di Matrix . Saat ini banyak bug diperbaiki dan Tox adalah pilihan terbaik yang tersedia untuk keamanan dan privasi bagi pengguna.
Lain kali saya akan memberi tahu Anda lebih lanjut tentang keadaan racun saat ini. Apa yang telah kami terapkan di Rust dan mengapa Anda harus mencobanya.
Reddit: komentar