- 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: сопутствующие изменения
177 lines
7.5 KiB
C
177 lines
7.5 KiB
C
/*
|
||
* 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);
|
||
}
|