Share complete membership verification for private grants
This commit is contained in:
1 parent
6a90d79d04
commit
e45401449e
3 files changed
+50
-45
No files matched your search
@@ -102,48 +102,7 @@ static int fb_key(uint64_t channel, const struct chat_feedback_record* r, merkle
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Полный мембер, доверенный ключ узла, join_sig и подпись дерева приглашений обязательны. */
|
|
||||||
static int fb_author_key(struct UTUN_INSTANCE* inst, const char* channel, uint64_t author, uint8_t key[32]) {
|
|
||||||
char sql[384];
|
|
||||||
snprintf(sql, sizeof(sql), "SELECT x25519_pubkey,ed25519_pubkey,signed_by,signature,join_sig,join_ts"
|
|
||||||
" FROM peers_%s WHERE node_id=? AND source=0", channel);
|
|
||||||
sqlite3_stmt* st = NULL; sqlite3* db = inst->topo_sqlite_db;
|
|
||||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return fb_db_error(inst, channel, "author lookup");
|
|
||||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)author);
|
|
||||||
int rc = sqlite3_step(st), ok = 0;
|
|
||||||
int x_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 0) : 0, ed_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 1) : 0;
|
|
||||||
int tree_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 3) : 0, join_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 4) : 0;
|
|
||||||
const char* reason = "member absent";
|
|
||||||
if (rc != SQLITE_ROW) goto done;
|
|
||||||
reason = "invalid member fields";
|
|
||||||
if (x_bytes != 32 || ed_bytes != 32 || tree_bytes != 64
|
|
||||||
|| join_bytes != 64 || sqlite3_column_int64(st, 5) <= 0) goto done;
|
|
||||||
struct ms_member_rec member = { .node_id = author, .x25519 = sqlite3_column_blob(st, 0),
|
|
||||||
.ed25519 = sqlite3_column_blob(st, 1), .signed_by = (uint64_t)sqlite3_column_int64(st, 2),
|
|
||||||
.signature = sqlite3_column_blob(st, 3), .join_sig = sqlite3_column_blob(st, 4),
|
|
||||||
.join_ts = (uint64_t)sqlite3_column_int64(st, 5) };
|
|
||||||
reason = "node_id/X25519 mismatch";
|
|
||||||
if (sc_derive_node_id_from_pubkey(member.x25519) != author) goto done;
|
|
||||||
uint8_t trusted[32], x[32], ed[32], body[112];
|
|
||||||
reason = "trusted Ed25519 key unavailable";
|
|
||||||
if (topo_node_sqlite_get_ed25519_pubkey(db, author, trusted) != 0) goto done;
|
|
||||||
reason = "trusted Ed25519 key mismatch";
|
|
||||||
if (memcmp(trusted, member.ed25519, 32)) goto done;
|
|
||||||
reason = "channel keys unavailable";
|
|
||||||
if (topo_node_sqlite_channel_get(db, channel, NULL, 0, NULL, x, ed, NULL) != 0) goto done;
|
|
||||||
reason = "invalid join signature";
|
|
||||||
int bytes = member_sync_build_join_msg(x, ed, author, member.x25519, member.join_ts, body, sizeof(body));
|
|
||||||
if (bytes != 112 || sc_ed25519_verify(member.ed25519, body, bytes, member.join_sig) != SC_OK) goto done;
|
|
||||||
reason = "invalid or unresolved invitation signature";
|
|
||||||
if (member_sync_verify_signature(db, channel, &member) != 1) goto done;
|
|
||||||
memcpy(key, member.ed25519, 32); ok = 1;
|
|
||||||
done:
|
|
||||||
sqlite3_finalize(st);
|
|
||||||
if (ok) return 0;
|
|
||||||
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC,
|
|
||||||
"feedback: author not verified ch=%s node=%016llx reason=%s rc=%d x_bytes=%d ed_bytes=%d tree_bytes=%d join_bytes=%d",
|
|
||||||
channel, (unsigned long long)author, reason, rc, x_bytes, ed_bytes, tree_bytes, join_bytes); return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Создаёт одну таблицу подписанных состояний и индекс обхода листьев. */
|
/* Создаёт одну таблицу подписанных состояний и индекс обхода листьев. */
|
||||||
int chat_feedback_ensure_channel(struct UTUN_INSTANCE* inst, const char* channel) {
|
int chat_feedback_ensure_channel(struct UTUN_INSTANCE* inst, const char* channel) {
|
||||||
@@ -197,7 +156,7 @@ static int fb_apply_row(struct UTUN_INSTANCE* inst, const char* channel, const s
|
|||||||
char table[40], sql[512]; uint64_t id; uint8_t body[FB_WIRE_MAX + 32], key[32];
|
char table[40], sql[512]; uint64_t id; uint8_t body[FB_WIRE_MAX + 32], key[32];
|
||||||
if (fb_table(inst, channel, table, &id) < 0) return -1;
|
if (fb_table(inst, channel, table, &id) < 0) return -1;
|
||||||
int bytes = fb_signed_bytes(id, r, body);
|
int bytes = fb_signed_bytes(id, r, body);
|
||||||
if (bytes < 0 || fb_author_key(inst, channel, r->node_id, key) < 0) return -1;
|
if (bytes < 0 || member_sync_verified_ed25519(inst, channel, r->node_id, key) < 0) return -1;
|
||||||
if (sc_ed25519_verify(key, body, bytes, r->signature) != SC_OK) {
|
if (sc_ed25519_verify(key, body, bytes, r->signature) != SC_OK) {
|
||||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: invalid signature ch=%s node=%016llx kind=%u ver=%llu",
|
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: invalid signature ch=%s node=%016llx kind=%u ver=%llu",
|
||||||
channel, (unsigned long long)r->node_id, r->kind, (unsigned long long)r->ver); return -1;
|
channel, (unsigned long long)r->node_id, r->kind, (unsigned long long)r->ver); return -1;
|
||||||
@@ -395,7 +354,7 @@ static int fb_apply_page(void* ctx, const char* channel, uint64_t peer, const ui
|
|||||||
|
|
||||||
static int fb_peer(void* ctx, const char* channel, uint64_t peer) {
|
static int fb_peer(void* ctx, const char* channel, uint64_t peer) {
|
||||||
struct UTUN_INSTANCE* inst = ctx; uint8_t key[32];
|
struct UTUN_INSTANCE* inst = ctx; uint8_t key[32];
|
||||||
return fb_author_key(inst, channel, peer, key) == 0;
|
return member_sync_verified_ed25519(inst, channel, peer, key) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct merkle_sync_data_ops fb_ops = {
|
static const struct merkle_sync_data_ops fb_ops = {
|
||||||
@@ -422,7 +381,7 @@ int chat_feedback_init(struct UTUN_INSTANCE* inst) {
|
|||||||
while ((rc = sqlite3_step(st)) == SQLITE_ROW) {
|
while ((rc = sqlite3_step(st)) == SQLITE_ROW) {
|
||||||
struct chat_feedback_record r; uint64_t id; uint8_t body[FB_WIRE_MAX + 32], key[32]; merkle_key_t record_key;
|
struct chat_feedback_record r; uint64_t id; uint8_t body[FB_WIRE_MAX + 32], key[32]; merkle_key_t record_key;
|
||||||
if (fb_read_row(st, &r) < 0 || fb_table(inst, channel, table, &id) < 0
|
if (fb_read_row(st, &r) < 0 || fb_table(inst, channel, table, &id) < 0
|
||||||
|| fb_author_key(inst, channel, r.node_id, key) < 0) { result = -1; break; }
|
|| member_sync_verified_ed25519(inst, channel, r.node_id, key) < 0) { result = -1; break; }
|
||||||
int bytes = fb_signed_bytes(id, &r, body);
|
int bytes = fb_signed_bytes(id, &r, body);
|
||||||
if (bytes < 0 || sc_ed25519_verify(key, body, bytes, r.signature) != SC_OK || fb_key(id, &r, &record_key) < 0
|
if (bytes < 0 || sc_ed25519_verify(key, body, bytes, r.signature) != SC_OK || fb_key(id, &r, &record_key) < 0
|
||||||
|| merkle_sync_recompute_path(inst, MT_DATA_FEEDBACK, channel, fb_read64(record_key.bytes)) < 0) {
|
|| merkle_sync_recompute_path(inst, MT_DATA_FEEDBACK, channel, fb_read64(record_key.bytes)) < 0) {
|
||||||
|
|||||||
@@ -1211,3 +1211,46 @@ int member_sync_count(struct UTUN_INSTANCE* inst, const char* ch_id) {
|
|||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Полный мембер, доверенный ключ узла, join_sig и подпись дерева приглашений обязательны. */
|
||||||
|
int member_sync_verified_ed25519(struct UTUN_INSTANCE* inst, const char* channel, uint64_t author, uint8_t key[32]) {
|
||||||
|
char sql[384];
|
||||||
|
snprintf(sql, sizeof(sql), "SELECT x25519_pubkey,ed25519_pubkey,signed_by,signature,join_sig,join_ts"
|
||||||
|
" FROM peers_%s WHERE node_id=? AND source=0", channel);
|
||||||
|
sqlite3_stmt* st = NULL; sqlite3* db = inst->topo_sqlite_db;
|
||||||
|
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) { DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "verified member lookup: %s", sqlite3_errmsg(db)); return -1; }
|
||||||
|
sqlite3_bind_int64(st, 1, (sqlite3_int64)author);
|
||||||
|
int rc = sqlite3_step(st), ok = 0;
|
||||||
|
int x_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 0) : 0, ed_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 1) : 0;
|
||||||
|
int tree_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 3) : 0, join_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 4) : 0;
|
||||||
|
const char* reason = "member absent";
|
||||||
|
if (rc != SQLITE_ROW) goto done;
|
||||||
|
reason = "invalid member fields";
|
||||||
|
if (x_bytes != 32 || ed_bytes != 32 || tree_bytes != 64
|
||||||
|
|| join_bytes != 64 || sqlite3_column_int64(st, 5) <= 0) goto done;
|
||||||
|
struct ms_member_rec member = { .node_id = author, .x25519 = sqlite3_column_blob(st, 0),
|
||||||
|
.ed25519 = sqlite3_column_blob(st, 1), .signed_by = (uint64_t)sqlite3_column_int64(st, 2),
|
||||||
|
.signature = sqlite3_column_blob(st, 3), .join_sig = sqlite3_column_blob(st, 4),
|
||||||
|
.join_ts = (uint64_t)sqlite3_column_int64(st, 5) };
|
||||||
|
reason = "node_id/X25519 mismatch";
|
||||||
|
if (sc_derive_node_id_from_pubkey(member.x25519) != author) goto done;
|
||||||
|
uint8_t trusted[32], x[32], ed[32], body[112];
|
||||||
|
reason = "trusted Ed25519 key unavailable";
|
||||||
|
if (topo_node_sqlite_get_ed25519_pubkey(db, author, trusted) != 0) goto done;
|
||||||
|
reason = "trusted Ed25519 key mismatch";
|
||||||
|
if (memcmp(trusted, member.ed25519, 32)) goto done;
|
||||||
|
reason = "channel keys unavailable";
|
||||||
|
if (topo_node_sqlite_channel_get(db, channel, NULL, 0, NULL, x, ed, NULL) != 0) goto done;
|
||||||
|
reason = "invalid join signature";
|
||||||
|
int bytes = member_sync_build_join_msg(x, ed, author, member.x25519, member.join_ts, body, sizeof(body));
|
||||||
|
if (bytes != 112 || sc_ed25519_verify(member.ed25519, body, bytes, member.join_sig) != SC_OK) goto done;
|
||||||
|
reason = "invalid or unresolved invitation signature";
|
||||||
|
if (member_sync_verify_signature(db, channel, &member) != 1) goto done;
|
||||||
|
memcpy(key, member.ed25519, 32); ok = 1;
|
||||||
|
done:
|
||||||
|
sqlite3_finalize(st);
|
||||||
|
if (ok) return 0;
|
||||||
|
DEBUG_WARN(DEBUG_CATEGORY_MEMBER_SYNC,
|
||||||
|
"member verification failed ch=%s node=%016llx reason=%s rc=%d x_bytes=%d ed_bytes=%d tree_bytes=%d join_bytes=%d",
|
||||||
|
channel, (unsigned long long)author, reason, rc, x_bytes, ed_bytes, tree_bytes, join_bytes); return -1;
|
||||||
|
}
|
||||||
@@ -25,6 +25,9 @@ struct TOPO_GROUP;
|
|||||||
struct sqlite3;
|
struct sqlite3;
|
||||||
|
|
||||||
/* Результат применения рекорда мембера (compare+update по версиям двух блоков). */
|
/* Результат применения рекорда мембера (compare+update по версиям двух блоков). */
|
||||||
|
/* Полная проверка членства, join и дерева приглашений; возвращает доверенный ключ, 0/-1. */
|
||||||
|
int member_sync_verified_ed25519(struct UTUN_INSTANCE* inst, const char* channel, uint64_t author, uint8_t key[32]);
|
||||||
|
|
||||||
#define MS_APPLY_CHANGED 0x01 /* запись и дерево обновлены */
|
#define MS_APPLY_CHANGED 0x01 /* запись и дерево обновлены */
|
||||||
#define MS_APPLY_STALE 0x02 /* локальная версия новее; обратный проход доставит её пиру */
|
#define MS_APPLY_STALE 0x02 /* локальная версия новее; обратный проход доставит её пиру */
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user