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

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