Шифрование тест 3
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import _sodium from "libsodium-wrappers";
|
import _sodium from "libsodium-wrappers-sumo";
|
||||||
|
|
||||||
type KeyInput = Buffer | Uint8Array;
|
type KeyInput = Buffer | Uint8Array;
|
||||||
|
|
||||||
@@ -17,64 +17,48 @@ export async function initE2EE(): Promise<void> {
|
|||||||
|
|
||||||
function toUint8Array(input: KeyInput): Uint8Array {
|
function toUint8Array(input: KeyInput): Uint8Array {
|
||||||
const u8 = input instanceof Uint8Array ? input : new Uint8Array(input);
|
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) {
|
function createFrameProcessor(key: Uint8Array) {
|
||||||
// Выделяем nonce один раз — переиспользуем на каждый фрейм
|
const nonceLen = sodium.crypto_stream_xchacha20_NONCEBYTES; // 24
|
||||||
const nonce = new Uint8Array(sodium.crypto_stream_chacha20_NONCEBYTES); // 8 bytes
|
const nonce = new Uint8Array(nonceLen);
|
||||||
const nonceView = new DataView(nonce.buffer, 0, nonce.byteLength);
|
|
||||||
|
|
||||||
return function processFrame(data: ArrayBuffer, timestamp: number): ArrayBuffer {
|
return function processFrame(data: ArrayBuffer, timestamp: unknown): ArrayBuffer {
|
||||||
const input = new Uint8Array(data);
|
const input = new Uint8Array(data);
|
||||||
|
fillNonceFromTimestamp(nonce, timestamp);
|
||||||
|
|
||||||
// Безопасно записываем nonce через отдельный DataView
|
const output = sodium.crypto_stream_xchacha20_xor(input, nonce, key);
|
||||||
nonceView.setUint32(0, (timestamp >>> 0) & 0xffffffff, false);
|
return output.buffer.slice(output.byteOffset, output.byteOffset + output.byteLength) as ArrayBuffer;
|
||||||
nonceView.setUint32(4, ((timestamp / 0x100000000) >>> 0) & 0xffffffff, false);
|
|
||||||
|
|
||||||
const output = sodium.crypto_stream_chacha20_xor(input, nonce, key);
|
|
||||||
return output.buffer as ArrayBuffer;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTransform(processFrame: (data: ArrayBuffer, timestamp: number) => ArrayBuffer) {
|
function createTransform(processFrame: (data: ArrayBuffer, timestamp: unknown) => ArrayBuffer) {
|
||||||
let frames = 0;
|
|
||||||
let slowFrames = 0;
|
|
||||||
let total = 0;
|
|
||||||
let max = 0;
|
|
||||||
|
|
||||||
return new TransformStream<any, any>({
|
return new TransformStream<any, any>({
|
||||||
transform(frame, controller) {
|
transform(frame, controller) {
|
||||||
const started = performance.now();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Передаём timestamp фрейма как nonce
|
frame.data = processFrame(frame.data, frame.timestamp);
|
||||||
const ts = typeof frame.timestamp === "number" ? frame.timestamp : 0;
|
|
||||||
frame.data = processFrame(frame.data, ts);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[E2EE] frame error:", 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);
|
controller.enqueue(frame);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -87,8 +71,9 @@ export async function attachSenderE2EE(sender: RTCRtpSender, keyInput: KeyInput)
|
|||||||
await initE2EE();
|
await initE2EE();
|
||||||
|
|
||||||
const key = toUint8Array(keyInput);
|
const key = toUint8Array(keyInput);
|
||||||
if (key.byteLength < sodium.crypto_stream_chacha20_KEYBYTES) {
|
const keyLen = sodium.crypto_stream_xchacha20_KEYBYTES; // 32
|
||||||
throw new Error(`Key must be at least ${sodium.crypto_stream_chacha20_KEYBYTES} bytes`);
|
if (key.byteLength < keyLen) {
|
||||||
|
throw new Error(`Key must be at least ${keyLen} bytes`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const anySender = sender as any;
|
const anySender = sender as any;
|
||||||
@@ -97,7 +82,7 @@ export async function attachSenderE2EE(sender: RTCRtpSender, keyInput: KeyInput)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { readable, writable } = anySender.createEncodedStreams();
|
const { readable, writable } = anySender.createEncodedStreams();
|
||||||
const processFrame = createFrameProcessor(key.slice(0, sodium.crypto_stream_chacha20_KEYBYTES));
|
const processFrame = createFrameProcessor(key.slice(0, keyLen));
|
||||||
|
|
||||||
readable
|
readable
|
||||||
.pipeThrough(createTransform(processFrame))
|
.pipeThrough(createTransform(processFrame))
|
||||||
@@ -112,8 +97,9 @@ export async function attachReceiverE2EE(receiver: RTCRtpReceiver, keyInput: Key
|
|||||||
await initE2EE();
|
await initE2EE();
|
||||||
|
|
||||||
const key = toUint8Array(keyInput);
|
const key = toUint8Array(keyInput);
|
||||||
if (key.byteLength < sodium.crypto_stream_chacha20_KEYBYTES) {
|
const keyLen = sodium.crypto_stream_xchacha20_KEYBYTES; // 32
|
||||||
throw new Error(`Key must be at least ${sodium.crypto_stream_chacha20_KEYBYTES} bytes`);
|
if (key.byteLength < keyLen) {
|
||||||
|
throw new Error(`Key must be at least ${keyLen} bytes`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const anyReceiver = receiver as any;
|
const anyReceiver = receiver as any;
|
||||||
@@ -122,7 +108,7 @@ export async function attachReceiverE2EE(receiver: RTCRtpReceiver, keyInput: Key
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { readable, writable } = anyReceiver.createEncodedStreams();
|
const { readable, writable } = anyReceiver.createEncodedStreams();
|
||||||
const processFrame = createFrameProcessor(key.slice(0, sodium.crypto_stream_chacha20_KEYBYTES));
|
const processFrame = createFrameProcessor(key.slice(0, keyLen));
|
||||||
|
|
||||||
readable
|
readable
|
||||||
.pipeThrough(createTransform(processFrame))
|
.pipeThrough(createTransform(processFrame))
|
||||||
|
|||||||
@@ -84,7 +84,6 @@
|
|||||||
"@mantine/form": "^8.3.12",
|
"@mantine/form": "^8.3.12",
|
||||||
"@mantine/hooks": "^8.3.12",
|
"@mantine/hooks": "^8.3.12",
|
||||||
"@mantine/modals": "^8.3.12",
|
"@mantine/modals": "^8.3.12",
|
||||||
"@noble/ciphers": "^1.3.0",
|
|
||||||
"@noble/secp256k1": "^3.0.0",
|
"@noble/secp256k1": "^3.0.0",
|
||||||
"@tabler/icons-react": "^3.31.0",
|
"@tabler/icons-react": "^3.31.0",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
@@ -112,6 +111,7 @@
|
|||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"libsodium": "^0.8.2",
|
"libsodium": "^0.8.2",
|
||||||
"libsodium-wrappers": "^0.8.2",
|
"libsodium-wrappers": "^0.8.2",
|
||||||
|
"libsodium-wrappers-sumo": "^0.8.2",
|
||||||
"lottie-react": "^2.4.1",
|
"lottie-react": "^2.4.1",
|
||||||
"node-forge": "^1.3.1",
|
"node-forge": "^1.3.1",
|
||||||
"node-machine-id": "^1.1.12",
|
"node-machine-id": "^1.1.12",
|
||||||
|
|||||||
Reference in New Issue
Block a user