Защищенная синхронизация ключей и мета-данных групп
This commit is contained in:
@@ -162,26 +162,10 @@ export function useGroups() : {
|
|||||||
const groupId = packet.getGroupId();
|
const groupId = packet.getGroupId();
|
||||||
info(`Creating group with id ${groupId}`);
|
info(`Creating group with id ${groupId}`);
|
||||||
const encryptKey = generateRandomKey(64);
|
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(`
|
joinGroup(await constructGroupString(groupId, title, encryptKey, description));
|
||||||
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)}`);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,9 +185,11 @@ export function useGroups() : {
|
|||||||
const groupId = parsed.groupId;
|
const groupId = parsed.groupId;
|
||||||
const title = parsed.title;
|
const title = parsed.title;
|
||||||
const description = parsed.description;
|
const description = parsed.description;
|
||||||
|
const encodedGroupString = await encodeWithPassword(privatePlain, groupString);
|
||||||
|
|
||||||
const packet = new PacketGroupJoin();
|
const packet = new PacketGroupJoin();
|
||||||
packet.setGroupId(parsed.groupId);
|
packet.setGroupId(parsed.groupId);
|
||||||
|
packet.setGroupString(encodedGroupString);
|
||||||
send(packet);
|
send(packet);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,16 @@ import { useSender } from "../ProtocolProvider/useSender";
|
|||||||
import { usePacket } from "../ProtocolProvider/usePacket";
|
import { usePacket } from "../ProtocolProvider/usePacket";
|
||||||
import { whenFinish } from "./dialogQueue";
|
import { whenFinish } from "./dialogQueue";
|
||||||
import { useProtocol } from "../ProtocolProvider/useProtocol";
|
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,6 +29,13 @@ export function useSynchronize() {
|
|||||||
const publicKey = usePublicKey();
|
const publicKey = usePublicKey();
|
||||||
const send = useSender();
|
const send = useSender();
|
||||||
const {protocol} = useProtocol();
|
const {protocol} = useProtocol();
|
||||||
|
const {parseGroupString} = useGroups();
|
||||||
|
const privatePlain = usePrivatePlain();
|
||||||
|
const {error, info} = useConsoleLogger('useSynchronize');
|
||||||
|
const {setInviteStatusByGroupId} = useGroupInviteStatus('');
|
||||||
|
const updateGroupInformation = useUpdateGroupInformation();
|
||||||
|
const {updateDialog} = useDialogsList();
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(protocol.handshakeExchangeComplete){
|
if(protocol.handshakeExchangeComplete){
|
||||||
@@ -38,15 +55,47 @@ export function useSynchronize() {
|
|||||||
send(packet);
|
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) => {
|
usePacket(25, async (packet: PacketSync) => {
|
||||||
const status = packet.getStatus();
|
const status = packet.getStatus();
|
||||||
if(status == SyncStatus.BATCH_START){
|
if(status == SyncStatus.BATCH_START){
|
||||||
setProtocolState(ProtocolState.SYNCHRONIZATION);
|
setProtocolState(ProtocolState.SYNCHRONIZATION);
|
||||||
}
|
}
|
||||||
if(status == SyncStatus.BATCH_END){
|
if(status == SyncStatus.BATCH_END){
|
||||||
console.info("Batch start");
|
/**
|
||||||
|
* Этот Promise ждет пока все сообщения синхронизируются и обработаются, только
|
||||||
|
* после этого
|
||||||
|
*/
|
||||||
await whenFinish();
|
await whenFinish();
|
||||||
console.info("Batch finished");
|
|
||||||
await runQuery(
|
await runQuery(
|
||||||
"INSERT INTO accounts_sync_times (account, last_sync) VALUES (?, ?) " +
|
"INSERT INTO accounts_sync_times (account, last_sync) VALUES (?, ?) " +
|
||||||
"ON CONFLICT(account) DO UPDATE SET last_sync = ? WHERE account = ?",
|
"ON CONFLICT(account) DO UPDATE SET last_sync = ? WHERE account = ?",
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ export class PacketGroupJoin extends Packet {
|
|||||||
|
|
||||||
private groupId: string = "";
|
private groupId: string = "";
|
||||||
private groupStatus: GroupStatus = GroupStatus.NOT_JOINED;
|
private groupStatus: GroupStatus = GroupStatus.NOT_JOINED;
|
||||||
|
/**
|
||||||
|
* Строка группы, которая содержит информацию о группе, такую как ее название, описание и ключ
|
||||||
|
* Строка зашифрована обратимым шифрованием, где ключом выступает - реальный приватный ключ
|
||||||
|
* входящего в группу клиента. Нужно это для будущей синхронзации, так как клиенту на его другом
|
||||||
|
* устройстве нужно получить ключ группы и ее информацию. Сервер расшифровать эту строку не может. Эту
|
||||||
|
* строку может расшифровать только клиент, так как она зашифрована его приватным ключом
|
||||||
|
*/
|
||||||
|
private groupString: string = "";
|
||||||
|
|
||||||
public getPacketId(): number {
|
public getPacketId(): number {
|
||||||
return 0x14;
|
return 0x14;
|
||||||
@@ -20,6 +28,7 @@ export class PacketGroupJoin extends Packet {
|
|||||||
public _receive(stream: Stream): void {
|
public _receive(stream: Stream): void {
|
||||||
this.groupId = stream.readString();
|
this.groupId = stream.readString();
|
||||||
this.groupStatus = stream.readInt8();
|
this.groupStatus = stream.readInt8();
|
||||||
|
this.groupString = stream.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public _send(): Promise<Stream> | Stream {
|
public _send(): Promise<Stream> | Stream {
|
||||||
@@ -27,6 +36,7 @@ export class PacketGroupJoin extends Packet {
|
|||||||
stream.writeInt16(this.getPacketId());
|
stream.writeInt16(this.getPacketId());
|
||||||
stream.writeString(this.groupId);
|
stream.writeString(this.groupId);
|
||||||
stream.writeInt8(this.groupStatus);
|
stream.writeInt8(this.groupStatus);
|
||||||
|
stream.writeString(this.groupString);
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,4 +56,12 @@ export class PacketGroupJoin extends Packet {
|
|||||||
return this.groupStatus;
|
return this.groupStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setGroupString(groupString: string) {
|
||||||
|
this.groupString = groupString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getGroupString(): string {
|
||||||
|
return this.groupString;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
export const SERVERS = [
|
export const SERVERS = [
|
||||||
//'wss://cdn.rosetta-im.com',
|
//'wss://cdn.rosetta-im.com',
|
||||||
//'ws://10.211.55.2:3000',
|
//'ws://10.211.55.2:3000',
|
||||||
//'ws://127.0.0.1:3000',
|
'ws://127.0.0.1:3000',
|
||||||
'wss://wss.rosetta.im'
|
//'wss://wss.rosetta.im'
|
||||||
];
|
];
|
||||||
|
|
||||||
export function selectServer(): string {
|
export function selectServer(): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user