Files
utun2/src/chat/chat_admin.c
T
evgeny 73fe99b33f dm: прямой обмен сообщениями (DM) + soundtouch + архитектура звонков
- src/dm/: dm_core/dm_crypto/dm_mailbox — прямой обмен сообщениями (DM)
- lib/soundtouch-master/: vendored SoundTouch (изменение темпа/высоты аудио)
- doc/dm_arch.md, doc/chat_call_arch.md: архитектура DM и звонков
- tests: test_dm.c, test_dm_e2e.c
- chat: рефакторинг chat_* модулей, member_sync, invite_build
- chatgui/chatgui-android: синхронизация с новым API чата
- lib/u_async, debug_config, utun_instance: сопутствующие изменения
2026-09-15 21:09:17 +03:00

177 lines
7.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) 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; }
struct chat_core_ctx* cc = CC(inst);
if (!cc || !cc->initialized || !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(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);
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 = CC(inst);
if (!cc || !cc->initialized || !inst || !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) { 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);
}