Browse Source

Ключи: node_id из SHA-256(privkey) через весь конфиг; приоритет privkey — pubkey/node_id всегда выводятся из него; CHANNEL_INFO_RESP: верификация inviter_join_sig через ETCP-ключи

topo_upd
Evgeny 3 months ago
parent
commit
ef24af7d24
  1. 107
      src/config_updater.c
  2. 15
      src/utun_instance.c
  3. 24
      tools/chatgui/transport/chat_sync.c

107
src/config_updater.c

@ -5,6 +5,7 @@
#include "secure_channel.h" #include "secure_channel.h"
#include "debug_config.h" #include "debug_config.h"
#include "../lib/platform_compat.h" #include "../lib/platform_compat.h"
#include "../lib/sha256.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -278,91 +279,80 @@ int config_ensure_keys_and_node_id(const char *filename) {
global->my_public_key_hex[0] ? strlen(global->my_public_key_hex) : 0, global->my_public_key_hex[0] ? strlen(global->my_public_key_hex) : 0,
(unsigned long long)global->my_node_id); (unsigned long long)global->my_node_id);
// Check if we need to generate anything // Check if we need to generate anything
int need_priv_key = !is_valid_priv_key(global->my_private_key_hex); int need_priv_key = !is_valid_priv_key(global->my_private_key_hex);
int need_pub_key = 0;
int need_node_id = 0;
int need_pub_key = !is_valid_pub_key(global->my_public_key_hex);
int need_node_id = !is_valid_node_id(global->my_node_id);
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Validation results - need_priv_key=%d, need_pub_key=%d, need_node_id=%d",
need_priv_key, need_pub_key, need_node_id);
if (!need_priv_key && !need_pub_key && !need_node_id) {
free_config(config);
return 0;
}
// Generate keys if needed // Generate keys if needed
char new_priv_key[PRIV_HEXKEY_LEN] = {0}; char new_priv_key[PRIV_HEXKEY_LEN] = {0};
char new_pub_key[PUB_HEXKEY_LEN] = {0}; char new_pub_key[PUB_HEXKEY_LEN] = {0};
uint64_t new_node_id = 0; uint64_t new_node_id = 0;
uint8_t priv_bin[SC_PRIVKEY_SIZE];
// ── Step 1: ensure valid privkey ──
if (need_priv_key) { if (need_priv_key) {
// Generate new keypair if private key is invalid
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Generating NEW keypair — private key was invalid/missing in %s", filename); DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Generating NEW keypair — private key was invalid/missing in %s", filename);
struct SC_MYKEYS mykeys; struct SC_MYKEYS mykeys;
if (sc_generate_keypair(&mykeys) != SC_OK) { if (sc_generate_keypair(&mykeys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate keypair"); DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate keypair");
free_config(config); free_config(config);
return -1; return -1;
} }
bytes_to_hex(mykeys.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key)); bytes_to_hex(mykeys.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
memcpy(priv_bin, mykeys.private_key, SC_PRIVKEY_SIZE);
bytes_to_hex(mykeys.public_key, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key)); } else {
// Convert existing privkey hex → binary
} else if (need_pub_key) {
// Compute public key from existing private key
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Computing public key from existing private key (pubkey was invalid/missing in %s)", filename);
uint8_t priv_bin[SC_PRIVKEY_SIZE];
uint8_t pub_bin[SC_PUBKEY_SIZE];
// Convert private key from hex to binary
for (int i = 0; i < SC_PRIVKEY_SIZE; i++) { for (int i = 0; i < SC_PRIVKEY_SIZE; i++) {
unsigned int byte; unsigned int byte;
if (sscanf(global->my_private_key_hex + i * 2, "%2x", &byte) != 1) { if (sscanf(global->my_private_key_hex + i * 2, "%2x", &byte) != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid private key hex format"); DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid private key hex format");
free_config(config); free_config(config);
return -1; return -1;
} }
priv_bin[i] = (uint8_t)byte; priv_bin[i] = (uint8_t)byte;
} }
// Compute public key
if (sc_compute_public_key_from_private(priv_bin, pub_bin) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to compute public key from private key");
free_config(config);
return -1;
}
// Convert to hex
bytes_to_hex(priv_bin, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key)); bytes_to_hex(priv_bin, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
}
bytes_to_hex(pub_bin, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key)); // ── Step 2: derive pubkey from privkey (always check) ──
uint8_t pub_bin[SC_PUBKEY_SIZE];
if (sc_compute_public_key_from_private(priv_bin, pub_bin) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to compute public key from private key");
free_config(config);
return -1;
}
bytes_to_hex(pub_bin, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
if (!is_valid_pub_key(global->my_public_key_hex) || strcmp(global->my_public_key_hex, new_pub_key) != 0) {
need_pub_key = 1;
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Pubkey mismatch (or missing), fixing: config=%s derived=%s",
global->my_public_key_hex[0] ? global->my_public_key_hex : "NULL", new_pub_key);
}
// ── Step 3: derive node_id from privkey via SHA-256 ──
{
uint8_t sha_hash[32];
SC_SHA256_CTX ctx;
sc_sha256_init(&ctx);
sc_sha256_update(&ctx, priv_bin, SC_PRIVKEY_SIZE);
sc_sha256_final(&ctx, sha_hash);
memcpy(&new_node_id, sha_hash, 8);
new_node_id &= 0x7FFFFFFFFFFFFFFFULL;
} }
if (!is_valid_node_id(global->my_node_id) || global->my_node_id != new_node_id) {
if (need_node_id) { need_node_id = 1;
if (random_bytes((uint8_t*)&new_node_id, sizeof(new_node_id)) != 0) { DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Node_id mismatch (or missing), fixing: config=%016llx derived=%016llx file=%s",
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate random node_id"); (unsigned long long)global->my_node_id, (unsigned long long)new_node_id, filename);
free_config(config);
return -1;
}
new_node_id &= 0x7FFFFFFFFFFFFFFF;
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Generating NEW random node_id=%016llx (old=%016llx). WARNING: existing invites will break! file=%s",
(unsigned long long)new_node_id, (unsigned long long)global->my_node_id, filename);
} }
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Validation results - need_priv_key=%d, need_pub_key=%d, need_node_id=%d",
need_priv_key, need_pub_key, need_node_id);
if (!need_priv_key && !need_pub_key && !need_node_id) {
free_config(config);
return 0;
}
free_config(config); free_config(config);
@ -389,7 +379,8 @@ int config_ensure_keys_and_node_id(const char *filename) {
size_t work_len = file_size; size_t work_len = file_size;
int ret = 0; int ret = 0;
int write_keys = (need_priv_key || need_pub_key || need_node_id);
if (need_node_id) { if (need_node_id) {
char node_id_hex[HEXNODEID_LEN + 1]; char node_id_hex[HEXNODEID_LEN + 1];
snprintf(node_id_hex, sizeof(node_id_hex), "%016llx", (unsigned long long)new_node_id); snprintf(node_id_hex, sizeof(node_id_hex), "%016llx", (unsigned long long)new_node_id);
@ -400,14 +391,14 @@ int config_ensure_keys_and_node_id(const char *filename) {
} }
} }
if (need_priv_key && ret == 0) { if (write_keys && ret == 0) {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Writing my_private_key to %s", filename); DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Writing my_private_key to %s", filename);
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_private_key", new_priv_key) < 0) { if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_private_key", new_priv_key) < 0) {
ret = -1; ret = -1;
} }
} }
if (need_pub_key && ret == 0) { if (write_keys && ret == 0) {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Writing my_public_key to %s", filename); DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Writing my_public_key to %s", filename);
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_public_key", new_pub_key) < 0) { if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_public_key", new_pub_key) < 0) {
ret = -1; ret = -1;

15
src/utun_instance.c

@ -18,6 +18,7 @@
#include "msg_transport.h" #include "msg_transport.h"
#include "../lib/u_async.h" #include "../lib/u_async.h"
#include "../lib/debug_config.h" #include "../lib/debug_config.h"
#include "../lib/sha256.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -82,12 +83,24 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
// Set node_id from config // Set node_id from config
instance->node_id = config->global.my_node_id; instance->node_id = config->global.my_node_id;
// Set my keys // Set my keys
if (sc_init_local_keys(&instance->my_keys, config->global.my_public_key_hex, config->global.my_private_key_hex) != SC_OK) { if (sc_init_local_keys(&instance->my_keys, config->global.my_public_key_hex, config->global.my_private_key_hex) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to initialize local keys"); DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to initialize local keys");
return -1; return -1;
} }
// Derive node_id from privkey if not set in config
if (!instance->node_id) {
uint8_t sha_hash[32];
SC_SHA256_CTX ctx;
sc_sha256_init(&ctx);
sc_sha256_update(&ctx, instance->my_keys.private_key, SC_PRIVKEY_SIZE);
sc_sha256_final(&ctx, sha_hash);
memcpy(&instance->node_id, sha_hash, 8);
instance->node_id &= 0x7FFFFFFFFFFFFFFFULL;
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "node_id derived from privkey: %016llx", (unsigned long long)instance->node_id);
}
if (sc_derive_ed25519_pubkey(instance->my_keys.private_key, instance->my_ed25519_pubkey) != SC_OK) { if (sc_derive_ed25519_pubkey(instance->my_keys.private_key, instance->my_ed25519_pubkey) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "Failed to derive Ed25519 pubkey"); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "Failed to derive Ed25519 pubkey");

