You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

178 lines
9.1 KiB

// route_crypto.c — пред/пост-обработка SVC_ROUTE пакетов (шифрование + подпись)
#include "route_crypto.h"
#include "utun_instance.h"
#include "topo_node.h"
#include "../transport_layer/secure_channel.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include <string.h>
#define MF_CRYPTO_DEBUG(svc, fmt, ...) do { \
if ((svc) == ETCP_RT_ID_MEMBER_FILES) DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files crypto: " fmt, ##__VA_ARGS__); \
} while (0)
// ECDH-контекст для пира (кэш в inst->e2e_ctx_cache). NULL = нет NODEINFO / ECDH не удался.
static struct secure_channel* route_crypto_e2e_ctx(struct UTUN_INSTANCE* inst, uint64_t peer_node_id) {
for (int i = 0; i < E2E_CTX_CACHE_SIZE; i++) {
if (inst->e2e_ctx_cache[i].valid && inst->e2e_ctx_cache[i].peer_node_id == peer_node_id)
return &inst->e2e_ctx_cache[i].ctx;
}
struct TOPO_NODE* tn = topo_node_registry_find(inst->topo_groups, peer_node_id);
if (!tn) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto: no NODEINFO for peer=%016llx", (unsigned long long)peer_node_id);
return NULL;
}
int slot = -1;
for (int i = 0; i < E2E_CTX_CACHE_SIZE; i++) {
if (!inst->e2e_ctx_cache[i].valid) { slot = i; break; }
}
if (slot < 0) slot = 0;
sc_init_ctx(&inst->e2e_ctx_cache[slot].ctx, &inst->my_keys);
if (sc_set_peer_public_key(&inst->e2e_ctx_cache[slot].ctx, tn->public_key, SC_PEER_PUBKEY_BIN) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto: ECDH failed peer=%016llx", (unsigned long long)peer_node_id);
inst->e2e_ctx_cache[slot].valid = 0;
return NULL;
}
inst->e2e_ctx_cache[slot].peer_node_id = peer_node_id;
inst->e2e_ctx_cache[slot].valid = 1;
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "route_crypto: new context for peer=%016llx slot=%d",
(unsigned long long)peer_node_id, slot);
return &inst->e2e_ctx_cache[slot].ctx;
}
int route_crypto_encode(struct UTUN_INSTANCE* inst, uint64_t peer_node_id,
const uint8_t* base, size_t base_len, int mode,
uint8_t** out, size_t* out_len) {
if (!inst || !base || base_len < SVC_ROUTE_HDR_SIZE || !out || !out_len) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_encode: invalid args inst=%p base=%p len=%zu mode=%d",
(void*)inst, (const void*)base, base_len, mode);
return -1;
}
const uint8_t* pl = base + SVC_ROUTE_HDR_SIZE;
size_t pl_len = base_len - SVC_ROUTE_HDR_SIZE;
const struct SVC_ROUTE_HDR* input = (const struct SVC_ROUTE_HDR*)base;
MF_CRYPTO_DEBUG(input->svc_id, "encode peer=%llx seq=%u mode=%02x plain=%zu",
(unsigned long long)peer_node_id, input->seq, mode, pl_len);
const uint8_t* body = pl;
size_t body_len = pl_len;
uint8_t* enc_buf = NULL;
if (mode & ROUTE_CRYPTO_ENCRYPT) {
struct secure_channel* e2e = route_crypto_e2e_ctx(inst, peer_node_id);
if (!e2e) { MF_CRYPTO_DEBUG(input->svc_id, "encode rejected: no ECDH context"); return -1; }
size_t enc_cap = pl_len + SC_NONCE_SIZE + SC_CRC32_SIZE + SC_TAG_SIZE;
enc_buf = u_malloc(enc_cap);
if (!enc_buf) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_encode: enc_buf alloc fail"); return -1; }
size_t enc_len = 0;
if (sc_encrypt(e2e, pl, pl_len, enc_buf, &enc_len) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_encode: sc_encrypt FAIL peer=%016llx len=%zu",
(unsigned long long)peer_node_id, pl_len);
u_free(enc_buf); return -1;
}
body = enc_buf; body_len = enc_len;
}
size_t sig_len = (mode & ROUTE_CRYPTO_SIGN) ? SC_SIGN_SIZE : 0;
size_t total = SVC_ROUTE_HDR_SIZE + body_len + sig_len;
uint8_t* dgram = u_malloc(total);
if (!dgram) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_encode: out alloc fail"); if (enc_buf) u_free(enc_buf); return -1; }
memcpy(dgram, base, SVC_ROUTE_HDR_SIZE);
memcpy(dgram + SVC_ROUTE_HDR_SIZE, body, body_len);
struct SVC_ROUTE_HDR* hdr = (struct SVC_ROUTE_HDR*)dgram;
if (mode & ROUTE_CRYPTO_ENCRYPT) hdr->flags |= ROUTER_FLAG_ENCRYPTED;
if (mode & ROUTE_CRYPTO_SIGN) {
hdr->flags |= ROUTER_FLAG_SIGNED;
if (sc_ed25519_sign(inst->my_ed25519_privkey, dgram, SVC_ROUTE_HDR_SIZE + body_len,
dgram + SVC_ROUTE_HDR_SIZE + body_len) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_encode: sign FAIL peer=%016llx", (unsigned long long)peer_node_id);
u_free(dgram); if (enc_buf) u_free(enc_buf); return -1;
}
}
if (enc_buf) u_free(enc_buf);
*out = dgram; *out_len = total;
MF_CRYPTO_DEBUG(input->svc_id, "encode done seq=%u wire=%zu", input->seq, total);
return 0;
}
int route_crypto_decode(struct UTUN_INSTANCE* inst,
const uint8_t* wire, size_t wire_len,
uint8_t** out, size_t* out_len, uint8_t* rx_flags) {
if (!inst || !wire || wire_len < SVC_ROUTE_HDR_SIZE || !out || !out_len || !rx_flags) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_decode: invalid args inst=%p wire=%p len=%zu",
(void*)inst, (const void*)wire, wire_len);
return -1;
}
struct SVC_ROUTE_HDR* hdr = (struct SVC_ROUTE_HDR*)wire;
uint8_t fl = hdr->flags & (ROUTER_FLAG_ENCRYPTED | ROUTER_FLAG_SIGNED);
MF_CRYPTO_DEBUG(hdr->svc_id, "decode src=%llx seq=%u flags=%02x wire=%zu",
(unsigned long long)hdr->src_node_id, hdr->seq, fl, wire_len);
*rx_flags = fl;
const uint8_t* body = wire + SVC_ROUTE_HDR_SIZE;
size_t body_len = wire_len - SVC_ROUTE_HDR_SIZE;
if (fl & ROUTER_FLAG_SIGNED) {
if (body_len < SC_SIGN_SIZE) {
MF_CRYPTO_DEBUG(hdr->svc_id, "decode rejected: signature truncated");
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_decode: SIGNED packet too short len=%zu from %016llx",
wire_len, (unsigned long long)hdr->src_node_id);
return -1;
}
size_t signed_len = SVC_ROUTE_HDR_SIZE + body_len - SC_SIGN_SIZE;
const uint8_t* sig = wire + signed_len;
struct TOPO_NODE* sni = topo_node_registry_find(inst->topo_groups, hdr->src_node_id);
if (!sni) {
MF_CRYPTO_DEBUG(hdr->svc_id, "decode rejected: source NODEINFO missing");
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_decode: SIGNED from unknown node %016llx — dropping",
(unsigned long long)hdr->src_node_id);
return -1;
}
uint8_t zero[SC_PUBKEY_SIZE] = {0};
if (memcmp(sni->ed25519_public_key, zero, SC_PUBKEY_SIZE) == 0) {
MF_CRYPTO_DEBUG(hdr->svc_id, "decode rejected: source Ed25519 key missing");
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_decode: no Ed25519 pubkey for node %016llx — dropping",
(unsigned long long)hdr->src_node_id);
return -1;
}
if (sc_ed25519_verify(sni->ed25519_public_key, wire, signed_len, sig) != SC_OK) {
MF_CRYPTO_DEBUG(hdr->svc_id, "decode rejected: Ed25519 signature invalid");
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_decode: signature FAILED for node %016llx svc_id=%u — dropping",
(unsigned long long)hdr->src_node_id, hdr->svc_id);
return -1;
}
body_len -= SC_SIGN_SIZE;
MF_CRYPTO_DEBUG(hdr->svc_id, "signature verified src=%llx", (unsigned long long)hdr->src_node_id);
}
uint8_t* pt_buf = NULL;
if (fl & ROUTER_FLAG_ENCRYPTED) {
struct secure_channel* e2e = route_crypto_e2e_ctx(inst, hdr->src_node_id);
if (!e2e) { MF_CRYPTO_DEBUG(hdr->svc_id, "decode rejected: no ECDH context"); return -1; }
pt_buf = u_malloc(body_len);
size_t pt_len = 0;
if (!pt_buf || sc_decrypt(e2e, body, body_len, pt_buf, &pt_len) != SC_OK) {
MF_CRYPTO_DEBUG(hdr->svc_id, "decode rejected: AES-CCM decrypt/authentication failed bytes=%zu", body_len);
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_decode: decrypt FAIL src=%016llx svc=%u len=%zu",
(unsigned long long)hdr->src_node_id, hdr->svc_id, body_len);
if (pt_buf) u_free(pt_buf);
return -1;
}
body = pt_buf; body_len = pt_len;
}
size_t total = SVC_ROUTE_HDR_SIZE + body_len;
uint8_t* dgram = u_malloc(total);
if (!dgram) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "route_crypto_decode: out alloc fail"); if (pt_buf) u_free(pt_buf); return -1; }
memcpy(dgram, wire, SVC_ROUTE_HDR_SIZE);
memcpy(dgram + SVC_ROUTE_HDR_SIZE, body, body_len);
((struct SVC_ROUTE_HDR*)dgram)->flags &= ~(ROUTER_FLAG_ENCRYPTED | ROUTER_FLAG_SIGNED);
if (pt_buf) u_free(pt_buf);
*out = dgram; *out_len = total;
MF_CRYPTO_DEBUG(hdr->svc_id, "decode done src=%llx seq=%u verified_flags=%02x plain=%zu",
(unsigned long long)hdr->src_node_id, hdr->seq, fl, body_len);
return 0;
}