e2e: AES-128-CCM encryption via BGP router (ROUTER_FLAG_ENCRYPTED), ECDH key per peer-pair from NODEINFO pubkeys, cache 8 entries
- etcp_router.h/c: new flag ROUTER_FLAG_ENCRYPTED (0x04), etcp_route_send_encrypted(), inflight/retrans/send_q support - utun_instance.h: e2e_ctx_cache[8] — per-peer sc_context_t cache with derived session key - topo_node.c: topo_node_sign_self() — single point for Ed25519 self-signing of NODEINFO - topo_node.c: fix stale x25519_self_sig after address update (topo_node_sign_self call) - nat_detection.c: fix stale x25519_self_sig after in-place socket meta modification - test_etcp_router_unit: 5 E2E tests (basic round-trip, no NODEINFO drop, tampered, cache reuse, ENCRYPTED+SIGNED combined) - builds clean, 70/70 tests pass
This commit is contained in:
1 parent
ca176e1f24
commit
b2e4aadbf0
44 files changed
+2586
-145
No files matched your search
@@ -5,6 +5,7 @@
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/memory_pool.h"
|
||||
#include "../lib/json_flat.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -40,6 +41,7 @@ int topo_node_sqlite_init(sqlite3* db) {
|
||||
" ed25519_pubkey BLOB,"
|
||||
" last_seen_at INTEGER,"
|
||||
" online INTEGER DEFAULT 0,"
|
||||
" client_type INTEGER NOT NULL DEFAULT 0,"
|
||||
" update_ts INTEGER DEFAULT 0,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
@@ -102,6 +104,8 @@ int topo_node_sqlite_init(sqlite3* db) {
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_exec(db, "ALTER TABLE nodes ADD COLUMN client_type INTEGER NOT NULL DEFAULT 0", NULL, NULL, NULL);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "topo_node_sqlite tables initialized");
|
||||
return 0;
|
||||
}
|
||||
@@ -116,18 +120,20 @@ int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_GROUPS* groups, struct TO
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT INTO nodes(node_id, name, x25519_pubkey, ed25519_pubkey, last_seen_at, online)"
|
||||
" VALUES(?,?,?,?,?,1)"
|
||||
"INSERT INTO nodes(node_id, name, x25519_pubkey, ed25519_pubkey, client_type, last_seen_at, online)"
|
||||
" VALUES(?,?,?,?,?,?,1)"
|
||||
" ON CONFLICT(node_id) DO UPDATE SET"
|
||||
" name=CASE WHEN excluded.name != '' THEN excluded.name ELSE nodes.name END, x25519_pubkey=excluded.x25519_pubkey,"
|
||||
" ed25519_pubkey=excluded.ed25519_pubkey, last_seen_at=excluded.last_seen_at,\n"
|
||||
" ed25519_pubkey=excluded.ed25519_pubkey, client_type=CASE WHEN excluded.client_type!=0 THEN excluded.client_type ELSE nodes.client_type END,"
|
||||
" last_seen_at=excluded.last_seen_at,\n"
|
||||
" created_at=COALESCE(nodes.created_at, excluded.created_at)",
|
||||
-1, &stmt, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)ni->node_id);
|
||||
sqlite3_bind_text(stmt, 2, ni->node_name ? ni->node_name : "", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 3, ni->public_key, 32, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 4, ni->ed25519_public_key, 32, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 5, (sqlite3_int64)now_sec);
|
||||
sqlite3_bind_int(stmt, 5, (int)ni->client_type);
|
||||
sqlite3_bind_int64(stmt, 6, (sqlite3_int64)now_sec);
|
||||
sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
@@ -160,12 +166,12 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
" update_ts INTEGER DEFAULT 0," /* NTP timestamp подписи update_sig */
|
||||
" userinfo TEXT NOT NULL DEFAULT ''," /* JSON с информацией о пользователе: {"name":"<display_name>"} */
|
||||
" creator_sig BLOB," /* Ed25519 подпись создателя (PEERS_JOIN_SIG_SIZE=64), пока заглушка */
|
||||
" adm_tags TEXT," /* теги администратора, key=value через пробел/запятую.
|
||||
сейчас: "supernode=yes" → node_type=4 */
|
||||
" adm_tags_sig BLOB," /* Ed25519 подпись над adm_tags (64 байта) — верификация пока не реализована */
|
||||
" adm_tags TEXT," /* JSON-теги администратора: {"key":"val",...,"ver":"N"}.
|
||||
подпись: sc_ed25519_sign(ch_ed_priv, json||node_id_8LE) */
|
||||
" adm_tags_sig BLOB," /* Ed25519 подпись над adm_tags (64 байта) канальным ключом */
|
||||
" node_type INTEGER NOT NULL DEFAULT 0," /* вычисляемый тип узла (topo_node_sqlite_nodeinfo_updated):
|
||||
0=неизвестно 1=прямой(addr_type NETIF/DIRECT/NAT_STRICT)
|
||||
2=EIM NAT 3=(не исп.) 4=суперузел(DIRECT+"supernode=yes") */
|
||||
0=неизвестно 1=прямой(addr_type NETIF/DIRECT/NAT_STRICT)
|
||||
2=EIM NAT 3=(не исп.) 4=суперузел(DIRECT+adm_tags.supernode=yes) */
|
||||
" node_RTT INTEGER," /* RTT до узла в мс, часть индекса (node_type, node_RTT) */
|
||||
" connected INTEGER NOT NULL DEFAULT 0," /* 1 = было активное подключение к узлу */
|
||||
" storage INTEGER NOT NULL DEFAULT 0," /* 1 = приоритетный хранитель аттачей */
|
||||
@@ -347,6 +353,21 @@ int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_get_priv(sqlite3* db, const char* channel_id,
|
||||
uint8_t* ed25519_priv_out) {
|
||||
if (!db || !channel_id || !ed25519_priv_out) return -1;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT ed25519_privkey FROM channels WHERE channel_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_finalize(stmt); return -1; }
|
||||
const uint8_t* blob = (const uint8_t*)sqlite3_column_blob(stmt, 0);
|
||||
if (!blob || sqlite3_column_bytes(stmt, 0) < 32) { sqlite3_finalize(stmt); return -1; }
|
||||
memcpy(ed25519_priv_out, blob, 32);
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id,
|
||||
uint8_t* buf, size_t buf_sz, size_t* out_len) {
|
||||
if (!db || !channel_id || !buf || !out_len) return -1;
|
||||
@@ -528,8 +549,9 @@ int topo_node_sqlite_nodeinfo_updated(sqlite3* db, uint64_t node_id) {
|
||||
if (sqlite3_step(a) == SQLITE_ROW) {
|
||||
const char* tags = (const char*)sqlite3_column_text(a, 0);
|
||||
if (tags) {
|
||||
if (strstr(tags, "supernode=yes")) node_type = 4;
|
||||
if (strstr(tags, "storage=yes")) is_storage = 1;
|
||||
char buf[64];
|
||||
if (json_flat_get(tags, "supernode", buf, sizeof(buf)) == 0 && strcmp(buf, "yes") == 0) node_type = 4;
|
||||
if (json_flat_get(tags, "storage", buf, sizeof(buf)) == 0 && strcmp(buf, "yes") == 0) is_storage = 1;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(a);
|
||||
@@ -598,10 +620,11 @@ struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* gr
|
||||
|
||||
uint8_t pubkey[32] = {0}, ed_pubkey[32] = {0};
|
||||
char* name = NULL;
|
||||
uint8_t client_type = 0;
|
||||
int found = 0;
|
||||
{
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db, "SELECT name,x25519_pubkey,ed25519_pubkey FROM nodes WHERE node_id=?",
|
||||
if (sqlite3_prepare_v2(db, "SELECT name,x25519_pubkey,ed25519_pubkey,client_type FROM nodes WHERE node_id=?",
|
||||
-1, &s, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(s, 1, (sqlite3_int64)node_id);
|
||||
if (sqlite3_step(s) == SQLITE_ROW) {
|
||||
@@ -611,6 +634,7 @@ struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* gr
|
||||
if (pk && sqlite3_column_bytes(s, 1) >= 32) memcpy(pubkey, pk, 32);
|
||||
const void* ep = sqlite3_column_blob(s, 2);
|
||||
if (ep && sqlite3_column_bytes(s, 2) >= 32) memcpy(ed_pubkey, ep, 32);
|
||||
client_type = (uint8_t)sqlite3_column_int(s, 3);
|
||||
found = 1;
|
||||
}
|
||||
sqlite3_finalize(s);
|
||||
@@ -681,6 +705,7 @@ struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* gr
|
||||
struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
|
||||
if (!ni) { u_free(name); return NULL; }
|
||||
ni->node_id = node_id; ni->ver = 1; ni->group_ref_count = 0;
|
||||
ni->client_type = client_type;
|
||||
memcpy(ni->public_key, pubkey, 32); memcpy(ni->ed25519_public_key, ed_pubkey, 32);
|
||||
ni->node_name = name;
|
||||
ni->v4_addrs = v4_head;
|
||||
|
||||
Reference in new issue
Block a user