24
tools/chatgui/transport/chat_sync.c

@ -853,25 +853,23 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
ch_id, name, (int)is_dm, owner, x25519, NULL, ed_pub, NULL, ch_sig); ch_id, name, (int)is_dm, owner, x25519, NULL, ed_pub, NULL, ch_sig);
chat_core_ensure_channel_ready(ch_id); chat_core_ensure_channel_ready(ch_id);
/* verify inviter's join_sig */ /* verify inviter's join_sig using ETCP-authenticated keys */
{ sqlite3* vdb = cs->inst->topo_groups->topo_sqlite_db; { struct ETCP_CONN* inv_conn = cs_find_conn_for_node(cs->inst, peer);
/* read inviter's Ed25519 pubkey */ if (!inv_conn) {
uint8_t inv_ed[32] = {0}; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP no ETCP conn for inviter peer=%016llx", CS_ID, (unsigned long long)peer);
sqlite3_stmt* es = NULL; return;
sqlite3_prepare_v2(vdb, "SELECT ed25519_pubkey FROM nodes WHERE node_id=?", -1, &es, NULL); }
if (es) { sqlite3_bind_int64(es, 1, (sqlite3_int64)peer); const uint8_t* inv_x25519 = inv_conn->crypto_ctx.peer_public_key;
if (sqlite3_step(es) == SQLITE_ROW) memcpy(inv_ed, sqlite3_column_blob(es, 0), 32); const uint8_t* inv_ed = inv_conn->peer_ed25519_pubkey;
sqlite3_finalize(es); }
/* verify */
uint8_t ivmsg[256]; size_t ilen = 0; uint8_t ivmsg[256]; size_t ilen = 0;
ilen += snprintf((char*)ivmsg + ilen, sizeof(ivmsg) - ilen, "%s", ch_id) + 1; ilen += snprintf((char*)ivmsg + ilen, sizeof(ivmsg) - ilen, "%s", ch_id) + 1;
memcpy(ivmsg + ilen, &peer, 8); ilen += 8; memcpy(ivmsg + ilen, &peer, 8); ilen += 8;
memcpy(ivmsg + ilen, x25519, 32); ilen += 32; memcpy(ivmsg + ilen, inv_x25519, 32); ilen += 32;
{ char nnm[64] = ""; _get_node_name(vdb, peer, nnm, sizeof(nnm)); { char nnm[64] = ""; _get_node_name(cs->inst->topo_groups->topo_sqlite_db, peer, nnm, sizeof(nnm));
size_t nl = strlen(nnm); memcpy(ivmsg + ilen, nnm, nl); ilen += nl; ivmsg[ilen++] = '\0'; size_t nl = strlen(nnm); memcpy(ivmsg + ilen, nnm, nl); ilen += nl; ivmsg[ilen++] = '\0';
if (cs_ed25519_verify(inv_ed, ivmsg, ilen, inviter_join_sig) != 0) { if (cs_ed25519_verify(inv_ed, ivmsg, ilen, inviter_join_sig) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP invalid inviter_join_sig peer=%016llx x25519=%016llx inv_ed=%016llx name=%s", DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP invalid inviter_join_sig peer=%016llx x25519=%016llx inv_ed=%016llx name=%s",
CS_ID, (unsigned long long)peer, *(const uint64_t*)x25519, *(const uint64_t*)inv_ed, nnm); CS_ID, (unsigned long long)peer, *(const uint64_t*)inv_x25519, *(const uint64_t*)inv_ed, nnm);
} }
} }
} }

Loading…
Cancel
Save