You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

KeyHandler.js 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { getLogger } from '@jitsi/logger';
  2. import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
  3. import RTCEvents from '../../service/RTC/RTCEvents';
  4. import browser from '../browser';
  5. import Deferred from '../util/Deferred';
  6. import Listenable from '../util/Listenable';
  7. import E2EEContext from './E2EEContext';
  8. const logger = getLogger(__filename);
  9. /**
  10. * Abstract class that integrates {@link E2EEContext} with a key management system.
  11. */
  12. export class KeyHandler extends Listenable {
  13. /**
  14. * Build a new KeyHandler instance, which will be used in a given conference.
  15. * @param {JitsiConference} conference - the current conference.
  16. * @param {object} options - the options passed to {E2EEContext}, see implemention.
  17. */
  18. constructor(conference, options = {}) {
  19. super();
  20. this.conference = conference;
  21. this.e2eeCtx = new E2EEContext(options);
  22. this.enabled = false;
  23. this._enabling = undefined;
  24. // Conference media events in order to attach the encryptor / decryptor.
  25. // FIXME add events to TraceablePeerConnection which will allow to see when there's new receiver or sender
  26. // added instead of shenanigans around conference track events and track muted.
  27. //
  28. this.conference.on(
  29. JitsiConferenceEvents._MEDIA_SESSION_STARTED,
  30. this._onMediaSessionStarted.bind(this));
  31. this.conference.on(
  32. JitsiConferenceEvents.TRACK_ADDED,
  33. track => track.isLocal() && this._onLocalTrackAdded(track));
  34. this.conference.rtc.on(
  35. RTCEvents.REMOTE_TRACK_ADDED,
  36. (track, tpc) => this._setupReceiverE2EEForTrack(tpc, track));
  37. this.conference.on(
  38. JitsiConferenceEvents.TRACK_MUTE_CHANGED,
  39. this._trackMuteChanged.bind(this));
  40. }
  41. /**
  42. * Indicates whether E2EE is currently enabled or not.
  43. *
  44. * @returns {boolean}
  45. */
  46. isEnabled() {
  47. return this.enabled;
  48. }
  49. /**
  50. * Enables / disables End-To-End encryption.
  51. *
  52. * @param {boolean} enabled - whether E2EE should be enabled or not.
  53. * @returns {void}
  54. */
  55. async setEnabled(enabled) {
  56. this._enabling && await this._enabling;
  57. if (enabled === this.enabled) {
  58. return;
  59. }
  60. this._enabling = new Deferred();
  61. this.enabled = enabled;
  62. if (!enabled) {
  63. this.e2eeCtx.cleanupAll();
  64. }
  65. this._setEnabled && await this._setEnabled(enabled);
  66. this.conference.setLocalParticipantProperty('e2ee.enabled', enabled);
  67. this.conference._restartMediaSessions();
  68. this._enabling.resolve();
  69. }
  70. /**
  71. * Sets the key for End-to-End encryption.
  72. *
  73. * @returns {void}
  74. */
  75. setEncryptionKey() {
  76. throw new Error('Not implemented by subclass');
  77. }
  78. /**
  79. * Setup E2EE on the new track that has been added to the conference, apply it on all the open peerconnections.
  80. * @param {JitsiLocalTrack} track - the new track that's being added to the conference.
  81. * @private
  82. */
  83. _onLocalTrackAdded(track) {
  84. for (const session of this.conference.getMediaSessions()) {
  85. this._setupSenderE2EEForTrack(session, track);
  86. }
  87. }
  88. /**
  89. * Setups E2E encryption for the new session.
  90. * @param {JingleSessionPC} session - the new media session.
  91. * @private
  92. */
  93. _onMediaSessionStarted(session) {
  94. const localTracks = this.conference.getLocalTracks();
  95. for (const track of localTracks) {
  96. this._setupSenderE2EEForTrack(session, track);
  97. }
  98. }
  99. /**
  100. * Setup E2EE for the receiving side.
  101. *
  102. * @private
  103. */
  104. _setupReceiverE2EEForTrack(tpc, track) {
  105. if (!this.enabled) {
  106. return;
  107. }
  108. const receiver = tpc.findReceiverForTrack(track.track);
  109. if (receiver) {
  110. this.e2eeCtx.handleReceiver(receiver, track.getType(), track.getParticipantId());
  111. } else {
  112. logger.warn(`Could not handle E2EE for ${track}: receiver not found in: ${tpc}`);
  113. }
  114. }
  115. /**
  116. * Setup E2EE for the sending side.
  117. *
  118. * @param {JingleSessionPC} session - the session which sends the media produced by the track.
  119. * @param {JitsiLocalTrack} track - the local track for which e2e encoder will be configured.
  120. * @private
  121. */
  122. _setupSenderE2EEForTrack(session, track) {
  123. if (!this.enabled) {
  124. return;
  125. }
  126. const pc = session.peerconnection;
  127. const sender = pc && pc.findSenderForTrack(track.track);
  128. if (sender) {
  129. this.e2eeCtx.handleSender(sender, track.getType(), track.getParticipantId());
  130. } else {
  131. logger.warn(`Could not handle E2EE for ${track}: sender not found in ${pc}`);
  132. }
  133. }
  134. /**
  135. * Setup E2EE on the sender that is created for the unmuted local video track.
  136. * @param {JitsiLocalTrack} track - the track for which muted status has changed.
  137. * @private
  138. */
  139. _trackMuteChanged(track) {
  140. if (browser.doesVideoMuteByStreamRemove() && track.isLocal() && track.isVideoTrack() && !track.isMuted()) {
  141. for (const session of this.conference.getMediaSessions()) {
  142. this._setupSenderE2EEForTrack(session, track);
  143. }
  144. }
  145. }
  146. }