From 85269d1da3f9f1bcaf22cee4e9f0a07aa7b9f4ce Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 23 Jul 2026 19:55:15 +0300 Subject: [PATCH] Ed25519 self-signature in NODEINFO + fix sync sequence and zero-key bug Add x25519_self_sig[64] to TOPOMSG_NODE/TOPO_NODE: - topo_node.h: add field to both structs (wire format + memory) - topo_node.c: compute Ed25519_sign(ed25519_privkey, x25519_pubkey) for own node - topo_node.c: serialize/deserialize x25519_self_sig - topo_group.c: verify Ed25519 self-sig on NODEINFO receive, reject forgery Fix Ed25519 pubkey zero-key bug: - topo_node_sqlite.c: get_ed25519_pubkey rejects all-zero key (MUST be non-zero) Delay db_sync until Ed25519 key is available: - db_sync.c: db_sync_on_conn_up checks db_get_ed25519_pubkey before starting - db_sync.c: db_sync_peer_check_cb also checks Ed25519 key availability - This prevents sync starting before NODEINFO with verified Ed25519 arrives --- src/db_sync.c | 16 ++++++++++++---- src/routing_layer/topo_group.c | 13 +++++++++++++ src/routing_layer/topo_node.c | 3 +++ src/routing_layer/topo_node.h | 4 +++- src/routing_layer/topo_node_sqlite.c | 2 +- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/db_sync.c b/src/db_sync.c index 7b7b56cd..8551406e 100644 --- a/src/db_sync.c +++ b/src/db_sync.c @@ -1165,7 +1165,7 @@ static void db_sync_on_conn_up(struct ETCP_CONN* conn, void* arg) DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "peer=%016llx init=%d links=%d", (unsigned long long)pid, conn->initialized, conn->links_up); db->last_connected_tb = get_time_tb(); - int synced = 0, skipped_state = 0, skipped_not_ready = 0; + int synced = 0, skipped_state = 0, skipped_not_ready = 0, skipped_no_edkey = 0; for (int i = 0; i < db->instance_count; i++) { struct DB_SYNC_INSTANCE* si = &db->instances[i]; if (!si->enabled) continue; @@ -1173,14 +1173,15 @@ static void db_sync_on_conn_up(struct ETCP_CONN* conn, void* arg) if (!p) continue; if (p->sync_state != 0) { skipped_state++; continue; } if (!conn->initialized || !conn->links_up) { skipped_not_ready++; continue; } + { uint8_t ek[32]; if (db_get_ed25519_pubkey(db, pid, ek) != 0) { skipped_no_edkey++; continue; } } p->sync_state = 1; p->sync_retry_count = 0; db_sync_initiate_sync(si, pid); synced++; } DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, - "conn_up peer=%016llx init=%d links=%d instances=%d synced=%d skipped_state=%d skipped_not_ready=%d tbls=[%s]", + "conn_up peer=%016llx init=%d links=%d instances=%d synced=%d skipped_state=%d skipped_not_ready=%d skipped_no_edkey=%d tbls=[%s]", (unsigned long long)pid, conn->initialized, conn->links_up, - db->instance_count, synced, skipped_state, skipped_not_ready, + db->instance_count, synced, skipped_state, skipped_not_ready, skipped_no_edkey, (synced > 0 && db->instance_count > 0) ? db->instances[0].table_name : "none"); } @@ -1308,7 +1309,14 @@ static void db_sync_peer_check_cb(void* arg) best = &si->peers[j]; } } - if (best) { best->sync_state = 1; db_sync_initiate_sync(si, best->node_id); total_synced++; } + if (best) { + uint8_t ek[32]; + if (db_get_ed25519_pubkey(db, best->node_id, ek) == 0) { + best->sync_state = 1; db_sync_initiate_sync(si, best->node_id); total_synced++; + } else { + best = NULL; total_skipped++; + } + } else { total_skipped++; } } diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index 1f5aab36..daf708c8 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -602,6 +602,19 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from return -1; } + /* verify Ed25519 self-signature: Ed25519_pubkey must sign X25519_pubkey */ + { + uint64_t ekchk; memcpy(&ekchk, new_ni->ed25519_public_key, 8); + if (ekchk != 0 && sc_ed25519_verify(new_ni->ed25519_public_key, new_ni->public_key, SC_PUBKEY_SIZE, new_ni->x25519_self_sig) != SC_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO x25519_self_sig VERIFY FAIL node=%016llx ed_pubkey=%016llx... from=%s — rejecting as forgery", + (unsigned long long)node_id, ekchk, from->log_name); + topo_node_free_raw(group->instance->topo_groups, new_ni); + u_free(new_subnets); u_free(new_tranzit); u_free(new_hop_list); + if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); } + return -1; + } + } + if (nodeinfo1) { nodeinfo1->node = topo_node_registry_acquire(group->instance->topo_groups, new_ni); nodeinfo1->hash_node_id = nodeinfo1->node ? nodeinfo1->node->node_id : 0; diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index aa635ba6..4e7ba309 100644 --- a/src/routing_layer/topo_node.c +++ b/src/routing_layer/topo_node.c @@ -151,6 +151,7 @@ int topo_node_serialize(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq, uint8_t msg.ver = ni->ver; memcpy(msg.public_key, ni->public_key, SC_PUBKEY_SIZE); memcpy(msg.ed25519_public_key, ni->ed25519_public_key, SC_PUBKEY_SIZE); + memcpy(msg.x25519_self_sig, ni->x25519_self_sig, 64); msg.node_name_len = ni->node_name ? (uint8_t)strlen(ni->node_name) : 0; msg.local_v4_sockets = topo_list_count((struct _topo_head*)ni->v4_sock_meta); msg.local_v4_addrs = topo_list_count((struct _topo_head*)ni->v4_addrs); @@ -230,6 +231,7 @@ int topo_node_deserialize(struct TOPO_GROUP* group, const uint8_t* data, size_t ni->ver = msg->ver; memcpy(ni->public_key, msg->public_key, SC_PUBKEY_SIZE); memcpy(ni->ed25519_public_key, msg->ed25519_public_key, SC_PUBKEY_SIZE); + memcpy(ni->x25519_self_sig, msg->x25519_self_sig, 64); const uint8_t* dp = data + TOPOMSG_NODE_HDR_SIZE; @@ -664,6 +666,7 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR ni->ver = (old_ver % 255) + 1; memcpy(ni->public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE); memcpy(ni->ed25519_public_key, group->ed25519_public_key, SC_PUBKEY_SIZE); + sc_ed25519_sign(instance->my_ed25519_privkey, instance->my_keys.public_key, SC_PUBKEY_SIZE, ni->x25519_self_sig); if (name_len) { ni->node_name = u_malloc(name_len + 1); if (ni->node_name) { memcpy(ni->node_name, instance->name, name_len); ni->node_name[name_len] = 0; } } lq->node = topo_node_registry_acquire(instance->topo_groups, ni); diff --git a/src/routing_layer/topo_node.h b/src/routing_layer/topo_node.h index 35e2a403..c6efd9b7 100644 --- a/src/routing_layer/topo_node.h +++ b/src/routing_layer/topo_node.h @@ -165,6 +165,7 @@ struct TOPOMSG_NODE { uint8_t ver; uint8_t public_key[SC_PUBKEY_SIZE]; uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; + uint8_t x25519_self_sig[64]; // Ed25519_sign(ed25519_privkey, x25519_pubkey, 32) uint8_t node_name_len; uint8_t local_v4_sockets; uint8_t local_v4_addrs; @@ -206,7 +207,8 @@ struct TOPO_NODE { uint8_t ver; uint8_t public_key[SC_PUBKEY_SIZE]; uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; - char* node_name; // отдельный u_strdup/u_free + uint8_t x25519_self_sig[64]; // Ed25519_sign(ed25519_privkey, x25519_pubkey, 32) + char* node_name; // отдельный u_strdup/u_free struct TOPO_SOCKMETA4* v4_sock_meta; // linked list head struct TOPO_ADDR4* v4_addrs; // linked list head struct TOPO_SOCKMETA6* v6_sock_meta; diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index 7a3b12eb..acdce966 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -555,7 +555,7 @@ int topo_node_sqlite_get_ed25519_pubkey(sqlite3* db, uint64_t node_id, uint8_t p if (sqlite3_step(stmt) == SQLITE_ROW) { const void* b = sqlite3_column_blob(stmt, 0); int bytes = sqlite3_column_bytes(stmt, 0); - if (b && bytes >= 32) { memcpy(pubkey_out, b, 32); rc = 0; } + if (b && bytes >= 32) { uint64_t chk; memcpy(&chk, b, 8); if (chk != 0) { memcpy(pubkey_out, b, 32); rc = 0; } } } sqlite3_finalize(stmt); return rc;