
大家好!
我喜欢Tox,并尊重这个项目及其工作的参与者。 为了帮助Tox开发人员和用户,我仔细研究了代码并注意到可能导致错误的安全感的潜在问题。 自从我最初于2016年以俄语发布此文章以来,Tox进行了许多改进,并且我领导的团队使用Rust编程语言从头重新编写了安全的Tox软件(请参阅Tox-rs )。 我确实建议在2019年使用Tox。让我们看一下实际上使我们在Rust中重写Tox的原因。
2016年的原创文章
有一种不健康的趋势,就是仅基于E2E系统来高估E2E系统的安全性。 我将提出客观事实,并附上我自己的评论,以便您得出自己的结论。
剧透:Tox开发人员同意我的观点,并且接受了我的源代码提取请求 。
一切始于有关Habr的文章(关于安装该节点的文章)( 俄语 )。
在评论中,人们抱怨在CentOS上构建和安装节点的复杂性,因此我决定在CMake上编写一个构建系统。 几天后,我准备向Freenode上的Tox社区介绍我的PR,但是我感到缺乏理解:
最初有人提供了cmake,但是其他开发人员不知道如何使用它,也无法使其构建代码,因此他们改用了autotools(原文如此!),现在他们变得更加了解。
我注意到,在Travis CI中未通过测试的代码仍被master分支接受,但他们回答:“我们知道我们需要对测试进行某些操作,但现在就解决。”
接下来,我深入研究了这个诱人的Messenger的代码。
事实2。 memset(ptr,0,size)在免费呼叫之前
我的眼睛被抓住了
memset(c, 0, sizeof(Net_Crypto)); free(c);
如果您仍然不熟悉PVS-Studio及其有关memset函数 PVS-Studio的文章:如果此后不使用该内存区域,则编译器可以删除“ memset”函数调用。 编译器的逻辑很简单:“调用'free'之后,您将不使用此变量,memset不会影响观察到的行为,让我删除对'memset'的无用调用”。
作为一个勤奋的学生,我将每次出现的memset($DST, 0, $SIZE)
替换为sodium_memzero和TESTS CRASHED。
事实3。 比较公钥容易受到定时攻击
有一个很棒的特殊功能可以比较toxcore
中的toxcore
:
int public_key_cmp(const uint8_t *pk1, const uint8_t *pk2) { return crypto_verify_32(pk1, pk2); }
crypto_verify_32-是NaCL / 钠密码库中的一个函数,可以帮助您避免定时攻击,因为它可以在恒定时间内工作,而memcmp可以在第一个不相等的字节上中断。 您应该使用crypto_verify_32比较敏感数据,例如密钥。
逐字节执行的字符串比较容易受到定时攻击的攻击,例如为了伪造MAC(请参阅Google的Keyczar密码库中的此 漏洞 )。
toxcore项目的代码库非常广泛,这就是Tox诞生时带有计时漏洞的原因:
bool id_equal(const uint8_t *dest, const uint8_t *src) { return memcmp(dest, src, crypto_box_PUBLICKEYBYTES) == 0; }
但这还不是全部。 开发人员仍然喜欢使用三种不同的功能以自己的方式比较密钥: id_equal或public_key_cmp和crypto_verify_32 。
这是DHT,洋葱路由和其他关键子系统的简短grep输出:
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)
事实4。 非恒定时间内的increment_nonce
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;
如果此类操作涉及秘密参数,则这些时序变化可能会泄漏某些信息。 有了足够的执行知识,仔细的统计分析甚至可能导致秘密参数的完全恢复。
钠中有一个特殊功能可以增加随机数:
一个不具讽刺意味的复活节彩蛋是, increment_nonce函数位于以以下单词开头的文件中:
此代码必须是完美的。 我们不会搞乱加密。
让我们仔细看看这个完美的文件。
事实5。 您可以在堆栈中找到密钥和私有数据
麻烦的代码:
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);
crypto_data_symmetric从Nacl / Sodium调用crypto_box_detached_afternm ,我不会输入整个代码,这是一个检查自己的链接 。
在四行代码中似乎很难犯错,不是吗?
让我们深入研究钠:
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; }
消除所有检查,我们得到:
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;
看起来熟悉吗? 是的 它是toxcore中的函数crypto_data的经过稍微修改的代码,唯一的区别是他们忘记了使用sodium_memzero清理堆栈上的密钥 ...而且还存在错误: handle_TCP_handshake , handle_handshake ,也许还有其他地方。
事实6。 编译器警告适用于dummiez!
来自toxcore项目的开发人员断然拒绝打开所有编译器警告的必要性,或者他们不知道它们。
未使用的功能(我对测试中的警告特别满意):
../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__,\ ^
还有几十条关于未使用变量,有符号和无符号比较的警告等等。
我的结论
来自资源库的报价:
我们希望Tox尽可能简单,同时又要保持尽可能的安全。
如果我(不是密码学家)一天可以找到如此可怕的错误,想象一下密码专家在有意挖掘一个月后能找到多少东西?
Tox的早期版本给依赖Tox安全性的用户带来了极大的危险。 专有解决方案不值得信赖,甚至开源解决方案也不如您希望的那样安全。 看一下Matrix中最近的安全漏洞。 如今,许多错误已得到修复,Tox是为用户提供安全性和隐私性的最佳选择。
下次我将告诉您有关tox-rs当前状态的更多信息。 我们在Rust中实现了什么以及为什么应该尝试。
Reddit: 评论