From 7eb4450befaea660a509f57a23d183f4996ad59f Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 3 Nov 2026 15:58:30 +0100 Subject: [PATCH] Verify group join binding independently of member profile --- src/chat/member_sync.c | 9 +++++++++ tests/test_member_adapter.c | 4 ++-- tests/test_member_purge.c | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/chat/member_sync.c b/src/chat/member_sync.c index 26425c035..40e93fee3 100644 --- a/src/chat/member_sync.c +++ b/src/chat/member_sync.c @@ -632,6 +632,10 @@ static int member_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id, int identity_ok = 1; int has_signature = m->signature && !_sig_is_zero64(m->signature); if (!is_local) { + if (m->join_sig && !_sig_is_zero64(m->join_sig)) { + int verified = _verify_join_sig(db, ch_id, m); + if (verified != 1) return verified == MS_VERIFY_ERROR ? MT_ERR_IO : MT_ERR_DATA; + } /* жёсткая привязка идентичности: node_id = derive(x25519), ed25519 — доверенный из nodes */ uint64_t derived = sc_derive_node_id_from_pubkey(m->x25519); if (!derived) { @@ -928,6 +932,11 @@ int member_sync_verify_local_record(struct sqlite3* db, const char* ch_id, const return 0; } + /* JOIN проверяется и при наличии профиля: профиль сам по себе не связывает запись с группой. */ + if (m->join_sig && !_sig_is_zero64(m->join_sig)) { + int verified = _verify_join_sig(db, ch_id, m); + if (verified != 1) return verified == MS_VERIFY_ERROR ? -1 : 0; + } /* update_sig присутствует — верифицируем */ if (m->update_sig && !_sig_is_zero64(m->update_sig)) { uint8_t vmsg[8192]; diff --git a/tests/test_member_adapter.c b/tests/test_member_adapter.c index 26c557d5f..195b63bc8 100644 --- a/tests/test_member_adapter.c +++ b/tests/test_member_adapter.c @@ -156,7 +156,7 @@ static void test_invitation_certificate(struct UASYNC* ua) { } uint64_t nid = sc_derive_node_id_from_pubkey(x); uint8_t join[64], tree[64], update[64], body[256]; int len = member_sync_build_join_msg(x, owner_pub, nid, x, 1, body, sizeof(body)); assert(len == 112); - assert(sc_ed25519_sign(priv, body, len, join) == SC_OK && member_sync_sign_pubkey(owner_priv, x, tree) == 0); + assert(sc_ed25519_sign(priv, body, len, join) == SC_OK && member_sync_sign_join(owner_priv, join, tree) == 0); struct ms_member_rec rec = { .node_id = nid, .x25519 = x, .ed25519 = pub, .join_sig = join, .join_ts = 1, .update_sig = update, .update_ts = 2, .userinfo = "{\"name\":\"profile\"}", .signature = tree }; len = member_sync_build_update_msg(join, rec.update_ts, rec.userinfo, body, sizeof(body)); assert(len > 0); @@ -213,7 +213,7 @@ static void test_owner_sizes(struct UASYNC* ua) { uint8_t join[64], tree[64], owner[64], update[64], msg[MS_ADM_TAGS_MAX + 8]; int n = member_sync_build_join_msg(x, owner_pub, nid, x, 1, msg, sizeof(msg)); assert(n > 0); assert(sc_ed25519_sign(priv, msg, n, join) == SC_OK); - assert(member_sync_sign_pubkey(owner_priv, x, tree) == 0); + assert(member_sync_sign_join(owner_priv, join, tree) == 0); char tags[MS_ADM_TAGS_MAX + 2]; char userinfo[MS_USERINFO_MAX + 1]; struct ms_member_rec rec = { .node_id = nid, .x25519 = x, .ed25519 = pub, diff --git a/tests/test_member_purge.c b/tests/test_member_purge.c index afdd95dad..300f611a4 100644 --- a/tests/test_member_purge.c +++ b/tests/test_member_purge.c @@ -105,7 +105,7 @@ static void purge_setup(struct purge_fixture* f, struct UASYNC* ua, int join_onl .signature = f->tree, .adm_tags = "{\"ver\":\"1\",\"storage\":\"yes\"}", .adm_tags_sig = f->owner_sig }; int len = member_sync_build_join_msg(f->x, f->owner, f->member.node_id, f->x, 1, body, sizeof(body)); assert(len == 112); assert(sc_ed25519_sign(priv, body, len, f->join) == SC_OK); - assert(member_sync_sign_pubkey(owner_priv, f->x, f->tree) == 0); + assert(member_sync_sign_join(owner_priv, f->join, f->tree) == 0); if (!join_only) { f->member.update_sig = f->update; f->member.update_ts = 2; len = member_sync_build_update_msg(f->join, 2, f->member.userinfo, body, sizeof(body)); assert(len > 0);