diff --git a/src/chat/chat_join.c b/src/chat/chat_join.c index f3f20b3d..be09d92a 100644 --- a/src/chat/chat_join.c +++ b/src/chat/chat_join.c @@ -140,6 +140,14 @@ void chat_join_process_request(struct UTUN_INSTANCE* inst, uint64_t group_id, char ch_id[32]; snprintf(ch_id, sizeof(ch_id), "%llu", (unsigned long long)group_id); sqlite3* db = inst->topo_sqlite_db; + /* 0. верификация join-ключа: принимаем только выданные нами (инвайтером) ключи */ + struct join_key_entry* ke = _keys_find(group_id, join_key); + if (!ke || ke->inviter_id != inst->node_id) { + DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "%s: process_request invalid join key ch=%s from=%016llx key=%016llx — drop", + CJ_ID, ch_id, (unsigned long long)from_node, (unsigned long long)join_key); + return; + } + /* 1. привязка идентичности: node_id = derive(x25519) */ if (sc_derive_node_id_from_pubkey(m.x25519) != m.node_id) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: process_request node_id/x25519 mismatch (forgery) nid=0x%016llx ch=%s", @@ -245,9 +253,10 @@ void chat_join_register_key(struct UTUN_INSTANCE* inst, uint64_t channel_id, uint64_t target_node_id, uint64_t join_key) { if (!inst || channel_id == 0 || join_key == 0) return; + /* инвайтер всегда хранит выданный ключ локально — для верификации REQUEST_FWD */ + _keys_put(channel_id, join_key, inst->node_id); + if (target_node_id == inst->node_id || target_node_id == 0) { - /* инвайтер и есть connection-узел — храним ключ локально */ - _keys_put(channel_id, join_key, inst->node_id); return; } diff --git a/tests/test_chat_join.c b/tests/test_chat_join.c index da7df1be..5d2d3b01 100644 --- a/tests/test_chat_join.c +++ b/tests/test_chat_join.c @@ -388,6 +388,18 @@ static void t_process_request(void) { uint64_t group_id = strtoull(CH_COMP, NULL, 10); + /* 0. неизвестный join-ключ → отклонён (ключ ещё не зарегистрирован) */ + TEST("process_request: unknown join_key rejected"); { + struct joiner j; make_joiner(&j, ch_x_pub, ch_ed_pub); + uint8_t buf[512]; size_t len = 0; + serialize_member(j.node_id, j.x_pub, j.ed_pub, j.join_sig, j.join_ts, buf, sizeof(buf), &len); + chat_join_process_request(inst, group_id, j.node_id, JOIN_KEY, buf, len); + if (!topo_node_sqlite_member_in_channel(db, CH_COMP, j.node_id)) OK(); else FAIL(); + } + + /* регистрируем ключ локально — дальше process_request его принимает */ + chat_join_register_key(inst, group_id, inst->node_id, JOIN_KEY); + /* 1. валидный join_sig → мембер добавлен, signed_by=self, подпись валидна */ TEST("process_request: valid join_sig accepted"); { struct joiner j; make_joiner(&j, ch_x_pub, ch_ed_pub); @@ -456,6 +468,7 @@ static void t_validate_pubkey(void) { uint64_t gid = strtoull("61616161616", NULL, 10); uint8_t buf[512]; size_t len = 0; serialize_member(j.node_id, j.x_pub, j.ed_pub, j.join_sig, j.join_ts, buf, sizeof(buf), &len); + chat_join_register_key(inst, gid, inst->node_id, JOIN_KEY); chat_join_process_request(inst, gid, j.node_id, JOIN_KEY, buf, len); TEST("validate_pubkey: member -> 1"); {