livekit-client 1.12.3 → 1.13.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 +198 -107
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +515 -192
- 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/api/SignalClient.d.ts +2 -5
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/turn.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +9 -3
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/KeyProvider.d.ts +10 -7
- package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
- package/dist/src/e2ee/constants.d.ts +2 -0
- package/dist/src/e2ee/constants.d.ts.map +1 -1
- package/dist/src/e2ee/events.d.ts +34 -0
- package/dist/src/e2ee/events.d.ts.map +1 -0
- package/dist/src/e2ee/index.d.ts +1 -0
- package/dist/src/e2ee/index.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +23 -33
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +1 -0
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +18 -13
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +6 -8
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/e2ee/worker/SifGuard.d.ts +11 -0
- package/dist/src/e2ee/worker/SifGuard.d.ts.map +1 -0
- package/dist/src/options.d.ts +5 -0
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/proto/livekit_models_pb.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -1
- package/dist/src/room/DeviceManager.d.ts +1 -0
- package/dist/src/room/DeviceManager.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +1 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/defaults.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +5 -0
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +0 -5
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/timers.d.ts +2 -2
- package/dist/src/room/timers.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts +9 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +3 -3
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +6 -0
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/processor/types.d.ts +14 -2
- package/dist/src/room/track/processor/types.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +1 -1
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +2 -5
- package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +9 -3
- package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +10 -7
- package/dist/ts4.2/src/e2ee/constants.d.ts +2 -0
- package/dist/ts4.2/src/e2ee/events.d.ts +34 -0
- package/dist/ts4.2/src/e2ee/index.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/types.d.ts +23 -33
- package/dist/ts4.2/src/e2ee/utils.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +18 -13
- package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +6 -8
- package/dist/ts4.2/src/e2ee/worker/SifGuard.d.ts +11 -0
- package/dist/ts4.2/src/options.d.ts +5 -0
- package/dist/ts4.2/src/room/DeviceManager.d.ts +1 -0
- package/dist/ts4.2/src/room/Room.d.ts +1 -1
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/ts4.2/src/room/participant/Participant.d.ts +5 -0
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +0 -5
- package/dist/ts4.2/src/room/timers.d.ts +2 -2
- package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +9 -1
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -3
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +6 -0
- package/dist/ts4.2/src/room/track/processor/types.d.ts +14 -2
- package/dist/ts4.2/src/room/types.d.ts +1 -1
- package/package.json +15 -16
- package/src/api/SignalClient.ts +13 -9
- package/src/connectionHelper/checks/turn.ts +1 -0
- package/src/connectionHelper/checks/webrtc.ts +9 -7
- package/src/connectionHelper/checks/websocket.ts +1 -0
- package/src/e2ee/E2eeManager.ts +129 -76
- package/src/e2ee/KeyProvider.ts +31 -16
- package/src/e2ee/constants.ts +3 -0
- package/src/e2ee/events.ts +48 -0
- package/src/e2ee/index.ts +1 -0
- package/src/e2ee/types.ts +27 -41
- package/src/e2ee/utils.ts +9 -0
- package/src/e2ee/worker/FrameCryptor.ts +90 -47
- package/src/e2ee/worker/ParticipantKeyHandler.ts +25 -26
- package/src/e2ee/worker/SifGuard.ts +47 -0
- package/src/e2ee/worker/e2ee.worker.ts +75 -68
- package/src/options.ts +6 -0
- package/src/proto/livekit_models_pb.ts +14 -0
- package/src/proto/livekit_rtc_pb.ts +14 -0
- package/src/room/DeviceManager.ts +7 -2
- package/src/room/PCTransport.ts +12 -2
- package/src/room/RTCEngine.ts +3 -2
- package/src/room/Room.ts +47 -22
- package/src/room/defaults.ts +1 -0
- package/src/room/participant/LocalParticipant.ts +18 -2
- package/src/room/participant/Participant.ts +16 -0
- package/src/room/participant/RemoteParticipant.ts +0 -12
- package/src/room/track/LocalAudioTrack.ts +45 -0
- package/src/room/track/LocalTrack.ts +22 -14
- package/src/room/track/LocalVideoTrack.ts +39 -0
- package/src/room/track/RemoteAudioTrack.ts +9 -1
- package/src/room/track/RemoteTrackPublication.ts +2 -2
- package/src/room/track/facingMode.ts +1 -1
- package/src/room/track/processor/types.ts +18 -2
- package/src/room/types.ts +5 -1
@@ -341,6 +341,8 @@ const KEY_PROVIDER_DEFAULTS = {
|
|
341
341
|
ratchetWindowSize: 8,
|
342
342
|
failureTolerance: DECRYPTION_FAILURE_TOLERANCE
|
343
343
|
};
|
344
|
+
const MAX_SIF_COUNT = 100;
|
345
|
+
const MAX_SIF_DURATION = 2000;
|
344
346
|
|
345
347
|
class LivekitError extends Error {
|
346
348
|
constructor(code, message) {
|
@@ -390,6 +392,26 @@ class CryptorError extends LivekitError {
|
|
390
392
|
}
|
391
393
|
}
|
392
394
|
|
395
|
+
var KeyProviderEvent;
|
396
|
+
(function (KeyProviderEvent) {
|
397
|
+
KeyProviderEvent["SetKey"] = "setKey";
|
398
|
+
KeyProviderEvent["RatchetRequest"] = "ratchetRequest";
|
399
|
+
KeyProviderEvent["KeyRatcheted"] = "keyRatcheted";
|
400
|
+
})(KeyProviderEvent || (KeyProviderEvent = {}));
|
401
|
+
var KeyHandlerEvent;
|
402
|
+
(function (KeyHandlerEvent) {
|
403
|
+
KeyHandlerEvent["KeyRatcheted"] = "keyRatcheted";
|
404
|
+
})(KeyHandlerEvent || (KeyHandlerEvent = {}));
|
405
|
+
var EncryptionEvent;
|
406
|
+
(function (EncryptionEvent) {
|
407
|
+
EncryptionEvent["ParticipantEncryptionStatusChanged"] = "participantEncryptionStatusChanged";
|
408
|
+
EncryptionEvent["EncryptionError"] = "encryptionError";
|
409
|
+
})(EncryptionEvent || (EncryptionEvent = {}));
|
410
|
+
var CryptorEvent;
|
411
|
+
(function (CryptorEvent) {
|
412
|
+
CryptorEvent["Error"] = "cryptorError";
|
413
|
+
})(CryptorEvent || (CryptorEvent = {}));
|
414
|
+
|
393
415
|
var events = {exports: {}};
|
394
416
|
|
395
417
|
var R = typeof Reflect === 'object' ? Reflect : null;
|
@@ -771,10 +793,6 @@ function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
|
|
771
793
|
}
|
772
794
|
var eventsExports = events.exports;
|
773
795
|
|
774
|
-
const CryptorEvent = {
|
775
|
-
Error: 'cryptorError'
|
776
|
-
};
|
777
|
-
|
778
796
|
var AudioPresets;
|
779
797
|
(function (AudioPresets) {
|
780
798
|
AudioPresets.telephone = {
|
@@ -865,6 +883,43 @@ function ratchet(material, salt) {
|
|
865
883
|
});
|
866
884
|
}
|
867
885
|
|
886
|
+
class SifGuard {
|
887
|
+
constructor() {
|
888
|
+
this.consecutiveSifCount = 0;
|
889
|
+
this.lastSifReceivedAt = 0;
|
890
|
+
this.userFramesSinceSif = 0;
|
891
|
+
}
|
892
|
+
recordSif() {
|
893
|
+
var _a;
|
894
|
+
this.consecutiveSifCount += 1;
|
895
|
+
(_a = this.sifSequenceStartedAt) !== null && _a !== void 0 ? _a : this.sifSequenceStartedAt = Date.now();
|
896
|
+
this.lastSifReceivedAt = Date.now();
|
897
|
+
}
|
898
|
+
recordUserFrame() {
|
899
|
+
if (this.sifSequenceStartedAt === undefined) {
|
900
|
+
return;
|
901
|
+
} else {
|
902
|
+
this.userFramesSinceSif += 1;
|
903
|
+
}
|
904
|
+
if (
|
905
|
+
// reset if we received more user frames than SIFs
|
906
|
+
this.userFramesSinceSif > this.consecutiveSifCount ||
|
907
|
+
// also reset if we got a new user frame and the latest SIF frame hasn't been updated in a while
|
908
|
+
Date.now() - this.lastSifReceivedAt > MAX_SIF_DURATION) {
|
909
|
+
this.reset();
|
910
|
+
}
|
911
|
+
}
|
912
|
+
isSifAllowed() {
|
913
|
+
return this.consecutiveSifCount < MAX_SIF_COUNT && (this.sifSequenceStartedAt === undefined || Date.now() - this.sifSequenceStartedAt < MAX_SIF_DURATION);
|
914
|
+
}
|
915
|
+
reset() {
|
916
|
+
this.userFramesSinceSif = 0;
|
917
|
+
this.consecutiveSifCount = 0;
|
918
|
+
this.sifSequenceStartedAt = undefined;
|
919
|
+
}
|
920
|
+
}
|
921
|
+
|
922
|
+
const encryptionEnabledMap = new Map();
|
868
923
|
class BaseFrameCryptor extends eventsExports.EventEmitter {
|
869
924
|
encodeFunction(encodedFrame, controller) {
|
870
925
|
throw Error('not implemented for subclass');
|
@@ -883,10 +938,11 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
883
938
|
super();
|
884
939
|
this.sendCounts = new Map();
|
885
940
|
this.keys = opts.keys;
|
886
|
-
this.
|
941
|
+
this.participantIdentity = opts.participantIdentity;
|
887
942
|
this.rtpMap = new Map();
|
888
943
|
this.keyProviderOptions = opts.keyProviderOptions;
|
889
|
-
this.
|
944
|
+
this.sifTrailer = (_a = opts.sifTrailer) !== null && _a !== void 0 ? _a : Uint8Array.from([]);
|
945
|
+
this.sifGuard = new SifGuard();
|
890
946
|
}
|
891
947
|
/**
|
892
948
|
* Assign a different participant to the cryptor.
|
@@ -895,14 +951,22 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
895
951
|
* @param keys
|
896
952
|
*/
|
897
953
|
setParticipant(id, keys) {
|
898
|
-
this.
|
954
|
+
this.participantIdentity = id;
|
899
955
|
this.keys = keys;
|
956
|
+
this.sifGuard.reset();
|
900
957
|
}
|
901
958
|
unsetParticipant() {
|
902
|
-
this.
|
959
|
+
this.participantIdentity = undefined;
|
903
960
|
}
|
904
|
-
|
905
|
-
|
961
|
+
isEnabled() {
|
962
|
+
if (this.participantIdentity) {
|
963
|
+
return encryptionEnabledMap.get(this.participantIdentity);
|
964
|
+
} else {
|
965
|
+
return undefined;
|
966
|
+
}
|
967
|
+
}
|
968
|
+
getParticipantIdentity() {
|
969
|
+
return this.participantIdentity;
|
906
970
|
}
|
907
971
|
getTrackId() {
|
908
972
|
return this.trackId;
|
@@ -923,7 +987,9 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
923
987
|
}
|
924
988
|
setupTransform(operation, readable, writable, trackId, codec) {
|
925
989
|
if (codec) {
|
926
|
-
|
990
|
+
workerLogger.info('setting codec on cryptor to', {
|
991
|
+
codec
|
992
|
+
});
|
927
993
|
this.videoCodec = codec;
|
928
994
|
}
|
929
995
|
const transformFn = operation === 'encode' ? this.encodeFunction : this.decodeFunction;
|
@@ -931,11 +997,14 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
931
997
|
transform: transformFn.bind(this)
|
932
998
|
});
|
933
999
|
readable.pipeThrough(transformStream).pipeTo(writable).catch(e => {
|
934
|
-
|
935
|
-
this.emit(
|
1000
|
+
workerLogger.warn(e);
|
1001
|
+
this.emit(CryptorEvent.Error, e instanceof CryptorError ? e : new CryptorError(e.message));
|
936
1002
|
});
|
937
1003
|
this.trackId = trackId;
|
938
1004
|
}
|
1005
|
+
setSifTrailer(trailer) {
|
1006
|
+
this.sifTrailer = trailer;
|
1007
|
+
}
|
939
1008
|
/**
|
940
1009
|
* Function that will be injected in a stream and will encrypt the given encoded frames.
|
941
1010
|
*
|
@@ -961,14 +1030,18 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
961
1030
|
encodeFunction(encodedFrame, controller) {
|
962
1031
|
var _a;
|
963
1032
|
return __awaiter(this, void 0, void 0, function* () {
|
964
|
-
if (!this.
|
1033
|
+
if (!this.isEnabled() ||
|
965
1034
|
// skip for encryption for empty dtx frames
|
966
1035
|
encodedFrame.data.byteLength === 0) {
|
967
1036
|
return controller.enqueue(encodedFrame);
|
968
1037
|
}
|
1038
|
+
const keySet = this.keys.getKeySet();
|
1039
|
+
if (!keySet) {
|
1040
|
+
throw new TypeError("key set not found for ".concat(this.participantIdentity, " at index ").concat(this.keys.getCurrentKeyIndex()));
|
1041
|
+
}
|
969
1042
|
const {
|
970
1043
|
encryptionKey
|
971
|
-
} =
|
1044
|
+
} = keySet;
|
972
1045
|
const keyIndex = this.keys.getCurrentKeyIndex();
|
973
1046
|
if (encryptionKey) {
|
974
1047
|
const iv = this.makeIV((_a = encodedFrame.getMetadata().synchronizationSource) !== null && _a !== void 0 ? _a : -1, encodedFrame.timestamp);
|
@@ -1016,13 +1089,23 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
1016
1089
|
*/
|
1017
1090
|
decodeFunction(encodedFrame, controller) {
|
1018
1091
|
return __awaiter(this, void 0, void 0, function* () {
|
1019
|
-
if (!this.
|
1092
|
+
if (!this.isEnabled() ||
|
1020
1093
|
// skip for decryption for empty dtx frames
|
1021
|
-
encodedFrame.data.byteLength === 0
|
1022
|
-
|
1023
|
-
isFrameServerInjected(encodedFrame.data, this.unencryptedFrameByteTrailer)) {
|
1094
|
+
encodedFrame.data.byteLength === 0) {
|
1095
|
+
this.sifGuard.recordUserFrame();
|
1024
1096
|
return controller.enqueue(encodedFrame);
|
1025
1097
|
}
|
1098
|
+
if (isFrameServerInjected(encodedFrame.data, this.sifTrailer)) {
|
1099
|
+
this.sifGuard.recordSif();
|
1100
|
+
if (this.sifGuard.isSifAllowed()) {
|
1101
|
+
return controller.enqueue(encodedFrame);
|
1102
|
+
} else {
|
1103
|
+
workerLogger.warn('SIF limit reached, dropping frame');
|
1104
|
+
return;
|
1105
|
+
}
|
1106
|
+
} else {
|
1107
|
+
this.sifGuard.recordUserFrame();
|
1108
|
+
}
|
1026
1109
|
const data = new Uint8Array(encodedFrame.data);
|
1027
1110
|
const keyIndex = data[encodedFrame.data.byteLength - 1];
|
1028
1111
|
if (this.keys.getKeySet(keyIndex) && this.keys.hasValidKey) {
|
@@ -1035,8 +1118,7 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
1035
1118
|
} catch (error) {
|
1036
1119
|
if (error instanceof CryptorError && error.reason === CryptorErrorReason.InvalidKey) {
|
1037
1120
|
if (this.keys.hasValidKey) {
|
1038
|
-
|
1039
|
-
this.emit(CryptorEvent.Error, new CryptorError("invalid key for participant ".concat(this.participantId), CryptorErrorReason.InvalidKey));
|
1121
|
+
this.emit(CryptorEvent.Error, new CryptorError("invalid key for participant ".concat(this.participantIdentity), CryptorErrorReason.InvalidKey));
|
1040
1122
|
this.keys.decryptionFailure();
|
1041
1123
|
}
|
1042
1124
|
} else {
|
@@ -1045,8 +1127,11 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
1045
1127
|
});
|
1046
1128
|
}
|
1047
1129
|
}
|
1130
|
+
} else if (!this.keys.getKeySet(keyIndex) && this.keys.hasValidKey) {
|
1131
|
+
// emit an error in case the key index is out of bounds but the key handler thinks we still have a valid key
|
1132
|
+
workerLogger.warn('skipping decryption due to missing key at index');
|
1133
|
+
this.emit(CryptorEvent.Error, new CryptorError("missing key at index for participant ".concat(this.participantIdentity), CryptorErrorReason.MissingKey));
|
1048
1134
|
}
|
1049
|
-
return controller.enqueue(encodedFrame);
|
1050
1135
|
});
|
1051
1136
|
}
|
1052
1137
|
/**
|
@@ -1061,6 +1146,9 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
1061
1146
|
var _a;
|
1062
1147
|
return __awaiter(this, void 0, void 0, function* () {
|
1063
1148
|
const keySet = this.keys.getKeySet(keyIndex);
|
1149
|
+
if (!ratchetOpts.encryptionKey && !keySet) {
|
1150
|
+
throw new TypeError("no encryption key found for decryption of ".concat(this.participantIdentity));
|
1151
|
+
}
|
1064
1152
|
// Construct frame trailer. Similar to the frame header described in
|
1065
1153
|
// https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2
|
1066
1154
|
// but we put it at the end.
|
@@ -1118,8 +1206,8 @@ class FrameCryptor extends BaseFrameCryptor {
|
|
1118
1206
|
workerLogger.debug('resetting to initial material');
|
1119
1207
|
this.keys.setKeyFromMaterial(initialMaterial.material, keyIndex);
|
1120
1208
|
}
|
1121
|
-
workerLogger.warn('maximum ratchet attempts exceeded
|
1122
|
-
|
1209
|
+
workerLogger.warn('maximum ratchet attempts exceeded');
|
1210
|
+
throw new CryptorError("valid key missing for participant ".concat(this.participantIdentity), CryptorErrorReason.InvalidKey);
|
1123
1211
|
}
|
1124
1212
|
} else {
|
1125
1213
|
throw new CryptorError('Decryption failed, most likely because of an invalid key', CryptorErrorReason.InvalidKey);
|
@@ -1291,6 +1379,9 @@ var NALUType;
|
|
1291
1379
|
* @internal
|
1292
1380
|
*/
|
1293
1381
|
function isFrameServerInjected(frameData, trailerBytes) {
|
1382
|
+
if (trailerBytes.byteLength === 0) {
|
1383
|
+
return false;
|
1384
|
+
}
|
1294
1385
|
const frameTrailer = new Uint8Array(frameData.slice(frameData.byteLength - trailerBytes.byteLength));
|
1295
1386
|
return trailerBytes.every((value, index) => value === frameTrailer[index]);
|
1296
1387
|
}
|
@@ -1309,27 +1400,24 @@ class ParticipantKeyHandler extends eventsExports.EventEmitter {
|
|
1309
1400
|
get hasValidKey() {
|
1310
1401
|
return this._hasValidKey;
|
1311
1402
|
}
|
1312
|
-
constructor(
|
1403
|
+
constructor(participantIdentity, keyProviderOptions) {
|
1313
1404
|
super();
|
1314
1405
|
this.decryptionFailureCount = 0;
|
1315
1406
|
this._hasValidKey = true;
|
1316
1407
|
this.currentKeyIndex = 0;
|
1317
|
-
this.cryptoKeyRing = new Array(KEYRING_SIZE);
|
1318
|
-
this.enabled = isEnabled;
|
1408
|
+
this.cryptoKeyRing = new Array(KEYRING_SIZE).fill(undefined);
|
1319
1409
|
this.keyProviderOptions = keyProviderOptions;
|
1320
1410
|
this.ratchetPromiseMap = new Map();
|
1321
|
-
this.
|
1411
|
+
this.participantIdentity = participantIdentity;
|
1322
1412
|
this.resetKeyStatus();
|
1323
1413
|
}
|
1324
|
-
setEnabled(enabled) {
|
1325
|
-
this.enabled = enabled;
|
1326
|
-
}
|
1327
1414
|
decryptionFailure() {
|
1328
1415
|
if (this.keyProviderOptions.failureTolerance < 0) {
|
1329
1416
|
return;
|
1330
1417
|
}
|
1331
1418
|
this.decryptionFailureCount += 1;
|
1332
1419
|
if (this.decryptionFailureCount > this.keyProviderOptions.failureTolerance) {
|
1420
|
+
workerLogger.warn("key for ".concat(this.participantIdentity, " is being marked as invalid"));
|
1333
1421
|
this._hasValidKey = false;
|
1334
1422
|
}
|
1335
1423
|
}
|
@@ -1353,19 +1441,23 @@ class ParticipantKeyHandler extends eventsExports.EventEmitter {
|
|
1353
1441
|
*/
|
1354
1442
|
ratchetKey(keyIndex) {
|
1355
1443
|
let setKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
1356
|
-
const currentKeyIndex = keyIndex !== null && keyIndex !== void 0 ? keyIndex :
|
1444
|
+
const currentKeyIndex = keyIndex !== null && keyIndex !== void 0 ? keyIndex : this.getCurrentKeyIndex();
|
1357
1445
|
const existingPromise = this.ratchetPromiseMap.get(currentKeyIndex);
|
1358
1446
|
if (typeof existingPromise !== 'undefined') {
|
1359
1447
|
return existingPromise;
|
1360
1448
|
}
|
1361
1449
|
const ratchetPromise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
1362
1450
|
try {
|
1363
|
-
const
|
1451
|
+
const keySet = this.getKeySet(currentKeyIndex);
|
1452
|
+
if (!keySet) {
|
1453
|
+
throw new TypeError("Cannot ratchet key without a valid keyset of participant ".concat(this.participantIdentity));
|
1454
|
+
}
|
1455
|
+
const currentMaterial = keySet.material;
|
1364
1456
|
const newMaterial = yield importKey(yield ratchet(currentMaterial, this.keyProviderOptions.ratchetSalt), currentMaterial.algorithm.name, 'derive');
|
1365
1457
|
if (setKey) {
|
1366
1458
|
this.setKeyFromMaterial(newMaterial, currentKeyIndex, true);
|
1459
|
+
this.emit(KeyHandlerEvent.KeyRatcheted, newMaterial, this.participantIdentity, currentKeyIndex);
|
1367
1460
|
}
|
1368
|
-
this.emit('keyRatcheted', newMaterial, keyIndex, this.participantId);
|
1369
1461
|
resolve(newMaterial);
|
1370
1462
|
} catch (e) {
|
1371
1463
|
reject(e);
|
@@ -1409,12 +1501,10 @@ class ParticipantKeyHandler extends eventsExports.EventEmitter {
|
|
1409
1501
|
}
|
1410
1502
|
setKeySet(keySet, keyIndex) {
|
1411
1503
|
let emitRatchetEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
}
|
1417
|
-
});
|
1504
|
+
this.cryptoKeyRing[keyIndex % this.cryptoKeyRing.length] = keySet;
|
1505
|
+
if (emitRatchetEvent) {
|
1506
|
+
this.emit(KeyHandlerEvent.KeyRatcheted, keySet.material, this.participantIdentity, keyIndex);
|
1507
|
+
}
|
1418
1508
|
}
|
1419
1509
|
setCurrentKeyIndex(index) {
|
1420
1510
|
return __awaiter(this, void 0, void 0, function* () {
|
@@ -1422,9 +1512,6 @@ class ParticipantKeyHandler extends eventsExports.EventEmitter {
|
|
1422
1512
|
this.resetKeyStatus();
|
1423
1513
|
});
|
1424
1514
|
}
|
1425
|
-
isEnabled() {
|
1426
|
-
return this.enabled;
|
1427
|
-
}
|
1428
1515
|
getCurrentKeyIndex() {
|
1429
1516
|
return this.currentKeyIndex;
|
1430
1517
|
}
|
@@ -1440,11 +1527,11 @@ class ParticipantKeyHandler extends eventsExports.EventEmitter {
|
|
1440
1527
|
|
1441
1528
|
const participantCryptors = [];
|
1442
1529
|
const participantKeys = new Map();
|
1443
|
-
let
|
1444
|
-
let publisherKeys;
|
1530
|
+
let sharedKeyHandler;
|
1445
1531
|
let isEncryptionEnabled = false;
|
1446
1532
|
let useSharedKey = false;
|
1447
1533
|
let sharedKey;
|
1534
|
+
let sifTrailer;
|
1448
1535
|
let keyProviderOptions = KEY_PROVIDER_DEFAULTS;
|
1449
1536
|
workerLogger.setDefaultLevel('info');
|
1450
1537
|
onmessage = ev => {
|
@@ -1458,36 +1545,35 @@ onmessage = ev => {
|
|
1458
1545
|
keyProviderOptions = data.keyProviderOptions;
|
1459
1546
|
useSharedKey = !!data.keyProviderOptions.sharedKey;
|
1460
1547
|
// acknowledge init successful
|
1461
|
-
const
|
1462
|
-
kind: '
|
1548
|
+
const ackMsg = {
|
1549
|
+
kind: 'initAck',
|
1463
1550
|
data: {
|
1464
1551
|
enabled: isEncryptionEnabled
|
1465
1552
|
}
|
1466
1553
|
};
|
1467
|
-
|
1468
|
-
publisherKeys.on('keyRatcheted', emitRatchetedKeys);
|
1469
|
-
postMessage(enableMsg);
|
1554
|
+
postMessage(ackMsg);
|
1470
1555
|
break;
|
1471
1556
|
case 'enable':
|
1472
|
-
setEncryptionEnabled(data.enabled, data.
|
1557
|
+
setEncryptionEnabled(data.enabled, data.participantIdentity);
|
1473
1558
|
workerLogger.info('updated e2ee enabled status');
|
1474
1559
|
// acknowledge enable call successful
|
1475
1560
|
postMessage(ev.data);
|
1476
1561
|
break;
|
1477
1562
|
case 'decode':
|
1478
|
-
let cryptor = getTrackCryptor(data.
|
1563
|
+
let cryptor = getTrackCryptor(data.participantIdentity, data.trackId);
|
1479
1564
|
cryptor.setupTransform(kind, data.readableStream, data.writableStream, data.trackId, data.codec);
|
1480
1565
|
break;
|
1481
1566
|
case 'encode':
|
1482
|
-
let pubCryptor =
|
1567
|
+
let pubCryptor = getTrackCryptor(data.participantIdentity, data.trackId);
|
1483
1568
|
pubCryptor.setupTransform(kind, data.readableStream, data.writableStream, data.trackId, data.codec);
|
1484
1569
|
break;
|
1485
1570
|
case 'setKey':
|
1486
1571
|
if (useSharedKey) {
|
1487
|
-
workerLogger.
|
1572
|
+
workerLogger.warn('set shared key');
|
1488
1573
|
setSharedKey(data.key, data.keyIndex);
|
1489
|
-
} else if (data.
|
1490
|
-
|
1574
|
+
} else if (data.participantIdentity) {
|
1575
|
+
workerLogger.warn("set participant sender key ".concat(data.participantIdentity));
|
1576
|
+
getParticipantKeyHandler(data.participantIdentity).setKey(data.key, data.keyIndex);
|
1491
1577
|
} else {
|
1492
1578
|
workerLogger.error('no participant Id was provided and shared key usage is disabled');
|
1493
1579
|
}
|
@@ -1496,98 +1582,97 @@ onmessage = ev => {
|
|
1496
1582
|
unsetCryptorParticipant(data.trackId);
|
1497
1583
|
break;
|
1498
1584
|
case 'updateCodec':
|
1499
|
-
getTrackCryptor(data.
|
1585
|
+
getTrackCryptor(data.participantIdentity, data.trackId).setVideoCodec(data.codec);
|
1500
1586
|
break;
|
1501
1587
|
case 'setRTPMap':
|
1502
|
-
|
1503
|
-
|
1588
|
+
// this is only used for the local participant
|
1589
|
+
participantCryptors.forEach(cr => {
|
1590
|
+
if (cr.getParticipantIdentity() === data.participantIdentity) {
|
1591
|
+
cr.setRtpMap(data.map);
|
1592
|
+
}
|
1504
1593
|
});
|
1505
1594
|
break;
|
1506
1595
|
case 'ratchetRequest':
|
1507
1596
|
handleRatchetRequest(data);
|
1597
|
+
break;
|
1598
|
+
case 'setSifTrailer':
|
1599
|
+
handleSifTrailer(data.trailer);
|
1600
|
+
break;
|
1508
1601
|
}
|
1509
1602
|
};
|
1510
1603
|
function handleRatchetRequest(data) {
|
1511
1604
|
return __awaiter(this, void 0, void 0, function* () {
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1605
|
+
if (useSharedKey) {
|
1606
|
+
const keyHandler = getSharedKeyHandler();
|
1607
|
+
yield keyHandler.ratchetKey(data.keyIndex);
|
1608
|
+
keyHandler.resetKeyStatus();
|
1609
|
+
} else if (data.participantIdentity) {
|
1610
|
+
const keyHandler = getParticipantKeyHandler(data.participantIdentity);
|
1611
|
+
yield keyHandler.ratchetKey(data.keyIndex);
|
1612
|
+
keyHandler.resetKeyStatus();
|
1613
|
+
} else {
|
1614
|
+
workerLogger.error('no participant Id was provided for ratchet request and shared key usage is disabled');
|
1615
|
+
}
|
1515
1616
|
});
|
1516
1617
|
}
|
1517
|
-
function getTrackCryptor(
|
1618
|
+
function getTrackCryptor(participantIdentity, trackId) {
|
1518
1619
|
let cryptor = participantCryptors.find(c => c.getTrackId() === trackId);
|
1519
1620
|
if (!cryptor) {
|
1520
1621
|
workerLogger.info('creating new cryptor for', {
|
1521
|
-
|
1622
|
+
participantIdentity
|
1522
1623
|
});
|
1523
1624
|
if (!keyProviderOptions) {
|
1524
1625
|
throw Error('Missing keyProvider options');
|
1525
1626
|
}
|
1526
1627
|
cryptor = new FrameCryptor({
|
1527
|
-
|
1528
|
-
keys: getParticipantKeyHandler(
|
1529
|
-
keyProviderOptions
|
1628
|
+
participantIdentity,
|
1629
|
+
keys: getParticipantKeyHandler(participantIdentity),
|
1630
|
+
keyProviderOptions,
|
1631
|
+
sifTrailer
|
1530
1632
|
});
|
1531
1633
|
setupCryptorErrorEvents(cryptor);
|
1532
1634
|
participantCryptors.push(cryptor);
|
1533
|
-
} else if (
|
1635
|
+
} else if (participantIdentity !== cryptor.getParticipantIdentity()) {
|
1534
1636
|
// assign new participant id to track cryptor and pass in correct key handler
|
1535
|
-
cryptor.setParticipant(
|
1637
|
+
cryptor.setParticipant(participantIdentity, getParticipantKeyHandler(participantIdentity));
|
1536
1638
|
}
|
1537
1639
|
return cryptor;
|
1538
1640
|
}
|
1539
|
-
function getParticipantKeyHandler(
|
1540
|
-
if (
|
1541
|
-
return
|
1641
|
+
function getParticipantKeyHandler(participantIdentity) {
|
1642
|
+
if (useSharedKey) {
|
1643
|
+
return getSharedKeyHandler();
|
1542
1644
|
}
|
1543
|
-
let keys = participantKeys.get(
|
1645
|
+
let keys = participantKeys.get(participantIdentity);
|
1544
1646
|
if (!keys) {
|
1545
|
-
keys = new ParticipantKeyHandler(
|
1647
|
+
keys = new ParticipantKeyHandler(participantIdentity, keyProviderOptions);
|
1546
1648
|
if (sharedKey) {
|
1547
1649
|
keys.setKey(sharedKey);
|
1548
1650
|
}
|
1549
|
-
|
1651
|
+
keys.on(KeyHandlerEvent.KeyRatcheted, emitRatchetedKeys);
|
1652
|
+
participantKeys.set(participantIdentity, keys);
|
1550
1653
|
}
|
1551
1654
|
return keys;
|
1552
1655
|
}
|
1656
|
+
function getSharedKeyHandler() {
|
1657
|
+
if (!sharedKeyHandler) {
|
1658
|
+
sharedKeyHandler = new ParticipantKeyHandler('shared-key', keyProviderOptions);
|
1659
|
+
}
|
1660
|
+
return sharedKeyHandler;
|
1661
|
+
}
|
1553
1662
|
function unsetCryptorParticipant(trackId) {
|
1554
1663
|
var _a;
|
1555
1664
|
(_a = participantCryptors.find(c => c.getTrackId() === trackId)) === null || _a === void 0 ? void 0 : _a.unsetParticipant();
|
1556
1665
|
}
|
1557
|
-
function
|
1558
|
-
|
1559
|
-
if (!publishCryptor) {
|
1560
|
-
if (!keyProviderOptions) {
|
1561
|
-
throw new TypeError('Missing keyProvider options');
|
1562
|
-
}
|
1563
|
-
publishCryptor = new FrameCryptor({
|
1564
|
-
keys: publisherKeys,
|
1565
|
-
participantId: 'publisher',
|
1566
|
-
keyProviderOptions
|
1567
|
-
});
|
1568
|
-
setupCryptorErrorEvents(publishCryptor);
|
1569
|
-
publishCryptors.push(publishCryptor);
|
1570
|
-
}
|
1571
|
-
return publishCryptor;
|
1572
|
-
}
|
1573
|
-
function setEncryptionEnabled(enable, participantId) {
|
1574
|
-
if (!participantId) {
|
1575
|
-
isEncryptionEnabled = enable;
|
1576
|
-
publisherKeys.setEnabled(enable);
|
1577
|
-
} else {
|
1578
|
-
getParticipantKeyHandler(participantId).setEnabled(enable);
|
1579
|
-
}
|
1666
|
+
function setEncryptionEnabled(enable, participantIdentity) {
|
1667
|
+
encryptionEnabledMap.set(participantIdentity, enable);
|
1580
1668
|
}
|
1581
1669
|
function setSharedKey(key, index) {
|
1582
1670
|
workerLogger.debug('setting shared key');
|
1583
1671
|
sharedKey = key;
|
1584
|
-
|
1585
|
-
for (const [, keyHandler] of participantKeys) {
|
1586
|
-
keyHandler.setKey(key, index);
|
1587
|
-
}
|
1672
|
+
getSharedKeyHandler().setKey(key, index);
|
1588
1673
|
}
|
1589
1674
|
function setupCryptorErrorEvents(cryptor) {
|
1590
|
-
cryptor.on(
|
1675
|
+
cryptor.on(CryptorEvent.Error, error => {
|
1591
1676
|
const msg = {
|
1592
1677
|
kind: 'error',
|
1593
1678
|
data: {
|
@@ -1597,17 +1682,23 @@ function setupCryptorErrorEvents(cryptor) {
|
|
1597
1682
|
postMessage(msg);
|
1598
1683
|
});
|
1599
1684
|
}
|
1600
|
-
function emitRatchetedKeys(material, keyIndex) {
|
1685
|
+
function emitRatchetedKeys(material, participantIdentity, keyIndex) {
|
1601
1686
|
const msg = {
|
1602
1687
|
kind: "ratchetKey",
|
1603
1688
|
data: {
|
1604
|
-
|
1689
|
+
participantIdentity,
|
1605
1690
|
keyIndex,
|
1606
1691
|
material
|
1607
1692
|
}
|
1608
1693
|
};
|
1609
1694
|
postMessage(msg);
|
1610
1695
|
}
|
1696
|
+
function handleSifTrailer(trailer) {
|
1697
|
+
sifTrailer = trailer;
|
1698
|
+
participantCryptors.forEach(c => {
|
1699
|
+
c.setSifTrailer(trailer);
|
1700
|
+
});
|
1701
|
+
}
|
1611
1702
|
// Operations using RTCRtpScriptTransform.
|
1612
1703
|
// @ts-ignore
|
1613
1704
|
if (self.RTCTransformEvent) {
|
@@ -1619,11 +1710,11 @@ if (self.RTCTransformEvent) {
|
|
1619
1710
|
transformer.handled = true;
|
1620
1711
|
const {
|
1621
1712
|
kind,
|
1622
|
-
|
1713
|
+
participantIdentity,
|
1623
1714
|
trackId,
|
1624
1715
|
codec
|
1625
1716
|
} = transformer.options;
|
1626
|
-
const cryptor =
|
1717
|
+
const cryptor = getTrackCryptor(participantIdentity, trackId);
|
1627
1718
|
workerLogger.debug('transform', {
|
1628
1719
|
codec
|
1629
1720
|
});
|