From 7e508f720da8d14e42f66229460bddccede466f6 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 3 Nov 2026 12:32:53 +0100 Subject: [PATCH] Accept subgroup messages and distinguish own device replicas --- src/dm/dm_core.c | 42 ++++++++++++++++++++++++++++++++---------- src/dm/dm_history.c | 2 +- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/dm/dm_core.c b/src/dm/dm_core.c index 550d4f5e8..de8caf4cb 100644 --- a/src/dm/dm_core.c +++ b/src/dm/dm_core.c @@ -542,7 +542,11 @@ int dm_accept_message(struct UTUN_INSTANCE* inst, const uint8_t* msg, size_t len return -1; } if (dm_verify_message(inst, inst->node_id, msg, len) != 0) return -1; - int deleted = dm_history_deleted(inst, msg, len, 0, NULL); + struct dm_message_view message; + if (dm_message_parse(msg, len, &message)) return -1; + int side = message.mode == DM_MODE_GROUPS ? dm_message_target(&message, inst->node_id, NULL, NULL) : 1; + int dir = side == 0; + int deleted = dm_history_deleted(inst, msg, len, dir, NULL); if (deleted < 0) return -1; uint64_t conv, seq, author; memcpy(&conv, msg, 8); @@ -558,38 +562,56 @@ int dm_accept_message(struct UTUN_INSTANCE* inst, const uint8_t* msg, size_t len if (created) { memset(&c, 0, sizeof(c)); snprintf(c.conv_id, sizeof(c.conv_id), "%s", conv_id); + c.mode = message.mode; c.peer_node_id = author; if (!dm_shared_group_id(dm, author, &c.group_id)) { DEBUG_WARN(DEBUG_CATEGORY_DM, "%s: unknown correspondent peer=%llu", DM_ID, (unsigned long long)author); return -1; } - if (dm_node_pubkeys(dm, author, c.peer_x25519, c.peer_ed25519) != 0 || + if (c.mode == DM_MODE_GROUPS) { + c.parties[0] = message.parties[side]; c.parties[1] = message.parties[1 - side]; + if (!side) { + c.peer_node_id = 0; + for (unsigned i = 0; i < message.target_count; i++) { + struct dm_target target; + if (dm_message_target_at(&message, i, &target)) return -1; + if (target.side == 1) { c.peer_node_id = target.node; break; } + } + } + memcpy(c.peer_x25519, c.parties[1].x, 32); memcpy(c.peer_ed25519, c.parties[1].ed, 32); + uint8_t private_key[32]; + if (dm_party_private(inst, c.parties, private_key)) return -1; + int error = dm_derive_content_key(private_key, c.parties[1].x, c.content_key); + OPENSSL_cleanse(private_key, sizeof(private_key)); + if (error) return -1; + } else if (dm_node_pubkeys(dm, author, c.peer_x25519, c.peer_ed25519) != 0 || dm_derive_content_key(inst->my_keys.private_key, c.peer_x25519, c.content_key) != 0) return -1; char name[128] = {0}; - chat_core_get_node_name(inst, author, name, sizeof(name)); + chat_core_get_node_name(inst, c.peer_node_id, name, sizeof(name)); dm_copy_peer_name(c.peer_name, name, strlen(name), conv_id); } - if (c.peer_node_id != author) { + if (c.mode != message.mode || (c.mode == DM_MODE_DEVICE && c.peer_node_id != author) || + (c.mode == DM_MODE_GROUPS && (!dm_party_equal(c.parties, message.parties + side) || + !dm_party_equal(c.parties + 1, message.parties + 1 - side)))) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: conversation author mismatch conv=%s", DM_ID, conv_id); return -1; } - uint16_t enc_len; - memcpy(&enc_len, msg + 33 + msg[32], 2); + if (!dm_conv_active(dm, &c)) return -1; uint8_t nonce[DM_NONCE_SIZE], plain[DM_DATA_MAX + 1]; size_t plain_len = 0; dm_build_nonce(conv, author, seq, nonce); - if (media_ccm_decrypt(c.content_key, nonce, msg + 35 + msg[32], enc_len, plain, &plain_len) != 0) return -1; + if (media_ccm_decrypt(c.content_key, nonce, message.ciphertext, message.ciphertext_size, plain, &plain_len) != 0) return -1; if (dm_message_media(msg, len)) { struct attachment_info info; if (attachment_decode(plain, plain_len, &info)) return -1; } if (dm_exec(dm, "BEGIN IMMEDIATE") != 0) return -1; - int inserted = dm_store_message(dm, conv_id, 0, msg, len); - if (seq > c.last_in_seq) c.last_in_seq = seq; /* Только статистика; не курсор dedup. */ + int inserted = dm_store_message(dm, conv_id, dir, msg, len); + if (!dir && seq > c.last_in_seq) c.last_in_seq = seq; /* Только статистика; не курсор dedup. */ uint64_t ts; memcpy(&ts, msg + 16, 8); if (ts > c.last_ts) c.last_ts = ts; - if (inserted < 0 || dm_conv_save(dm, &c) != 0 || dm_media_track(inst, 0, msg, len) != 0 || + if (inserted < 0 || dm_conv_save(dm, &c) != 0 || dm_media_track(inst, dir, msg, len) != 0 || (inserted && dm_history_show(inst, conv_id)) || dm_exec(dm, "COMMIT") != 0) { dm_exec(dm, "ROLLBACK"); return -1; diff --git a/src/dm/dm_history.c b/src/dm/dm_history.c index 2b7b38234..906592396 100644 --- a/src/dm/dm_history.c +++ b/src/dm/dm_history.c @@ -19,7 +19,7 @@ int dm_history_init(struct UTUN_INSTANCE* inst) { const char* sql = "CREATE TABLE IF NOT EXISTS dm_history(conv_id TEXT PRIMARY KEY,generation INTEGER NOT NULL,hidden INTEGER NOT NULL);" "CREATE TABLE IF NOT EXISTS dm_deleted_messages(conv_id TEXT NOT NULL,dir INTEGER NOT NULL,seq INTEGER NOT NULL," - "body_hash BLOB NOT NULL,media_id BLOB,media_receipt BLOB,PRIMARY KEY(conv_id,dir,seq));" + "author INTEGER NOT NULL,body_hash BLOB NOT NULL,media_id BLOB,media_receipt BLOB,PRIMARY KEY(conv_id,author,seq));" "CREATE TABLE IF NOT EXISTS dm_local_files(path TEXT PRIMARY KEY,conv_id TEXT NOT NULL,generation INTEGER NOT NULL," "busy INTEGER NOT NULL DEFAULT 0,retry_at INTEGER NOT NULL DEFAULT 0);" "UPDATE dm_local_files SET busy=0;";