|
|
|
@ -102,7 +102,7 @@ static int fb_key(uint64_t channel, const struct chat_feedback_record* r, merkle |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Членство и привязка Ed25519 к X25519 подтверждаются существующими подписями мембера. */ |
|
|
|
/* Полный мембер, доверенный ключ узла, join_sig и подпись дерева приглашений обязательны. */ |
|
|
|
static int fb_author_key(struct UTUN_INSTANCE* inst, const char* channel, uint64_t author, uint8_t key[32]) { |
|
|
|
static int fb_author_key(struct UTUN_INSTANCE* inst, const char* channel, uint64_t author, uint8_t key[32]) { |
|
|
|
char sql[384]; |
|
|
|
char sql[384]; |
|
|
|
snprintf(sql, sizeof(sql), "SELECT x25519_pubkey,ed25519_pubkey,signed_by,signature,join_sig,join_ts" |
|
|
|
snprintf(sql, sizeof(sql), "SELECT x25519_pubkey,ed25519_pubkey,signed_by,signature,join_sig,join_ts" |
|
|
|
@ -111,24 +111,38 @@ static int fb_author_key(struct UTUN_INSTANCE* inst, const char* channel, uint64 |
|
|
|
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return fb_db_error(inst, channel, "author lookup"); |
|
|
|
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); |
|
|
|
sqlite3_bind_int64(st, 1, (sqlite3_int64)author); |
|
|
|
int rc = sqlite3_step(st), ok = 0; |
|
|
|
int rc = sqlite3_step(st), ok = 0; |
|
|
|
if (rc == SQLITE_ROW && sqlite3_column_bytes(st, 0) == 32 && sqlite3_column_bytes(st, 1) == 32 |
|
|
|
int x_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 0) : 0, ed_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 1) : 0; |
|
|
|
&& sqlite3_column_bytes(st, 3) == 64 && sqlite3_column_bytes(st, 4) == 64 && sqlite3_column_int64(st, 5) > 0) { |
|
|
|
int tree_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 3) : 0, join_bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 4) : 0; |
|
|
|
struct ms_member_rec member = { .node_id = author, .x25519 = sqlite3_column_blob(st, 0), |
|
|
|
const char* reason = "member absent"; |
|
|
|
.ed25519 = sqlite3_column_blob(st, 1), .signed_by = (uint64_t)sqlite3_column_int64(st, 2), |
|
|
|
if (rc != SQLITE_ROW) goto done; |
|
|
|
.signature = sqlite3_column_blob(st, 3), .join_sig = sqlite3_column_blob(st, 4), |
|
|
|
reason = "invalid member fields"; |
|
|
|
.join_ts = (uint64_t)sqlite3_column_int64(st, 5) }; |
|
|
|
if (x_bytes != 32 || ed_bytes != 32 || tree_bytes != 64 |
|
|
|
uint8_t x[32], ed[32], body[112]; |
|
|
|
|| join_bytes != 64 || sqlite3_column_int64(st, 5) <= 0) goto done; |
|
|
|
int n = topo_node_sqlite_channel_get(db, channel, NULL, 0, NULL, x, ed, NULL); |
|
|
|
struct ms_member_rec member = { .node_id = author, .x25519 = sqlite3_column_blob(st, 0), |
|
|
|
if (n == 0) n = member_sync_build_join_msg(x, ed, author, member.x25519, member.join_ts, body, sizeof(body)); |
|
|
|
.ed25519 = sqlite3_column_blob(st, 1), .signed_by = (uint64_t)sqlite3_column_int64(st, 2), |
|
|
|
ok = n == 112 && sc_derive_node_id_from_pubkey(member.x25519) == author |
|
|
|
.signature = sqlite3_column_blob(st, 3), .join_sig = sqlite3_column_blob(st, 4), |
|
|
|
&& member_sync_verify_signature(db, channel, &member) == 1 |
|
|
|
.join_ts = (uint64_t)sqlite3_column_int64(st, 5) }; |
|
|
|
&& sc_ed25519_verify(member.ed25519, body, n, member.join_sig) == SC_OK; |
|
|
|
reason = "node_id/X25519 mismatch"; |
|
|
|
if (ok) memcpy(key, member.ed25519, 32); |
|
|
|
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); |
|
|
|
sqlite3_finalize(st); |
|
|
|
if (ok) return 0; |
|
|
|
if (ok) return 0; |
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "feedback: author not verified ch=%s node=%016llx lookup=%d", |
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, |
|
|
|
channel, (unsigned long long)author, rc); return -1; |
|
|
|
"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; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Создаёт одну таблицу подписанных состояний и индекс обхода листьев. */ |
|
|
|
/* Создаёт одну таблицу подписанных состояний и индекс обхода листьев. */ |
|
|
|
|