livekit-client 2.15.5 → 2.15.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +54 -50
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +62 -16
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +0 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/sifPayload.d.ts +22 -0
- package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts +1 -0
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +4 -1
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +8 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +0 -1
- package/dist/ts4.2/src/e2ee/worker/sifPayload.d.ts +22 -0
- package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +1 -0
- package/dist/ts4.2/src/room/track/Track.d.ts +4 -1
- package/dist/ts4.2/src/room/utils.d.ts +8 -0
- package/package.json +7 -7
- package/src/e2ee/E2eeManager.ts +18 -1
- package/src/e2ee/worker/FrameCryptor.ts +8 -18
- package/src/e2ee/worker/e2ee.worker.ts +6 -1
- package/src/e2ee/worker/sifPayload.ts +75 -0
- package/src/room/Room.ts +11 -2
- package/src/room/track/LocalTrack.ts +5 -2
- package/src/room/track/RemoteVideoTrack.ts +12 -2
- package/src/room/track/Track.ts +10 -1
- package/src/room/utils.ts +12 -3
- package/dist/src/e2ee/worker/SifGuard.d.ts +0 -11
- package/dist/src/e2ee/worker/SifGuard.d.ts.map +0 -1
- package/dist/ts4.2/src/e2ee/worker/SifGuard.d.ts +0 -11
- package/src/e2ee/worker/SifGuard.ts +0 -47
@@ -484,8 +484,6 @@ const KEY_PROVIDER_DEFAULTS = {
|
|
484
484
|
failureTolerance: DECRYPTION_FAILURE_TOLERANCE,
|
485
485
|
keyringSize: 16
|
486
486
|
};
|
487
|
-
const MAX_SIF_COUNT = 100;
|
488
|
-
const MAX_SIF_DURATION = 2000;
|
489
487
|
|
490
488
|
class LivekitError extends Error {
|
491
489
|
constructor(code, message) {
|
@@ -1093,42 +1091,6 @@ function writeRbsp(data_in) {
|
|
1093
1091
|
return new Uint8Array(dataOut);
|
1094
1092
|
}
|
1095
1093
|
|
1096
|
-
class SifGuard {
|
1097
|
-
constructor() {
|
1098
|
-
this.consecutiveSifCount = 0;
|
1099
|
-
this.lastSifReceivedAt = 0;
|
1100
|
-
this.userFramesSinceSif = 0;
|
1101
|
-
}
|
1102
|
-
recordSif() {
|
1103
|
-
var _a;
|
1104
|
-
this.consecutiveSifCount += 1;
|
1105
|
-
(_a = this.sifSequenceStartedAt) !== null && _a !== void 0 ? _a : this.sifSequenceStartedAt = Date.now();
|
1106
|
-
this.lastSifReceivedAt = Date.now();
|
1107
|
-
}
|
1108
|
-
recordUserFrame() {
|
1109
|
-
if (this.sifSequenceStartedAt === undefined) {
|
1110
|
-
return;
|
1111
|
-
} else {
|
1112
|
-
this.userFramesSinceSif += 1;
|
1113
|
-
}
|
1114
|
-
if (
|
1115
|
-
// reset if we received more user frames than SIFs
|
1116
|
-
this.userFramesSinceSif > this.consecutiveSifCount ||
|
1117
|
-
// also reset if we got a new user frame and the latest SIF frame hasn't been updated in a while
|
1118
|
-
Date.now() - this.lastSifReceivedAt > MAX_SIF_DURATION) {
|
1119
|
-
this.reset();
|
1120
|
-
}
|
1121
|
-
}
|
1122
|
-
isSifAllowed() {
|
1123
|
-
return this.consecutiveSifCount < MAX_SIF_COUNT && (this.sifSequenceStartedAt === undefined || Date.now() - this.sifSequenceStartedAt < MAX_SIF_DURATION);
|
1124
|
-
}
|
1125
|
-
reset() {
|
1126
|
-
this.userFramesSinceSif = 0;
|
1127
|
-
this.consecutiveSifCount = 0;
|
1128
|
-
this.sifSequenceStartedAt = undefined;
|
1129
|
-
}
|
1130
|
-
}
|
1131
|
-
|
1132
1094
|
/**
|
1133
1095
|
* NALU (Network Abstraction Layer Unit) utilities for H.264 and H.265 video processing
|
1134
1096
|
* Contains functions for parsing and working with NALUs in video frames
|
@@ -1389,6 +1351,49 @@ function processNALUsForEncryption(data, knownCodec) {
|
|
1389
1351
|
};
|
1390
1352
|
}
|
1391
1353
|
|
1354
|
+
/**
|
1355
|
+
* Create a crypto hash using Web Crypto API for secure comparison operations
|
1356
|
+
*/
|
1357
|
+
function cryptoHash(data) {
|
1358
|
+
return __awaiter(this, void 0, void 0, function* () {
|
1359
|
+
const hashBuffer = yield crypto.subtle.digest('SHA-256', data);
|
1360
|
+
const hashArray = new Uint8Array(hashBuffer);
|
1361
|
+
return Array.from(hashArray).map(b => b.toString(16).padStart(2, '0')).join('');
|
1362
|
+
});
|
1363
|
+
}
|
1364
|
+
/**
|
1365
|
+
* Pre-computed SHA-256 hashes for secure comparison operations
|
1366
|
+
*/
|
1367
|
+
const CryptoHashes = {
|
1368
|
+
VP8KeyFrame8x8: 'ef0161653d8b2b23aad46624b420af1d03ce48950e9fc85718028f91b50f9219',
|
1369
|
+
H264KeyFrame2x2SPS: 'f0a0e09647d891d6d50aa898bce7108090375d0d55e50a2bb21147afee558e44',
|
1370
|
+
H264KeyFrame2x2PPS: '61d9665eed71b6d424ae9539330a3bdd5cb386d4d781c808219a6e36750493a7',
|
1371
|
+
H264KeyFrame2x2IDR: 'faffc26b68a2fc09096fa20f3351e706398b6f838a7500c8063472c2e476e90d',
|
1372
|
+
OpusSilenceFrame: 'aad8d31fc56b2802ca500e58c2fb9d0b29ad71bb7cb52cd6530251eade188988'
|
1373
|
+
};
|
1374
|
+
/**
|
1375
|
+
* Check if a byte array matches any of the known SIF payload frame types using secure crypto hashes
|
1376
|
+
*/
|
1377
|
+
function identifySifPayload(data) {
|
1378
|
+
return __awaiter(this, void 0, void 0, function* () {
|
1379
|
+
const hash = yield cryptoHash(data);
|
1380
|
+
switch (hash) {
|
1381
|
+
case CryptoHashes.VP8KeyFrame8x8:
|
1382
|
+
return 'vp8';
|
1383
|
+
case CryptoHashes.H264KeyFrame2x2SPS:
|
1384
|
+
return 'h264';
|
1385
|
+
case CryptoHashes.H264KeyFrame2x2PPS:
|
1386
|
+
return 'h264';
|
1387
|
+
case CryptoHashes.H264KeyFrame2x2IDR:
|
1388
|
+
return 'h264';
|
1389
|
+
case CryptoHashes.OpusSilenceFrame:
|
1390
|
+
return 'opus';
|
1391
|
+
default:
|
1392
|
+
return null;
|
1393
|
+
}
|
1394
|
+
});
|
1395
|
+
}
|
1396
|
+
|
1392
1397
|
const encryptionEnabledMap = new Map();
|
1393
1398
|
class BaseFrameCryptor extends eventsExports.EventEmitter {
|
1394
1399
|
encodeFunction(encodedFrame, controller) {
|
@@ -1413,7 +1418,6 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
1413
1418
|
this.rtpMap = new Map();
|
1414
1419
|
this.keyProviderOptions = opts.keyProviderOptions;
|
1415
1420
|
this.sifTrailer = (_a = opts.sifTrailer) !== null && _a !== void 0 ? _a : Uint8Array.from([]);
|
1416
|
-
this.sifGuard = new SifGuard();
|
1417
1421
|
}
|
1418
1422
|
get logContext() {
|
1419
1423
|
return {
|
@@ -1437,7 +1441,6 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
1437
1441
|
}
|
1438
1442
|
this.participantIdentity = id;
|
1439
1443
|
this.keys = keys;
|
1440
|
-
this.sifGuard.reset();
|
1441
1444
|
}
|
1442
1445
|
unsetParticipant() {
|
1443
1446
|
workerLogger.debug('unsetting participant', this.logContext);
|
@@ -1599,22 +1602,17 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
1599
1602
|
if (!this.isEnabled() ||
|
1600
1603
|
// skip for decryption for empty dtx frames
|
1601
1604
|
encodedFrame.data.byteLength === 0) {
|
1602
|
-
workerLogger.debug('skipping empty frame', this.logContext);
|
1603
|
-
this.sifGuard.recordUserFrame();
|
1604
1605
|
return controller.enqueue(encodedFrame);
|
1605
1606
|
}
|
1606
1607
|
if (isFrameServerInjected(encodedFrame.data, this.sifTrailer)) {
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
encodedFrame.data = encodedFrame.data.slice(0, encodedFrame.data.byteLength - this.sifTrailer.byteLength);
|
1608
|
+
encodedFrame.data = encodedFrame.data.slice(0, encodedFrame.data.byteLength - this.sifTrailer.byteLength);
|
1609
|
+
if (yield identifySifPayload(encodedFrame.data)) {
|
1610
|
+
workerLogger.debug('enqueue SIF', this.logContext);
|
1611
1611
|
return controller.enqueue(encodedFrame);
|
1612
1612
|
} else {
|
1613
|
-
workerLogger.warn('SIF
|
1613
|
+
workerLogger.warn('Unexpected SIF frame payload, dropping frame', this.logContext);
|
1614
1614
|
return;
|
1615
1615
|
}
|
1616
|
-
} else {
|
1617
|
-
this.sifGuard.recordUserFrame();
|
1618
1616
|
}
|
1619
1617
|
const data = new Uint8Array(encodedFrame.data);
|
1620
1618
|
const keyIndex = data[encodedFrame.data.byteLength - 1];
|
@@ -2099,6 +2097,11 @@ onmessage = ev => {
|
|
2099
2097
|
break;
|
2100
2098
|
case 'updateCodec':
|
2101
2099
|
getTrackCryptor(data.participantIdentity, data.trackId).setVideoCodec(data.codec);
|
2100
|
+
workerLogger.info('updated codec', {
|
2101
|
+
participantIdentity: data.participantIdentity,
|
2102
|
+
trackId: data.trackId,
|
2103
|
+
codec: data.codec
|
2104
|
+
});
|
2102
2105
|
break;
|
2103
2106
|
case 'setRTPMap':
|
2104
2107
|
// this is only used for the local participant
|
@@ -2148,7 +2151,8 @@ function getTrackCryptor(participantIdentity, trackId) {
|
|
2148
2151
|
let cryptor = cryptors[0];
|
2149
2152
|
if (!cryptor) {
|
2150
2153
|
workerLogger.info('creating new cryptor for', {
|
2151
|
-
participantIdentity
|
2154
|
+
participantIdentity,
|
2155
|
+
trackId
|
2152
2156
|
});
|
2153
2157
|
if (!keyProviderOptions) {
|
2154
2158
|
throw Error('Missing keyProvider options');
|