Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

ManagedKeyHandler.js 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import { getLogger } from '@jitsi/logger';
  2. import debounce from 'lodash.debounce';
  3. import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
  4. import { KeyHandler } from './KeyHandler';
  5. import { OlmAdapter } from './OlmAdapter';
  6. import { importKey, ratchet } from './crypto-utils';
  7. const logger = getLogger(__filename);
  8. // Period which we'll wait before updating / rotating our keys when a participant
  9. // joins or leaves.
  10. const DEBOUNCE_PERIOD = 5000;
  11. /**
  12. * This module integrates {@link E2EEContext} with {@link OlmAdapter} in order to distribute the keys for encryption.
  13. */
  14. export class ManagedKeyHandler extends KeyHandler {
  15. /**
  16. * Build a new AutomaticKeyHandler instance, which will be used in a given conference.
  17. */
  18. constructor(conference) {
  19. super(conference);
  20. this._key = undefined;
  21. this._conferenceJoined = false;
  22. this._olmAdapter = new OlmAdapter(conference);
  23. this._rotateKey = debounce(this._rotateKeyImpl, DEBOUNCE_PERIOD);
  24. this._ratchetKey = debounce(this._ratchetKeyImpl, DEBOUNCE_PERIOD);
  25. // Olm signalling events.
  26. this._olmAdapter.on(
  27. OlmAdapter.events.PARTICIPANT_KEY_UPDATED,
  28. this._onParticipantKeyUpdated.bind(this));
  29. this.conference.on(
  30. JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,
  31. this._onParticipantPropertyChanged.bind(this));
  32. this.conference.on(
  33. JitsiConferenceEvents.USER_JOINED,
  34. this._onParticipantJoined.bind(this));
  35. this.conference.on(
  36. JitsiConferenceEvents.USER_LEFT,
  37. this._onParticipantLeft.bind(this));
  38. this.conference.on(
  39. JitsiConferenceEvents.CONFERENCE_JOINED,
  40. () => {
  41. this._conferenceJoined = true;
  42. });
  43. }
  44. /**
  45. * When E2EE is enabled it initializes sessions and sets the key.
  46. * Cleans up the sessions when disabled.
  47. *
  48. * @param {boolean} enabled - whether E2EE should be enabled or not.
  49. * @returns {void}
  50. */
  51. async _setEnabled(enabled) {
  52. if (enabled) {
  53. await this._olmAdapter.initSessions();
  54. } else {
  55. this._olmAdapter.clearAllParticipantsSessions();
  56. }
  57. // Generate a random key in case we are enabling.
  58. this._key = enabled ? this._generateKey() : false;
  59. // Send it to others using the E2EE olm channel.
  60. const index = await this._olmAdapter.updateKey(this._key);
  61. // Set our key so we begin encrypting.
  62. this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
  63. }
  64. /**
  65. * Handles an update in a participant's presence property.
  66. *
  67. * @param {JitsiParticipant} participant - The participant.
  68. * @param {string} name - The name of the property that changed.
  69. * @param {*} oldValue - The property's previous value.
  70. * @param {*} newValue - The property's new value.
  71. * @private
  72. */
  73. async _onParticipantPropertyChanged(participant, name, oldValue, newValue) {
  74. switch (name) {
  75. case 'e2ee.idKey':
  76. logger.debug(`Participant ${participant.getId()} updated their id key: ${newValue}`);
  77. break;
  78. case 'e2ee.enabled':
  79. if (!newValue && this.enabled) {
  80. this._olmAdapter.clearParticipantSession(participant);
  81. }
  82. break;
  83. }
  84. }
  85. /**
  86. * Advances (using ratcheting) the current key when a new participant joins the conference.
  87. * @private
  88. */
  89. _onParticipantJoined() {
  90. if (this._conferenceJoined && this.enabled) {
  91. this._ratchetKey();
  92. }
  93. }
  94. /**
  95. * Rotates the current key when a participant leaves the conference.
  96. * @private
  97. */
  98. _onParticipantLeft(id) {
  99. this.e2eeCtx.cleanup(id);
  100. if (this.enabled) {
  101. this._rotateKey();
  102. }
  103. }
  104. /**
  105. * Rotates the local key. Rotating the key implies creating a new one, then distributing it
  106. * to all participants and once they all received it, start using it.
  107. *
  108. * @private
  109. */
  110. async _rotateKeyImpl() {
  111. logger.debug('Rotating key');
  112. this._key = this._generateKey();
  113. const index = await this._olmAdapter.updateKey(this._key);
  114. this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
  115. }
  116. /**
  117. * Advances the current key by using ratcheting.
  118. *
  119. * @private
  120. */
  121. async _ratchetKeyImpl() {
  122. logger.debug('Ratchetting key');
  123. const material = await importKey(this._key);
  124. const newKey = await ratchet(material);
  125. this._key = new Uint8Array(newKey);
  126. const index = this._olmAdapter.updateCurrentKey(this._key);
  127. this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
  128. }
  129. /**
  130. * Handles an update in a participant's key.
  131. *
  132. * @param {string} id - The participant ID.
  133. * @param {Uint8Array | boolean} key - The new key for the participant.
  134. * @param {Number} index - The new key's index.
  135. * @private
  136. */
  137. _onParticipantKeyUpdated(id, key, index) {
  138. logger.debug(`Participant ${id} updated their key`);
  139. this.e2eeCtx.setKey(id, key, index);
  140. }
  141. /**
  142. * Generates a new 256 bit random key.
  143. *
  144. * @returns {Uint8Array}
  145. * @private
  146. */
  147. _generateKey() {
  148. return window.crypto.getRandomValues(new Uint8Array(32));
  149. }
  150. }