livekit-client 1.13.3 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. package/README.md +9 -4
  2. package/dist/livekit-client.e2ee.worker.js +1 -1
  3. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  4. package/dist/livekit-client.e2ee.worker.mjs +86 -16
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +92 -12
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.umd.js +1 -1
  9. package/dist/livekit-client.umd.js.map +1 -1
  10. package/dist/src/e2ee/utils.d.ts +3 -0
  11. package/dist/src/e2ee/utils.d.ts.map +1 -1
  12. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  13. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  14. package/dist/src/room/events.d.ts +1 -1
  15. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  16. package/dist/src/room/track/LocalTrack.d.ts +7 -0
  17. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  18. package/dist/src/room/track/RemoteTrack.d.ts +7 -0
  19. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
  20. package/dist/src/room/track/options.d.ts +7 -3
  21. package/dist/src/room/track/options.d.ts.map +1 -1
  22. package/dist/ts4.2/src/e2ee/utils.d.ts +3 -0
  23. package/dist/ts4.2/src/room/events.d.ts +1 -1
  24. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +7 -0
  25. package/dist/ts4.2/src/room/track/RemoteTrack.d.ts +7 -0
  26. package/dist/ts4.2/src/room/track/options.d.ts +7 -3
  27. package/package.json +1 -1
  28. package/src/e2ee/utils.ts +52 -0
  29. package/src/e2ee/worker/FrameCryptor.ts +45 -27
  30. package/src/room/RTCEngine.ts +3 -2
  31. package/src/room/Room.ts +3 -3
  32. package/src/room/events.ts +1 -1
  33. package/src/room/participant/LocalParticipant.ts +7 -6
  34. package/src/room/track/LocalTrack.ts +14 -0
  35. package/src/room/track/RemoteTrack.ts +14 -0
  36. package/src/room/track/options.ts +7 -3
@@ -882,6 +882,54 @@ function ratchet(material, salt) {
882
882
  return crypto.subtle.deriveBits(algorithmOptions, material, 256);
883
883
  });
884
884
  }
885
+ function needsRbspUnescaping(frameData) {
886
+ for (var i = 0; i < frameData.length - 3; i++) {
887
+ if (frameData[i] == 0 && frameData[i + 1] == 0 && frameData[i + 2] == 3) return true;
888
+ }
889
+ return false;
890
+ }
891
+ function parseRbsp(stream) {
892
+ const dataOut = [];
893
+ var length = stream.length;
894
+ for (var i = 0; i < stream.length;) {
895
+ // Be careful about over/underflow here. byte_length_ - 3 can underflow, and
896
+ // i + 3 can overflow, but byte_length_ - i can't, because i < byte_length_
897
+ // above, and that expression will produce the number of bytes left in
898
+ // the stream including the byte at i.
899
+ if (length - i >= 3 && !stream[i] && !stream[i + 1] && stream[i + 2] == 3) {
900
+ // Two rbsp bytes.
901
+ dataOut.push(stream[i++]);
902
+ dataOut.push(stream[i++]);
903
+ // Skip the emulation byte.
904
+ i++;
905
+ } else {
906
+ // Single rbsp byte.
907
+ dataOut.push(stream[i++]);
908
+ }
909
+ }
910
+ return new Uint8Array(dataOut);
911
+ }
912
+ const kZerosInStartSequence = 2;
913
+ const kEmulationByte = 3;
914
+ function writeRbsp(data_in) {
915
+ const dataOut = [];
916
+ var numConsecutiveZeros = 0;
917
+ for (var i = 0; i < data_in.length; ++i) {
918
+ var byte = data_in[i];
919
+ if (byte <= kEmulationByte && numConsecutiveZeros >= kZerosInStartSequence) {
920
+ // Need to escape.
921
+ dataOut.push(kEmulationByte);
922
+ numConsecutiveZeros = 0;
923
+ }
924
+ dataOut.push(byte);
925
+ if (byte == 0) {
926
+ ++numConsecutiveZeros;
927
+ } else {
928
+ numConsecutiveZeros = 0;
929
+ }
930
+ }
931
+ return new Uint8Array(dataOut);
932
+ }
885
933
 
