From 7c0814345df44e90cdba20ffd73ae70030118169 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 3 Nov 2026 15:57:52 +0100 Subject: [PATCH] Bind inviter certificates to the signed group join epoch --- src/chat/member_sync.c | 21 ++++++++++++--------- src/chat/member_sync.h | 12 ++++++------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/chat/member_sync.c b/src/chat/member_sync.c index d116d259e..26425c035 100644 --- a/src/chat/member_sync.c +++ b/src/chat/member_sync.c @@ -506,17 +506,17 @@ static int _verify_join_sig(sqlite3* db, const char* ch_id, const struct ms_memb /* ── Дерево приглашений: подпись pubkey узла ── */ -int member_sync_build_sign_msg(const uint8_t* x25519, uint8_t* out, int out_sz) { - if (!x25519 || !out || out_sz < MEMBER_SIGN_MSG_SIZE) return -1; - memcpy(out, x25519, SC_PUBKEY_SIZE); - memcpy(out + SC_PUBKEY_SIZE, MEMBER_SIGN_SUFFIX, MEMBER_SIGN_SUFFIX_LEN); +int member_sync_build_sign_msg(const uint8_t* join_sig, uint8_t* out, int out_sz) { + if (!join_sig || !out || out_sz < MEMBER_SIGN_MSG_SIZE) return -1; + memcpy(out, join_sig, 64); + memcpy(out + 64, MEMBER_SIGN_SUFFIX, MEMBER_SIGN_SUFFIX_LEN); return MEMBER_SIGN_MSG_SIZE; } -int member_sync_sign_pubkey(const uint8_t* signer_ed_priv, const uint8_t* x25519, uint8_t sig[64]) { - if (!signer_ed_priv || !x25519 || !sig) return -1; +int member_sync_sign_join(const uint8_t* signer_ed_priv, const uint8_t* join_sig, uint8_t sig[64]) { + if (!signer_ed_priv || !join_sig || !sig) return -1; uint8_t msg[MEMBER_SIGN_MSG_SIZE]; - if (member_sync_build_sign_msg(x25519, msg, (int)sizeof(msg)) < 0) return -1; + if (member_sync_build_sign_msg(join_sig, msg, (int)sizeof(msg)) < 0) return -1; if (sc_ed25519_sign(signer_ed_priv, msg, sizeof(msg), sig) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: sign_pubkey — sc_ed25519_sign FAILED", MS_ID); return -1; @@ -529,8 +529,11 @@ int member_sync_verify_signature(sqlite3* db, const char* ch_id, const struct ms DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: invalid invitation verification arguments", MS_ID); return MS_VERIFY_ERROR; } if (!m->signature || _sig_is_zero64(m->signature)) return 0; - uint8_t msg[MEMBER_SIGN_MSG_SIZE]; - if (member_sync_build_sign_msg(m->x25519, msg, (int)sizeof(msg)) < 0) { + uint8_t msg[MEMBER_SIGN_MSG_SIZE], stored_join[64]; uint64_t stored_ts; + const uint8_t* join = m->join_sig; + if (!join && !topo_node_sqlite_member_get_join(db, ch_id, m->node_id, stored_join, &stored_ts)) join = stored_join; + if (!join || _sig_is_zero64(join)) return 0; + if (member_sync_build_sign_msg(join, msg, (int)sizeof(msg)) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: invitation message build failed", MS_ID); return MS_VERIFY_ERROR; } diff --git a/src/chat/member_sync.h b/src/chat/member_sync.h index f36d2853c..69d17addf 100644 --- a/src/chat/member_sync.h +++ b/src/chat/member_sync.h @@ -78,20 +78,20 @@ struct ms_member_rec { const uint8_t* adm_tags_sig; /* 64 байта, может быть NULL */ int storage; /* вход не используется: сохраняемое значение выводится из adm_tags.storage="yes" */ uint64_t signed_by; /* дерево приглашений: 0 = корень (подписан ключом группы), иначе node_id инвайтера */ - const uint8_t* signature; /* 64 байта, Ed25519 подпись x25519||"group_join" ключом родителя (или канальным ed25519) */ + const uint8_t* signature; /* Ed25519 подпись join_sig||"group_join" ключом инвайтера/группы: привязана к группе и эпохе. */ }; /* ── Дерево приглашений (подпись pubkey узла) ── */ #define MEMBER_SIGN_SUFFIX "group_join" #define MEMBER_SIGN_SUFFIX_LEN 10 -#define MEMBER_SIGN_MSG_SIZE (SC_PUBKEY_SIZE + MEMBER_SIGN_SUFFIX_LEN) /* 42 */ +#define MEMBER_SIGN_MSG_SIZE (64 + MEMBER_SIGN_SUFFIX_LEN) /* 74 */ -/* Собрать сообщение подписи: x25519(32) || "group_join". Возвращает 42 или -1. */ -int member_sync_build_sign_msg(const uint8_t* x25519, uint8_t* out, int out_sz); +/* Собрать сообщение подтверждения: join_sig(64) || "group_join". Возвращает 74 или -1. */ +int member_sync_build_sign_msg(const uint8_t* join_sig, uint8_t* out, int out_sz); -/* Подписать pubkey узла: sign(signer_ed_priv, x25519||"group_join"). Возвращает 0/-1. */ -int member_sync_sign_pubkey(const uint8_t* signer_ed_priv, const uint8_t* x25519, uint8_t sig[64]); +/* Подтвердить конкретный JOIN (группа/узел/эпоха уже связаны join_sig). Возвращает 0/-1. */ +int member_sync_sign_join(const uint8_t* signer_ed_priv, const uint8_t* join_sig, uint8_t sig[64]); /* Верифицировать подпись дерева мембера: * signed_by==0 → ключ канала (ch_ed25519), иначе ed25519 pubkey узла signed_by.