Защищенная синхронизация ключей и мета-данных групп

This commit is contained in:
RoyceDa
2026-02-24 14:33:47 +02:00
parent 8952fe43e8
commit bf057c14f4
4 changed files with 77 additions and 24 deletions

View File

@@ -162,26 +162,10 @@ export function useGroups() : {
const groupId = packet.getGroupId();
info(`Creating group with id ${groupId}`);
const encryptKey = generateRandomKey(64);
const secureKey = await encodeWithPassword(privatePlain, encryptKey);
let content = await encodeWithPassword(encryptKey, `$a=Group created`);
let plainMessage = await encodeWithPassword(privatePlain, `$a=Group created`);
await runQuery(`
INSERT INTO groups (account, group_id, title, description, key) VALUES (?, ?, ?, ?, ?)
`, [publicKey, groupId, title, description, secureKey]);
await runQuery(`
INSERT INTO messages
(from_public_key, to_public_key, content, timestamp, read, chacha_key, from_me, plain_message, account, message_id, delivered, attachments) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [publicKey, "#group:" + groupId, content, Date.now(), 1, "", 1, plainMessage, publicKey, generateRandomKey(16),
DeliveredMessageState.DELIVERED
, '[]']);
updateDialog("#group:" + groupId);
updateGroupInformation({
groupId: groupId,
title: title,
description: description
});
setLoading(false);
navigate(`/main/chat/${prepareForRoute(groupId)}`);
/**
* После создания группы в нее необходимо зайти, в соотвествии с новым протоколом
*/
joinGroup(await constructGroupString(groupId, title, encryptKey, description));
});
}
@@ -201,9 +185,11 @@ export function useGroups() : {
const groupId = parsed.groupId;
const title = parsed.title;
const description = parsed.description;
const encodedGroupString = await encodeWithPassword(privatePlain, groupString);
const packet = new PacketGroupJoin();
packet.setGroupId(parsed.groupId);
packet.setGroupString(encodedGroupString);
send(packet);
setLoading(true);

View File

@@ -8,6 +8,16 @@ import { useSender } from "../ProtocolProvider/useSender";
import { usePacket } from "../ProtocolProvider/usePacket";
import { whenFinish } from "./dialogQueue";
import { useProtocol } from "../ProtocolProvider/useProtocol";
import { PacketGroupJoin } from "../ProtocolProvider/protocol/packets/packet.group.join";
import { useGroups } from "./useGroups";
import { decodeWithPassword, encodeWithPassword } from "@/app/workers/crypto/crypto";
import { usePrivatePlain } from "../AccountProvider/usePrivatePlain";
import { GroupStatus } from "../ProtocolProvider/protocol/packets/packet.group.invite.info";
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
import { useNavigate } from "react-router-dom";
import { useDialogsList } from "../DialogListProvider/useDialogsList";
import { useUpdateGroupInformation } from "../InformationProvider/useUpdateGroupInformation";
import { useGroupInviteStatus } from "./useGroupInviteStatus";
/**
* Хук отвечает за синхронизацию сообщений, запрос синхронизации
@@ -19,7 +29,14 @@ export function useSynchronize() {
const publicKey = usePublicKey();
const send = useSender();
const {protocol} = useProtocol();
const {parseGroupString} = useGroups();
const privatePlain = usePrivatePlain();
const {error, info} = useConsoleLogger('useSynchronize');
const {setInviteStatusByGroupId} = useGroupInviteStatus('');
const updateGroupInformation = useUpdateGroupInformation();
const {updateDialog} = useDialogsList();
useEffect(() => {
if(protocol.handshakeExchangeComplete){
trySync();
@@ -38,15 +55,47 @@ export function useSynchronize() {
send(packet);
}
/**
* Пакет приходит либо при входе в группу (но там используется слушатель once), либо при
* синхронизации. В данном случае этот пакет прийдет только при синхронизации
*/
usePacket(20, async (packet: PacketGroupJoin) => {
const decryptedGroupString = await decodeWithPassword(privatePlain, packet.getGroupString());
const parsed = await parseGroupString(decryptedGroupString);
if(!parsed){
error("Received invalid group string, skipping");
return;
}
const groupStatus = packet.getGroupStatus();
if(groupStatus != GroupStatus.JOINED){
error("Cannot sync group that is not joined, skipping");
return;
}
const secureKey = await encodeWithPassword(privatePlain, parsed.encryptKey);
await runQuery(`
INSERT INTO groups (account, group_id, title, description, key) VALUES (?, ?, ?, ?, ?)
`, [publicKey, parsed.groupId, parsed.title, parsed.description, secureKey]);
updateDialog("#group:" + parsed.groupId);
setInviteStatusByGroupId(parsed.groupId, GroupStatus.JOINED);
updateGroupInformation({
groupId: parsed.groupId,
title: parsed.title,
description: parsed.description
});
info("Group synchronized " + parsed.groupId);
}, [publicKey]);
usePacket(25, async (packet: PacketSync) => {
const status = packet.getStatus();
if(status == SyncStatus.BATCH_START){
setProtocolState(ProtocolState.SYNCHRONIZATION);
}
if(status == SyncStatus.BATCH_END){
console.info("Batch start");
/**
* Этот Promise ждет пока все сообщения синхронизируются и обработаются, только
* после этого
*/
await whenFinish();
console.info("Batch finished");
await runQuery(
"INSERT INTO accounts_sync_times (account, last_sync) VALUES (?, ?) " +
"ON CONFLICT(account) DO UPDATE SET last_sync = ? WHERE account = ?",