From c59437502319d3b9084a64ce541a0a3042332de9 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sat, 18 Jul 2026 00:41:38 +0300 Subject: [PATCH] fix: derive node_id from pubkey instead of privkey MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sc_derive_node_id() hashed the private key, while invite links (joindialog.cpp) hashed the public key — giving two different node_ids for the same keypair. Removed sc_derive_node_id(), kept only sc_derive_node_id_from_pubkey(). All callers updated to use pubkey-based derivation. --- src/config_updater.c | 4 ++-- src/secure_channel.c | 6 +++--- src/secure_channel.h | 2 +- src/utun_instance.c | 8 +++++--- tools/chatgui/src/joindialog.cpp | 19 +++---------------- 5 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/config_updater.c b/src/config_updater.c index f6f5bee1..f9a8bf00 100644 --- a/src/config_updater.c +++ b/src/config_updater.c @@ -293,8 +293,8 @@ int config_ensure_keys_and_node_id(const char *filename) { global->my_public_key_hex[0] ? global->my_public_key_hex : "NULL", new_pub_key); } - // ── Step 3: derive node_id from privkey ── - new_node_id = sc_derive_node_id(priv_bin); + // ── Step 3: derive node_id from pubkey ── + new_node_id = sc_derive_node_id_from_pubkey(pub_bin); if (!is_valid_node_id(global->my_node_id) || global->my_node_id != new_node_id) { need_node_id = 1; DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Node_id mismatch (or missing), fixing: config=%016llx derived=%016llx file=%s", diff --git a/src/secure_channel.c b/src/secure_channel.c index ce78eb35..81c325e0 100644 --- a/src/secure_channel.c +++ b/src/secure_channel.c @@ -422,10 +422,10 @@ sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed2 return SC_OK; } -uint64_t sc_derive_node_id(const uint8_t *private_key) { - if (!private_key) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_node_id: NULL private_key"); return 0; } +uint64_t sc_derive_node_id_from_pubkey(const uint8_t *public_key) { + if (!public_key) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_node_id_from_pubkey: NULL public_key"); return 0; } uint8_t sha_hash[SC_HASH_SIZE]; SC_SHA256_CTX ctx; - sc_sha256_init(&ctx); sc_sha256_update(&ctx, private_key, SC_PRIVKEY_SIZE); sc_sha256_final(&ctx, sha_hash); + sc_sha256_init(&ctx); sc_sha256_update(&ctx, public_key, SC_PUBKEY_SIZE); sc_sha256_final(&ctx, sha_hash); uint64_t node_id; memcpy(&node_id, sha_hash, 8); node_id &= 0x7FFFFFFFFFFFFFFFULL; diff --git a/src/secure_channel.h b/src/secure_channel.h index 686b4505..d086d9cc 100644 --- a/src/secure_channel.h +++ b/src/secure_channel.h @@ -114,7 +114,7 @@ sc_status_t sc_ed25519_verify(const uint8_t pubkey[32], const uint8_t* msg, size sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed25519_pubkey_out); -uint64_t sc_derive_node_id(const uint8_t *private_key); +uint64_t sc_derive_node_id_from_pubkey(const uint8_t *public_key); #ifdef __cplusplus diff --git a/src/utun_instance.c b/src/utun_instance.c index 78d93b63..2639702a 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -90,10 +90,12 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u return -1; } - // Derive node_id from privkey if not set in config + // Derive node_id from pubkey if not set in config if (!instance->node_id) { - instance->node_id = sc_derive_node_id(instance->my_keys.private_key); - DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "node_id derived from privkey: %016llx", (unsigned long long)instance->node_id); + uint8_t pubkey[SC_PUBKEY_SIZE]; + sc_compute_public_key_from_private(instance->my_keys.private_key, pubkey); + instance->node_id = sc_derive_node_id_from_pubkey(pubkey); + DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "node_id derived from pubkey: %016llx", (unsigned long long)instance->node_id); } if (sc_derive_ed25519_pubkey(instance->my_keys.private_key, instance->my_ed25519_pubkey) != SC_OK) { diff --git a/tools/chatgui/src/joindialog.cpp b/tools/chatgui/src/joindialog.cpp index 8012f267..8e740b90 100644 --- a/tools/chatgui/src/joindialog.cpp +++ b/tools/chatgui/src/joindialog.cpp @@ -14,6 +14,7 @@ #include extern "C" { +#include "../../../src/secure_channel.h" #include "../../../lib/sha256.h" } @@ -92,14 +93,7 @@ JoinDialog::JoinDialog(UtunNode* node, DbManager* db, QWidget* parent) setStatus(""); return; } - uint8_t hash[32]; - SC_SHA256_CTX ctx; - sc_sha256_init(&ctx); - sc_sha256_update(&ctx, (const uint8_t*)d.pubkey.constData(), 32); - sc_sha256_final(&ctx, hash); - uint64_t nodeId; - memcpy(&nodeId, hash, sizeof(nodeId)); - nodeId &= 0x7FFFFFFFFFFFFFFFULL; + uint64_t nodeId = sc_derive_node_id_from_pubkey((const uint8_t*)d.pubkey.constData()); QStringList addrList; for (const auto& a : d.addrs) { @@ -162,14 +156,7 @@ void JoinDialog::onConnectClicked() { return; } - uint8_t hash[32]; - SC_SHA256_CTX ctx; - sc_sha256_init(&ctx); - sc_sha256_update(&ctx, (const uint8_t*)d.pubkey.constData(), 32); - sc_sha256_final(&ctx, hash); - uint64_t nodeId; - memcpy(&nodeId, hash, sizeof(nodeId)); - nodeId &= 0x7FFFFFFFFFFFFFFFULL; + uint64_t nodeId = sc_derive_node_id_from_pubkey((const uint8_t*)d.pubkey.constData()); GUI_INFO("join: decoded invite ch=%llu pubkey=%016llx... nodeId=0x%016llx addrs=%d", d.channelId, *(const uint64_t*)d.pubkey.constData(), nodeId, d.addrs.size());