886
934
  class SifGuard {
887
935
  constructor() {
@@ -1045,8 +1093,9 @@ class FrameCryptor extends BaseFrameCryptor {
1045
1093
  const keyIndex = this.keys.getCurrentKeyIndex();
1046
1094
  if (encryptionKey) {
1047
1095
  const iv = this.makeIV((_a = encodedFrame.getMetadata().synchronizationSource) !== null && _a !== void 0 ? _a : -1, encodedFrame.timestamp);
1096
+ let frameInfo = this.getUnencryptedBytes(encodedFrame);
1048
1097
  // Thіs is not encrypted and contains the VP8 payload descriptor or the Opus TOC byte.
1049
- const frameHeader = new Uint8Array(encodedFrame.data, 0, this.getUnencryptedBytes(encodedFrame));
1098
+ const frameHeader = new Uint8Array(encodedFrame.data, 0, frameInfo.unencryptedBytes);
1050
1099
  // Frame trailer contains the R|IV_LENGTH and key index
1051
1100
  const frameTrailer = new Uint8Array(2);
1052
1101
  frameTrailer[0] = IV_LENGTH;
@@ -1063,14 +1112,18 @@ class FrameCryptor extends BaseFrameCryptor {
1063
1112
  name: ENCRYPTION_ALGORITHM,
1064
1113
  iv,
1065
1114
  additionalData: new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength)
1066
- }, encryptionKey, new Uint8Array(encodedFrame.data, this.getUnencryptedBytes(encodedFrame)));
1067
- const newData = new ArrayBuffer(frameHeader.byteLength + cipherText.byteLength + iv.byteLength + frameTrailer.byteLength);
1068
- const newUint8 = new Uint8Array(newData);
1069
- newUint8.set(frameHeader); // copy first bytes.
1070
- newUint8.set(new Uint8Array(cipherText), frameHeader.byteLength); // add ciphertext.
1071
- newUint8.set(new Uint8Array(iv), frameHeader.byteLength + cipherText.byteLength); // append IV.
1072
- newUint8.set(frameTrailer, frameHeader.byteLength + cipherText.byteLength + iv.byteLength); // append frame trailer.
1073
- encodedFrame.data = newData;
1115
+ }, encryptionKey, new Uint8Array(encodedFrame.data, frameInfo.unencryptedBytes));
1116
+ let newDataWithoutHeader = new Uint8Array(cipherText.byteLength + iv.byteLength + frameTrailer.byteLength);
1117
+ newDataWithoutHeader.set(new Uint8Array(cipherText)); // add ciphertext.
1118
+ newDataWithoutHeader.set(new Uint8Array(iv), cipherText.byteLength); // append IV.
1119
+ newDataWithoutHeader.set(frameTrailer, cipherText.byteLength + iv.byteLength); // append frame trailer.
1120
+ if (frameInfo.isH264) {
1121
+ newDataWithoutHeader = writeRbsp(newDataWithoutHeader);
1122
+ }
1123
+ var newData = new Uint8Array(frameHeader.byteLength + newDataWithoutHeader.byteLength);
1124
+ newData.set(frameHeader);
1125
+ newData.set(newDataWithoutHeader, frameHeader.byteLength);
1126
+ encodedFrame.data = newData.buffer;
1074
1127
  return controller.enqueue(encodedFrame);
