From 1d6c30fb08b88e15a82108701e12514fa116770c Mon Sep 17 00:00:00 2001 From: RoyceDa Date: Sat, 21 Mar 2026 19:16:44 +0200 Subject: [PATCH] =?UTF-8?q?=D0=A8=D0=B8=D1=84=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/providers/CallProvider/audioE2EE.ts | 86 +++++++++++-------------- package.json | 2 +- 2 files changed, 37 insertions(+), 51 deletions(-) diff --git a/app/providers/CallProvider/audioE2EE.ts b/app/providers/CallProvider/audioE2EE.ts index 7de4900..b7505d6 100644 --- a/app/providers/CallProvider/audioE2EE.ts +++ b/app/providers/CallProvider/audioE2EE.ts @@ -1,4 +1,4 @@ -import _sodium from "libsodium-wrappers"; +import _sodium from "libsodium-wrappers-sumo"; type KeyInput = Buffer | Uint8Array; @@ -17,64 +17,48 @@ export async function initE2EE(): Promise { function toUint8Array(input: KeyInput): Uint8Array { const u8 = input instanceof Uint8Array ? input : new Uint8Array(input); - return new Uint8Array(u8.slice().buffer); + return new Uint8Array(u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength)); +} + +function fillNonceFromTimestamp(nonce: Uint8Array, tsRaw: unknown): void { + nonce.fill(0); + + let ts = 0n; + if (typeof tsRaw === "bigint") ts = tsRaw; + else if (typeof tsRaw === "number" && Number.isFinite(tsRaw)) ts = BigInt(Math.floor(tsRaw)); + + // Записываем 64-bit timestamp в первые 8 байт nonce (BE) + nonce[0] = Number((ts >> 56n) & 0xffn); + nonce[1] = Number((ts >> 48n) & 0xffn); + nonce[2] = Number((ts >> 40n) & 0xffn); + nonce[3] = Number((ts >> 32n) & 0xffn); + nonce[4] = Number((ts >> 24n) & 0xffn); + nonce[5] = Number((ts >> 16n) & 0xffn); + nonce[6] = Number((ts >> 8n) & 0xffn); + nonce[7] = Number(ts & 0xffn); } function createFrameProcessor(key: Uint8Array) { - // Выделяем nonce один раз — переиспользуем на каждый фрейм - const nonce = new Uint8Array(sodium.crypto_stream_chacha20_NONCEBYTES); // 8 bytes - const nonceView = new DataView(nonce.buffer, 0, nonce.byteLength); + const nonceLen = sodium.crypto_stream_xchacha20_NONCEBYTES; // 24 + const nonce = new Uint8Array(nonceLen); - return function processFrame(data: ArrayBuffer, timestamp: number): ArrayBuffer { + return function processFrame(data: ArrayBuffer, timestamp: unknown): ArrayBuffer { const input = new Uint8Array(data); + fillNonceFromTimestamp(nonce, timestamp); - // Безопасно записываем nonce через отдельный DataView - nonceView.setUint32(0, (timestamp >>> 0) & 0xffffffff, false); - nonceView.setUint32(4, ((timestamp / 0x100000000) >>> 0) & 0xffffffff, false); - - const output = sodium.crypto_stream_chacha20_xor(input, nonce, key); - return output.buffer as ArrayBuffer; + const output = sodium.crypto_stream_xchacha20_xor(input, nonce, key); + return output.buffer.slice(output.byteOffset, output.byteOffset + output.byteLength) as ArrayBuffer; }; } -function createTransform(processFrame: (data: ArrayBuffer, timestamp: number) => ArrayBuffer) { - let frames = 0; - let slowFrames = 0; - let total = 0; - let max = 0; - +function createTransform(processFrame: (data: ArrayBuffer, timestamp: unknown) => ArrayBuffer) { return new TransformStream({ transform(frame, controller) { - const started = performance.now(); - try { - // Передаём timestamp фрейма как nonce - const ts = typeof frame.timestamp === "number" ? frame.timestamp : 0; - frame.data = processFrame(frame.data, ts); + frame.data = processFrame(frame.data, frame.timestamp); } catch (e) { console.error("[E2EE] frame error:", e); - controller.enqueue(frame); - return; } - - const elapsed = performance.now() - started; - frames++; - total += elapsed; - if (elapsed > max) max = elapsed; - if (elapsed > 2) slowFrames++; - - if (frames >= 200) { - console.info("[E2EE stats]", { - avgMs: +(total / frames).toFixed(3), - maxMs: +max.toFixed(3), - slowFrames, - }); - frames = 0; - slowFrames = 0; - total = 0; - max = 0; - } - controller.enqueue(frame); } }); @@ -87,8 +71,9 @@ export async function attachSenderE2EE(sender: RTCRtpSender, keyInput: KeyInput) await initE2EE(); const key = toUint8Array(keyInput); - if (key.byteLength < sodium.crypto_stream_chacha20_KEYBYTES) { - throw new Error(`Key must be at least ${sodium.crypto_stream_chacha20_KEYBYTES} bytes`); + const keyLen = sodium.crypto_stream_xchacha20_KEYBYTES; // 32 + if (key.byteLength < keyLen) { + throw new Error(`Key must be at least ${keyLen} bytes`); } const anySender = sender as any; @@ -97,7 +82,7 @@ export async function attachSenderE2EE(sender: RTCRtpSender, keyInput: KeyInput) } const { readable, writable } = anySender.createEncodedStreams(); - const processFrame = createFrameProcessor(key.slice(0, sodium.crypto_stream_chacha20_KEYBYTES)); + const processFrame = createFrameProcessor(key.slice(0, keyLen)); readable .pipeThrough(createTransform(processFrame)) @@ -112,8 +97,9 @@ export async function attachReceiverE2EE(receiver: RTCRtpReceiver, keyInput: Key await initE2EE(); const key = toUint8Array(keyInput); - if (key.byteLength < sodium.crypto_stream_chacha20_KEYBYTES) { - throw new Error(`Key must be at least ${sodium.crypto_stream_chacha20_KEYBYTES} bytes`); + const keyLen = sodium.crypto_stream_xchacha20_KEYBYTES; // 32 + if (key.byteLength < keyLen) { + throw new Error(`Key must be at least ${keyLen} bytes`); } const anyReceiver = receiver as any; @@ -122,7 +108,7 @@ export async function attachReceiverE2EE(receiver: RTCRtpReceiver, keyInput: Key } const { readable, writable } = anyReceiver.createEncodedStreams(); - const processFrame = createFrameProcessor(key.slice(0, sodium.crypto_stream_chacha20_KEYBYTES)); + const processFrame = createFrameProcessor(key.slice(0, keyLen)); readable .pipeThrough(createTransform(processFrame)) diff --git a/package.json b/package.json index 6518075..7728df2 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ "@mantine/form": "^8.3.12", "@mantine/hooks": "^8.3.12", "@mantine/modals": "^8.3.12", - "@noble/ciphers": "^1.3.0", "@noble/secp256k1": "^3.0.0", "@tabler/icons-react": "^3.31.0", "@types/crypto-js": "^4.2.2", @@ -112,6 +111,7 @@ "jszip": "^3.10.1", "libsodium": "^0.8.2", "libsodium-wrappers": "^0.8.2", + "libsodium-wrappers-sumo": "^0.8.2", "lottie-react": "^2.4.1", "node-forge": "^1.3.1", "node-machine-id": "^1.1.12",