Browse Source
- chat_admin: передача канальных privkey другому узлу (encrypted+signed, ETCP_RT_ID_ADMIN_KEY), событие ADMIN_KEY_RECEIVED - member_sync/bridge: tags verified/deleted, is_channel_owner, transfer_admin (android JNI + chatgui memberpropsdialog) - node_conn_direct: оживление timed_out entry — обновление линков в существующем conn (убрать старые, добавить новые) + немедленная очистка не поднявшегося conn без fin_wait — фикс invite/reconnectv2
21 changed files with 615 additions and 160 deletions
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* chat_admin.c — передача канального приватного ключа (прав админа) другому узлу |
||||
* |
||||
* Владелец канала передаёт оба ключа (ed25519_privkey + x25519_privkey) выбранному |
||||
* мемберу через etcp_router с ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT. |
||||
* Получатель верифицирует ключи (вывод pubkey == сохранённые pubkey канала) и импортирует. |
||||
*/ |
||||
|
||||
#include "chat_admin.h" |
||||
#include "chat_core_priv.h" |
||||
#include "chat_event.h" |
||||
|
||||
#include "../utun_instance.h" |
||||
#include "../routing_layer/topo_node_sqlite.h" |
||||
#include "../routing_layer/etcp_router.h" |
||||
#include "../routing_layer/route_crypto.h" |
||||
#include "../transport_layer/etcp_api.h" |
||||
#include "../transport_layer/etcp.h" |
||||
#include "../transport_layer/secure_channel.h" |
||||
#include "../../lib/mem.h" |
||||
#include "../../lib/ll_queue.h" |
||||
#include "../../lib/debug_config.h" |
||||
|
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <openssl/evp.h> |
||||
|
||||
#define CA_ID "chat_admin" |
||||
|
||||
/* ─── ed25519 pubkey из ed25519 privkey (канальные ключи — независимая пара) ─── */ |
||||
|
||||
static int ed25519_pub_from_priv(const uint8_t priv[32], uint8_t pub[32]) { |
||||
EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, priv, 32); |
||||
if (!pkey) return -1; |
||||
size_t plen = 32; |
||||
int ok = EVP_PKEY_get_raw_public_key(pkey, pub, &plen) == 1 && plen == 32; |
||||
EVP_PKEY_free(pkey); |
||||
return ok ? 0 : -1; |
||||
} |
||||
|
||||
/* ─── приём переданного ключа ───
|
||||
* payload: [group_id:8 LE][ed25519_priv:32][x25519_priv:32] |
||||
*/ |
||||
static void admin_key_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { |
||||
if (!entry) return; |
||||
if (!conn || !entry->dgram || entry->len < ROUTER_SVC_PAYLOAD_OFF + 8 + 32 + 32) { |
||||
queue_dgram_free(entry); queue_entry_free(entry); return; |
||||
} |
||||
struct UTUN_INSTANCE* inst = conn->instance; |
||||
if (!inst) { queue_dgram_free(entry); queue_entry_free(entry); return; } |
||||
if (!g_cc.initialized || !g_cc.db) { queue_dgram_free(entry); queue_entry_free(entry); return; } |
||||
|
||||
const uint8_t* d = entry->dgram; |
||||
uint64_t from_node; |
||||
memcpy(&from_node, d + ROUTER_SVC_SRC_OFF, 8); |
||||
|
||||
const uint8_t* payload = d + ROUTER_SVC_PAYLOAD_OFF; |
||||
uint64_t group_id; memcpy(&group_id, payload, 8); |
||||
const uint8_t* ed_priv = payload + 8; |
||||
const uint8_t* x_priv = payload + 8 + 32; |
||||
|
||||
char ch_id[64]; snprintf(ch_id, sizeof(ch_id), "%llu", (unsigned long long)group_id); |
||||
|
||||
/* проверить, что ключи соответствуют публичным ключам канала (защита от подлога) */ |
||||
uint8_t ch_x_pub[32], ch_ed_pub[32]; |
||||
if (topo_node_sqlite_channel_get(g_cc.db, ch_id, NULL, 0, NULL, ch_x_pub, ch_ed_pub, NULL) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — no channel ch=%s from 0x%016llx, drop", |
||||
CA_ID, ch_id, (unsigned long long)from_node); |
||||
queue_dgram_free(entry); queue_entry_free(entry); return; |
||||
} |
||||
|
||||
uint8_t ed_pub_check[32], x_pub_check[32]; |
||||
if (ed25519_pub_from_priv(ed_priv, ed_pub_check) != 0 |
||||
|| memcmp(ed_pub_check, ch_ed_pub, 32) != 0 |
||||
|| sc_compute_public_key_from_private(x_priv, x_pub_check) != SC_OK |
||||
|| memcmp(x_pub_check, ch_x_pub, 32) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — key mismatch for ch=%s from 0x%016llx, drop", |
||||
CA_ID, ch_id, (unsigned long long)from_node); |
||||
queue_dgram_free(entry); queue_entry_free(entry); return; |
||||
} |
||||
|
||||
if (topo_node_sqlite_channel_set_privs(g_cc.db, ch_id, x_priv, ed_priv) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — set_privs FAILED ch=%s from 0x%016llx", |
||||
CA_ID, ch_id, (unsigned long long)from_node); |
||||
queue_dgram_free(entry); queue_entry_free(entry); return; |
||||
} |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "%s: received admin key ch=%s from 0x%016llx — imported, now co-owner", |
||||
CA_ID, ch_id, (unsigned long long)from_node); |
||||
|
||||
uint8_t evt[72]; |
||||
uint8_t cl = (uint8_t)strlen(ch_id); |
||||
evt[0] = cl; memcpy(evt + 1, ch_id, cl); |
||||
memcpy(evt + 1 + cl, &from_node, 8); |
||||
chat_event_post(CHAT_EVT_ADMIN_KEY_RECEIVED, evt, 1 + cl + 8); |
||||
|
||||
queue_dgram_free(entry); queue_entry_free(entry); |
||||
} |
||||
|
||||
/* ─── жизненный цикл ─── */ |
||||
|
||||
int chat_admin_init(struct UTUN_INSTANCE* inst) { |
||||
if (!inst) return -1; |
||||
if (etcp_router_bind(inst, ETCP_RT_ID_ADMIN_KEY, admin_key_recv_cb) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: etcp_router_bind failed", CA_ID); |
||||
return -1; |
||||
} |
||||
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "%s: initialized", CA_ID); |
||||
return 0; |
||||
} |
||||
|
||||
void chat_admin_destroy(struct UTUN_INSTANCE* inst) { |
||||
if (!inst) return; |
||||
etcp_router_unbind(inst, ETCP_RT_ID_ADMIN_KEY); |
||||
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "%s: destroyed", CA_ID); |
||||
} |
||||
|
||||
/* ─── передача прав (вызывается из uasync-потока) ─── */ |
||||
|
||||
void chat_core_transfer_admin(const char* ch_id, uint64_t target_node_id) { |
||||
if (!g_cc.initialized || !g_cc.inst || !g_cc.db) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — not initialized", CA_ID); |
||||
return; |
||||
} |
||||
if (!ch_id || !ch_id[0] || target_node_id == 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — invalid args ch=%s target=0x%016llx", |
||||
CA_ID, ch_id ? ch_id : "(null)", (unsigned long long)target_node_id); |
||||
return; |
||||
} |
||||
|
||||
uint8_t x_priv[32], ed_priv[32]; |
||||
if (topo_node_sqlite_channel_get_privs(g_cc.db, ch_id, x_priv, ed_priv) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — no channel privkey ch=%s (not owner?)", CA_ID, ch_id); |
||||
return; |
||||
} |
||||
|
||||
uint64_t group_id = strtoull(ch_id, NULL, 10); |
||||
if (group_id == 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — invalid ch_id=%s", CA_ID, ch_id); |
||||
return; |
||||
} |
||||
|
||||
uint8_t payload[8 + 32 + 32]; |
||||
memcpy(payload, &group_id, 8); |
||||
memcpy(payload + 8, ed_priv, 32); |
||||
memcpy(payload + 8 + 32, x_priv, 32); |
||||
|
||||
struct ll_entry* entry = queue_entry_new(0); |
||||
if (!entry) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — queue_entry_new failed", CA_ID); return; } |
||||
entry->dgram = u_malloc(1 + sizeof(payload)); |
||||
if (!entry->dgram) { queue_entry_free(entry); return; } |
||||
entry->dgram[0] = ETCP_RT_ID_ADMIN_KEY; |
||||
memcpy(entry->dgram + 1, payload, sizeof(payload)); |
||||
entry->len = 1 + (uint16_t)sizeof(payload); |
||||
|
||||
int rc = etcp_route_send(g_cc.inst, group_id, target_node_id, entry, 1, |
||||
ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT); |
||||
if (rc != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — etcp_route_send FAILED rc=%d ch=%s → 0x%016llx", |
||||
CA_ID, rc, ch_id, (unsigned long long)target_node_id); |
||||
queue_dgram_free(entry); queue_entry_free(entry); |
||||
return; |
||||
} |
||||
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "%s: admin key sent ch=%s → 0x%016llx (encrypted+signed)", |
||||
CA_ID, ch_id, (unsigned long long)target_node_id); |
||||
} |
||||
|
||||
void chat_core_transfer_admin_trampoline(void* arg) { |
||||
struct chat_transfer_admin_req* req = (struct chat_transfer_admin_req*)arg; |
||||
if (!req) return; |
||||
chat_core_transfer_admin(req->ch_id, req->target_node_id); |
||||
u_free(req); |
||||
} |
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* chat_admin.h — передача канального приватного ключа (прав админа) другому узлу |
||||
* |
||||
* Владелец канала (имеющий ed25519_privkey + x25519_privkey) может передать оба ключа |
||||
* выбранному мемберу. Передача идёт через сервисный пакет etcp_router с |
||||
* ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT: X25519 ECDH + AES-128-CCM и Ed25519-подпись. |
||||
* Получатель автоматически импортирует ключи и становится совладельцем канала. |
||||
*/ |
||||
|
||||
#ifndef CHAT_ADMIN_H |
||||
#define CHAT_ADMIN_H |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
struct UTUN_INSTANCE; |
||||
|
||||
/* Зарегистрировать/снять обработчик сервиса ETCP_RT_ID_ADMIN_KEY. */ |
||||
int chat_admin_init(struct UTUN_INSTANCE* inst); |
||||
void chat_admin_destroy(struct UTUN_INSTANCE* inst); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
#endif /* CHAT_ADMIN_H */ |
||||
Loading…
Reference in new issue