1075
1128
  } catch (e) {
1076
1129
  // TODO: surface this to the app.
@@ -1150,6 +1203,7 @@ class FrameCryptor extends BaseFrameCryptor {
1150
1203
  if (!ratchetOpts.encryptionKey && !keySet) {
1151
1204
  throw new TypeError("no encryption key found for decryption of ".concat(this.participantIdentity));
1152
1205
  }
1206
+ let frameInfo = this.getUnencryptedBytes(encodedFrame);
1153
1207
  // Construct frame trailer. Similar to the frame header described in
1154
1208
  // https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2
1155
1209
  // but we put it at the end.
@@ -1158,7 +1212,15 @@ class FrameCryptor extends BaseFrameCryptor {
1158
1212
  // payload |IV...(length = IV_LENGTH)|R|IV_LENGTH|KID |
1159
1213
  // ---------+-------------------------+-+---------+----
1160
1214
  try {
1161
- const frameHeader = new Uint8Array(encodedFrame.data, 0, this.getUnencryptedBytes(encodedFrame));
1215
+ const frameHeader = new Uint8Array(encodedFrame.data, 0, frameInfo.unencryptedBytes);
1216
+ var encryptedData = new Uint8Array(encodedFrame.data, frameHeader.length, encodedFrame.data.byteLength - frameHeader.length);
1217
+ if (frameInfo.isH264 && needsRbspUnescaping(encryptedData)) {
1218
+ encryptedData = parseRbsp(encryptedData);
1219
+ const newUint8 = new Uint8Array(frameHeader.byteLength + encryptedData.byteLength);
1220
+ newUint8.set(frameHeader);
1221
+ newUint8.set(encryptedData, frameHeader.byteLength);
1222
+ encodedFrame.data = newUint8.buffer;
1223
+ }
1162
1224
  const frameTrailer = new Uint8Array(encodedFrame.data, encodedFrame.data.byteLength - 2, 2);
1163
1225
  const ivLength = frameTrailer[0];
1164
1226
  const iv = new Uint8Array(encodedFrame.data, encodedFrame.data.byteLength - ivLength - frameTrailer.byteLength, ivLength);
@@ -1253,26 +1315,32 @@ class FrameCryptor extends BaseFrameCryptor {
1253
1315
  }
1254
1316
  getUnencryptedBytes(frame) {
1255
1317
  var _a;
1318
+ var frameInfo = {
1319
+ unencryptedBytes: 0,
1320
+ isH264: false
1321
+ };
1256
1322
  if (isVideoFrame(frame)) {
1257
1323
  let detectedCodec = (_a = this.getVideoCodec(frame)) !== null && _a !== void 0 ? _a : this.videoCodec;
1258
1324
  if (detectedCodec === 'av1' || detectedCodec === 'vp9') {
1259
1325
  throw new Error("".concat(detectedCodec, " is not yet supported for end to end encryption"));
1260
1326
  }
1261
1327
  if (detectedCodec === 'vp8') {
1262
- return UNENCRYPTED_BYTES[frame.type];
1328
+ frameInfo.unencryptedBytes = UNENCRYPTED_BYTES[frame.type];
1329
+ return frameInfo;
1263
1330
  }
1264
1331
  const data = new Uint8Array(frame.data);
1265
1332
  try {
1266
1333
  const naluIndices = findNALUIndices(data);
1267
1334
  // if the detected codec is undefined we test whether it _looks_ like a h264 frame as a best guess
1268
- const isH264 = detectedCodec === 'h264' || naluIndices.some(naluIndex => [NALUType.SLICE_IDR, NALUType.SLICE_NON_IDR].includes(parseNALUType(data[naluIndex])));
1269
- if (isH264) {
1335
+ frameInfo.isH264 = detectedCodec === 'h264' || naluIndices.some(naluIndex => [NALUType.SLICE_IDR, NALUType.SLICE_NON_IDR].includes(parseNALUType(data[naluIndex])));
1336
+ if (frameInfo.isH264) {
1270
1337
  for (const index of naluIndices) {
1271
1338
  let type = parseNALUType(data[index]);
1272
1339
  switch (type) {
1273
1340
  case NALUType.SLICE_IDR:
1274
1341
  case NALUType.SLICE_NON_IDR:
1275
- return index + 2;
1342
+ frameInfo.unencryptedBytes = index + 2;
1343
+ return frameInfo;
1276
1344
  default:
1277
1345
  break;
1278
1346
  }
@@ -1282,9 +1350,11 @@ class FrameCryptor extends BaseFrameCryptor {
1282
1350
  } catch (e) {
1283
1351
  // no op, we just continue and fallback to vp8
1284
1352
  }
1285
- return UNENCRYPTED_BYTES[frame.type];
1353
+ frameInfo.unencryptedBytes = UNENCRYPTED_BYTES[frame.type];
1354
+ return frameInfo;
1286
1355
  } else {
1287
- return UNENCRYPTED_BYTES.audio;
1356
+ frameInfo.unencryptedBytes = UNENCRYPTED_BYTES.audio;
1357
+ return frameInfo;
1288
1358
  }
1289
1359
  }
1290
1360
  /**