diff --git a/src/dm/dm_core.c b/src/dm/dm_core.c index de8caf4cb..88a76cadd 100644 --- a/src/dm/dm_core.c +++ b/src/dm/dm_core.c @@ -823,13 +823,21 @@ done: /* Открыть существующую беседу либо создать при подтверждённом отсутствии записи. */ 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) { + const char* peer_name, const char* source_ch_id, enum dm_mode mode, uint64_t* result_conv) { if (!inst || !peer_node_id || !peer_x25519 || !peer_ed25519) return -1; if (peer_node_id == inst->node_id) return -1; struct dm_state* dm = dm_of(inst); if (!dm || !dm->initialized) return -1; uint64_t conv_num = dm_derive_conv_id(inst->node_id, peer_node_id); + struct dm_party parties[2] = {{0}}; + if (mode == DM_MODE_GROUPS) { + if (dm_party_resolve(inst, inst->node_id, mode, parties) || dm_party_resolve(inst, peer_node_id, mode, parties + 1)) return -1; + conv_num = dm_identity_conv_id(mode, parties); + if (!conv_num) return -1; + } else if (mode != DM_MODE_DEVICE) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM start: invalid mode=%d", mode); return -1; + } char conv_id[64]; snprintf(conv_id, sizeof(conv_id), "%llu", (unsigned long long)conv_num); @@ -837,6 +845,10 @@ int dm_start(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, enum dm_conv_result loaded = dm_conv_load(dm, conv_id, &c); if (loaded == DM_CONV_ERROR) return -1; if (loaded == DM_CONV_FOUND) { + if (c.mode != mode || (mode == DM_MODE_DEVICE && c.peer_node_id != peer_node_id) || + (mode == DM_MODE_GROUPS && (!dm_party_equal(c.parties, parties) || !dm_party_equal(c.parties + 1, parties + 1)))) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM start: conversation identity collision conv=%s", conv_id); return -1; + } /* Беседа уже есть, но могла быть авто-создана с group_id=0 — дописываем из source_ch_id, * чтобы маршрутизация шла через группу (а не только прямое соединение). */ if (c.group_id == 0 && source_ch_id && source_ch_id[0]) { @@ -853,12 +865,21 @@ int dm_start(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, } else { memset(&c, 0, sizeof(c)); snprintf(c.conv_id, sizeof(c.conv_id), "%s", conv_id); + c.mode = mode; c.peer_node_id = peer_node_id; memcpy(c.peer_x25519, peer_x25519, 32); memcpy(c.peer_ed25519, peer_ed25519, 32); dm_copy_peer_name(c.peer_name, peer_name, peer_name ? strlen(peer_name) : 0, conv_id); c.group_id = source_ch_id ? strtoull(source_ch_id, NULL, 10) : 0; - if (dm_derive_content_key(inst->my_keys.private_key, c.peer_x25519, c.content_key) != 0) return -1; + if (mode == DM_MODE_GROUPS) { + memcpy(c.parties, parties, sizeof(parties)); + memcpy(c.peer_x25519, parties[1].x, 32); memcpy(c.peer_ed25519, parties[1].ed, 32); + uint8_t private_key[32]; + if (dm_party_private(inst, parties, private_key)) return -1; + int error = dm_derive_content_key(private_key, parties[1].x, c.content_key); + OPENSSL_cleanse(private_key, sizeof(private_key)); + if (error) return -1; + } else if (dm_derive_content_key(inst->my_keys.private_key, c.peer_x25519, c.content_key) != 0) return -1; if (dm_conv_save(dm, &c) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: conv_save failed conv=%s", DM_ID, conv_id); @@ -881,6 +902,7 @@ int dm_start(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, memcpy(evt + 1 + cl, &generation, 8); chat_event_post(inst, CHAT_EVT_DM_CONV_UPDATED, evt, 1 + (int)cl + 8); } + if (result_conv) *result_conv = conv_num; return 0; } @@ -1159,6 +1181,7 @@ void dm_start_trampoline(void* arg) { struct dm_start_req* req = (struct dm_start_req*)arg; if (!req) return; struct UTUN_INSTANCE* inst = req->inst; + enum dm_mode mode = req->mode; uint64_t peer = req->peer_node_id; char src[64]; strncpy(src, req->source_ch_id, sizeof(src) - 1); src[sizeof(src) - 1] = '\0'; char name[128]; strncpy(name, req->peer_name, sizeof(name) - 1); name[sizeof(name) - 1] = '\0'; @@ -1177,7 +1200,7 @@ void dm_start_trampoline(void* arg) { DM_ID, (unsigned long long)peer); return; } - if (dm_start(inst, peer, x25519, ed25519, name[0] ? name : NULL, src[0] ? src : NULL) != 0) + if (dm_start(inst, peer, x25519, ed25519, name[0] ? name : NULL, src[0] ? src : NULL, mode, NULL) != 0) DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: dm_start_trampoline — dm_start failed peer=0x%016llx", DM_ID, (unsigned long long)peer); } @@ -1293,7 +1316,7 @@ void dm_invite_trampoline(void* arg) { if (loaded == DM_CONV_NOT_FOUND) { uint8_t x25519[32], ed25519[32]; if (dm_node_pubkeys(dm, req->peer_node_id, x25519, ed25519) != 0 || - dm_start(req->inst, req->peer_node_id, x25519, ed25519, req->peer_name, req->source_ch_id) != 0) { + dm_start(req->inst, req->peer_node_id, x25519, ed25519, req->peer_name, req->source_ch_id, DM_MODE_DEVICE, NULL) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "invite PM: conversation failed peer=%llu", (unsigned long long)req->peer_node_id); dm_invite_done(req, DM_INVITE_START_ERROR); return; } diff --git a/src/dm/dm_core.h b/src/dm/dm_core.h index 0bf51ee9f..d45ca35ce 100644 --- a/src/dm/dm_core.h +++ b/src/dm/dm_core.h @@ -92,7 +92,7 @@ void dm_core_destroy(struct UTUN_INSTANCE* inst); * Создание разрешено только после успешного SELECT без строки; существующие seq сохраняются. */ 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); + const char* peer_name, const char* source_ch_id, enum dm_mode mode, uint64_t* result_conv); /* Отправить сообщение в беседу. Шифрует, подписывает, сохраняет локально и * доставляет (прямое соединение при доступности пира, иначе mailbox). @@ -143,6 +143,7 @@ struct dm_start_req { uint64_t peer_node_id; /* Другой участник общей группы; ключи читаются из nodes. */ char source_ch_id[64]; /* Десятичный ID общей группы либо пустая строка. */ char peer_name[128]; /* UTF-8 имя с NUL; dm_start ограничит до 63 байт. */ + enum dm_mode mode; /* Один переключатель DEVICE/GROUPS для обеих сторон. */ }; void dm_start_trampoline(void* arg);