/* * dm_core.h — прямой p2p чат между двумя пользователями (DM) * * Отдельная подсистема (НЕ канал): без TOPO_GROUP/member_sync/merkle. * - conv_id и content_key детерминированно выводятся обеими сторонами * из своих ключей и pubkey пира (см. dm_crypto.h) — без переговоров. * - сообщения идут через etcp_router (прямое соединение или релей), * при недоступности пира — через dm_mailbox (storage-узлы). * - содержимое шифруется E2E (AES-256-CCM), запись подписывается Ed25519. * * Два независимых направленных потока (я→пир и пир→я) с монотонным seq. */ #ifndef DM_CORE_H #define DM_CORE_H #include #include #include struct UTUN_INSTANCE; #ifdef __cplusplus extern "C" { #endif /* ── Каноническое тело DM-сообщения (общий wire-формат dm_core и dm_mailbox) ── * [conv_id:8][seq:8][ts:8][author:8][ct_len:1][ct:ct_len][data_len:2][data_enc:data_len][sig:64] * data_enc — AES-256-CCM (шифротекст + tag), sig — Ed25519 автора по всем полям до sig. */ #define DM_MSG_FIXED_HDR 33 /* conv_id+seq+ts+author(32) + ct_len(1) — до ct */ #define DM_MSG_SIG_SIZE 64 /* Вычислить полную длину канонического тела из его заголовка. * Возвращает 1 и *out_len, либо 0 если буфер мал/повреждён. */ static inline int dm_msg_len(const uint8_t* msg, size_t avail, size_t* out_len) { if (!msg || avail < DM_MSG_FIXED_HDR) return 0; uint8_t ct_len = msg[32]; if (avail < (size_t)DM_MSG_FIXED_HDR + ct_len + 2) return 0; uint16_t data_len; memcpy(&data_len, msg + DM_MSG_FIXED_HDR + ct_len, 2); size_t total = (size_t)DM_MSG_FIXED_HDR + ct_len + 2 + data_len + DM_MSG_SIG_SIZE; if (avail < total) return 0; *out_len = total; return 1; } /* Жизненный цикл: инициализация после chat_core_init, destroy перед chat_core_destroy. */ int dm_core_init(struct UTUN_INSTANCE* inst); void dm_core_destroy(struct UTUN_INSTANCE* inst); /* Начать беседу с пользователем (peer). source_ch_id — канал, где нашли target * (общая группа; нужен для маршрутизации и подключения). Создаёт беседу если её * ещё нет, выводит conv_id/ключи, пытается установить прямое соединение. * Возвращает 0 при успехе, <0 при ошибке. */ int dm_start(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, const uint8_t peer_x25519[32], const uint8_t peer_ed25519[32], const char* peer_name, const char* source_ch_id); /* Отправить сообщение в беседу. Шифрует, подписывает, сохраняет локально и * доставляет (прямое соединение при доступности пира, иначе mailbox). * conv_id — строковое представление (как в dm_conversations.conv_id). */ int dm_send(struct UTUN_INSTANCE* inst, const char* conv_id, const char* content_type, const uint8_t* data, uint32_t data_len); /* Список бесед в JSON: [{conv_id,peer_node_id,peer_name,last_ts,out_seq,in_seq}] */ int dm_list_conversations_json(struct UTUN_INSTANCE* inst, char* buf, size_t buf_size, size_t* out_len); /* Сообщения беседы в JSON (расшифрованные): [{dir,seq,ts,author,ct,data}] * count=0 → все, offset=0 → с начала. */ int dm_list_messages_json(struct UTUN_INSTANCE* inst, const char* conv_id, int count, int offset, char* buf, size_t buf_size, size_t* out_len); #ifdef __cplusplus } #endif #endif /* DM_CORE_H */