livekit-client 2.15.5 → 2.15.7

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.
Files changed (46) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +54 -50
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +81 -40
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.umd.js +1 -1
  8. package/dist/livekit-client.umd.js.map +1 -1
  9. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  10. package/dist/src/e2ee/worker/FrameCryptor.d.ts +0 -1
  11. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  12. package/dist/src/e2ee/worker/sifPayload.d.ts +22 -0
  13. package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -0
  14. package/dist/src/room/PCTransport.d.ts.map +1 -1
  15. package/dist/src/room/Room.d.ts.map +1 -1
  16. package/dist/src/room/participant/LocalParticipant.d.ts +1 -3
  17. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  18. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  19. package/dist/src/room/track/RemoteVideoTrack.d.ts +1 -0
  20. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  21. package/dist/src/room/track/Track.d.ts +4 -1
  22. package/dist/src/room/track/Track.d.ts.map +1 -1
  23. package/dist/src/room/utils.d.ts +8 -0
  24. package/dist/src/room/utils.d.ts.map +1 -1
  25. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +0 -1
  26. package/dist/ts4.2/src/e2ee/worker/sifPayload.d.ts +22 -0
  27. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +1 -3
  28. package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +1 -0
  29. package/dist/ts4.2/src/room/track/Track.d.ts +4 -1
  30. package/dist/ts4.2/src/room/utils.d.ts +8 -0
  31. package/package.json +10 -9
  32. package/src/e2ee/E2eeManager.ts +18 -1
  33. package/src/e2ee/worker/FrameCryptor.ts +8 -18
  34. package/src/e2ee/worker/e2ee.worker.ts +6 -1
  35. package/src/e2ee/worker/sifPayload.ts +75 -0
  36. package/src/room/PCTransport.ts +14 -5
  37. package/src/room/Room.ts +12 -3
  38. package/src/room/participant/LocalParticipant.ts +9 -23
  39. package/src/room/track/LocalTrack.ts +5 -2
  40. package/src/room/track/RemoteVideoTrack.ts +12 -2
  41. package/src/room/track/Track.ts +10 -1
  42. package/src/room/utils.ts +12 -3
  43. package/dist/src/e2ee/worker/SifGuard.d.ts +0 -11
  44. package/dist/src/e2ee/worker/SifGuard.d.ts.map +0 -1
  45. package/dist/ts4.2/src/e2ee/worker/SifGuard.d.ts +0 -11
  46. 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
- workerLogger.debug('enqueue SIF', this.logContext);
1608
- this.sifGuard.recordSif();
1609
- if (this.sifGuard.isSifAllowed()) {
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 limit reached, dropping frame');
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');