Compare commits
10 Commits
3492a881cc
...
779c265851
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
779c265851 | ||
|
|
8ac952071d | ||
|
|
e1f5cb7eb8 | ||
|
|
30f2c90015 | ||
|
|
a341aedd8d | ||
|
|
a9164c7087 | ||
|
|
04dd23dd5c | ||
|
|
5979c31120 | ||
|
|
c8c85991c7 | ||
|
|
c052fdae41 |
@@ -16,7 +16,6 @@ import { attachReceiverE2EE, attachSenderE2EE } from "./audioE2EE";
|
||||
import { useDeattachedSender } from "../DialogProvider/useDeattachedSender";
|
||||
import { AttachmentType } from "../ProtocolProvider/protocol/packets/packet.message";
|
||||
import { generateRandomKey } from "@/app/utils/utils";
|
||||
import { useSystemInformation } from "../SystemProvider/useSystemInformation";
|
||||
|
||||
export interface CallContextValue {
|
||||
call: (callable: string) => void;
|
||||
@@ -86,7 +85,12 @@ export function CallProvider(props : CallProviderProps) {
|
||||
const soundRef = useRef<boolean>(true);
|
||||
const {sendMessage} = useDeattachedSender();
|
||||
const hasRemoteTrackRef = useRef<boolean>(false);
|
||||
const systemInformation = useSystemInformation();
|
||||
|
||||
/**
|
||||
* Используются для входа в звонок
|
||||
*/
|
||||
const callSessionIdRef = useRef<string>("");
|
||||
const callTokenRef = useRef<string>("");
|
||||
|
||||
const {playSound, stopSound, stopLoopSound} = useSound();
|
||||
const {setWindowPriority} = useWindow();
|
||||
@@ -196,8 +200,6 @@ export function CallProvider(props : CallProviderProps) {
|
||||
let answerSignal = new PacketWebRTC();
|
||||
answerSignal.setSignalType(WebRTCSignalType.ANSWER);
|
||||
answerSignal.setSdpOrCandidate(JSON.stringify(answer));
|
||||
answerSignal.setPublicKey(publicKey);
|
||||
answerSignal.setDeviceId(systemInformation.id);
|
||||
send(answerSignal);
|
||||
info("Received WebRTC offer, set remote description and sent answer");
|
||||
return;
|
||||
@@ -214,15 +216,13 @@ export function CallProvider(props : CallProviderProps) {
|
||||
openCallsModal("The connection with the user was lost. The call has ended.")
|
||||
end();
|
||||
}
|
||||
if(activeCall){
|
||||
if(signalType == SignalType.RINGING_TIMEOUT) {
|
||||
/**
|
||||
* У нас уже есть активный звонок, игнорируем все сигналы, кроме сигналов от текущего звонка
|
||||
* Другой стороне был отправлен сигнал звонка, но она не ответила на него в течении определенного времени
|
||||
*/
|
||||
if(packet.getSrc() != activeCall && packet.getSrc() != publicKey){
|
||||
console.info("Received signal from " + packet.getSrc() + " but active call is with " + activeCall + ", ignoring");
|
||||
info("Received signal for another call, ignoring");
|
||||
return;
|
||||
}
|
||||
openCallsModal("The user did not answer the call in time. Please try again later.");
|
||||
end();
|
||||
return;
|
||||
}
|
||||
if(signalType == SignalType.END_CALL){
|
||||
/**
|
||||
@@ -247,68 +247,65 @@ export function CallProvider(props : CallProviderProps) {
|
||||
info("Received incoming call from " + packet.getSrc() + " but we are already on a call, sent busy signal");
|
||||
return;
|
||||
}
|
||||
callSessionIdRef.current = packet.getCallId();
|
||||
callTokenRef.current = packet.getJoinToken();
|
||||
setWindowPriority(true);
|
||||
playSound("ringtone.mp3", true);
|
||||
setActiveCall(packet.getSrc());
|
||||
setCallState(CallState.INCOMING);
|
||||
setShowCallView(true);
|
||||
}
|
||||
if(signalType == SignalType.KEY_EXCHANGE && roleRef.current == CallRole.CALLER){
|
||||
console.info("EXCHANGE SIGNAL RECEIVED, CALLER ROLE");
|
||||
/**
|
||||
* Другая сторона сгенерировала ключи для сессии и отправила нам публичную часть,
|
||||
* теперь мы можем создать общую секретную сессию для шифрования звонка
|
||||
*/
|
||||
const sharedPublic = packet.getSharedPublic();
|
||||
if(!sharedPublic){
|
||||
info("Received key exchange signal without shared public key");
|
||||
return;
|
||||
}
|
||||
const sessionKeys = generateSessionKeys();
|
||||
const computedSharedSecret = nacl.box.before(Buffer.from(sharedPublic, 'hex'), sessionKeys.secretKey);
|
||||
sharedSecretRef.current = Buffer.from(computedSharedSecret).toString('hex');
|
||||
info("Generated shared secret for call session: " + sharedSecretRef.current);
|
||||
/**
|
||||
* Нам нужно отправить свой публичный ключ другой стороне, чтобы она тоже могла создать общую секретную сессию
|
||||
*/
|
||||
const signalPacket = new PacketSignalPeer();
|
||||
signalPacket.setSrc(publicKey);
|
||||
signalPacket.setDst(packet.getSrc());
|
||||
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
||||
signalPacket.setSharedPublic(Buffer.from(sessionKeys.publicKey).toString('hex'));
|
||||
send(signalPacket);
|
||||
setCallState(CallState.WEB_RTC_EXCHANGE);
|
||||
/**
|
||||
* Создаем комнату на сервере SFU, комнату создает звонящий
|
||||
*/
|
||||
let webRtcSignal = new PacketSignalPeer();
|
||||
webRtcSignal.setSignalType(SignalType.CREATE_ROOM);
|
||||
webRtcSignal.setSrc(publicKey);
|
||||
webRtcSignal.setDst(packet.getSrc());
|
||||
send(webRtcSignal);
|
||||
}
|
||||
if(signalType == SignalType.KEY_EXCHANGE && roleRef.current == CallRole.CALLEE){
|
||||
if(signalType == SignalType.KEY_EXCHANGE){
|
||||
console.info("EXCHANGE SIGNAL RECEIVED, CALLEE ROLE");
|
||||
/**
|
||||
* Мы отправили свою публичную часть ключа другой стороне,
|
||||
* теперь мы получили ее публичную часть и можем создать общую
|
||||
* секретную сессию для шифрования звонка
|
||||
* Другая сторона отправила нам ключи, теперь отправляем ей свои для генерации общего секрета
|
||||
*/
|
||||
const sharedPublic = packet.getSharedPublic();
|
||||
if(!sharedPublic){
|
||||
info("Received key exchange signal without shared public key");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!sessionKeys){
|
||||
info("Received key exchange signal but session keys are not generated");
|
||||
return;
|
||||
}
|
||||
|
||||
const computedSharedSecret = nacl.box.before(Buffer.from(sharedPublic, 'hex'), sessionKeys.secretKey);
|
||||
sharedSecretRef.current = Buffer.from(computedSharedSecret).toString('hex');
|
||||
info("Generated shared secret for call session: " + sharedSecretRef.current);
|
||||
setCallState(CallState.WEB_RTC_EXCHANGE);
|
||||
|
||||
if(roleRef.current == CallRole.CALLER){
|
||||
/**
|
||||
* Вызывающий уже отправил ключ, сессия сгенерирована, сообщаем серверу что звонок активен
|
||||
*/
|
||||
const activeSignal = new PacketSignalPeer();
|
||||
activeSignal.setSrc(publicKey);
|
||||
activeSignal.setDst(activeCall);
|
||||
activeSignal.setSignalType(SignalType.ACTIVE);
|
||||
send(activeSignal);
|
||||
return;
|
||||
}
|
||||
const signalPacket = new PacketSignalPeer();
|
||||
signalPacket.setSrc(publicKey);
|
||||
signalPacket.setDst(activeCall);
|
||||
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
||||
signalPacket.setSharedPublic(Buffer.from(sessionKeys.publicKey).toString('hex'));
|
||||
send(signalPacket);
|
||||
}
|
||||
if(signalType == SignalType.CREATE_ROOM) {
|
||||
if(signalType == SignalType.ACCEPT){
|
||||
/**
|
||||
* Другая сторона приняла наш звонок, комната на SFU создалась, нужно сгенерировать ключи
|
||||
*/
|
||||
const keys = generateSessionKeys();
|
||||
const signalPacket = new PacketSignalPeer();
|
||||
signalPacket.setSrc(publicKey);
|
||||
signalPacket.setDst(activeCall);
|
||||
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
||||
signalPacket.setSharedPublic(Buffer.from(keys.publicKey).toString('hex'));
|
||||
send(signalPacket);
|
||||
}
|
||||
if(signalType == SignalType.ACTIVE) {
|
||||
if(!sessionKeys){
|
||||
/**
|
||||
* Сервер может отправить CREATE_ROOM сигнал, даже если мы приняли звонок на другом устройстве, по этому проверяем,
|
||||
@@ -319,12 +316,6 @@ export function CallProvider(props : CallProviderProps) {
|
||||
end();
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Создана комната для обмена WebRTC потоками, но такое событие сервер может отправить даже если звонок
|
||||
* был принят с другого устройства, по этому проверяем, наш ли звонок
|
||||
*/
|
||||
roomIdRef.current = packet.getRoomId();
|
||||
info("WebRTC room created with id: " + packet.getRoomId());
|
||||
/**
|
||||
* Нужно отправить свой SDP оффер другой стороне, чтобы установить WebRTC соединение
|
||||
*/
|
||||
@@ -341,8 +332,6 @@ export function CallProvider(props : CallProviderProps) {
|
||||
let candidateSignal = new PacketWebRTC();
|
||||
candidateSignal.setSignalType(WebRTCSignalType.ICE_CANDIDATE);
|
||||
candidateSignal.setSdpOrCandidate(JSON.stringify(event.candidate));
|
||||
candidateSignal.setPublicKey(publicKey);
|
||||
candidateSignal.setDeviceId(systemInformation.id);
|
||||
send(candidateSignal);
|
||||
}
|
||||
}
|
||||
@@ -404,8 +393,6 @@ export function CallProvider(props : CallProviderProps) {
|
||||
let offerSignal = new PacketWebRTC();
|
||||
offerSignal.setSignalType(WebRTCSignalType.OFFER);
|
||||
offerSignal.setSdpOrCandidate(JSON.stringify(offer));
|
||||
offerSignal.setPublicKey(publicKey);
|
||||
offerSignal.setDeviceId(systemInformation.id);
|
||||
send(offerSignal);
|
||||
return;
|
||||
}
|
||||
@@ -429,7 +416,9 @@ export function CallProvider(props : CallProviderProps) {
|
||||
{text}
|
||||
</Text>
|
||||
<Flex align={'center'} justify={'flex-end'}>
|
||||
<Button color={'red'} variant={'subtle'} onClick={() => modals.closeAll()} mt="md">
|
||||
<Button style={{
|
||||
outline: 'none'
|
||||
}} color={'red'} variant={'subtle'} onClick={() => modals.closeAll()} mt="md">
|
||||
Close
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -471,6 +460,8 @@ export function CallProvider(props : CallProviderProps) {
|
||||
const packetSignal = new PacketSignalPeer();
|
||||
packetSignal.setSrc(publicKey);
|
||||
packetSignal.setDst(activeCall);
|
||||
packetSignal.setCallId(callSessionIdRef.current);
|
||||
packetSignal.setJoinToken(callTokenRef.current);
|
||||
packetSignal.setSignalType(SignalType.END_CALL);
|
||||
send(packetSignal);
|
||||
end();
|
||||
@@ -541,15 +532,22 @@ export function CallProvider(props : CallProviderProps) {
|
||||
stopLoopSound();
|
||||
stopSound();
|
||||
/**
|
||||
* Звонок принят, генерируем ключи для сессии и отправляем их другой стороне для установления защищенного канала связи
|
||||
* Звонок принят, генерируем свой ключ для будующего обмена
|
||||
*/
|
||||
generateSessionKeys();
|
||||
/**
|
||||
* Отправляем сигнал что звонок принят другой стороне, чтобы она могла начать обмен ключами и установку соединения
|
||||
*/
|
||||
const keys = generateSessionKeys();
|
||||
const signalPacket = new PacketSignalPeer();
|
||||
signalPacket.setSrc(publicKey);
|
||||
signalPacket.setDst(activeCall);
|
||||
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
||||
signalPacket.setSharedPublic(Buffer.from(keys.publicKey).toString('hex'));
|
||||
signalPacket.setCallId(callSessionIdRef.current);
|
||||
signalPacket.setJoinToken(callTokenRef.current);
|
||||
signalPacket.setSignalType(SignalType.ACCEPT);
|
||||
send(signalPacket);
|
||||
/**
|
||||
* Устанавливаем состояние звонка и стадию обмена ключами
|
||||
*/
|
||||
setCallState(CallState.KEY_EXCHANGE);
|
||||
roleRef.current = CallRole.CALLEE;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,14 @@ export enum SignalType {
|
||||
KEY_EXCHANGE = 1,
|
||||
ACTIVE_CALL = 2,
|
||||
END_CALL = 3,
|
||||
CREATE_ROOM = 4,
|
||||
/**
|
||||
* Переведен в стадию активного, значит комната на SFU уже создана и можно начинать обмен сигналами WebRTC
|
||||
*/
|
||||
ACTIVE = 4,
|
||||
END_CALL_BECAUSE_PEER_DISCONNECTED = 5,
|
||||
END_CALL_BECAUSE_BUSY = 6
|
||||
END_CALL_BECAUSE_BUSY = 6,
|
||||
ACCEPT = 7,
|
||||
RINGING_TIMEOUT = 8
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,12 +33,8 @@ export class PacketSignalPeer extends Packet {
|
||||
|
||||
private signalType: SignalType = SignalType.CALL;
|
||||
|
||||
/**
|
||||
* Используется если SignalType == CREATE_ROOM,
|
||||
* для идентификации комнаты на SFU сервере, в которой будет происходить обмен сигналами
|
||||
* WebRTC для установления P2P соединения между участниками звонка
|
||||
*/
|
||||
private roomId: string = "";
|
||||
private callId: string = "";
|
||||
private joinToken: string = "";
|
||||
|
||||
|
||||
public getPacketId(): number {
|
||||
@@ -42,7 +43,9 @@ export class PacketSignalPeer extends Packet {
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.signalType = stream.readInt8();
|
||||
if(this.signalType == SignalType.END_CALL_BECAUSE_BUSY || this.signalType == SignalType.END_CALL_BECAUSE_PEER_DISCONNECTED){
|
||||
if(this.signalType == SignalType.END_CALL_BECAUSE_BUSY
|
||||
|| this.signalType == SignalType.RINGING_TIMEOUT
|
||||
|| this.signalType == SignalType.END_CALL_BECAUSE_PEER_DISCONNECTED){
|
||||
return;
|
||||
}
|
||||
this.src = stream.readString();
|
||||
@@ -50,8 +53,9 @@ export class PacketSignalPeer extends Packet {
|
||||
if(this.signalType == SignalType.KEY_EXCHANGE){
|
||||
this.sharedPublic = stream.readString();
|
||||
}
|
||||
if(this.signalType == SignalType.CREATE_ROOM){
|
||||
this.roomId = stream.readString();
|
||||
if(this.signalType == SignalType.CALL || this.signalType == SignalType.ACCEPT || this.signalType == SignalType.END_CALL){
|
||||
this.callId = stream.readString();
|
||||
this.joinToken = stream.readString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +63,9 @@ export class PacketSignalPeer extends Packet {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeInt8(this.signalType);
|
||||
if(this.signalType == SignalType.END_CALL_BECAUSE_BUSY || this.signalType == SignalType.END_CALL_BECAUSE_PEER_DISCONNECTED){
|
||||
if(this.signalType == SignalType.END_CALL_BECAUSE_BUSY
|
||||
|| this.signalType == SignalType.RINGING_TIMEOUT
|
||||
|| this.signalType == SignalType.END_CALL_BECAUSE_PEER_DISCONNECTED){
|
||||
return stream;
|
||||
}
|
||||
stream.writeString(this.src);
|
||||
@@ -67,8 +73,9 @@ export class PacketSignalPeer extends Packet {
|
||||
if(this.signalType == SignalType.KEY_EXCHANGE){
|
||||
stream.writeString(this.sharedPublic);
|
||||
}
|
||||
if(this.signalType == SignalType.CREATE_ROOM){
|
||||
stream.writeString(this.roomId);
|
||||
if(this.signalType == SignalType.CALL || this.signalType == SignalType.ACCEPT || this.signalType == SignalType.END_CALL){
|
||||
stream.writeString(this.callId);
|
||||
stream.writeString(this.joinToken);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
@@ -105,12 +112,20 @@ export class PacketSignalPeer extends Packet {
|
||||
this.src = src;
|
||||
}
|
||||
|
||||
public getRoomId(): string {
|
||||
return this.roomId;
|
||||
public getCallId(): string {
|
||||
return this.callId;
|
||||
}
|
||||
|
||||
public setRoomId(roomId: string) {
|
||||
this.roomId = roomId;
|
||||
|
||||
public setCallId(callId: string) {
|
||||
this.callId = callId;
|
||||
}
|
||||
|
||||
public getJoinToken(): string {
|
||||
return this.joinToken;
|
||||
}
|
||||
|
||||
public setJoinToken(joinToken: string) {
|
||||
this.joinToken = joinToken;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,8 +15,6 @@ export class PacketWebRTC extends Packet {
|
||||
|
||||
private signalType: WebRTCSignalType = WebRTCSignalType.OFFER;
|
||||
private sdpOrCandidate: string = "";
|
||||
private publicKey: string = "";
|
||||
private deviceId: string = "";
|
||||
|
||||
public getPacketId(): number {
|
||||
return 27;
|
||||
@@ -25,8 +23,6 @@ export class PacketWebRTC extends Packet {
|
||||
public _receive(stream: Stream): void {
|
||||
this.signalType = stream.readInt8();
|
||||
this.sdpOrCandidate = stream.readString();
|
||||
this.publicKey = stream.readString();
|
||||
this.deviceId = stream.readString();
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
@@ -34,8 +30,6 @@ export class PacketWebRTC extends Packet {
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeInt8(this.signalType);
|
||||
stream.writeString(this.sdpOrCandidate);
|
||||
stream.writeString(this.publicKey);
|
||||
stream.writeString(this.deviceId);
|
||||
return stream;
|
||||
}
|
||||
|
||||
@@ -55,20 +49,4 @@ export class PacketWebRTC extends Packet {
|
||||
return this.sdpOrCandidate;
|
||||
}
|
||||
|
||||
public setPublicKey(key: string) {
|
||||
this.publicKey = key;
|
||||
}
|
||||
|
||||
public getPublicKey(): string {
|
||||
return this.publicKey;
|
||||
}
|
||||
|
||||
public setDeviceId(id: string) {
|
||||
this.deviceId = id;
|
||||
}
|
||||
|
||||
public getDeviceId(): string {
|
||||
return this.deviceId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
export const APP_VERSION = "1.1.8";
|
||||
export const APP_VERSION = "1.1.9";
|
||||
export const CORE_MIN_REQUIRED_VERSION = "1.5.5";
|
||||
|
||||
export const RELEASE_NOTICE = `
|
||||
**Обновление v1.1.8** :emoji_1f631:
|
||||
- Новый протокол для обмена звонками, который должен улучшить стабильность и качество звонков.
|
||||
**Обновление v1.1.9** :emoji_1f631:
|
||||
- Новый протокол для обмена звонками с использованием callId и joinToken для улучшенной безопасности и надежности.
|
||||
- Исправление ошибок и улучшение производительности при обработке звонков.
|
||||
`;
|
||||
Reference in New Issue
Block a user