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
This commit is contained in:
1 parent
3890d2038d
commit
85269d1da3
5 files changed
+32
-6
No files matched your search
+12
-4
@@ -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++; }
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user