etcp_router: Ed25519 digital signature for service messages
- Add ed25519_public_key[32] to NODEINFO struct for pubkey distribution - Add ed25519_public_key[32] to ROUTE_BGP (derived from X25519 privkey at init) - Add ROUTER_FLAG_SIGNED (0x08) to SVC_ROUTE_HDR flags byte - router_send_one_flags: when is_signed, sign [hdr][payload] and append 64-byte sig - etcp_router_recv_cb: verify Ed25519 signature when ROUTER_FLAG_SIGNED set - New API: etcp_router_conn_send_signed() - Drop signed packets if sender node not in routing table or no Ed25519 key - 5 unit tests in test_etcp_router_unit.c covering OK/tampered/unknown/no-key/short
This commit is contained in:
1 parent
2bbdf8195f
commit
6717c863e7
47 files changed
+18845
-16
No files matched your search
@@ -11,6 +11,7 @@ utun_CORE_SOURCES = \
|
||||
route_bgp.c \
|
||||
route_ping.c \
|
||||
route_node.c \
|
||||
route_node_lmdb.c \
|
||||
route_connectivity.c \
|
||||
routing.c \
|
||||
tun_if.c \
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Архитектура чата (группового)
|
||||
|
||||
1. сообщения чата (текст + ссылки на аттачи) хранятся в таблице:
|
||||
id (primary key), timestamp, user_id, msg (utf8 text 4k max), sign
|
||||
|
||||
2. реакции: key(msg_id, user_id), type, sign
|
||||
3. аттачи: key(msg_id, user_id), filename, sign
|
||||
|
||||
Структура каталогов:
|
||||
chats.db - база
|
||||
<caht_id>
|
||||
+4
-1
@@ -386,6 +386,9 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
if (strcmp(key, "log_file") == 0) {
|
||||
return assign_string(global->log_file, sizeof(global->log_file), value);
|
||||
}
|
||||
if (strcmp(key, "db_path") == 0) {
|
||||
return assign_string(global->db_path, sizeof(global->db_path), value);
|
||||
}
|
||||
/* debug_categories key is deprecated - use [debug] section instead */
|
||||
if (strcmp(key, "enable_timestamp") == 0) {
|
||||
global->enable_timestamp = atoi(value);
|
||||
@@ -407,7 +410,7 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
global->tun_test_mode = atoi(value);
|
||||
return 0;
|
||||
}
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown global option '%s'. Valid: my_node_name, my_private_key, my_public_key, my_node_id, tun_ifname, tun_ip, mtu, keepalive_timeout, keepalive_interval, bbr_max_cwnd, debug_level, log_file, enable_timestamp, enable_function_names, enable_file_lines, enable_colors, tun_test_mode", filename, line_num, key);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown global option '%s'. Valid: my_node_name, my_private_key, my_public_key, my_node_id, tun_ifname, tun_ip, mtu, keepalive_timeout, keepalive_interval, bbr_max_cwnd, debug_level, log_file, db_path, enable_timestamp, enable_function_names, enable_file_lines, enable_colors, tun_test_mode", filename, line_num, key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ struct global_config {
|
||||
|
||||
// Debug and logging configuration
|
||||
char log_file[256]; // Path to log file (empty = stdout)
|
||||
char db_path[256]; // Path to LMDB nodeinfo database (empty = disabled)
|
||||
char debug_level[16]; // debug level: error, warn, info, debug, trace
|
||||
int enable_timestamp; // enable timestamps in logs
|
||||
int enable_function_names; // enable function names in logs
|
||||
|
||||
+94
-12
@@ -22,7 +22,7 @@ static void router_schedule_ack(struct ETCP_ROUTER_CONN* rconn);
|
||||
static void router_try_assembly(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn);
|
||||
static void router_deliver(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn, const uint8_t* payload, size_t payload_len);
|
||||
static int router_send_one(struct ETCP_ROUTER_CONN* rconn, const uint8_t* payload, size_t pl_len);
|
||||
static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* payload, size_t pl_len, uint32_t seq_flags);
|
||||
static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* payload, size_t pl_len, uint32_t seq_flags, int is_signed);
|
||||
static void router_send_to(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t svc_id, uint32_t seq_flags);
|
||||
static void router_drain_send_q(struct ETCP_ROUTER_CONN* rconn);
|
||||
static void router_send_resume_cb(void* arg);
|
||||
@@ -47,15 +47,16 @@ static struct ETCP_ROUTER_CONN* router_conn_find(struct UTUN_INSTANCE* inst,
|
||||
// ====================================================================
|
||||
|
||||
static int router_send_one(struct ETCP_ROUTER_CONN* rconn, const uint8_t* payload, size_t pl_len) {
|
||||
return router_send_one_flags(rconn, payload, pl_len, 0);
|
||||
return router_send_one_flags(rconn, payload, pl_len, 0, 0);
|
||||
}
|
||||
|
||||
static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* payload, size_t pl_len, uint32_t seq_flags) {
|
||||
static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* payload, size_t pl_len, uint32_t seq_flags, int is_signed) {
|
||||
struct UTUN_INSTANCE* inst = rconn->inst;
|
||||
uint32_t seq = (seq_flags & (ROUTER_SEQ_CLOSE_FLAG | ROUTER_SEQ_RST_FLAG))
|
||||
? 0 : rconn->tx_seq++;
|
||||
seq |= seq_flags;
|
||||
size_t total_len = SVC_ROUTE_HDR_SIZE + pl_len;
|
||||
size_t sig_len = is_signed ? SC_SIGN_SIZE : 0;
|
||||
size_t total_len = SVC_ROUTE_HDR_SIZE + pl_len + sig_len;
|
||||
uint8_t* dgram = u_malloc(total_len);
|
||||
if (!dgram) { if (!seq_flags) rconn->tx_seq--; return -1; }
|
||||
struct SVC_ROUTE_HDR* hdr = (struct SVC_ROUTE_HDR*)dgram;
|
||||
@@ -70,25 +71,46 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t*
|
||||
f |= ROUTER_FLAG_START;
|
||||
rconn->start_sent = 1;
|
||||
}
|
||||
if (is_signed) f |= ROUTER_FLAG_SIGNED;
|
||||
hdr->flags = f;
|
||||
}
|
||||
if (pl_len > 0) memcpy(dgram + SVC_ROUTE_HDR_SIZE, payload, pl_len);
|
||||
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, rconn->remote_node_id);
|
||||
if (!conn) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: no route to %016llx svc_id=%u",
|
||||
(unsigned long long)rconn->remote_node_id, rconn->svc_id);
|
||||
u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1;
|
||||
}
|
||||
|
||||
if (is_signed) {
|
||||
struct sc_stream_sign_state sign_state;
|
||||
if (sc_stream_sign_init(&conn->crypto_ctx, &sign_state) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_init failed svc_id=%u", rconn->svc_id);
|
||||
u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1;
|
||||
}
|
||||
size_t data_len = SVC_ROUTE_HDR_SIZE + pl_len;
|
||||
if (sc_stream_sign_update(&sign_state, dgram, data_len) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_update failed svc_id=%u", rconn->svc_id);
|
||||
sc_stream_sign_cleanup(&sign_state); u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1;
|
||||
}
|
||||
size_t actual_sig_len = SC_SIGN_SIZE;
|
||||
if (sc_stream_sign_final(&sign_state, dgram + data_len, &actual_sig_len) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_final failed svc_id=%u", rconn->svc_id);
|
||||
u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1;
|
||||
}
|
||||
}
|
||||
|
||||
struct ll_entry* entry = queue_entry_new(0);
|
||||
if (!entry) { u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1; }
|
||||
entry->dgram = dgram;
|
||||
entry->len = total_len;
|
||||
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, rconn->remote_node_id);
|
||||
if (!conn) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: no route to %016llx svc_id=%u",
|
||||
(unsigned long long)rconn->remote_node_id, rconn->svc_id);
|
||||
queue_entry_free(entry); queue_dgram_free(entry); if (!seq_flags) rconn->tx_seq--; return -1;
|
||||
}
|
||||
rconn->last_dgram_ts = get_current_timestamp();
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "ETCP_SEND: svc_id=%u seq=%u len=%zu inflight=%d → %016llx",
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "ETCP_SEND: svc_id=%u seq=%u len=%zu inflight=%d signed=%d → %016llx",
|
||||
rconn->svc_id, seq, pl_len,
|
||||
(int32_t)(rconn->tx_seq - rconn->tx_acked),
|
||||
is_signed,
|
||||
(unsigned long long)rconn->remote_node_id);
|
||||
return etcp_send(conn, entry);
|
||||
}
|
||||
@@ -136,7 +158,7 @@ static void router_close_and_notify(struct ETCP_ROUTER_CONN* rconn) {
|
||||
router_send_close_to_service(rconn);
|
||||
// Отправляем CLOSE удалённой стороне
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, rconn->remote_node_id);
|
||||
if (conn) router_send_one_flags(rconn, NULL, 0, ROUTER_SEQ_CLOSE_FLAG);
|
||||
if (conn) router_send_one_flags(rconn, NULL, 0, ROUTER_SEQ_CLOSE_FLAG, 0);
|
||||
// Таймеры
|
||||
if (rconn->ack_timer) { uasync_cancel_timeout(inst->ua, rconn->ack_timer); rconn->ack_timer = NULL; }
|
||||
if (rconn->idle_ack_timer) { uasync_cancel_timeout(inst->ua, rconn->idle_ack_timer); rconn->idle_ack_timer = NULL; }
|
||||
@@ -343,6 +365,52 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
hdr->svc_id, hdr->seq, pl_len, (unsigned long long)hdr->src_node_id);
|
||||
|
||||
if (hdr->dst_node_id == inst->node_id) {
|
||||
// ========== Проверка подписи (если флаг ROUTER_FLAG_SIGNED) ==========
|
||||
size_t actual_payload = pl_len;
|
||||
if (hdr->flags & ROUTER_FLAG_SIGNED) {
|
||||
if (pl_len < SC_SIGN_SIZE) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: SIGNED packet too short len=%zu from %016llx", pl_len, (unsigned long long)hdr->src_node_id);
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
actual_payload = pl_len - SC_SIGN_SIZE;
|
||||
uint8_t* sig = pl + actual_payload;
|
||||
struct ROUTE_BGP* bgp = inst->bgp;
|
||||
struct NODEINFO_Q* nq = bgp ? route_bgp_get_node(bgp, hdr->src_node_id) : NULL;
|
||||
if (!nq) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: SIGNED packet from unknown node %016llx — dropping", (unsigned long long)hdr->src_node_id);
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
{
|
||||
uint8_t zero[SC_PUBKEY_SIZE] = {0};
|
||||
if (memcmp(nq->node.ed25519_public_key, zero, SC_PUBKEY_SIZE) == 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: no Ed25519 pubkey for node %016llx — dropping", (unsigned long long)hdr->src_node_id);
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct sc_stream_sign_state vfy_state;
|
||||
if (sc_stream_sign_verify_init(&vfy_state, nq->node.ed25519_public_key) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router: sign_verify_init failed for node %016llx", (unsigned long long)hdr->src_node_id);
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
size_t signed_len = SVC_ROUTE_HDR_SIZE + actual_payload;
|
||||
if (sc_stream_sign_update(&vfy_state, entry->dgram, signed_len) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router: sign_update failed for node %016llx", (unsigned long long)hdr->src_node_id);
|
||||
sc_stream_sign_cleanup(&vfy_state); queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
if (sc_stream_sign_verify(&vfy_state, sig, SC_SIGN_SIZE) != SC_OK) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: signature verification FAILED for node %016llx svc_id=%u — dropping", (unsigned long long)hdr->src_node_id, hdr->svc_id);
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
pl_len = actual_payload;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE, "router: signature verified for node %016llx svc_id=%u", (unsigned long long)hdr->src_node_id, hdr->svc_id);
|
||||
}
|
||||
|
||||
// ========== Мы — целевая нода ==========
|
||||
struct ETCP_ROUTER_CONN* rconn = router_conn_find(inst, hdr->src_node_id, hdr->svc_id);
|
||||
|
||||
@@ -713,6 +781,20 @@ int etcp_router_conn_send(struct ETCP_ROUTER_CONN* rconn,
|
||||
return router_send_one(rconn, data, len);
|
||||
}
|
||||
|
||||
int etcp_router_conn_send_signed(struct ETCP_ROUTER_CONN* rconn,
|
||||
const uint8_t* data, size_t len) {
|
||||
if (!rconn || !data || len == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_conn_send_signed: invalid args rconn=%p data=%p len=%zu",
|
||||
(void*)rconn, (const void*)data, len);
|
||||
return -1;
|
||||
}
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= ROUTER_MAX_INFLIGHT) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_conn_send_signed: inflight full svc_id=%u", rconn->svc_id);
|
||||
return -1;
|
||||
}
|
||||
return router_send_one_flags(rconn, data, len, 0, 1);
|
||||
}
|
||||
|
||||
void etcp_router_conn_close(struct ETCP_ROUTER_CONN* rconn) {
|
||||
router_close_and_notify(rconn);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ struct SVC_ROUTE_HDR {
|
||||
// Биты в flags
|
||||
#define ROUTER_FLAG_START 0x80
|
||||
#define ROUTER_FLAG_RST 0x40
|
||||
#define ROUTER_FLAG_SIGNED 0x08 // пакет содержит Ed25519 подпись (64 байта после payload)
|
||||
#define ROUTER_SESS_ID_SHIFT 4
|
||||
#define ROUTER_SESS_ID_MASK 0x30
|
||||
|
||||
@@ -96,6 +97,10 @@ struct ETCP_ROUTER_CONN* etcp_router_conn_get(struct UTUN_INSTANCE* inst,
|
||||
int etcp_router_conn_send(struct ETCP_ROUTER_CONN* rconn,
|
||||
const uint8_t* data, size_t len);
|
||||
|
||||
// Отправить данные с Ed25519 подписью (аналог etcp_router_conn_send с флагом ROUTER_FLAG_SIGNED)
|
||||
int etcp_router_conn_send_signed(struct ETCP_ROUTER_CONN* rconn,
|
||||
const uint8_t* data, size_t len);
|
||||
|
||||
// Закрыть seq-подключение
|
||||
void etcp_router_conn_close(struct ETCP_ROUTER_CONN* rconn);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "etcp_debug.h"
|
||||
#include "config_parser.h"
|
||||
#include "route_node.h"
|
||||
#include "route_node_lmdb.h"
|
||||
#include "route_lib.h"
|
||||
#include "route_bgp.h"
|
||||
#include "route_ping.h"
|
||||
@@ -244,6 +245,16 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
|
||||
bgp->instance = instance;
|
||||
bgp->ping_pending = NULL;
|
||||
bgp->next_ping_req_id = 1;
|
||||
bgp->nodeinfo_env = NULL;
|
||||
bgp->nodeinfo_dbi = 0;
|
||||
|
||||
sc_derive_ed25519_pubkey(instance->my_keys.private_key, bgp->ed25519_public_key);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Ed25519 pubkey derived from X25519 private key");
|
||||
|
||||
// Создаём/открываем LMDB базу nodeinfo если задан db_path в конфиге
|
||||
if (instance->config && instance->config->global.db_path[0]) {
|
||||
route_node_lmdb_init(bgp, instance->config->global.db_path);
|
||||
}
|
||||
|
||||
// Create nodes queue with hash support for fast lookup by node_id
|
||||
bgp->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, offsetof(struct NODEINFO_Q, node.node_id)-sizeof(struct ll_entry), 8, "bgp_nodes");
|
||||
@@ -324,6 +335,8 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
|
||||
u_free(instance->bgp->local_node);
|
||||
}
|
||||
|
||||
route_node_lmdb_destroy(instance->bgp);
|
||||
|
||||
if (instance->bgp->nodes) {
|
||||
queue_free(instance->bgp->nodes);
|
||||
}
|
||||
@@ -813,6 +826,10 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co
|
||||
route_insert(bgp->instance->rt, nodeinfo1);
|
||||
}
|
||||
|
||||
if (node_id != bgp->instance->node_id) {
|
||||
route_node_lmdb_put(bgp, nodeinfo1);
|
||||
}
|
||||
|
||||
int hop_count=nodeinfo1->node.hop_count;
|
||||
|
||||
// рассылаем по узлам которых нет в hop_list этой ноды (loop prevention)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/liblmdb/lmdb.h"
|
||||
#include "route_lib.h"
|
||||
#include "route_node.h"
|
||||
#include "secure_channel.h"
|
||||
@@ -88,6 +89,9 @@ struct ROUTE_BGP {
|
||||
struct route_ping_pending* ping_pending;
|
||||
uint32_t next_ping_req_id;
|
||||
uint8_t allow_nat_check_local; // 1 = разрешить NAT check для локальных подсетей (для тестов)
|
||||
uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; // свой Ed25519 pubkey (для подписи сообщений)
|
||||
MDB_env* nodeinfo_env; // LMDB environment handle
|
||||
MDB_dbi nodeinfo_dbi; // LMDB database handle for nodeinfo table
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,6 +176,9 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG
|
||||
|| (vc6 != (int)bgp->local_node->node.local_v6_subnets)
|
||||
|| (sock6_count != (int)bgp->local_node->node.local_v6_sockets)
|
||||
|| (addr6_count != (int)bgp->local_node->node.local_v6_addrs);
|
||||
if (bgp->local_node && bgp->instance && bgp->instance->bgp) {
|
||||
if (memcmp(bgp->local_node->node.ed25519_public_key, bgp->instance->bgp->ed25519_public_key, SC_PUBKEY_SIZE) != 0) changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
@@ -193,6 +196,7 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG
|
||||
bgp->local_node->node.local_v6_subnets = vc6;
|
||||
bgp->local_node->node.node_name_len = name_len;
|
||||
memcpy(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE);
|
||||
memcpy(bgp->local_node->node.ed25519_public_key, bgp->ed25519_public_key, SC_PUBKEY_SIZE);
|
||||
bgp->local_node->dirty = 1;
|
||||
bgp->local_node->last_ver = bgp->local_node->node.ver;
|
||||
|
||||
|
||||
+2
-1
@@ -57,7 +57,8 @@ struct NODE_CONNECTIVITY {
|
||||
struct NODEINFO {
|
||||
uint64_t node_id; // (big-endian)
|
||||
uint8_t ver; // версия пакета (циклический счетчик чтобы быстро сравнивать с локальной копией - были ли обновления)
|
||||
uint8_t public_key[SC_PUBKEY_SIZE]; // node pubkey
|
||||
uint8_t public_key[SC_PUBKEY_SIZE]; // X25519 pubkey
|
||||
uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; // Ed25519 pubkey for signature verification
|
||||
uint8_t node_name_len; // размер в байтах (без null терминации)
|
||||
uint8_t local_v4_sockets; // NODEINFO_IPV4_SOCKET_META число метаданных ipv4 сокетов
|
||||
uint8_t local_v4_addrs; // NODEINFO_IPV4_ADDR число типизированных ipv4 адресов узла (плоский список)
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include "route_node_lmdb.h"
|
||||
#include "route_bgp.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
|
||||
#define NODEINFO_DB_MAPSIZE (10UL * 1024 * 1024)
|
||||
|
||||
int route_node_lmdb_init(struct ROUTE_BGP* bgp, const char* db_path) {
|
||||
if (!bgp || !db_path || !db_path[0]) return -1;
|
||||
|
||||
int rc = mdb_env_create(&bgp->nodeinfo_env);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_create failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_env_set_mapsize(bgp->nodeinfo_env, NODEINFO_DB_MAPSIZE);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_set_mapsize failed: %s", mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_env_set_maxdbs(bgp->nodeinfo_env, 2);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_set_maxdbs failed: %s", mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_env_open(bgp->nodeinfo_env, db_path, 0, 0644);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_open(%s) failed: %s", db_path, mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
MDB_txn* txn = NULL;
|
||||
rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_txn_begin failed: %s", mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_dbi_open(txn, "nodeinfo", MDB_CREATE, &bgp->nodeinfo_dbi);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_dbi_open(nodeinfo) failed: %s", mdb_strerror(rc));
|
||||
mdb_txn_abort(txn);
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_txn_commit failed: %s", mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "LMDB nodeinfo database opened: %s", db_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void route_node_lmdb_destroy(struct ROUTE_BGP* bgp) {
|
||||
if (!bgp) return;
|
||||
if (bgp->nodeinfo_env) {
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "LMDB nodeinfo database closed");
|
||||
}
|
||||
}
|
||||
|
||||
int route_node_lmdb_put(struct ROUTE_BGP* bgp, struct NODEINFO_Q* nq) {
|
||||
if (!bgp || !bgp->nodeinfo_env || !nq) return -1;
|
||||
|
||||
int dyn_size = nodeinfo_dyn_size(&nq->node);
|
||||
size_t val_size = sizeof(struct NODEINFO) + dyn_size;
|
||||
|
||||
MDB_val key, data;
|
||||
key.mv_size = 8;
|
||||
key.mv_data = &nq->node.node_id;
|
||||
|
||||
data.mv_size = val_size;
|
||||
data.mv_data = (void*)&nq->node;
|
||||
|
||||
MDB_txn* txn = NULL;
|
||||
int rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB put mdb_txn_begin failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_put(txn, bgp->nodeinfo_dbi, &key, &data, 0);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_put node %016llx failed: %s",
|
||||
(unsigned long long)nq->node.node_id, mdb_strerror(rc));
|
||||
mdb_txn_abort(txn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB put mdb_txn_commit failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int route_node_lmdb_del(struct ROUTE_BGP* bgp, uint64_t node_id) {
|
||||
if (!bgp || !bgp->nodeinfo_env) return -1;
|
||||
|
||||
MDB_val key;
|
||||
key.mv_size = 8;
|
||||
key.mv_data = &node_id;
|
||||
|
||||
MDB_txn* txn = NULL;
|
||||
int rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB del mdb_txn_begin failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_del(txn, bgp->nodeinfo_dbi, &key, NULL);
|
||||
if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_del node %016llx failed: %s",
|
||||
(unsigned long long)node_id, mdb_strerror(rc));
|
||||
mdb_txn_abort(txn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB del mdb_txn_commit failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef ROUTE_NODE_LMDB_H
|
||||
#define ROUTE_NODE_LMDB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "route_node.h"
|
||||
|
||||
struct ROUTE_BGP;
|
||||
|
||||
int route_node_lmdb_init(struct ROUTE_BGP* bgp, const char* db_path);
|
||||
void route_node_lmdb_destroy(struct ROUTE_BGP* bgp);
|
||||
int route_node_lmdb_put(struct ROUTE_BGP* bgp, struct NODEINFO_Q* nq);
|
||||
int route_node_lmdb_del(struct ROUTE_BGP* bgp, uint64_t node_id);
|
||||
|
||||
#endif
|
||||
@@ -381,6 +381,126 @@ void sc_stream_cleanup(struct sc_stream_state *state) {
|
||||
state->initialized = 0;
|
||||
}
|
||||
|
||||
// --- Streaming Ed25519 signature ---
|
||||
// SHA-512 (инкрементально, EVP_DigestUpdate) → 64-байтный хеш → Ed25519 (one-shot EVP_DigestSign).
|
||||
// Ed25519 внутренне SHA-512'ит переданное, получается SHA-512(SHA-512(данные)) — консистентно с обеих сторон.
|
||||
// Память O(1), подходит для потоков произвольного размера.
|
||||
|
||||
static int sc_derive_ed25519_key(const uint8_t *x25519_privkey, EVP_PKEY **pkey_out) {
|
||||
uint8_t hash[SHA512_DIGEST_LENGTH];
|
||||
SHA512(x25519_privkey, SC_PRIVKEY_SIZE, hash);
|
||||
*pkey_out = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, hash, SC_PRIVKEY_SIZE);
|
||||
if (!*pkey_out) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_ed25519_key: EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed25519_pubkey_out) {
|
||||
if (!x25519_privkey || !ed25519_pubkey_out) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_ed25519_pubkey: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
EVP_PKEY *pkey = NULL;
|
||||
if (sc_derive_ed25519_key(x25519_privkey, &pkey) != SC_OK) return SC_ERR_CRYPTO;
|
||||
size_t pub_len = SC_PUBKEY_SIZE;
|
||||
if (EVP_PKEY_get_raw_public_key(pkey, ed25519_pubkey_out, &pub_len) <= 0 || pub_len != SC_PUBKEY_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_ed25519_pubkey: EVP_PKEY_get_raw_public_key failed");
|
||||
EVP_PKEY_free(pkey); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_PKEY_free(pkey);
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_init(sc_context_t *ctx, struct sc_stream_sign_state *state) {
|
||||
if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->initialized || !ctx->pk) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: ctx not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
memset(state, 0, sizeof(*state));
|
||||
if (sc_derive_ed25519_key(ctx->pk->private_key, (EVP_PKEY**)&state->pkey) != SC_OK) return SC_ERR_CRYPTO;
|
||||
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: EVP_MD_CTX_new failed"); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestInit_ex(md_ctx, EVP_sha512(), NULL) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: EVP_DigestInit_ex failed");
|
||||
EVP_MD_CTX_free(md_ctx); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO;
|
||||
}
|
||||
state->md_ctx = md_ctx;
|
||||
state->is_sign = 1;
|
||||
state->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: ok");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_verify_init(struct sc_stream_sign_state *state, const uint8_t *ed25519_pubkey) {
|
||||
if (!state || !ed25519_pubkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
memset(state, 0, sizeof(*state));
|
||||
state->pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
if (!state->pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: EVP_PKEY_new_raw_public_key failed"); return SC_ERR_CRYPTO; }
|
||||
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: EVP_MD_CTX_new failed"); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestInit_ex(md_ctx, EVP_sha512(), NULL) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: EVP_DigestInit_ex failed");
|
||||
EVP_MD_CTX_free(md_ctx); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO;
|
||||
}
|
||||
state->md_ctx = md_ctx;
|
||||
state->is_sign = 0;
|
||||
state->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: ok");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_update(struct sc_stream_sign_state *state, const uint8_t *data, size_t len) {
|
||||
if (len == 0) return SC_OK;
|
||||
if (!state || !data) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_update: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_update: not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (EVP_DigestUpdate((EVP_MD_CTX *)state->md_ctx, data, len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_update: EVP_DigestUpdate failed");
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_final(struct sc_stream_sign_state *state, uint8_t *sig_out, size_t *sig_len) {
|
||||
if (!state || !sig_out || !sig_len) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized || !state->is_sign) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: not initialized for sign"); return SC_ERR_NOT_INITIALIZED; }
|
||||
uint8_t hash[EVP_MAX_MD_SIZE]; unsigned hash_len = sizeof(hash);
|
||||
if (EVP_DigestFinal_ex((EVP_MD_CTX *)state->md_ctx, hash, &hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: EVP_DigestFinal_ex failed");
|
||||
sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_MD_CTX *sctx = EVP_MD_CTX_new();
|
||||
if (!sctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: EVP_MD_CTX_new failed"); sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestSignInit(sctx, NULL, NULL, NULL, (EVP_PKEY*)state->pkey) != 1
|
||||
|| EVP_DigestSign(sctx, sig_out, sig_len, hash, hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: EVP_DigestSign failed");
|
||||
EVP_MD_CTX_free(sctx); sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_MD_CTX_free(sctx);
|
||||
sc_stream_sign_cleanup(state);
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_verify(struct sc_stream_sign_state *state, const uint8_t *sig, size_t sig_len) {
|
||||
if (!state || !sig) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized || state->is_sign) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: not initialized for verify"); return SC_ERR_NOT_INITIALIZED; }
|
||||
uint8_t hash[EVP_MAX_MD_SIZE]; unsigned hash_len = sizeof(hash);
|
||||
if (EVP_DigestFinal_ex((EVP_MD_CTX *)state->md_ctx, hash, &hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: EVP_DigestFinal_ex failed");
|
||||
sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_MD_CTX *vctx = EVP_MD_CTX_new();
|
||||
if (!vctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: EVP_MD_CTX_new failed"); sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestVerifyInit(vctx, NULL, NULL, NULL, (EVP_PKEY*)state->pkey) != 1
|
||||
|| EVP_DigestVerify(vctx, sig, sig_len, hash, hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: EVP_DigestVerify failed");
|
||||
EVP_MD_CTX_free(vctx); sc_stream_sign_cleanup(state); return SC_ERR_AUTH_FAILED;
|
||||
}
|
||||
EVP_MD_CTX_free(vctx);
|
||||
sc_stream_sign_cleanup(state);
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
void sc_stream_sign_cleanup(struct sc_stream_sign_state *state) {
|
||||
if (!state) return;
|
||||
if (state->md_ctx) { EVP_MD_CTX_free((EVP_MD_CTX *)state->md_ctx); state->md_ctx = NULL; }
|
||||
if (state->pkey) { EVP_PKEY_free((EVP_PKEY *)state->pkey); state->pkey = NULL; }
|
||||
state->initialized = 0;
|
||||
}
|
||||
|
||||
// --- Common crypto utilities ---
|
||||
|
||||
sc_status_t sc_sha_transcode(const uint8_t *key, size_t key_len, uint8_t *data, size_t data_len) {
|
||||
|
||||
@@ -84,4 +84,26 @@ sc_status_t sc_stream_init(sc_context_t *ctx, struct sc_stream_state *state, uin
|
||||
sc_status_t sc_stream_xor(struct sc_stream_state *state, uint8_t *data, size_t data_len);
|
||||
void sc_stream_cleanup(struct sc_stream_state *state);
|
||||
|
||||
// --- Streaming Ed25519 signature ---
|
||||
// SHA-512 инкрементально (EVP_DigestUpdate) + Ed25519 подпись хеша (one-shot EVP_DigestSign).
|
||||
// Память O(1), не зависит от размера данных.
|
||||
|
||||
#define SC_SIGN_SIZE 64 // Ed25519 signature size
|
||||
|
||||
struct sc_stream_sign_state {
|
||||
void *md_ctx; // EVP_MD_CTX* — SHA-512 аккумулятор
|
||||
void *pkey; // EVP_PKEY* — Ed25519 ключ
|
||||
uint8_t initialized;
|
||||
uint8_t is_sign; // 1=sign, 0=verify
|
||||
};
|
||||
|
||||
sc_status_t sc_stream_sign_init(sc_context_t *ctx, struct sc_stream_sign_state *state);
|
||||
sc_status_t sc_stream_sign_verify_init(struct sc_stream_sign_state *state, const uint8_t *ed25519_pubkey);
|
||||
sc_status_t sc_stream_sign_update(struct sc_stream_sign_state *state, const uint8_t *data, size_t len);
|
||||
sc_status_t sc_stream_sign_final(struct sc_stream_sign_state *state, uint8_t *sig_out, size_t *sig_len);
|
||||
sc_status_t sc_stream_sign_verify(struct sc_stream_sign_state *state, const uint8_t *sig, size_t sig_len);
|
||||
void sc_stream_sign_cleanup(struct sc_stream_sign_state *state);
|
||||
|
||||
sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed25519_pubkey_out);
|
||||
|
||||
#endif // SECURE_CHANNEL_H
|
||||
Reference in new issue
Block a user