Files
utun2/src/chat/chat_admin.c
T

193 lines
8.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: Ed25519 private key initialization failed", CA_ID); return -1; }
size_t plen = 32;
int ok = EVP_PKEY_get_raw_public_key(pkey, pub, &plen) == 1 && plen == 32;
EVP_PKEY_free(pkey);
if (!ok) DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: Ed25519 public key derivation failed bytes=%zu", CA_ID, plen);
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) {
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — invalid delivery len=%u", CA_ID, entry->len);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
struct UTUN_INSTANCE* inst = conn->instance;
if (!inst) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — no instance", CA_ID);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
struct chat_core_ctx* cc = CC(inst);
if (!cc || !cc->initialized || !cc->db) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — not initialized", CA_ID);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
if (!sqlite3_get_autocommit(cc->db)) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — import rejected inside external transaction", CA_ID);
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(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(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);
if (!sqlite3_get_autocommit(cc->db)) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — rollback left transaction open ch=%s; restart required", CA_ID, ch_id);
inst->running = 0;
}
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(inst, 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(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t target_node_id) {
struct chat_core_ctx* cc = inst ? CC(inst) : NULL;
if (!cc || !cc->initialized || !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(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) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — payload allocation failed", CA_ID); 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(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->inst, req->ch_id, req->target_node_id);
u_free(req);
}